public async Task <CoreNode> CreateNodeAsync(bool start = false) { var child = Path.Combine(Root, Last.ToString()); Last++; try { var cfgPath = Path.Combine(child, "data", "bitcoin.conf"); if (File.Exists(cfgPath)) { var config = NodeConfigParameters.Load(cfgPath); var rpcPort = config["regtest.rpcport"]; var rpcUser = config["regtest.rpcuser"]; var rpcPassword = config["regtest.rpcpassword"]; var pidFileName = config["regtest.pid"]; var credentials = new NetworkCredential(rpcUser, rpcPassword); try { var rpc = new RPCClient(credentials, new Uri("http://127.0.0.1:" + rpcPort + "/"), Network.RegTest); await rpc.StopAsync(); var pidFile = Path.Combine(child, "data", "regtest", pidFileName); if (File.Exists(pidFile)) { var pid = File.ReadAllText(pidFile); using var process = Process.GetProcessById(int.Parse(pid)); process.WaitForExit(); } else { var allProcesses = Process.GetProcesses(); var bitcoindProcesses = allProcesses.Where(x => x.ProcessName.Contains("bitcoind")); if (bitcoindProcesses.Count() == 1) { var bitcoind = bitcoindProcesses.First(); bitcoind.WaitForExit(); } } } catch (Exception) { } } await IoHelpers.DeleteRecursivelyWithMagicDustAsync(child); await TryRemoveWorkingDirectoryAsync(); Directory.CreateDirectory(WorkingDirectory); } catch (DirectoryNotFoundException) { } var node = await CoreNode.CreateAsync(child, this); Nodes.Add(node); if (start) { await node.StartAsync(); } return(node); }
public async Task TryKillAsync(bool cleanFolder = true) { try { using (await KillerLock.LockAsync()) { try { await CreateRpcClient().StopAsync(); if (!Process.WaitForExit(20000)) { //log this } } catch (Exception) { } State = CoreNodeState.Killed; } if (cleanFolder) { await IoHelpers.DeleteRecursivelyWithMagicDustAsync(Folder); } } catch { } }
private async Task <TransactionProcessor> CreateTransactionProcessorAsync([CallerMemberName] string callerName = "") { var datadir = EnvironmentHelpers.GetDataDir(Path.Combine("WalletWasabi", "Bench")); var dir = Path.Combine(datadir, callerName, "TransactionStore"); Console.WriteLine(dir); await IoHelpers.DeleteRecursivelyWithMagicDustAsync(dir); // Create the services. // 1. Create connection service. var nodes = new NodesGroup(Network.Main); var bitcoinStore = new BitcoinStore(); var serviceConfiguration = new ServiceConfiguration(2, 2, 21, 50, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 45678), Money.Coins(0.0001m)); // 2. Create wasabi synchronizer service. var synchronizer = new WasabiSynchronizer(Network.Main, bitcoinStore, () => new Uri("http://localhost:35474"), null); synchronizer.Start(requestInterval: TimeSpan.FromDays(1), TimeSpan.FromDays(1), 1000); // 3. Create key manager service. var keyManager = KeyManager.CreateNew(out _, "password"); // 4. Create chaumian coinjoin client. var chaumianClient = new CoinJoinClient(synchronizer, Network.Main, keyManager); // 5. Create wallet service. await bitcoinStore.InitializeAsync(dir, Network.Main); var workDir = Path.Combine(datadir, EnvironmentHelpers.GetMethodName()); var feeProviders = new FeeProviders(new[] { synchronizer }); var wallet = new WalletService(bitcoinStore, keyManager, synchronizer, nodes, workDir, serviceConfiguration, feeProviders); return(wallet.TransactionProcessor); }
private async Task CleanFolderAsync() { try { await IoHelpers.DeleteRecursivelyWithMagicDustAsync(Folder); } catch (DirectoryNotFoundException) { } }
public static async Task <CoreNode> CreateAsync(string folder, NodeBuilder builder) { await IoHelpers.DeleteRecursivelyWithMagicDustAsync(folder); Directory.CreateDirectory(folder); return(new CoreNode(folder, builder)); }
private volatile bool _disposedValue = false; // To detect redundant calls public RegTestFixture() { RuntimeParams.SetDataDir(Path.Combine(Tests.Global.Instance.DataDir, "RegTests", "Backend")); RuntimeParams.LoadAsync().GetAwaiter().GetResult(); var hostedServices = new HostedServices(); BackendRegTestNode = TestNodeBuilder.CreateAsync(hostedServices, callerFilePath: "RegTests", callerMemberName: "BitcoinCoreData").GetAwaiter().GetResult(); var testnetBackendDir = EnvironmentHelpers.GetDataDir(Path.Combine("WalletWasabi", "Tests", "RegTests", "Backend")); IoHelpers.DeleteRecursivelyWithMagicDustAsync(testnetBackendDir).GetAwaiter().GetResult(); Thread.Sleep(100); Directory.CreateDirectory(testnetBackendDir); Thread.Sleep(100); var config = new Config( BackendRegTestNode.RpcClient.Network, BackendRegTestNode.RpcClient.CredentialString.ToString(), new IPEndPoint(IPAddress.Loopback, Network.Main.DefaultPort), new IPEndPoint(IPAddress.Loopback, Network.TestNet.DefaultPort), BackendRegTestNode.P2pEndPoint, new IPEndPoint(IPAddress.Loopback, Network.Main.RPCPort), new IPEndPoint(IPAddress.Loopback, Network.TestNet.RPCPort), BackendRegTestNode.RpcEndPoint); var configFilePath = Path.Combine(testnetBackendDir, "Config.json"); config.SetFilePath(configFilePath); config.ToFile(); var roundConfig = CreateRoundConfig(Money.Coins(0.1m), Constants.OneDayConfirmationTarget, 0.7, 0.1m, 100, 120, 60, 60, 60, 1, 24, true, 11); var roundConfigFilePath = Path.Combine(testnetBackendDir, "CcjRoundConfig.json"); roundConfig.SetFilePath(roundConfigFilePath); roundConfig.ToFile(); var conf = new ConfigurationBuilder() .AddInMemoryCollection(new[] { new KeyValuePair <string, string>("datadir", testnetBackendDir) }) .Build(); BackendEndPoint = $"http://localhost:{new Random().Next(37130, 38000)}/"; BackendHost = Host.CreateDefaultBuilder() .ConfigureWebHostDefaults(webBuilder => webBuilder .UseStartup <Startup>() .UseConfiguration(conf) .UseWebRoot("../../../../WalletWasabi.Backend/wwwroot") .UseUrls(BackendEndPoint)) .Build(); Global = (Backend.Global)BackendHost.Services.GetService(typeof(Backend.Global)); Global.HostedServices = hostedServices; var hostInitializationTask = BackendHost.RunWithTasksAsync(); Logger.LogInfo($"Started Backend webhost: {BackendEndPoint}"); var delayTask = Task.Delay(3000); Task.WaitAny(delayTask, hostInitializationTask); // Wait for server to initialize (Without this OSX CI will fail) }
private static async Task TryRemoveWorkingDirectoryAsync() { try { await IoHelpers.DeleteRecursivelyWithMagicDustAsync(WorkingDirectory); } catch (DirectoryNotFoundException) { } }
private async Task <(string walletsPath, string walletsBackupPath)> CleanupWalletDirectoriesAsync(string baseDir) { var walletsPath = Path.Combine(baseDir, WalletDirectories.WalletsDirName); var walletsBackupPath = Path.Combine(baseDir, WalletDirectories.WalletsBackupDirName); await IoHelpers.DeleteRecursivelyWithMagicDustAsync(walletsPath); await IoHelpers.DeleteRecursivelyWithMagicDustAsync(walletsBackupPath); return(walletsPath, walletsBackupPath); }
private static void CreateDigests() { var tempDir = "DigestTempDir"; IoHelpers.DeleteRecursivelyWithMagicDustAsync(tempDir).GetAwaiter(); Directory.CreateDirectory(tempDir); var torDaemonsDir = Path.Combine(LibraryProjectDirectory, "TorDaemons"); string torWinZip = Path.Combine(torDaemonsDir, "tor-win32.zip"); IoHelpers.BetterExtractZipToDirectoryAsync(torWinZip, tempDir).GetAwaiter(); File.Move(Path.Combine(tempDir, "Tor", "tor.exe"), Path.Combine(tempDir, "TorWin")); string torLinuxZip = Path.Combine(torDaemonsDir, "tor-linux64.zip"); IoHelpers.BetterExtractZipToDirectoryAsync(torLinuxZip, tempDir).GetAwaiter(); File.Move(Path.Combine(tempDir, "Tor", "tor"), Path.Combine(tempDir, "TorLin")); string torOsxZip = Path.Combine(torDaemonsDir, "tor-osx64.zip"); IoHelpers.BetterExtractZipToDirectoryAsync(torOsxZip, tempDir).GetAwaiter(); File.Move(Path.Combine(tempDir, "Tor", "tor"), Path.Combine(tempDir, "TorOsx")); string hwiSoftwareDir = Path.Combine(LibraryProjectDirectory, "Hwi", "Software"); string hwiWinZip = Path.Combine(hwiSoftwareDir, "hwi-win64.zip"); IoHelpers.BetterExtractZipToDirectoryAsync(hwiWinZip, tempDir).GetAwaiter(); File.Move(Path.Combine(tempDir, "hwi.exe"), Path.Combine(tempDir, "HwiWin")); string hwiLinuxZip = Path.Combine(hwiSoftwareDir, "hwi-linux64.zip"); IoHelpers.BetterExtractZipToDirectoryAsync(hwiLinuxZip, tempDir).GetAwaiter(); File.Move(Path.Combine(tempDir, "hwi"), Path.Combine(tempDir, "HwiLin")); string hwiOsxZip = Path.Combine(hwiSoftwareDir, "hwi-osx64.zip"); IoHelpers.BetterExtractZipToDirectoryAsync(hwiOsxZip, tempDir).GetAwaiter(); File.Move(Path.Combine(tempDir, "hwi"), Path.Combine(tempDir, "HwiOsx")); var tempDirInfo = new DirectoryInfo(tempDir); var binaries = tempDirInfo.GetFiles(); Console.WriteLine("Digests:"); foreach (var file in binaries) { var filePath = file.FullName; var hash = ByteHelpers.ToHex(IoHelpers.GetHashFile(filePath)).ToLowerInvariant(); Console.WriteLine($"{file.Name} : {hash}"); } IoHelpers.DeleteRecursivelyWithMagicDustAsync(tempDir).GetAwaiter(); }
public RegTestFixture() { BackendNodeBuilder = NodeBuilder.CreateAsync(nameof(RegTestFixture)).GetAwaiter().GetResult(); BackendNodeBuilder.CreateNodeAsync().GetAwaiter().GetResult(); BackendNodeBuilder.StartAllAsync().GetAwaiter().GetResult(); BackendRegTestNode = BackendNodeBuilder.Nodes[0]; var rpc = BackendRegTestNode.CreateRpcClient(); var connectionString = new RPCCredentialString() { Server = rpc.Address.AbsoluteUri, UserPassword = BackendRegTestNode.Creds }.ToString(); var testnetBackendDir = EnvironmentHelpers.GetDataDir(Path.Combine("WalletWasabi", "Tests", "Backend")); IoHelpers.DeleteRecursivelyWithMagicDustAsync(testnetBackendDir).GetAwaiter().GetResult(); Thread.Sleep(100); Directory.CreateDirectory(testnetBackendDir); Thread.Sleep(100); var config = new Config(BackendNodeBuilder.Network, connectionString, IPAddress.Loopback.ToString(), IPAddress.Loopback.ToString(), BackendRegTestNode.Endpoint.Address.ToString(), Network.Main.DefaultPort, Network.TestNet.DefaultPort, BackendRegTestNode.Endpoint.Port); var configFilePath = Path.Combine(testnetBackendDir, "Config.json"); config.SetFilePath(configFilePath); config.ToFileAsync().GetAwaiter().GetResult(); var roundConfig = CreateRoundConfig(Money.Coins(0.1m), Constants.OneDayConfirmationTarget, 0.7, 0.1m, 100, 120, 60, 60, 60, 1, 24, true, 11); var roundConfigFilePath = Path.Combine(testnetBackendDir, "CcjRoundConfig.json"); roundConfig.SetFilePath(roundConfigFilePath); roundConfig.ToFileAsync().GetAwaiter().GetResult(); var conf = new ConfigurationBuilder() .AddInMemoryCollection(new[] { new KeyValuePair <string, string>("datadir", testnetBackendDir) }) .Build(); BackendEndPoint = $"http://localhost:{new Random().Next(37130, 38000)}/"; BackendHost = WebHost.CreateDefaultBuilder() .UseStartup <Startup>() .UseConfiguration(conf) .UseWebRoot("../../../../WalletWasabi.Backend/wwwroot") .UseUrls(BackendEndPoint) .Build(); Global = (Backend.Global)BackendHost.Services.GetService(typeof(Backend.Global)); var hostInitializationTask = BackendHost.RunWithTasksAsync(); Logger.LogInfo($"Started Backend webhost: {BackendEndPoint}", nameof(Global)); var delayTask = Task.Delay(3000); Task.WaitAny(delayTask, hostInitializationTask); // Wait for server to initialize (Without this OSX CI will fail) }
private static async Task TryRemoveWorkingDirectoryAsync() { try { await IoHelpers.DeleteRecursivelyWithMagicDustAsync(WorkingDirectory); } catch (DirectoryNotFoundException) { } catch (Exception ex) { Logger.LogError <NodeBuilder>(ex); } }
public void Kill(bool cleanFolder = true) { lock (_l) { if (_process != null && !_process.HasExited) { _process.Kill(); _process.WaitForExit(); } State = CoreNodeState.Killed; if (cleanFolder) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(Folder).GetAwaiter().GetResult(); } } }
private async Task <TransactionProcessor> CreateTransactionProcessorAsync([CallerMemberName] string callerName = "") { var keyManager = KeyManager.CreateNew(out _, "password"); keyManager.AssertCleanKeysIndexed(); var txStore = new AllTransactionStore(); var dir = Path.Combine(Global.Instance.DataDir, callerName, "TransactionStore"); await IoHelpers.DeleteRecursivelyWithMagicDustAsync(dir); await txStore.InitializeAsync(dir, Network.RegTest); return(new TransactionProcessor( txStore, keyManager, Money.Coins(0.0001m))); }
public static async Task <NodeBuilder> CreateAsync([CallerMemberName] string caller = null, string version = "0.16.0") { var directory = Path.Combine(SharedFixture.DataDir, caller); version = version ?? "0.16.0"; var path = await EnsureDownloadedAsync(version); try { await IoHelpers.DeleteRecursivelyWithMagicDustAsync(directory); } catch (DirectoryNotFoundException) { } Directory.CreateDirectory(directory); return(new NodeBuilder(directory, path)); }
public async Task StopAsync() { try { using (await KillerLock.LockAsync()) { await RpcClient.StopAsync(); using var timeout = new CancellationTokenSource(20000); await Process.WaitForExitAsync(timeout.Token); await IoHelpers.DeleteRecursivelyWithMagicDustAsync(DataDir); } } catch (Exception ex) { Logging.Logger.LogWarning(ex); } }
public async Task <CoreNode> CreateNodeAsync(bool start = false) { var child = Path.Combine(_root, _last.ToString()); _last++; try { await IoHelpers.DeleteRecursivelyWithMagicDustAsync(child); } catch (DirectoryNotFoundException) { } var node = await CoreNode.CreateAsync(child, this); Nodes.Add(node); if (start) { await node.StartAsync(); } return(node); }
private static async Task <string> EnsureDownloadedAsync(string version) { //is a file if (version.Length >= 2 && version[1] == ':') { return(version); } string zip; string bitcoind; string bitcoindFolderName = $"bitcoin-{version}"; // Remove old bitcoind folders. IEnumerable <string> existingBitcoindFolderPaths = Directory.EnumerateDirectories(Global.Instance.DataDir, "bitcoin-*", SearchOption.TopDirectoryOnly); foreach (string dirPath in existingBitcoindFolderPaths) { string dirName = Path.GetFileName(dirPath); if (bitcoindFolderName != dirName) { await IoHelpers.DeleteRecursivelyWithMagicDustAsync(dirPath); } } if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { bitcoind = Path.Combine(Global.Instance.DataDir, bitcoindFolderName, "bin", "bitcoind.exe"); if (File.Exists(bitcoind)) { return(bitcoind); } zip = Path.Combine(Global.Instance.DataDir, $"bitcoin-{version}-win64.zip"); string url = string.Format("https://bitcoincore.org/bin/bitcoin-core-{0}/" + Path.GetFileName(zip), version); using (var client = new HttpClient()) { client.Timeout = TimeSpan.FromMinutes(10.0); var data = await client.GetByteArrayAsync(url); await File.WriteAllBytesAsync(zip, data); ZipFile.ExtractToDirectory(zip, new FileInfo(zip).Directory.FullName); } } else { bitcoind = Path.Combine(Global.Instance.DataDir, bitcoindFolderName, "bin", "bitcoind"); if (File.Exists(bitcoind)) { return(bitcoind); } zip = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? Path.Combine(Global.Instance.DataDir, $"bitcoin-{version}-x86_64-linux-gnu.tar.gz") : Path.Combine(Global.Instance.DataDir, $"bitcoin-{version}-osx64.tar.gz"); string url = string.Format("https://bitcoincore.org/bin/bitcoin-core-{0}/" + Path.GetFileName(zip), version); using (var client = new HttpClient()) { client.Timeout = TimeSpan.FromMinutes(10.0); var data = await client.GetByteArrayAsync(url); await File.WriteAllBytesAsync(zip, data); using (var process = Process.Start("tar", "-zxvf " + zip + " -C " + Global.Instance.DataDir)) { process.WaitForExit(); } } } File.Delete(zip); return(bitcoind); }
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); }
private static void Main(string[] args) { // 0. Dump Client version (or else wrong .msi will be created) - Helpers.Constants.ClientVersion // 1. Publish with Packager. // 2. Build WIX project with Release and x64 configuration. // 3. Sign with Packager, set restore true so the password won't be kept. var doPublish = true; var doSign = false; var doRestoreThisFile = false; var pfxPassword = "******"; string pfxPath = "C:\\digicert.pfx"; string packagerProjectDirectory = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..\\..\\..\\")); string solutionDirectory = Path.GetFullPath(Path.Combine(packagerProjectDirectory, "..\\")); string guiProjectDirectory = Path.GetFullPath(Path.Combine(solutionDirectory, "WalletWasabi.Gui\\")); string libraryProjectDirectory = Path.GetFullPath(Path.Combine(solutionDirectory, "WalletWasabi\\")); string wixProjectDirectory = Path.GetFullPath(Path.Combine(solutionDirectory, "WalletWasabi.WindowsInstaller\\")); string binDistDirectory = Path.GetFullPath(Path.Combine(guiProjectDirectory, "bin\\dist")); Console.WriteLine($"{nameof(solutionDirectory)}:\t\t{solutionDirectory}"); Console.WriteLine($"{nameof(packagerProjectDirectory)}:\t{packagerProjectDirectory}"); Console.WriteLine($"{nameof(guiProjectDirectory)}:\t\t{guiProjectDirectory}"); Console.WriteLine($"{nameof(libraryProjectDirectory)}:\t\t{libraryProjectDirectory}"); Console.WriteLine($"{nameof(wixProjectDirectory)}:\t\t{wixProjectDirectory}"); Console.WriteLine($"{nameof(binDistDirectory)}:\t\t{binDistDirectory}"); string versionPrefix = Helpers.Constants.ClientVersion.ToString(); string executableName = "wassabee"; Console.WriteLine(); Console.WriteLine($"{nameof(versionPrefix)}:\t\t\t{versionPrefix}"); Console.WriteLine($"{nameof(executableName)}:\t\t\t{executableName}"); // https://docs.microsoft.com/en-us/dotnet/articles/core/rid-catalog // BOTTLENECKS: // Tor - win-32, linux-32, osx-64 // .NET Core - win-32, linux-64, osx-64 // Avalonia - win7-32, linux-64, osx-64 // We'll only support x64, if someone complains, we can come back to it. // For 32 bit Windows there needs to be a lot of WIX configuration to be done. var targets = new List <string> { "win7-x64", "linux-x64", "osx-x64" }; Console.WriteLine(); Console.Write($"{nameof(targets)}:\t\t\t"); targets.ForEach(x => { if (targets.Last() != x) { Console.Write($"{x}, "); } else { Console.Write(x); } }); Console.WriteLine(); if (doPublish) { if (Directory.Exists(binDistDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(binDistDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {binDistDirectory}"); } var psiBuild = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = guiProjectDirectory }; using (var pBuild = Process.Start(psiBuild)) { pBuild.StandardInput.WriteLine("dotnet clean --configuration Release && exit"); pBuild.WaitForExit(); } var guiBinReleaseDirectory = Path.GetFullPath(Path.Combine(guiProjectDirectory, "bin\\Release")); var libraryBinReleaseDirectory = Path.GetFullPath(Path.Combine(libraryProjectDirectory, "bin\\Release")); if (Directory.Exists(guiBinReleaseDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(guiBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {guiBinReleaseDirectory}"); } if (Directory.Exists(libraryBinReleaseDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(libraryBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {libraryBinReleaseDirectory}"); } foreach (string target in targets) { string currentBinDistDirectory; string publishedFolder = Path.Combine(binDistDirectory, target); string macWasabiAppDir = Path.Combine(publishedFolder, "Wasabi Wallet.App"); string macContentsDir = Path.Combine(macWasabiAppDir, "Contents"); if (target.StartsWith("osx")) { currentBinDistDirectory = Path.GetFullPath(Path.Combine(macContentsDir, "MacOS")); } else { currentBinDistDirectory = publishedFolder; } Console.WriteLine(); Console.WriteLine($"{nameof(currentBinDistDirectory)}:\t{currentBinDistDirectory}"); Console.WriteLine(); if (!Directory.Exists(currentBinDistDirectory)) { Directory.CreateDirectory(currentBinDistDirectory); Console.WriteLine($"Created {currentBinDistDirectory}"); } var psiClean = new ProcessStartInfo { FileName = "dotnet", Arguments = $"clean", WorkingDirectory = guiProjectDirectory }; using (var pClean = Process.Start(psiClean)) { pClean.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 doesn't 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 var psiPublish = new ProcessStartInfo { FileName = "dotnet", Arguments = $"publish --configuration Release --force --output \"{currentBinDistDirectory}\" --self-contained true --runtime \"{target}\" /p:VersionPrefix={versionPrefix} --disable-parallel --no-cache", WorkingDirectory = guiProjectDirectory }; using (var pPublish = Process.Start(psiPublish)) { pPublish.WaitForExit(); } // 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); if (target.StartsWith("win")) { var psiEditbin = new ProcessStartInfo { FileName = "editbin", Arguments = $"\"{newExecutablePath}\" /SUBSYSTEM:WINDOWS", WorkingDirectory = currentBinDistDirectory }; using (var pEditbin = Process.Start(psiEditbin)) { pEditbin.WaitForExit(); } var icoPath = Path.Combine(guiProjectDirectory, "Assets", "WasabiLogo.ico"); var psiRcedit = new ProcessStartInfo { FileName = "rcedit", Arguments = $"\"{newExecutablePath}\" --set-icon \"{icoPath}\" --set-file-version \"{versionPrefix}\" --set-product-version \"{versionPrefix}\" --set-version-string \"LegalCopyright\" \"MIT\" --set-version-string \"CompanyName\" \"zkSNACKs\" --set-version-string \"FileDescription\" \"Privacy focused, ZeroLink compliant Bitcoin wallet.\" --set-version-string \"ProductName\" \"Wasabi Wallet\"", WorkingDirectory = currentBinDistDirectory }; using (var pRcedit = Process.Start(psiRcedit)) { pRcedit.WaitForExit(); } } else if (target.StartsWith("osx")) { string resourcesDir = Path.Combine(macContentsDir, "Resources"); string infoFilePath = Path.Combine(macContentsDir, "Info.plist"); Directory.CreateDirectory(resourcesDir); var iconpath = Path.Combine(guiProjectDirectory, "Assets", "WasabiLogo.icns"); File.Copy(iconpath, Path.Combine(resourcesDir, "WasabiLogo.icns")); string infoContent = $@"<?xml version=""1.0"" encoding=""UTF-8""?> <!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd""> <plist version = ""1.0""> <dict> <key>LSMinimumSystemVersion</key> <string>10.12</string> <key>LSArchitecturePriority</key> <array> <string>x86_64</string> </array> <key>CFBundleIconFile</key> <string>WasabiLogo.icns</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>{versionPrefix}</string> <key>CFBundleVersion</key> <string>{versionPrefix}</string> <key>CFBundleExecutable</key> <string>wassabee</string> <key>CFBundleName</key> <string>Wasabi Wallet</string> <key>CFBundleIdentifier</key> <string>zksnacks.wasabiwallet</string> <key>NSHighResolutionCapable</key> <true/> <key>NSAppleScriptEnabled</key> <true/> <key>LSApplicationCategoryType</key> <string>public.app-category.finance</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> </dict> </plist> "; File.WriteAllText(infoFilePath, infoContent); var psiCreateSymLink = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = publishedFolder }; using (var createSymLinkProcess = Process.Start(psiCreateSymLink)) { createSymLinkProcess.StandardInput.WriteLine($"wsl ln -s /Applications && exit"); createSymLinkProcess.WaitForExit(); } //how to generate .DS_Store file - https://github.com/zkSNACKs/WalletWasabi/pull/928/commits/e38ed672dee25f6e45a3eb16584887cc6d48c4e6 var dmgContentDir = Path.Combine(packagerProjectDirectory, "Content", "Osx"); IoHelpers.CopyFilesRecursively(new DirectoryInfo(dmgContentDir), new DirectoryInfo(publishedFolder)); var psiGenIsoImage = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; string uncompressedDmgFileName = $"Wasabi-uncompressed.dmg"; string uncompressedDmgFilePath = Path.Combine(binDistDirectory, uncompressedDmgFileName); string dmgFileName = $"Wasabi-{versionPrefix}.dmg"; using (var genIsoImageProcess = Process.Start(psiGenIsoImage)) { // http://www.nathancoulson.com/proj_cross_tools.php // -D: Do not use deep directory relocation, and instead just pack them in the way we see them // -V: Volume Label // -no-pad: Do not pad the end by 150 sectors (300kb). As it is not a cd image, not required // -apple -r: Creates a .dmg image genIsoImageProcess.StandardInput.WriteLine($"wsl genisoimage -D -V \"Wasabi Wallet\" -no-pad -apple -r -o \"{uncompressedDmgFileName}\" \"{new DirectoryInfo(publishedFolder).Name}\" && exit"); genIsoImageProcess.WaitForExit(); } // cd ~ // git clone https://github.com/planetbeing/libdmg-hfsplus.git && cd libdmg-hfsplus // https://github.com/planetbeing/libdmg-hfsplus/issues/14 // mkdir build && cd build // sudo apt-get install zlib1g-dev // cmake .. // cd build // sudo apt-get install libssl1.0-dev // cmake .. // cd ~/libdmg-hfsplus/build/ // make var psiDmg = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; using (var dmgProcess = Process.Start(psiDmg)) { dmgProcess.StandardInput.WriteLine($"wsl ~/libdmg-hfsplus/build/dmg/./dmg dmg \"{uncompressedDmgFileName}\" \"{dmgFileName}\" && exit"); dmgProcess.WaitForExit(); } // In case compression above doesn't work: //var psiBzip = new ProcessStartInfo //{ // FileName = "cmd", // RedirectStandardInput = true, // WorkingDirectory = binDistDirectory //}; //var bzipProcess = Process.Start(psiBzip); //bzipProcess.StandardInput.WriteLine($"wsl bzip2 \"{uncompressedDmgFileName}\" && exit"); //bzipProcess.WaitForExit(); IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); File.Delete(uncompressedDmgFilePath); IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {publishedFolder}"); } else if (target.StartsWith("linux")) { Console.WriteLine("Create Linux .tar.gz"); if (!Directory.Exists(publishedFolder)) { throw new Exception($"{publishedFolder} doesn't exist."); } var newFolderName = $"WasabiLinux-{versionPrefix}"; var newFolderPath = Path.Combine(binDistDirectory, newFolderName); Directory.Move(publishedFolder, newFolderPath); publishedFolder = newFolderPath; var psiTar = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; using (var tarProcess = Process.Start(psiTar)) { tarProcess.StandardInput.WriteLine($"wsl tar -pczvf {newFolderName}.tar.gz {newFolderName} && exit"); tarProcess.WaitForExit(); } IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {publishedFolder}"); } } } if (doSign is true) { foreach (string target in targets) { if (target.StartsWith("win", StringComparison.OrdinalIgnoreCase)) { string publishedFolder = Path.Combine(binDistDirectory, target); Console.WriteLine("Move created .msi"); var msiPath = Path.Combine(wixProjectDirectory, @"bin\Release\Wasabi.msi"); if (!File.Exists(msiPath)) { throw new Exception(".msi doesn't exist. Expected path: Wasabi.msi."); } var msiFileName = Path.GetFileNameWithoutExtension(msiPath); var newMsiPath = Path.Combine(binDistDirectory, $"{msiFileName}-{versionPrefix}.msi"); File.Move(msiPath, newMsiPath); // Sign code with digicert. var psiSigntool = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; using (var signToolProcess = Process.Start(psiSigntool)) { signToolProcess.StandardInput.WriteLine($"signtool sign /d \"Wasabi Wallet\" /f \"{pfxPath}\" /p {pfxPassword} /t http://timestamp.digicert.com /a \"{newMsiPath}\" && exit"); signToolProcess.WaitForExit(); } IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {publishedFolder}"); } } Console.WriteLine("Signing final files..."); var finalFiles = Directory.GetFiles(binDistDirectory); foreach (var finalFile in finalFiles) { var psiSignProcess = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; using (var signProcess = Process.Start(psiSignProcess)) { signProcess.StandardInput.WriteLine($"gpg --armor --detach-sign {finalFile} && exit"); signProcess.WaitForExit(); } var psiRestoreHeat = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = wixProjectDirectory }; using (var restoreHeatProcess = Process.Start(psiRestoreHeat)) { restoreHeatProcess.StandardInput.WriteLine($"git checkout -- ComponentsGenerated.wxs && exit"); restoreHeatProcess.WaitForExit(); } if (doRestoreThisFile) { var psiRestoreThisFile = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = packagerProjectDirectory }; using (var restoreThisFileProcess = Process.Start(psiRestoreThisFile)) { restoreThisFileProcess.StandardInput.WriteLine($"git checkout -- Program.cs && exit"); restoreThisFileProcess.WaitForExit(); } } } IoHelpers.OpenFolderInFileExplorer(binDistDirectory); return; // No need for readkey here. } }
private async Task CleanFolderAsync() { await IoHelpers.DeleteRecursivelyWithMagicDustAsync(Folder); }
private static void Publish() { if (Directory.Exists(BinDistDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(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.DeleteRecursivelyWithMagicDustAsync(guiBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {guiBinReleaseDirectory}"); } if (Directory.Exists(libraryBinReleaseDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(libraryBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {libraryBinReleaseDirectory}"); } 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 = $"publish --configuration Release --force --output \"{currentBinDistDirectory}\" --self-contained true --runtime \"{target}\" /p:VersionPrefix={VersionPrefix} --disable-parallel --no-cache /p:DebugType=none /p:DebugSymbols=false /p:ErrorReport=none /p:DocumentationFile=\"\" /p:Deterministic=true", WorkingDirectory = GuiProjectDirectory })) { process.WaitForExit(); } 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.DeleteRecursivelyWithMagicDustAsync(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 icoPath = Path.Combine(GuiProjectDirectory, "Assets", "WasabiLogo.ico"); using (var process = Process.Start(new ProcessStartInfo { FileName = "rcedit", // https://github.com/electron/rcedit/ Arguments = $"\"{newExecutablePath}\" --set-icon \"{icoPath}\" --set-file-version \"{VersionPrefix}\" --set-product-version \"{VersionPrefix}\" --set-version-string \"LegalCopyright\" \"MIT\" --set-version-string \"CompanyName\" \"zkSNACKs\" --set-version-string \"FileDescription\" \"Privacy focused, ZeroLink compliant Bitcoin wallet.\" --set-version-string \"ProductName\" \"Wasabi Wallet\"", WorkingDirectory = currentBinDistDirectory })) { process.WaitForExit(); } var daemonExePath = newExecutablePath[0..^ 4] + "d.exe";
private static void Main(string[] args) { // 0. Dump Client version (or else wrong .msi will be created) - Helpers.Constants.ClientVersion // 1. Publish with Packager. // 2. Build WIX project with Release and x64 configuration. // 3. Sign with Packager. bool doPublish = true; bool doSign = false; string packagerProjectDirectory = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..\\..\\..\\")); string solutionDirectory = Path.GetFullPath(Path.Combine(packagerProjectDirectory, "..\\")); string guiProjectDirectory = Path.GetFullPath(Path.Combine(solutionDirectory, "WalletWasabi.Gui\\")); string libraryProjectDirectory = Path.GetFullPath(Path.Combine(solutionDirectory, "WalletWasabi\\")); string wixProjectDirectory = Path.GetFullPath(Path.Combine(solutionDirectory, "WalletWasabi.WindowsInstaller\\")); string binDistDirectory = Path.GetFullPath(Path.Combine(guiProjectDirectory, "bin\\dist")); Console.WriteLine($"{nameof(solutionDirectory)}:\t\t{solutionDirectory}"); Console.WriteLine($"{nameof(packagerProjectDirectory)}:\t{packagerProjectDirectory}"); Console.WriteLine($"{nameof(guiProjectDirectory)}:\t\t{guiProjectDirectory}"); Console.WriteLine($"{nameof(libraryProjectDirectory)}:\t\t{libraryProjectDirectory}"); Console.WriteLine($"{nameof(wixProjectDirectory)}:\t\t{wixProjectDirectory}"); Console.WriteLine($"{nameof(binDistDirectory)}:\t\t{binDistDirectory}"); string versionPrefix = Helpers.Constants.ClientVersion.ToString(); string executableName = "wassabee"; Console.WriteLine(); Console.WriteLine($"{nameof(versionPrefix)}:\t\t\t{versionPrefix}"); Console.WriteLine($"{nameof(executableName)}:\t\t\t{executableName}"); // https://docs.microsoft.com/en-us/dotnet/articles/core/rid-catalog // BOTTLENECKS: // Tor - win-32, linux-32, osx-64 // .NET Core - win-32, linux-64, osx-64 // Avalonia - win7-32, linux-64, osx-64 // We'll only support x64, if someone complains, we can come back to it. // For 32 bit Windows there needs to be a lot of WIX configuration to be done. var targets = new List <string> { "win7-x64", "linux-x64", "osx-x64" }; Console.WriteLine(); Console.Write($"{nameof(targets)}:\t\t\t"); targets.ForEach(x => { if (targets.Last() != x) { Console.Write($"{x}, "); } else { Console.Write(x); } }); Console.WriteLine(); if (doPublish) { if (Directory.Exists(binDistDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(binDistDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {binDistDirectory}"); } var psiBuild = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = guiProjectDirectory }; var pBuild = Process.Start(psiBuild); pBuild.StandardInput.WriteLine("dotnet clean --configuration Release && exit"); pBuild.WaitForExit(); var guiBinReleaseDirectory = Path.GetFullPath(Path.Combine(guiProjectDirectory, "bin\\Release")); var libraryBinReleaseDirectory = Path.GetFullPath(Path.Combine(libraryProjectDirectory, "bin\\Release")); if (Directory.Exists(guiBinReleaseDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(guiBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {guiBinReleaseDirectory}"); } if (Directory.Exists(libraryBinReleaseDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(libraryBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {libraryBinReleaseDirectory}"); } foreach (string target in targets) { string currentBinDistDirectory = Path.GetFullPath(Path.Combine(binDistDirectory, target)); Console.WriteLine(); Console.WriteLine($"{nameof(currentBinDistDirectory)}:\t{currentBinDistDirectory}"); Console.WriteLine(); if (!Directory.Exists(currentBinDistDirectory)) { Directory.CreateDirectory(currentBinDistDirectory); Console.WriteLine($"Created {currentBinDistDirectory}"); } // 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 doesn't 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 var psiPublish = new ProcessStartInfo { FileName = "dotnet", Arguments = $"publish --configuration Release --force --output {currentBinDistDirectory} --self-contained true --runtime {target} /p:VersionPrefix={versionPrefix} --disable-parallel --no-cache", WorkingDirectory = guiProjectDirectory }; var pPublish = Process.Start(psiPublish); pPublish.WaitForExit(); // 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); if (target.StartsWith("win")) { var psiEditbin = new ProcessStartInfo { FileName = "editbin", Arguments = $"\"{newExecutablePath}\" /SUBSYSTEM:WINDOWS", WorkingDirectory = currentBinDistDirectory }; var pEditbin = Process.Start(psiEditbin); pEditbin.WaitForExit(); var icoPath = Path.Combine(guiProjectDirectory, "Assets", "WasabiLogo.ico"); var psiRcedit = new ProcessStartInfo { FileName = "rcedit", Arguments = $"\"{newExecutablePath}\" --set-icon \"{icoPath}\" --set-file-version \"{versionPrefix}\" --set-product-version \"{versionPrefix}\" --set-version-string \"LegalCopyright\" \"MIT\" --set-version-string \"CompanyName\" \"zkSNACKs\" --set-version-string \"FileDescription\" \"Privacy focused, ZeroLink compliant Bitcoin wallet.\" --set-version-string \"ProductName\" \"Wasabi Wallet\"", WorkingDirectory = currentBinDistDirectory }; var pRcedit = Process.Start(psiRcedit); pRcedit.WaitForExit(); } } } if (doSign is true) { foreach (string target in targets) { var publishedFolder = Path.Combine(binDistDirectory, $"{target}"); if (target.StartsWith("win", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("Move created .msi"); var msiPath = Path.Combine(wixProjectDirectory, @"bin\Release\WasabiInstaller.msi"); if (!File.Exists(msiPath)) { throw new Exception(".msi doesn't exist. Expected path: WasabiInstaller.msi."); } var msiFileName = Path.GetFileName(msiPath); var newMsiPath = Path.Combine(binDistDirectory, msiFileName); File.Move(msiPath, newMsiPath); } else if (target.StartsWith("linux", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("Create Linux .tar.gz"); if (!Directory.Exists(publishedFolder)) { throw new Exception($"{publishedFolder} doesn't exist."); } var newFolderName = "WasabiLinux"; var newFolderPath = Path.Combine(binDistDirectory, newFolderName); Directory.Move(publishedFolder, newFolderPath); publishedFolder = newFolderPath; var psiTar = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; var tarProcess = Process.Start(psiTar); tarProcess.StandardInput.WriteLine($"wsl tar -pczvf {newFolderName}.tar.gz {newFolderName} && exit"); tarProcess.WaitForExit(); } else // if (target.StartsWith("osx", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("Create OSX .tar.gz"); if (!Directory.Exists(publishedFolder)) { throw new Exception($"{publishedFolder} doesn't exist."); } var newFolderName = "WasabiOsx"; var newFolderPath = Path.Combine(binDistDirectory, newFolderName); Directory.Move(publishedFolder, newFolderPath); publishedFolder = newFolderPath; var psiTar = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; var tarProcess = Process.Start(psiTar); tarProcess.StandardInput.WriteLine($"wsl tar -pczvf {newFolderName}.tar.gz {newFolderName} && exit"); tarProcess.WaitForExit(); } if (Directory.Exists(publishedFolder)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {publishedFolder}"); } } Console.WriteLine("Signing final files..."); var finalFiles = Directory.GetFiles(binDistDirectory); foreach (var finalFile in finalFiles) { var psiSignProcess = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; var signProcess = Process.Start(psiSignProcess); signProcess.StandardInput.WriteLine($"gpg --armor --detach-sign {finalFile} && exit"); signProcess.WaitForExit(); var psiRestoreHeat = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = wixProjectDirectory }; var restoreHeatProcess = Process.Start(psiRestoreHeat); restoreHeatProcess.StandardInput.WriteLine($"git checkout -- ComponentsGenerated.wxs && exit"); restoreHeatProcess.WaitForExit(); } IoHelpers.OpenFolderInFileExplorer(binDistDirectory); } Console.WriteLine(); Console.WriteLine("FINISHED! Press key to exit..."); Console.ReadKey(); }
private static void Main(string[] args) { // 0. Dump Client version (or else wrong .msi will be created) - Helpers.Constants.ClientVersion // 1. Publish with Packager. // 2. Build WIX project with Release and x64 configuration. // 3. Sign with Packager, set restore true so the password won't be kept. var doPublish = true; var doSign = false; var doRestoreThisFile = false; var pfxPassword = "******"; string pfxPath = "C:\\digicert.pfx"; string packagerProjectDirectory = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..\\..\\..\\")); string solutionDirectory = Path.GetFullPath(Path.Combine(packagerProjectDirectory, "..\\")); string guiProjectDirectory = Path.GetFullPath(Path.Combine(solutionDirectory, "WalletWasabi.Gui\\")); string libraryProjectDirectory = Path.GetFullPath(Path.Combine(solutionDirectory, "WalletWasabi\\")); string wixProjectDirectory = Path.GetFullPath(Path.Combine(solutionDirectory, "WalletWasabi.WindowsInstaller\\")); string binDistDirectory = Path.GetFullPath(Path.Combine(guiProjectDirectory, "bin\\dist")); Console.WriteLine($"{nameof(solutionDirectory)}:\t\t{solutionDirectory}"); Console.WriteLine($"{nameof(packagerProjectDirectory)}:\t{packagerProjectDirectory}"); Console.WriteLine($"{nameof(guiProjectDirectory)}:\t\t{guiProjectDirectory}"); Console.WriteLine($"{nameof(libraryProjectDirectory)}:\t\t{libraryProjectDirectory}"); Console.WriteLine($"{nameof(wixProjectDirectory)}:\t\t{wixProjectDirectory}"); Console.WriteLine($"{nameof(binDistDirectory)}:\t\t{binDistDirectory}"); string versionPrefix = Helpers.Constants.ClientVersion.ToString(); string executableName = "wassabee"; Console.WriteLine(); Console.WriteLine($"{nameof(versionPrefix)}:\t\t\t{versionPrefix}"); Console.WriteLine($"{nameof(executableName)}:\t\t\t{executableName}"); // https://docs.microsoft.com/en-us/dotnet/articles/core/rid-catalog // BOTTLENECKS: // Tor - win-32, linux-32, osx-64 // .NET Core - win-32, linux-64, osx-64 // Avalonia - win7-32, linux-64, osx-64 // We'll only support x64, if someone complains, we can come back to it. // For 32 bit Windows there needs to be a lot of WIX configuration to be done. var targets = new List <string> { "win7-x64", "linux-x64", "osx-x64" }; Console.WriteLine(); Console.Write($"{nameof(targets)}:\t\t\t"); targets.ForEach(x => { if (targets.Last() != x) { Console.Write($"{x}, "); } else { Console.Write(x); } }); Console.WriteLine(); if (doPublish) { if (Directory.Exists(binDistDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(binDistDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {binDistDirectory}"); } var psiBuild = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = guiProjectDirectory }; using (var pBuild = Process.Start(psiBuild)) { pBuild.StandardInput.WriteLine("dotnet clean --configuration Release && exit"); pBuild.WaitForExit(); } var guiBinReleaseDirectory = Path.GetFullPath(Path.Combine(guiProjectDirectory, "bin\\Release")); var libraryBinReleaseDirectory = Path.GetFullPath(Path.Combine(libraryProjectDirectory, "bin\\Release")); if (Directory.Exists(guiBinReleaseDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(guiBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {guiBinReleaseDirectory}"); } if (Directory.Exists(libraryBinReleaseDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(libraryBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {libraryBinReleaseDirectory}"); } foreach (string target in targets) { string currentBinDistDirectory; string publishedFolder = Path.Combine(binDistDirectory, target); string macWasabiAppDir = Path.Combine(publishedFolder, "Wasabi Wallet.App"); // This should be lowercase .app, but MAC will prevent people from upgrading if we change it. string macContentsDir = Path.Combine(macWasabiAppDir, "Contents"); if (target.StartsWith("osx")) { currentBinDistDirectory = Path.GetFullPath(Path.Combine(macContentsDir, "MacOS")); } else { currentBinDistDirectory = publishedFolder; } Console.WriteLine(); Console.WriteLine($"{nameof(currentBinDistDirectory)}:\t{currentBinDistDirectory}"); Console.WriteLine(); if (!Directory.Exists(currentBinDistDirectory)) { Directory.CreateDirectory(currentBinDistDirectory); Console.WriteLine($"Created {currentBinDistDirectory}"); } var psiClean = new ProcessStartInfo { FileName = "dotnet", Arguments = $"clean", WorkingDirectory = guiProjectDirectory }; using (var pClean = Process.Start(psiClean)) { pClean.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 doesn't 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 var psiPublish = new ProcessStartInfo { FileName = "dotnet", Arguments = $"publish --configuration Release --force --output \"{currentBinDistDirectory}\" --self-contained true --runtime \"{target}\" /p:VersionPrefix={versionPrefix} --disable-parallel --no-cache", WorkingDirectory = guiProjectDirectory }; using (var pPublish = Process.Start(psiPublish)) { pPublish.WaitForExit(); } // Remove Tor binaries those 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 = "linux"; } 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); } } // 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 = DirSize(new DirectoryInfo(publishedFolder)) / 1000; if (target.StartsWith("win")) { var psiEditbin = new ProcessStartInfo { FileName = "editbin", Arguments = $"\"{newExecutablePath}\" /SUBSYSTEM:WINDOWS", WorkingDirectory = currentBinDistDirectory }; using (var pEditbin = Process.Start(psiEditbin)) { pEditbin.WaitForExit(); } var icoPath = Path.Combine(guiProjectDirectory, "Assets", "WasabiLogo.ico"); var psiRcedit = new ProcessStartInfo { FileName = "rcedit", Arguments = $"\"{newExecutablePath}\" --set-icon \"{icoPath}\" --set-file-version \"{versionPrefix}\" --set-product-version \"{versionPrefix}\" --set-version-string \"LegalCopyright\" \"MIT\" --set-version-string \"CompanyName\" \"zkSNACKs\" --set-version-string \"FileDescription\" \"Privacy focused, ZeroLink compliant Bitcoin wallet.\" --set-version-string \"ProductName\" \"Wasabi Wallet\"", WorkingDirectory = currentBinDistDirectory }; using (var pRcedit = Process.Start(psiRcedit)) { pRcedit.WaitForExit(); } } else if (target.StartsWith("osx")) { string resourcesDir = Path.Combine(macContentsDir, "Resources"); string infoFilePath = Path.Combine(macContentsDir, "Info.plist"); Directory.CreateDirectory(resourcesDir); var iconpath = Path.Combine(guiProjectDirectory, "Assets", "WasabiLogo.icns"); File.Copy(iconpath, Path.Combine(resourcesDir, "WasabiLogo.icns")); string infoContent = $@"<?xml version=""1.0"" encoding=""UTF-8""?> <!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd""> <plist version = ""1.0""> <dict> <key>LSMinimumSystemVersion</key> <string>10.12</string> <key>LSArchitecturePriority</key> <array> <string>x86_64</string> </array> <key>CFBundleIconFile</key> <string>WasabiLogo.icns</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>{versionPrefix}</string> <key>CFBundleVersion</key> <string>{versionPrefix}</string> <key>CFBundleExecutable</key> <string>{executableName}</string> <key>CFBundleName</key> <string>Wasabi Wallet</string> <key>CFBundleIdentifier</key> <string>zksnacks.wasabiwallet</string> <key>NSHighResolutionCapable</key> <true/> <key>NSAppleScriptEnabled</key> <true/> <key>LSApplicationCategoryType</key> <string>public.app-category.finance</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> </dict> </plist> "; File.WriteAllText(infoFilePath, infoContent); var psiCreateSymLink = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = publishedFolder }; using (var createSymLinkProcess = Process.Start(psiCreateSymLink)) { createSymLinkProcess.StandardInput.WriteLine($"wsl ln -s /Applications && exit"); createSymLinkProcess.WaitForExit(); } //how to generate .DS_Store file - https://github.com/zkSNACKs/WalletWasabi/pull/928/commits/e38ed672dee25f6e45a3eb16584887cc6d48c4e6 var dmgContentDir = Path.Combine(packagerProjectDirectory, "Content", "Osx"); IoHelpers.CopyFilesRecursively(new DirectoryInfo(dmgContentDir), new DirectoryInfo(publishedFolder)); var psiGenIsoImage = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; string uncompressedDmgFileName = $"Wasabi-uncompressed.dmg"; string uncompressedDmgFilePath = Path.Combine(binDistDirectory, uncompressedDmgFileName); string dmgFileName = $"Wasabi-{versionPrefix}.dmg"; using (var genIsoImageProcess = Process.Start(psiGenIsoImage)) { // http://www.nathancoulson.com/proj_cross_tools.php // -D: Do not use deep directory relocation, and instead just pack them in the way we see them // -V: Volume Label // -no-pad: Do not pad the end by 150 sectors (300kb). As it is not a cd image, not required // -apple -r: Creates a .dmg image genIsoImageProcess.StandardInput.WriteLine($"wsl genisoimage -D -V \"Wasabi Wallet\" -no-pad -apple -r -dir-mode 755 -o \"{uncompressedDmgFileName}\" \"{new DirectoryInfo(publishedFolder).Name}\" && exit"); genIsoImageProcess.WaitForExit(); } // cd ~ // git clone https://github.com/planetbeing/libdmg-hfsplus.git && cd libdmg-hfsplus // https://github.com/planetbeing/libdmg-hfsplus/issues/14 // mkdir build && cd build // sudo apt-get install zlib1g-dev // cmake .. // cd build // sudo apt-get install libssl1.0-dev // cmake .. // cd ~/libdmg-hfsplus/build/ // make var psiDmg = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; using (var dmgProcess = Process.Start(psiDmg)) { dmgProcess.StandardInput.WriteLine($"wsl ~/libdmg-hfsplus/build/dmg/./dmg dmg \"{uncompressedDmgFileName}\" \"{dmgFileName}\" && exit"); dmgProcess.WaitForExit(); } // In case compression above doesn't work: //var psiBzip = new ProcessStartInfo //{ // FileName = "cmd", // RedirectStandardInput = true, // WorkingDirectory = binDistDirectory //}; //var bzipProcess = Process.Start(psiBzip); //bzipProcess.StandardInput.WriteLine($"wsl bzip2 \"{uncompressedDmgFileName}\" && exit"); //bzipProcess.WaitForExit(); IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); File.Delete(uncompressedDmgFilePath); IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {publishedFolder}"); } else if (target.StartsWith("linux")) { Console.WriteLine("Create Linux .tar.gz"); if (!Directory.Exists(publishedFolder)) { throw new Exception($"{publishedFolder} doesn't exist."); } var newFolderName = $"WasabiLinux-{versionPrefix}"; var newFolderPath = Path.Combine(binDistDirectory, newFolderName); Directory.Move(publishedFolder, newFolderPath); publishedFolder = newFolderPath; var psiTar = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; using (var tarProcess = Process.Start(psiTar)) { tarProcess.StandardInput.WriteLine($"wsl tar -pczvf {newFolderName}.tar.gz {newFolderName} && exit"); tarProcess.WaitForExit(); } Console.WriteLine("Create Linux .deb"); var debFolderRelativePath = "deb"; var debFolderPath = Path.Combine(binDistDirectory, debFolderRelativePath); var linuxUsrLocalBinFolder = "/usr/local/bin/"; var debUsrLocalBinFolderRelativePath = Path.Combine(debFolderRelativePath, "usr", "local", "bin"); var debUsrLocalBinFolderPath = Path.Combine(binDistDirectory, debUsrLocalBinFolderRelativePath); Directory.CreateDirectory(debUsrLocalBinFolderPath); var debUsrAppFolderRelativePath = Path.Combine(debFolderRelativePath, "usr", "share", "applications"); var debUsrAppFolderPath = Path.Combine(binDistDirectory, debUsrAppFolderRelativePath); Directory.CreateDirectory(debUsrAppFolderPath); var debUsrShareIconsFolderRelativePath = Path.Combine(debFolderRelativePath, "usr", "share", "icons", "hicolor"); var debUsrShareIconsFolderPath = Path.Combine(binDistDirectory, debUsrShareIconsFolderRelativePath); var debianFolderRelativePath = Path.Combine(debFolderRelativePath, "DEBIAN"); var debianFolderPath = Path.Combine(binDistDirectory, debianFolderRelativePath); Directory.CreateDirectory(debianFolderPath); newFolderName = "wasabiwallet"; var linuxWasabiWalletFolder = LinuxPathCombine(linuxUsrLocalBinFolder, newFolderName); var newFolderRelativePath = Path.Combine(debUsrLocalBinFolderRelativePath, newFolderName); newFolderPath = Path.Combine(binDistDirectory, newFolderRelativePath); Directory.Move(publishedFolder, newFolderPath); var assetsFolder = Path.Combine(guiProjectDirectory, "Assets"); var assetsInfo = new DirectoryInfo(assetsFolder); foreach (var file in assetsInfo.EnumerateFiles()) { var number = file.Name.Split(new string[] { "WasabiLogo", ".png" }, StringSplitOptions.RemoveEmptyEntries); if (number.Count() == 1 && int.TryParse(number.First(), out int size)) { string destFolder = Path.Combine(debUsrShareIconsFolderPath, $"{size}x{size}", "apps"); Directory.CreateDirectory(destFolder); file.CopyTo(Path.Combine(destFolder, $"{executableName}.png")); } } var controlFilePath = Path.Combine(debianFolderPath, "control"); // License format doesn't yet work, but should work in the future, it's work in progress: https://bugs.launchpad.net/ubuntu/+source/software-center/+bug/435183 var controlFileContent = $"Package: {executableName}\n" + $"Priority: optional\n" + $"Section: utils\n" + $"Maintainer: nopara73 <*****@*****.**>\n" + $"Version: {versionPrefix}\n" + $"Homepage: http://wasabiwallet.io\n" + $"Vcs-Git: git://github.com/zkSNACKs/WalletWasabi.git\n" + $"Vcs-Browser: https://github.com/zkSNACKs/WalletWasabi\n" + $"Architecture: amd64\n" + $"License: Open Source (MIT)\n" + $"Installed-Size: {installedSizeKb}\n" + $"Description: open-source, non-custodial, privacy focused Bitcoin wallet\n" + $" Built-in Tor, CoinJoin and Coin Control features.\n"; File.WriteAllText(controlFilePath, controlFileContent, Encoding.ASCII); var desktopFilePath = Path.Combine(debUsrAppFolderPath, $"{executableName}.desktop"); var desktopFileContent = $"[Desktop Entry]\n" + $"Type=Application\n" + $"Name=Wasabi Wallet\n" + $"GenericName=Bitcoin Wallet\n" + $"Comment=Privacy focused Bitcoin wallet.\n" + $"Icon={executableName}\n" + $"Terminal=false\n" + $"Exec={executableName}\n" + $"Categories=Office;Finance;\n" + $"Keywords=bitcoin;wallet;crypto;blockchain;wasabi;privacy;anon;awesome;qwe;asd;\n"; File.WriteAllText(desktopFilePath, desktopFileContent, Encoding.ASCII); var wasabiStarterScriptPath = Path.Combine(debUsrLocalBinFolderPath, $"{executableName}"); var wasabiStarterScriptContent = $"#!/bin/sh\n" + $"{ linuxWasabiWalletFolder.TrimEnd('/')}/./{executableName}\n"; File.WriteAllText(wasabiStarterScriptPath, wasabiStarterScriptContent, Encoding.ASCII); string debExeLinuxPath = LinuxPathCombine(newFolderRelativePath, executableName); string debDestopFileLinuxPath = LinuxPathCombine(debUsrAppFolderRelativePath, $"{executableName}.desktop"); var wasabiStarterScriptLinuxPath = LinuxPathCombine(debUsrLocalBinFolderRelativePath, $"{executableName}"); var psi = new ProcessStartInfo { FileName = "wsl", Arguments = $"cd ~ && sudo umount /mnt/c && sudo mount -t drvfs C: /mnt/c -o metadata && cd /mnt/c/Users/user/Desktop/WalletWasabi/WalletWasabi.Gui/bin/dist && sudo chmod +x {debExeLinuxPath} && sudo chmod +x {wasabiStarterScriptLinuxPath} && sudo chmod -R 0644 {debDestopFileLinuxPath} && sudo chmod -R 0775 {LinuxPath(debianFolderRelativePath)} && dpkg --build {LinuxPath(debFolderRelativePath)} $(pwd)", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; using (var p = Process.Start(psi)) { p.WaitForExit(); } IoHelpers.DeleteRecursivelyWithMagicDustAsync(debFolderPath).GetAwaiter().GetResult(); string oldDeb = Path.Combine(binDistDirectory, $"{executableName}_{versionPrefix}_amd64.deb"); string newDeb = Path.Combine(binDistDirectory, $"Wasabi-{versionPrefix}.deb"); File.Move(oldDeb, newDeb); IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {publishedFolder}"); } } } if (doSign is true) { foreach (string target in targets) { if (target.StartsWith("win", StringComparison.OrdinalIgnoreCase)) { string publishedFolder = Path.Combine(binDistDirectory, target); Console.WriteLine("Move created .msi"); var msiPath = Path.Combine(wixProjectDirectory, @"bin\Release\Wasabi.msi"); if (!File.Exists(msiPath)) { throw new Exception(".msi doesn't exist. Expected path: Wasabi.msi."); } var msiFileName = Path.GetFileNameWithoutExtension(msiPath); var newMsiPath = Path.Combine(binDistDirectory, $"{msiFileName}-{versionPrefix}.msi"); File.Move(msiPath, newMsiPath); // Sign code with digicert. var psiSigntool = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; using (var signToolProcess = Process.Start(psiSigntool)) { signToolProcess.StandardInput.WriteLine($"signtool sign /d \"Wasabi Wallet\" /f \"{pfxPath}\" /p {pfxPassword} /t http://timestamp.digicert.com /a \"{newMsiPath}\" && exit"); signToolProcess.WaitForExit(); } IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {publishedFolder}"); } } Console.WriteLine("Signing final files..."); var finalFiles = Directory.GetFiles(binDistDirectory); foreach (var finalFile in finalFiles) { var psiSignProcess = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = binDistDirectory }; using (var signProcess = Process.Start(psiSignProcess)) { signProcess.StandardInput.WriteLine($"gpg --armor --detach-sign {finalFile} && exit"); signProcess.WaitForExit(); } var psiRestoreHeat = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = wixProjectDirectory }; using (var restoreHeatProcess = Process.Start(psiRestoreHeat)) { restoreHeatProcess.StandardInput.WriteLine($"git checkout -- ComponentsGenerated.wxs && exit"); restoreHeatProcess.WaitForExit(); } if (doRestoreThisFile) { var psiRestoreThisFile = new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = packagerProjectDirectory }; using (var restoreThisFileProcess = Process.Start(psiRestoreThisFile)) { restoreThisFileProcess.StandardInput.WriteLine($"git checkout -- Program.cs && exit"); restoreThisFileProcess.WaitForExit(); } } } IoHelpers.OpenFolderInFileExplorer(binDistDirectory); return; // No need for readkey here. } }
private static void Sign() { foreach (string target in Targets) { if (target.StartsWith("win", StringComparison.OrdinalIgnoreCase)) { string publishedFolder = Path.Combine(BinDistDirectory, target); Console.WriteLine("Move created .msi"); var msiPath = Path.Combine(WixProjectDirectory, @"bin\Release\Wasabi.msi"); if (!File.Exists(msiPath)) { throw new Exception(".msi does not exist. Expected path: Wasabi.msi."); } var msiFileName = Path.GetFileNameWithoutExtension(msiPath); var newMsiPath = Path.Combine(BinDistDirectory, $"{msiFileName}-{VersionPrefix}.msi"); File.Move(msiPath, newMsiPath); Console.Write("Enter Code Signing Certificate Password: "******"cmd", RedirectStandardInput = true, WorkingDirectory = BinDistDirectory })) { process.StandardInput.WriteLine($"signtool sign /d \"Wasabi Wallet\" /f \"{PfxPath}\" /p {pfxPassword} /t http://timestamp.digicert.com /a \"{newMsiPath}\" && exit"); process.WaitForExit(); } IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {publishedFolder}"); } } Console.WriteLine("Signing final files..."); var finalFiles = Directory.GetFiles(BinDistDirectory); foreach (var finalFile in finalFiles) { using (var process = Process.Start(new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = BinDistDirectory })) { process.StandardInput.WriteLine($"gpg --armor --detach-sign {finalFile} && exit"); process.WaitForExit(); } using (var process = Process.Start(new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = WixProjectDirectory })) { process.StandardInput.WriteLine($"git checkout -- ComponentsGenerated.wxs && exit"); process.WaitForExit(); } } IoHelpers.OpenFolderInFileExplorer(BinDistDirectory); }
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 void Publish() { if (Directory.Exists(BinDistDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(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.DeleteRecursivelyWithMagicDustAsync(guiBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {guiBinReleaseDirectory}"); } if (Directory.Exists(libraryBinReleaseDirectory)) { IoHelpers.DeleteRecursivelyWithMagicDustAsync(libraryBinReleaseDirectory).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {libraryBinReleaseDirectory}"); } 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 = $"publish --configuration Release --force --output \"{currentBinDistDirectory}\" --self-contained true --runtime \"{target}\" /p:VersionPrefix={VersionPrefix} --disable-parallel --no-cache /p:DebugType=none /p:DebugSymbols=false /p:ErrorReport=none /p:DocumentationFile=\"\" /p:Deterministic=true", WorkingDirectory = GuiProjectDirectory })) { process.WaitForExit(); } Tools.ClearSha512Tags(currentBinDistDirectory); //Tools.RemoveSosDocsUnix(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.DeleteRecursivelyWithMagicDustAsync(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 icoPath = Path.Combine(GuiProjectDirectory, "Assets", "WasabiLogo.ico"); using (var process = Process.Start(new ProcessStartInfo { FileName = "rcedit", // https://github.com/electron/rcedit/ Arguments = $"\"{newExecutablePath}\" --set-icon \"{icoPath}\" --set-file-version \"{VersionPrefix}\" --set-product-version \"{VersionPrefix}\" --set-version-string \"LegalCopyright\" \"MIT\" --set-version-string \"CompanyName\" \"zkSNACKs\" --set-version-string \"FileDescription\" \"Privacy focused, ZeroLink compliant Bitcoin wallet.\" --set-version-string \"ProductName\" \"Wasabi Wallet\"", WorkingDirectory = currentBinDistDirectory })) { process.WaitForExit(); } var daemonExePath = newExecutablePath.Substring(0, newExecutablePath.Length - 4) + "d.exe"; File.Copy(newExecutablePath, daemonExePath); // Do not open console. if (!NSubsysUtil.ProcessFile(newExecutablePath)) { Console.WriteLine("ERROR: Could not remove console from exe."); } // 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. } } else if (target.StartsWith("osx")) { // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!! if (OnlyBinaries) { continue; } var tempName = Path.Combine(BinDistDirectory, $"temp-{target}"); Directory.Move(currentBinDistDirectory, tempName); currentBinDistDirectory = tempName; string macWasabiAppDir = Path.Combine(publishedFolder, "Wasabi Wallet.App"); // This should be lowercase .app, but MAC will prevent people from upgrading if we change it. string macContentsDir = Path.Combine(macWasabiAppDir, "Contents"); string newName = Path.GetFullPath(Path.Combine(macContentsDir, "MacOS")); Directory.CreateDirectory(macContentsDir); Directory.Move(currentBinDistDirectory, newName); currentBinDistDirectory = newName; string resourcesDir = Path.Combine(macContentsDir, "Resources"); string infoFilePath = Path.Combine(macContentsDir, "Info.plist"); Directory.CreateDirectory(resourcesDir); var iconPath = Path.Combine(GuiProjectDirectory, "Assets", "WasabiLogo.icns"); File.Copy(iconPath, Path.Combine(resourcesDir, "WasabiLogo.icns")); string infoContent = $@"<?xml version=""1.0"" encoding=""UTF-8""?> <!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd""> <plist version = ""1.0""> <dict> <key>LSMinimumSystemVersion</key> <string>10.12</string> <key>LSArchitecturePriority</key> <array> <string>x86_64</string> </array> <key>CFBundleIconFile</key> <string>WasabiLogo.icns</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>{VersionPrefix}</string> <key>CFBundleVersion</key> <string>{VersionPrefix}</string> <key>CFBundleExecutable</key> <string>{ExecutableName}</string> <key>CFBundleName</key> <string>Wasabi Wallet</string> <key>CFBundleIdentifier</key> <string>zksnacks.wasabiwallet</string> <key>NSHighResolutionCapable</key> <true/> <key>NSAppleScriptEnabled</key> <true/> <key>LSApplicationCategoryType</key> <string>public.app-category.finance</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> </dict> </plist> "; File.WriteAllText(infoFilePath, infoContent); using (var process = Process.Start(new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = publishedFolder })) { process.StandardInput.WriteLine($"wsl ln -s /Applications && exit"); process.WaitForExit(); } //how to generate .DS_Store file - https://github.com/zkSNACKs/WalletWasabi/pull/928/commits/e38ed672dee25f6e45a3eb16584887cc6d48c4e6 var dmgContentDir = Path.Combine(PackagerProjectDirectory, "Content", "Osx"); IoHelpers.CopyFilesRecursively(new DirectoryInfo(dmgContentDir), new DirectoryInfo(publishedFolder)); string uncompressedDmgFileName = $"Wasabi-uncompressed.dmg"; string uncompressedDmgFilePath = Path.Combine(BinDistDirectory, uncompressedDmgFileName); string dmgFileName = $"Wasabi-{VersionPrefix}.dmg"; using (var process = Process.Start(new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = BinDistDirectory })) { // http://www.nathancoulson.com/proj_cross_tools.php // -D: Do not use deep directory relocation, and instead just pack them in the way we see them // -V: Volume Label // -no-pad: Do not pad the end by 150 sectors (300kb). As it is not a cd image, not required // -apple -r: Creates a .dmg image process.StandardInput.WriteLine($"wsl genisoimage -D -V \"Wasabi Wallet\" -no-pad -apple -r -dir-mode 755 -o \"{uncompressedDmgFileName}\" \"{new DirectoryInfo(publishedFolder).Name}\" && exit"); process.WaitForExit(); } // cd ~ // git clone https://github.com/planetbeing/libdmg-hfsplus.git && cd libdmg-hfsplus // https://github.com/planetbeing/libdmg-hfsplus/issues/14 // mkdir build && cd build // sudo apt-get install zlib1g-dev // cmake .. // cd build // sudo apt-get install libssl1.0-dev // cmake .. // cd ~/libdmg-hfsplus/build/ // make using (var process = Process.Start(new ProcessStartInfo { FileName = "cmd", RedirectStandardInput = true, WorkingDirectory = BinDistDirectory })) { process.StandardInput.WriteLine($"wsl ~/libdmg-hfsplus/build/dmg/./dmg dmg \"{uncompressedDmgFileName}\" \"{dmgFileName}\" && exit"); process.WaitForExit(); } IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); File.Delete(uncompressedDmgFilePath); IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {publishedFolder}"); } else if (target.StartsWith("linux")) { // IF IT'S IN ONLYBINARIES MODE DON'T DO ANYTHING FANCY PACKAGING AFTER THIS!!! if (OnlyBinaries) { continue; } Console.WriteLine("Create Linux .tar.gz"); if (!Directory.Exists(publishedFolder)) { throw new Exception($"{publishedFolder} does not exist."); } var newFolderName = $"WasabiLinux-{VersionPrefix}"; var newFolderPath = Path.Combine(BinDistDirectory, newFolderName); Directory.Move(publishedFolder, newFolderPath); publishedFolder = newFolderPath; var linuxPath = $"/mnt/c/{Tools.LinuxPath(BinDistDirectory.Replace("C:\\", ""))}"; // We assume that it is on drive C:\. var commands = new[] { "cd ~", "sudo umount /mnt/c", "sudo mount -t drvfs C: /mnt/c -o metadata", $"cd {linuxPath}", $"sudo find ./{newFolderName} -type f -exec chmod 644 {{}} \\;", $"sudo find ./{newFolderName} -type f \\( -name 'wassabee' -o -name 'hwi' -o -name 'bitcoind' \\) -exec chmod +x {{}} \\;", $"tar -pczvf {newFolderName}.tar.gz {newFolderName}" }; string arguments = string.Join(" && ", commands); using (var process = Process.Start(new ProcessStartInfo { FileName = "wsl", Arguments = arguments, RedirectStandardInput = true, WorkingDirectory = BinDistDirectory })) { process.WaitForExit(); } Console.WriteLine("Create Linux .deb"); var debFolderRelativePath = "deb"; var debFolderPath = Path.Combine(BinDistDirectory, debFolderRelativePath); var linuxUsrLocalBinFolder = "/usr/local/bin/"; var debUsrLocalBinFolderRelativePath = Path.Combine(debFolderRelativePath, "usr", "local", "bin"); var debUsrLocalBinFolderPath = Path.Combine(BinDistDirectory, debUsrLocalBinFolderRelativePath); Directory.CreateDirectory(debUsrLocalBinFolderPath); var debUsrAppFolderRelativePath = Path.Combine(debFolderRelativePath, "usr", "share", "applications"); var debUsrAppFolderPath = Path.Combine(BinDistDirectory, debUsrAppFolderRelativePath); Directory.CreateDirectory(debUsrAppFolderPath); var debUsrShareIconsFolderRelativePath = Path.Combine(debFolderRelativePath, "usr", "share", "icons", "hicolor"); var debUsrShareIconsFolderPath = Path.Combine(BinDistDirectory, debUsrShareIconsFolderRelativePath); var debianFolderRelativePath = Path.Combine(debFolderRelativePath, "DEBIAN"); var debianFolderPath = Path.Combine(BinDistDirectory, debianFolderRelativePath); Directory.CreateDirectory(debianFolderPath); newFolderName = "wasabiwallet"; var linuxWasabiWalletFolder = Tools.LinuxPathCombine(linuxUsrLocalBinFolder, newFolderName); var newFolderRelativePath = Path.Combine(debUsrLocalBinFolderRelativePath, newFolderName); newFolderPath = Path.Combine(BinDistDirectory, newFolderRelativePath); Directory.Move(publishedFolder, newFolderPath); var assetsFolder = Path.Combine(GuiProjectDirectory, "Assets"); var assetsInfo = new DirectoryInfo(assetsFolder); foreach (var file in assetsInfo.EnumerateFiles()) { var number = file.Name.Split(new string[] { "WasabiLogo", ".png" }, StringSplitOptions.RemoveEmptyEntries); if (number.Length == 1 && int.TryParse(number.First(), out int size)) { string destFolder = Path.Combine(debUsrShareIconsFolderPath, $"{size}x{size}", "apps"); Directory.CreateDirectory(destFolder); file.CopyTo(Path.Combine(destFolder, $"{ExecutableName}.png")); } } var controlFilePath = Path.Combine(debianFolderPath, "control"); // License format does not yet work, but should work in the future, it's work in progress: https://bugs.launchpad.net/ubuntu/+source/software-center/+bug/435183 var controlFileContent = $"Package: {ExecutableName}\n" + $"Priority: optional\n" + $"Section: utils\n" + $"Maintainer: nopara73 <*****@*****.**>\n" + $"Version: {VersionPrefix}\n" + $"Homepage: http://wasabiwallet.io\n" + $"Vcs-Git: git://github.com/zkSNACKs/WalletWasabi.git\n" + $"Vcs-Browser: https://github.com/zkSNACKs/WalletWasabi\n" + $"Architecture: amd64\n" + $"License: Open Source (MIT)\n" + $"Installed-Size: {installedSizeKb}\n" + $"Description: open-source, non-custodial, privacy focused Bitcoin wallet\n" + $" Built-in Tor, CoinJoin and Coin Control features.\n"; File.WriteAllText(controlFilePath, controlFileContent, Encoding.ASCII); var desktopFilePath = Path.Combine(debUsrAppFolderPath, $"{ExecutableName}.desktop"); var desktopFileContent = $"[Desktop Entry]\n" + $"Type=Application\n" + $"Name=Wasabi Wallet\n" + $"StartupWMClass=Wasabi Wallet\n" + $"GenericName=Bitcoin Wallet\n" + $"Comment=Privacy focused Bitcoin wallet.\n" + $"Icon={ExecutableName}\n" + $"Terminal=false\n" + $"Exec={ExecutableName}\n" + $"Categories=Office;Finance;\n" + $"Keywords=bitcoin;wallet;crypto;blockchain;wasabi;privacy;anon;awesome;qwe;asd;\n"; File.WriteAllText(desktopFilePath, desktopFileContent, Encoding.ASCII); var wasabiStarterScriptPath = Path.Combine(debUsrLocalBinFolderPath, $"{ExecutableName}"); var wasabiStarterScriptContent = $"#!/bin/sh\n" + $"{ linuxWasabiWalletFolder.TrimEnd('/')}/{ExecutableName} $@\n"; File.WriteAllText(wasabiStarterScriptPath, wasabiStarterScriptContent, Encoding.ASCII); string debExeLinuxPath = Tools.LinuxPathCombine(newFolderRelativePath, ExecutableName); string debDestopFileLinuxPath = Tools.LinuxPathCombine(debUsrAppFolderRelativePath, $"{ExecutableName}.desktop"); commands = new[] { "cd ~", "sudo umount /mnt/c", "sudo mount -t drvfs C: /mnt/c -o metadata", $"cd {linuxPath}", $"sudo find {Tools.LinuxPath(newFolderRelativePath)} -type f -exec chmod 644 {{}} \\;", $"sudo find {Tools.LinuxPath(newFolderRelativePath)} -type f \\( -name 'wassabee' -o -name 'hwi' -o -name 'bitcoind' \\) -exec chmod +x {{}} \\;", $"sudo chmod -R 0775 {Tools.LinuxPath(debianFolderRelativePath)}", $"sudo chmod -R 0644 {debDestopFileLinuxPath}", $"dpkg --build {Tools.LinuxPath(debFolderRelativePath)} $(pwd)" }; arguments = string.Join(" && ", commands); using (var process = Process.Start(new ProcessStartInfo { FileName = "wsl", Arguments = arguments, RedirectStandardInput = true, WorkingDirectory = BinDistDirectory })) { process.WaitForExit(); } IoHelpers.DeleteRecursivelyWithMagicDustAsync(debFolderPath).GetAwaiter().GetResult(); string oldDeb = Path.Combine(BinDistDirectory, $"{ExecutableName}_{VersionPrefix}_amd64.deb"); string newDeb = Path.Combine(BinDistDirectory, $"Wasabi-{VersionPrefix}.deb"); File.Move(oldDeb, newDeb); IoHelpers.DeleteRecursivelyWithMagicDustAsync(publishedFolder).GetAwaiter().GetResult(); Console.WriteLine($"Deleted {publishedFolder}"); } } }
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 { })