/*****************************************************************************************/ /// <summary> /// Connect to a blockchain /// </summary> /// <param name="hostname">Blockchain host</param> /// <param name="blockchain">Blockchain name</param> /// <param name="port">Blockchain port</param> /// <param name="localPort">Blockchain local port</param> /// <param name="rpcPort">Blockchain local RPC port</param> /// <param name="clean">Should clean local blockchain directory?</param> /// <returns></returns> public async Task <MultichainModel> Connect(string hostname, string blockchain, int port, int localPort, int rpcPort, bool clean = true) { MultichainModel chain; if (!Connections.TryGetValue(blockchain, out chain)) { chain = new MultichainModel(hostname, port, blockchain, RpcUser, MultiChainTools.RandomString(), localPort, rpcPort); Connections[blockchain] = chain; } var model = await RunDaemon(chain, clean); await ConnectRpc(model); return(model); }
/// <summary> /// Runs multichaind in a background process, connecting to a specified blockchain. /// Optionally callsback on successful launch, determined by multichaind's stdout/err. /// </summary> /// <param name="chain">Blockchain connection/status data</param> /// <param name="clean">Should clean local blockchain directory?</param> /// <returns>Blockchain connection/status data</returns> private static async Task <MultichainModel> RunDaemon(MultichainModel chain, bool clean) { if (chain.Process != null) { try { if (!chain.Process.HasExited) { return(chain); } } // May throw exception if process has become unassociated catch (InvalidOperationException) { } Debug.WriteLine($"Restarting Multichaind for chain: {chain.Name}!!"); } // Get working directory and multichaind.exe path var evotoDir = MultiChainTools.GetAppDataFolder(allowSubDir: false); var multichainDPath = Path.Combine(evotoDir, "multichaind.exe"); MultiChainTools.EnsureFileExists(multichainDPath, Resources.multichaind); var dataDir = MultiChainTools.GetAppDataFolder(); // Clean if required (multichain bug) if (clean) { MultiChainTools.CleanBlockchain(dataDir, chain.Name); } Debug.WriteLine( $"Starting MultiChain connection to {chain.Name}@{chain.Hostname}:{chain.Port} ({chain.LocalPort})"); Debug.WriteLine($"RPC Data: {RpcUser} : {chain.RpcPassword} : {chain.RpcPort}"); var pArgs = $"{chain.Name}@{chain.Hostname}:{chain.Port} -daemon -datadir={dataDir} -server -port={chain.LocalPort}" + $" -rpcuser={RpcUser} -rpcpassword={chain.RpcPassword} -rpcport={chain.RpcPort}"; chain.Process = new Process { StartInfo = { // Stop the process from opening a new window RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, // Setup executable and parameters FileName = multichainDPath, Arguments = pArgs } }; var taskCompletion = new TaskCompletionSource <bool>(); // Connect to outputs chain.Process.ErrorDataReceived += (sender, args) => { if (string.IsNullOrWhiteSpace(args.Data)) { return; } Debug.WriteLine($"Multichaind Error ({chain.Name}): {args.Data}"); }; chain.Process.OutputDataReceived += (sender, e) => { if (string.IsNullOrWhiteSpace(e.Data)) { return; } Debug.WriteLine($"Multichaind ({chain.Name}): {e.Data}"); if (e.Data.Contains("Node started")) { taskCompletion.SetResult(true); } }; // Launch process var success = chain.Process.Start(); if (!success) { throw new CouldNotStartProcessException(); } // Read outputs chain.Process.BeginOutputReadLine(); chain.Process.BeginErrorReadLine(); chain.Process.EnableRaisingEvents = true; chain.Process.Exited += (sender, args) => { // May have been disposed taskCompletion.TrySetResult(false); }; await Task.Run(() => { if (!taskCompletion.Task.Wait(TimeSpan.FromMinutes(1))) { chain.Process.Kill(); throw new CouldNotConnectToBlockchainException("Timed Out"); } if (!taskCompletion.Task.Result) { throw new CouldNotConnectToBlockchainException(); } }); return(chain); }
public static async Task CreateBlockchain(string blockchainName) { var evotoDir = MultiChainTools.GetAppDataFolder(allowSubDir: false); var multichainUtilPath = Path.Combine(evotoDir, "multichain-util.exe"); MultiChainTools.EnsureFileExists(multichainUtilPath, Resources.multichain_util); Debug.WriteLine($"Creating MultiChain: {blockchainName}"); var dataDir = MultiChainTools.GetAppDataFolder(); var process = new Process { StartInfo = { // Stop the process from opening a new window RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, // Setup executable and parameters FileName = multichainUtilPath, Arguments = $"-datadir={dataDir} create {blockchainName}" } }; var outputQueue = new Queue <string>(); var errQueue = new Queue <string>(); process.OutputDataReceived += (sender, args) => { if (string.IsNullOrWhiteSpace(args.Data)) { return; } Debug.WriteLine($"Multichain-util: {args.Data}"); outputQueue.Enqueue(args.Data); }; process.ErrorDataReceived += (sender, args) => { if (string.IsNullOrWhiteSpace(args.Data)) { return; } Debug.WriteLine($"Multichain-util Error: {args.Data}"); errQueue.Enqueue(args.Data); }; // Go var success = process.Start(); if (!success) { throw new CouldNotCreateBlockchainException("Could not start process"); } process.BeginOutputReadLine(); process.BeginErrorReadLine(); // Wait for process to end await Task.Run(() => { if (process.WaitForExit(10 * 1000)) { // Allow error events time to be processed Thread.Sleep(100); if (errQueue.Count > 0) { throw new CouldNotCreateBlockchainException(string.Join("\n", errQueue)); } } else { // Process hanging process.Kill(); throw new CouldNotCreateBlockchainException("Process timed out"); } }); }