private BlockNotifier CreateNotifier(ConcurrentChain chain) { var rpc = new MockRpcClient(); rpc.OnGetBestBlockHashAsync = () => Task.FromResult(chain.Tip.HashBlock); rpc.OnGetBlockAsync = (blockHash) => Task.FromResult(Block.CreateBlock(chain.GetBlock(blockHash).Header, rpc.Network)); rpc.OnGetBlockHeaderAsync = (blockHash) => Task.FromResult(chain.GetBlock(blockHash).Header); var notifier = new BlockNotifier(TimeSpan.FromMilliseconds(100), rpc); return(notifier); }
public async Task UnsynchronizedBitcoinNodeAsync() { var rpc = new MockRpcClient { OnGetBlockchainInfoAsync = () => Task.FromResult(new BlockchainInfo { Headers = 0, Blocks = 0, InitialBlockDownload = false }), }; var blockNotifier = new BlockNotifier(TimeSpan.MaxValue, rpc); var indexer = new IndexBuilderService(rpc, blockNotifier, "."); indexer.Synchronize(); await Task.Delay(TimeSpan.FromSeconds(1)); //// Assert.False(indexer.IsRunning); // <------------ ERROR: it should have stopped but there is a bug for RegTest Assert.Throws <ArgumentOutOfRangeException>(() => indexer.GetLastFilter()); // There are no filters }
private volatile bool _disposedValue = false; // To detect redundant calls public Coordinator(Network network, BlockNotifier blockNotifier, string folderPath, IRPCClient rpc, CoordinatorRoundConfig roundConfig) { Network = Guard.NotNull(nameof(network), network); BlockNotifier = Guard.NotNull(nameof(blockNotifier), blockNotifier); FolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(folderPath), folderPath, trim: true); RpcClient = Guard.NotNull(nameof(rpc), rpc); RoundConfig = Guard.NotNull(nameof(roundConfig), roundConfig); Rounds = ImmutableList <CoordinatorRound> .Empty; LastSuccessfulCoinJoinTime = DateTimeOffset.UtcNow; Directory.CreateDirectory(FolderPath); UtxoReferee = new UtxoReferee(Network, FolderPath, RpcClient, RoundConfig); if (File.Exists(CoinJoinsFilePath)) { try { var getTxTasks = new List <(Task <Transaction> txTask, string line)>(); var batch = RpcClient.PrepareBatch(); var toRemove = new List <string>(); string[] allLines = File.ReadAllLines(CoinJoinsFilePath); foreach (string line in allLines) { try { getTxTasks.Add((batch.GetRawTransactionAsync(uint256.Parse(line)), line)); } catch (Exception ex) { toRemove.Add(line); var logEntry = ex is RPCException rpce && rpce.RPCCode == RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY ? $"CoinJoins file contains invalid transaction ID {line}" : $"CoinJoins file got corrupted. Deleting offending line \"{line[..20]}\".";
public IndexBuilderService(IRPCClient rpc, BlockNotifier blockNotifier, string indexFilePath) { RpcClient = Guard.NotNull(nameof(rpc), rpc); BlockNotifier = Guard.NotNull(nameof(blockNotifier), blockNotifier); IndexFilePath = Guard.NotNullOrEmptyOrWhitespace(nameof(indexFilePath), indexFilePath); Index = new List <FilterModel>(); IndexLock = new AsyncLock(); StartingHeight = SmartHeader.GetStartingHeader(RpcClient.Network).Height; _serviceStatus = NotStarted; IoHelpers.EnsureContainingDirectoryExists(IndexFilePath); // Testing permissions. using (var _ = File.Open(IndexFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite)) { } if (File.Exists(IndexFilePath)) { if (RpcClient.Network == Network.RegTest) { File.Delete(IndexFilePath); // RegTest is not a global ledger, better to delete it. } else { foreach (var line in File.ReadAllLines(IndexFilePath)) { var filter = FilterModel.FromLine(line); Index.Add(filter); } } } BlockNotifier.OnBlock += BlockNotifier_OnBlock; }
public static PeerManager getTestPeerManager() { var logger = new TestLogger(); var broadcaster = new TestBroadcaster(); var feeEstiamtor = new TestFeeEstimator(); var n = NBitcoin.Network.TestNet; var chainWatchInterface = new ChainWatchInterfaceUtil(n); var seed = new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; var keysInterface = new KeysManager(seed, DateTime.UnixEpoch); var blockNotifier = BlockNotifier.Create(chainWatchInterface); var manyChannelMonitor = ManyChannelMonitor.Create(n, chainWatchInterface, broadcaster, logger, feeEstiamtor); var channelManager = ChannelManager.Create(n, TestUserConfig.Default, chainWatchInterface, keysInterface, logger, broadcaster, feeEstiamtor, 400000, manyChannelMonitor); blockNotifier.RegisterChannelManager(channelManager); blockNotifier.RegisterManyChannelMonitor(manyChannelMonitor); var peerManager = PeerManager.Create( seed, in TestUserConfig.Default, chainWatchInterface, logger, keysInterface.GetNodeSecret().ToBytes(), channelManager, blockNotifier, 10000 ); return(peerManager); }
public void RegistrationTests() { var keySeed = new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; var keysInterface = new KeysManager(keySeed, DateTime.UnixEpoch); var logger = new TestLogger(); var broadcaster = new TestBroadcaster(); var feeEstiamtor = new TestFeeEstimator(); var n = NBitcoin.Network.TestNet; var chainWatchInterface = new ChainWatchInterfaceUtil(n); using var blockNotifier = BlockNotifier.Create(chainWatchInterface); using var manyChannelMonitor = ManyChannelMonitor.Create(n, chainWatchInterface, broadcaster, logger, feeEstiamtor); blockNotifier.RegisterManyChannelMonitor(manyChannelMonitor); using var channelManager = ChannelManager.Create(n, UserConfig.GetDefault(), chainWatchInterface, keysInterface, logger, broadcaster, feeEstiamtor, 0, manyChannelMonitor); blockNotifier.RegisterChannelManager(channelManager); // second block in testnet3 var block = (Block.Parse("0100000006128e87be8b1b4dea47a7247d5528d2702c96826c7a648497e773b800000000e241352e3bec0a95a6217e10c3abb54adfa05abb12c126695595580fb92e222032e7494dffff001d00d235340101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0432e7494d010e062f503253482fffffffff0100f2052a010000002321038a7f6ef1c8ca0c588aa53fa860128077c9e6c11e6830f4d7ee4e763a56b7718fac00000000", n)); blockNotifier.BlockConnected(block, 1); var b = manyChannelMonitor.Serialize(_pool); var(manyChannelMonitor2, keyToHeaderHash) = ManyChannelMonitor.Deserialize(new ManyChannelMonitorReadArgs(chainWatchInterface, broadcaster, logger, feeEstiamtor, n), b.AsMemory(), _pool); using (manyChannelMonitor2) { Assert.True(NBitcoin.Utils.ArrayEqual(b, manyChannelMonitor2.Serialize(_pool))); // without creating any channel, it will result to empty. Assert.Empty(keyToHeaderHash); } blockNotifier.UnregisterManyChannelMonitor(manyChannelMonitor); blockNotifier.UnregisterChannelManager(channelManager); }
public async Task BlockNotifierTestsAsync() { using HostedServices services = new(); var coreNode = await TestNodeBuilder.CreateAsync(services); await services.StartAllAsync(); try { var rpc = coreNode.RpcClient; var walletName = "wallet.dat"; await rpc.CreateWalletAsync(walletName); BlockNotifier notifier = services.FirstOrDefault <BlockNotifier>(); // Make sure we get notification for one block. EventAwaiter <Block> blockEventAwaiter = new(h => notifier.OnBlock += h, h => notifier.OnBlock -= h); var hash = (await rpc.GenerateAsync(1)).First(); var block = await blockEventAwaiter.WaitAsync(TimeSpan.FromSeconds(21)); Assert.Equal(hash, block.GetHash()); // Make sure we get notifications about 10 blocks created at the same time. var blockNum = 10; EventsAwaiter <Block> blockEventsAwaiter = new(h => notifier.OnBlock += h, h => notifier.OnBlock -= h, blockNum); var hashes = (await rpc.GenerateAsync(blockNum)).ToArray(); var arrivedBlocks = (await blockEventsAwaiter.WaitAsync(TimeSpan.FromSeconds(21))).ToArray(); for (int i = 0; i < hashes.Length; i++) { var expected = hashes[i]; var actual = arrivedBlocks[i].GetHash(); Assert.Equal(expected, actual); } // Make sure we get reorg notifications. var reorgNum = 3; var newBlockNum = reorgNum + 1; EventsAwaiter <uint256> reorgEventsAwaiter = new(h => notifier.OnReorg += h, h => notifier.OnReorg -= h, reorgNum); blockEventsAwaiter = new( h => notifier.OnBlock += h, h => notifier.OnBlock -= h, newBlockNum); var reorgedHashes = hashes.TakeLast(reorgNum).ToArray(); await rpc.InvalidateBlockAsync(reorgedHashes[0]); var newHashes = (await rpc.GenerateAsync(newBlockNum)).ToArray(); var reorgedHeaders = (await reorgEventsAwaiter.WaitAsync(TimeSpan.FromSeconds(21))).ToArray(); var newBlocks = (await blockEventsAwaiter.WaitAsync(TimeSpan.FromSeconds(21))).ToArray(); reorgedHashes = reorgedHashes.Reverse().ToArray(); for (int i = 0; i < reorgedHashes.Length; i++) { var expected = reorgedHashes[i]; var actual = reorgedHeaders[i]; Assert.Equal(expected, actual); } for (int i = 0; i < newHashes.Length; i++) { var expected = newHashes[i]; var actual = newBlocks[i].GetHash(); Assert.Equal(expected, actual); } } finally { await services.StopAllAsync(); await coreNode.TryStopAsync(); } }
private volatile bool _disposedValue = false; // To detect redundant calls public Coordinator(Network network, BlockNotifier blockNotifier, string folderPath, IRPCClient rpc, CoordinatorRoundConfig roundConfig) { Network = Guard.NotNull(nameof(network), network); BlockNotifier = Guard.NotNull(nameof(blockNotifier), blockNotifier); FolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(folderPath), folderPath, trim: true); RpcClient = Guard.NotNull(nameof(rpc), rpc); RoundConfig = Guard.NotNull(nameof(roundConfig), roundConfig); Rounds = new List <CoordinatorRound>(); RoundsListLock = new AsyncLock(); CoinJoins = new List <uint256>(); UnconfirmedCoinJoins = new List <uint256>(); CoinJoinsLock = new AsyncLock(); LastSuccessfulCoinJoinTime = DateTimeOffset.UtcNow; Directory.CreateDirectory(FolderPath); UtxoReferee = new UtxoReferee(Network, FolderPath, RpcClient, RoundConfig); if (File.Exists(CoinJoinsFilePath)) { try { var getTxTasks = new List <(Task <Transaction> txTask, string line)>(); var batch = RpcClient.PrepareBatch(); var toRemove = new List <string>(); string[] allLines = File.ReadAllLines(CoinJoinsFilePath); foreach (string line in allLines) { try { getTxTasks.Add((batch.GetRawTransactionAsync(uint256.Parse(line)), line)); } catch (Exception ex) { toRemove.Add(line); var logEntry = ex is RPCException rpce && rpce.RPCCode == RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY ? $"CoinJoins file contains invalid transaction ID {line}" : $"CoinJoins file got corrupted. Deleting offending line \"{line.Substring(0, 20)}\"."; Logger.LogWarning($"{logEntry}. {ex.GetType()}: {ex.Message}"); } } batch.SendBatchAsync().GetAwaiter().GetResult(); foreach (var(txTask, line) in getTxTasks) { try { var tx = txTask.GetAwaiter().GetResult(); CoinJoins.Add(tx.GetHash()); } catch (Exception ex) { toRemove.Add(line); var logEntry = ex is RPCException rpce && rpce.RPCCode == RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY ? $"CoinJoins file contains invalid transaction ID {line}" : $"CoinJoins file got corrupted. Deleting offending line \"{line.Substring(0, 20)}\"."; Logger.LogWarning($"{logEntry}. {ex.GetType()}: {ex.Message}"); } } if (toRemove.Count != 0) // a little performance boost, it'll be empty almost always { var newAllLines = allLines.Where(x => !toRemove.Contains(x)); File.WriteAllLines(CoinJoinsFilePath, newAllLines); } } catch (Exception ex) { Logger.LogWarning($"CoinJoins file got corrupted. Deleting {CoinJoinsFilePath}. {ex.GetType()}: {ex.Message}"); File.Delete(CoinJoinsFilePath); } uint256[] mempoolHashes = RpcClient.GetRawMempoolAsync().GetAwaiter().GetResult(); UnconfirmedCoinJoins.AddRange(CoinJoins.Intersect(mempoolHashes)); } try { string roundCountFilePath = Path.Combine(folderPath, "RoundCount.txt"); if (File.Exists(roundCountFilePath)) { string roundCount = File.ReadAllText(roundCountFilePath); CoordinatorRound.RoundCount = long.Parse(roundCount); } else { // First time initializes (so the first constructor will increment it and we'll start from 1.) CoordinatorRound.RoundCount = 0; } } catch (Exception ex) { CoordinatorRound.RoundCount = 0; Logger.LogInfo($"{nameof(CoordinatorRound.RoundCount)} file was corrupt. Resetting to 0."); Logger.LogDebug(ex); } BlockNotifier.OnBlock += BlockNotifier_OnBlockAsync; }