Exemple #1
0
        private async Task DeleteIfDeprecatedAsync(DigestableSafeMutexIoManager ioManager)
        {
            string firstLine;

            using (var content = ioManager.OpenText())
            {
                firstLine = await content.ReadLineAsync().ConfigureAwait(false);
            }

            try
            {
                FilterModel.FromLine(firstLine);
            }
            catch
            {
                Logger.LogWarning("Old Index file detected. Deleting it.");
                MatureIndexFileManager.DeleteMe();
                ImmatureIndexFileManager.DeleteMe();
                Logger.LogWarning("Successfully deleted old Index file.");
            }
        }
        public static FilterModel GetStartingFilter(Network network)
        {
            var startingHeader = SmartHeader.GetStartingHeader(network);

            if (network == Network.Main)
            {
                return(FilterModel.FromLine($"{startingHeader.Height}:{startingHeader.BlockHash}:02832810ec08a0:{startingHeader.PrevHash}:{startingHeader.BlockTime.ToUnixTimeSeconds()}"));
            }
            else if (network == Network.TestNet)
            {
                return(FilterModel.FromLine($"{startingHeader.Height}:{startingHeader.BlockHash}:00000000000f0d5edcaeba823db17f366be49a80d91d15b77747c2e017b8c20a:{startingHeader.PrevHash}:{startingHeader.BlockTime.ToUnixTimeSeconds()}"));
            }
            else if (network == Network.RegTest)
            {
                GolombRiceFilter filter = IndexBuilderService.CreateDummyEmptyFilter(startingHeader.BlockHash);
                return(FilterModel.FromLine($"{startingHeader.Height}:{startingHeader.BlockHash}:{filter.ToString()}:{startingHeader.PrevHash}:{startingHeader.BlockTime.ToUnixTimeSeconds()}"));
            }
            else
            {
                throw new NotSupportedNetworkException(network);
            }
        }
        public static FilterModel GetStartingFilter(Network network)
        {
            var startingHeader = SmartHeader.GetStartingHeader(network);

            if (network == Network.Main)
            {
                return(FilterModel.FromLine($"{startingHeader.Height}:{startingHeader.BlockHash}:fd37025542ac13565ae0612c2d2c178df07d05402c527fd91942a68300e20853315e278ebc861f7ec7e20ee5c7ea39197ab66ec38c6cacfb7a960e87eafe739fbb58e703811b2ab0cc9ac55c34f3a6d70536776a34679680d5539e3ff089a08d6fed732e3c26e4b5527790cd14a7bd83c8142b5cf4f10fbd09baed653cb4ebf9100bcc5d30878b1aaa01a00230d505004e5d686fd752ec8624f88d640867cab753ca1d3b7829b05a2c20606279242688ba319c2173a5a885399b58c45127bdf976c4d45e79425ff809da944d30405f929c10186e1666bb444cdeece71133049e5e44a265d44c4122e83919d6573220f3b9bdf38ee3037e91b0ae0485707e9a8755dd530fff74338652b55aa547f3c505db5f0a1b92f95fd95852d48167ef857d55500dfb8c7209f05c1669a73f15e7bb26537462a05da4f0d803315dd43630b23019be48a556ebe50752bdd37080460254e4d6d92be80ab0ed8efd882fd0c360fbf00b387b6e5ef4d9a27267b2f51151684fa7cf3064f8e2fc918843215512c838fdd35270da33df3d203e02539f1b030863619a4fdd93b7771de31d35436a50c904225942fda79e2606a21d64dbb110c79f202d10acd94a2ac7cf24cff2dc816ac6828941abec3d9ae721c09f5a2070d3a99aba0fb1e33d7de26c3c2b7f40b002719138826a670c38d56fe5a67e827bf48865f2c34e4eafa68486ecb419118144d212d5a10ee41f26c07b2456c5a0b74fb86cb4eaec27ac2cf80847e57d711754589716c70eea0d1c595b6dd413aa588542acc5ea9d5c102f3b4aac876bb91f57c1eea063d520517bd0c48e2d79ec2164525e1c24a34e8f50e4256790395fcdd5c71b50949738ab92d44476c2073e16bedb4b31517d2264ae4d824b0990fe316919c432edc9f71c1a6a21937786b68e9e11f788fba04ad1253cfcd3148698ec8a4a1054a33aa51adcdb61c922d44ebefb6dc1aab67d07b48bb702606047230e4439bec8d1b371683be41617e4f163509b09f5cd6577956b1857a338764486017949f594c90be92783776ff0ba0d6d517d18d5842c36eee044290df2ca0ad3818f3bcd874ad23e41a77c441da273f20e3472801a4892b635d3d2a0b76ca1c0025b5283850897993410f5c654527ebfc446445ca10d0264c964b86719a108179f303165b3b5f53441d9fe9f6e1a1ae2d2d61c6c34752ca9b02b6c8fb2166dba95370ad1cace915e90cab14797a33be4454fe44dfecb015c59e76b60336192b262169db6461b26da3b03cdcca4c185e7d76bef944ea02a350449e9119aeb82ac094b87cd3fec4bd289c55bf008e3eb614017240c73fb3440de1cbd0f1e634bd997f1c10ccf97598b8d0144bd8608999a6420ff65cfb17f275b33e16d08bd5f01ff2a98b02f3cc84b1931b68b4d47ebab850824d55029e2deaab509bbe09b698675add8a5119630207c41c54a9503ea2ab15ce4446504ce19c52313733a95fed0dea0f9e56c5658d52d43f09b7a246140dac1e49aeb00d7d0b50aac0fab135249f523ee1fc59d8d2c18af69db23fc26a006451e132448bc7b671fa864897a8e23b1bf4b97397d091f8106e12174efd82e7f4479aab210f488cd4cb45a093392ecab407ad3a8836c771ec87d36d41aeeac20c36b3d39c87ea1eaba86411a78ca33f658cceadb903ec7482a395f11b7aa54eecd9da1a9a542695723fff6056ac736b788abd2498488a6737236d70e7aceecdc71e4878c2f1e390866020cf20681230f3858dd7ae44b9bdb0a3726b12db3b42b2863cf03d3e34b733d9c63e92c5b07bf942dc740a2c902621c1f74c4e00e3a36002fb8a24febc89aa5350544566de06ba81aa915e115368aabe7fdedb676082abac78a1b216d7e56c8ed0da8ada71bc2f46ade811233f9568308136700e21b5941286d0d48fe43009b48161837e5255e639d8a4225928a2637d745cbc580c9b14c50860dba2f1be321463e47cb4914c81c7b81206090aa23a73103f139dfca303447cc7140eef9a5f5235675073cdcd5635abad96de169975f2cf06689c9dc9c9d7557d504c7ec1541b493769a2106537d56d489f8800d5d76870c716678ff3672c76b8a179b0a601858b8973ed59a9ecc13f115b44d2a3251e658b95bd5f3f1aa21c57a6eb960dcbf968890:{startingHeader.PrevHash}:{startingHeader.BlockTime.ToUnixTimeSeconds()}"));
            }
            else if (network == Network.TestNet)
            {
                return(FilterModel.FromLine($"{startingHeader.Height}:{startingHeader.BlockHash}:00000000000f0d5edcaeba823db17f366be49a80d91d15b77747c2e017b8c20a:{startingHeader.PrevHash}:{startingHeader.BlockTime.ToUnixTimeSeconds()}"));
            }
            else if (network == Network.RegTest)
            {
                GolombRiceFilter filter = IndexBuilderService.CreateDummyEmptyFilter(startingHeader.BlockHash);
                return(FilterModel.FromLine($"{startingHeader.Height}:{startingHeader.BlockHash}:{filter}:{startingHeader.PrevHash}:{startingHeader.BlockTime.ToUnixTimeSeconds()}"));
            }
            else
            {
                throw new NotSupportedNetworkException(network);
            }
        }
Exemple #4
0
        public static FilterModel GetStartingFilter(Network network)
        {
            var startingHeader = SmartHeader.GetStartingHeader(network);

            if (network == NBitcoin.Altcoins.Litecoin.Instance.Mainnet)
            {
                return(FilterModel.FromLine($"{startingHeader.Height}:{startingHeader.BlockHash}:{startingHeader.BlockHash}:{startingHeader.PrevHash}:{startingHeader.BlockTime.ToUnixTimeSeconds()}"));
            }
            else if (network == NBitcoin.Altcoins.Litecoin.Instance.Testnet)
            {
                return(FilterModel.FromLine($"{startingHeader.Height}:{startingHeader.BlockHash}:{startingHeader.BlockHash}:{startingHeader.PrevHash}:{startingHeader.BlockTime.ToUnixTimeSeconds()}"));
            }
            else if (network == NBitcoin.Altcoins.Litecoin.Instance.Regtest)
            {
                GolombRiceFilter filter = IndexBuilderService.CreateDummyEmptyFilter(startingHeader.BlockHash);
                return(FilterModel.FromLine($"{startingHeader.Height}:{startingHeader.BlockHash}:{filter}:{startingHeader.PrevHash}:{startingHeader.BlockTime.ToUnixTimeSeconds()}"));
            }
            else
            {
                throw new NotSupportedNetworkException(network);
            }
        }
    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 async Task ForeachFiltersAsync(Func <FilterModel, Task> todo, Height fromHeight, CancellationToken cancel = default)
        {
            using (await MatureIndexFileManager.Mutex.LockAsync(cancel).ConfigureAwait(false))
                using (await IndexLock.LockAsync(cancel).ConfigureAwait(false))
                {
                    var firstImmatureHeight = ImmatureFilters.FirstOrDefault()?.Header?.Height;
                    if (!firstImmatureHeight.HasValue || firstImmatureHeight.Value > fromHeight)
                    {
                        if (MatureIndexFileManager.Exists())
                        {
                            uint height = StartingHeight;
                            using var sr = MatureIndexFileManager.OpenText();
                            if (!sr.EndOfStream)
                            {
                                var    lineTask = sr.ReadLineAsync();
                                Task   tTask    = Task.CompletedTask;
                                string line     = null;
                                while (lineTask != null)
                                {
                                    if (firstImmatureHeight == height)
                                    {
                                        break;                                 // Let's use our the immature filters from here on. The content is the same, just someone else modified the file.
                                    }

                                    if (line is null)
                                    {
                                        line = await lineTask.ConfigureAwait(false);
                                    }

                                    lineTask = sr.EndOfStream ? null : sr.ReadLineAsync();

                                    if (height < fromHeight.Value)
                                    {
                                        height++;
                                        line = null;
                                        continue;
                                    }

                                    var filter = FilterModel.FromLine(line);

                                    await tTask.ConfigureAwait(false);

                                    tTask = todo(filter);

                                    height++;

                                    line = null;
                                }
                                await tTask.ConfigureAwait(false);
                            }

                            while (!sr.EndOfStream)
                            {
                                var line = await sr.ReadLineAsync().ConfigureAwait(false);

                                if (firstImmatureHeight == height)
                                {
                                    break;                             // Let's use our the immature filters from here on. The content is the same, just someone else modified the file.
                                }

                                if (height < fromHeight.Value)
                                {
                                    height++;
                                    continue;
                                }

                                var filter = FilterModel.FromLine(line);

                                await todo(filter).ConfigureAwait(false);

                                height++;
                            }
                        }
                    }

                    foreach (FilterModel filter in ImmatureFilters)
                    {
                        await todo(filter).ConfigureAwait(false);
                    }
                }
        }
        /// <inheritdoc />
        public override object?ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var value = Guard.Correct((string)reader.Value);

            return(string.IsNullOrWhiteSpace(value) ? default : FilterModel.FromLine(value));
        }
Exemple #8
0
        public void Synchronize(TimeSpan requestInterval)
        {
            Guard.NotNull(nameof(requestInterval), requestInterval);
            Interlocked.Exchange(ref _running, 1);

            Task.Run(async() =>
            {
                try
                {
                    while (IsRunning)
                    {
                        try
                        {
                            // If stop was requested return.
                            if (IsRunning == false)
                            {
                                return;
                            }

                            FilterModel bestKnownFilter;
                            using (await IndexLock.LockAsync())
                            {
                                bestKnownFilter = Index.Last();
                            }

                            var response = await Client.SendAsync(HttpMethod.Get, $"/api/v1/btc/Blockchain/filters/{bestKnownFilter.BlockHash}");

                            if (response.StatusCode == HttpStatusCode.NoContent)
                            {
                                continue;
                            }
                            if (response.StatusCode == HttpStatusCode.OK)
                            {
                                var filters = await response.Content.ReadAsJsonAsync <List <string> >();
                                using (await IndexLock.LockAsync())
                                {
                                    for (int i = 0; i < filters.Count; i++)
                                    {
                                        var filterModel = FilterModel.FromLine(filters[i], bestKnownFilter.BlockHeight + i + 1);

                                        Index.Add(filterModel);
                                        OnNewFilter(filterModel);
                                    }

                                    if (filters.Count == 1)                                     // minor optimization
                                    {
                                        await File.AppendAllLinesAsync(IndexFilePath, new[] { Index.Last().ToLine() });
                                    }
                                    else
                                    {
                                        await File.WriteAllLinesAsync(IndexFilePath, Index.Select(x => x.ToLine()));
                                    }

                                    Logger.LogInfo <IndexDownloader>($"Downloaded filters for blocks from {bestKnownFilter.BlockHeight.Value + 1} to {Index.Last().BlockHeight}.");
                                }

                                continue;
                            }
                            else if (response.StatusCode == HttpStatusCode.NotFound)
                            {
                                // Reorg happened
                                var reorgedHash = bestKnownFilter.BlockHash;
                                Logger.LogInfo <IndexDownloader>($"REORG Invalid Block: {reorgedHash}");
                                // 1. Rollback index
                                using (await IndexLock.LockAsync())
                                {
                                    Index.RemoveAt(Index.Count - 1);
                                }

                                OnReorg(reorgedHash);

                                // 2. Serialize Index. (Remove last line.)
                                var lines = File.ReadAllLines(IndexFilePath);
                                File.WriteAllLines(IndexFilePath, lines.Take(lines.Length - 1).ToArray());

                                // 3. Skip the last valid block.
                                continue;
                            }
                            else
                            {
                                var error = await response.Content.ReadAsStringAsync();
                                throw new HttpRequestException($"{response.StatusCode.ToReasonString()}: {error}");
                            }
                        }
                        catch (Exception ex)
                        {
                            Logger.LogError <IndexDownloader>(ex);
                        }
                        finally
                        {
                            await Task.Delay(requestInterval);                             // Ask for new index in every requestInterval.
                        }
                    }
                }
                finally
                {
                    if (IsStopping)
                    {
                        Interlocked.Exchange(ref _running, 3);
                    }
                }
            });
        }
Exemple #9
0
        public async Task FilterBuilderTestAsync()
        {
            using (var builder = await NodeBuilder.CreateAsync())
            {
                await builder.CreateNodeAsync();

                await builder.StartAllAsync();

                CoreNode regtestNode = builder.Nodes[0];
                regtestNode.Generate(101);
                RPCClient rpc = regtestNode.CreateRpcClient();

                var indexBuilderServiceDir = Path.Combine(SharedFixture.DataDir, nameof(IndexBuilderService));
                var indexFilePath          = Path.Combine(indexBuilderServiceDir, $"Index{rpc.Network}.dat");
                var utxoSetFilePath        = Path.Combine(indexBuilderServiceDir, $"UtxoSet{rpc.Network}.dat");

                var indexBuilderService = new IndexBuilderService(rpc, indexFilePath, utxoSetFilePath);
                try
                {
                    indexBuilderService.Synchronize();

                    // Test initial synchronization.
                    var     times     = 0;
                    uint256 firstHash = await rpc.GetBlockHashAsync(0);

                    while (indexBuilderService.GetFilterLinesExcluding(firstHash, 102, out _).filters.Count() != 101)
                    {
                        if (times > 500)                         // 30 sec
                        {
                            throw new TimeoutException($"{nameof(IndexBuilderService)} test timed out.");
                        }
                        await Task.Delay(100);

                        times++;
                    }

                    // Test later synchronization.
                    regtestNode.Generate(10);
                    times = 0;
                    while (indexBuilderService.GetFilterLinesExcluding(firstHash, 112, out bool found5).filters.Count() != 111)
                    {
                        Assert.True(found5);
                        if (times > 500)                         // 30 sec
                        {
                            throw new TimeoutException($"{nameof(IndexBuilderService)} test timed out.");
                        }
                        await Task.Delay(100);

                        times++;
                    }

                    // Test correct number of filters is received.
                    var hundredthHash = await rpc.GetBlockHashAsync(100);

                    Assert.Equal(11, indexBuilderService.GetFilterLinesExcluding(hundredthHash, 12, out bool found).filters.Count());
                    Assert.True(found);
                    var bestHash = await rpc.GetBestBlockHashAsync();

                    Assert.Empty(indexBuilderService.GetFilterLinesExcluding(bestHash, 1, out bool found2).filters);
                    Assert.True(found2);
                    Assert.Empty(indexBuilderService.GetFilterLinesExcluding(uint256.Zero, 1, out bool found3).filters);
                    Assert.False(found3);

                    // Test filter block hashes are correct.
                    var filters = indexBuilderService.GetFilterLinesExcluding(firstHash, 112, out bool found4).filters.ToArray();
                    Assert.True(found4);
                    for (int i = 0; i < 111; i++)
                    {
                        var expectedHash = await rpc.GetBlockHashAsync(i + 1);

                        var filterModel = FilterModel.FromLine(filters[i], i);
                        Assert.Equal(expectedHash, filterModel.BlockHash);
                        Assert.Null(filterModel.Filter);
                    }
                }
                finally
                {
                    if (indexBuilderService != null)
                    {
                        await indexBuilderService.StopAsync();
                    }
                }
            }
        }
Exemple #10
0
        public void Synchronize(TimeSpan requestInterval)
        {
            Guard.NotNull(nameof(requestInterval), requestInterval);
            Interlocked.Exchange(ref _running, 1);

            Task.Run(async() =>
            {
                FilterModel bestKnownFilter = null;

                try
                {
                    while (IsRunning)
                    {
                        try
                        {
                            // If stop was requested return.
                            if (IsRunning == false)
                            {
                                return;
                            }

                            using (await IndexLock.LockAsync())
                            {
                                bestKnownFilter = Index.Last();
                            }

                            var filters = await WasabiClient.GetFiltersAsync(bestKnownFilter.BlockHash, 1000);

                            if (!filters.Any())
                            {
                                continue;
                            }
                            using (await IndexLock.LockAsync())
                            {
                                var filtersList = filters.ToList();                                 // performance
                                for (int i = 0; i < filtersList.Count; i++)
                                {
                                    var filterModel = FilterModel.FromLine(filtersList[i], bestKnownFilter.BlockHeight + i + 1);

                                    Index.Add(filterModel);
                                    NewFilter?.Invoke(this, filterModel);
                                }

                                if (filtersList.Count == 1)                                 // minor optimization
                                {
                                    await File.AppendAllLinesAsync(IndexFilePath, new[] { Index.Last().ToLine() });
                                }
                                else
                                {
                                    await File.WriteAllLinesAsync(IndexFilePath, Index.Select(x => x.ToLine()));
                                }

                                Logger.LogInfo <IndexDownloader>($"Downloaded filters for blocks from {bestKnownFilter.BlockHeight.Value + 1} to {Index.Last().BlockHeight}.");
                            }

                            continue;
                        }
                        catch (HttpRequestException ex) when(ex.Message.StartsWith(HttpStatusCode.NotFound.ToReasonString()))
                        {
                            // Reorg happened
                            var reorgedHash = bestKnownFilter.BlockHash;
                            Logger.LogInfo <IndexDownloader>($"REORG Invalid Block: {reorgedHash}");
                            // 1. Rollback index
                            using (await IndexLock.LockAsync())
                            {
                                Index.RemoveAt(Index.Count - 1);
                            }

                            Reorged?.Invoke(this, reorgedHash);

                            // 2. Serialize Index. (Remove last line.)
                            var lines = File.ReadAllLines(IndexFilePath);
                            File.WriteAllLines(IndexFilePath, lines.Take(lines.Length - 1).ToArray());

                            // 3. Skip the last valid block.
                            continue;
                        }
                        catch (Exception ex)
                        {
                            Logger.LogError <IndexDownloader>(ex);
                        }
                        finally
                        {
                            await Task.Delay(requestInterval);                             // Ask for new index in every requestInterval.
                        }
                    }
                }
                finally
                {
                    if (IsStopping)
                    {
                        Interlocked.Exchange(ref _running, 3);
                    }
                }
            });
        }