/// <summary> /// The expected sequence of arguments: /// <list> /// <item> /// 1, [network-name] [options] [rpc-command] [rpc-params]. /// </item> /// <item> /// 2, [network-name] [options] [api-controller "/" api-command] [api-params]. /// </item> /// </list> /// </summary> public static void Main(string[] args) { try { // Preprocess the command line arguments var argList = new List <string>(args); string networkName = null; if (argList.Any()) { networkName = argList.First(); argList.RemoveAt(0); } var optionList = new List <string>(); while ((argList.Any()) && (argList[0].StartsWith('-'))) { optionList.Add(argList[0]); argList.RemoveAt(0); } string method = ""; if (argList.Any()) { method = argList.First().ToUpper(); if (method == "GET" || method == "POST" || method == "DELETE") { argList.RemoveAt(0); } else { method = ""; } } string command = string.Empty; if (argList.Any()) { command = argList.First(); argList.RemoveAt(0); } var commandArgList = new List <string>(argList); // Display help if required. if (optionList.Contains("-help") || optionList.Contains("--help") || string.IsNullOrWhiteSpace(command)) { var builder = new StringBuilder(); builder.AppendLine("Usage:"); builder.AppendLine(" dotnet run <Stratis.Bitcoin.Cli/Stratis.Bitcoin.Cli.dll> [network-name] [options] [method] <command> [arguments]"); builder.AppendLine(); builder.AppendLine("Command line arguments:"); builder.AppendLine(); builder.AppendLine("[network-name] Name of the network - e.g. \"stratis\" or \"solaris\" or \"bitcoin\"."); builder.AppendLine("[options] Options for the CLI (optional) - e.g. -help, -rpcuser, see below."); builder.AppendLine("[method] Method to use for API calls - 'GET', 'POST' or 'DELETE'."); builder.AppendLine("[command] Name of RPC method or API <controller>/<method>."); builder.AppendLine("[arguments] Argument by position (RPC) or Name = Value pairs (API) (optional)."); builder.AppendLine(); builder.AppendLine("Options:"); builder.AppendLine("-help This help message"); builder.AppendLine("-rpcconnect=<ip> Send commands to node running on <ip> (default: 127.0.0.1)"); builder.AppendLine("-rpcport=<port> Connect to JSON-RPC on <port> (default for Stratis: 26174 or default for Solaris: 61000 or default for Bitcoin: 8332)"); builder.AppendLine("-rpcuser=<user> Username for JSON-RPC connections"); builder.AppendLine("-rpcpassword=<pw> Password for JSON-RPC connections"); builder.AppendLine(); builder.AppendLine("Examples:"); builder.AppendLine(); builder.AppendLine("dotnet run stratis -testnet GET Wallet/history WalletName=testwallet - Lists all the historical transactions of the wallet called 'testwallet' on the stratis test network."); builder.AppendLine("dotnet run stratis -rpcuser=stratistestuser -rpcpassword=stratistestpassword -rpcconnect=127.0.0.3 -rpcport=26174 getinfo - Displays general information about the Stratis node on the 127.0.0.3:26174, authenticating with the RPC specified user."); builder.AppendLine("dotnet run bitcoin -rpcuser=btctestuser -rpcpassword=btctestpass getbalance - Displays the current balance of the opened wallet on the 127.0.0.1:8332 node, authenticating with the RPC specified user."); Console.WriteLine(builder); return; } // Determine API port. NetworksSelector networksSelector = null; if (networkName.Contains("stratis")) { networksSelector = Networks.Networks.Stratis; } else if (networkName.Contains("solaris")) { networksSelector = Networks.Networks.Solaris; } else { networksSelector = Networks.Networks.Bitcoin; } // API calls require both the contoller name and the method name separated by "/". // If this is not an API call then assume it is an RPC call. if (!command.Contains("/")) { // Process RPC call. try { string[] options = optionList.Append("-server").ToArray(); var nodeSettings = new NodeSettings(networksSelector: networksSelector, protocolVersion: ProtocolVersion.PROVEN_HEADER_VERSION, args: options) { MinProtocolVersion = ProtocolVersion.ALT_PROTOCOL_VERSION }; var rpcSettings = new RpcSettings(nodeSettings); Network network = nodeSettings.Network; // Find the binding to 127.0.0.1 or the first available. The logic in RPC settings ensures there will be at least 1. System.Net.IPEndPoint nodeEndPoint = rpcSettings.Bind.FirstOrDefault(b => b.Address.ToString() == "127.0.0.1") ?? rpcSettings.Bind[0]; var rpcUri = new Uri($"http://{nodeEndPoint}"); // Process the command line RPC arguments // TODO: this should probably be moved to the NodeSettings.FromArguments if (options.GetValueOf("-rpcbind") != null) { rpcUri = new Uri($"http://{options.GetValueOf("-rpcbind")}"); } if (options.GetValueOf("-rpcconnect") != null || options.GetValueOf("-rpcport") != null) { string rpcAddress = options.GetValueOf("-rpcconnect") ?? "127.0.0.1"; int rpcPort = rpcSettings.RPCPort; int.TryParse(options.GetValueOf("-rpcport"), out rpcPort); rpcUri = new Uri($"http://{rpcAddress}:{rpcPort}"); } rpcSettings.RpcUser = options.GetValueOf("-rpcuser") ?? rpcSettings.RpcUser; rpcSettings.RpcPassword = options.GetValueOf("-rpcpassword") ?? rpcSettings.RpcPassword; Console.WriteLine($"Connecting to the following RPC node: http://{rpcSettings.RpcUser}:{rpcSettings.RpcPassword}@{rpcUri.Authority}."); // Initialize the RPC client with the configured or passed userid, password and endpoint. var rpcClient = new RPCClient($"{rpcSettings.RpcUser}:{rpcSettings.RpcPassword}", rpcUri, network); // Execute the RPC command Console.WriteLine($"Sending RPC command '{command} {string.Join(" ", commandArgList)}' to '{rpcUri}'."); RPCResponse response = rpcClient.SendCommand(command, commandArgList.ToArray()); // Return the result as a string to the console. Console.WriteLine(response.ResultString); } catch (Exception err) { Console.WriteLine(err.Message); } } else { // Process API call. string[] options = optionList.ToArray(); var nodeSettings = new NodeSettings(networksSelector: networksSelector, protocolVersion: ProtocolVersion.PROVEN_HEADER_VERSION, args: options) { MinProtocolVersion = ProtocolVersion.ALT_PROTOCOL_VERSION }; var apiSettings = new ApiSettings(nodeSettings); string url = $"http://localhost:{apiSettings.ApiPort}/api".AppendPathSegment(command); object commandArgObj = GetAnonymousObjectFromDictionary(commandArgList .Select(a => a.Split('=')) .ToDictionary(a => a[0], a => a[1])); HttpResponseMessage httpResponse; switch (method) { case "POST": httpResponse = CallApiPost(url, commandArgObj); break; case "DELETE": httpResponse = CallApiDelete(url, commandArgObj); break; default: httpResponse = CallApiGet(url, commandArgObj); break; } var response = httpResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult(); // Format and return the result as a string to the console. Console.WriteLine(JsonConvert.SerializeObject(JsonConvert.DeserializeObject <object>(response), Formatting.Indented)); } } catch (Exception err) { // Report any errors to the console. Console.WriteLine(ExceptionToString(err)); } }
private void DrawConsoleUI(float fYOffset) { if (m_bNeedResetFocus) { GUI.FocusControl(null); m_bNeedResetFocus = false; } GUILayout.Space(10); GUILayout.BeginHorizontal(); GUILayout.Space(20f); Color kOrgColor = GUI.color; GUI.color = Color.cyan; GUILayout.Label("Please see unity console for output detail.", GUILayout.Width(300)); GUI.color = kOrgColor; GUILayout.EndHorizontal(); GUILayout.Space(5); GUILayout.BeginHorizontal(); GUILayout.Space(20f); GUILayout.Label("RPC Command:", GUILayout.Width(100)); GUILayout.EndHorizontal(); //GUILayout.Space(5); GUILayoutOption[] arrLO = new GUILayoutOption[2]; arrLO[0] = GUILayout.Width(170); arrLO[1] = GUILayout.Height(20); GUILayout.BeginHorizontal(); GUILayout.Space(20.0f); Color kOldC = GUI.color; GUI.color = new Color(255 / 255.0f, 160 / 255.0f, 50 / 255.0f); GUILayout.Label("Command Type:", GUILayout.Width(120.0f)); int iSelCmdTypeIndex = EditorGUILayout.Popup(m_iLastSelCmdTypeIndex, m_arrCmdType, arrLO); if (iSelCmdTypeIndex != m_iLastSelCmdTypeIndex) { m_iLastSelCmdIndex = 0; m_iLastSelCmdTypeIndex = iSelCmdTypeIndex; } GUI.color = kOldC; GUILayout.EndHorizontal(); string[] arrCmd = m_listCommand[m_iLastSelCmdTypeIndex]; string[] arrArgInfo = m_listArgsInfo[m_iLastSelCmdTypeIndex]; GUILayout.BeginHorizontal(); GUILayout.Space(20.0f); kOldC = GUI.color; GUI.color = Color.yellow; GUILayout.Label("Command Select:", GUILayout.Width(120.0f)); int iSelCmdIndex = EditorGUILayout.Popup(m_iLastSelCmdIndex, arrCmd, arrLO); GUI.color = kOldC; if (iSelCmdIndex != m_iLastSelCmdIndex) { if (arrCmd[iSelCmdIndex] != COMMAND_SPLITE) { m_strRawCurArgInfo = arrArgInfo[iSelCmdIndex]; //m_strCurArgsInfo = arrArgInfo[iSelCmdIndex]; ResolveArgType(); if (string.IsNullOrEmpty(m_strCurArgsInfo)) { m_strRawExeRpcCommand = arrCmd[iSelCmdIndex]; } else { m_strRawExeRpcCommand = arrCmd[iSelCmdIndex] + " "; } GUI.FocusControl("RPCCmdInput"); } else { m_strRawExeRpcCommand = ""; m_strCurArgsInfo = ""; GUI.FocusControl(null); } m_iLastSelCmdIndex = iSelCmdIndex; } GUILayout.Space(5); kOldC = GUI.color; GUI.color = new Color(0.69f, 0.77f, 0.95f); GUILayout.Label(m_strCurArgsInfo, GUILayout.Width(400.0f)); GUI.color = kOldC; GUILayout.EndHorizontal(); GUILayout.Space(5); GUILayout.BeginHorizontal(); GUILayout.Space(20f); //arrLO = new GUILayoutOption[2]; float fTAWidth = ms_kSig.position.width - 40.0f; arrLO[0] = GUILayout.Width(fTAWidth); //arrLO[0] = GUILayout.Width(560); arrLO[1] = GUILayout.Height(100); GUI.SetNextControlName("RPCCmdInput"); m_strRawExeRpcCommand = EditorGUILayout.TextArea(m_strRawExeRpcCommand, arrLO); //m_strRpcCommand = GUILayout.TextArea(m_strRpcCommand, arrLO); GUILayout.EndHorizontal(); GUILayout.Space(10); GUILayout.BeginHorizontal(); float fBtnWidth = 100.0f; float fBtnHeight = 25.0f; float fBtnXOffset = 210.0f; float fXSpace = fBtnXOffset; GUILayout.Space(fXSpace); arrLO[0] = GUILayout.Width(fBtnWidth); arrLO[1] = GUILayout.Height(fBtnHeight); if (GUILayout.Button("Execute", arrLO)) { if (m_bCBInited == false) { if (EditorUtility.DisplayDialog("Erorr", "MagnaChain has not initialized", "OK")) { return; } } if (string.IsNullOrEmpty(m_strRawExeRpcCommand)) { if (EditorUtility.DisplayDialog("Prompt", "The rpc command is empty.", "OK")) { return; } } try { ResolveExeCommand(); RPCResponse kRsp = m_kCB.SendCommand(m_strExeRpcCommand, m_arrRpcArgs); if (kRsp == null) { LogError("Execute command error!"); } else { if (kRsp.Error == null) { LogSys(kRsp.ResultString); } else { LogError("Error: " + kRsp.Error.Code + " msg: " + kRsp.Error.Message); } } } catch (Exception e) { LogError(e.Message); } } float fXSpace2 = (ms_kSig.position.width * 0.5f - fBtnWidth - fBtnXOffset) * 2.0f; //fXSpace = fXSpace2 - fXSpace; GUILayout.Space(fXSpace2); if (GUILayout.Button("Clear", arrLO)) { m_strRawExeRpcCommand = ""; GUI.FocusControl(null); } GUILayout.EndHorizontal(); GUILayout.Space(20); GUILayout.BeginHorizontal(); GUILayout.Space(20.0f); GUILayout.Label("Template Contract:", GUILayout.Width(120.0f)); ms_iSelTemplateLuaFineIndex = EditorGUILayout.Popup(ms_iSelTemplateLuaFineIndex, ms_arrTemplateLuaFile, GUILayout.Width(200)); if (ms_arrTemplateLuaFile.Length > 0 && ms_iSelTemplateLuaFineIndex > 0) { ms_strSelTemplateLuaFile = ms_arrTemplateLuaFile[ms_iSelTemplateLuaFineIndex]; string strFullPath = ms_strTemplateLuaPrefix + ms_strSelTemplateLuaFile; if (strFullPath != m_strLuaFile) { m_strLuaFile = strFullPath; } } GUILayout.EndHorizontal(); GUILayout.Space(6); //m_strLuaFile = DrawFileChooser("CFLabe", "CFInput", "CFBtn", 20.0f, true, "Contract File:", 90.0f, m_strLuaFile, 220.0f, "", false, false, "lua", 70, 100); GUILayout.BeginHorizontal(); GUILayout.Space(20.0f); GUI.SetNextControlName("CFLabe"); GUILayout.Label("Contract File:", GUILayout.Width(90.0f)); // 120 //strFilePath = EditorGUILayout.TextField(strFilePath, GUILayout.Width(fTextFiledWidth)); // 330 float fTFWidth; fTFWidth = ms_kSig.position.width - 20.0f - 90.0f - 10.0f - 70.0f - 20.0f - 10.0f - 90.0f; //arrLO = new GUILayoutOption[2]; arrLO[0] = GUILayout.Height(18.0f); arrLO[1] = GUILayout.Width(fTFWidth); GUI.SetNextControlName("CFInput"); //strFilePath = GUILayout.TextField(strFilePath, 512, GUILayout.Width(fTextFiledWidth)); m_strLuaFile = EditorGUILayout.TextField(m_strLuaFile, arrLO); GUILayout.Space(10); arrLO[1] = GUILayout.Width(70.0f); GUI.SetNextControlName("CFBtn"); if (GUILayout.Button("Browser", arrLO)) { ms_iSelTemplateLuaFineIndex = 0; m_strLuaFile = EditorUtility.OpenFilePanel("Choose " + "Contract File:", "", "lua"); } arrLO[0] = GUILayout.Width(70.0f); fXSpace = 5.0f; GUILayout.Space(fXSpace); if (GUILayout.Button("Edit", arrLO)) { if (!string.IsNullOrEmpty(m_strLuaFile)) { Process process = new Process(); process.StartInfo.FileName = "notepad.exe"; process.StartInfo.Arguments = m_strLuaFile; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardInput = false; process.StartInfo.RedirectStandardOutput = false; process.StartInfo.RedirectStandardError = false; process.StartInfo.CreateNoWindow = true; process.Start(); m_bEditLuaFile = true; } } GUILayout.EndHorizontal(); GUILayout.Space(10); m_strCostCLAddress = DrawFileChooser("CCALabel", "CCAInput", "CCABtn", 20.0f, false, "Cost MCL Address:", 130.0f, m_strCostCLAddress, 320.0f, "", false, true); GUILayout.Space(5); m_strCostAmount = DrawFileChooser("CAXLabel", "CAXInput", "CAXBtn", 20.0f, false, "Cost Amount:", 130.0f, m_strCostAmount, 320.0f, "", false, true); GUILayout.Space(5); m_strSenderCLAddress = DrawFileChooser("CRALabel", "CRAInput", "CRABtn", 20.0f, false, "Sender MCL Address:", 130.0f, m_strSenderCLAddress, 320.0f, "", false, true); GUILayout.Space(10); GUILayout.BeginHorizontal(); fBtnWidth = 160.0f; arrLO[0] = GUILayout.Width(fBtnWidth); arrLO[1] = GUILayout.Height(25.0f); fXSpace = (ms_kSig.position.width - fBtnWidth) * 0.5f; GUILayout.Space(fXSpace); if (GUILayout.Button("Publish Contract", arrLO)) { if (m_bCBInited == false) { if (EditorUtility.DisplayDialog("Erorr", "MagnaChain has not initialized", "OK")) { return; } } if (string.IsNullOrEmpty(m_strLuaFile)) { if (EditorUtility.DisplayDialog("Prompt", "Please select contract file first.", "OK")) { return; } } if (m_bEditLuaFile) { if (EditorUtility.DisplayDialog("Prompt", "You may have modified contract file, please make sure the contract file have saved.", "Continue", "Cancel") == false) { return; } m_bEditLuaFile = false; } string strTxt = null; try { strTxt = File.ReadAllText(m_strLuaFile); } catch (Exception e) { LogError(e.Message); return; } if (string.IsNullOrEmpty(strTxt)) { EditorUtility.DisplayDialog("Prompt", "The contract content is empty!", "OK"); return; } float fCostAmount = 0.0f; try { fCostAmount = Convert.ToSingle(m_strCostAmount); } catch { fCostAmount = 0.0f; } bool bRet = m_kCB.PublishContract(out ms_strContractAddress, strTxt, m_strCostCLAddress, fCostAmount, m_strSenderCLAddress); if (bRet == false) { LogError("Publish contract failed: " + m_strLuaFile); return; } LogSys("Publish contract successfully, contract address: " + ms_strContractAddress); string strPrefix = Application.dataPath; string strKey = strPrefix + "#ms_strContractAddress"; PlayerPrefs.SetString(strKey, ms_strContractAddress); PlayerPrefs.Save(); } GUILayout.EndHorizontal(); GUILayout.Space(15); ms_strContractAddress = DrawFileChooser("CADLabel", "CADInput", "CADBtn", 20.0f, false, "Contract Address:", 120.0f, ms_strContractAddress, 330.0f, "", false, true); GUILayout.Space(5); m_strCallFunction = DrawFileChooser("CFLabel", "CFInput", "CFBtn", 20.0f, false, "Call Function:", 120.0f, m_strCallFunction, 330.0f, "", false, true); GUILayout.Space(5); m_strCallArgs = DrawFileChooser("CALabel", "CAInput", "CABtn", 20.0f, false, "Call Arguments:", 120.0f, m_strCallArgs, 330.0f, "", false, true); GUILayout.Space(15); GUILayout.BeginHorizontal(); float fTogWidth = 120.0f; arrLO[0] = GUILayout.Width(fTogWidth); float fTogX = 40.0f; GUILayout.Space(fTogX); m_bContractSendCall = GUILayout.Toggle(m_bContractSendCall, "Send Call", arrLO); fBtnWidth = 180.0f; arrLO[0] = GUILayout.Width(fBtnWidth); fXSpace = ms_kSig.position.width * 0.5f - fTogWidth - fTogX - fBtnWidth * 0.5f; GUILayout.Space(fXSpace); if (GUILayout.Button("Call Contract Function", arrLO)) { if (m_bCBInited == false) { if (EditorUtility.DisplayDialog("Erorr", "MagnaChain has not initialized", "OK")) { return; } } if (string.IsNullOrEmpty(ms_strContractAddress)) { if (EditorUtility.DisplayDialog("Prompt", "Please input contract address first.", "OK")) { return; } } if (string.IsNullOrEmpty(m_strCallFunction)) { if (EditorUtility.DisplayDialog("Prompt", "Please input call function name first.", "OK")) { return; } } float fCostAmount = 0.0f; try { fCostAmount = Convert.ToSingle(m_strCostAmount); } catch { fCostAmount = 0.0f; } object[] arrArg = null; if (!string.IsNullOrEmpty(m_strCallArgs)) { //char[] arrSpt = new char[1]; //arrSpt[0] = ' '; arrArg = System.Text.RegularExpressions.Regex.Split(m_strCallArgs, @"\s+");//m_strCallArgs.Split(arrSpt); } string strRet = m_kCB.CallContractFunction(m_bContractSendCall, ms_strContractAddress, m_strCostCLAddress, fCostAmount, m_strSenderCLAddress, m_strCallFunction, arrArg); if (string.IsNullOrEmpty(strRet)) { LogError("CallContractFunction failed!"); return; } LogSys("CallContractFunction succeed: " + strRet); } //fXSpace = ms_kSig.position.width - fBtnOffset * 2.0f - fBtnWidth * 2.0f; //GUILayout.Space(fXSpace); //if (GUILayout.Button("SendCall Contract", arrLO)) //{ // if (m_bCBInited == false) // { // if (EditorUtility.DisplayDialog("Erorr", "MagnaChain has not initialized", "OK")) // { // return; // } // } // if (string.IsNullOrEmpty(m_strContractAddress)) // { // if (EditorUtility.DisplayDialog("Prompt", "Please publish contract first.", "OK")) // { // return; // } // } // if (string.IsNullOrEmpty(m_strCallFunction)) // { // if (EditorUtility.DisplayDialog("Prompt", "Please input call function name first.", "OK")) // { // return; // } // } // string strRet = m_kCB.SendCallContract(m_strContractAddress, m_strCallFunction, m_strSenderAddress, m_strCallArgs); // if (string.IsNullOrEmpty(strRet)) // { // LogError("SendCallContract failed: " + m_strContractAddress); // return; // } // LogSys("SendCallContract succeed: " + strRet); //} GUILayout.EndHorizontal(); }
public CcjCoordinator(Network network, string folderPath, RPCClient rpc, CcjRoundConfig roundConfig) { Network = Guard.NotNull(nameof(network), network); FolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(folderPath), folderPath, trim: true); RpcClient = Guard.NotNull(nameof(rpc), rpc); RoundConfig = Guard.NotNull(nameof(roundConfig), roundConfig); Rounds = new List <CcjRound>(); RoundsListLock = new AsyncLock(); CoinJoins = new List <uint256>(); UnconfirmedCoinJoins = new List <uint256>(); CoinJoinsLock = new AsyncLock(); Directory.CreateDirectory(FolderPath); UtxoReferee = new UtxoReferee(Network, FolderPath, RpcClient, RoundConfig); // Initialize RsaKey string rsaKeyPath = Path.Combine(FolderPath, "RsaKey.json"); if (File.Exists(rsaKeyPath)) { string rsaKeyJson = File.ReadAllText(rsaKeyPath, encoding: Encoding.UTF8); RsaKey = BlindingRsaKey.CreateFromJson(rsaKeyJson); } else { RsaKey = new BlindingRsaKey(); File.WriteAllText(rsaKeyPath, RsaKey.ToJson(), encoding: Encoding.UTF8); Logger.LogInfo <CcjCoordinator>($"Created RSA key at: {rsaKeyPath}"); } if (File.Exists(CoinJoinsFilePath)) { try { var toRemove = new List <string>(); string[] allLines = File.ReadAllLines(CoinJoinsFilePath); foreach (string line in allLines) { try { uint256 txHash = new uint256(line); RPCResponse getRawTransactionResponse = RpcClient.SendCommand(RPCOperations.getrawtransaction, txHash.ToString(), true); CoinJoins.Add(txHash); if (getRawTransactionResponse.Result.Value <int>("confirmations") <= 0) { UnconfirmedCoinJoins.Add(txHash); } } catch (Exception ex) { toRemove.Add(line); var logEntry = ex is RPCException rpce && rpce.RPCCode == RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY ? $"CoinJoins file contains invalid transaction ID {line}" : $"CoinJoins file got corrupted. Deleting offending line \"{line.Substring(0, 20)}\"."; Logger.LogWarning <CcjCoordinator>($"{logEntry}. {ex.GetType()}: {ex.Message}"); } } if (toRemove.Count != 0) // a little performance boost, it'll be empty almost always { var newAllLines = allLines.Where(x => !toRemove.Contains(x)); File.WriteAllLines(CoinJoinsFilePath, newAllLines); } } catch (Exception ex) { Logger.LogWarning <CcjCoordinator>($"CoinJoins file got corrupted. Deleting {CoinJoinsFilePath}. {ex.GetType()}: {ex.Message}"); File.Delete(CoinJoinsFilePath); } } }
public async Task GetTransactionOnUnconfirmedTransactionAsync() { using (NodeBuilder builder = NodeBuilder.Create(this)) { // Arrange. // Create a sending and a receiving node. CoreNode sendingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Miner).Start(); CoreNode receivingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Listener).Start(); TestHelper.ConnectAndSync(sendingNode, receivingNode); // Get an address to send to. IEnumerable <string> unusedaddresses = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/unusedAddresses") .SetQueryParams(new { walletName = "mywallet", accountName = "account 0", count = 1 }) .GetJsonAsync <IEnumerable <string> >(); // Build and send the transaction with an Op_Return. WalletBuildTransactionModel buildTransactionModel = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/build-transaction") .PostJsonAsync(new BuildTransactionRequest { WalletName = "mywallet", AccountName = "account 0", FeeType = "low", Password = "******", ShuffleOutputs = false, AllowUnconfirmed = true, Recipients = unusedaddresses.Select(address => new RecipientModel { DestinationAddress = address, Amount = "1" }).ToList(), }) .ReceiveJson <WalletBuildTransactionModel>(); await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/send-transaction") .PostJsonAsync(new SendTransactionRequest { Hex = buildTransactionModel.Hex }) .ReceiveJson <WalletSendTransactionModel>(); uint256 txId = buildTransactionModel.TransactionId; TestBase.WaitLoop(() => { WalletHistoryModel history = $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/history") .SetQueryParams(new { walletName = "mywallet", AccountName = "account 0" }) .GetAsync() .ReceiveJson <WalletHistoryModel>().GetAwaiter().GetResult(); return(history.AccountsHistoryModel.First().TransactionsHistory.Any(h => h.Id == txId)); }); TestBase.WaitLoop(() => { WalletHistoryModel history = $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/history") .SetQueryParams(new { walletName = "mywallet", AccountName = "account 0" }) .GetAsync() .ReceiveJson <WalletHistoryModel>().GetAwaiter().GetResult(); return(history.AccountsHistoryModel.First().TransactionsHistory.Any(h => h.Id == txId)); }); Transaction trx = this.network.Consensus.ConsensusFactory.CreateTransaction(buildTransactionModel.Hex); RPCClient rpcReceivingNode = receivingNode.CreateRPCClient(); RPCResponse txReceivingWallet = rpcReceivingNode.SendCommand(RPCOperations.gettransaction, txId.ToString()); RPCClient rpcSendingNode = sendingNode.CreateRPCClient(); RPCResponse txSendingWallet = rpcSendingNode.SendCommand(RPCOperations.gettransaction, txId.ToString()); // Assert. GetTransactionModel resultSendingWallet = txSendingWallet.Result.ToObject <GetTransactionModel>(); resultSendingWallet.Amount.Should().Be((decimal) - 97999999.9999); resultSendingWallet.Fee.Should().Be((decimal) - 0.0001); resultSendingWallet.Confirmations.Should().Be(0); resultSendingWallet.TransactionId.Should().Be(txId); resultSendingWallet.BlockHash.Should().BeNull(); resultSendingWallet.BlockIndex.Should().BeNull(); resultSendingWallet.BlockTime.Should().BeNull(); resultSendingWallet.TimeReceived.Should().BeGreaterThan((DateTimeOffset.Now - TimeSpan.FromMinutes(1)).ToUnixTimeSeconds()); resultSendingWallet.Details.Count.Should().Be(2); GetTransactionDetailsModel detailsSendingWallet = resultSendingWallet.Details.Last(); detailsSendingWallet.Address.Should().Be(unusedaddresses.Single()); detailsSendingWallet.Amount.Should().Be((decimal) - 1.00000000); detailsSendingWallet.Fee.Should().Be((decimal) - 0.0001); detailsSendingWallet.Category.Should().Be(GetTransactionDetailsCategoryModel.Send); detailsSendingWallet.OutputIndex.Should().Be(1); // The output at index 0 is the change. GetTransactionModel resultReceivingWallet = txReceivingWallet.Result.ToObject <GetTransactionModel>(); resultReceivingWallet.Amount.Should().Be((decimal)1.00000000); resultReceivingWallet.Fee.Should().BeNull(); resultReceivingWallet.Confirmations.Should().Be(0); resultReceivingWallet.TransactionId.Should().Be(txId); resultReceivingWallet.BlockHash.Should().BeNull(); resultReceivingWallet.BlockIndex.Should().BeNull(); resultReceivingWallet.BlockTime.Should().BeNull(); resultReceivingWallet.TimeReceived.Should().BeGreaterThan((DateTimeOffset.Now - TimeSpan.FromMinutes(1)).ToUnixTimeSeconds()); resultReceivingWallet.TransactionTime.Should().BeGreaterThan((DateTimeOffset.Now - TimeSpan.FromMinutes(1)).ToUnixTimeSeconds()); resultReceivingWallet.Details.Should().ContainSingle(); GetTransactionDetailsModel detailsReceivingWallet = resultReceivingWallet.Details.Single(); detailsReceivingWallet.Address.Should().Be(unusedaddresses.Single()); detailsReceivingWallet.Amount.Should().Be((decimal)1.00000000); detailsReceivingWallet.Fee.Should().BeNull(); detailsReceivingWallet.Category.Should().Be(GetTransactionDetailsCategoryModel.Receive); detailsReceivingWallet.OutputIndex.Should().Be(1); } }
/// <summary> /// Upload a file to your Brightcove account /// </summary> /// <param name="video"> /// The metadata for the video you'd like to create. This takes the form of a /// JSON object of name/value pairs, each of which corresponds to a settable /// property of the Video object. /// </param> /// <param name="filename"> /// The name of the file that's being uploaded. You don't need to specify this in /// the JSON if it is specified in the file part of the POST. /// </param> /// <param name="file"> /// A byte array of the video file you're uploading. This takes the /// form of a file part, in a multipart/form-data HTTP request. This input stream and /// the filename and maxSide parameters are automatically inferred from that file part. /// </param> /// <param name="encode_to"> /// If the file requires transcoding, use this parameter to specify the target encoding. Valid /// values are MP4 or FLV, representing the H264 and VP6 codecs respectively. Note that transcoding /// of FLV files to another codec is not currently supported. This parameter is optional and defaults to FLV. /// </param> /// <param name="create_multiple_renditions"> /// If the file is a supported transcodeable type, this optional flag can be used to control the /// number of transcoded renditions. If true (default), multiple renditions at varying encoding /// rates and dimensions are created. Setting this to false will cause a single transcoded VP6 /// rendition to be created at the standard encoding rate and dimensions. /// </param> /// <param name="H264NoProcessing"> /// If the video file is H.264 encoded and if create_multiple_ renditions=true, then multiple /// VP6 renditions are created and in addition the H.264 source is retained as an additional rendition. /// </param> /// <param name="preserve_source_rendition"> /// Use this option to prevent H.264 source files from being transcoded. This parameter cannot be /// used in combination with create_multiple_renditions. It is optional and defaults to false. /// </param> /// <param name="maxsize"> /// The maximum size that the file will be. This is used as a limiter to know when /// something has gone wrong with the upload. The maxSize is same as the file you uploaded. /// You don't need to specify this in the JSON if it is specified in the file part of the POST. /// </param> /// <param name="file_checksum"> /// An optional MD5 hash of the file. The checksum can be used to verify that the file checked /// into your Brightcove Media Library is the same as the file you uploaded. /// </param> /// <returns> /// The id of the video that's been created. if null or error returns -1 /// </returns> private RPCResponse <long> CreateVideo(BCVideo video, string filename, byte[] file, BCEncodeType encode_to, bool create_multiple_renditions, bool H264NoProcessing, bool preserve_source_rendition, long maxsize, string file_checksum, string fileFullPath) { // Generate post objects Dictionary <string, object> postParams = new Dictionary <string, object>(); //add video to the post params RPCRequest rpc = new RPCRequest(); rpc.method = "create_video"; rpc.parameters = "\"video\": " + video.ToCreateJSON() + ", \"token\": \"" + Account.WriteToken.Value + "\""; if (maxsize > -1) { rpc.parameters += ", \"maxsize\": " + maxsize.ToString(); } if (file_checksum != null) { rpc.parameters += ", \"file_checksum\": \"" + file_checksum + "\""; } if (!encode_to.Equals(BCEncodeType.UNDEFINED)) { rpc.parameters += ", \"encode_to\": " + encode_to.ToString(); } rpc.parameters += ", \"create_multiple_renditions\": " + create_multiple_renditions.ToString().ToLower(); rpc.parameters += ", \"H264NoProcessing\": " + H264NoProcessing.ToString().ToLower(); rpc.parameters += ", \"preserve_source_rendition\": " + preserve_source_rendition.ToString().ToLower(); postParams.Add("json", rpc.ToJSON()); //add the file to the post if (!string.IsNullOrEmpty(filename)) { rpc.parameters += ", \"filename\": \"" + filename + "\""; } if (!string.IsNullOrEmpty(filename) && file != null) { postParams.Add("file", new FileParameter(file, filename)); } else if (!string.IsNullOrEmpty(fileFullPath)) { postParams.Add("file", new UploadFileParameter(fileFullPath)); } //Get the JSon reader returned from the APIRequest RPCResponse rpcr = string.IsNullOrEmpty(fileFullPath) ? BCAPIRequest.ExecuteWrite(postParams, this.Account) : BCAPIRequest.ExecuteWriteNew(postParams, this.Account); RPCResponse <long> rpcr2 = new RPCResponse <long>(); rpcr2.error = rpcr.error; rpcr2.id = rpcr.id; if (!string.IsNullOrEmpty(rpcr.result)) { rpcr2.result = long.Parse(rpcr.result); } else { rpcr2.result = -1; } return(rpcr2); }
IEnumerator FetchTile(Vector2 index) { Vector3 pos = indexToPosition(index); // Temporal Placeholder GameObject plane = Instantiate(baseTile, pos, Quaternion.identity); GameObject loader = Instantiate(loading, pos, Quaternion.identity); loader.transform.position = new Vector3(pos.x, pos.y + 2, pos.z); // Basic Auth Dictionary <string, string> headers = new Dictionary <string, string>(); headers["Authorization"] = "Basic " + System.Convert.ToBase64String( System.Text.Encoding.ASCII.GetBytes("bitcoinrpc:38Dpwnjsj1zn3QETJ6GKv8YkHomA")); // JSON Data string json = "{\"method\":\"gettile\",\"params\":[" + index [0] + "," + index [1] + "],\"id\":0}"; byte[] data = System.Text.Encoding.ASCII.GetBytes(json.ToCharArray()); WWW www = new WWW("http://s1.decentraland.org:8301", data, headers); yield return(www); if (string.IsNullOrEmpty(www.error)) { RPCResponse response = JsonUtility.FromJson <RPCResponse>(www.text); Destroy(loader); if (response.IsEmpty()) { // TODO: do empty behavior } else if (response.IsUnmined()) { names.Add(index, "Unclaimed Land"); } else if (response.HasData()) { // Download tile content string fileName = "" + index [0] + "." + index [1] + ".lnd"; www = new WWW("http://s1.decentraland.org:9301/tile/" + fileName); yield return(www); if (string.IsNullOrEmpty(www.error)) { Debug.Log("Downloaded content for tile (" + index[0] + "," + index[1] + ")"); STile t = STile.FromBytes(www.bytes); t.ToInstance(pos); names.Add(index, t.GetName()); } else { Debug.Log("Can't fetch tile content! " + index + " " + www.error); } } } else { Debug.Log("Error on RPC call 'gettile': " + www.error); } }
protected void Page_Load(object sender, EventArgs e) { //available querystring values for context info (id, language, version, database) try { //get the current item and database currentDB = Sitecore.Configuration.Factory.GetDatabase("master"); currentItem = new VideoItem(currentDB.Items[HttpContext.Current.Request.QueryString["id"].ToString()]); Item acct = currentItem.videoItem.Parent.Parent; //if current parent isn't the account then it's the parent of the folder if (!acct.TemplateName.Equals("Account Folder")) { acct = acct.Parent; } accountItem = new AccountItem(acct.ID, acct.InnerData, acct.Database); bc = new BCAPI(accountItem.PublisherID); //set the form values for the video if (!IsPostBack) { loadFormWithCurrentValues(); } //show the video id ltlVideoID.Text = currentItem.VideoID.ToString(); //show the video upload status RPCResponse <UploadStatusEnum> rpcr = bc.GetUploadStatus(currentItem.VideoID); ltlStatus.Text = rpcr.result.ToString(); ltlCreation.Text = currentItem.CreationDate.ToString("MMMM d, yyyy"); ltlModified.Text = currentItem.LastModifiedDate.ToString("MMMM d, yyyy"); ltlPublished.Text = currentItem.PublishedDate.ToString("MMMM d, yyyy"); try { long milliseconds = currentItem.Length; string lengthText = ""; long hours = (milliseconds / 60000000); if (hours >= 1) { milliseconds -= hours * 60000000; lengthText += hours + " hours, "; } long mins = (milliseconds / 60000); if (mins >= 1) { milliseconds -= mins * 60000; lengthText += mins + " minutes, "; } long secs = (milliseconds / 1000); if (secs >= 1) { milliseconds -= secs * 1000; lengthText += secs + " seconds"; } ltlLength.Text = lengthText; } catch (Exception ex) { } ltlPlays.Text = currentItem.PlaysTotal.ToString(); ltlPlaysTrailing.Text = currentItem.PlaysTrailingWeek.ToString(); } catch (Exception ex) { ltlError.Text = ex.ToString(); } }
/// <summary> /// The expected sequence of arguments: /// <list> /// <item> /// 1, [network-name] [options] [rpc-command] [rpc-params]. /// </item> /// <item> /// 2, [network-name] [options] [api-controller "/" api-command] [api-params]. /// </item> /// </list> /// </summary> public static void Main(string[] args) { try { // Preprocess the command line arguments var argList = new List <string>(args); string networkName = null; if (argList.Any()) { networkName = argList.First(); argList.RemoveAt(0); } var optionList = new List <string>(); while ((argList.Any()) && (argList[0].StartsWith('-'))) { optionList.Add(argList[0]); argList.RemoveAt(0); } string command = string.Empty; if (argList.Any()) { command = argList.First(); argList.RemoveAt(0); } var commandArgList = new List <string>(argList); // Display help if required. if (optionList.Contains("-help") || optionList.Contains("--help") || string.IsNullOrWhiteSpace(command)) { var builder = new StringBuilder(); builder.AppendLine("Usage:"); builder.AppendLine(" dotnet run <Stratis.Bitcoin.Cli/Stratis.Bitcoin.Cli.dll> [network-name] [options] <command> [arguments]"); builder.AppendLine(); builder.AppendLine("Command line arguments:"); builder.AppendLine(); builder.AppendLine("[network-name] Name of the network - e.g. \"stratis\", \"stratismain\", \"stratistest\", \"bitcoinmain\", \"bitcointest\"."); builder.AppendLine("[options] Options for the CLI (optional) - e.g. -help, -rpcuser, see below."); builder.AppendLine("[command] Name of RPC method or API <controller>/<method>."); builder.AppendLine("[arguments] Argument by position (RPC) or Name = Value pairs (API) (optional)."); builder.AppendLine(); builder.AppendLine("Options:"); builder.AppendLine("-help This help message"); builder.AppendLine("-rpcconnect=<ip> Send commands to node running on <ip> (default: 127.0.0.1)"); builder.AppendLine("-rpcport=<port> Connect to JSON-RPC on <port> (default for Stratis: 26174 or default for Bitcoin: 8332)"); builder.AppendLine("-rpcuser=<user> Username for JSON-RPC connections"); builder.AppendLine("-rpcpassword=<pw> Password for JSON-RPC connections"); builder.AppendLine(); builder.AppendLine("Examples:"); builder.AppendLine(); builder.AppendLine("dotnet run stratis Wallet/history WalletName=testwallet - Lists all the historical transactions of the wallet called 'testwallet'."); builder.AppendLine("dotnet run stratis getinfo -rpcuser=stratistestuser -rpcpassword=stratistestpassword -rpcconnect=127.0.0.3 -rpcport=26174 - Displays general information about the Stratis node on the 127.0.0.3:26174, authenticating with the RPC specified user."); builder.AppendLine("dotnet run bitcoin getbalance -rpcuser=btctestuser -rpcpassword=btctestpass - Displays the current balance of the opened wallet on the 127.0.0.1:8332 node, authenticating with the RPC specified user."); Console.WriteLine(builder); return; } // Determine API port. int defaultRestApiPort = 0; Network network = null; if (networkName.Contains("stratis")) { defaultRestApiPort = 37221; network = Network.StratisMain; } else { defaultRestApiPort = 37220; network = Network.Main; } // API calls require both the contoller name and the method name separated by "/". // If this is not an API call then assume it is an RPC call. if (!command.Contains("/")) { // Process RPC call. try { var options = optionList.Append("-server").ToArray(); NodeSettings nodeSettings = new NodeSettings(network, args: options); var rpcSettings = new RpcSettings(nodeSettings); // Find the binding to 127.0.0.1 or the first available. The logic in RPC settings ensures there will be at least 1. System.Net.IPEndPoint nodeEndPoint = rpcSettings.Bind.FirstOrDefault(b => b.Address.ToString() == "127.0.0.1") ?? rpcSettings.Bind[0]; Uri rpcUri = new Uri($"http://{nodeEndPoint}"); // Process the command line RPC arguments // TODO: this should probably be moved to the NodeSettings.FromArguments if (options.GetValueOf("-rpcbind") != null) { rpcUri = new Uri($"http://{options.GetValueOf("-rpcbind")}"); } if (options.GetValueOf("-rpcconnect") != null || options.GetValueOf("-rpcport") != null) { string rpcAddress = options.GetValueOf("-rpcconnect") ?? "127.0.0.1"; int rpcPort = rpcSettings.RPCPort; int.TryParse(options.GetValueOf("-rpcport"), out rpcPort); rpcUri = new Uri($"http://{rpcAddress}:{rpcPort}"); } rpcSettings.RpcUser = options.GetValueOf("-rpcuser") ?? rpcSettings.RpcUser; rpcSettings.RpcPassword = options.GetValueOf("-rpcpassword") ?? rpcSettings.RpcPassword; Console.WriteLine($"Connecting to the following RPC node: http://{rpcSettings.RpcUser}:{rpcSettings.RpcPassword}@{rpcUri.Authority}..."); // Initialize the RPC client with the configured or passed userid, password and endpoint. var rpcClient = new RPCClient($"{rpcSettings.RpcUser}:{rpcSettings.RpcPassword}", rpcUri, network); // Execute the RPC command Console.WriteLine($"Sending RPC command '{command} {string.Join(" ", commandArgList)}' to '{rpcUri}'..."); RPCResponse response = rpcClient.SendCommand(command, commandArgList.ToArray()); // Return the result as a string to the console. Console.WriteLine(response.ResultString); } catch (Exception err) { Console.WriteLine(err.Message); } } else { // Process API call. using (var client = new HttpClient()) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var url = $"http://localhost:{defaultRestApiPort}/api/{command}"; if (commandArgList.Any()) { url += $"?{string.Join("&", commandArgList)}"; } try { // Get the response. Console.WriteLine($"Sending API command to {url}..."); var response = client.GetStringAsync(url).GetAwaiter().GetResult(); // Format and return the result as a string to the console. Console.WriteLine(JsonConvert.SerializeObject(JsonConvert.DeserializeObject <object>(response), Formatting.Indented)); } catch (Exception err) { Console.WriteLine(ExceptionToString(err)); } } } } catch (Exception err) { // Report any errors to the console. Console.WriteLine(ExceptionToString(err)); } }
void PublishTile(Vector2 index, string content) { publishing = true; publishError = false; published = false; // Basic Auth Dictionary <string, string> headers = new Dictionary <string, string>(); headers["Authorization"] = "Basic " + System.Convert.ToBase64String( System.Text.Encoding.ASCII.GetBytes("bitcoinrpc:" + nodeAuth) ); // TODO: fix compression efforts // byte[] binaryContent = System.Text.Encoding.ASCII.GetBytes(content); // byte[] compressedBinaryContent = CLZF2.Compress(binaryContent); // Debug.Log("Content length:"); // Debug.Log(binaryContent.Length); // Debug.Log("Content length compressed:"); // Debug.Log(compressedBinaryContent.Length); // string json = "{\"method\":\"settile\",\"params\":[" + index [0] + "," + index [1] + ",\"" + compressedBinaryContent + "\"],\"id\":0}"; string json = "{\"method\":\"settile\",\"params\":[" + index[0] + "," + index[1] + ",\"" + content + "\"],\"id\":0}"; byte[] data = System.Text.Encoding.ASCII.GetBytes(json.ToCharArray()); WWW www = new WWW(nodeAddress, data, headers); while (!www.isDone) { } // busy wait // Web transaction error if (!string.IsNullOrEmpty(www.error)) { Debug.Log("Error publishing tile! Web error: " + www.error); publishError = true; } else { // Process RPC response string responseJson = www.text; Debug.Log(responseJson); RPCResponse response = JsonUtility.FromJson <RPCResponse>(responseJson); // RPC error response /* * NOTE: Not great as it assumes code 0 is success, nowhere guaranteed in JSONRPC or the bitcoin API. * Artifact of the JsonUtility deserialization which generates an instance for a `null` value in the JSON. * Should not be done this way in production. */ if (response.error != null && response.error.code != 0) { Debug.Log("Error publishing tile! RPC error: " + response.error.message); publishError = true; } else { Debug.Log("Successfully published tile!"); published = true; } } publishing = false; }
public CcjCoordinator(Network network, TrustedNodeNotifyingBehavior trustedNodeNotifyingBehavior, string folderPath, RPCClient rpc, CcjRoundConfig roundConfig) { Network = Guard.NotNull(nameof(network), network); TrustedNodeNotifyingBehavior = Guard.NotNull(nameof(trustedNodeNotifyingBehavior), trustedNodeNotifyingBehavior); FolderPath = Guard.NotNullOrEmptyOrWhitespace(nameof(folderPath), folderPath, trim: true); RpcClient = Guard.NotNull(nameof(rpc), rpc); RoundConfig = Guard.NotNull(nameof(roundConfig), roundConfig); Rounds = new List <CcjRound>(); RoundsListLock = new AsyncLock(); CoinJoins = new List <uint256>(); CoinJoinsLock = new AsyncLock(); Directory.CreateDirectory(FolderPath); UtxoReferee = new UtxoReferee(Network, FolderPath, RpcClient, RoundConfig); if (File.Exists(CoinJoinsFilePath)) { try { var toRemove = new List <string>(); string[] allLines = File.ReadAllLines(CoinJoinsFilePath); foreach (string line in allLines) { try { uint256 txHash = new uint256(line); RPCResponse getRawTransactionResponse = RpcClient.SendCommand(RPCOperations.getrawtransaction, txHash.ToString(), true); CoinJoins.Add(txHash); } catch (Exception ex) { toRemove.Add(line); var logEntry = ex is RPCException rpce && rpce.RPCCode == RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY ? $"CoinJoins file contains invalid transaction ID {line}" : $"CoinJoins file got corrupted. Deleting offending line \"{line.Substring(0, 20)}\"."; Logger.LogWarning <CcjCoordinator>($"{logEntry}. {ex.GetType()}: {ex.Message}"); } } if (toRemove.Count != 0) // a little performance boost, it'll be empty almost always { var newAllLines = allLines.Where(x => !toRemove.Contains(x)); File.WriteAllLines(CoinJoinsFilePath, newAllLines); } } catch (Exception ex) { Logger.LogWarning <CcjCoordinator>($"CoinJoins file got corrupted. Deleting {CoinJoinsFilePath}. {ex.GetType()}: {ex.Message}"); File.Delete(CoinJoinsFilePath); } } try { string roundCountFilePath = Path.Combine(folderPath, "RoundCount.txt"); if (File.Exists(roundCountFilePath)) { string roundCount = File.ReadAllText(roundCountFilePath); CcjRound.RoundCount = long.Parse(roundCount); } else { // First time initializes (so the first constructor will increment it and we'll start from 1.) CcjRound.RoundCount = 0; } } catch (Exception ex) { CcjRound.RoundCount = 0; Logger.LogInfo <CcjCoordinator>($"{nameof(CcjRound.RoundCount)} file was corrupt. Resetting to 0."); Logger.LogDebug <CcjCoordinator>(ex); } TrustedNodeNotifyingBehavior.Block += TrustedNodeNotifyingBehavior_BlockAsync; }
public static async Task <RPCCapabilities> TestRPCAsync(NBXplorerNetwork networkInfo, RPCClient rpcClient, CancellationToken cancellation) { var network = networkInfo.NBitcoinNetwork; Logs.Configuration.LogInformation($"{networkInfo.CryptoCode}: Testing RPC connection to " + rpcClient.Address.AbsoluteUri); RPCResponse blockchainInfo = null; try { int time = 0; retry: try { blockchainInfo = await rpcClient.SendCommandAsync("getblockchaininfo"); blockchainInfo.ThrowIfError(); } catch (RPCException ex) when(IsTransient(ex)) { Logs.Configuration.LogInformation($"{networkInfo.CryptoCode}: Transient error '{ex.Message}', retrying soon..."); time++; await Task.Delay(Math.Min(1000 * time, 10000), cancellation); goto retry; } } catch (ConfigException) { throw; } catch (RPCException ex) { Logs.Configuration.LogError($"{networkInfo.CryptoCode}: Invalid response from RPC server " + ex.Message); throw new ConfigException(); } catch (Exception ex) { Logs.Configuration.LogError($"{networkInfo.CryptoCode}: Error connecting to RPC server " + ex.Message); throw new ConfigException(); } Logs.Configuration.LogInformation($"{networkInfo.CryptoCode}: RPC connection successful"); if (blockchainInfo?.Result["chain"]?.Value <string>() is string rpcChain) { List <string> expectedNames = new List <string>(); expectedNames.Add(network.ChainName.ToString()); expectedNames.Add(network.ChainName.ToString().Replace("net", string.Empty, StringComparison.OrdinalIgnoreCase)); if (!expectedNames.Any(e => rpcChain.Equals(e, StringComparison.OrdinalIgnoreCase))) { Logs.Configuration.LogError($"{networkInfo.CryptoCode}: NBXplorer expected chain is '{network.ChainName}', but the RPC node is running '{rpcChain}'"); throw new ConfigException(); } } var capabilities = await rpcClient.ScanRPCCapabilitiesAsync(); if (capabilities.Version < networkInfo.MinRPCVersion) { Logs.Configuration.LogError($"{networkInfo.CryptoCode}: The minimum node version required is {networkInfo.MinRPCVersion} (detected: {capabilities.Version})"); throw new ConfigException(); } Logs.Configuration.LogInformation($"{networkInfo.CryptoCode}: Full node version detected: {capabilities.Version}"); return(capabilities); }
private static async Task AssertRpcNodeFullyInitializedAsync() { try { var blockchainInfoRequest = new RPCRequest(RPCOperations.getblockchaininfo, parameters: null); RPCResponse blockchainInfo = await RpcClient.SendCommandAsync(blockchainInfoRequest, throwIfRPCError : true); if (string.IsNullOrWhiteSpace(blockchainInfo?.ResultString)) // should never happen { throw new NotSupportedException("string.IsNullOrWhiteSpace(blockchainInfo?.ResultString) == true"); } int blocks = blockchainInfo.Result.Value <int>("blocks"); if (blocks == 0 && Config.Network != Network.RegTest) { throw new NotSupportedException("blocks == 0"); } int headers = blockchainInfo.Result.Value <int>("headers"); if (headers == 0 && Config.Network != Network.RegTest) { throw new NotSupportedException("headers == 0"); } if (blocks != headers) { throw new NotSupportedException("Bitcoin Core is not fully synchronized."); } Logger.LogInfo <RPCClient>("Bitcoin Core is fully synchronized."); if (Config.Network != Network.RegTest) // RegTest cannot estimate fees. { var estimateSmartFeeResponse = await RpcClient.TryEstimateSmartFeeAsync(2, EstimateSmartFeeMode.Conservative); if (estimateSmartFeeResponse == null) { throw new NotSupportedException($"Bitcoin Core cannot estimate network fees yet."); } Logger.LogInfo <RPCClient>("Bitcoin Core fee estimation is working."); } else // Make sure there's at least 101 block, if not generate it { if (blocks < 101) { var generateBlocksResponse = await RpcClient.GenerateAsync(101); if (generateBlocksResponse == null) { throw new NotSupportedException($"Bitcoin Core cannot cannot generate blocks on the RegTest."); } blockchainInfoRequest = new RPCRequest(RPCOperations.getblockchaininfo, parameters: null); blockchainInfo = await RpcClient.SendCommandAsync(blockchainInfoRequest, throwIfRPCError : true); blocks = blockchainInfo.Result.Value <int>("blocks"); if (blocks == 0) { throw new NotSupportedException("blocks == 0"); } Logger.LogInfo <RPCClient>($"Generated 101 block on RegTest. Number of blocks {blocks}."); } } } catch (WebException) { Logger.LogInfo($"Bitcoin Core is not running, or incorrect RPC credentials or network is given in the config file: `{ConfigFilePath}`."); throw; } }
public override void Deprecated_Page_Load() { long vid = 0; bool doFormLoad = true; vcErrorCode = "0";; if (!this.IsAdmin) { message.InnerHtml = "<p class=\"error\">ERROR: user " + this.CurrentUser.LoginName + " is not an administrator</p>"; doFormLoad = false; } long.TryParse(Request["videoid"], out vid); this.Page.Form.Enctype = "multipart/form-data"; // Check for deletion if (Request.Form["btnCancel"] == "Cancel") { doFormLoad = false; } else if (this.IsAdmin && (vid > 0) && (Request.Form["btnDelete"] == "Delete")) { RPCResponse response = this.bcApi.DeleteVideo(vid); if (string.IsNullOrEmpty(response.result)) { message.InnerHtml = "<p class=\"success\">Your changes have been saved. Allow up to five minutes for the changes to propagate through the Brightcove Video Cloud.</p>"; } else { message.InnerHtml = "<p class=\"error\">ERROR: Your changes could not be saved. " + response.result + "</p>"; } doFormLoad = false; } else { this.SetVideo(vid); } if (doFormLoad && this.IsPostBack) { int fileIndex = 1; HttpPostedFile file = null; file = Request.Files["file" + fileIndex.ToString()]; if ((file != null) && !string.IsNullOrEmpty(file.FileName)) { message.InnerHtml = string.Empty; do { // Check for 2GB or smaller file size long maxSize = (long)2 * 1024 * 1024 * 1024; if (file.InputStream.Length > maxSize) { message.InnerHtml = "<p class=\"error\">ERROR: File size is greater than 2GB</p>"; } else { byte[] buffer = new byte[file.InputStream.Length]; file.InputStream.Read(buffer, 0, buffer.Length); if (vid <= 0) { RPCResponse <long> response = null; for (int i = 0; (i < this.writeTokenList.Length) && (i < 5); i++) { response = this.bcApi.CreateVideo(this.video, file.FileName, buffer, BCEncodeType.UNDEFINED, true); // Detect error: 213, ConcurrentWritesExceededError if (response.error.code == "213") { this.accountConfig.WriteToken.Value = writeTokenList[i]; this.ResetApiConnection(); } else { break; } } if (response.result <= 0) { vid = 0; message.InnerHtml += "<p class=\"error\">ERROR: " + response.error.message + "</p>"; vcErrorCode = response.error.code; } else { vid = Convert.ToInt64(response.result); this.video.id = vid; message.InnerHtml += "SUCCESS - " + vid.ToString() + "<br />"; // Update thumbnail image if (!this.AddImage(Request.Files["file_thumbnailUrl"], ImageTypeEnum.THUMBNAIL, this.video.thumbnailURL)) { message.InnerHtml += ", thumbnail file upload failed"; } // Update video still image if (!this.AddImage(Request.Files["file_videoStillUrl"], ImageTypeEnum.VIDEO_STILL, this.video.videoStillURL)) { message.InnerHtml += ", video still file upload failed"; } } // Reset the video ID vid = 0; } } fileIndex++; file = Request.Files["file" + fileIndex.ToString()]; } while ((file != null) && !string.IsNullOrEmpty(file.FileName)); } else { if (!string.IsNullOrEmpty(this.video.name) && !string.IsNullOrEmpty(this.video.shortDescription)) { // Only update the video if valid this.bcApi.UpdateVideo(this.video); message.InnerHtml = "SUCCESS - " + vid.ToString(); } } if (vid > 0) { // Update thumbnail image if (!this.AddImage(Request.Files["file_thumbnailUrl"], ImageTypeEnum.THUMBNAIL, this.video.thumbnailURL)) { message.InnerHtml += ", thumbnail file upload failed"; } // Update video still image if (!this.AddImage(Request.Files["file_videoStillUrl"], ImageTypeEnum.VIDEO_STILL, this.video.videoStillURL)) { message.InnerHtml += ", video still file upload failed"; } } } if (this.video != null) { if (string.IsNullOrEmpty(this.video.name)) { this.video.name = " "; } string output = string.Format( @"<script language=""javascript"" type=""text/javascript"" charset=""utf-8"" defer=""defer"">/*<![CDATA[*/ var vcVideoResult = {0}; var vcIsAdmin = {1}; var vcErrorCode = {2}; if (vcVideoResult != null) {{ var video = document.forms[0]; if (vcVideoResult.name) {{ if (vcVideoResult.name == ' ') {{ vcVideoResult.name = ''; }} video['name'].value = vcVideoResult.name; }} if (vcVideoResult.shortDescription) video['shortDesc'].value = vcVideoResult.shortDescription; if (vcVideoResult.longDescription) video['longDesc'].value = vcVideoResult.longDescription; if (vcVideoResult.itemState) {{ if (vcVideoResult.itemState != 'ACTIVE') {{ video['isActive'].selectedIndex = 1; }} }} if (vcVideoResult.linkURL) video['linkUrl'].value = vcVideoResult.linkURL; if (vcVideoResult.linkText) video['linkText'].value = vcVideoResult.linkText; if (vcVideoResult.videoStillURL) video['videoStillUrl'].value = vcVideoResult.videoStillURL; if (vcVideoResult.thumbnailURL) video['thumbnailUrl'].value = vcVideoResult.thumbnailURL; if (vcVideoResult.referenceId) video['referenceId'].value = vcVideoResult.referenceId; if (vcVideoResult.economics) {{ if (vcVideoResult.economics == 'FREE') {{ video['economics'].selectedIndex = 1; }} else if (vcVideoResult.economics == 'AD_SUPPORTED') {{ video['economics'].selectedIndex = 2; }} }} if (vcVideoResult.startDate) video['startDate'].value = new Date(new Number(vcVideoResult.startDate)).format('M/dd/yyyy'); if (vcVideoResult.endDate) video['endDate'].value = new Date(new Number(vcVideoResult.endDate)).format('M/dd/yyyy'); if (vcVideoResult.customFields) {{ for (var key in vcVideoResult.customFields) {{ video['customFields'].value += key + ':' + vcVideoResult.customFields[key] + ','; }} }} if (vcVideoResult.tags) video['tags'].value = vcVideoResult.tags; }} /*]]>*/</script>", this.video.ToJSON().Replace(": AD_SUPPORTED", ": \"AD_SUPPORTED\"").Replace(": FREE", ": \"FREE\""), this.IsAdmin.ToString().ToLower(), vcErrorCode); this.Controls.Add(new LiteralControl(output)); } }
/// <summary> /// The expected sequence of arguments: /// 1) [network-name] [rpc-command] [rpc-params]. /// OR /// 2) [network-name] [api-controller "/" api-command] [api-params]. /// </summary> public static void Main(string[] args) { try { // Display help if required. if (args.Length == 0 || args.Contains("-help") || args.Contains("--help")) { var builder = new StringBuilder(); builder.AppendLine("Usage:"); builder.AppendLine(" dotnet run <Stratis.Bitcoin.Cli/Stratis.Bitcoin.Cli.dll> [network-name] [command] [arguments]"); builder.AppendLine(); builder.AppendLine("Command line arguments:"); builder.AppendLine(); builder.AppendLine("[network-name] Name of the network - e.g. \"stratis\"."); builder.AppendLine("[command] Name of RPC method or API <controller>/<method>."); builder.AppendLine("[arguments] Argument by position (RPC) or Name = Value pairs (API)."); Console.WriteLine(builder); return; } // Determine API port. string blockchain = "bitcoin"; int apiPort = 37220; if (args.Any(a => a.Contains("stratis"))) { blockchain = "stratis"; apiPort = 37221; // hack until static flags are removed. var s = Network.StratisMain; var st = Network.StratisTest; } // The first argument is the network name. var network = Network.GetNetwork(args.First()); // API calls require both the contoller name and the method name separated by "/". // If this is not an API call then assume it is an RPC call. if (!args.ElementAt(1).Contains("/")) { // Process RPC call. try { NodeSettings nodeSettings = new NodeSettings(blockchain, network).LoadArguments(args); var rpcSettings = new RpcSettings(); rpcSettings.Load(nodeSettings); // Find the binding to 127.0.0.1 or the first available. The logic in RPC settings ensures there will be at least 1. System.Net.IPEndPoint nodeEndPoint = rpcSettings.Bind.FirstOrDefault(b => b.Address.ToString() == "127.0.0.1") ?? rpcSettings.Bind[0]; // Initialize the RPC client with the configured or passed userid, password and endpoint RPCClient rpc = new RPCClient($"{rpcSettings.RpcUser}:{rpcSettings.RpcPassword}", new Uri($"http://{nodeEndPoint}")); // Execute the RPC command Console.WriteLine($"Sending RPC command '{string.Join(" ", args.Skip(1))}' to 'http://{nodeEndPoint}'..."); RPCResponse response = rpc.SendCommand(args.ElementAt(1), args.Skip(2).ToArray()); // Return the result as a string to the console. Console.WriteLine(response.ResultString); } catch (Exception err) { Console.WriteLine(err.Message); } } else { // Process API call. using (var client = new HttpClient()) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var url = $"http://localhost:{apiPort}/api/{args.ElementAt(1)}?{string.Join("&", args.Skip(2))}"; try { // Get the response. Console.WriteLine($"Sending API command to {url}..."); var response = client.GetStringAsync(url).GetAwaiter().GetResult(); // Format and return the result as a string to the console. Console.WriteLine(JsonConvert.SerializeObject(JsonConvert.DeserializeObject <object>(response), Formatting.Indented)); } catch (Exception err) { Console.WriteLine(ExceptionToString(err)); } } } } catch (Exception err) { // Report any errors to the console. Console.WriteLine(ExceptionToString(err)); } }
public async Task <RPCResponse> SendCommandAsync(RPCRequest request, bool throwIfRPCError = true) { RPCResponse response = null; HttpWebRequest webRequest = response == null?CreateWebRequest() : null; if (response == null) { var writer = new StringWriter(); request.WriteJSON(writer); writer.Flush(); var json = writer.ToString(); var bytes = Encoding.UTF8.GetBytes(json); #if !(PORTABLE || NETCORE) webRequest.ContentLength = bytes.Length; #endif var dataStream = await webRequest.GetRequestStreamAsync().ConfigureAwait(false); await dataStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); await dataStream.FlushAsync().ConfigureAwait(false); dataStream.Dispose(); } WebResponse webResponse = null; WebResponse errorResponse = null; try { webResponse = response == null ? await webRequest.GetResponseAsync().ConfigureAwait(false) : null; response = response ?? RPCResponse.Load(await ToMemoryStreamAsync(webResponse.GetResponseStream()).ConfigureAwait(false)); if (throwIfRPCError) { response.ThrowIfError(); } } catch (WebException ex) { if (ex.Response == null || ex.Response.ContentLength == 0 || !ex.Response.ContentType.Equals("application/json", StringComparison.Ordinal)) { throw; } errorResponse = ex.Response; response = RPCResponse.Load(await ToMemoryStreamAsync(errorResponse.GetResponseStream()).ConfigureAwait(false)); if (throwIfRPCError) { response.ThrowIfError(); } } finally { if (errorResponse != null) { errorResponse.Dispose(); errorResponse = null; } if (webResponse != null) { webResponse.Dispose(); webResponse = null; } } return(response); }
public void CanSendCommand() { RPCResponse response = this.rpcTestFixture.RpcClient.SendCommand(RPCOperations.getinfo); Assert.NotNull(response.Result); }
public Transaction PerformBreezeRegistration(BreezeConfiguration config, string regStorePath, string configurationHash, string onionAddress, RsaKey tumblerKey) { Network network = Network.ImpleumMain; if (config.TumblerNetwork == Network.TestNet || config.TumblerNetwork == Network.RegTest) { network = Network.ImpleumTest; } RPCHelper stratisHelper = null; RPCClient stratisRpc = null; BitcoinSecret privateKeyEcdsa = null; try { stratisHelper = new RPCHelper(network); stratisRpc = stratisHelper.GetClient(config.RpcUser, config.RpcPassword, config.RpcUrl); privateKeyEcdsa = stratisRpc.DumpPrivKey(BitcoinAddress.Create(config.TumblerEcdsaKeyAddress)); } catch (Exception e) { Console.WriteLine("ERROR: Unable to retrieve private key to fund registration transaction"); Console.WriteLine("Is the Stratis wallet unlocked & RPC enabled?"); Console.WriteLine(e); Environment.Exit(0); } RegistrationToken registrationToken = new RegistrationToken(PROTOCOL_VERSION_TO_USE, config.TumblerEcdsaKeyAddress, config.Ipv4Address, config.Ipv6Address, onionAddress, configurationHash, config.Port, privateKeyEcdsa.PubKey); byte[] msgBytes = registrationToken.GetRegistrationTokenBytes(tumblerKey, privateKeyEcdsa); // Create the registration transaction using the bytes generated above Transaction rawTx = CreateBreezeRegistrationTx(network, msgBytes, config.TxOutputValueSetting); TransactionUtils txUtils = new TransactionUtils(); RegistrationStore regStore = new RegistrationStore(regStorePath); try { // Replace fundrawtransaction with C# implementation. The legacy wallet // software does not support the RPC call. Transaction fundedTx = txUtils.FundRawTx(stratisRpc, rawTx, config.TxFeeValueSetting, BitcoinAddress.Create(config.TumblerEcdsaKeyAddress)); RPCResponse signedTx = stratisRpc.SendCommand("signrawtransaction", fundedTx.ToHex()); Transaction txToSend = new Transaction(((JObject)signedTx.Result)["hex"].Value <string>()); RegistrationRecord regRecord = new RegistrationRecord(DateTime.Now, Guid.NewGuid(), txToSend.GetHash().ToString(), txToSend.ToHex(), registrationToken, null); regStore.Add(regRecord); stratisRpc.SendRawTransaction(txToSend); return(txToSend); } catch (Exception e) { Console.WriteLine("ERROR: Unable to broadcast registration transaction"); Console.WriteLine(e); } return(null); }
public async Task GetTransactionOnConfirmedTransactionAsync() { using (NodeBuilder builder = NodeBuilder.Create(this)) { // Arrange. // Create a sending and a receiving node. CoreNode sendingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Miner).Start(); CoreNode receivingNode = builder.CreateStratisPosNode(this.network).WithReadyBlockchainData(ReadyBlockchain.StratisRegTest150Listener).Start(); TestHelper.ConnectAndSync(sendingNode, receivingNode); // Get an address to send to. IEnumerable <string> unusedaddresses = await $"http://localhost:{receivingNode.ApiPort}/api" .AppendPathSegment("wallet/unusedAddresses") .SetQueryParams(new { walletName = "mywallet", accountName = "account 0", count = 1 }) .GetJsonAsync <IEnumerable <string> >(); // Build and send the transaction with an Op_Return. WalletBuildTransactionModel buildTransactionModel = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/build-transaction") .PostJsonAsync(new BuildTransactionRequest { WalletName = "mywallet", AccountName = "account 0", FeeType = "low", Password = "******", ShuffleOutputs = false, AllowUnconfirmed = true, Recipients = unusedaddresses.Select(address => new RecipientModel { DestinationAddress = address, Amount = "1" }).ToList(), }) .ReceiveJson <WalletBuildTransactionModel>(); await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("wallet/send-transaction") .PostJsonAsync(new SendTransactionRequest { Hex = buildTransactionModel.Hex }) .ReceiveJson <WalletSendTransactionModel>(); uint256 txId = buildTransactionModel.TransactionId; // Mine and sync so that we make sure the receiving node is up to date. TestHelper.MineBlocks(sendingNode, 1); TestHelper.WaitForNodeToSync(sendingNode, receivingNode); // Get the block that was mined. string lastBlockHash = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("consensus/getbestblockhash") .GetJsonAsync <string>(); BlockModel blockModelAtTip = await $"http://localhost:{sendingNode.ApiPort}/api" .AppendPathSegment("blockstore/block") .SetQueryParams(new { hash = lastBlockHash, outputJson = true }) .GetJsonAsync <BlockModel>(); Transaction trx = this.network.Consensus.ConsensusFactory.CreateTransaction(buildTransactionModel.Hex); RPCClient rpcReceivingNode = receivingNode.CreateRPCClient(); RPCResponse txReceivingWallet = rpcReceivingNode.SendCommand(RPCOperations.gettransaction, txId.ToString()); RPCClient rpcSendingNode = sendingNode.CreateRPCClient(); RPCResponse txSendingWallet = rpcSendingNode.SendCommand(RPCOperations.gettransaction, txId.ToString()); // Assert. GetTransactionModel resultSendingWallet = txSendingWallet.Result.ToObject <GetTransactionModel>(); resultSendingWallet.Amount.Should().Be((decimal) - 1.00000000); resultSendingWallet.Fee.Should().Be((decimal) - 0.0001); resultSendingWallet.Confirmations.Should().Be(1); resultSendingWallet.Isgenerated.Should().BeNull(); resultSendingWallet.TransactionId.Should().Be(txId); resultSendingWallet.BlockHash.Should().Be(uint256.Parse(blockModelAtTip.Hash)); resultSendingWallet.BlockIndex.Should().Be(1); resultSendingWallet.BlockTime.Should().Be(blockModelAtTip.Time); resultSendingWallet.TimeReceived.Should().BeGreaterThan((DateTimeOffset.Now - TimeSpan.FromMinutes(1)).ToUnixTimeSeconds()); resultSendingWallet.TransactionTime.Should().Be(((PosTransaction)trx).Time); resultSendingWallet.Details.Count.Should().Be(1); GetTransactionDetailsModel detailsSendingWallet = resultSendingWallet.Details.Single(); detailsSendingWallet.Address.Should().Be(unusedaddresses.First()); detailsSendingWallet.Amount.Should().Be((decimal) - 1.00000000); detailsSendingWallet.Fee.Should().Be((decimal) - 0.0001); detailsSendingWallet.Category.Should().Be(GetTransactionDetailsCategoryModel.Send); detailsSendingWallet.OutputIndex.Should().Be(1); GetTransactionModel resultReceivingWallet = txReceivingWallet.Result.ToObject <GetTransactionModel>(); resultReceivingWallet.Amount.Should().Be((decimal)1.00000000); resultReceivingWallet.Fee.Should().BeNull(); resultReceivingWallet.Confirmations.Should().Be(1); resultReceivingWallet.Isgenerated.Should().BeNull(); resultReceivingWallet.TransactionId.Should().Be(txId); resultReceivingWallet.BlockHash.Should().Be(uint256.Parse(blockModelAtTip.Hash)); resultReceivingWallet.BlockIndex.Should().Be(1); resultReceivingWallet.BlockTime.Should().Be(blockModelAtTip.Time); resultReceivingWallet.TimeReceived.Should().BeGreaterThan((DateTimeOffset.Now - TimeSpan.FromMinutes(1)).ToUnixTimeSeconds()); resultReceivingWallet.TransactionTime.Should().BeGreaterThan((DateTimeOffset.Now - TimeSpan.FromMinutes(1)).ToUnixTimeSeconds()); resultReceivingWallet.Details.Should().ContainSingle(); GetTransactionDetailsModel detailsReceivingWallet = resultReceivingWallet.Details.Single(); detailsReceivingWallet.Address.Should().Be(unusedaddresses.Single()); detailsReceivingWallet.Amount.Should().Be((decimal)1.00000000); detailsReceivingWallet.Fee.Should().BeNull(); detailsReceivingWallet.Category.Should().Be(GetTransactionDetailsCategoryModel.Receive); detailsReceivingWallet.OutputIndex.Should().Be(1); } }
public async Task <IActionResult> InputsAsync([FromBody] InputsRequest request) { var roundId = Global.StateMachine.RoundId; TumblerPhase phase = TumblerPhase.InputRegistration; try { if (Global.StateMachine.Phase != TumblerPhase.InputRegistration || !Global.StateMachine.AcceptRequest) { return(new ObjectResult(new FailureResponse { Message = "Wrong phase" })); } // Check not nulls string blindedOutputString = request.BlindedOutput.Trim(); if (string.IsNullOrWhiteSpace(blindedOutputString)) { return(new BadRequestResult()); } if (string.IsNullOrWhiteSpace(request.ChangeOutput)) { return(new BadRequestResult()); } if (request.Inputs == null || request.Inputs.Count() == 0) { return(new BadRequestResult()); } // Check format (parse everyting)) if (Global.StateMachine.BlindedOutputs.Contains(blindedOutputString)) { throw new ArgumentException("Blinded output has already been registered"); } byte[] blindedOutput = HexHelpers.GetBytes(blindedOutputString); Network network = Global.Config.Network; var changeOutput = new BitcoinWitPubKeyAddress(request.ChangeOutput, expectedNetwork: network); if (request.Inputs.Count() > Global.Config.MaximumInputsPerAlices) { throw new NotSupportedException("Too many inputs provided"); } var inputs = new HashSet <(TxOut Output, OutPoint OutPoint)>(); var alicesToRemove = new HashSet <Guid>(); using (await InputRegistrationLock.LockAsync()) { foreach (InputProofModel input in request.Inputs) { var op = new OutPoint(); op.FromHex(input.Input); if (inputs.Any(x => x.OutPoint.Hash == op.Hash && x.OutPoint.N == op.N)) { throw new ArgumentException("Attempting to register an input twice is not permitted"); } foreach (var a in Global.StateMachine.Alices) { if (a.Inputs.Any(x => x.OutPoint.Hash == op.Hash && x.OutPoint.N == op.N)) { alicesToRemove.Add(a.UniqueId); // input is already registered by this alice, remove it if all the checks are completed fine } } BannedUtxo banned = Global.UtxoReferee.Utxos.FirstOrDefault(x => x.Utxo.Hash == op.Hash && x.Utxo.N == op.N); if (banned != default(BannedUtxo)) { var maxBan = (int)TimeSpan.FromDays(30).TotalMinutes; int banLeft = maxBan - (int)((DateTimeOffset.UtcNow - banned.TimeOfBan).TotalMinutes); throw new ArgumentException($"Input is banned for {banLeft} minutes"); } var getTxOutResponse = await Global.RpcClient.GetTxOutAsync(op.Hash, (int)op.N, true); // Check if inputs are unspent if (getTxOutResponse == null) { throw new ArgumentException("Provided input is not unspent"); } // Check if inputs are unconfirmed, if so check if they are part of previous CoinJoin if (getTxOutResponse.Confirmations <= 0) { if (!Global.CoinJoinStore.Transactions .Any(x => x.State >= CoinJoinTransactionState.Succeeded && x.Transaction.GetHash() == op.Hash)) { throw new ArgumentException("Provided input is not confirmed, nor spends a previous CJ transaction"); } else { // after 24 unconfirmed cj in the mempool dont't let unconfirmed coinjoin to be registered var unconfirmedCoinJoins = Global.CoinJoinStore.Transactions.Where(x => x.State == CoinJoinTransactionState.Succeeded); if (unconfirmedCoinJoins.Count() >= 24) { var toFailed = new HashSet <uint256>(); var toConfirmed = new HashSet <uint256>(); foreach (var tx in unconfirmedCoinJoins) { RPCResponse getRawTransactionResponse = (await Global.RpcClient.SendCommandAsync("getrawtransaction", tx.Transaction.GetHash().ToString(), true)); if (string.IsNullOrWhiteSpace(getRawTransactionResponse?.ResultString)) { toFailed.Add(tx.Transaction.GetHash()); } if (getRawTransactionResponse.Result.Value <int>("confirmations") > 0) { toConfirmed.Add(tx.Transaction.GetHash()); } } foreach (var tx in toFailed) { Global.CoinJoinStore.TryUpdateState(tx, CoinJoinTransactionState.Failed); } foreach (var tx in toConfirmed) { Global.CoinJoinStore.TryUpdateState(tx, CoinJoinTransactionState.Confirmed); } if (toFailed.Count + toConfirmed.Count > 0) { await Global.CoinJoinStore.ToFileAsync(Global.CoinJoinStorePath); } // if couldn't remove any unconfirmed tx then refuse registration if (Global.CoinJoinStore.Transactions.Count(x => x.State == CoinJoinTransactionState.Succeeded) >= 24) { throw new ArgumentException("Registering unconfirmed CJ transaction output is currently not allowed due to too long mempool chain"); } } } } // Check coinbase > 100 if (getTxOutResponse.Confirmations < 100) { if (getTxOutResponse.IsCoinBase) { throw new ArgumentException("Provided input is unspendable"); } } // Check if inputs are native segwit if (getTxOutResponse.ScriptPubKeyType != "witness_v0_keyhash") { throw new ArgumentException("Provided input is not witness_v0_keyhash"); } var txout = getTxOutResponse.TxOut; var address = (BitcoinWitPubKeyAddress)txout.ScriptPubKey.GetDestinationAddress(network); // Check if proofs are valid var validProof = address.VerifyMessage(blindedOutputString, input.Proof); if (!validProof) { throw new ArgumentException("Provided proof is invalid"); } inputs.Add((txout, op)); } // Check if inputs have enough coins Money amount = Money.Zero; foreach (Money val in inputs.Select(x => x.Output.Value)) { amount += val; } Money feeToPay = (inputs.Count() * Global.StateMachine.FeePerInputs + 2 * Global.StateMachine.FeePerOutputs); Money changeAmount = amount - (Global.StateMachine.Denomination + feeToPay); if (changeAmount < Money.Zero + new Money(548)) // 546 is dust { throw new ArgumentException("Total provided inputs must be > denomination + fee + dust"); } byte[] signature = Global.RsaKey.SignBlindedData(blindedOutput); Global.StateMachine.BlindedOutputs.Add(blindedOutputString); Guid uniqueId = Guid.NewGuid(); var alice = new Alice { UniqueId = uniqueId, ChangeOutput = changeOutput, ChangeAmount = changeAmount, State = AliceState.InputsRegistered }; alice.Inputs = new ConcurrentHashSet <(TxOut Output, OutPoint OutPoint)>(); foreach (var input in inputs) { alice.Inputs.Add(input); } AssertPhase(roundId, phase); foreach (var aliceToRemove in alicesToRemove) { if (Global.StateMachine.TryRemoveAlice(aliceToRemove)) { await Global.StateMachine.BroadcastPeerRegisteredAsync(); } } Global.StateMachine.Alices.Add(alice); await Global.StateMachine.BroadcastPeerRegisteredAsync(); if (Global.StateMachine.Alices.Count >= Global.StateMachine.AnonymitySet) { Global.StateMachine.UpdatePhase(TumblerPhase.ConnectionConfirmation); } TumblerStateMachine.EstimateInputAndOutputSizes(out int inputSizeInBytes, out int outputSizeInBytes); int estimatedTxSize = Global.StateMachine.Alices.SelectMany(x => x.Inputs).Count() * inputSizeInBytes + 2 * outputSizeInBytes; if (estimatedTxSize >= 90000) // standard transaction is < 100KB { Global.StateMachine.UpdatePhase(TumblerPhase.ConnectionConfirmation); } var ret = new ObjectResult(new InputsResponse() { UniqueId = uniqueId.ToString(), SignedBlindedOutput = HexHelpers.ToString(signature) }); return(ret); } } catch (Exception ex) { return(new ObjectResult(new FailureResponse { Message = ex.Message })); } }