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; } }
public AllTransactionStore(string workFolderPath, Network network) { WorkFolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(workFolderPath), workFolderPath, trim: true); IoHelpers.EnsureDirectoryExists(WorkFolderPath); Network = Guard.NotNull(nameof(network), network); }
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); } } }
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); } } }
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); } } }
/// <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); }
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); }
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}."); } }
public CoordinatorParameters(string dataDir) { ApplicationDataDir = dataDir; IoHelpers.EnsureDirectoryExists(CoordinatorDataDir); var runtimeConfigurationFilePath = Path.Combine(ApplicationDataDir, "WabiSabiConfig.json"); RuntimeCoordinatorConfig = new(runtimeConfigurationFilePath); RuntimeCoordinatorConfig.LoadOrCreateDefaultFile(); }
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); }
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); }
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); }
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); } }
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); } } }
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); } } }
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."); }
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..])}";
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 { })
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 { })
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); } } }
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";
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); }