public async Task InitializeAsync(string workFolderPath, Network network)
        {
            using (BenchmarkLogger.Measure())
            {
                WorkFolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true);
                IoHelpers.EnsureDirectoryExists(WorkFolderPath);

                Network = Guard.NotNull(nameof(network), network);

                IndexStore       = new IndexStore();
                TransactionStore = new AllTransactionStore();
                var networkWorkFolderPath = Path.Combine(WorkFolderPath, Network.ToString());
                var indexStoreFolderPath  = Path.Combine(networkWorkFolderPath, "IndexStore");
                SmartHeaderChain = new SmartHeaderChain();
                MempoolService   = new MempoolService();

                var initTasks = new[]
                {
                    IndexStore.InitializeAsync(indexStoreFolderPath, Network, SmartHeaderChain),
                    TransactionStore.InitializeAsync(networkWorkFolderPath, Network)
                };

                await Task.WhenAll(initTasks).ConfigureAwait(false);

                IsInitialized = true;
            }
        }
Example #2
0
        public AllTransactionStore(string workFolderPath, Network network)
        {
            WorkFolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true);
            IoHelpers.EnsureDirectoryExists(WorkFolderPath);

            Network = Guard.NotNull(nameof(network), network);
        }
Example #3
0
        public async Task InitializeAsync(string workFolderPath, Network network, string operationName, CancellationToken cancel)
        {
            using (BenchmarkLogger.Measure(operationName: operationName))
            {
                WorkFolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true);
                Network        = Guard.NotNull(nameof(network), network);

                var transactionsFilePath = Path.Combine(WorkFolderPath, "Transactions.dat");

                // In Transactions.dat every line starts with the tx id, so the first character is the best for digest creation.
                TransactionsFileManager = new IoManager(transactionsFilePath);

                cancel.ThrowIfCancellationRequested();
                using (await TransactionsFileAsyncLock.LockAsync(cancel).ConfigureAwait(false))
                {
                    IoHelpers.EnsureDirectoryExists(WorkFolderPath);
                    cancel.ThrowIfCancellationRequested();

                    if (!TransactionsFileManager.Exists())
                    {
                        await SerializeAllTransactionsNoLockAsync().ConfigureAwait(false);

                        cancel.ThrowIfCancellationRequested();
                    }

                    await InitializeTransactionsNoLockAsync(cancel).ConfigureAwait(false);
                }
            }
        }
Example #4
0
        public async Task InitializeAsync(CancellationToken cancel = default)
        {
            using (BenchmarkLogger.Measure())
            {
                using (await IndexLock.LockAsync().ConfigureAwait(false))
                    using (await MatureIndexAsyncLock.LockAsync().ConfigureAwait(false))
                        using (await ImmatureIndexAsyncLock.LockAsync().ConfigureAwait(false))
                        {
                            IoHelpers.EnsureDirectoryExists(WorkFolderPath);

                            cancel.ThrowIfCancellationRequested();

                            await EnsureBackwardsCompatibilityAsync().ConfigureAwait(false);

                            if (Network == Network.RegTest)
                            {
                                MatureIndexFileManager.DeleteMe();                 // RegTest is not a global ledger, better to delete it.
                                ImmatureIndexFileManager.DeleteMe();
                            }
                            cancel.ThrowIfCancellationRequested();

                            if (!MatureIndexFileManager.Exists())
                            {
                                await MatureIndexFileManager.WriteAllLinesAsync(new[] { StartingFilter.ToLine() }).ConfigureAwait(false);
                            }
                            cancel.ThrowIfCancellationRequested();

                            await InitializeFiltersAsync(cancel).ConfigureAwait(false);
                        }
            }
        }
Example #5
0
        public async Task InitializeAsync(string workFolderPath, Network network, string operationName)
        {
            using (BenchmarkLogger.Measure(operationName: operationName))
            {
                WorkFolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true);
                Network        = Guard.NotNull(nameof(network), network);

                Transactions     = new Dictionary <uint256, SmartTransaction>();
                TransactionsLock = new object();

                var fileName             = Path.Combine(WorkFolderPath, "Transactions.dat");
                var transactionsFilePath = Path.Combine(WorkFolderPath, fileName);
                TransactionsFileManager = new MutexIoManager(transactionsFilePath);

                using (await TransactionsFileManager.Mutex.LockAsync().ConfigureAwait(false))
                {
                    IoHelpers.EnsureDirectoryExists(WorkFolderPath);

                    if (!TransactionsFileManager.Exists())
                    {
                        await SerializeAllTransactionsNoMutexAsync().ConfigureAwait(false);
                    }

                    await InitializeTransactionsNoMutexAsync().ConfigureAwait(false);
                }
            }
        }
Example #6
0
    /// <summary>
    /// Creates new instance of <see cref="PidFile"/>.
    /// </summary>
    /// <param name="dataDir">Path to location where to store PID file.</param>
    /// <param name="pidFileName">File name.</param>
    public PidFile(string dataDir, string pidFileName)
    {
        string checkedDataDir = Guard.NotNullOrEmptyOrWhitespace(nameof(dataDir), dataDir);

        IoHelpers.EnsureDirectoryExists(checkedDataDir);

        FilePath = Path.Combine(checkedDataDir, pidFileName);
    }
        public IndexStore(string workFolderPath, Network network, SmartHeaderChain hashChain)
        {
            WorkFolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true);
            IoHelpers.EnsureDirectoryExists(WorkFolderPath);

            Network          = Guard.NotNull(nameof(network), network);
            SmartHeaderChain = Guard.NotNull(nameof(hashChain), hashChain);
        }
Example #8
0
    public async Task ToFileAsync(string folderPath)
    {
        folderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(folderPath), folderPath);
        IoHelpers.EnsureDirectoryExists(folderPath);
        string filePath = Path.Combine(folderPath, $"{Version}.txt");

        RemoveCandidates(folderPath);
        await File.WriteAllTextAsync(filePath, Content).ConfigureAwait(false);
    }
Example #9
0
 private static void RemoveCandidates(string legalFolderPath)
 {
     IoHelpers.EnsureDirectoryExists(legalFolderPath);
     foreach (var candidate in FindCandidates(legalFolderPath))
     {
         File.Delete(candidate);
         Logger.LogInfo($"Removed legal doc candidate: {candidate}.");
     }
 }
Example #10
0
    public CoordinatorParameters(string dataDir)
    {
        ApplicationDataDir = dataDir;
        IoHelpers.EnsureDirectoryExists(CoordinatorDataDir);

        var runtimeConfigurationFilePath = Path.Combine(ApplicationDataDir, "WabiSabiConfig.json");

        RuntimeCoordinatorConfig = new(runtimeConfigurationFilePath);
        RuntimeCoordinatorConfig.LoadOrCreateDefaultFile();
    }
Example #11
0
        public async Task InitializeAsync(string workFolderPath, Network network)
        {
            WorkFolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true);
            IoHelpers.EnsureDirectoryExists(WorkFolderPath);

            Network = Guard.NotNull(nameof(network), network);

            IndexStore = new IndexStore();
            var indexStoreFolderPath = Path.Combine(WorkFolderPath, Network.ToString());

            HashChain = new HashChain();
            await IndexStore.InitializeAsync(indexStoreFolderPath, Network, HashChain);
        }
Example #12
0
    public IndexStore(string workFolderPath, Network network, SmartHeaderChain smartHeaderChain)
    {
        WorkFolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true);
        IoHelpers.EnsureDirectoryExists(WorkFolderPath);
        var indexFilePath = Path.Combine(WorkFolderPath, "MatureIndex.dat");

        MatureIndexFileManager = new DigestableSafeIoManager(indexFilePath, useLastCharacterDigest: true);
        var immatureIndexFilePath = Path.Combine(WorkFolderPath, "ImmatureIndex.dat");

        ImmatureIndexFileManager = new DigestableSafeIoManager(immatureIndexFilePath, useLastCharacterDigest: true);

        Network          = network;
        StartingFilter   = StartingFilters.GetStartingFilter(Network);
        SmartHeaderChain = smartHeaderChain;
    }
        public BitcoinStore(
            string workFolderPath,
            Network network,
            IndexStore indexStore,
            AllTransactionStore transactionStore,
            MempoolService mempoolService)
        {
            WorkFolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true);
            IoHelpers.EnsureDirectoryExists(WorkFolderPath);

            Network          = Guard.NotNull(nameof(network), network);
            IndexStore       = indexStore;
            TransactionStore = transactionStore;
            MempoolService   = mempoolService;
        }
    private void CreateFolders()
    {
        try
        {
            if (Directory.Exists(BlocksFolderPath) && Network == Network.RegTest)
            {
                Directory.Delete(BlocksFolderPath, true);
            }
        }
        catch (Exception ex)
        {
            Logger.LogDebug(ex);
        }

        IoHelpers.EnsureDirectoryExists(BlocksFolderPath);
    }
Example #15
0
    public IndexStore(string workFolderPath, Network network, SmartHeaderChain hashChain)
    {
        WorkFolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true);
        IoHelpers.EnsureDirectoryExists(WorkFolderPath);
        var indexFilePath = Path.Combine(WorkFolderPath, "MatureIndex.dat");

        MatureIndexFileManager = new DigestableSafeIoManager(indexFilePath, digestRandomIndex: -1);
        var immatureIndexFilePath = Path.Combine(WorkFolderPath, "ImmatureIndex.dat");

        ImmatureIndexFileManager = new DigestableSafeIoManager(immatureIndexFilePath, digestRandomIndex: -1);

        Network = Guard.NotNull(nameof(network), network);

        StartingFilter = StartingFilters.GetStartingFilter(Network);

        SmartHeaderChain = Guard.NotNull(nameof(hashChain), hashChain);
    }
Example #16
0
        public async Task InitializeAsync(string workFolderPath, Network network)
        {
            using (BenchmarkLogger.Measure())
            {
                WorkFolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true);
                IoHelpers.EnsureDirectoryExists(WorkFolderPath);

                Network = Guard.NotNull(nameof(network), network);

                IndexStore = new IndexStore();
                var indexStoreFolderPath = Path.Combine(WorkFolderPath, Network.ToString(), "IndexStore");
                HashChain      = new HashChain();
                MempoolService = new MempoolService();

                await IndexStore.InitializeAsync(indexStoreFolderPath, Network, HashChain).ConfigureAwait(false);
            }
        }
Example #17
0
        public async Task InitializeAsync(string workFolderPath, Network network, HashChain hashChain)
        {
            WorkFolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true);
            Network        = Guard.NotNull(nameof(network), network);
            HashChain      = Guard.NotNull(nameof(hashChain), hashChain);
            var indexFilePath = Path.Combine(WorkFolderPath, "MatureIndex.dat");

            MatureIndexFileManager = new IoManager(indexFilePath, digestRandomIndex: -1);
            var immatureIndexFilePath = Path.Combine(WorkFolderPath, "ImmatureIndex.dat");

            ImmatureIndexFileManager = new IoManager(immatureIndexFilePath, digestRandomIndex: -1);

            StartingFilter = StartingFilters.GetStartingFilter(Network);
            StartingHeight = StartingFilters.GetStartingHeight(Network);

            ImmatureFilters = new List <FilterModel>(150);

            IndexLock = new AsyncLock();

            using (await IndexLock.LockAsync())
            {
                using (await MatureIndexFileManager.Mutex.LockAsync())
                    using (await ImmatureIndexFileManager.Mutex.LockAsync())
                    {
                        IoHelpers.EnsureDirectoryExists(WorkFolderPath);

                        await TryEnsureBackwardsCompatibilityAsync();

                        if (Network == Network.RegTest)
                        {
                            MatureIndexFileManager.DeleteMe();                     // RegTest is not a global ledger, better to delete it.
                            ImmatureIndexFileManager.DeleteMe();
                        }

                        if (!MatureIndexFileManager.Exists())
                        {
                            await MatureIndexFileManager.WriteAllLinesAsync(new[] { StartingFilter.ToHeightlessLine() });
                        }

                        await InitializeFiltersAsync();
                    }
            }
        }
        public async Task InitializeAsync(string workFolderPath, Network network, SmartHeaderChain hashChain)
        {
            using (BenchmarkLogger.Measure())
            {
                WorkFolderPath   = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true);
                Network          = Guard.NotNull(nameof(network), network);
                SmartHeaderChain = Guard.NotNull(nameof(hashChain), hashChain);
                var indexFilePath = Path.Combine(WorkFolderPath, "MatureIndex.dat");
                MatureIndexFileManager = new DigestableSafeMutexIoManager(indexFilePath, digestRandomIndex: -1);
                var immatureIndexFilePath = Path.Combine(WorkFolderPath, "ImmatureIndex.dat");
                ImmatureIndexFileManager = new DigestableSafeMutexIoManager(immatureIndexFilePath, digestRandomIndex: -1);

                StartingFilter = StartingFilters.GetStartingFilter(Network);
                StartingHeight = StartingFilter.Header.Height;

                ImmatureFilters = new List <FilterModel>(150);

                IndexLock = new AsyncLock();

                using (await IndexLock.LockAsync().ConfigureAwait(false))
                    using (await MatureIndexFileManager.Mutex.LockAsync().ConfigureAwait(false))
                        using (await ImmatureIndexFileManager.Mutex.LockAsync().ConfigureAwait(false))
                        {
                            IoHelpers.EnsureDirectoryExists(WorkFolderPath);

                            await EnsureBackwardsCompatibilityAsync().ConfigureAwait(false);

                            if (Network == NBitcoin.Altcoins.Litecoin.Instance.Regtest)
                            {
                                MatureIndexFileManager.DeleteMe();                 // RegTest is not a global ledger, better to delete it.
                                ImmatureIndexFileManager.DeleteMe();
                            }

                            if (!MatureIndexFileManager.Exists())
                            {
                                await MatureIndexFileManager.WriteAllLinesAsync(new[] { StartingFilter.ToLine() }).ConfigureAwait(false);
                            }

                            await InitializeFiltersAsync().ConfigureAwait(false);
                        }
            }
        }
Example #19
0
        public static LegalDocuments?TryLoadAgreed(string dataDir)
        {
            var legalFolderPath = Path.Combine(dataDir, LegalFolderName);

            IoHelpers.EnsureDirectoryExists(legalFolderPath);
            var legalDocCandidates     = FindCandidates(legalFolderPath);
            var legalDocCandidateCount = legalDocCandidates.Count();

            if (legalDocCandidateCount == 1)
            {
                var filePath = legalDocCandidates.Single();
                return(new LegalDocuments(filePath));
            }
            else if (legalDocCandidateCount > 1)
            {
                RemoveCandidates(legalFolderPath);
            }

            return(null);
        }
        public async Task InitializeAsync()
        {
            using (BenchmarkLogger.Measure())
            {
                var indexFilePath = Path.Combine(WorkFolderPath, "MatureIndex.dat");
                MatureIndexFileManager = new DigestableSafeMutexIoManager(indexFilePath, digestRandomIndex: -1);
                var immatureIndexFilePath = Path.Combine(WorkFolderPath, "ImmatureIndex.dat");
                ImmatureIndexFileManager = new DigestableSafeMutexIoManager(immatureIndexFilePath, digestRandomIndex: -1);

                StartingFilter = StartingFilters.GetStartingFilter(Network);
                StartingHeight = StartingFilter.Header.Height;

                ImmatureFilters = new List <FilterModel>(150);

                IndexLock = new AsyncLock();

                using (await IndexLock.LockAsync().ConfigureAwait(false))
                    using (await MatureIndexFileManager.Mutex.LockAsync().ConfigureAwait(false))
                        using (await ImmatureIndexFileManager.Mutex.LockAsync().ConfigureAwait(false))
                        {
                            IoHelpers.EnsureDirectoryExists(WorkFolderPath);

                            await EnsureBackwardsCompatibilityAsync().ConfigureAwait(false);

                            if (Network == Network.RegTest)
                            {
                                MatureIndexFileManager.DeleteMe();                 // RegTest is not a global ledger, better to delete it.
                                ImmatureIndexFileManager.DeleteMe();
                            }

                            if (!MatureIndexFileManager.Exists())
                            {
                                await MatureIndexFileManager.WriteAllLinesAsync(new[] { StartingFilter.ToLine() }).ConfigureAwait(false);
                            }

                            await InitializeFiltersAsync().ConfigureAwait(false);
                        }
            }
        }
Example #21
0
        public static async Task <CoreNode> CreateAsync(CoreNodeParams coreNodeParams, CancellationToken cancel)
        {
            Guard.NotNull(nameof(coreNodeParams), coreNodeParams);
            using (BenchmarkLogger.Measure())
            {
                var coreNode = new CoreNode();
                coreNode.DataDir        = coreNodeParams.DataDir;
                coreNode.Network        = coreNodeParams.Network;
                coreNode.MempoolService = coreNodeParams.MempoolService;

                var configPath = Path.Combine(coreNode.DataDir, "bitcoin.conf");
                coreNode.Config = new CoreConfig();
                if (File.Exists(configPath))
                {
                    var configString = await File.ReadAllTextAsync(configPath).ConfigureAwait(false);

                    coreNode.Config.TryAdd(configString);
                }

                var      configDic         = coreNode.Config.ToDictionary();
                string   rpcUser           = null;
                string   rpcPassword       = null;
                EndPoint whitebind         = null;
                string   rpcHost           = null;
                int?     rpcPort           = null;
                string   rpcCookieFilePath = null;
                foreach (var networkPrefixWithDot in NetworkTranslator.GetConfigPrefixesWithDots(coreNode.Network))
                {
                    var rpcc = configDic.TryGet($"{networkPrefixWithDot}rpccookiefile");
                    var ru   = configDic.TryGet($"{networkPrefixWithDot}rpcuser");
                    var rp   = configDic.TryGet($"{networkPrefixWithDot}rpcpassword");
                    var wbs  = configDic.TryGet($"{networkPrefixWithDot}whitebind");
                    var rpst = configDic.TryGet($"{networkPrefixWithDot}rpchost");
                    var rpts = configDic.TryGet($"{networkPrefixWithDot}rpcport");

                    if (rpcc != null)
                    {
                        rpcCookieFilePath = rpcc;
                    }
                    if (ru != null)
                    {
                        rpcUser = ru;
                    }
                    if (rp != null)
                    {
                        rpcPassword = rp;
                    }
                    if (wbs != null && EndPointParser.TryParse(wbs, coreNode.Network.DefaultPort, out EndPoint wb))
                    {
                        whitebind = wb;
                    }
                    if (rpst != null)
                    {
                        rpcHost = rpst;
                    }
                    if (rpts != null && int.TryParse(rpts, out int rpt))
                    {
                        rpcPort = rpt;
                    }
                }

                string authString;
                bool   cookieAuth = rpcCookieFilePath != null;
                if (cookieAuth)
                {
                    authString = $"cookiefile={rpcCookieFilePath}";
                }
                else
                {
                    rpcUser ??= Encoders.Hex.EncodeData(RandomUtils.GetBytes(21));
                    rpcPassword ??= Encoders.Hex.EncodeData(RandomUtils.GetBytes(21));
                    authString = $"{rpcUser}:{rpcPassword}";
                }

                coreNode.P2pEndPoint = whitebind ?? coreNodeParams.P2pEndPointStrategy.EndPoint;
                rpcHost ??= coreNodeParams.RpcEndPointStrategy.EndPoint.GetHostOrDefault();
                rpcPort ??= coreNodeParams.RpcEndPointStrategy.EndPoint.GetPortOrDefault();
                EndPointParser.TryParse($"{rpcHost}:{rpcPort}", coreNode.Network.RPCPort, out EndPoint rpce);
                coreNode.RpcEndPoint = rpce;

                coreNode.RpcClient = new RPCClient($"{authString}", coreNode.RpcEndPoint.ToString(coreNode.Network.DefaultPort), coreNode.Network);

                if (coreNodeParams.TryRestart)
                {
                    await coreNode.TryStopAsync(false).ConfigureAwait(false);
                }

                if (coreNodeParams.TryDeleteDataDir)
                {
                    await IoHelpers.DeleteRecursivelyWithMagicDustAsync(coreNode.DataDir).ConfigureAwait(false);
                }

                IoHelpers.EnsureDirectoryExists(coreNode.DataDir);

                var configPrefix       = NetworkTranslator.GetConfigPrefix(coreNode.Network);
                var desiredConfigLines = new List <string>()
                {
                    $"{configPrefix}.server			= 1",
                    $"{configPrefix}.listen			= 1",
                    $"{configPrefix}.whitebind		= {coreNode.P2pEndPoint.ToString(coreNode.Network.DefaultPort)}",
                    $"{configPrefix}.rpchost		= {coreNode.RpcEndPoint.GetHostOrDefault()}",
                    $"{configPrefix}.rpcport		= {coreNode.RpcEndPoint.GetPortOrDefault()}"
                };

                if (!cookieAuth)
                {
                    desiredConfigLines.Add($"{configPrefix}.rpcuser		= {coreNode.RpcClient.CredentialString.UserPassword.UserName}");
                    desiredConfigLines.Add($"{configPrefix}.rpcpassword	= {coreNode.RpcClient.CredentialString.UserPassword.Password}");
                }

                if (coreNodeParams.TxIndex != null)
                {
                    desiredConfigLines.Add($"{configPrefix}.txindex = {coreNodeParams.TxIndex}");
                }

                if (coreNodeParams.Prune != null)
                {
                    desiredConfigLines.Add($"{configPrefix}.prune = {coreNodeParams.Prune}");
                }

                var sectionComment = $"# The following configuration options were added or modified by Wasabi Wallet.";
                // If the comment is not already present.
                // And there would be new config entries added.
                var throwAwayConfig = new CoreConfig(coreNode.Config);
                throwAwayConfig.AddOrUpdate(string.Join(Environment.NewLine, desiredConfigLines));
                if (!coreNode.Config.ToString().Contains(sectionComment, StringComparison.Ordinal) &&
                    throwAwayConfig.Count != coreNode.Config.Count)
                {
                    desiredConfigLines.Insert(0, sectionComment);
                }

                if (coreNode.Config.AddOrUpdate(string.Join(Environment.NewLine, desiredConfigLines)) ||
                    !File.Exists(configPath))
                {
                    IoHelpers.EnsureContainingDirectoryExists(configPath);
                    await File.WriteAllTextAsync(configPath, coreNode.Config.ToString());
                }

                // If it isn't already running, then we run it.
                if (await coreNode.RpcClient.TestAsync().ConfigureAwait(false) is null)
                {
                    Logger.LogInfo("Bitcoin Core is already running.");
                }
Example #22
0
    private static async Task PublishAsync()
    {
        if (Directory.Exists(BinDistDirectory))
        {
            await IoHelpers.TryDeleteDirectoryAsync(BinDistDirectory).ConfigureAwait(false);

            Console.WriteLine($"Deleted {BinDistDirectory}");
        }

        StartProcessAndWaitForExit("dotnet", DesktopProjectDirectory, arguments: "clean --configuration Release");

        var desktopBinReleaseDirectory = Path.GetFullPath(Path.Combine(DesktopProjectDirectory, "bin", "Release"));
        var libraryBinReleaseDirectory = Path.GetFullPath(Path.Combine(LibraryProjectDirectory, "bin", "Release"));

        if (Directory.Exists(desktopBinReleaseDirectory))
        {
            await IoHelpers.TryDeleteDirectoryAsync(desktopBinReleaseDirectory).ConfigureAwait(false);

            Console.WriteLine($"Deleted {desktopBinReleaseDirectory}");
        }
        if (Directory.Exists(libraryBinReleaseDirectory))
        {
            await IoHelpers.TryDeleteDirectoryAsync(libraryBinReleaseDirectory).ConfigureAwait(false);

            Console.WriteLine($"Deleted {libraryBinReleaseDirectory}");
        }

        var deterministicFileNameTag = IsContinuousDelivery ? $"{DateTimeOffset.UtcNow:ddMMyyyy}{DateTimeOffset.UtcNow.TimeOfDay.TotalSeconds}" : VersionPrefix;
        var deliveryPath             = IsContinuousDelivery ? Path.Combine(BinDistDirectory, "cdelivery") : BinDistDirectory;

        IoHelpers.EnsureDirectoryExists(deliveryPath);
        Console.WriteLine($"Binaries will be delivered here: {deliveryPath}");

        string buildInfoJson = GetBuildInfoData();

        CheckUncommittedGitChanges();

        foreach (string target in Targets)
        {
            string publishedFolder         = Path.Combine(BinDistDirectory, target);
            string currentBinDistDirectory = publishedFolder;

            Console.WriteLine();
            Console.WriteLine($"{nameof(currentBinDistDirectory)}:\t{currentBinDistDirectory}");

            Console.WriteLine();
            if (!Directory.Exists(currentBinDistDirectory))
            {
                Directory.CreateDirectory(currentBinDistDirectory);
                Console.WriteLine($"Created {currentBinDistDirectory}");
            }

            string buildInfoPath = Path.Combine(currentBinDistDirectory, "BUILDINFO.json");
            File.WriteAllText(buildInfoPath, buildInfoJson);

            StartProcessAndWaitForExit("dotnet", DesktopProjectDirectory, arguments: "clean");

            // https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish?tabs=netcore21
            // -c|--configuration {Debug|Release}
            //		Defines the build configuration. The default value is Debug.
            // --force
            //		Forces all dependencies to be resolved even if the last restore was successful. Specifying this flag is the same as deleting the project.assets.json file.
            // -o|--output <OUTPUT_DIRECTORY>
            //		Specifies the path for the output directory.
            //		If not specified, it defaults to ./bin/[configuration]/[framework]/publish/ for a framework-dependent deployment or
            //		./bin/[configuration]/[framework]/[runtime]/publish/ for a self-contained deployment.
            //		If the path is relative, the output directory generated is relative to the project file location, not to the current working directory.
            // --self-contained
            //		Publishes the .NET Core runtime with your application so the runtime does not need to be installed on the target machine.
            //		If a runtime identifier is specified, its default value is true. For more information about the different deployment types, see .NET Core application deployment.
            // -r|--runtime <RUNTIME_IDENTIFIER>
            //		Publishes the application for a given runtime. This is used when creating a self-contained deployment (SCD).
            //		For a list of Runtime Identifiers (RIDs), see the RID catalog. Default is to publish a framework-dependent deployment (FDD).
            // --version-suffix <VERSION_SUFFIX>
            //		Defines the version suffix to replace the asterisk (*) in the version field of the project file.
            // https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-restore?tabs=netcore2x
            // --disable-parallel
            //		Disables restoring multiple projects in parallel.
            // --no-cache
            //		Specifies to not cache packages and HTTP requests.
            // https://github.com/dotnet/docs/issues/7568
            // /p:Version=1.2.3.4
            //		"dotnet publish" supports msbuild command line options like /p:Version=1.2.3.4

            string dotnetProcessArgs = string.Join(
                " ",
                $"publish",
                $"--configuration Release",
                $"--force",
                $"--output \"{currentBinDistDirectory}\"",
                $"--self-contained true",
                $"--runtime \"{target}\"",
                $"--disable-parallel",
                $"--no-cache",
                $"/p:VersionPrefix={VersionPrefix}",
                $"/p:DebugType=none",
                $"/p:DebugSymbols=false",
                $"/p:ErrorReport=none",
                $"/p:DocumentationFile=\"\"",
                $"/p:Deterministic=true",
                $"/p:RestoreLockedMode=true");

            StartProcessAndWaitForExit(
                "dotnet",
                DesktopProjectDirectory,
                arguments: dotnetProcessArgs,
                redirectStandardOutput: true);

            Tools.ClearSha512Tags(currentBinDistDirectory);

            // Remove Tor binaries that are not relevant to the platform.
            var toNotRemove = "";
            if (target.StartsWith("win"))
            {
                toNotRemove = "win";
            }
            else if (target.StartsWith("linux"))
            {
                toNotRemove = "lin";
            }
            else if (target.StartsWith("osx"))
            {
                toNotRemove = "osx";
            }

            // Remove binaries that are not relevant to the platform.
            var binaryFolder = new DirectoryInfo(Path.Combine(currentBinDistDirectory, "Microservices", "Binaries"));

            foreach (var dir in binaryFolder.EnumerateDirectories())
            {
                if (!dir.Name.Contains(toNotRemove, StringComparison.OrdinalIgnoreCase))
                {
                    await IoHelpers.TryDeleteDirectoryAsync(dir.FullName).ConfigureAwait(false);
                }
            }

            // Rename the final exe.
            string oldExecutablePath;
            string newExecutablePath;
            if (target.StartsWith("win"))
            {
                oldExecutablePath = Path.Combine(currentBinDistDirectory, "WalletWasabi.Fluent.Desktop.exe");
                newExecutablePath = Path.Combine(currentBinDistDirectory, $"{ExecutableName}.exe");

                // Delete unused executables.
                File.Delete(Path.Combine(currentBinDistDirectory, "WalletWasabi.Fluent.exe"));
            }
            else             // Linux & OSX
            {
                oldExecutablePath = Path.Combine(currentBinDistDirectory, "WalletWasabi.Fluent.Desktop");
                newExecutablePath = Path.Combine(currentBinDistDirectory, ExecutableName);

                // Delete unused executables.
                File.Delete(Path.Combine(currentBinDistDirectory, "WalletWasabi.Fluent"));
            }
            File.Move(oldExecutablePath, newExecutablePath);

            long installedSizeKb = Tools.DirSize(new DirectoryInfo(publishedFolder)) / 1000;

            if (target.StartsWith("win"))
            {
                // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!!
                if (OnlyBinaries)
                {
                    continue;                     // In Windows build at this moment it does not matter though.
                }

                ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(deliveryPath, $"Wasabi-{deterministicFileNameTag}-{target}.zip"));

                if (IsContinuousDelivery)
                {
                    continue;
                }
            }
            else if (target.StartsWith("osx"))
            {
                // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!!
                if (OnlyBinaries)
                {
                    continue;
                }

                ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(deliveryPath, $"Wasabi-{deterministicFileNameTag}-{target}.zip"));

                if (IsContinuousDelivery)
                {
                    continue;
                }

                ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(BinDistDirectory, $"Wasabi-osx-{VersionPrefix}.zip"));

                await IoHelpers.TryDeleteDirectoryAsync(currentBinDistDirectory).ConfigureAwait(false);

                Console.WriteLine($"Deleted {currentBinDistDirectory}");
            }
            else if (target.StartsWith("linux"))
            {
                // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!!
                if (OnlyBinaries)
                {
                    continue;
                }

                ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(deliveryPath, $"Wasabi-{deterministicFileNameTag}-{target}.zip"));

                if (IsContinuousDelivery)
                {
                    continue;
                }

                Console.WriteLine("Create Linux .tar.gz");
                if (!Directory.Exists(publishedFolder))
                {
                    throw new Exception($"{publishedFolder} does not exist.");
                }
                var newFolderName = $"Wasabi-{VersionPrefix}";
                var newFolderPath = Path.Combine(BinDistDirectory, newFolderName);
                Directory.Move(publishedFolder, newFolderPath);
                publishedFolder = newFolderPath;

                var driveLetterUpper = BinDistDirectory[0];
                var driveLetterLower = char.ToLower(driveLetterUpper);

                var linuxPath = $"/mnt/{driveLetterLower}/{Tools.LinuxPath(BinDistDirectory[3..])}";
Example #23
0
    public static async Task <CoreNode> CreateAsync(CoreNodeParams coreNodeParams, CancellationToken cancel)
    {
        Guard.NotNull(nameof(coreNodeParams), coreNodeParams);
        using (BenchmarkLogger.Measure())
        {
            var coreNode = new CoreNode
            {
                DataDir        = coreNodeParams.DataDir,
                Network        = coreNodeParams.Network,
                MempoolService = coreNodeParams.MempoolService
            };

            var configPath = Path.Combine(coreNode.DataDir, "bitcoin.conf");
            coreNode.Config = new CoreConfig();
            if (File.Exists(configPath))
            {
                var configString = await File.ReadAllTextAsync(configPath, cancel).ConfigureAwait(false);

                coreNode.Config.AddOrUpdate(configString);                 // Bitcoin Core considers the last entry to be valid.
            }
            cancel.ThrowIfCancellationRequested();

            var configTranslator = new CoreConfigTranslator(coreNode.Config, coreNode.Network);

            string?   rpcUser           = configTranslator.TryGetRpcUser();
            string?   rpcPassword       = configTranslator.TryGetRpcPassword();
            string?   rpcCookieFilePath = configTranslator.TryGetRpcCookieFile();
            string?   rpcHost           = configTranslator.TryGetRpcBind();
            int?      rpcPort           = configTranslator.TryGetRpcPort();
            WhiteBind?whiteBind         = configTranslator.TryGetWhiteBind();

            string authString;
            bool   cookieAuth = rpcCookieFilePath is not null;
            if (cookieAuth)
            {
                authString = $"cookiefile={rpcCookieFilePath}";
            }
            else
            {
                rpcUser ??= Encoders.Hex.EncodeData(RandomUtils.GetBytes(21));
                rpcPassword ??= Encoders.Hex.EncodeData(RandomUtils.GetBytes(21));
                authString = $"{rpcUser}:{rpcPassword}";
            }

            coreNode.P2pEndPoint = whiteBind?.EndPoint ?? coreNodeParams.P2pEndPointStrategy.EndPoint;

            if (rpcHost is null)
            {
                coreNodeParams.RpcEndPointStrategy.EndPoint.TryGetHost(out rpcHost);
            }

            if (rpcPort is null)
            {
                coreNodeParams.RpcEndPointStrategy.EndPoint.TryGetPort(out rpcPort);
            }

            if (!EndPointParser.TryParse($"{rpcHost}:{rpcPort}", coreNode.Network.RPCPort, out EndPoint? rpce))
            {
                throw new InvalidOperationException($"Failed to get RPC endpoint on {rpcHost}:{rpcPort}.");
            }
            coreNode.RpcEndPoint = rpce;

            var rpcClient = new RPCClient(
                $"{authString}",
                coreNode.RpcEndPoint.ToString(coreNode.Network.DefaultPort),
                coreNode.Network);
            coreNode.RpcClient = new CachedRpcClient(rpcClient, coreNodeParams.Cache);

            if (coreNodeParams.TryRestart)
            {
                await coreNode.TryStopAsync(false).ConfigureAwait(false);
            }
            cancel.ThrowIfCancellationRequested();

            if (coreNodeParams.TryDeleteDataDir)
            {
                await IoHelpers.TryDeleteDirectoryAsync(coreNode.DataDir).ConfigureAwait(false);
            }
            cancel.ThrowIfCancellationRequested();

            IoHelpers.EnsureDirectoryExists(coreNode.DataDir);

            var configPrefix             = NetworkTranslator.GetConfigPrefix(coreNode.Network);
            var whiteBindPermissionsPart = !string.IsNullOrWhiteSpace(whiteBind?.Permissions) ? $"{whiteBind?.Permissions}@" : "";

            if (!coreNode.RpcEndPoint.TryGetHost(out string?rpcBindParameter) || !coreNode.RpcEndPoint.TryGetPort(out int?rpcPortParameter))
            {
                throw new ArgumentException("Endpoint type is not supported.", nameof(coreNode.RpcEndPoint));
            }

            var desiredConfigLines = new List <string>()
            {
                $"{configPrefix}.server			= 1",
                $"{configPrefix}.listen			= 1",
                $"{configPrefix}.daemon			= 0",                                 // https://github.com/zkSNACKs/WalletWasabi/issues/3588
                $"{configPrefix}.whitebind		= {whiteBindPermissionsPart}{coreNode.P2pEndPoint.ToString(coreNode.Network.DefaultPort)}",
                $"{configPrefix}.rpcbind		= {rpcBindParameter}",
                $"{configPrefix}.rpcallowip		= {IPAddress.Loopback}",
                $"{configPrefix}.rpcport		= {rpcPortParameter}"
            };

            if (!cookieAuth)
            {
                desiredConfigLines.Add($"{configPrefix}.rpcuser		= {coreNode.RpcClient.CredentialString.UserPassword.UserName}");
                desiredConfigLines.Add($"{configPrefix}.rpcpassword	= {coreNode.RpcClient.CredentialString.UserPassword.Password}");
            }

            if (coreNodeParams.TxIndex is { })
Example #24
0
    private static async Task PublishAsync()
    {
        if (Directory.Exists(BinDistDirectory))
        {
            await IoHelpers.TryDeleteDirectoryAsync(BinDistDirectory).ConfigureAwait(false);

            Console.WriteLine($"# Deleted {BinDistDirectory}");
        }

        Console.WriteLine($"# Run dotnet restore");
        StartProcessAndWaitForExit("dotnet", DesktopProjectDirectory, arguments: "restore --locked-mode");

        Console.WriteLine($"# Run dotnet clean");
        StartProcessAndWaitForExit("dotnet", DesktopProjectDirectory, arguments: "clean --configuration Release");

        string desktopBinReleaseDirectory = Path.GetFullPath(Path.Combine(DesktopProjectDirectory, "bin", "Release"));
        string libraryBinReleaseDirectory = Path.GetFullPath(Path.Combine(LibraryProjectDirectory, "bin", "Release"));

        if (Directory.Exists(desktopBinReleaseDirectory))
        {
            await IoHelpers.TryDeleteDirectoryAsync(desktopBinReleaseDirectory).ConfigureAwait(false);

            Console.WriteLine($"#Deleted {desktopBinReleaseDirectory}");
        }

        if (Directory.Exists(libraryBinReleaseDirectory))
        {
            await IoHelpers.TryDeleteDirectoryAsync(libraryBinReleaseDirectory).ConfigureAwait(false);

            Console.WriteLine($"# Deleted {libraryBinReleaseDirectory}");
        }

        var deterministicFileNameTag = IsContinuousDelivery ? $"{DateTimeOffset.UtcNow:ddMMyyyy}{DateTimeOffset.UtcNow.TimeOfDay.TotalSeconds}" : VersionPrefix;
        var deliveryPath             = IsContinuousDelivery ? Path.Combine(BinDistDirectory, "cdelivery") : BinDistDirectory;

        IoHelpers.EnsureDirectoryExists(deliveryPath);
        Console.WriteLine($"# Binaries will be delivered here: {deliveryPath}");

        string buildInfoJson = GetBuildInfoData();

        CheckUncommittedGitChanges();

        foreach (string target in Targets)
        {
            string publishedFolder         = Path.Combine(BinDistDirectory, target);
            string currentBinDistDirectory = publishedFolder;

            Console.WriteLine();
            Console.WriteLine($"{nameof(currentBinDistDirectory)}:\t{currentBinDistDirectory}");

            Console.WriteLine();
            if (!Directory.Exists(currentBinDistDirectory))
            {
                Directory.CreateDirectory(currentBinDistDirectory);
                Console.WriteLine($"# Created {currentBinDistDirectory}");
            }

            string buildInfoPath = Path.Combine(currentBinDistDirectory, "BUILDINFO.json");
            File.WriteAllText(buildInfoPath, buildInfoJson);

            StartProcessAndWaitForExit("dotnet", DesktopProjectDirectory, arguments: "clean");

            // See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish for details.
            string dotnetProcessArgs = string.Join(
                " ",
                $"publish",
                $"--configuration Release",
                $"--force",
                $"--output \"{currentBinDistDirectory}\"",
                $"--self-contained true",
                $"--runtime \"{target}\"",
                $"--disable-parallel",
                $"--no-cache",
                $"--no-restore",
                $"/p:VersionPrefix={VersionPrefix}",
                $"/p:DebugType=none",
                $"/p:DebugSymbols=false",
                $"/p:ErrorReport=none",
                $"/p:DocumentationFile=\"\"",
                $"/p:Deterministic=true");

            StartProcessAndWaitForExit(
                "dotnet",
                DesktopProjectDirectory,
                arguments: dotnetProcessArgs,
                redirectStandardOutput: true);

            Tools.ClearSha512Tags(currentBinDistDirectory);

            // Remove Tor binaries that are not relevant to the platform.
            var toNotRemove = "";
            if (target.StartsWith("win"))
            {
                toNotRemove = "win";
            }
            else if (target.StartsWith("linux"))
            {
                toNotRemove = "lin";
            }
            else if (target.StartsWith("osx"))
            {
                toNotRemove = "osx";
            }

            // Remove binaries that are not relevant to the platform.
            var binaryFolder = new DirectoryInfo(Path.Combine(currentBinDistDirectory, "Microservices", "Binaries"));

            foreach (var dir in binaryFolder.EnumerateDirectories())
            {
                if (!dir.Name.Contains(toNotRemove, StringComparison.OrdinalIgnoreCase))
                {
                    await IoHelpers.TryDeleteDirectoryAsync(dir.FullName).ConfigureAwait(false);
                }
            }

            // Rename the final exe.
            string oldExecutablePath;
            string newExecutablePath;
            if (target.StartsWith("win"))
            {
                oldExecutablePath = Path.Combine(currentBinDistDirectory, "WalletWasabi.Fluent.Desktop.exe");
                newExecutablePath = Path.Combine(currentBinDistDirectory, $"{ExecutableName}.exe");

                // Delete unused executables.
                File.Delete(Path.Combine(currentBinDistDirectory, "WalletWasabi.Fluent.exe"));
            }
            else             // Linux & OSX
            {
                oldExecutablePath = Path.Combine(currentBinDistDirectory, "WalletWasabi.Fluent.Desktop");
                newExecutablePath = Path.Combine(currentBinDistDirectory, ExecutableName);

                // Delete unused executables.
                File.Delete(Path.Combine(currentBinDistDirectory, "WalletWasabi.Fluent"));
            }
            File.Move(oldExecutablePath, newExecutablePath);

            long installedSizeKb = Tools.DirSize(new DirectoryInfo(publishedFolder)) / 1000;

            if (target.StartsWith("win"))
            {
                // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!!
                if (OnlyBinaries)
                {
                    continue;                     // In Windows build at this moment it does not matter though.
                }

                ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(deliveryPath, $"Wasabi-{deterministicFileNameTag}-{GetPackageTargetPostfix(target)}.zip"));

                if (IsContinuousDelivery)
                {
                    continue;
                }
            }
            else if (target.StartsWith("osx"))
            {
                // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!!
                if (OnlyBinaries)
                {
                    continue;
                }

                ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(deliveryPath, $"Wasabi-{deterministicFileNameTag}-{GetPackageTargetPostfix(target)}.zip"));

                if (IsContinuousDelivery)
                {
                    continue;
                }

                // Only add postfix to the final package if arm64, otherwise nothing.
                var postfix = target.Contains("arm64") ? "-arm64" : "";

                // After notarization this will be the filename of the dmg file.
                var zipFileName = $"WasabiToNotarize-{deterministicFileNameTag}{postfix}.zip";
                var zipFilePath = Path.Combine(BinDistDirectory, zipFileName);

                ZipFile.CreateFromDirectory(currentBinDistDirectory, zipFilePath);

                await IoHelpers.TryDeleteDirectoryAsync(currentBinDistDirectory).ConfigureAwait(false);

                Console.WriteLine($"# Deleted {currentBinDistDirectory}");

                try
                {
                    var drive          = Tools.GetSingleUsbDrive();
                    var targetFilePath = Path.Combine(drive, zipFileName);

                    Console.WriteLine($"# Trying to move unsigned zip file to removable ('{targetFilePath}').");
                    File.Move(zipFilePath, targetFilePath, overwrite: true);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"# There was an error during copying the file to removable: '{ex.Message}'");
                }
            }
            else if (target.StartsWith("linux"))
            {
                // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!!
                if (OnlyBinaries)
                {
                    continue;
                }

                ZipFile.CreateFromDirectory(currentBinDistDirectory, Path.Combine(deliveryPath, $"Wasabi-{deterministicFileNameTag}-{GetPackageTargetPostfix(target)}.zip"));

                if (IsContinuousDelivery)
                {
                    continue;
                }

                Console.WriteLine("# Create Linux .tar.gz");
                if (!Directory.Exists(publishedFolder))
                {
                    throw new Exception($"{publishedFolder} does not exist.");
                }
                var newFolderName = $"Wasabi-{VersionPrefix}";
                var newFolderPath = Path.Combine(BinDistDirectory, newFolderName);
                Directory.Move(publishedFolder, newFolderPath);
                publishedFolder = newFolderPath;

                var driveLetterUpper = BinDistDirectory[0];
                var driveLetterLower = char.ToLower(driveLetterUpper);

                var linuxPath = $"/mnt/{driveLetterLower}/{Tools.LinuxPath(BinDistDirectory[3..])}";
        public static async Task <CoreNode> CreateAsync(CoreNodeParams coreNodeParams, CancellationToken cancel)
        {
            Guard.NotNull(nameof(coreNodeParams), coreNodeParams);
            using (BenchmarkLogger.Measure())
            {
                var coreNode = new CoreNode();
                coreNode.HostedServices = coreNodeParams.HostedServices;
                coreNode.DataDir        = coreNodeParams.DataDir;
                coreNode.Network        = coreNodeParams.Network;
                coreNode.MempoolService = coreNodeParams.MempoolService;

                var configPath = Path.Combine(coreNode.DataDir, "bitcoin.conf");
                coreNode.Config = new CoreConfig();
                if (File.Exists(configPath))
                {
                    var configString = await File.ReadAllTextAsync(configPath).ConfigureAwait(false);

                    coreNode.Config.AddOrUpdate(configString);                     // Bitcoin Core considers the last entry to be valid.
                }
                cancel.ThrowIfCancellationRequested();

                var configTranslator = new CoreConfigTranslator(coreNode.Config, coreNode.Network);

                string    rpcUser           = configTranslator.TryGetRpcUser();
                string    rpcPassword       = configTranslator.TryGetRpcPassword();
                string    rpcCookieFilePath = configTranslator.TryGetRpcCookieFile();
                string    rpcHost           = configTranslator.TryGetRpcBind();
                int?      rpcPort           = configTranslator.TryGetRpcPort();
                WhiteBind whiteBind         = configTranslator.TryGetWhiteBind();

                string authString;
                bool   cookieAuth = rpcCookieFilePath is { };
                if (cookieAuth)
                {
                    authString = $"cookiefile={rpcCookieFilePath}";
                }
                else
                {
                    rpcUser ??= Encoders.Hex.EncodeData(RandomUtils.GetBytes(21));
                    rpcPassword ??= Encoders.Hex.EncodeData(RandomUtils.GetBytes(21));
                    authString = $"{rpcUser}:{rpcPassword}";
                }

                coreNode.P2pEndPoint = whiteBind?.EndPoint ?? coreNodeParams.P2pEndPointStrategy.EndPoint;
                rpcHost ??= coreNodeParams.RpcEndPointStrategy.EndPoint.GetHostOrDefault();
                rpcPort ??= coreNodeParams.RpcEndPointStrategy.EndPoint.GetPortOrDefault();
                EndPointParser.TryParse($"{rpcHost}:{rpcPort}", coreNode.Network.RPCPort, out EndPoint rpce);
                coreNode.RpcEndPoint = rpce;

                var rpcClient = new RPCClient(
                    $"{authString}",
                    coreNode.RpcEndPoint.ToString(coreNode.Network.DefaultPort),
                    coreNode.Network);
                coreNode.RpcClient = new CachedRpcClient(rpcClient, coreNodeParams.Cache);

                if (coreNodeParams.TryRestart)
                {
                    await coreNode.TryStopAsync(false).ConfigureAwait(false);
                }
                cancel.ThrowIfCancellationRequested();

                if (coreNodeParams.TryDeleteDataDir)
                {
                    await IoHelpers.DeleteRecursivelyWithMagicDustAsync(coreNode.DataDir).ConfigureAwait(false);
                }
                cancel.ThrowIfCancellationRequested();

                IoHelpers.EnsureDirectoryExists(coreNode.DataDir);

                var configPrefix             = NetworkTranslator.GetConfigPrefix(coreNode.Network);
                var whiteBindPermissionsPart = !string.IsNullOrWhiteSpace(whiteBind?.Permissions) ? $"{whiteBind?.Permissions}@" : "";
                var desiredConfigLines       = new List <string>()
                {
                    $"{configPrefix}.server			= 1",
                    $"{configPrefix}.listen			= 1",
                    $"{configPrefix}.whitebind		= {whiteBindPermissionsPart}{coreNode.P2pEndPoint.ToString(coreNode.Network.DefaultPort)}",
                    $"{configPrefix}.rpcbind		= {coreNode.RpcEndPoint.GetHostOrDefault()}",
                    $"{configPrefix}.rpcallowip		= {IPAddress.Loopback}",
                    $"{configPrefix}.rpcport		= {coreNode.RpcEndPoint.GetPortOrDefault()}"
                };

                if (!cookieAuth)
                {
                    desiredConfigLines.Add($"{configPrefix}.rpcuser		= {coreNode.RpcClient.CredentialString.UserPassword.UserName}");
                    desiredConfigLines.Add($"{configPrefix}.rpcpassword	= {coreNode.RpcClient.CredentialString.UserPassword.Password}");
                }

                if (coreNodeParams.TxIndex is { })
Example #26
0
    public static void Sign(ArgsProcessor argsProcessor)
    {
        if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
        {
            throw new NotSupportedException("This signing method is only valid on macOS!");
        }

        Console.WriteLine("Phase: finding the zip file on desktop which contains the compiled binaries from Windows.");

        string desktopPath          = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
        string removableDriveFolder = Tools.GetSingleUsbDrive();

        var srcZipFileNamePattern = "WasabiToNotarize-*";
        var files = Directory.GetFiles(removableDriveFolder, srcZipFileNamePattern);

        if (files.Length != 2)
        {
            throw new InvalidDataException($"{srcZipFileNamePattern} file missing or there are more! There must be exactly two!");
        }

        var(appleId, password) = argsProcessor.GetAppleIdAndPassword();

        while (string.IsNullOrWhiteSpace(appleId))
        {
            Console.WriteLine("Enter appleId (email):");
            appleId = Console.ReadLine();
        }

        while (string.IsNullOrWhiteSpace(password))
        {
            Console.WriteLine("Enter password:"******"WasabiToNotarize-2.0.0.0-arm64.zip or WasabiToNotarize-2.0.0.0.zip ".
            var workingDir          = Path.Combine(desktopPath, "wasabiTemp");
            var dmgPath             = Path.Combine(workingDir, "dmg");
            var unzippedPath        = Path.Combine(workingDir, "unzipped");
            var appName             = $"{Constants.AppName}.app";
            var appPath             = Path.Combine(dmgPath, appName);
            var appContentsPath     = Path.Combine(appPath, "Contents");
            var appMacOsPath        = Path.Combine(appContentsPath, "MacOS");
            var appResPath          = Path.Combine(appContentsPath, "Resources");
            var appFrameworksPath   = Path.Combine(appContentsPath, "Frameworks");
            var infoFilePath        = Path.Combine(appContentsPath, "Info.plist");
            var dmgFileName         = zipFile.Replace("WasabiToNotarize", "Wasabi").Replace("zip", "dmg");
            var dmgFilePath         = Path.Combine(workingDir, dmgFileName);
            var dmgUnzippedFilePath = Path.Combine(workingDir, $"Wasabi.tmp.dmg");
            var appNotarizeFilePath = Path.Combine(workingDir, $"Wasabi-{versionPrefix}.zip");
            var contentsPath        = Path.GetFullPath(Path.Combine(Program.PackagerProjectDirectory.Replace("\\", "//"), "Content", "Osx"));
            var entitlementsPath    = Path.Combine(contentsPath, "entitlements.plist");
            var dmgContentsDir      = Path.Combine(contentsPath, "Dmg");
            var desktopDmgFilePath  = Path.Combine(desktopPath, dmgFileName);

            var signArguments = $"--sign \"L233B2JQ68\" --verbose --force --options runtime --timestamp";

            Console.WriteLine("Phase: creating the working directory.");

            if (Directory.Exists(workingDir))
            {
                DeleteWithChmod(workingDir);
            }

            if (File.Exists(desktopDmgFilePath))
            {
                File.Delete(desktopDmgFilePath);
            }

            Console.WriteLine("Phase: creating the app.");

            IoHelpers.EnsureDirectoryExists(appResPath);
            IoHelpers.EnsureDirectoryExists(appMacOsPath);

            ZipFile.ExtractToDirectory(zipPath, appMacOsPath);             // Copy the binaries.

            IoHelpers.CopyFilesRecursively(new DirectoryInfo(Path.Combine(contentsPath, "App")), new DirectoryInfo(appPath));

            Console.WriteLine("Update the plist file with current information for example with version.");

            var    lines            = File.ReadAllLines(infoFilePath);
            string?bundleIdentifier = null;

            for (int i = 0; i < lines.Length; i++)
            {
                string line = lines[i];
                if (!line.TrimStart().StartsWith("<key>", StringComparison.InvariantCultureIgnoreCase))
                {
                    continue;
                }

                if (line.Contains("CFBundleShortVersionString", StringComparison.InvariantCulture) ||
                    line.Contains("CFBundleVersion", StringComparison.InvariantCulture))
                {
                    lines[i + 1] = lines[i + 1].Replace("?", $"{Version.Parse(versionPrefix).ToString(3)}");                     // Apple allow only 3 version tags in plist.
                }
                else if (line.Contains("CFBundleIdentifier", StringComparison.InvariantCulture))
                {
                    bundleIdentifier = lines[i + 1].Trim().Replace("<string>", "").Replace("</string>", "");
                }
            }
            if (string.IsNullOrWhiteSpace(bundleIdentifier))
            {
                throw new InvalidDataException("Bundle identifier not found in plist file.");
            }

            File.Delete(infoFilePath);

            File.WriteAllLines(infoFilePath, lines);

            using (var process = Process.Start(new ProcessStartInfo
            {
                FileName = "chmod",
                Arguments = $"-R u+rwX,go+rX,go-w \"{appPath}\"",
                WorkingDirectory = workingDir
            }))
            {
                WaitProcessToFinish(process, "chmod");
            }

            var filesToCheck = new[] { entitlementsPath };

            foreach (var file in filesToCheck)
            {
                if (!File.Exists(file))
                {
                    throw new FileNotFoundException($"File missing: {file}");
                }
            }

            Console.WriteLine("Signing the files in app.");

            IoHelpers.EnsureDirectoryExists(appResPath);
            IoHelpers.EnsureDirectoryExists(appMacOsPath);

            var executables = GetExecutables(appPath);

            // The main executable needs to be signed last.
            var filesToSignInOrder = Directory.GetFiles(appPath, "*.*", SearchOption.AllDirectories)
                                     .OrderBy(file => executables.Contains(file))
                                     .OrderBy(file => new FileInfo(file).Name == "wassabee")
                                     .ToArray();

            foreach (var file in executables)
            {
                using var process = Process.Start(new ProcessStartInfo
                {
                    FileName         = "chmod",
                    Arguments        = $"u+x \"{file}\"",
                    WorkingDirectory = workingDir
                });
                WaitProcessToFinish(process, "chmod");
            }

            SignDirectory(filesToSignInOrder, workingDir, signArguments, entitlementsPath);

            Console.WriteLine("Phase: verifying the signature.");

            Verify(appPath);

            Console.WriteLine("Phase: notarize the app.");

            // Source: https://blog.frostwire.com/2019/08/27/apple-notarization-the-signature-of-the-binary-is-invalid-one-other-reason-not-explained-in-apple-developer-documentation/
            using (var process = Process.Start(new ProcessStartInfo
            {
                FileName = "ditto",
                Arguments = $"-c -k --keepParent \"{appPath}\" \"{appNotarizeFilePath}\"",
                WorkingDirectory = workingDir
            }))
            {
                WaitProcessToFinish(process, "ditto");
            }

            Notarize(appleId, password, appNotarizeFilePath, bundleIdentifier);
            Staple(appPath);

            using (var process = Process.Start(new ProcessStartInfo
            {
                FileName = "spctl",
                Arguments = $"-a -t exec -vv \"{appPath}\"",
                WorkingDirectory = workingDir,
                RedirectStandardError = true
            }))
            {
                var    nonNullProcess = WaitProcessToFinish(process, "spctl");
                string result         = nonNullProcess.StandardError.ReadToEnd();
                if (!result.Contains(": accepted"))
                {
                    throw new InvalidOperationException(result);
                }
            }

            Console.WriteLine("Phase: creating the dmg.");

            if (File.Exists(dmgFilePath))
            {
                File.Delete(dmgFilePath);
            }

            Console.WriteLine("Phase: creating dmg.");

            IoHelpers.CopyFilesRecursively(new DirectoryInfo(dmgContentsDir), new DirectoryInfo(dmgPath));

            File.Copy(Path.Combine(contentsPath, "WasabiLogo.icns"), Path.Combine(dmgPath, ".VolumeIcon.icns"), true);

            var temp = Path.Combine(dmgPath, ".DS_Store.dat");
            File.Move(temp, Path.Combine(dmgPath, ".DS_Store"), true);

            using (var process = Process.Start(new ProcessStartInfo
            {
                FileName = "ln",
                Arguments = "-s /Applications",
                WorkingDirectory = dmgPath
            }))
            {
                WaitProcessToFinish(process, "ln");
            }

            var hdutilCreateArgs = string.Join(
                " ",
                new string[]
            {
                "create",
                $"\"{dmgUnzippedFilePath}\"",
                "-ov",
                $"-volname \"Wasabi Wallet\"",
                "-fs HFS+",
                $"-srcfolder \"{dmgPath}\""
            });

            using (var process = Process.Start(new ProcessStartInfo
            {
                FileName = "hdiutil",
                Arguments = hdutilCreateArgs,
                WorkingDirectory = dmgPath
            }))
            {
                WaitProcessToFinish(process, "hdiutil");
            }

            var hdutilConvertArgs = string.Join(
                " ",
                new string[]
            {
                "convert",
                $"\"{dmgUnzippedFilePath}\"",
                "-format UDZO",
                $"-o \"{dmgFilePath}\""
            });

            using (var process = Process.Start(new ProcessStartInfo
            {
                FileName = "hdiutil",
                Arguments = hdutilConvertArgs,
                WorkingDirectory = dmgPath
            }))
            {
                WaitProcessToFinish(process, "hdiutil");
            }

            Console.WriteLine("Phase: signing the dmg file.");

            SignFile($"{signArguments} --entitlements \"{entitlementsPath}\" \"{dmgFilePath}\"", dmgPath);

            Console.WriteLine("Phase: verifying the signature.");

            Verify(dmgFilePath);

            Console.WriteLine("Phase: notarize dmg");
            Notarize(appleId, password, dmgFilePath, bundleIdentifier);

            Console.WriteLine("Phase: staple dmp");
            Staple(dmgFilePath);

            using (var process = Process.Start(new ProcessStartInfo
            {
                FileName = "spctl",
                Arguments = $"-a -t open --context context:primary-signature -v \"{dmgFilePath}\"",
                WorkingDirectory = workingDir,
                RedirectStandardError = true
            }))
            {
                var    nonNullProcess = WaitProcessToFinish(process, "spctl");
                string result         = nonNullProcess.StandardError.ReadToEnd();
                if (!result.Contains(": accepted"))
                {
                    throw new InvalidOperationException(result);
                }
            }

            File.Move(dmgFilePath, desktopDmgFilePath);
            DeleteWithChmod(workingDir);

            Console.WriteLine("Phase: finish.");

            var toRemovableFilePath = Path.Combine(removableDriveFolder, Path.GetFileName(desktopDmgFilePath));
            File.Move(desktopDmgFilePath, toRemovableFilePath, true);

            if (File.Exists(zipPath))
            {
                File.Delete(zipPath);
            }
        }
    }
Example #27
0
        private static void Publish()
        {
            if (Directory.Exists(BinDistDirectory))
            {
                IoHelpers.TryDeleteDirectoryAsync(BinDistDirectory).GetAwaiter().GetResult();
                Console.WriteLine($"Deleted {BinDistDirectory}");
            }

            using (var process = Process.Start(new ProcessStartInfo
            {
                FileName = "cmd",
                RedirectStandardInput = true,
                WorkingDirectory = GuiProjectDirectory
            }))
            {
                process.StandardInput.WriteLine("dotnet clean --configuration Release && exit");
                process.WaitForExit();
            }

            var guiBinReleaseDirectory     = Path.GetFullPath(Path.Combine(GuiProjectDirectory, "bin", "Release"));
            var libraryBinReleaseDirectory = Path.GetFullPath(Path.Combine(LibraryProjectDirectory, "bin", "Release"));

            if (Directory.Exists(guiBinReleaseDirectory))
            {
                IoHelpers.TryDeleteDirectoryAsync(guiBinReleaseDirectory).GetAwaiter().GetResult();
                Console.WriteLine($"Deleted {guiBinReleaseDirectory}");
            }
            if (Directory.Exists(libraryBinReleaseDirectory))
            {
                IoHelpers.TryDeleteDirectoryAsync(libraryBinReleaseDirectory).GetAwaiter().GetResult();
                Console.WriteLine($"Deleted {libraryBinReleaseDirectory}");
            }

            var deterministicFileNameTag = IsContinuousDelivery ? $"{DateTimeOffset.UtcNow:ddMMyyyy}{DateTimeOffset.UtcNow.TimeOfDay.TotalSeconds}" : VersionPrefix;
            var deliveryPath             = IsContinuousDelivery ? Path.Combine(BinDistDirectory, "cdelivery") : BinDistDirectory;

            IoHelpers.EnsureDirectoryExists(deliveryPath);
            Console.WriteLine($"Binaries will be delivered here: {deliveryPath}");

            foreach (string target in Targets)
            {
                string publishedFolder         = Path.Combine(BinDistDirectory, target);
                string currentBinDistDirectory = publishedFolder;

                Console.WriteLine();
                Console.WriteLine($"{nameof(currentBinDistDirectory)}:\t{currentBinDistDirectory}");

                Console.WriteLine();
                if (!Directory.Exists(currentBinDistDirectory))
                {
                    Directory.CreateDirectory(currentBinDistDirectory);
                    Console.WriteLine($"Created {currentBinDistDirectory}");
                }

                using (var process = Process.Start(new ProcessStartInfo
                {
                    FileName = "dotnet",
                    Arguments = $"clean",
                    WorkingDirectory = GuiProjectDirectory
                }))
                {
                    process.WaitForExit();
                }

                // https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish?tabs=netcore21
                // -c|--configuration {Debug|Release}
                //		Defines the build configuration. The default value is Debug.
                // --force
                //		Forces all dependencies to be resolved even if the last restore was successful. Specifying this flag is the same as deleting the project.assets.json file.
                // -o|--output <OUTPUT_DIRECTORY>
                //		Specifies the path for the output directory.
                //		If not specified, it defaults to ./bin/[configuration]/[framework]/publish/ for a framework-dependent deployment or
                //		./bin/[configuration]/[framework]/[runtime]/publish/ for a self-contained deployment.
                //		If the path is relative, the output directory generated is relative to the project file location, not to the current working directory.
                // --self-contained
                //		Publishes the .NET Core runtime with your application so the runtime does not need to be installed on the target machine.
                //		If a runtime identifier is specified, its default value is true. For more information about the different deployment types, see .NET Core application deployment.
                // -r|--runtime <RUNTIME_IDENTIFIER>
                //		Publishes the application for a given runtime. This is used when creating a self-contained deployment (SCD).
                //		For a list of Runtime Identifiers (RIDs), see the RID catalog. Default is to publish a framework-dependent deployment (FDD).
                // --version-suffix <VERSION_SUFFIX>
                //		Defines the version suffix to replace the asterisk (*) in the version field of the project file.
                // https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-restore?tabs=netcore2x
                // --disable-parallel
                //		Disables restoring multiple projects in parallel.
                // --no-cache
                //		Specifies to not cache packages and HTTP requests.
                // https://github.com/dotnet/docs/issues/7568
                // /p:Version=1.2.3.4
                //		"dotnet publish" supports msbuild command line options like /p:Version=1.2.3.4
                using (var process = Process.Start(new ProcessStartInfo
                {
                    FileName = "dotnet",
                    Arguments = string.Join(" ",
                                            $"publish",
                                            $"--configuration Release",
                                            $"--force",
                                            $"--output \"{currentBinDistDirectory}\"",
                                            $"--self-contained true",
                                            $"--runtime \"{target}\"",
                                            $"--disable-parallel",
                                            $"--no-cache",
                                            $"/p:VersionPrefix={VersionPrefix}",
                                            $"/p:DebugType=none",
                                            $"/p:DebugSymbols=false",
                                            $"/p:ErrorReport=none",
                                            $"/p:DocumentationFile=\"\"",
                                            $"/p:Deterministic=true",
                                            $"/p:RestoreLockedMode=true"),
                    WorkingDirectory = GuiProjectDirectory,
                    RedirectStandardOutput = true
                }))
                {
                    string error = process.StandardOutput.ReadToEnd();
                    process.WaitForExit();
                    if (process.ExitCode != 0)
                    {
                        throw new InvalidOperationException($"dotnet publish returned with error code {process.ExitCode}. Error message was: {error ?? "none"}");
                    }
                }

                Tools.ClearSha512Tags(currentBinDistDirectory);

                // Remove Tor binaries that are not relevant to the platform.
                var torFolder   = new DirectoryInfo(Path.Combine(currentBinDistDirectory, "TorDaemons"));
                var toNotRemove = "";
                if (target.StartsWith("win"))
                {
                    toNotRemove = "win";
                }
                else if (target.StartsWith("linux"))
                {
                    toNotRemove = "lin";
                }
                else if (target.StartsWith("osx"))
                {
                    toNotRemove = "osx";
                }

                foreach (var file in torFolder.EnumerateFiles())
                {
                    if (!file.Name.Contains("data", StringComparison.OrdinalIgnoreCase) && !file.Name.Contains(toNotRemove, StringComparison.OrdinalIgnoreCase))
                    {
                        File.Delete(file.FullName);
                    }
                }

                // Remove binaries that are not relevant to the platform.
                var binaryFolder = new DirectoryInfo(Path.Combine(currentBinDistDirectory, "Microservices", "Binaries"));

                foreach (var dir in binaryFolder.EnumerateDirectories())
                {
                    if (!dir.Name.Contains(toNotRemove, StringComparison.OrdinalIgnoreCase))
                    {
                        IoHelpers.TryDeleteDirectoryAsync(dir.FullName).GetAwaiter().GetResult();
                    }
                }

                // Rename the final exe.
                string oldExecutablePath;
                string newExecutablePath;
                if (target.StartsWith("win"))
                {
                    oldExecutablePath = Path.Combine(currentBinDistDirectory, "WalletWasabi.Gui.exe");
                    newExecutablePath = Path.Combine(currentBinDistDirectory, $"{ExecutableName}.exe");
                }
                else                 // Linux & OSX
                {
                    oldExecutablePath = Path.Combine(currentBinDistDirectory, "WalletWasabi.Gui");
                    newExecutablePath = Path.Combine(currentBinDistDirectory, ExecutableName);
                }
                File.Move(oldExecutablePath, newExecutablePath);

                long installedSizeKb = Tools.DirSize(new DirectoryInfo(publishedFolder)) / 1000;

                if (target.StartsWith("win"))
                {
                    var daemonExePath = newExecutablePath[0..^ 4] + "d.exe";
Example #28
0
        public static async Task <CoreNode> CreateAsync(string dataDir)
        {
            var coreNode = new CoreNode();

            coreNode.DataDir = Guard.NotNullOrEmptyOrWhitespace(nameof(dataDir), dataDir);

            var configPath = Path.Combine(coreNode.DataDir, "bitcoin.conf");

            if (File.Exists(configPath))
            {
                var foundConfig = await NodeConfigParameters.LoadAsync(configPath);

                var rpcPortString = foundConfig["regtest.rpcport"];
                var rpcUser       = foundConfig["regtest.rpcuser"];
                var rpcPassword   = foundConfig["regtest.rpcpassword"];
                var pidFileName   = foundConfig["regtest.pid"];
                var credentials   = new NetworkCredential(rpcUser, rpcPassword);
                try
                {
                    var rpc = new RPCClient(credentials, new Uri("http://127.0.0.1:" + rpcPortString + "/"), Network.RegTest);
                    await rpc.StopAsync();

                    var pidFile = Path.Combine(coreNode.DataDir, "regtest", pidFileName);
                    if (File.Exists(pidFile))
                    {
                        var pid = await File.ReadAllTextAsync(pidFile);

                        using var process = Process.GetProcessById(int.Parse(pid));
                        await process.WaitForExitAsync(CancellationToken.None);
                    }
                    else
                    {
                        var allProcesses      = Process.GetProcesses();
                        var bitcoindProcesses = allProcesses.Where(x => x.ProcessName.Contains("bitcoind"));
                        if (bitcoindProcesses.Count() == 1)
                        {
                            var bitcoind = bitcoindProcesses.First();
                            await bitcoind.WaitForExitAsync(CancellationToken.None);
                        }
                    }
                }
                catch (Exception)
                {
                }
            }

            await IoHelpers.DeleteRecursivelyWithMagicDustAsync(coreNode.DataDir);

            IoHelpers.EnsureDirectoryExists(coreNode.DataDir);

            var pass  = Encoders.Hex.EncodeData(RandomUtils.GetBytes(20));
            var creds = new NetworkCredential(pass, pass);

            var portArray = new int[2];
            var i         = 0;

            while (i < portArray.Length)
            {
                var port = RandomUtils.GetUInt32() % 4000;
                port += 10000;
                if (portArray.Any(p => p == port))
                {
                    continue;
                }

                try
                {
                    var listener = new TcpListener(IPAddress.Loopback, (int)port);
                    listener.Start();
                    listener.Stop();
                    portArray[i] = (int)port;
                    i++;
                }
                catch (SocketException)
                {
                }
            }

            var p2pPort = portArray[0];
            var rpcPort = portArray[1];

            coreNode.P2pEndPoint = new IPEndPoint(IPAddress.Loopback, p2pPort);
            coreNode.RpcEndPoint = new IPEndPoint(IPAddress.Loopback, rpcPort);

            coreNode.RpcClient = new RPCClient($"{creds.UserName}:{creds.Password}", coreNode.RpcEndPoint.ToString(rpcPort), Network.RegTest);

            var config = new NodeConfigParameters
            {
                { "regtest", "1" },
                { "regtest.rest", "1" },
                { "regtest.listenonion", "0" },
                { "regtest.server", "1" },
                { "regtest.txindex", "1" },
                { "regtest.rpcuser", coreNode.RpcClient.CredentialString.UserPassword.UserName },
                { "regtest.rpcpassword", coreNode.RpcClient.CredentialString.UserPassword.Password },
                { "regtest.whitebind", "127.0.0.1:" + p2pPort.ToString() },
                { "regtest.rpcport", coreNode.RpcEndPoint.GetPortOrDefault().ToString() },
                { "regtest.printtoconsole", "0" },               // Set it to one if do not mind loud debug logs
                { "regtest.keypool", "10" },
                { "regtest.pid", "bitcoind.pid" }
            };

            await File.WriteAllTextAsync(configPath, config.ToString());

            using (await coreNode.KillerLock.LockAsync())
            {
                coreNode.Bridge  = new BitcoindProcessBridge();
                coreNode.Process = coreNode.Bridge.Start($"-conf=bitcoin.conf -datadir={coreNode.DataDir} -debug=1", false);
                string pidFile = Path.Combine(coreNode.DataDir, "regtest", "bitcoind.pid");
                if (!File.Exists(pidFile))
                {
                    Directory.CreateDirectory(Path.Combine(coreNode.DataDir, "regtest"));
                    await File.WriteAllTextAsync(pidFile, coreNode.Process.Id.ToString());
                }
            }
            while (true)
            {
                try
                {
                    await coreNode.RpcClient.GetBlockHashAsync(0);

                    break;
                }
                catch
                {
                }
                if (coreNode.Process is null || coreNode.Process.HasExited)
                {
                    break;
                }
            }

            return(coreNode);
        }