From cefd2d18c70686e41c121803a44c00ab4d310fb6 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Mon, 26 Jun 2023 14:31:15 +1000 Subject: [PATCH 01/11] main: Added multisig detect --- FireWallet/MainForm.Designer.cs | 42 +++++--- FireWallet/MainForm.cs | 40 +++++-- FireWallet/NewAccountForm.Designer.cs | 82 +++++++++++++++ FireWallet/NewAccountForm.cs | 143 ++++++++++++++++++-------- 4 files changed, 243 insertions(+), 64 deletions(-) diff --git a/FireWallet/MainForm.Designer.cs b/FireWallet/MainForm.Designer.cs index cf2a87a..c376f3c 100644 --- a/FireWallet/MainForm.Designer.cs +++ b/FireWallet/MainForm.Designer.cs @@ -39,6 +39,7 @@ namespace FireWallet toolStripStatusLabelNetwork = new ToolStripStatusLabel(); toolStripStatusLabelstatus = new ToolStripStatusLabel(); toolStripStatusLabelaccount = new ToolStripStatusLabel(); + toolStripStatusLabelMultisig = new ToolStripStatusLabel(); toolStripStatusLabelLedger = new ToolStripStatusLabel(); toolStripSplitButtonlogout = new ToolStripSplitButton(); toolStripDropDownButtonHelp = new ToolStripDropDownButton(); @@ -65,6 +66,7 @@ namespace FireWallet buttonNavSend = new Button(); buttonNavPortfolio = new Button(); panelPortfolio = new Panel(); + buttonSendAll = new Button(); buttonRedeemAll = new Button(); buttonRevealAll = new Button(); groupBoxTransactions = new GroupBox(); @@ -127,7 +129,6 @@ namespace FireWallet textBoxExAddr = new TextBox(); labelSettings4 = new Label(); textBoxExTX = new TextBox(); - buttonSendAll = new Button(); statusStripmain.SuspendLayout(); panelaccount.SuspendLayout(); groupBoxaccount.SuspendLayout(); @@ -151,7 +152,7 @@ namespace FireWallet // statusStripmain // statusStripmain.Dock = DockStyle.Top; - statusStripmain.Items.AddRange(new ToolStripItem[] { toolStripStatusLabelNetwork, toolStripStatusLabelstatus, toolStripStatusLabelaccount, toolStripStatusLabelLedger, toolStripSplitButtonlogout, toolStripDropDownButtonHelp }); + statusStripmain.Items.AddRange(new ToolStripItem[] { toolStripStatusLabelNetwork, toolStripStatusLabelstatus, toolStripStatusLabelaccount, toolStripStatusLabelMultisig, toolStripStatusLabelLedger, toolStripSplitButtonlogout, toolStripDropDownButtonHelp }); statusStripmain.Location = new Point(0, 0); statusStripmain.Name = "statusStripmain"; statusStripmain.Size = new Size(1152, 22); @@ -183,12 +184,20 @@ namespace FireWallet toolStripStatusLabelaccount.Size = new Size(55, 17); toolStripStatusLabelaccount.Text = "Account:"; // + // toolStripStatusLabelMultisig + // + toolStripStatusLabelMultisig.Margin = new Padding(50, 3, 50, 2); + toolStripStatusLabelMultisig.Name = "toolStripStatusLabelMultisig"; + toolStripStatusLabelMultisig.Size = new Size(50, 17); + toolStripStatusLabelMultisig.Text = "Multisig"; + toolStripStatusLabelMultisig.Visible = false; + // // toolStripStatusLabelLedger // toolStripStatusLabelLedger.Margin = new Padding(50, 3, 50, 2); toolStripStatusLabelLedger.Name = "toolStripStatusLabelLedger"; - toolStripStatusLabelLedger.Size = new Size(71, 17); - toolStripStatusLabelLedger.Text = "Cold Wallet:"; + toolStripStatusLabelLedger.Size = new Size(68, 17); + toolStripStatusLabelLedger.Text = "Cold Wallet"; toolStripStatusLabelLedger.Visible = false; // // toolStripSplitButtonlogout @@ -459,6 +468,18 @@ namespace FireWallet panelPortfolio.TabIndex = 7; panelPortfolio.Visible = false; // + // buttonSendAll + // + buttonSendAll.FlatStyle = FlatStyle.Flat; + buttonSendAll.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); + buttonSendAll.Location = new Point(761, 12); + buttonSendAll.Name = "buttonSendAll"; + buttonSendAll.Size = new Size(106, 44); + buttonSendAll.TabIndex = 9; + buttonSendAll.Text = "Send All TXs"; + buttonSendAll.UseVisualStyleBackColor = true; + buttonSendAll.Click += buttonSendAll_Click; + // // buttonRedeemAll // buttonRedeemAll.FlatStyle = FlatStyle.Flat; @@ -1121,18 +1142,6 @@ namespace FireWallet textBoxExTX.Size = new Size(307, 29); textBoxExTX.TabIndex = 1; // - // buttonSendAll - // - buttonSendAll.FlatStyle = FlatStyle.Flat; - buttonSendAll.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); - buttonSendAll.Location = new Point(761, 12); - buttonSendAll.Name = "buttonSendAll"; - buttonSendAll.Size = new Size(106, 44); - buttonSendAll.TabIndex = 9; - buttonSendAll.Text = "Send All TXs"; - buttonSendAll.UseVisualStyleBackColor = true; - buttonSendAll.Click += buttonSendAll_Click; - // // MainForm // AutoScaleDimensions = new SizeF(7F, 15F); @@ -1281,5 +1290,6 @@ namespace FireWallet private ToolStripMenuItem otherProjectsToolStripMenuItem; private Button buttonRedeemAll; private Button buttonSendAll; + private ToolStripStatusLabel toolStripStatusLabelMultisig; } } \ No newline at end of file diff --git a/FireWallet/MainForm.cs b/FireWallet/MainForm.cs index e0b4def..cf23640 100644 --- a/FireWallet/MainForm.cs +++ b/FireWallet/MainForm.cs @@ -37,6 +37,7 @@ namespace FireWallet // Batching variables public bool BatchMode { get; set; } public BatchForm BatchForm { get; set; } + public bool multiSig { get; set; } #endregion #region Application public MainForm() @@ -767,11 +768,11 @@ namespace FireWallet if (jObject["watchOnly"].ToString() == "True") { WatchOnly = true; - toolStripStatusLabelLedger.Text = "Cold Wallet"; toolStripStatusLabelLedger.Visible = true; buttonRevealAll.Visible = false; buttonRedeemAll.Visible = false; buttonSendAll.Visible = false; + buttonAddressVerify.Visible = true; } else { @@ -780,15 +781,38 @@ namespace FireWallet buttonRevealAll.Visible = true; buttonRedeemAll.Visible = true; buttonSendAll.Visible = true; + buttonAddressVerify.Visible = false; } - if (WatchOnly) + + path = "wallet/" + Account + "/account/default"; + APIresponse = await APIGet(path, true); + if (APIresponse.Contains("Error")) { - buttonAddressVerify.Visible = true; + AddLog("Error getting default account"); + multiSig = false; } else { - buttonAddressVerify.Visible = false; + jObject = JObject.Parse(APIresponse); + if (jObject["n"].ToString() == "1") + { + multiSig = false; + } + else + { + multiSig = true; + } } + + if (multiSig) + { + toolStripStatusLabelMultisig.Visible = true; + } + else + { + toolStripStatusLabelMultisig.Visible = false; + } + return true; } @@ -1763,7 +1787,8 @@ namespace FireWallet { labelSendingError.Show(); labelSendingError.Text = "HIP-02 lookup failed"; - } else + } + else { labelSendingHIPAddress.Text = address; labelSendingHIPAddress.Show(); @@ -1897,7 +1922,10 @@ namespace FireWallet { AddLog("Failed:"); AddLog(APIresp.ToString()); - NotifyForm notify = new NotifyForm("Error Transaction Failed"); + JObject error = JObject.Parse(APIresp["error"].ToString()); + string ErrorMessage = error["message"].ToString(); + + NotifyForm notify = new NotifyForm("Error Transaction Failed\n" + ErrorMessage); notify.ShowDialog(); return; } diff --git a/FireWallet/NewAccountForm.Designer.cs b/FireWallet/NewAccountForm.Designer.cs index b661948..6e4089b 100644 --- a/FireWallet/NewAccountForm.Designer.cs +++ b/FireWallet/NewAccountForm.Designer.cs @@ -46,9 +46,18 @@ label2 = new Label(); groupBoxSeed = new GroupBox(); textBoxSeedPhrase = new TextBox(); + groupBoxMulti = new GroupBox(); + numericUpDownM = new NumericUpDown(); + numericUpDownN = new NumericUpDown(); + label7 = new Label(); + label6 = new Label(); + checkBoxMulti = new CheckBox(); groupBoxMode.SuspendLayout(); groupBoxNew.SuspendLayout(); groupBoxSeed.SuspendLayout(); + groupBoxMulti.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)numericUpDownM).BeginInit(); + ((System.ComponentModel.ISupportInitialize)numericUpDownN).BeginInit(); SuspendLayout(); // // buttonNext @@ -241,11 +250,74 @@ textBoxSeedPhrase.Size = new Size(438, 288); textBoxSeedPhrase.TabIndex = 0; // + // groupBoxMulti + // + groupBoxMulti.Controls.Add(numericUpDownM); + groupBoxMulti.Controls.Add(numericUpDownN); + groupBoxMulti.Controls.Add(label7); + groupBoxMulti.Controls.Add(label6); + groupBoxMulti.Controls.Add(checkBoxMulti); + groupBoxMulti.Location = new Point(125, 22); + groupBoxMulti.Name = "groupBoxMulti"; + groupBoxMulti.Size = new Size(450, 319); + groupBoxMulti.TabIndex = 6; + groupBoxMulti.TabStop = false; + groupBoxMulti.Text = "Multisig"; + // + // numericUpDownM + // + numericUpDownM.Location = new Point(223, 91); + numericUpDownM.Maximum = new decimal(new int[] { 1000, 0, 0, 0 }); + numericUpDownM.Minimum = new decimal(new int[] { 1, 0, 0, 0 }); + numericUpDownM.Name = "numericUpDownM"; + numericUpDownM.Size = new Size(120, 23); + numericUpDownM.TabIndex = 2; + numericUpDownM.Value = new decimal(new int[] { 1, 0, 0, 0 }); + // + // numericUpDownN + // + numericUpDownN.Location = new Point(223, 55); + numericUpDownN.Maximum = new decimal(new int[] { 1000, 0, 0, 0 }); + numericUpDownN.Minimum = new decimal(new int[] { 1, 0, 0, 0 }); + numericUpDownN.Name = "numericUpDownN"; + numericUpDownN.Size = new Size(120, 23); + numericUpDownN.TabIndex = 2; + numericUpDownN.Value = new decimal(new int[] { 1, 0, 0, 0 }); + // + // label7 + // + label7.AutoSize = true; + label7.Location = new Point(6, 93); + label7.Name = "label7"; + label7.Size = new Size(211, 15); + label7.TabIndex = 1; + label7.Text = "Required Signers to send a transaction:"; + // + // label6 + // + label6.AutoSize = true; + label6.Location = new Point(141, 61); + label6.Name = "label6"; + label6.Size = new Size(76, 15); + label6.TabIndex = 1; + label6.Text = "Total Signers:"; + // + // checkBoxMulti + // + checkBoxMulti.AutoSize = true; + checkBoxMulti.Location = new Point(6, 23); + checkBoxMulti.Name = "checkBoxMulti"; + checkBoxMulti.Size = new Size(115, 19); + checkBoxMulti.TabIndex = 0; + checkBoxMulti.Text = "Create a multisig"; + checkBoxMulti.UseVisualStyleBackColor = true; + // // NewAccountForm // AutoScaleDimensions = new SizeF(7F, 15F); AutoScaleMode = AutoScaleMode.Font; ClientSize = new Size(680, 430); + Controls.Add(groupBoxMulti); Controls.Add(groupBoxSeed); Controls.Add(buttonCancel); Controls.Add(buttonNext); @@ -263,6 +335,10 @@ groupBoxNew.PerformLayout(); groupBoxSeed.ResumeLayout(false); groupBoxSeed.PerformLayout(); + groupBoxMulti.ResumeLayout(false); + groupBoxMulti.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)numericUpDownM).EndInit(); + ((System.ComponentModel.ISupportInitialize)numericUpDownN).EndInit(); ResumeLayout(false); } @@ -285,5 +361,11 @@ private Label label5; private GroupBox groupBoxSeed; private TextBox textBoxSeedPhrase; + private GroupBox groupBoxMulti; + private CheckBox checkBoxMulti; + private NumericUpDown numericUpDownM; + private NumericUpDown numericUpDownN; + private Label label7; + private Label label6; } } \ No newline at end of file diff --git a/FireWallet/NewAccountForm.cs b/FireWallet/NewAccountForm.cs index a0ea6a4..3b73886 100644 --- a/FireWallet/NewAccountForm.cs +++ b/FireWallet/NewAccountForm.cs @@ -148,53 +148,111 @@ namespace FireWallet if (page == 1) { - // Create new wallet - buttonNext.Enabled = false; - string path = "wallet/" + textBoxNewName.Text; - string content = "{\"passphrase\":\"" + textBoxNewPass1.Text + "\"}"; - string response = await APIPut(path, true, content); - if (response == "Error") - { - NotifyForm notify = new NotifyForm("Error creating wallet"); - notify.ShowDialog(); - notify.Dispose(); - buttonNext.Enabled = true; - return; - } - mainForm.AddLog("Created wallet: " + textBoxNewName.Text); - NotifyForm notify2 = new NotifyForm("Created wallet: " + textBoxNewName.Text); - notify2.ShowDialog(); - notify2.Dispose(); - this.Close(); + groupBoxMulti.Show(); + page = 6; } else if (page == 2) { groupBoxSeed.Show(); - buttonNext.Text = "Import"; page = 3; } else if (page == 3) { - // Create new wallet - buttonNext.Enabled = false; - string path = "wallet/" + textBoxNewName.Text; - string content = "{\"passphrase\":\"" + textBoxNewPass1.Text + "\",\"mnemonic\":\"" + textBoxSeedPhrase.Text + "\"}"; - string response = await APIPut(path, true, content); - if (response == "Error") - { - NotifyForm notify = new NotifyForm("Error creating wallet"); - notify.ShowDialog(); - notify.Dispose(); - buttonNext.Enabled = true; - return; - } - mainForm.AddLog("Created wallet: " + textBoxNewName.Text); - NotifyForm notify2 = new NotifyForm("Imported wallet: " + textBoxNewName.Text); - notify2.ShowDialog(); - notify2.Dispose(); - this.Close(); + page = 5; + groupBoxMulti.Show(); + buttonNext.Text = "Import"; } + else if (page == 5) + { + if (!checkBoxMulti.Checked) + { + // Import wallet from seed + buttonNext.Enabled = false; + string path = "wallet/" + textBoxNewName.Text; + string content = "{\"passphrase\":\"" + textBoxNewPass1.Text + "\",\"mnemonic\":\"" + textBoxSeedPhrase.Text + "\"}"; + string response = await APIPut(path, true, content); + if (response == "Error") + { + NotifyForm notify = new NotifyForm("Error creating wallet"); + notify.ShowDialog(); + notify.Dispose(); + buttonNext.Enabled = true; + return; + } + mainForm.AddLog("Created wallet: " + textBoxNewName.Text); + NotifyForm notify2 = new NotifyForm("Imported wallet: " + textBoxNewName.Text); + notify2.ShowDialog(); + notify2.Dispose(); + this.Close(); + } + else + { + // Import wallet from seed and create multisig + buttonNext.Enabled = false; + string path = "wallet/" + textBoxNewName.Text; + string content = "{\"passphrase\":\"" + textBoxNewPass1.Text + "\",\"mnemonic\":\"" + textBoxSeedPhrase.Text + "\", \"type\":\"multisig\",\"m\":"+numericUpDownM.Value.ToString()+ ",\"n\":" +numericUpDownN.Value.ToString() + "}"; + string response = await APIPut(path, true, content); + if (response == "Error") + { + NotifyForm notify = new NotifyForm("Error creating wallet"); + notify.ShowDialog(); + notify.Dispose(); + buttonNext.Enabled = true; + return; + } + mainForm.AddLog("Created wallet: " + textBoxNewName.Text); + NotifyForm notify2 = new NotifyForm("Imported wallet: " + textBoxNewName.Text); + notify2.ShowDialog(); + notify2.Dispose(); + this.Close(); + } + } + else if (page == 6) + { + if (!checkBoxMulti.Checked) + { + // Create new wallet + buttonNext.Enabled = false; + string path = "wallet/" + textBoxNewName.Text; + string content = "{\"passphrase\":\"" + textBoxNewPass1.Text + "\"}"; + string response = await APIPut(path, true, content); + if (response == "Error") + { + NotifyForm notify = new NotifyForm("Error creating wallet"); + notify.ShowDialog(); + notify.Dispose(); + buttonNext.Enabled = true; + return; + } + mainForm.AddLog("Created wallet: " + textBoxNewName.Text); + NotifyForm notify2 = new NotifyForm("Created wallet: " + textBoxNewName.Text); + notify2.ShowDialog(); + notify2.Dispose(); + this.Close(); + } else + { + // Create new wallet + buttonNext.Enabled = false; + string path = "wallet/" + textBoxNewName.Text; + string content = "{\"passphrase\":\"" + textBoxNewPass1.Text + "\", \"type\":\"multisig\",\"m\":"+numericUpDownM.Value.ToString()+ ",\"n\":" +numericUpDownN.Value.ToString() + "}"; + string response = await APIPut(path, true, content); + if (response == "Error") + { + NotifyForm notify = new NotifyForm("Error creating wallet"); + notify.ShowDialog(); + notify.Dispose(); + buttonNext.Enabled = true; + return; + } + mainForm.AddLog("Created wallet: " + textBoxNewName.Text); + NotifyForm notify2 = new NotifyForm("Created wallet: " + textBoxNewName.Text); + notify2.ShowDialog(); + notify2.Dispose(); + this.Close(); + } + } + else if (page == 4) { try @@ -228,7 +286,7 @@ namespace FireWallet } catch (Exception ex) { - mainForm.AddLog(ex.Message); + mainForm.AddLog(ex.Message); NotifyForm notify = new NotifyForm("Error importing wallet\n" + ex.Message); notify.ShowDialog(); notify.Dispose(); @@ -256,16 +314,17 @@ namespace FireWallet HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Put, "http://" + ip + ":" + port + "/" + path); req.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes("x:" + key))); req.Content = new StringContent(content); - + try { // Send request HttpResponseMessage resp = await httpClient.SendAsync(req); - + if (resp.IsSuccessStatusCode) { return await resp.Content.ReadAsStringAsync(); - } else + } + else { mainForm.AddLog("Put Error: " + await resp.Content.ReadAsStringAsync()); return "Error"; @@ -277,7 +336,7 @@ namespace FireWallet return "Error"; } - + } } } From b1d21289abdc9f5d212ceb30284e2d67f8f1f226 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Mon, 26 Jun 2023 16:20:54 +1000 Subject: [PATCH 02/11] main: Initial send multisig to address --- FireWallet/MainForm.Designer.cs | 16 ++ FireWallet/MainForm.cs | 282 ++++++++++++++++++++++---------- 2 files changed, 210 insertions(+), 88 deletions(-) diff --git a/FireWallet/MainForm.Designer.cs b/FireWallet/MainForm.Designer.cs index c376f3c..f1e8c22 100644 --- a/FireWallet/MainForm.Designer.cs +++ b/FireWallet/MainForm.Designer.cs @@ -129,6 +129,7 @@ namespace FireWallet textBoxExAddr = new TextBox(); labelSettings4 = new Label(); textBoxExTX = new TextBox(); + buttonMultiSign = new Button(); statusStripmain.SuspendLayout(); panelaccount.SuspendLayout(); groupBoxaccount.SuspendLayout(); @@ -365,6 +366,7 @@ namespace FireWallet // panelNav // panelNav.Controls.Add(buttonNavSettings); + panelNav.Controls.Add(buttonMultiSign); panelNav.Controls.Add(buttonBatch); panelNav.Controls.Add(buttonNavDomains); panelNav.Controls.Add(buttonNavReceive); @@ -1142,6 +1144,19 @@ namespace FireWallet textBoxExTX.Size = new Size(307, 29); textBoxExTX.TabIndex = 1; // + // buttonMultiSign + // + buttonMultiSign.FlatStyle = FlatStyle.Flat; + buttonMultiSign.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); + buttonMultiSign.Location = new Point(12, 301); + buttonMultiSign.Name = "buttonMultiSign"; + buttonMultiSign.Size = new Size(89, 30); + buttonMultiSign.TabIndex = 3; + buttonMultiSign.TabStop = false; + buttonMultiSign.Text = "Import TX"; + buttonMultiSign.UseVisualStyleBackColor = true; + buttonMultiSign.Click += buttonMultiSign_Click; + // // MainForm // AutoScaleDimensions = new SizeF(7F, 15F); @@ -1291,5 +1306,6 @@ namespace FireWallet private Button buttonRedeemAll; private Button buttonSendAll; private ToolStripStatusLabel toolStripStatusLabelMultisig; + private Button buttonMultiSign; } } \ No newline at end of file diff --git a/FireWallet/MainForm.cs b/FireWallet/MainForm.cs index cf23640..dc7134f 100644 --- a/FireWallet/MainForm.cs +++ b/FireWallet/MainForm.cs @@ -1,17 +1,17 @@ using System.Diagnostics; -using System.Runtime.InteropServices; -using Newtonsoft.Json.Linq; -using Point = System.Drawing.Point; -using Size = System.Drawing.Size; -using QRCoder; -using System.Text.RegularExpressions; -using System.Security.Cryptography; -using System.Text; using System.Net; +using System.Net.Security; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Text.RegularExpressions; using DnsClient; using DnsClient.Protocol; -using System.Security.Cryptography.X509Certificates; -using System.Net.Security; +using Newtonsoft.Json.Linq; +using QRCoder; +using Point = System.Drawing.Point; +using Size = System.Drawing.Size; namespace FireWallet { @@ -1519,6 +1519,12 @@ namespace FireWallet } #endregion #region Nav + private void buttonMultiSign_Click(object sender, EventArgs e) + { + ImportTXForm importTXForm = new ImportTXForm(this); + importTXForm.ShowDialog(); + importTXForm.Dispose(); + } private async void PortfolioPanel_Click(object sender, EventArgs e) { hidePages(); @@ -1912,7 +1918,7 @@ namespace FireWallet return; } - if (!WatchOnly) + if (!WatchOnly && !multiSig) { string content = "{\"method\": \"sendtoaddress\",\"params\": [ \"" + address + "\", " + amount.ToString() + ", \"\", \"\", " + subtractFee + " ]}"; @@ -1940,96 +1946,136 @@ namespace FireWallet labelSendingError.Text = ""; buttonNavPortfolio.PerformClick(); } - else // Cold wallet signing + else // Cold or multisig wallet signing { - AddLog("Sending CW " + amount.ToString() + " HNS to " + address); - - if (!Directory.Exists(dir + "hsd-ledger")) + if (multiSig) { - if (CheckNodeInstalled() == false) + if (!WatchOnly) { - AddLog("Node not installed"); - NotifyForm notify1 = new NotifyForm("Node not installed\nPlease install it to use Ledger"); - notify1.ShowDialog(); - notify1.Dispose(); - return; + string content = "{\"method\": \"createsendtoaddress\",\"params\": [ \"" + address + "\", " + + amount.ToString() + ", \"\", \"\", " + subtractFee + " ]}"; + string output = await APIPost("", true, content); + JObject APIresp = JObject.Parse(output); + if (APIresp["error"].ToString() != "") + { + AddLog("Failed:"); + AddLog(APIresp.ToString()); + JObject error = JObject.Parse(APIresp["error"].ToString()); + string ErrorMessage = error["message"].ToString(); + + NotifyForm notify = new NotifyForm("Error Transaction Failed\n" + ErrorMessage); + notify.ShowDialog(); + return; + } + JObject result = JObject.Parse(APIresp["result"].ToString()); + string hex = result["hex"].ToString(); + + content = "{\"tx\": \"" + hex + "\",\"passphrase\":\"" + Password + "\"}"; + output = await APIPost("wallet/" + Account + "/sign", true, content); + if (!output.Contains("hex")) + { + AddLog("Failed:"); + AddLog(APIresp.ToString()); + NotifyForm notify = new NotifyForm("Error Transaction Failed\nCheck the logs"); + notify.ShowDialog(); + return; + } + ExportTransaction(output); } - AddLog("Installing hsd-ledger"); - // Try to install hsd-ledger - try + + } + else // Cold wallet non multisig + { + AddLog("Sending CW " + amount.ToString() + " HNS to " + address); + + if (!Directory.Exists(dir + "hsd-ledger")) { - NotifyForm Notifyinstall = new NotifyForm("Installing hsd-ledger\nThis may take a few minutes\nDo not close FireWallet", false); - Notifyinstall.Show(); - // Wait for the notification to show - await Task.Delay(1000); + if (CheckNodeInstalled() == false) + { + AddLog("Node not installed"); + NotifyForm notify1 = new NotifyForm("Node not installed\nPlease install it to use Ledger"); + notify1.ShowDialog(); + notify1.Dispose(); + return; + } + AddLog("Installing hsd-ledger"); - string repositoryUrl = "https://github.com/handshake-org/hsd-ledger.git"; - string destinationPath = dir + "hsd-ledger"; - CloneRepository(repositoryUrl, destinationPath); + // Try to install hsd-ledger + try + { + NotifyForm Notifyinstall = new NotifyForm("Installing hsd-ledger\nThis may take a few minutes\nDo not close FireWallet", false); + Notifyinstall.Show(); + // Wait for the notification to show + await Task.Delay(1000); + + string repositoryUrl = "https://github.com/handshake-org/hsd-ledger.git"; + string destinationPath = dir + "hsd-ledger"; + CloneRepository(repositoryUrl, destinationPath); + + Notifyinstall.CloseNotification(); + Notifyinstall.Dispose(); + } + catch (Exception ex) + { + NotifyForm notifyError = new NotifyForm("Error installing hsd-ledger\n" + ex.Message); + AddLog(ex.Message); + notifyError.ShowDialog(); + notifyError.Dispose(); + return; + } - Notifyinstall.CloseNotification(); - Notifyinstall.Dispose(); } - catch (Exception ex) + + NotifyForm notify = new NotifyForm("Please confirm the transaction on your Ledger device", false); + notify.Show(); + + var proc = new Process(); + proc.StartInfo.CreateNoWindow = true; + proc.StartInfo.RedirectStandardInput = true; + proc.StartInfo.RedirectStandardOutput = true; + proc.StartInfo.UseShellExecute = false; + proc.StartInfo.RedirectStandardError = true; + proc.StartInfo.FileName = "node.exe"; + proc.StartInfo.Arguments = dir + "hsd-ledger/bin/hsd-ledger sendtoaddress " + textBoxSendingTo.Text + + " " + textBoxSendingAmount.Text + " --api-key " + NodeSettings["Key"] + " -w " + Account; + var outputBuilder = new StringBuilder(); + + // Event handler for capturing output data + proc.OutputDataReceived += (sender, args) => { - NotifyForm notifyError = new NotifyForm("Error installing hsd-ledger\n" + ex.Message); - AddLog(ex.Message); + if (!string.IsNullOrEmpty(args.Data)) + { + outputBuilder.AppendLine(args.Data); + } + }; + + proc.Start(); + proc.BeginOutputReadLine(); + proc.WaitForExit(); + + notify.CloseNotification(); + notify.Dispose(); + + string output = outputBuilder.ToString(); + AddLog(output); + if (output.Contains("Submitted TXID")) + { + string hash = output.Substring(output.IndexOf("Submitted TXID") + 16, 64); + string link = UserSettings["explorer-tx"] + hash; + NotifyForm notifySuccess = new NotifyForm("Transaction Sent\nThis transaction could take up to 20 minutes to mine", + "Explorer", link); + notifySuccess.ShowDialog(); + textBoxSendingTo.Text = ""; + textBoxSendingAmount.Text = ""; + buttonNavPortfolio.PerformClick(); + } + else + { + NotifyForm notifyError = new NotifyForm("Error Transaction Failed\nCheck logs for more details"); notifyError.ShowDialog(); notifyError.Dispose(); - return; } - - } - - NotifyForm notify = new NotifyForm("Please confirm the transaction on your Ledger device", false); - notify.Show(); - - var proc = new Process(); - proc.StartInfo.CreateNoWindow = true; - proc.StartInfo.RedirectStandardInput = true; - proc.StartInfo.RedirectStandardOutput = true; - proc.StartInfo.UseShellExecute = false; - proc.StartInfo.RedirectStandardError = true; - proc.StartInfo.FileName = "node.exe"; - proc.StartInfo.Arguments = dir + "hsd-ledger/bin/hsd-ledger sendtoaddress " + textBoxSendingTo.Text - + " " + textBoxSendingAmount.Text + " --api-key " + NodeSettings["Key"] + " -w " + Account; - var outputBuilder = new StringBuilder(); - - // Event handler for capturing output data - proc.OutputDataReceived += (sender, args) => - { - if (!string.IsNullOrEmpty(args.Data)) - { - outputBuilder.AppendLine(args.Data); - } - }; - - proc.Start(); - proc.BeginOutputReadLine(); - proc.WaitForExit(); - - notify.CloseNotification(); - notify.Dispose(); - - string output = outputBuilder.ToString(); - AddLog(output); - if (output.Contains("Submitted TXID")) - { - string hash = output.Substring(output.IndexOf("Submitted TXID") + 16, 64); - string link = UserSettings["explorer-tx"] + hash; - NotifyForm notifySuccess = new NotifyForm("Transaction Sent\nThis transaction could take up to 20 minutes to mine", - "Explorer", link); - notifySuccess.ShowDialog(); - textBoxSendingTo.Text = ""; - textBoxSendingAmount.Text = ""; - buttonNavPortfolio.PerformClick(); - } - else - { - NotifyForm notifyError = new NotifyForm("Error Transaction Failed\nCheck logs for more details"); - notifyError.ShowDialog(); - notifyError.Dispose(); } } @@ -2042,6 +2088,7 @@ namespace FireWallet labelSendingError.Text = ex.Message; } } + public void CloneRepository(string repositoryUrl, string destinationPath) { try @@ -2650,5 +2697,64 @@ namespace FireWallet #endregion + + #region Multi + private void ExportTransaction(string rawTX) + { + JObject tx = JObject.Parse(rawTX); + JObject toExport = new JObject(); + toExport["version"] = 1; + toExport["tx"] = tx["hex"]; + JArray inputsParsed = new JArray(); + JArray outputsParsed = new JArray(); + JArray inputs = JArray.Parse(tx["inputs"].ToString()); + JArray outputs = JArray.Parse(tx["outputs"].ToString()); + foreach (JObject input in inputs) + { + JObject coin = JObject.Parse(input["coin"].ToString()); + JObject covenant = JObject.Parse(coin["covenant"].ToString()); + string type = covenant["type"].ToString(); + JObject data = new JObject(); + if (type == "0") + { + inputsParsed.Add(data); + } + else + { + AddLog("Not supported yet"); + } + } + foreach (JObject output in outputs) + { + JObject covenant = JObject.Parse(output["covenant"].ToString()); + string type = covenant["type"].ToString(); + JObject data = new JObject(); + if (type == "0") + { + outputsParsed.Add(data); + } + else + { + AddLog("Not supported yet"); + } + } + JObject metadata = new JObject(); + metadata["inputs"] = inputsParsed; + metadata["outputs"] = outputsParsed; + toExport["metadata"] = metadata; + + SaveFileDialog saveFileDialog = new SaveFileDialog + { + Filter = "JSON file (*.json)|*.json", + Title = "Save transaction as JSON" + }; + if (saveFileDialog.ShowDialog() == DialogResult.OK) + { + StreamWriter sw = new StreamWriter(saveFileDialog.FileName); + sw.Write(toExport.ToString()); + sw.Dispose(); + } + } + #endregion } } \ No newline at end of file From 5be4f0d6e91bf94afd652a8b3540e66b8cc001fb Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Mon, 26 Jun 2023 16:21:05 +1000 Subject: [PATCH 03/11] multisig: Added import tx --- FireWallet/ImportTXForm.Designer.cs | 131 +++++++ FireWallet/ImportTXForm.cs | 192 +++++++++ FireWallet/ImportTXForm.resx | 587 ++++++++++++++++++++++++++++ 3 files changed, 910 insertions(+) create mode 100644 FireWallet/ImportTXForm.Designer.cs create mode 100644 FireWallet/ImportTXForm.cs create mode 100644 FireWallet/ImportTXForm.resx diff --git a/FireWallet/ImportTXForm.Designer.cs b/FireWallet/ImportTXForm.Designer.cs new file mode 100644 index 0000000..0ce2c2b --- /dev/null +++ b/FireWallet/ImportTXForm.Designer.cs @@ -0,0 +1,131 @@ +namespace FireWallet +{ + partial class ImportTXForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ImportTXForm)); + groupBoxIn = new GroupBox(); + panelIn = new Panel(); + groupBoxOut = new GroupBox(); + panelOut = new Panel(); + buttonSign = new Button(); + Cancelbutton2 = new Button(); + groupBoxIn.SuspendLayout(); + groupBoxOut.SuspendLayout(); + SuspendLayout(); + // + // groupBoxIn + // + groupBoxIn.Controls.Add(panelIn); + groupBoxIn.Location = new Point(12, 83); + groupBoxIn.Name = "groupBoxIn"; + groupBoxIn.Size = new Size(341, 355); + groupBoxIn.TabIndex = 3; + groupBoxIn.TabStop = false; + groupBoxIn.Text = "Inputs"; + // + // panelIn + // + panelIn.AutoScroll = true; + panelIn.Dock = DockStyle.Fill; + panelIn.Location = new Point(3, 19); + panelIn.Name = "panelIn"; + panelIn.Size = new Size(335, 333); + panelIn.TabIndex = 0; + // + // groupBoxOut + // + groupBoxOut.Controls.Add(panelOut); + groupBoxOut.Location = new Point(359, 83); + groupBoxOut.Name = "groupBoxOut"; + groupBoxOut.Size = new Size(429, 355); + groupBoxOut.TabIndex = 0; + groupBoxOut.TabStop = false; + groupBoxOut.Text = "Outputs"; + // + // panelOut + // + panelOut.AutoScroll = true; + panelOut.Dock = DockStyle.Fill; + panelOut.Location = new Point(3, 19); + panelOut.Name = "panelOut"; + panelOut.Size = new Size(423, 333); + panelOut.TabIndex = 0; + // + // buttonSign + // + buttonSign.FlatStyle = FlatStyle.Flat; + buttonSign.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); + buttonSign.Location = new Point(705, 444); + buttonSign.Name = "buttonSign"; + buttonSign.Size = new Size(83, 36); + buttonSign.TabIndex = 2; + buttonSign.Text = "Sign"; + buttonSign.UseVisualStyleBackColor = true; + // + // Cancelbutton2 + // + Cancelbutton2.FlatStyle = FlatStyle.Flat; + Cancelbutton2.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); + Cancelbutton2.Location = new Point(616, 444); + Cancelbutton2.Name = "Cancelbutton2"; + Cancelbutton2.Size = new Size(83, 36); + Cancelbutton2.TabIndex = 2; + Cancelbutton2.Text = "Cancel"; + Cancelbutton2.UseVisualStyleBackColor = true; + Cancelbutton2.Click += Cancelbutton2_Click; + // + // ImportTXForm + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(800, 485); + Controls.Add(groupBoxOut); + Controls.Add(groupBoxIn); + Controls.Add(Cancelbutton2); + Controls.Add(buttonSign); + FormBorderStyle = FormBorderStyle.FixedSingle; + Icon = (Icon)resources.GetObject("$this.Icon"); + MaximizeBox = false; + Name = "ImportTXForm"; + Text = "ImportTXForm"; + Load += ImportTXForm_Load; + groupBoxIn.ResumeLayout(false); + groupBoxOut.ResumeLayout(false); + ResumeLayout(false); + } + + #endregion + private GroupBox groupBoxIn; + private GroupBox groupBoxOut; + private Button buttonSign; + private Button Cancelbutton2; + private Panel panelIn; + private Panel panelOut; + } +} \ No newline at end of file diff --git a/FireWallet/ImportTXForm.cs b/FireWallet/ImportTXForm.cs new file mode 100644 index 0000000..d9696e9 --- /dev/null +++ b/FireWallet/ImportTXForm.cs @@ -0,0 +1,192 @@ +using Newtonsoft.Json.Linq; + +namespace FireWallet +{ + public partial class ImportTXForm : Form + { + MainForm mainForm; + JObject tx; + public ImportTXForm(MainForm mainForm) + { + InitializeComponent(); + this.mainForm = mainForm; + } + + private void ImportTXForm_Load(object sender, EventArgs e) + { + // Theme + this.BackColor = ColorTranslator.FromHtml(mainForm.Theme["background"]); + this.ForeColor = ColorTranslator.FromHtml(mainForm.Theme["foreground"]); + foreach (Control c in Controls) + { + mainForm.ThemeControl(c); + } + OpenFileDialog openFileDialog = new OpenFileDialog(); + openFileDialog.Filter = "Transaction files (*.json)|*.json"; + if (openFileDialog.ShowDialog() == DialogResult.OK) + { + string tx = System.IO.File.ReadAllText(openFileDialog.FileName); + try + { + JObject txObj = JObject.Parse(tx); + this.tx = txObj; + ParseTX(); + } + catch + { + NotifyForm notifyForm = new NotifyForm("Invalid transaction file."); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + this.Close(); + } + } + } + + private void Cancelbutton2_Click(object sender, EventArgs e) + { + this.Close(); + } + private async void ParseTX() + { + if (tx == null) this.Close(); + + string hex = tx["tx"].ToString(); + string content = "{\"method\":\"decoderawtransaction\",\"params\":[\"" + hex + "\"]}"; + string response = await mainForm.APIPost("", false, content); + if (response == null) + { + NotifyForm notifyForm = new NotifyForm("Error decoding transaction"); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + return; + } + JObject json = JObject.Parse(response); + if (json["error"].ToString() != "") + { + NotifyForm notifyForm = new NotifyForm("Error decoding transaction"); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + return; + } + JObject metadata = (JObject)tx["metadata"]; + JArray metaInputs = (JArray)metadata["inputs"]; + JArray metaOutputs = (JArray)metadata["outputs"]; + + JArray inputs = (JArray)json["result"]["vin"]; + JArray outputs = (JArray)json["result"]["vout"]; + for (int i = 0; i < inputs.Count; i++) + { + JObject input = (JObject)inputs[i]; + JObject metaInput = (JObject)metaInputs[i]; + + Panel PanelInput = new Panel(); + PanelInput.Size = new Size(panelIn.Width - SystemInformation.VerticalScrollBarWidth - 10, 50); + PanelInput.Location = new Point(5, panelIn.Controls.Count * 50); + PanelInput.BorderStyle = BorderStyle.FixedSingle; + + Label txid = new Label(); + txid.Text = "TXID: " + input["txid"].ToString(); + txid.Location = new Point(5, 5); + txid.AutoSize = true; + PanelInput.Controls.Add(txid); + + if (metaInput.ContainsKey("sighashType")) + { + Label sighashType = new Label(); + sighashType.Text = "Sighash Type: " + metaInput["sighashType"].ToString(); + sighashType.Location = new Point(5, 25); + sighashType.AutoSize = true; + PanelInput.Controls.Add(sighashType); + } + + //Label address = new Label(); + //string addressString = input["address"].ToString().Substring(0, 5) + "..." + input["address"].ToString().Substring(input["address"].ToString().Length - 5, 5); + //address.Text = "Address: " + addressString; + //address.Location = new Point(5, 5); + //address.AutoSize = true; + //PanelInput.Controls.Add(address); + + //Label amount = new Label(); + //Decimal value = Decimal.Parse(input["value"].ToString()) / 1000000; + //amount.Text = "Amount: " + value.ToString(); + //amount.Location = new Point(5, 25); + //amount.AutoSize = true; + //PanelInput.Controls.Add(amount); + + //if (input["path"].ToString() != "") + //{ + // Label ownAddress = new Label(); + // ownAddress.Text = "Own Address"; + // ownAddress.Location = new Point(PanelInput.Width - 100, 5); + // ownAddress.AutoSize = true; + // PanelInput.Controls.Add(ownAddress); + //} + + panelIn.Controls.Add(PanelInput); + } + + for (int i = 0; i < outputs.Count; i++) + { + JObject output = (JObject)outputs[i]; + JObject metaOutput = (JObject)metaOutputs[i]; + + Panel PanelOutput = new Panel(); + PanelOutput.Size = new Size(panelOut.Width - SystemInformation.VerticalScrollBarWidth - 10, 50); + PanelOutput.Location = new Point(5, panelOut.Controls.Count * 50); + PanelOutput.BorderStyle = BorderStyle.FixedSingle; + + Label address = new Label(); + + JObject addressRaw = (JObject)output["address"]; + + string addressString = addressRaw["string"].ToString().Substring(0, 5) + "..." + addressRaw["string"].ToString().Substring(addressRaw["string"].ToString().Length - 5, 5); + address.Text = "Address: " + addressString; + address.Location = new Point(5, 5); + address.AutoSize = true; + PanelOutput.Controls.Add(address); + + JObject covenant = (JObject)output["covenant"]; + if (covenant.ContainsKey("action")) + { + if (covenant["action"].ToString() != "NONE") + { + if (metaOutput.ContainsKey("name")) + { + + + + Label covenantLabel = new Label(); + string name = metaOutput["name"].ToString(); + + covenantLabel.Text = covenant["action"].ToString() + ": " + name; + covenantLabel.Location = new Point(5, 25); + covenantLabel.AutoSize = true; + PanelOutput.Controls.Add(covenantLabel); + } + } + } + + bool own = false; + string addressResp = await mainForm.APIGet("wallet/" + mainForm.Account + "/key/" + addressRaw["string"].ToString(),true); + if (addressResp != "Error") own = true; + + if (own) + { + Label ownAddress = new Label(); + ownAddress.Text = "Own Address"; + ownAddress.Location = new Point(PanelOutput.Width - 100, 5); + ownAddress.AutoSize = true; + PanelOutput.Controls.Add(ownAddress); + } + + Label amount = new Label(); + Decimal value = Decimal.Parse(output["value"].ToString()); + amount.Text = "Amount: " + value.ToString(); + amount.Location = new Point(PanelOutput.Width - 100, 25); + amount.AutoSize = true; + PanelOutput.Controls.Add(amount); + panelOut.Controls.Add(PanelOutput); + } + } + } +} diff --git a/FireWallet/ImportTXForm.resx b/FireWallet/ImportTXForm.resx new file mode 100644 index 0000000..0464da4 --- /dev/null +++ b/FireWallet/ImportTXForm.resx @@ -0,0 +1,587 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAUAEBAAAAEAIABoBAAAVgAAABgYAAABACAAiAkAAL4EAAAgIAAAAQAgAKgQAABGDgAAMDAAAAEA + IACoJQAA7h4AAAAAAAABACAAfScAAJZEAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAI8/zACPQM0Aj0DNEo9AzVePQM02j0DNAI9AzQAAAAAAAAAAAI8/zQCPQM0Aj0DNNI9A + zViPQM0Sj0DNAI8/zACPQM0Uj0DNXY9AzcOPQM37j0DN1o9AzTuPQM0Ajz/NAI4/zACPQM0Aj0DNOY9A + zdWPQM37j0DNxI9AzV6PQM0Vj0DNso9AzfqPQM3/j0DN/49Azf+PQM3Wj0DNO49AzQCPQM0Aj0DNOY9A + zdSPQM3/j0DN/49Azf+PQM36j0DNtY9AzdmPQM3/j0DN/49Azf+PQM3/j0DN/49AzdaPQM05j0DNN49A + zdWPQM3/j0DN/49Azf+PQM3/j0DN/49AzdyRQ8XYkUPF/5FDxf+RQ8X/kUPF/5FDxf+RQ8X/kkPFsZJD + xa+RQ8X/kUPF/5FDxf+RQ8X/kUPF/5FDxf+RQ8Xbmkuu2JpLrv+aS67/mkuv/ppLrv6aS67/mkuu/5pL + r76aS6+9mkuu/5pLrv+aS67+mkuv/ppLrv+aS67/mkuu26VSkNilUpD/pVKQ/6RSkOajUZWrpVKQ+aVS + kP+lUpC+pVKQvKVSkP+lUpD6o1GVq6RSkOWlUpD/pVKQ/6VSkNuwVG3YsFRt/7BUbf+wVG3drlRzKq5U + c4+wVG3/sFRtv7BUbb6wVG3/rlRzka5UcymwVG3csFRt/7BUbf+wVG3cuVNI2blTSP+5U0j/uVNI3rlT + Rxy0VWELuFNOlblTSLq5U0i5uFNOlrRVYAy5U0cbuVNI3blTSP+5U0j/uVNI3MFQHtnBUB7/wVAe/8FQ + Ht/AUB4ewFAkAL5RMxC/USxQv1EsUL5RMxDAUCUAwFAeHMFQHt3BUB7/wVAe/8FQHtzDTwnZw08J/8NP + Cf/DTwnfw08JHsNPCQDETgAAx0wAAcdNAAHFTQAAw08JAMNPCR3DTwnew08J/8NPCf/DTwncw08J2MNP + Cf/DTwn/w08Jw8NPCRXDTwkAAAAAAAAAAAAAAAAAAAAAAMNPCQDDTwkUw08JwcNPCf/DTwn/w08J28NP + CdbDTwnbw08JfMNPCSHDTggAw08JAAAAAAAAAAAAAAAAAAAAAADCTwkAwU4HAMNPCSDDTwl7w08J28NP + CdjDTwlVw08JI8NOCAHDTwkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDTwgAwk4IAcNP + CSLDTwlWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//AADH4wAAA8AAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAABmAAAAfg + AAAP8AAAH/gAAP//AAAoAAAAGAAAADAAAAABACAAAAAAAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI8/zACPP8wDjz/MAo8/ + zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI8/zQCPP80Cjz/NA48/zQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACPQM0Ajz/NBY9AzTePQM2Wj0DNYo4/zAOPP80AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAj0DNAI8/zQKPQM1gj0DNl49AzTmPP80Fjz/NAAAAAAAAAAAAjz/NAI8/zQWPQM04j0DNm49A + zeuPQM3/j0DN749AzWeOP8wDjz/NAAAAAAAAAAAAAAAAAAAAAACPP80Ajz/MAo9AzWSPQM3uj0DN/49A + zeuPQM2dj0DNOY8/zQWPQM0Aj0DNN49AzZyPQM3rj0DN/49Azf+PQM3/j0DN/49Aze+PQM1njz/MA48/ + zQAAAAAAAAAAAI9AzQCOP8wCj0DNZI9Aze6PQM3/j0DN/49Azf+PQM3/j0DN7I9AzZ6PQM05j0DNuY9A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3vj0DNZo4/zAOPQMwAjz/NAI8/zQKPQM1jj0DN7o9A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM2+j0DNwY9Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN749AzWeOP84Djj7OAo9AzWWPQM3uj0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3Gj0DMwY9AzP+PQMz/j0DM/49AzP+PQMz/j0DM/49AzP+PQMz/j0DM/49AzPCPQMtZj0DLVo9A + zO+PQMz/j0DM/49AzP+PQMz/j0DM/49AzP+PQMz/j0DM/49AzP+PQMzFkkTDwJJEw/+SRMP/kkTD/5JE + w/+SRMP/kkTD/5JEw/+SRMP/kkTD/5JEw/+SRMOekkTDm5JEw/+SRMP/kkTD/5JEw/+SRMP/kkTD/5JE + w/+SRMP/kkTD/5JEw/+SRMPFmEqzwJhKs/+YSrP/mEqz/5hKs/+YSrP/mEqz/5hKs/+YSrP/mEqz/5hK + s/+YSrOfmEqznJhKs/+YSrP/mEqz/5hKs/+YSrP/mEqz/5hKs/+YSrP/mEqz/5hKs/+YSrPFn0+gwJ9P + oP+fT6D/n0+g/59PoP+fT6D2n0+h7J9PoP+fT6D/n0+g/59PoP+fT6Cfn0+gm59PoP+fT6D/n0+g/59P + oP+fT6Hsn0+g9p9PoP+fT6D/n0+g/59PoP+fT6DGp1OKwadTiv+nU4r/p1OK/6dTiv+nU4rdpVKPYqZT + jNynU4r/p1OK/6dTiv+nU4qfp1OKnKdTiv+nU4r/p1OK/6ZTjN6lUo9jp1OK26dTiv+nU4r/p1OK/6dT + iv+nU4rGrlRzwa5Uc/+uVHP/rlRz/65Uc/+uVHPdrlRzHKxUe0StVHTfrlRz/65Uc/+uVHOgrlRzna5U + c/+uVHP/rlR04KxUe0auVHMarlRz265Uc/+uVHP/rlRz/65Uc/+uVHPGtVRawbVUWv+1VFr/tVRa/7VU + Wv+1VFrdtVRaHLZTVgCzVWJHtFRc37VUWv+1VFqhtVRanbVUWv+0VFzhs1RiSbdTVAC1VFoatVRa27VU + Wv+1VFr/tVRa/7VUWv+1VFrHu1JBwbtSQf+7UkH/u1JB/7tSQf+7UkHdu1JBHbtSQgDAUikAuVNKR7tT + Q+K7UkGgu1JBnbtTQ+S5U0lJw1AYALtSQgC7UkEau1JB27tSQf+7UkH/u1JB/7tSQf+7UkHHwFAlwcBQ + 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 + zf+PQM38j0DNl48/zA+PP80AAAAAAAAAAAAAAAAAAAAAAI9AzQCPQM0Oj0DNk49AzfuPQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/Y9AzdWPQM1ej0DNp49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM38j0DNl48/zA+PQM0AAAAAAAAAAACPQM0Aj0DNDo9AzZOPQM37j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Aza6PQM2oj0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM38j0DNl48/zRCPP80Aj0DNAI8/zQ+PQM2Uj0DN+49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DNr49AzaePQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM38j0DNmo4/zQ+PP80Oj0DNlo9A + zfyPQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM2ukEHKp5BB + yv+QQcr/kEHK/5BByv+QQcr/kEHK/5BByv+QQcr/kEHK/5BByv+QQcr/kEHK/5BByv+QQcr9kEHJZ5BB + yWKQQcr8kEHK/5BByv+QQcr/kEHK/5BByv+QQcr/kEHK/5BByv+QQcr/kEHK/5BByv+QQcr/kEHK/49B + yq6TRcGnk0XB/5NFwf+TRcH/k0XB/5NFwf+TRcH/k0XB/5NFwf+TRcH/k0XB/5NFwf+TRcH/k0XB/5NF + wf+TRcGEk0XBf5NFwf+TRcH/k0XB/5NFwf+TRcH/k0XB/5NFwf+TRcH/k0XB/5NFwf+TRcH/k0XB/5NF + wf+TRcH/k0XBrpdJtaeXSbX/l0m1/5dJtf+XSbX/l0m1/5dJtf+XSbX/l0m1/5dJtf+XSbX/l0m1/5dJ + tf+XSbX/l0m1/5dJtYKXSbZ9l0m1/5dJtf+XSbX/l0m1/5dJtf+XSbX/l0m1/5dJtf+XSbX/l0m1/5dJ + tf+XSbX/l0m1/5dJtf+XSbWunE2op5xNqP+cTaj/nE2o/5xNqP+cTaj/nE2o/5xNqP+cTaf/nE2o/5xN + qP+cTaj/nE2o/5xNqP+cTaj/nE2ogZxNqHycTaj/nE2o/5xNqP+cTaj/nE2o/5xNqP+cTaf/nE2o/5xN + qP+cTaj/nE2o/5xNqP+cTaj/nE2o/5xNqK6iUZinolGY/6JRmP+iUZj/olGY/6JRmP+iUZj/olGY6KFQ + mrmiUZj8olGY/6JRmP+iUZj/olGY/6JRmP+iUZiBolGYfKJRmP+iUZj/olGY/6JRmP+iUZj/olGY/KFQ + mrqiUZjnolGY/6JRmP+iUZj/olGY/6JRmP+iUZj/olGYrqhTh6eoU4f/qFOH/6hTh/+oU4f/qFOH/6hT + h/+oU4fcplOLL6dTip6oU4f9qFOH/6hTh/+oU4f/qFOH/6hTh4KnU4d9qFOH/6hTh/+oU4f/qFOH/6hT + h/6nU4qiplOLLqhTh9moU4f/qFOH/6hTh/+oU4f/qFOH/6hTh/+oU4evrVR2p61Udv+tVHb/rVR2/61U + dv+tVHb/rVR2/61UdtytVHYbqlSAEaxUeaGtVHb9rVR2/61Udv+tVHb/rVR2g61Udn6tVHb/rVR2/61U + dv+tVHb+rFR5pKpUfxOtVHYYrVR22q1Udv+tVHb/rVR2/61Udv+tVHb/rVR2/61Udq+yVGSoslVk/7JV + ZP+yVWT/slVk/7JVZP+yVWT/slVk3LJUZByxVWcAsFVtFLJVZ6KyVWT+slVk/7JVZP+yVGSEslRkf7JV + ZP+yVWT/slVk/rJVZqawVW0VsVRnALJUZBmyVGTZslVk/7JVZP+yVWT/slVk/7JVZP+yVWT/slRkr7dU + Uai3VFH/t1RR/7dUUf+3VFH/t1RR/7dUUf+3VFHct1NRHLdTUQC1VFoAtVRaFLdUVKO3VFH+t1RR/7dU + UYO3VFF+t1RR/7dUUf63VFSmtVRaFrVUWQC3U1EAt1NRGbdUUdm3VFH/t1RR/7dUUf+3VFH/t1RR/7dU + Uf+3VFGwvFI+qLxSPv+8Uj7/vFI+/7xSPv+8Uj7/vFI+/7xSPty7Uj4cu1I+AMNRHQC6U0cAulNIFLtS + QaG8Uj7/vFI+gbxSPny8Uj7/u1JBpbpTSBa6U0cAv1E0ALtSPgC7Uj4ZvFI+2bxSPv+8Uj7/vFI+/7xS + Pv+8Uj7/vFI+/7xSPrC/UCqov1Eq/79RKv+/USr/v1Eq/79RKv+/USr/v1Aq3b9QKRy/UCkAAAAAANJE + AAC+UTMAvlE0E79RLZ+/UCp6v1Eqdr9RLaK+UTQUvlEzAMNNFwAAAAAAv1ApAL9QKRm/UCrZv1Eq/79R + Kv+/USr/v1Eq/79RKv+/USr/v1AqsMJPEqjCTxL/wk8S/8JPEv/CTxL/wk8S/8JPEv/CTxLdwk8SHcJP + EgAAAAAAAAAAAAAAAADBTx4AwU8eE8FPHCfBUBwnwVAeFMFQHgD/AAAAAAAAAAAAAADCTxIAwk8SGsJP + EtrCTxL/wk8S/8JPEv/CTxL/wk8S/8JPEv/CTxKww08JqMNPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NP + Cd7DTwgdw08IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMNP + CADDTwgaw08J2sNPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCbDDTwmow08J/8NPCf/DTwn/w08J/8NP + Cf/DTwn/w08J3sNPCR3DTwkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAw08JAMNPCRvDTwnbw08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08Jr8NPCafDTwn/w08J/8NP + Cf/DTwn/w08J/8NPCf/DTwndw08JHMNPCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADDTwkAw08JGsNPCdrDTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwmuw08Jp8NP + Cf/DTwn/w08J/8NPCf/DTwn/w08J88NPCZrDTgkMw08JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJPCQDCTwkKw08JlsNPCfLDTwn/w08J/8NPCf/DTwn/w08J/8NP + Ca3DTwmmw08J/8NPCf/DTwn/w08J8sNPCavDTwlGw08JCcNPCQDCTgcAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw08GAMNPCQDCTwgIw08JRMNPCanDTwnxw08J/8NP + Cf/DTwn/w08JrMNPCaTDTwn/w08J8cNPCazDTwlGw04ICcNPCADDTgkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMFMCQDDTwkAwk8JCMNP + CUTDTwmqw08J8cNPCf/DTwmqw08JkcNPCarDTwlFw08ICcNPCQDCTAgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADCTwYAw08JAMNPCQjDTwlDw08JqcNPCZbDTwkXw08JCcNPCQC/SwUAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAw00FAMNPCQDDTgkIw04JGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////5/ + /j/4P/wf4B/4B4AP8AAAB+AAAAPAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABA + AgAAYAYAAHAOAAB4HgAAfD4AAH/+AAB//gAAf/4AAH/+AAD//wAD///AD///8D////z//////////ygA + AAAwAAAAYAAAAAEAIAAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjz7LAI8+ + ywCPPssAjz7LAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjkDLAI4/ywCOP8sAjkDLAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI5A + zACOP8wBjz/NJY9AzXmPQM1Ig0LIAI5AzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACOP8wAkkfWAI9AzUOPQM17j0DNKI8/ + zQKPP80AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACPP8wAjj/LAY8/zCaPQM2Cj0DN3I9Azf+PQM3hj0DNToQ2wQCOP8wAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI8/zQCORMsAj0DNSI9A + zd6PQM3/j0DN3o9AzYWPP80ojj7MAo8/zQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAjz/NAI8+zAGPQM0mj0DNgo9AzdyPQM3+j0DN/49Azf+PQM3/j0DN449AzU+QPcsAjz/NAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjz/NAI9J + zwCPQM1Jj0DN349Azf+PQM3/j0DN/49Azf6PQM3ej0DNhY8/zSiPPswCjz/MAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAI8/zACOPswCjz/NJ49AzYOPQM3dj0DN/o9Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zeSPQM1QijbKAI4/zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACOP8wAk0XXAI9AzUqPQM3gj0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/o9Azd+PQM2Gjz/NKY8/ + zAKPP80AAAAAAAAAAACPP8wAjj7MAo9AzSqPQM2Fj0DN3o9Azf6PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3kjz/NT4w6xgCOP8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAI8/zQCTS84Ajz/NSo9AzeCPQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3+j0DN349AzYiPP80sjj/NA44/zQCPQM0Xj0DNhI9AzeGPQM3+j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN449AzVCQLsMAjz/MAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAjj/NAJJB0ACPQM1Kj0DN4I9Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf6PQM3jj0DNiY8/zRqPQM1nj0DN+o9Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49AzeOPP81PhzjFAI4/ + zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACPQM0Ak0HLAI9AzUmPQM3gj0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/I8/zXOPQM11j0DN/Y9A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3jj0DNT4c/wwCOP8wAAAAAAAAAAAAAAAAAAAAAAI8/zQCNSs4Aj0DNSo9AzeCPQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/48/ + zX+PQM11j0DN/Y9Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN449AzVCNOM0Ajj/NAAAAAAAAAAAAjj/MAJdJ3gCPP81Kj0DN4I9A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/o8/zX6PQM10j0DN/Y9Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49AzeSPQM1Si0HNAY5AzQCPP80AkDjLAI9A + zU2PQM3hj0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/o8/zX2PQM1zj0DN/Y9Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3mjz/NVY07 + zwGRRsoAjz/NT49AzeOPQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/o8/zX2PQMtzj0DL/Y9Ay/+PQMv/j0DL/49A + y/+PQMv/j0DL/49Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/49A + y/+PQMv/j0DL349AyymPQMslj0DL2o9Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/49A + y/+PQMv/j0DL/49Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/o9Ay32RQ8ZykUPG/ZFD + xv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FD + xv+RQ8b/kUPG/5FDxv+RQ8b/kUPG+5FDxk2RQ8ZGkUPG+JFDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FD + xv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/pFD + xn2URsBylEbA/ZRGwP+URsD/lEbA/5RGwP+URsD/lEbA/5RGwP+URsD/lEbA/5RGwP+URsD/lEbA/5RG + wP+URsD/lEbA/5RGwP+URsD/lEbA/5RGwP+URsD/lEbA/JRGwFCURsBJlEbA+ZRGwP+URsD/lEbA/5RG + wP+URsD/lEbA/5RGwP+URsD/lEbA/5RGwP+URsD/lEbA/5RGwP+URsD/lEbA/5RGwP+URsD/lEbA/5RG + wP+URsD/lEbA/pRGwH2XSbhyl0m4/ZdJuP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJ + uP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJuP+XSbj/l0m4+5dJuE6XSbhGl0m4+ZdJ + uP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJ + uP+XSbj/l0m4/5dJuP+XSbj/l0m4/pdJuH2aS69ymkyv/ZpMr/+aTK//mkyv/5pMr/+aTK//mkyv/5pM + r/+aTK//mkyv/5pMr/+aTK//mkyv/5pMr/+aTK//mkyv/5pMr/+aTK//mkyv/5pMr/+aTK//mkyv+5pM + r02aTK9Fmkyv+JpMr/+aTK//mkyv/5pMr/+aTK//mkyv/5pMr/+aTK//mkyv/5pMr/+aTK//mkyv/5pM + r/+aTK//mkyv/5pMr/+aTK//mkyv/5pMr/+aTK//mkyv/ppMr32dTqVynU6l/Z1Opf+dTqX/nU6l/51O + pf+dTqX/nU6l/51Opf+dTqX/nU6l/51Opf+dTqX/nU6l/51Opf+dTqX/nU6l/51Opf+dTqX/nU6l/51O + pf+dTqX/nU6l+51OpU2dTqVEnU6l+J1Opf+dTqX/nU6l/51Opf+dTqX/nU6l/51Opf+dTqX/nU6l/51O + pf+dTqX/nU6l/51Opf+dTqX/nU6l/51Opf+dTqX/nU6l/51Opf+dTqX/nU6l/p1OpX2hUJtyoVCb/aFQ + m/+hUJv/oVCb/6FQm/+hUJv/oVCb/6FQm/+hUJv/oVCb/6FQm+uhUJzHoVCb/aFQm/+hUJv/oVCb/6FQ + m/+hUJv/oVCb/6FQm/+hUJv/oVCb+6FQm02hUJtFoVCb+KFQm/+hUJv/oVCb/6FQm/+hUJv/oVCb/6FQ + m/+hUJv/oVCb/qFQnMihUJvpoVCb/6FQm/+hUJv/oVCb/6FQm/+hUJv/oVCb/6FQm/+hUJv/oVCb/qFQ + m32lUpBypVKQ/aVSkP+lUpD/pVKQ/6VSkP+lUpD/pVKQ/6VSkP+lUpD/pVKQ/6VSkNqkUpI3pFKRraVS + kP+lUpD/pVKQ/6VSkP+lUpD/pVKQ/6VSkP+lUpD/pVKQ+6VSkE2lUpBFpVKQ+KVSkP+lUpD/pVKQ/6VS + kP+lUpD/pVKQ/6VSkP+lUpD/pFKRsqRSkzalUpDVpVKQ/6VSkP+lUpD/pVKQ/6VSkP+lUpD/pVKQ/6VS + kP+lUpD/pVKQ/qVSkH6oU4VzqFOF/ahThf+oU4X/qFOF/6hThf+oU4X/qFOF/6hThf+oU4X/qFOF/6hT + hdupU4QaplOKGahThq+oU4X/qFOF/6hThf+oU4X/qFOF/6hThf+oU4X/qFOF+6hThU6oU4VHqFOF+ahT + hf+oU4X/qFOF/6hThf+oU4X/qFOF/6hThf+oU4a0p1OKHalThBeoU4XXqFOF/6hThf+oU4X/qFOF/6hT + hf+oU4X/qFOF/6hThf+oU4X/qFOF/6hThX6sVHlzrFR5/axUef+sVHn/rFR5/6xUef+sVHn/rFR5/6xU + ef+sVHn/rFR5/6xUedqsVHkbq1R7AKpUfhusVHuwrFR5/6xUef+sVHn/rFR5/6xUef+sVHn/rFR5+6xU + eU+sVHlIrFR5+axUef+sVHn/rFR5/6xUef+sVHn/rFR5/6xUerWqVH4eq1R7AKxUeRisVHnWrFR5/6xU + ef+sVHn/rFR5/6xUef+sVHn/rFR5/6xUef+sVHn/rFR5/6xUeX+wVW1zsFVt/bBVbf+wVW3/sFVt/7BV + bf+wVW3/sFVt/7BVbf+wVW3/sFVt/7BVbdqwVW0bsFVtAK5VcQCuVHIcr1VvsrBVbf+wVW3/sFVt/7BV + bf+wVW3/sFVt+7BVbVCwVW1IsFVt+bBVbf+wVW3/sFVt/7BVbf+wVW3/r1Vutq5Uch+uVXEAsFVtALBV + bRewVW3WsFVt/7BVbf+wVW3/sFVt/7BVbf+wVW3/sFVt/7BVbf+wVW3/sFVt/7BVbX+zVGF0s1Rh/bNU + Yf+zVGH/s1Rh/7NUYf+zVGH/s1Rh/7NUYf+zVGH/s1Rh/7NUYdqzVGEbs1RhALJUZACyVWUAslVmHbNU + YrOzVGD/s1Rh/7NUYf+zVGH/s1Rh+7NUYVCzVGFJs1Rh+bNUYf+zVGH/s1Rh/7NUYP+zVGK3slVmILJV + ZQCyVGYAs1RhALNUYRezVGHVs1Rh/7NUYf+zVGH/s1Rh/7NUYf+zVGH/s1Rh/7NUYf+zVGH/s1Rh/7NU + YX+3VFR0t1RU/bdUVP+3VFT/t1RU/7dUVP+3VFT/t1RU/7dUVP+3VFT/t1RU/7dUVNq2VFQbtlRUAAAA + AAC5UE8AtVRZALVUWh62VFazt1RU/7dUVP+3VFT/t1RU+7dUVE+2VFRIt1RU+bdUVP+3VFT/t1RU/7ZU + Vri1VFohtVRZALZSVgAAAAAAtlNUALZTVBe2VFTVt1RU/7dUVP+3VFT/t1RU/7dUVP+3VFT/t1RU/7dU + VP+3VFT/t1RU/7ZUVIC6U0h0ulNI/bpTSP+6U0j/ulNI/7pTSP+6U0j/ulNI/7pTSP+6U0j/ulNI/7pT + SNq6U0gbulNIAAAAAAAAAAAAuFNPALlTTAC4U00euVNJs7pTSP+6U0j/uVNI+7lTSE65U0hGuVNI+bpT + SP+6U0j/uVNJt7hTTSG4U00AvlQ5AAAAAAAAAAAAuVNIALlTSBe5U0jVulNI/7pTSP+6U0j/ulNI/7pT + SP+6U0j/ulNI/7pTSP+6U0j/ulNI/7pTSIC8Ujt0vFI7/bxSO/+8Ujv/vFI7/7xSO/+8Ujv/vFI7/7xS + O/+8Ujv/vFI7/7xSO9q8UjsbvFI7AAAAAAAAAAAAAAAAALtRPwC7UkAAu1JBHbxSPbC8Ujv/vFI7+7xS + O0y8UjtEvFI7+LxSO/+8Ujy1u1JBILtSPwC7UkEAAAAAAAAAAAAAAAAAvFI7ALxSOxe8UjvVvFI7/7xS + O/+8Ujv/vFI7/7xSO/+8Ujv/vFI7/7xSO/+8Ujv/vFI7/7xSO4C/US11v1Et/b9RLf+/US3/v1Et/79R + Lf+/US3/v1Et/79RLf+/US3/v1Et/79RLdu/US0bv1EtAAAAAAAAAAAAAAAAAAAAAADAUBwAvlEzAL5R + NBq+US+rv1Et+L9RLUm/US1Cv1Et9r5RL7C+UTMdvlEzAMBOKwAAAAAAAAAAAAAAAAAAAAAAvlEtAL5R + LRe/US3Vv1Et/79RLf+/US3/v1Et/79RLf+/US3/v1Et/79RLf+/US3/v1Et/79RLYDBUB51wVAe/cFQ + Hv/BUB7/wVAe/8FQHv/BUB7/wVAe/8FQHv/BUB7/wVAe/8FQHtvBUB4cwVAeAAAAAAAAAAAAAAAAAAAA + AAAAAAAAwU8kAMBQJQDAUCYYwFAhm8FQH0DBUB86wVAhncBQJhvAUCUAwFAlAAAAAAAAAAAAAAAAAAAA + AAAAAAAAwVAeAMFPHhfBUB7WwVAe/8FQHv/BUB7/wVAe/8FQHv/BUB7/wVAe/8FQHv/BUB7/wVAe/8FQ + HoDCTw51wk8O/cNPDv/DTw7/w08O/8NPDv/DTw7/w08O/8NPDv/DTw7/w08O/8JPDtzCTw4cwk8OAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMM9AADCTxcAwk8WD8JPFg7CTxYNwk8WEMJPFwDETwUAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAwk8OAMJPDhjCTw7Ww08O/8NPDv/DTw7/w08O/8NPDv/DTw7/w08O/8NP + Dv/DTw7/w08O/8JPDoDDTwh0w08J/cNPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NP + CdzDTggdw04IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwk8IAMJPCBjDTwnXw08J/8NPCf/DTwn/w08J/8NP + Cf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCIDDTwl0w08J/cNPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NP + Cf/DTwn/w08J/8NPCdzDTwgdw08IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw04IAMNOCBnDTwnXw08J/8NP + Cf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCX/DTwl0w08J/cNPCf/DTwn/w08J/8NP + Cf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCd3DTwgdw08IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw08IAMNP + CBnDTwnYw08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCX/DTwlzw08J/cNP + Cf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCdzDTwgdw08IAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAw08IAMNPCBnDTwnYw08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/sNP + CX7DTwlzw08J/cNPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCdXDTgkXw04JAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAwk8JAMJPCRTDTwnQw08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NP + Cf/DTwn/w08J/sNPCX3DTwlyw08J/cNPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn9w08J1cNP + CWjDTggEw04IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwk8IAMFOCAPDTwljw08J08NPCfzDTwn/w08J/8NP + Cf/DTwn/w08J/8NPCf/DTwn/w08J/sNPCXvDTwlxw08J/cNPCf/DTwn/w08J/8NPCf/DTwn/w08J/cNP + CdPDTwl0w04JHsNPBwHDTwgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJPCAC/TQcAw08JHMNP + CXHDTwnRw08J/MNPCf/DTwn/w08J/8NPCf/DTwn/w08J/sNPCXrDTwlww08J/cNPCf/DTwn/w08J/8NP + Cf3DTwnUw08JdMNOCR3DTQcAw04IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAwk4IAMFLBwDCTwkbw08JccNPCdHDTwn8w08J/8NPCf/DTwn/w08J/sNPCXjDTwluw08J/MNP + Cf/DTwn8w08J08NPCXXDTwkewU0EAMJOBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCTgkAv00KAMNOCBzDTwlyw08J0cNPCfzDTwn/w08J/sNP + CXbDTwltw08J+cNPCdDDTwlyw04JHcFPBgDCTggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJOCQDDTgkAw08JG8NP + CW/DTwnOw08J+cNPCXXDTwlGw08JaMJPCRrATAUAwk4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAwk8IAL5QBADDTwgZw08JaMNOCUrDTQgBw00IAMNNCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDTggAw04IAMNPCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + RU5ErkJggg== + + + \ No newline at end of file From 6da345e65012ff81424134528e39c710595576d9 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Mon, 26 Jun 2023 18:13:08 +1000 Subject: [PATCH 04/11] multisig: Added info on signers Added variables to track the number of total signers, required signers and signed signatures. Also added code to get multisig information from transaction inputs and display it in the UI. --- FireWallet/ImportTXForm.Designer.cs | 79 ++++++++++++++++-- FireWallet/ImportTXForm.cs | 125 +++++++++++++++++++++++----- 2 files changed, 174 insertions(+), 30 deletions(-) diff --git a/FireWallet/ImportTXForm.Designer.cs b/FireWallet/ImportTXForm.Designer.cs index 0ce2c2b..2e46a28 100644 --- a/FireWallet/ImportTXForm.Designer.cs +++ b/FireWallet/ImportTXForm.Designer.cs @@ -35,6 +35,11 @@ panelOut = new Panel(); buttonSign = new Button(); Cancelbutton2 = new Button(); + label1 = new Label(); + labelSigsTotal = new Label(); + labelSigsReq = new Label(); + labelSigsSigned = new Label(); + labelSigInfo = new Label(); groupBoxIn.SuspendLayout(); groupBoxOut.SuspendLayout(); SuspendLayout(); @@ -44,7 +49,7 @@ groupBoxIn.Controls.Add(panelIn); groupBoxIn.Location = new Point(12, 83); groupBoxIn.Name = "groupBoxIn"; - groupBoxIn.Size = new Size(341, 355); + groupBoxIn.Size = new Size(376, 355); groupBoxIn.TabIndex = 3; groupBoxIn.TabStop = false; groupBoxIn.Text = "Inputs"; @@ -55,15 +60,15 @@ panelIn.Dock = DockStyle.Fill; panelIn.Location = new Point(3, 19); panelIn.Name = "panelIn"; - panelIn.Size = new Size(335, 333); + panelIn.Size = new Size(370, 333); panelIn.TabIndex = 0; // // groupBoxOut // groupBoxOut.Controls.Add(panelOut); - groupBoxOut.Location = new Point(359, 83); + groupBoxOut.Location = new Point(391, 80); groupBoxOut.Name = "groupBoxOut"; - groupBoxOut.Size = new Size(429, 355); + groupBoxOut.Size = new Size(484, 355); groupBoxOut.TabIndex = 0; groupBoxOut.TabStop = false; groupBoxOut.Text = "Outputs"; @@ -74,25 +79,26 @@ panelOut.Dock = DockStyle.Fill; panelOut.Location = new Point(3, 19); panelOut.Name = "panelOut"; - panelOut.Size = new Size(423, 333); + panelOut.Size = new Size(478, 333); panelOut.TabIndex = 0; // // buttonSign // buttonSign.FlatStyle = FlatStyle.Flat; buttonSign.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); - buttonSign.Location = new Point(705, 444); + buttonSign.Location = new Point(789, 444); buttonSign.Name = "buttonSign"; buttonSign.Size = new Size(83, 36); buttonSign.TabIndex = 2; buttonSign.Text = "Sign"; buttonSign.UseVisualStyleBackColor = true; + buttonSign.Click += buttonSign_Click; // // Cancelbutton2 // Cancelbutton2.FlatStyle = FlatStyle.Flat; Cancelbutton2.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); - Cancelbutton2.Location = new Point(616, 444); + Cancelbutton2.Location = new Point(700, 444); Cancelbutton2.Name = "Cancelbutton2"; Cancelbutton2.Size = new Size(83, 36); Cancelbutton2.TabIndex = 2; @@ -100,11 +106,60 @@ Cancelbutton2.UseVisualStyleBackColor = true; Cancelbutton2.Click += Cancelbutton2_Click; // + // label1 + // + label1.AutoSize = true; + label1.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); + label1.Location = new Point(12, 9); + label1.Name = "label1"; + label1.Size = new Size(87, 21); + label1.TabIndex = 4; + label1.Text = "Signatures:"; + // + // labelSigsTotal + // + labelSigsTotal.BackColor = Color.Blue; + labelSigsTotal.Location = new Point(105, 7); + labelSigsTotal.Name = "labelSigsTotal"; + labelSigsTotal.Size = new Size(250, 32); + labelSigsTotal.TabIndex = 5; + // + // labelSigsReq + // + labelSigsReq.BackColor = Color.FromArgb(255, 128, 0); + labelSigsReq.Location = new Point(105, 7); + labelSigsReq.Name = "labelSigsReq"; + labelSigsReq.Size = new Size(191, 32); + labelSigsReq.TabIndex = 6; + // + // labelSigsSigned + // + labelSigsSigned.BackColor = Color.Lime; + labelSigsSigned.Location = new Point(105, 7); + labelSigsSigned.Name = "labelSigsSigned"; + labelSigsSigned.Size = new Size(100, 32); + labelSigsSigned.TabIndex = 7; + // + // labelSigInfo + // + labelSigInfo.AutoSize = true; + labelSigInfo.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); + labelSigInfo.Location = new Point(361, 7); + labelSigInfo.Name = "labelSigInfo"; + labelSigInfo.Size = new Size(19, 21); + labelSigInfo.TabIndex = 8; + labelSigInfo.Text = "#"; + // // ImportTXForm // AutoScaleDimensions = new SizeF(7F, 15F); AutoScaleMode = AutoScaleMode.Font; - ClientSize = new Size(800, 485); + ClientSize = new Size(887, 485); + Controls.Add(labelSigInfo); + Controls.Add(labelSigsSigned); + Controls.Add(labelSigsReq); + Controls.Add(labelSigsTotal); + Controls.Add(label1); Controls.Add(groupBoxOut); Controls.Add(groupBoxIn); Controls.Add(Cancelbutton2); @@ -113,11 +168,12 @@ Icon = (Icon)resources.GetObject("$this.Icon"); MaximizeBox = false; Name = "ImportTXForm"; - Text = "ImportTXForm"; + Text = "Import TX"; Load += ImportTXForm_Load; groupBoxIn.ResumeLayout(false); groupBoxOut.ResumeLayout(false); ResumeLayout(false); + PerformLayout(); } #endregion @@ -127,5 +183,10 @@ private Button Cancelbutton2; private Panel panelIn; private Panel panelOut; + private Label label1; + private Label labelSigsTotal; + private Label labelSigsReq; + private Label labelSigsSigned; + private Label labelSigInfo; } } \ No newline at end of file diff --git a/FireWallet/ImportTXForm.cs b/FireWallet/ImportTXForm.cs index d9696e9..ab9de67 100644 --- a/FireWallet/ImportTXForm.cs +++ b/FireWallet/ImportTXForm.cs @@ -6,6 +6,9 @@ namespace FireWallet { MainForm mainForm; JObject tx; + int totalSigs; + int reqSigs; + int sigs; public ImportTXForm(MainForm mainForm) { InitializeComponent(); @@ -14,6 +17,11 @@ namespace FireWallet private void ImportTXForm_Load(object sender, EventArgs e) { + // Default variables + totalSigs = 3; + reqSigs = 2; + sigs = 0; + // Theme this.BackColor = ColorTranslator.FromHtml(mainForm.Theme["background"]); this.ForeColor = ColorTranslator.FromHtml(mainForm.Theme["foreground"]); @@ -74,6 +82,58 @@ namespace FireWallet JArray inputs = (JArray)json["result"]["vin"]; JArray outputs = (JArray)json["result"]["vout"]; + + // Get multisig info + JObject firstIn = (JObject)inputs[0]; + JArray witnesses = (JArray)firstIn["txinwitness"]; + + string scriptSig = witnesses[witnesses.Count - 1].ToString(); + // decode script + string sigGetContent = "{\"method\":\"decodescript\",\"params\":[\"" + scriptSig + "\"]}"; + string sigGetResponse = await mainForm.APIPost("", false, sigGetContent); + if (sigGetResponse == null) + { + NotifyForm notifyForm = new NotifyForm("Error decoding transaction"); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + return; + } + JObject sigGetJson = JObject.Parse(sigGetResponse); + if (sigGetJson["error"].ToString() != "") + { + NotifyForm notifyForm = new NotifyForm("Error decoding transaction"); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + return; + } + JObject sigGetResult = (JObject)sigGetJson["result"]; + string[] asm = sigGetResult["asm"].ToString().Split(" "); + string totalSigsStr = asm[asm.Length - 2]; + totalSigs = int.Parse(totalSigsStr.Replace("OP_","")); + reqSigs = int.Parse(sigGetResult["reqSigs"].ToString()); + sigs = -1; + for (int i = 0; i < witnesses.Count; i++) + { + string witness = witnesses[i].ToString(); + if (witness != "") + { + sigs++; + } + } + + + + + + // Set sig label sizes + labelSigsReq.Width = (labelSigsTotal.Width / totalSigs) * reqSigs; + labelSigsSigned.Width = (labelSigsTotal.Width / totalSigs) * sigs; + labelSigInfo.Text = "Signed: " + sigs + "\nReq: " + reqSigs + " of "+ totalSigs; + + + + + for (int i = 0; i < inputs.Count; i++) { JObject input = (JObject)inputs[i]; @@ -84,11 +144,6 @@ namespace FireWallet PanelInput.Location = new Point(5, panelIn.Controls.Count * 50); PanelInput.BorderStyle = BorderStyle.FixedSingle; - Label txid = new Label(); - txid.Text = "TXID: " + input["txid"].ToString(); - txid.Location = new Point(5, 5); - txid.AutoSize = true; - PanelInput.Controls.Add(txid); if (metaInput.ContainsKey("sighashType")) { @@ -99,19 +154,40 @@ namespace FireWallet PanelInput.Controls.Add(sighashType); } - //Label address = new Label(); - //string addressString = input["address"].ToString().Substring(0, 5) + "..." + input["address"].ToString().Substring(input["address"].ToString().Length - 5, 5); - //address.Text = "Address: " + addressString; - //address.Location = new Point(5, 5); - //address.AutoSize = true; - //PanelInput.Controls.Add(address); + string txid = input["txid"].ToString(); + int vout = int.Parse(input["vout"].ToString()); + string txInfo = await mainForm.APIGet("tx/" + txid, false); + if (txInfo == "Error" || txInfo == "") + { + NotifyForm notifyForm = new NotifyForm("Error getting transaction info"); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + return; + } + else + { + JObject txInfoJson = JObject.Parse(txInfo); + JArray txoutputs = (JArray)txInfoJson["outputs"]; + JObject txoutput = (JObject)txoutputs[vout]; + string txoutputAddress = txoutput["address"].ToString(); + + Label address = new Label(); + string addressString = txoutputAddress.Substring(0, 5) + "..." + txoutputAddress.Substring(txoutputAddress.Length - 5, 5); + address.Text = "Address: " + addressString; + address.Location = new Point(5, 5); + address.AutoSize = true; + PanelInput.Controls.Add(address); + + Label amount = new Label(); + Decimal value = Decimal.Parse(txoutput["value"].ToString()) / 1000000; + amount.Text = "Amount: " + value.ToString(); + amount.Location = new Point(5, 25); + amount.AutoSize = true; + PanelInput.Controls.Add(amount); + + } + - //Label amount = new Label(); - //Decimal value = Decimal.Parse(input["value"].ToString()) / 1000000; - //amount.Text = "Amount: " + value.ToString(); - //amount.Location = new Point(5, 25); - //amount.AutoSize = true; - //PanelInput.Controls.Add(amount); //if (input["path"].ToString() != "") //{ @@ -129,7 +205,7 @@ namespace FireWallet { JObject output = (JObject)outputs[i]; JObject metaOutput = (JObject)metaOutputs[i]; - + Panel PanelOutput = new Panel(); PanelOutput.Size = new Size(panelOut.Width - SystemInformation.VerticalScrollBarWidth - 10, 50); PanelOutput.Location = new Point(5, panelOut.Controls.Count * 50); @@ -167,14 +243,14 @@ namespace FireWallet } bool own = false; - string addressResp = await mainForm.APIGet("wallet/" + mainForm.Account + "/key/" + addressRaw["string"].ToString(),true); + string addressResp = await mainForm.APIGet("wallet/" + mainForm.Account + "/key/" + addressRaw["string"].ToString(), true); if (addressResp != "Error") own = true; if (own) { Label ownAddress = new Label(); ownAddress.Text = "Own Address"; - ownAddress.Location = new Point(PanelOutput.Width - 100, 5); + ownAddress.Location = new Point(PanelOutput.Width - 150, 5); ownAddress.AutoSize = true; PanelOutput.Controls.Add(ownAddress); } @@ -182,11 +258,18 @@ namespace FireWallet Label amount = new Label(); Decimal value = Decimal.Parse(output["value"].ToString()); amount.Text = "Amount: " + value.ToString(); - amount.Location = new Point(PanelOutput.Width - 100, 25); + amount.Location = new Point(PanelOutput.Width - 150, 25); amount.AutoSize = true; PanelOutput.Controls.Add(amount); panelOut.Controls.Add(PanelOutput); } } + + private async void buttonSign_Click(object sender, EventArgs e) + { + string content = "{\"tx\":\"" + tx["tx"].ToString() + "\", \"passphrase\":\"" + mainForm.Password + "\"}"; + string response = await mainForm.APIPost("wallet/" + mainForm.Account + "/sign", true, content); + mainForm.AddLog(response); + } } } From 54d5c63db7575d802546c103c5f25c44e3f13e3c Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Mon, 26 Jun 2023 18:48:12 +1000 Subject: [PATCH 05/11] multisig: Finished signing for hot wallets --- FireWallet/ImportTXForm.Designer.cs | 35 +++++++++++++- FireWallet/ImportTXForm.cs | 75 ++++++++++++++++++++++++++--- FireWallet/MainForm.cs | 2 +- 3 files changed, 103 insertions(+), 9 deletions(-) diff --git a/FireWallet/ImportTXForm.Designer.cs b/FireWallet/ImportTXForm.Designer.cs index 2e46a28..3007d92 100644 --- a/FireWallet/ImportTXForm.Designer.cs +++ b/FireWallet/ImportTXForm.Designer.cs @@ -40,6 +40,8 @@ labelSigsReq = new Label(); labelSigsSigned = new Label(); labelSigInfo = new Label(); + buttonExport = new Button(); + buttonSend = new Button(); groupBoxIn.SuspendLayout(); groupBoxOut.SuspendLayout(); SuspendLayout(); @@ -86,7 +88,7 @@ // buttonSign.FlatStyle = FlatStyle.Flat; buttonSign.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); - buttonSign.Location = new Point(789, 444); + buttonSign.Location = new Point(700, 444); buttonSign.Name = "buttonSign"; buttonSign.Size = new Size(83, 36); buttonSign.TabIndex = 2; @@ -98,7 +100,7 @@ // Cancelbutton2.FlatStyle = FlatStyle.Flat; Cancelbutton2.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); - Cancelbutton2.Location = new Point(700, 444); + Cancelbutton2.Location = new Point(12, 441); Cancelbutton2.Name = "Cancelbutton2"; Cancelbutton2.Size = new Size(83, 36); Cancelbutton2.TabIndex = 2; @@ -150,6 +152,31 @@ labelSigInfo.TabIndex = 8; labelSigInfo.Text = "#"; // + // buttonExport + // + buttonExport.Enabled = false; + buttonExport.FlatStyle = FlatStyle.Flat; + buttonExport.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); + buttonExport.Location = new Point(611, 444); + buttonExport.Name = "buttonExport"; + buttonExport.Size = new Size(83, 36); + buttonExport.TabIndex = 2; + buttonExport.Text = "Export"; + buttonExport.UseVisualStyleBackColor = true; + buttonExport.Click += buttonExport_Click; + // + // buttonSend + // + buttonSend.FlatStyle = FlatStyle.Flat; + buttonSend.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); + buttonSend.Location = new Point(789, 444); + buttonSend.Name = "buttonSend"; + buttonSend.Size = new Size(83, 36); + buttonSend.TabIndex = 2; + buttonSend.Text = "Send"; + buttonSend.UseVisualStyleBackColor = true; + buttonSend.Click += buttonSend_Click; + // // ImportTXForm // AutoScaleDimensions = new SizeF(7F, 15F); @@ -162,6 +189,8 @@ Controls.Add(label1); Controls.Add(groupBoxOut); Controls.Add(groupBoxIn); + Controls.Add(buttonSend); + Controls.Add(buttonExport); Controls.Add(Cancelbutton2); Controls.Add(buttonSign); FormBorderStyle = FormBorderStyle.FixedSingle; @@ -188,5 +217,7 @@ private Label labelSigsReq; private Label labelSigsSigned; private Label labelSigInfo; + private Button buttonExport; + private Button buttonSend; } } \ No newline at end of file diff --git a/FireWallet/ImportTXForm.cs b/FireWallet/ImportTXForm.cs index ab9de67..7a4c41b 100644 --- a/FireWallet/ImportTXForm.cs +++ b/FireWallet/ImportTXForm.cs @@ -9,6 +9,7 @@ namespace FireWallet int totalSigs; int reqSigs; int sigs; + string signedTX; public ImportTXForm(MainForm mainForm) { InitializeComponent(); @@ -18,6 +19,7 @@ namespace FireWallet private void ImportTXForm_Load(object sender, EventArgs e) { // Default variables + signedTX = ""; totalSigs = 3; reqSigs = 2; sigs = 0; @@ -109,7 +111,7 @@ namespace FireWallet JObject sigGetResult = (JObject)sigGetJson["result"]; string[] asm = sigGetResult["asm"].ToString().Split(" "); string totalSigsStr = asm[asm.Length - 2]; - totalSigs = int.Parse(totalSigsStr.Replace("OP_","")); + totalSigs = int.Parse(totalSigsStr.Replace("OP_", "")); reqSigs = int.Parse(sigGetResult["reqSigs"].ToString()); sigs = -1; for (int i = 0; i < witnesses.Count; i++) @@ -121,14 +123,14 @@ namespace FireWallet } } - + // Set sig label sizes labelSigsReq.Width = (labelSigsTotal.Width / totalSigs) * reqSigs; labelSigsSigned.Width = (labelSigsTotal.Width / totalSigs) * sigs; - labelSigInfo.Text = "Signed: " + sigs + "\nReq: " + reqSigs + " of "+ totalSigs; + labelSigInfo.Text = "Signed: " + sigs + "\nReq: " + reqSigs + " of " + totalSigs; @@ -267,9 +269,70 @@ namespace FireWallet private async void buttonSign_Click(object sender, EventArgs e) { - string content = "{\"tx\":\"" + tx["tx"].ToString() + "\", \"passphrase\":\"" + mainForm.Password + "\"}"; - string response = await mainForm.APIPost("wallet/" + mainForm.Account + "/sign", true, content); - mainForm.AddLog(response); + if (!mainForm.WatchOnly) + { + string content = "{\"tx\":\"" + tx["tx"].ToString() + "\", \"passphrase\":\"" + mainForm.Password + "\"}"; + string response = await mainForm.APIPost("wallet/" + mainForm.Account + "/sign", true, content); + if (response == "Error" || response == "") + { + NotifyForm notifyForm = new NotifyForm("Error signing transaction"); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + return; + } + buttonSign.Enabled = false; + buttonExport.Enabled = true; + sigs++; + // Set sig label sizes + labelSigsReq.Width = (labelSigsTotal.Width / totalSigs) * reqSigs; + labelSigsSigned.Width = (labelSigsTotal.Width / totalSigs) * sigs; + labelSigInfo.Text = "Signed: " + sigs + "\nReq: " + reqSigs + " of " + totalSigs; + signedTX = response; + } + } + + private void buttonExport_Click(object sender, EventArgs e) + { + mainForm.ExportTransaction(signedTX); + } + + private async void buttonSend_Click(object sender, EventArgs e) + { + string content = ""; + if (signedTX != "") + { + JObject signed = JObject.Parse(signedTX); + content = "{\"method\":\"sendrawtransaction\", \"params\":[\"" + signed["hex"].ToString() + "\"]}"; + } + else + { + content = "{\"method\":\"sendrawtransaction\", \"params\":[\"" + tx["tx"].ToString() + "\"]}"; + } + string response = await mainForm.APIPost("", false, content); + if (response == "Error" || response == "") + { + mainForm.AddLog(response); + NotifyForm notifyError = new NotifyForm("Error sending transaction"); + notifyError.ShowDialog(); + notifyError.Dispose(); + return; + } + JObject responseJson = JObject.Parse(response); + if (responseJson["error"].ToString() != "") + { + mainForm.AddLog(response); + JObject error = (JObject)responseJson["error"]; + NotifyForm notifyError = new NotifyForm("Error sending transaction\n" + error["message"].ToString()); + notifyError.ShowDialog(); + notifyError.Dispose(); + return; + } + string txHash = responseJson["result"].ToString(); + NotifyForm notifyForm = new NotifyForm("Transaction sent\nIf the transaction hasn't been signed it might not be mined", "Explorer", mainForm.UserSettings["explorer-tx"] + txHash); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + this.Close(); + } } } diff --git a/FireWallet/MainForm.cs b/FireWallet/MainForm.cs index dc7134f..e769fc1 100644 --- a/FireWallet/MainForm.cs +++ b/FireWallet/MainForm.cs @@ -2699,7 +2699,7 @@ namespace FireWallet #region Multi - private void ExportTransaction(string rawTX) + public void ExportTransaction(string rawTX) { JObject tx = JObject.Parse(rawTX); JObject toExport = new JObject(); From db5934bf4a3f0f908f838db90f7b31dfd062c190 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Mon, 26 Jun 2023 21:21:03 +1000 Subject: [PATCH 06/11] batch: Fixed batch form flashing when it imports --- FireWallet/BatchForm.cs | 60 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/FireWallet/BatchForm.cs b/FireWallet/BatchForm.cs index ca43c28..90896a9 100644 --- a/FireWallet/BatchForm.cs +++ b/FireWallet/BatchForm.cs @@ -78,7 +78,6 @@ namespace FireWallet tx.Controls.Add(deleteTX); panelTXs.Controls.Add(tx); - UpdateTheme(); } public void AddBatch(string domain, string operation, decimal bid, decimal lockup) { @@ -142,7 +141,6 @@ namespace FireWallet tx.Controls.Add(deleteTX); panelTXs.Controls.Add(tx); - UpdateTheme(); } public void AddBatch(string domain, string operation, string toAddress) { @@ -202,7 +200,6 @@ namespace FireWallet tx.Controls.Add(deleteTX); panelTXs.Controls.Add(tx); - UpdateTheme(); } public void AddBatch(string domain, string operation, DNS[] updateRecords) @@ -258,7 +255,6 @@ namespace FireWallet tx.Controls.Add(deleteTX); panelTXs.Controls.Add(tx); - UpdateTheme(); } private void FixSpacing() @@ -278,7 +274,7 @@ namespace FireWallet } #endregion #region Theming - private void UpdateTheme() + public void UpdateTheme() { // Check if file exists if (!Directory.Exists(dir)) @@ -471,7 +467,7 @@ namespace FireWallet HttpClient httpClient = new HttpClient(); private async void buttonSend_Click(object sender, EventArgs e) { - if (!mainForm.WatchOnly) + if (!mainForm.WatchOnly && !mainForm.multiSig) { string batchTX = "[" + string.Join(", ", batches.Select(batch => batch.ToString())) + "]"; string content = "{\"method\": \"sendbatch\",\"params\":[ " + batchTX + "]}"; @@ -519,6 +515,58 @@ namespace FireWallet notifyForm2.Dispose(); this.Close(); } + else if (!mainForm.WatchOnly) + { + string batchTX = "[" + string.Join(", ", batches.Select(batch => batch.ToString())) + "]"; + string content = "{\"method\": \"createbatch\",\"params\":[ " + batchTX + "]}"; + string responce = await APIPost("", true, content); + + if (responce == "Error") + { + AddLog("Error sending batch"); + NotifyForm notifyForm = new NotifyForm("Error sending batch"); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + return; + } + + JObject jObject = JObject.Parse(responce); + if (jObject["error"].ToString() != "") + { + AddLog("Error: "); + AddLog(jObject["error"].ToString()); + if (jObject["error"].ToString().Contains("Batch output addresses would exceed lookahead")) + { + NotifyForm notifyForm = new NotifyForm("Error: \nBatch output addresses would exceed lookahead\nYour batch might have too many TXs."); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + } + else if (jObject["error"].ToString().Contains("Name is not registered")) + { + NotifyForm notifyForm = new NotifyForm("Error: \nName is not registered\nRemember you can't renew domains in transfer"); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + } + else + { + NotifyForm notifyForm = new NotifyForm("Error: \n" + jObject["error"].ToString()); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + } + return; + } + + string[] domains = batches.Select(batch => batch.domain).ToArray(); + string tx = await mainForm.ExportTransaction(jObject["result"].ToString(),domains); + if (tx != "Error") + { + ImportTXForm importTXForm = new ImportTXForm(mainForm, tx); + this.Hide(); + importTXForm.ShowDialog(); + importTXForm.Dispose(); + this.Close(); + } + } else // watch only { string batchTX = "[" + string.Join(", ", batches.Select(batch => batch.ToString())) + "]"; From f69f16df4a92d62f703339869357784d8e3c7d3e Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Mon, 26 Jun 2023 21:21:55 +1000 Subject: [PATCH 07/11] multisig: Added multisig from batching --- FireWallet/FireWallet.csproj | 1 + FireWallet/ImportTXForm.cs | 61 +++++++++++------ FireWallet/MainForm.cs | 128 ++++++++++++++++++++++++++++++++++- 3 files changed, 168 insertions(+), 22 deletions(-) diff --git a/FireWallet/FireWallet.csproj b/FireWallet/FireWallet.csproj index 04dfcfd..a783757 100644 --- a/FireWallet/FireWallet.csproj +++ b/FireWallet/FireWallet.csproj @@ -29,6 +29,7 @@ + diff --git a/FireWallet/ImportTXForm.cs b/FireWallet/ImportTXForm.cs index 7a4c41b..7c3bc66 100644 --- a/FireWallet/ImportTXForm.cs +++ b/FireWallet/ImportTXForm.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json.Linq; +using System.Windows.Forms.VisualStyles; +using Newtonsoft.Json.Linq; namespace FireWallet { @@ -10,15 +11,25 @@ namespace FireWallet int reqSigs; int sigs; string signedTX; + string[] domains; public ImportTXForm(MainForm mainForm) { InitializeComponent(); this.mainForm = mainForm; + tx = null; + } + public ImportTXForm(MainForm mainForm, string tx) + { + InitializeComponent(); + this.mainForm = mainForm; + JObject txObj = JObject.Parse(tx); + this.tx = txObj; } private void ImportTXForm_Load(object sender, EventArgs e) { // Default variables + domains = new string[0]; signedTX = ""; totalSigs = 3; reqSigs = 2; @@ -31,25 +42,28 @@ namespace FireWallet { mainForm.ThemeControl(c); } - OpenFileDialog openFileDialog = new OpenFileDialog(); - openFileDialog.Filter = "Transaction files (*.json)|*.json"; - if (openFileDialog.ShowDialog() == DialogResult.OK) + if (tx == null) { - string tx = System.IO.File.ReadAllText(openFileDialog.FileName); - try + OpenFileDialog openFileDialog = new OpenFileDialog(); + openFileDialog.Filter = "Transaction files (*.json)|*.json"; + if (openFileDialog.ShowDialog() == DialogResult.OK) { - JObject txObj = JObject.Parse(tx); - this.tx = txObj; - ParseTX(); - } - catch - { - NotifyForm notifyForm = new NotifyForm("Invalid transaction file."); - notifyForm.ShowDialog(); - notifyForm.Dispose(); - this.Close(); + string tx = System.IO.File.ReadAllText(openFileDialog.FileName); + try + { + JObject txObj = JObject.Parse(tx); + this.tx = txObj; + } + catch + { + NotifyForm notifyForm = new NotifyForm("Invalid transaction file."); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + this.Close(); + } } } + ParseTX(); } private void Cancelbutton2_Click(object sender, EventArgs e) @@ -230,9 +244,6 @@ namespace FireWallet { if (metaOutput.ContainsKey("name")) { - - - Label covenantLabel = new Label(); string name = metaOutput["name"].ToString(); @@ -240,6 +251,14 @@ namespace FireWallet covenantLabel.Location = new Point(5, 25); covenantLabel.AutoSize = true; PanelOutput.Controls.Add(covenantLabel); + + string[] domainsNew = new string[domains.Length + 1]; + for (int j = 0; j < domains.Length; j++) + { + domainsNew[j] = domains[j]; + } + domainsNew[domainsNew.Length - 1] = name; + domains = domainsNew; } } } @@ -288,12 +307,14 @@ namespace FireWallet labelSigsSigned.Width = (labelSigsTotal.Width / totalSigs) * sigs; labelSigInfo.Text = "Signed: " + sigs + "\nReq: " + reqSigs + " of " + totalSigs; signedTX = response; + } else { + } } private void buttonExport_Click(object sender, EventArgs e) { - mainForm.ExportTransaction(signedTX); + mainForm.ExportTransaction(signedTX,domains); } private async void buttonSend_Click(object sender, EventArgs e) diff --git a/FireWallet/MainForm.cs b/FireWallet/MainForm.cs index e769fc1..c45f459 100644 --- a/FireWallet/MainForm.cs +++ b/FireWallet/MainForm.cs @@ -6,6 +6,7 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.RegularExpressions; +using System.Windows.Forms.VisualStyles; using DnsClient; using DnsClient.Protocol; using Newtonsoft.Json.Linq; @@ -1982,6 +1983,26 @@ namespace FireWallet } ExportTransaction(output); } + else + { + string content = "{\"method\": \"createsendtoaddress\",\"params\": [ \"" + address + "\", " + + amount.ToString() + ", \"\", \"\", " + subtractFee + " ]}"; + string output = await APIPost("", true, content); + JObject APIresp = JObject.Parse(output); + if (APIresp["error"].ToString() != "") + { + AddLog("Failed:"); + AddLog(APIresp.ToString()); + JObject error = JObject.Parse(APIresp["error"].ToString()); + string ErrorMessage = error["message"].ToString(); + + NotifyForm notify = new NotifyForm("Error Transaction Failed\n" + ErrorMessage); + notify.ShowDialog(); + return; + } + string signed = signRaw(output,new string[0]); + ExportTransaction(signed); + } } @@ -2521,6 +2542,7 @@ namespace FireWallet BatchMode = true; } BatchForm.AddBatch(domain, operation); + BatchForm.UpdateTheme(); } public void AddBatch(string domain, string operation, decimal bid, decimal lockup) @@ -2533,6 +2555,7 @@ namespace FireWallet BatchMode = true; } BatchForm.AddBatch(domain, operation, bid, lockup); + BatchForm.UpdateTheme(); } public void AddBatch(string domain, string operation, DNS[] updateRecords) @@ -2544,6 +2567,7 @@ namespace FireWallet BatchMode = true; } BatchForm.AddBatch(domain, operation, updateRecords); + BatchForm.UpdateTheme(); } public void AddBatch(string domain, string operation, string address) { @@ -2554,6 +2578,7 @@ namespace FireWallet BatchMode = true; } BatchForm.AddBatch(domain, operation, address); + BatchForm.UpdateTheme(); } public void FinishBatch() { @@ -2699,7 +2724,11 @@ namespace FireWallet #region Multi - public void ExportTransaction(string rawTX) + public async Task ExportTransaction(string rawTX) + { + return await ExportTransaction(rawTX, new string[0]); + } + public async Task ExportTransaction(string rawTX, string[] domains) { JObject tx = JObject.Parse(rawTX); JObject toExport = new JObject(); @@ -2709,6 +2738,14 @@ namespace FireWallet JArray outputsParsed = new JArray(); JArray inputs = JArray.Parse(tx["inputs"].ToString()); JArray outputs = JArray.Parse(tx["outputs"].ToString()); + + Dictionary hashes = new Dictionary(); + foreach (string domain in domains) + { + // sha3 hash of domain + hashes.Add(CalculateSHA3Hash(domain),domain); + } + foreach (JObject input in inputs) { JObject coin = JObject.Parse(input["coin"].ToString()); @@ -2722,6 +2759,7 @@ namespace FireWallet else { AddLog("Not supported yet"); + return "Error"; } } foreach (JObject output in outputs) @@ -2735,7 +2773,20 @@ namespace FireWallet } else { - AddLog("Not supported yet"); + AddLog(covenant.ToString()); + string hash = covenant["items"][0].ToString(); + if (hashes.ContainsKey(hash)) + { + data["name"] = hashes[hash]; + } + else + { + AddLog("Cannot find name for hash " + hash); + return "Error"; + } + + //data["name"]; + outputsParsed.Add(data); } } JObject metadata = new JObject(); @@ -2753,8 +2804,81 @@ namespace FireWallet StreamWriter sw = new StreamWriter(saveFileDialog.FileName); sw.Write(toExport.ToString()); sw.Dispose(); + return toExport.ToString(); + }else + { + return "Error"; } } + public string signRaw(string tx, string[] domains) + { + string domainslist = string.Join(",", domains.Select(domain => "\\\"" + domain + "\\\"")); + NotifyForm notify = new NotifyForm("Please confirm the transaction on your Ledger device", false); + notify.Show(); + + var proc = new Process(); + proc.StartInfo.CreateNoWindow = true; + proc.StartInfo.RedirectStandardInput = true; + proc.StartInfo.RedirectStandardOutput = true; + proc.StartInfo.UseShellExecute = false; + proc.StartInfo.RedirectStandardError = true; + proc.StartInfo.FileName = "node.exe"; + proc.StartInfo.WorkingDirectory = dir + "hsd-ledger/bin/"; + string args = "hsd-ledger/bin/hsd-ledger signraw \"\"" + tx.Replace("\"", "\\\"").Trim() + "\"\" [" + domainslist + "] --api-key " + NodeSettings["Key"] + " -w " + Account; + AddLog(args); + + proc.StartInfo.Arguments = dir + args; + var outputBuilder = new StringBuilder(); + + // Event handler for capturing output data + proc.OutputDataReceived += (sender, args) => + { + if (!string.IsNullOrEmpty(args.Data)) + { + outputBuilder.AppendLine(args.Data); + } + }; + + proc.Start(); + proc.BeginOutputReadLine(); + proc.WaitForExit(); + + notify.CloseNotification(); + notify.Dispose(); + + string output = outputBuilder.ToString(); + AddLog(output); + if (output.Length > 2) + { + // Signed + return output; + } + else + { + AddLog(args); + AddLog(proc.StandardError.ReadToEnd()); + NotifyForm notifyError = new NotifyForm("Error sign Failed\nCheck logs for more details"); + notifyError.ShowDialog(); + notifyError.Dispose(); + return "Error"; + } + } + static string CalculateSHA3Hash(string input) + { + var hashAlgorithm = new Org.BouncyCastle.Crypto.Digests.Sha3Digest(256); + + // Choose correct encoding based on your usecase + byte[] inputBytes = Encoding.UTF8.GetBytes(input); + + hashAlgorithm.BlockUpdate(inputBytes, 0, inputBytes.Length); + + byte[] result = new byte[256/8]; + hashAlgorithm.DoFinal(result, 0); + + string hashString = BitConverter.ToString(result); + hashString = hashString.Replace("-", "").ToLowerInvariant(); + return hashString; + } #endregion } } \ No newline at end of file From 6cc6cd4a790181e91069f790d69af14f6cfb72b9 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Mon, 26 Jun 2023 21:25:42 +1000 Subject: [PATCH 08/11] multisig: Disabled bulk actions and a set to reqiure batch - Added condition to disable buttonActionMain when mainForm.multiSig is true - Removed buttons (buttonRevealAll, buttonRedeemAll, and buttonSendAll) when WatchOnly or multiSig is true - Added buttons (buttonAddressVerify) when WatchOnly is true - Updated conditions for showing/hiding toolStripStatusLabelLedger and toolStripStatusLabelMultisig --- FireWallet/DomainForm.cs | 4 ++-- FireWallet/MainForm.cs | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/FireWallet/DomainForm.cs b/FireWallet/DomainForm.cs index 3cca470..adad9d6 100644 --- a/FireWallet/DomainForm.cs +++ b/FireWallet/DomainForm.cs @@ -157,9 +157,9 @@ namespace FireWallet network = Convert.ToInt32(nodeSettings["Network"]); GetName(); - if (mainForm.WatchOnly) + if (mainForm.WatchOnly || mainForm.multiSig) { - buttonActionMain.Enabled = false; // Only allow sending in batches for ledger + buttonActionMain.Enabled = false; // Only allow sending in batches for ledger and multisig to prevent confusion } } #region API diff --git a/FireWallet/MainForm.cs b/FireWallet/MainForm.cs index c45f459..5fb48b0 100644 --- a/FireWallet/MainForm.cs +++ b/FireWallet/MainForm.cs @@ -770,18 +770,12 @@ namespace FireWallet { WatchOnly = true; toolStripStatusLabelLedger.Visible = true; - buttonRevealAll.Visible = false; - buttonRedeemAll.Visible = false; - buttonSendAll.Visible = false; buttonAddressVerify.Visible = true; } else { WatchOnly = false; toolStripStatusLabelLedger.Visible = false; - buttonRevealAll.Visible = true; - buttonRedeemAll.Visible = true; - buttonSendAll.Visible = true; buttonAddressVerify.Visible = false; } @@ -813,6 +807,18 @@ namespace FireWallet { toolStripStatusLabelMultisig.Visible = false; } + if (WatchOnly || multiSig) + { + buttonRevealAll.Visible = false; + buttonRedeemAll.Visible = false; + buttonSendAll.Visible = false; + } + else + { + buttonRevealAll.Visible = true; + buttonRedeemAll.Visible = true; + buttonSendAll.Visible = true; + } return true; } From fe7fc4cee9ba177c505bc158b88f94f6765065a4 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Mon, 26 Jun 2023 21:44:11 +1000 Subject: [PATCH 09/11] multisig: Cleaned up code and added checks to not show >n signers --- FireWallet/BatchForm.cs | 2 +- FireWallet/ImportTXForm.Designer.cs | 2 +- FireWallet/ImportTXForm.cs | 30 +++++++++-------------------- FireWallet/MainForm.cs | 1 - 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/FireWallet/BatchForm.cs b/FireWallet/BatchForm.cs index 90896a9..7b01fe6 100644 --- a/FireWallet/BatchForm.cs +++ b/FireWallet/BatchForm.cs @@ -515,7 +515,7 @@ namespace FireWallet notifyForm2.Dispose(); this.Close(); } - else if (!mainForm.WatchOnly) + else if (mainForm.multiSig) { string batchTX = "[" + string.Join(", ", batches.Select(batch => batch.ToString())) + "]"; string content = "{\"method\": \"createbatch\",\"params\":[ " + batchTX + "]}"; diff --git a/FireWallet/ImportTXForm.Designer.cs b/FireWallet/ImportTXForm.Designer.cs index 3007d92..2541a10 100644 --- a/FireWallet/ImportTXForm.Designer.cs +++ b/FireWallet/ImportTXForm.Designer.cs @@ -106,7 +106,7 @@ Cancelbutton2.TabIndex = 2; Cancelbutton2.Text = "Cancel"; Cancelbutton2.UseVisualStyleBackColor = true; - Cancelbutton2.Click += Cancelbutton2_Click; + Cancelbutton2.Click += Cancel; // // label1 // diff --git a/FireWallet/ImportTXForm.cs b/FireWallet/ImportTXForm.cs index 7c3bc66..508dabb 100644 --- a/FireWallet/ImportTXForm.cs +++ b/FireWallet/ImportTXForm.cs @@ -66,7 +66,7 @@ namespace FireWallet ParseTX(); } - private void Cancelbutton2_Click(object sender, EventArgs e) + private void Cancel(object sender, EventArgs e) { this.Close(); } @@ -137,18 +137,16 @@ namespace FireWallet } } - - - - // Set sig label sizes labelSigsReq.Width = (labelSigsTotal.Width / totalSigs) * reqSigs; labelSigsSigned.Width = (labelSigsTotal.Width / totalSigs) * sigs; labelSigInfo.Text = "Signed: " + sigs + "\nReq: " + reqSigs + " of " + totalSigs; - - - + if (sigs >= totalSigs) + { + buttonSign.Enabled = false; + buttonSign.Text = "Signed"; + } for (int i = 0; i < inputs.Count; i++) { @@ -202,18 +200,6 @@ namespace FireWallet PanelInput.Controls.Add(amount); } - - - - //if (input["path"].ToString() != "") - //{ - // Label ownAddress = new Label(); - // ownAddress.Text = "Own Address"; - // ownAddress.Location = new Point(PanelInput.Width - 100, 5); - // ownAddress.AutoSize = true; - // PanelInput.Controls.Add(ownAddress); - //} - panelIn.Controls.Add(PanelInput); } @@ -308,7 +294,9 @@ namespace FireWallet labelSigInfo.Text = "Signed: " + sigs + "\nReq: " + reqSigs + " of " + totalSigs; signedTX = response; } else { - + NotifyForm notifyForm = new NotifyForm("Ledger signing not supported yet 😢"); + notifyForm.ShowDialog(); + notifyForm.Dispose(); } } diff --git a/FireWallet/MainForm.cs b/FireWallet/MainForm.cs index 5fb48b0..d6fc032 100644 --- a/FireWallet/MainForm.cs +++ b/FireWallet/MainForm.cs @@ -2779,7 +2779,6 @@ namespace FireWallet } else { - AddLog(covenant.ToString()); string hash = covenant["items"][0].ToString(); if (hashes.ContainsKey(hash)) { From c964aa00648a20846f7166ad2041aa561964cecc Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Tue, 27 Jun 2023 11:49:47 +1000 Subject: [PATCH 10/11] multisig: Added settings and cleaned up new account creation --- FireWallet/MainForm.Designer.cs | 40 +- FireWallet/MainForm.cs | 28 +- FireWallet/MultisigSettingsForm.Designer.cs | 141 +++++ FireWallet/MultisigSettingsForm.cs | 227 ++++++++ FireWallet/MultisigSettingsForm.resx | 587 ++++++++++++++++++++ FireWallet/NewAccountForm.cs | 89 ++- 6 files changed, 1081 insertions(+), 31 deletions(-) create mode 100644 FireWallet/MultisigSettingsForm.Designer.cs create mode 100644 FireWallet/MultisigSettingsForm.cs create mode 100644 FireWallet/MultisigSettingsForm.resx diff --git a/FireWallet/MainForm.Designer.cs b/FireWallet/MainForm.Designer.cs index f1e8c22..f4461f7 100644 --- a/FireWallet/MainForm.Designer.cs +++ b/FireWallet/MainForm.Designer.cs @@ -60,6 +60,7 @@ namespace FireWallet buttonaccountnew = new Button(); panelNav = new Panel(); buttonNavSettings = new Button(); + buttonMultiSign = new Button(); buttonBatch = new Button(); buttonNavDomains = new Button(); buttonNavReceive = new Button(); @@ -129,7 +130,7 @@ namespace FireWallet textBoxExAddr = new TextBox(); labelSettings4 = new Label(); textBoxExTX = new TextBox(); - buttonMultiSign = new Button(); + buttonMultiSettings = new Button(); statusStripmain.SuspendLayout(); panelaccount.SuspendLayout(); groupBoxaccount.SuspendLayout(); @@ -366,6 +367,7 @@ namespace FireWallet // panelNav // panelNav.Controls.Add(buttonNavSettings); + panelNav.Controls.Add(buttonMultiSettings); panelNav.Controls.Add(buttonMultiSign); panelNav.Controls.Add(buttonBatch); panelNav.Controls.Add(buttonNavDomains); @@ -391,6 +393,19 @@ namespace FireWallet buttonNavSettings.UseVisualStyleBackColor = true; buttonNavSettings.Click += buttonNavSettings_Click; // + // buttonMultiSign + // + buttonMultiSign.FlatStyle = FlatStyle.Flat; + buttonMultiSign.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); + buttonMultiSign.Location = new Point(12, 301); + buttonMultiSign.Name = "buttonMultiSign"; + buttonMultiSign.Size = new Size(89, 30); + buttonMultiSign.TabIndex = 3; + buttonMultiSign.TabStop = false; + buttonMultiSign.Text = "Import TX"; + buttonMultiSign.UseVisualStyleBackColor = true; + buttonMultiSign.Click += buttonMultiSign_Click; + // // buttonBatch // buttonBatch.FlatStyle = FlatStyle.Flat; @@ -1144,18 +1159,18 @@ namespace FireWallet textBoxExTX.Size = new Size(307, 29); textBoxExTX.TabIndex = 1; // - // buttonMultiSign + // buttonMultiSettings // - buttonMultiSign.FlatStyle = FlatStyle.Flat; - buttonMultiSign.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); - buttonMultiSign.Location = new Point(12, 301); - buttonMultiSign.Name = "buttonMultiSign"; - buttonMultiSign.Size = new Size(89, 30); - buttonMultiSign.TabIndex = 3; - buttonMultiSign.TabStop = false; - buttonMultiSign.Text = "Import TX"; - buttonMultiSign.UseVisualStyleBackColor = true; - buttonMultiSign.Click += buttonMultiSign_Click; + buttonMultiSettings.FlatStyle = FlatStyle.Flat; + buttonMultiSettings.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); + buttonMultiSettings.Location = new Point(12, 466); + buttonMultiSettings.Name = "buttonMultiSettings"; + buttonMultiSettings.Size = new Size(89, 30); + buttonMultiSettings.TabIndex = 3; + buttonMultiSettings.TabStop = false; + buttonMultiSettings.Text = "Multisig"; + buttonMultiSettings.UseVisualStyleBackColor = true; + buttonMultiSettings.Click += buttonMultiSettings_Click; // // MainForm // @@ -1307,5 +1322,6 @@ namespace FireWallet private Button buttonSendAll; private ToolStripStatusLabel toolStripStatusLabelMultisig; private Button buttonMultiSign; + private Button buttonMultiSettings; } } \ No newline at end of file diff --git a/FireWallet/MainForm.cs b/FireWallet/MainForm.cs index d6fc032..60045e3 100644 --- a/FireWallet/MainForm.cs +++ b/FireWallet/MainForm.cs @@ -802,10 +802,12 @@ namespace FireWallet if (multiSig) { toolStripStatusLabelMultisig.Visible = true; + buttonMultiSettings.Visible = true; } else { toolStripStatusLabelMultisig.Visible = false; + buttonMultiSettings.Visible = false; } if (WatchOnly || multiSig) { @@ -2006,7 +2008,7 @@ namespace FireWallet notify.ShowDialog(); return; } - string signed = signRaw(output,new string[0]); + string signed = signRaw(output, new string[0]); ExportTransaction(signed); } @@ -2735,7 +2737,7 @@ namespace FireWallet return await ExportTransaction(rawTX, new string[0]); } public async Task ExportTransaction(string rawTX, string[] domains) - { + { JObject tx = JObject.Parse(rawTX); JObject toExport = new JObject(); toExport["version"] = 1; @@ -2744,12 +2746,12 @@ namespace FireWallet JArray outputsParsed = new JArray(); JArray inputs = JArray.Parse(tx["inputs"].ToString()); JArray outputs = JArray.Parse(tx["outputs"].ToString()); - - Dictionary hashes = new Dictionary(); + + Dictionary hashes = new Dictionary(); foreach (string domain in domains) { // sha3 hash of domain - hashes.Add(CalculateSHA3Hash(domain),domain); + hashes.Add(CalculateSHA3Hash(domain), domain); } foreach (JObject input in inputs) @@ -2789,7 +2791,7 @@ namespace FireWallet AddLog("Cannot find name for hash " + hash); return "Error"; } - + //data["name"]; outputsParsed.Add(data); } @@ -2810,7 +2812,8 @@ namespace FireWallet sw.Write(toExport.ToString()); sw.Dispose(); return toExport.ToString(); - }else + } + else { return "Error"; } @@ -2871,19 +2874,26 @@ namespace FireWallet static string CalculateSHA3Hash(string input) { var hashAlgorithm = new Org.BouncyCastle.Crypto.Digests.Sha3Digest(256); - + // Choose correct encoding based on your usecase byte[] inputBytes = Encoding.UTF8.GetBytes(input); hashAlgorithm.BlockUpdate(inputBytes, 0, inputBytes.Length); - byte[] result = new byte[256/8]; + byte[] result = new byte[256 / 8]; hashAlgorithm.DoFinal(result, 0); string hashString = BitConverter.ToString(result); hashString = hashString.Replace("-", "").ToLowerInvariant(); return hashString; } + private void buttonMultiSettings_Click(object sender, EventArgs e) + { + MultisigSettingsForm multisigSettingsForm = new MultisigSettingsForm(this); + multisigSettingsForm.Show(); + } #endregion + + } } \ No newline at end of file diff --git a/FireWallet/MultisigSettingsForm.Designer.cs b/FireWallet/MultisigSettingsForm.Designer.cs new file mode 100644 index 0000000..6842c55 --- /dev/null +++ b/FireWallet/MultisigSettingsForm.Designer.cs @@ -0,0 +1,141 @@ +namespace FireWallet +{ + partial class MultisigSettingsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MultisigSettingsForm)); + groupBoxSigners = new GroupBox(); + panelSigners = new Panel(); + labelSigners = new Label(); + groupBoxAddSig = new GroupBox(); + buttoAddSigner = new Button(); + textBoxAddSig = new TextBox(); + labelAddXpub = new Label(); + groupBoxSigners.SuspendLayout(); + groupBoxAddSig.SuspendLayout(); + SuspendLayout(); + // + // groupBoxSigners + // + groupBoxSigners.Controls.Add(panelSigners); + groupBoxSigners.Location = new Point(0, 0); + groupBoxSigners.Name = "groupBoxSigners"; + groupBoxSigners.Size = new Size(418, 463); + groupBoxSigners.TabIndex = 0; + groupBoxSigners.TabStop = false; + groupBoxSigners.Text = "Signers"; + // + // panelSigners + // + panelSigners.Dock = DockStyle.Fill; + panelSigners.Location = new Point(3, 19); + panelSigners.Name = "panelSigners"; + panelSigners.Size = new Size(412, 441); + panelSigners.TabIndex = 0; + // + // labelSigners + // + labelSigners.AutoSize = true; + labelSigners.Location = new Point(424, 9); + labelSigners.Name = "labelSigners"; + labelSigners.Size = new Size(48, 15); + labelSigners.TabIndex = 1; + labelSigners.Text = "Signers:"; + // + // groupBoxAddSig + // + groupBoxAddSig.Controls.Add(buttoAddSigner); + groupBoxAddSig.Controls.Add(textBoxAddSig); + groupBoxAddSig.Controls.Add(labelAddXpub); + groupBoxAddSig.Location = new Point(421, 255); + groupBoxAddSig.Name = "groupBoxAddSig"; + groupBoxAddSig.Size = new Size(381, 208); + groupBoxAddSig.TabIndex = 2; + groupBoxAddSig.TabStop = false; + groupBoxAddSig.Text = "Add Signer"; + // + // buttoAddSigner + // + buttoAddSigner.FlatStyle = FlatStyle.Flat; + buttoAddSigner.Location = new Point(6, 45); + buttoAddSigner.Name = "buttoAddSigner"; + buttoAddSigner.Size = new Size(75, 23); + buttoAddSigner.TabIndex = 2; + buttoAddSigner.Text = "Add"; + buttoAddSigner.UseVisualStyleBackColor = true; + buttoAddSigner.Click += buttoAddSigner_Click; + // + // textBoxAddSig + // + textBoxAddSig.Location = new Point(51, 16); + textBoxAddSig.Name = "textBoxAddSig"; + textBoxAddSig.Size = new Size(324, 23); + textBoxAddSig.TabIndex = 1; + textBoxAddSig.KeyDown += textBoxAddSig_KeyDown; + // + // labelAddXpub + // + labelAddXpub.AutoSize = true; + labelAddXpub.Location = new Point(6, 19); + labelAddXpub.Name = "labelAddXpub"; + labelAddXpub.Size = new Size(39, 15); + labelAddXpub.TabIndex = 0; + labelAddXpub.Text = "XPUB:"; + // + // MultisigSettingsForm + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(814, 475); + Controls.Add(groupBoxAddSig); + Controls.Add(labelSigners); + Controls.Add(groupBoxSigners); + FormBorderStyle = FormBorderStyle.Fixed3D; + Icon = (Icon)resources.GetObject("$this.Icon"); + MaximizeBox = false; + Name = "MultisigSettingsForm"; + Text = "Multisig Settings"; + Load += MultisigSettingsForm_Load; + groupBoxSigners.ResumeLayout(false); + groupBoxAddSig.ResumeLayout(false); + groupBoxAddSig.PerformLayout(); + ResumeLayout(false); + PerformLayout(); + } + + #endregion + + private GroupBox groupBoxSigners; + private Label labelSigners; + private Panel panelSigners; + private GroupBox groupBoxAddSig; + private Button buttoAddSigner; + private TextBox textBoxAddSig; + private Label labelAddXpub; + } +} \ No newline at end of file diff --git a/FireWallet/MultisigSettingsForm.cs b/FireWallet/MultisigSettingsForm.cs new file mode 100644 index 0000000..ea4594a --- /dev/null +++ b/FireWallet/MultisigSettingsForm.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Windows.Forms; +using Newtonsoft.Json.Linq; + +namespace FireWallet +{ + public partial class MultisigSettingsForm : Form + { + MainForm mainForm; + int n; + int m; + int current; + string OwnKey; + public MultisigSettingsForm(MainForm mainForm) + { + InitializeComponent(); + this.mainForm = mainForm; + this.BackColor = ColorTranslator.FromHtml(mainForm.Theme["background"]); + this.ForeColor = ColorTranslator.FromHtml(mainForm.Theme["foreground"]); + foreach (Control c in Controls) + { + mainForm.ThemeControl(c); + } + } + + private void MultisigSettingsForm_Load(object sender, EventArgs e) + { + UpdateInfo(); + } + private async void UpdateInfo() + { + // Get multisig info + string infoResp = await mainForm.APIGet("wallet/" + mainForm.Account + "/account/default", true); + mainForm.AddLog(infoResp); + + if (infoResp == "Error") + { + NotifyForm notifyForm = new NotifyForm("Error getting multisig info"); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + this.Close(); + } + JObject info = JObject.Parse(infoResp); + n = int.Parse(info["n"].ToString()); + m = int.Parse(info["m"].ToString()); + labelSigners.Text = "Signers: " + m + "/" + n; + + panelSigners.Controls.Clear(); + Panel selfInfo = new Panel(); + selfInfo.Width = panelSigners.Width - SystemInformation.VerticalScrollBarWidth; + selfInfo.Height = 50; + selfInfo.Location = new Point(0, 0); + selfInfo.BackColor = ColorTranslator.FromHtml(mainForm.Theme["background"]); + selfInfo.ForeColor = ColorTranslator.FromHtml(mainForm.Theme["foreground"]); + selfInfo.BorderStyle = BorderStyle.FixedSingle; + + Label selfLabel = new Label(); + selfLabel.Text = "Own Key"; + selfLabel.Location = new Point(10, 10); + selfLabel.AutoSize = true; + selfInfo.Controls.Add(selfLabel); + + Label pubKey = new Label(); + OwnKey = info["accountKey"].ToString(); + string pukKeyShort = info["accountKey"].ToString().Substring(0, 10) + "..." + info["accountKey"].ToString().Substring(info["accountKey"].ToString().Length - 10); + + pubKey.Text = pukKeyShort; + pubKey.Location = new Point(10, 30); + pubKey.AutoSize = true; + selfInfo.Controls.Add(pubKey); + + Button copyPubKey = new Button(); + copyPubKey.Text = "Copy"; + copyPubKey.AutoSize = true; + copyPubKey.FlatStyle = FlatStyle.Flat; + copyPubKey.Location = new Point(selfInfo.Width - copyPubKey.Width - 10, 10); + copyPubKey.Click += (s, e) => + { + Clipboard.SetText(OwnKey); + }; + selfInfo.Controls.Add(copyPubKey); + panelSigners.Controls.Add(selfInfo); + + // Get signers + int y = 50; + JArray signers = JArray.Parse(info["keys"].ToString()); + current = signers.Count; + foreach (string sig in signers) + { + Panel signerInfo = new Panel(); + signerInfo.Width = panelSigners.Width - SystemInformation.VerticalScrollBarWidth; + signerInfo.Height = 50; + signerInfo.Location = new Point(0, y); + signerInfo.BackColor = ColorTranslator.FromHtml(mainForm.Theme["background"]); + signerInfo.ForeColor = ColorTranslator.FromHtml(mainForm.Theme["foreground"]); + signerInfo.BorderStyle = BorderStyle.FixedSingle; + + Label signerLabel = new Label(); + signerLabel.Text = "Signer"; + signerLabel.Location = new Point(10, 10); + signerLabel.AutoSize = true; + signerInfo.Controls.Add(signerLabel); + + Label signerKey = new Label(); + string signerKeyShort = sig.Substring(0, 10) + "..." + sig.Substring(sig.Length - 10); + signerKey.Text = signerKeyShort; + signerKey.Location = new Point(10, 30); + signerKey.AutoSize = true; + signerInfo.Controls.Add(signerKey); + + Button DelSignerKey = new Button(); + DelSignerKey.Text = "Remove"; + DelSignerKey.AutoSize = true; + DelSignerKey.FlatStyle = FlatStyle.Flat; + DelSignerKey.Location = new Point(signerInfo.Width - DelSignerKey.Width - 10, 10); + DelSignerKey.Click += (s, e) => + { + RemoveSig(sig); + }; + signerInfo.Controls.Add(DelSignerKey); + panelSigners.Controls.Add(signerInfo); + y += 50; + } + + + } + private async Task RemoveSig(string sig) + { + string path = "wallet/" + mainForm.Account + "/shared-key"; + string content = "{\"accountKey\": \"" + sig + "\",\"account\":\"default\"}"; + string resp = await APIPut(path, true, content); + mainForm.AddLog(resp); + UpdateInfo(); + + } + private async Task AddSigAsync(string sig) + { + string path = "wallet/" + mainForm.Account + "/shared-key"; + string content = "{\"accountKey\": \"" + sig + "\",\"account\":\"default\"}"; + string resp = await APIPut(path, true, content); + mainForm.AddLog(resp); + UpdateInfo(); + } + + private async void buttoAddSigner_Click(object sender, EventArgs e) + { + if (textBoxAddSig.Text.Length < 5) + { + NotifyForm notifyForm = new NotifyForm("You need to add a XPUB key"); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + return; + } + + await AddSigAsync(textBoxAddSig.Text); + + textBoxAddSig.Text = ""; + } + + HttpClient httpClient = new HttpClient(); + /// + /// Put to HSD API + /// + /// Path to put to + /// Whether to use port 12039 + /// Content to put + /// + public async Task APIPut(string path, bool wallet, string content) + { + if (content == "{\"passphrase\": \"\",\"timeout\": 60}") + { + return ""; + } + string key = mainForm.NodeSettings["Key"]; + string ip = mainForm.NodeSettings["IP"]; + string port = "1203"; + if (mainForm.HSDNetwork == 1) + { + port = "1303"; + } + if (wallet) port = port + "9"; + else port = port + "7"; + + HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Put, "http://" + ip + ":" + port + "/" + path); + req.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes("x:" + key))); + req.Content = new StringContent(content); + // Send request + + + try + { + HttpResponseMessage resp = await httpClient.SendAsync(req); + if (!resp.IsSuccessStatusCode) + { + mainForm.AddLog("Post Error: " + resp.StatusCode); + mainForm.AddLog(await resp.Content.ReadAsStringAsync()); + return "Error"; + } + return await resp.Content.ReadAsStringAsync(); + + } + catch (Exception ex) + { + mainForm.AddLog("Post Error: " + ex.Message); + return "Error"; + } + } + + private void textBoxAddSig_KeyDown(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Enter) + { + buttoAddSigner_Click(sender, e); + e.SuppressKeyPress = true; + } + } + } +} diff --git a/FireWallet/MultisigSettingsForm.resx b/FireWallet/MultisigSettingsForm.resx new file mode 100644 index 0000000..0464da4 --- /dev/null +++ b/FireWallet/MultisigSettingsForm.resx @@ -0,0 +1,587 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAUAEBAAAAEAIABoBAAAVgAAABgYAAABACAAiAkAAL4EAAAgIAAAAQAgAKgQAABGDgAAMDAAAAEA + IACoJQAA7h4AAAAAAAABACAAfScAAJZEAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAI8/zACPQM0Aj0DNEo9AzVePQM02j0DNAI9AzQAAAAAAAAAAAI8/zQCPQM0Aj0DNNI9A + zViPQM0Sj0DNAI8/zACPQM0Uj0DNXY9AzcOPQM37j0DN1o9AzTuPQM0Ajz/NAI4/zACPQM0Aj0DNOY9A + zdWPQM37j0DNxI9AzV6PQM0Vj0DNso9AzfqPQM3/j0DN/49Azf+PQM3Wj0DNO49AzQCPQM0Aj0DNOY9A + zdSPQM3/j0DN/49Azf+PQM36j0DNtY9AzdmPQM3/j0DN/49Azf+PQM3/j0DN/49AzdaPQM05j0DNN49A + zdWPQM3/j0DN/49Azf+PQM3/j0DN/49AzdyRQ8XYkUPF/5FDxf+RQ8X/kUPF/5FDxf+RQ8X/kkPFsZJD + xa+RQ8X/kUPF/5FDxf+RQ8X/kUPF/5FDxf+RQ8Xbmkuu2JpLrv+aS67/mkuv/ppLrv6aS67/mkuu/5pL + r76aS6+9mkuu/5pLrv+aS67+mkuv/ppLrv+aS67/mkuu26VSkNilUpD/pVKQ/6RSkOajUZWrpVKQ+aVS + kP+lUpC+pVKQvKVSkP+lUpD6o1GVq6RSkOWlUpD/pVKQ/6VSkNuwVG3YsFRt/7BUbf+wVG3drlRzKq5U + c4+wVG3/sFRtv7BUbb6wVG3/rlRzka5UcymwVG3csFRt/7BUbf+wVG3cuVNI2blTSP+5U0j/uVNI3rlT + Rxy0VWELuFNOlblTSLq5U0i5uFNOlrRVYAy5U0cbuVNI3blTSP+5U0j/uVNI3MFQHtnBUB7/wVAe/8FQ + Ht/AUB4ewFAkAL5RMxC/USxQv1EsUL5RMxDAUCUAwFAeHMFQHt3BUB7/wVAe/8FQHtzDTwnZw08J/8NP + Cf/DTwnfw08JHsNPCQDETgAAx0wAAcdNAAHFTQAAw08JAMNPCR3DTwnew08J/8NPCf/DTwncw08J2MNP + Cf/DTwn/w08Jw8NPCRXDTwkAAAAAAAAAAAAAAAAAAAAAAMNPCQDDTwkUw08JwcNPCf/DTwn/w08J28NP + CdbDTwnbw08JfMNPCSHDTggAw08JAAAAAAAAAAAAAAAAAAAAAADCTwkAwU4HAMNPCSDDTwl7w08J28NP + CdjDTwlVw08JI8NOCAHDTwkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDTwgAwk4IAcNP + CSLDTwlWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//AADH4wAAA8AAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAABmAAAAfg + AAAP8AAAH/gAAP//AAAoAAAAGAAAADAAAAABACAAAAAAAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI8/zACPP8wDjz/MAo8/ + zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI8/zQCPP80Cjz/NA48/zQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACPQM0Ajz/NBY9AzTePQM2Wj0DNYo4/zAOPP80AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAj0DNAI8/zQKPQM1gj0DNl49AzTmPP80Fjz/NAAAAAAAAAAAAjz/NAI8/zQWPQM04j0DNm49A + zeuPQM3/j0DN749AzWeOP8wDjz/NAAAAAAAAAAAAAAAAAAAAAACPP80Ajz/MAo9AzWSPQM3uj0DN/49A + zeuPQM2dj0DNOY8/zQWPQM0Aj0DNN49AzZyPQM3rj0DN/49Azf+PQM3/j0DN/49Aze+PQM1njz/MA48/ + zQAAAAAAAAAAAI9AzQCOP8wCj0DNZI9Aze6PQM3/j0DN/49Azf+PQM3/j0DN7I9AzZ6PQM05j0DNuY9A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3vj0DNZo4/zAOPQMwAjz/NAI8/zQKPQM1jj0DN7o9A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM2+j0DNwY9Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN749AzWeOP84Djj7OAo9AzWWPQM3uj0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3Gj0DMwY9AzP+PQMz/j0DM/49AzP+PQMz/j0DM/49AzP+PQMz/j0DM/49AzPCPQMtZj0DLVo9A + zO+PQMz/j0DM/49AzP+PQMz/j0DM/49AzP+PQMz/j0DM/49AzP+PQMzFkkTDwJJEw/+SRMP/kkTD/5JE + w/+SRMP/kkTD/5JEw/+SRMP/kkTD/5JEw/+SRMOekkTDm5JEw/+SRMP/kkTD/5JEw/+SRMP/kkTD/5JE + w/+SRMP/kkTD/5JEw/+SRMPFmEqzwJhKs/+YSrP/mEqz/5hKs/+YSrP/mEqz/5hKs/+YSrP/mEqz/5hK + s/+YSrOfmEqznJhKs/+YSrP/mEqz/5hKs/+YSrP/mEqz/5hKs/+YSrP/mEqz/5hKs/+YSrPFn0+gwJ9P + oP+fT6D/n0+g/59PoP+fT6D2n0+h7J9PoP+fT6D/n0+g/59PoP+fT6Cfn0+gm59PoP+fT6D/n0+g/59P + oP+fT6Hsn0+g9p9PoP+fT6D/n0+g/59PoP+fT6DGp1OKwadTiv+nU4r/p1OK/6dTiv+nU4rdpVKPYqZT + jNynU4r/p1OK/6dTiv+nU4qfp1OKnKdTiv+nU4r/p1OK/6ZTjN6lUo9jp1OK26dTiv+nU4r/p1OK/6dT + iv+nU4rGrlRzwa5Uc/+uVHP/rlRz/65Uc/+uVHPdrlRzHKxUe0StVHTfrlRz/65Uc/+uVHOgrlRzna5U + c/+uVHP/rlR04KxUe0auVHMarlRz265Uc/+uVHP/rlRz/65Uc/+uVHPGtVRawbVUWv+1VFr/tVRa/7VU + Wv+1VFrdtVRaHLZTVgCzVWJHtFRc37VUWv+1VFqhtVRanbVUWv+0VFzhs1RiSbdTVAC1VFoatVRa27VU + Wv+1VFr/tVRa/7VUWv+1VFrHu1JBwbtSQf+7UkH/u1JB/7tSQf+7UkHdu1JBHbtSQgDAUikAuVNKR7tT + Q+K7UkGgu1JBnbtTQ+S5U0lJw1AYALtSQgC7UkEau1JB27tSQf+7UkH/u1JB/7tSQf+7UkHHwFAlwcBQ + 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 + zf+PQM38j0DNl48/zA+PP80AAAAAAAAAAAAAAAAAAAAAAI9AzQCPQM0Oj0DNk49AzfuPQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/Y9AzdWPQM1ej0DNp49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM38j0DNl48/zA+PQM0AAAAAAAAAAACPQM0Aj0DNDo9AzZOPQM37j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Aza6PQM2oj0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM38j0DNl48/zRCPP80Aj0DNAI8/zQ+PQM2Uj0DN+49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DNr49AzaePQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM38j0DNmo4/zQ+PP80Oj0DNlo9A + zfyPQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM2ukEHKp5BB + yv+QQcr/kEHK/5BByv+QQcr/kEHK/5BByv+QQcr/kEHK/5BByv+QQcr/kEHK/5BByv+QQcr9kEHJZ5BB + yWKQQcr8kEHK/5BByv+QQcr/kEHK/5BByv+QQcr/kEHK/5BByv+QQcr/kEHK/5BByv+QQcr/kEHK/49B + yq6TRcGnk0XB/5NFwf+TRcH/k0XB/5NFwf+TRcH/k0XB/5NFwf+TRcH/k0XB/5NFwf+TRcH/k0XB/5NF + wf+TRcGEk0XBf5NFwf+TRcH/k0XB/5NFwf+TRcH/k0XB/5NFwf+TRcH/k0XB/5NFwf+TRcH/k0XB/5NF + wf+TRcH/k0XBrpdJtaeXSbX/l0m1/5dJtf+XSbX/l0m1/5dJtf+XSbX/l0m1/5dJtf+XSbX/l0m1/5dJ + tf+XSbX/l0m1/5dJtYKXSbZ9l0m1/5dJtf+XSbX/l0m1/5dJtf+XSbX/l0m1/5dJtf+XSbX/l0m1/5dJ + tf+XSbX/l0m1/5dJtf+XSbWunE2op5xNqP+cTaj/nE2o/5xNqP+cTaj/nE2o/5xNqP+cTaf/nE2o/5xN + qP+cTaj/nE2o/5xNqP+cTaj/nE2ogZxNqHycTaj/nE2o/5xNqP+cTaj/nE2o/5xNqP+cTaf/nE2o/5xN + qP+cTaj/nE2o/5xNqP+cTaj/nE2o/5xNqK6iUZinolGY/6JRmP+iUZj/olGY/6JRmP+iUZj/olGY6KFQ + mrmiUZj8olGY/6JRmP+iUZj/olGY/6JRmP+iUZiBolGYfKJRmP+iUZj/olGY/6JRmP+iUZj/olGY/KFQ + mrqiUZjnolGY/6JRmP+iUZj/olGY/6JRmP+iUZj/olGYrqhTh6eoU4f/qFOH/6hTh/+oU4f/qFOH/6hT + h/+oU4fcplOLL6dTip6oU4f9qFOH/6hTh/+oU4f/qFOH/6hTh4KnU4d9qFOH/6hTh/+oU4f/qFOH/6hT + h/6nU4qiplOLLqhTh9moU4f/qFOH/6hTh/+oU4f/qFOH/6hTh/+oU4evrVR2p61Udv+tVHb/rVR2/61U + dv+tVHb/rVR2/61UdtytVHYbqlSAEaxUeaGtVHb9rVR2/61Udv+tVHb/rVR2g61Udn6tVHb/rVR2/61U + dv+tVHb+rFR5pKpUfxOtVHYYrVR22q1Udv+tVHb/rVR2/61Udv+tVHb/rVR2/61Udq+yVGSoslVk/7JV + ZP+yVWT/slVk/7JVZP+yVWT/slVk3LJUZByxVWcAsFVtFLJVZ6KyVWT+slVk/7JVZP+yVGSEslRkf7JV + ZP+yVWT/slVk/rJVZqawVW0VsVRnALJUZBmyVGTZslVk/7JVZP+yVWT/slVk/7JVZP+yVWT/slRkr7dU + Uai3VFH/t1RR/7dUUf+3VFH/t1RR/7dUUf+3VFHct1NRHLdTUQC1VFoAtVRaFLdUVKO3VFH+t1RR/7dU + UYO3VFF+t1RR/7dUUf63VFSmtVRaFrVUWQC3U1EAt1NRGbdUUdm3VFH/t1RR/7dUUf+3VFH/t1RR/7dU + Uf+3VFGwvFI+qLxSPv+8Uj7/vFI+/7xSPv+8Uj7/vFI+/7xSPty7Uj4cu1I+AMNRHQC6U0cAulNIFLtS + QaG8Uj7/vFI+gbxSPny8Uj7/u1JBpbpTSBa6U0cAv1E0ALtSPgC7Uj4ZvFI+2bxSPv+8Uj7/vFI+/7xS + Pv+8Uj7/vFI+/7xSPrC/UCqov1Eq/79RKv+/USr/v1Eq/79RKv+/USr/v1Aq3b9QKRy/UCkAAAAAANJE + AAC+UTMAvlE0E79RLZ+/UCp6v1Eqdr9RLaK+UTQUvlEzAMNNFwAAAAAAv1ApAL9QKRm/UCrZv1Eq/79R + Kv+/USr/v1Eq/79RKv+/USr/v1AqsMJPEqjCTxL/wk8S/8JPEv/CTxL/wk8S/8JPEv/CTxLdwk8SHcJP + EgAAAAAAAAAAAAAAAADBTx4AwU8eE8FPHCfBUBwnwVAeFMFQHgD/AAAAAAAAAAAAAADCTxIAwk8SGsJP + EtrCTxL/wk8S/8JPEv/CTxL/wk8S/8JPEv/CTxKww08JqMNPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NP + Cd7DTwgdw08IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMNP + CADDTwgaw08J2sNPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCbDDTwmow08J/8NPCf/DTwn/w08J/8NP + Cf/DTwn/w08J3sNPCR3DTwkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAw08JAMNPCRvDTwnbw08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08Jr8NPCafDTwn/w08J/8NP + Cf/DTwn/w08J/8NPCf/DTwndw08JHMNPCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADDTwkAw08JGsNPCdrDTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwmuw08Jp8NP + Cf/DTwn/w08J/8NPCf/DTwn/w08J88NPCZrDTgkMw08JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJPCQDCTwkKw08JlsNPCfLDTwn/w08J/8NPCf/DTwn/w08J/8NP + Ca3DTwmmw08J/8NPCf/DTwn/w08J8sNPCavDTwlGw08JCcNPCQDCTgcAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw08GAMNPCQDCTwgIw08JRMNPCanDTwnxw08J/8NP + Cf/DTwn/w08JrMNPCaTDTwn/w08J8cNPCazDTwlGw04ICcNPCADDTgkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMFMCQDDTwkAwk8JCMNP + CUTDTwmqw08J8cNPCf/DTwmqw08JkcNPCarDTwlFw08ICcNPCQDCTAgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADCTwYAw08JAMNPCQjDTwlDw08JqcNPCZbDTwkXw08JCcNPCQC/SwUAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAw00FAMNPCQDDTgkIw04JGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////5/ + /j/4P/wf4B/4B4AP8AAAB+AAAAPAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABA + AgAAYAYAAHAOAAB4HgAAfD4AAH/+AAB//gAAf/4AAH/+AAD//wAD///AD///8D////z//////////ygA + AAAwAAAAYAAAAAEAIAAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjz7LAI8+ + ywCPPssAjz7LAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjkDLAI4/ywCOP8sAjkDLAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI5A + zACOP8wBjz/NJY9AzXmPQM1Ig0LIAI5AzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACOP8wAkkfWAI9AzUOPQM17j0DNKI8/ + zQKPP80AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACPP8wAjj/LAY8/zCaPQM2Cj0DN3I9Azf+PQM3hj0DNToQ2wQCOP8wAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI8/zQCORMsAj0DNSI9A + zd6PQM3/j0DN3o9AzYWPP80ojj7MAo8/zQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAjz/NAI8+zAGPQM0mj0DNgo9AzdyPQM3+j0DN/49Azf+PQM3/j0DN449AzU+QPcsAjz/NAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjz/NAI9J + zwCPQM1Jj0DN349Azf+PQM3/j0DN/49Azf6PQM3ej0DNhY8/zSiPPswCjz/MAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAI8/zACOPswCjz/NJ49AzYOPQM3dj0DN/o9Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zeSPQM1QijbKAI4/zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACOP8wAk0XXAI9AzUqPQM3gj0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/o9Azd+PQM2Gjz/NKY8/ + zAKPP80AAAAAAAAAAACPP8wAjj7MAo9AzSqPQM2Fj0DN3o9Azf6PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3kjz/NT4w6xgCOP8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAI8/zQCTS84Ajz/NSo9AzeCPQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3+j0DN349AzYiPP80sjj/NA44/zQCPQM0Xj0DNhI9AzeGPQM3+j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN449AzVCQLsMAjz/MAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAjj/NAJJB0ACPQM1Kj0DN4I9Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf6PQM3jj0DNiY8/zRqPQM1nj0DN+o9Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49AzeOPP81PhzjFAI4/ + zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACPQM0Ak0HLAI9AzUmPQM3gj0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/I8/zXOPQM11j0DN/Y9A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3jj0DNT4c/wwCOP8wAAAAAAAAAAAAAAAAAAAAAAI8/zQCNSs4Aj0DNSo9AzeCPQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/48/ + zX+PQM11j0DN/Y9Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN449AzVCNOM0Ajj/NAAAAAAAAAAAAjj/MAJdJ3gCPP81Kj0DN4I9A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/o8/zX6PQM10j0DN/Y9Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49AzeSPQM1Si0HNAY5AzQCPP80AkDjLAI9A + zU2PQM3hj0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/o8/zX2PQM1zj0DN/Y9Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3mjz/NVY07 + zwGRRsoAjz/NT49AzeOPQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/49A + zf+PQM3/j0DN/49Azf+PQM3/j0DN/49Azf+PQM3/j0DN/o8/zX2PQMtzj0DL/Y9Ay/+PQMv/j0DL/49A + y/+PQMv/j0DL/49Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/49A + y/+PQMv/j0DL349AyymPQMslj0DL2o9Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/49A + y/+PQMv/j0DL/49Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/49Ay/+PQMv/j0DL/o9Ay32RQ8ZykUPG/ZFD + xv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FD + xv+RQ8b/kUPG/5FDxv+RQ8b/kUPG+5FDxk2RQ8ZGkUPG+JFDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FD + xv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/5FDxv+RQ8b/kUPG/pFD + xn2URsBylEbA/ZRGwP+URsD/lEbA/5RGwP+URsD/lEbA/5RGwP+URsD/lEbA/5RGwP+URsD/lEbA/5RG + wP+URsD/lEbA/5RGwP+URsD/lEbA/5RGwP+URsD/lEbA/JRGwFCURsBJlEbA+ZRGwP+URsD/lEbA/5RG + wP+URsD/lEbA/5RGwP+URsD/lEbA/5RGwP+URsD/lEbA/5RGwP+URsD/lEbA/5RGwP+URsD/lEbA/5RG + wP+URsD/lEbA/pRGwH2XSbhyl0m4/ZdJuP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJ + uP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJuP+XSbj/l0m4+5dJuE6XSbhGl0m4+ZdJ + uP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJuP+XSbj/l0m4/5dJ + uP+XSbj/l0m4/5dJuP+XSbj/l0m4/pdJuH2aS69ymkyv/ZpMr/+aTK//mkyv/5pMr/+aTK//mkyv/5pM + r/+aTK//mkyv/5pMr/+aTK//mkyv/5pMr/+aTK//mkyv/5pMr/+aTK//mkyv/5pMr/+aTK//mkyv+5pM + r02aTK9Fmkyv+JpMr/+aTK//mkyv/5pMr/+aTK//mkyv/5pMr/+aTK//mkyv/5pMr/+aTK//mkyv/5pM + r/+aTK//mkyv/5pMr/+aTK//mkyv/5pMr/+aTK//mkyv/ppMr32dTqVynU6l/Z1Opf+dTqX/nU6l/51O + pf+dTqX/nU6l/51Opf+dTqX/nU6l/51Opf+dTqX/nU6l/51Opf+dTqX/nU6l/51Opf+dTqX/nU6l/51O + pf+dTqX/nU6l+51OpU2dTqVEnU6l+J1Opf+dTqX/nU6l/51Opf+dTqX/nU6l/51Opf+dTqX/nU6l/51O + pf+dTqX/nU6l/51Opf+dTqX/nU6l/51Opf+dTqX/nU6l/51Opf+dTqX/nU6l/p1OpX2hUJtyoVCb/aFQ + m/+hUJv/oVCb/6FQm/+hUJv/oVCb/6FQm/+hUJv/oVCb/6FQm+uhUJzHoVCb/aFQm/+hUJv/oVCb/6FQ + m/+hUJv/oVCb/6FQm/+hUJv/oVCb+6FQm02hUJtFoVCb+KFQm/+hUJv/oVCb/6FQm/+hUJv/oVCb/6FQ + m/+hUJv/oVCb/qFQnMihUJvpoVCb/6FQm/+hUJv/oVCb/6FQm/+hUJv/oVCb/6FQm/+hUJv/oVCb/qFQ + m32lUpBypVKQ/aVSkP+lUpD/pVKQ/6VSkP+lUpD/pVKQ/6VSkP+lUpD/pVKQ/6VSkNqkUpI3pFKRraVS + kP+lUpD/pVKQ/6VSkP+lUpD/pVKQ/6VSkP+lUpD/pVKQ+6VSkE2lUpBFpVKQ+KVSkP+lUpD/pVKQ/6VS + kP+lUpD/pVKQ/6VSkP+lUpD/pFKRsqRSkzalUpDVpVKQ/6VSkP+lUpD/pVKQ/6VSkP+lUpD/pVKQ/6VS + kP+lUpD/pVKQ/qVSkH6oU4VzqFOF/ahThf+oU4X/qFOF/6hThf+oU4X/qFOF/6hThf+oU4X/qFOF/6hT + hdupU4QaplOKGahThq+oU4X/qFOF/6hThf+oU4X/qFOF/6hThf+oU4X/qFOF+6hThU6oU4VHqFOF+ahT + hf+oU4X/qFOF/6hThf+oU4X/qFOF/6hThf+oU4a0p1OKHalThBeoU4XXqFOF/6hThf+oU4X/qFOF/6hT + hf+oU4X/qFOF/6hThf+oU4X/qFOF/6hThX6sVHlzrFR5/axUef+sVHn/rFR5/6xUef+sVHn/rFR5/6xU + ef+sVHn/rFR5/6xUedqsVHkbq1R7AKpUfhusVHuwrFR5/6xUef+sVHn/rFR5/6xUef+sVHn/rFR5+6xU + eU+sVHlIrFR5+axUef+sVHn/rFR5/6xUef+sVHn/rFR5/6xUerWqVH4eq1R7AKxUeRisVHnWrFR5/6xU + ef+sVHn/rFR5/6xUef+sVHn/rFR5/6xUef+sVHn/rFR5/6xUeX+wVW1zsFVt/bBVbf+wVW3/sFVt/7BV + bf+wVW3/sFVt/7BVbf+wVW3/sFVt/7BVbdqwVW0bsFVtAK5VcQCuVHIcr1VvsrBVbf+wVW3/sFVt/7BV + bf+wVW3/sFVt+7BVbVCwVW1IsFVt+bBVbf+wVW3/sFVt/7BVbf+wVW3/r1Vutq5Uch+uVXEAsFVtALBV + bRewVW3WsFVt/7BVbf+wVW3/sFVt/7BVbf+wVW3/sFVt/7BVbf+wVW3/sFVt/7BVbX+zVGF0s1Rh/bNU + Yf+zVGH/s1Rh/7NUYf+zVGH/s1Rh/7NUYf+zVGH/s1Rh/7NUYdqzVGEbs1RhALJUZACyVWUAslVmHbNU + YrOzVGD/s1Rh/7NUYf+zVGH/s1Rh+7NUYVCzVGFJs1Rh+bNUYf+zVGH/s1Rh/7NUYP+zVGK3slVmILJV + ZQCyVGYAs1RhALNUYRezVGHVs1Rh/7NUYf+zVGH/s1Rh/7NUYf+zVGH/s1Rh/7NUYf+zVGH/s1Rh/7NU + YX+3VFR0t1RU/bdUVP+3VFT/t1RU/7dUVP+3VFT/t1RU/7dUVP+3VFT/t1RU/7dUVNq2VFQbtlRUAAAA + AAC5UE8AtVRZALVUWh62VFazt1RU/7dUVP+3VFT/t1RU+7dUVE+2VFRIt1RU+bdUVP+3VFT/t1RU/7ZU + Vri1VFohtVRZALZSVgAAAAAAtlNUALZTVBe2VFTVt1RU/7dUVP+3VFT/t1RU/7dUVP+3VFT/t1RU/7dU + VP+3VFT/t1RU/7ZUVIC6U0h0ulNI/bpTSP+6U0j/ulNI/7pTSP+6U0j/ulNI/7pTSP+6U0j/ulNI/7pT + SNq6U0gbulNIAAAAAAAAAAAAuFNPALlTTAC4U00euVNJs7pTSP+6U0j/uVNI+7lTSE65U0hGuVNI+bpT + SP+6U0j/uVNJt7hTTSG4U00AvlQ5AAAAAAAAAAAAuVNIALlTSBe5U0jVulNI/7pTSP+6U0j/ulNI/7pT + SP+6U0j/ulNI/7pTSP+6U0j/ulNI/7pTSIC8Ujt0vFI7/bxSO/+8Ujv/vFI7/7xSO/+8Ujv/vFI7/7xS + O/+8Ujv/vFI7/7xSO9q8UjsbvFI7AAAAAAAAAAAAAAAAALtRPwC7UkAAu1JBHbxSPbC8Ujv/vFI7+7xS + O0y8UjtEvFI7+LxSO/+8Ujy1u1JBILtSPwC7UkEAAAAAAAAAAAAAAAAAvFI7ALxSOxe8UjvVvFI7/7xS + O/+8Ujv/vFI7/7xSO/+8Ujv/vFI7/7xSO/+8Ujv/vFI7/7xSO4C/US11v1Et/b9RLf+/US3/v1Et/79R + Lf+/US3/v1Et/79RLf+/US3/v1Et/79RLdu/US0bv1EtAAAAAAAAAAAAAAAAAAAAAADAUBwAvlEzAL5R + NBq+US+rv1Et+L9RLUm/US1Cv1Et9r5RL7C+UTMdvlEzAMBOKwAAAAAAAAAAAAAAAAAAAAAAvlEtAL5R + LRe/US3Vv1Et/79RLf+/US3/v1Et/79RLf+/US3/v1Et/79RLf+/US3/v1Et/79RLYDBUB51wVAe/cFQ + Hv/BUB7/wVAe/8FQHv/BUB7/wVAe/8FQHv/BUB7/wVAe/8FQHtvBUB4cwVAeAAAAAAAAAAAAAAAAAAAA + AAAAAAAAwU8kAMBQJQDAUCYYwFAhm8FQH0DBUB86wVAhncBQJhvAUCUAwFAlAAAAAAAAAAAAAAAAAAAA + AAAAAAAAwVAeAMFPHhfBUB7WwVAe/8FQHv/BUB7/wVAe/8FQHv/BUB7/wVAe/8FQHv/BUB7/wVAe/8FQ + HoDCTw51wk8O/cNPDv/DTw7/w08O/8NPDv/DTw7/w08O/8NPDv/DTw7/w08O/8JPDtzCTw4cwk8OAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMM9AADCTxcAwk8WD8JPFg7CTxYNwk8WEMJPFwDETwUAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAwk8OAMJPDhjCTw7Ww08O/8NPDv/DTw7/w08O/8NPDv/DTw7/w08O/8NP + Dv/DTw7/w08O/8JPDoDDTwh0w08J/cNPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NP + CdzDTggdw04IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwk8IAMJPCBjDTwnXw08J/8NPCf/DTwn/w08J/8NP + Cf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCIDDTwl0w08J/cNPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NP + Cf/DTwn/w08J/8NPCdzDTwgdw08IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw04IAMNOCBnDTwnXw08J/8NP + Cf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCX/DTwl0w08J/cNPCf/DTwn/w08J/8NP + Cf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCd3DTwgdw08IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw08IAMNP + CBnDTwnYw08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCX/DTwlzw08J/cNP + Cf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCdzDTwgdw08IAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAw08IAMNPCBnDTwnYw08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/sNP + CX7DTwlzw08J/cNPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCdXDTgkXw04JAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAwk8JAMJPCRTDTwnQw08J/8NPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NP + Cf/DTwn/w08J/sNPCX3DTwlyw08J/cNPCf/DTwn/w08J/8NPCf/DTwn/w08J/8NPCf/DTwn9w08J1cNP + CWjDTggEw04IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwk8IAMFOCAPDTwljw08J08NPCfzDTwn/w08J/8NP + Cf/DTwn/w08J/8NPCf/DTwn/w08J/sNPCXvDTwlxw08J/cNPCf/DTwn/w08J/8NPCf/DTwn/w08J/cNP + CdPDTwl0w04JHsNPBwHDTwgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJPCAC/TQcAw08JHMNP + CXHDTwnRw08J/MNPCf/DTwn/w08J/8NPCf/DTwn/w08J/sNPCXrDTwlww08J/cNPCf/DTwn/w08J/8NP + Cf3DTwnUw08JdMNOCR3DTQcAw04IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAwk4IAMFLBwDCTwkbw08JccNPCdHDTwn8w08J/8NPCf/DTwn/w08J/sNPCXjDTwluw08J/MNP + Cf/DTwn8w08J08NPCXXDTwkewU0EAMJOBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCTgkAv00KAMNOCBzDTwlyw08J0cNPCfzDTwn/w08J/sNP + CXbDTwltw08J+cNPCdDDTwlyw04JHcFPBgDCTggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMJOCQDDTgkAw08JG8NP + CW/DTwnOw08J+cNPCXXDTwlGw08JaMJPCRrATAUAwk4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAwk8IAL5QBADDTwgZw08JaMNOCUrDTQgBw00IAMNNCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDTggAw04IAMNPCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + RU5ErkJggg== + + + \ No newline at end of file diff --git a/FireWallet/NewAccountForm.cs b/FireWallet/NewAccountForm.cs index 3b73886..ef87d27 100644 --- a/FireWallet/NewAccountForm.cs +++ b/FireWallet/NewAccountForm.cs @@ -12,6 +12,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.VisualBasic.Devices; +using Newtonsoft.Json.Linq; namespace FireWallet { @@ -34,7 +35,7 @@ namespace FireWallet { mainForm.ThemeControl(c); } - + groupBoxMulti.Hide(); } @@ -215,7 +216,8 @@ namespace FireWallet // Create new wallet buttonNext.Enabled = false; string path = "wallet/" + textBoxNewName.Text; - string content = "{\"passphrase\":\"" + textBoxNewPass1.Text + "\"}"; + string content = "{}"; + //content = "{\"passphrase\":\"" + textBoxNewPass1.Text + "\"}"; string response = await APIPut(path, true, content); if (response == "Error") { @@ -226,16 +228,51 @@ namespace FireWallet return; } mainForm.AddLog("Created wallet: " + textBoxNewName.Text); - NotifyForm notify2 = new NotifyForm("Created wallet: " + textBoxNewName.Text); - notify2.ShowDialog(); - notify2.Dispose(); + + // Show SEED PHRASE + path = "wallet/" + textBoxNewName.Text + "/master"; + response = await mainForm.APIGet(path, true); + JObject resp = JObject.Parse(response); + if (resp["encrypted"].ToString() == "False") + { + JObject mnemonic = JObject.Parse(resp["mnemonic"].ToString()); + string phrase = mnemonic["phrase"].ToString(); + NotifyForm notifyForm = new NotifyForm("SEED PHRASE\nSTORE THIS SOMEWHERE SECURE\n" + phrase, "Copy", phrase, true); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + } + + // Select wallet + content = "{\"method\":\"selectwallet\",\"params\":[\"" + textBoxNewName.Text + "\"]}"; + response = await mainForm.APIPost("", true, content); + if (response == "Error") + { + NotifyForm notify = new NotifyForm("Error selecting wallet"); + notify.ShowDialog(); + notify.Dispose(); + buttonNext.Enabled = true; + return; + } + + // Encrypt wallet + content = "{\"method\":\"encryptwallet\",\"params\":[\"" + textBoxNewPass1.Text + "\"]}"; + response = await mainForm.APIPost("", true, content); + if (response == "Error") + { + NotifyForm notify = new NotifyForm("Error encrypting wallet"); + notify.ShowDialog(); + notify.Dispose(); + buttonNext.Enabled = true; + return; + } + mainForm.AddLog("Encrypted wallet: " + textBoxNewName.Text); this.Close(); } else { // Create new wallet buttonNext.Enabled = false; string path = "wallet/" + textBoxNewName.Text; - string content = "{\"passphrase\":\"" + textBoxNewPass1.Text + "\", \"type\":\"multisig\",\"m\":"+numericUpDownM.Value.ToString()+ ",\"n\":" +numericUpDownN.Value.ToString() + "}"; + string content = "{\"type\":\"multisig\",\"m\":"+numericUpDownM.Value.ToString()+ ",\"n\":" +numericUpDownN.Value.ToString() + "}"; string response = await APIPut(path, true, content); if (response == "Error") { @@ -245,11 +282,43 @@ namespace FireWallet buttonNext.Enabled = true; return; } - mainForm.AddLog("Created wallet: " + textBoxNewName.Text); - NotifyForm notify2 = new NotifyForm("Created wallet: " + textBoxNewName.Text); - notify2.ShowDialog(); - notify2.Dispose(); + path = "wallet/" + textBoxNewName.Text + "/master"; + response = await mainForm.APIGet(path, true); + JObject resp = JObject.Parse(response); + if (resp["encrypted"].ToString() == "False") + { + JObject mnemonic = JObject.Parse(resp["mnemonic"].ToString()); + string phrase = mnemonic["phrase"].ToString(); + NotifyForm notifyForm = new NotifyForm("SEED PHRASE\nSTORE THIS SOMEWHERE SECURE\n" + phrase, "Copy", phrase, true); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + + } + // Select wallet + content = "{\"method\":\"selectwallet\",\"params\":[\"" + textBoxNewName.Text + "\"]}"; + response = await mainForm.APIPost("", true, content); + if (response == "Error") + { + NotifyForm notify = new NotifyForm("Error selecting wallet"); + notify.ShowDialog(); + notify.Dispose(); + buttonNext.Enabled = true; + return; + } + // Encrypt wallet + content = "{\"method\":\"encryptwallet\",\"params\":[\"" + textBoxNewPass1.Text + "\"]}"; + response = await mainForm.APIPost("", true, content); + if (response == "Error") + { + NotifyForm notify = new NotifyForm("Error encrypting wallet"); + notify.ShowDialog(); + notify.Dispose(); + buttonNext.Enabled = true; + return; + } + mainForm.AddLog("Encrypted wallet: " + textBoxNewName.Text); this.Close(); + } } From ac5cc3f82e0f60e449bddd0711504e5d4f9aad21 Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Tue, 27 Jun 2023 12:15:51 +1000 Subject: [PATCH 11/11] package: Cleaned up code and increased version number --- FireWallet/BatchForm.cs | 1 - FireWallet/FireWallet.csproj | 2 +- FireWallet/MainForm.cs | 1 - FireWallet/MultisigSettingsForm.cs | 13 +------------ FireWallet/NewAccountForm.cs | 13 +------------ FireWalletSetup/FireWalletSetup.vdproj | 6 +++--- 6 files changed, 6 insertions(+), 30 deletions(-) diff --git a/FireWallet/BatchForm.cs b/FireWallet/BatchForm.cs index 7b01fe6..bf77044 100644 --- a/FireWallet/BatchForm.cs +++ b/FireWallet/BatchForm.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Net; using System.Runtime.InteropServices; using System.Text; -using System.Windows.Forms.VisualStyles; using Newtonsoft.Json.Linq; using ContentAlignment = System.Drawing.ContentAlignment; using Point = System.Drawing.Point; diff --git a/FireWallet/FireWallet.csproj b/FireWallet/FireWallet.csproj index a783757..386934d 100644 --- a/FireWallet/FireWallet.csproj +++ b/FireWallet/FireWallet.csproj @@ -12,7 +12,7 @@ HSDBatcher.png https://github.com/Nathanwoodburn/FireWallet git - 3.4 + 4.0 diff --git a/FireWallet/MainForm.cs b/FireWallet/MainForm.cs index 60045e3..1bd227a 100644 --- a/FireWallet/MainForm.cs +++ b/FireWallet/MainForm.cs @@ -6,7 +6,6 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.RegularExpressions; -using System.Windows.Forms.VisualStyles; using DnsClient; using DnsClient.Protocol; using Newtonsoft.Json.Linq; diff --git a/FireWallet/MultisigSettingsForm.cs b/FireWallet/MultisigSettingsForm.cs index ea4594a..982387c 100644 --- a/FireWallet/MultisigSettingsForm.cs +++ b/FireWallet/MultisigSettingsForm.cs @@ -1,15 +1,4 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using System.Web; -using System.Windows.Forms; -using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Linq; namespace FireWallet { diff --git a/FireWallet/NewAccountForm.cs b/FireWallet/NewAccountForm.cs index ef87d27..deafc09 100644 --- a/FireWallet/NewAccountForm.cs +++ b/FireWallet/NewAccountForm.cs @@ -1,17 +1,6 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Diagnostics; -using System.Drawing; -using System.Linq; -using System.Net.Http; -using System.Security.Principal; +using System.Diagnostics; using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Windows.Forms; -using Microsoft.VisualBasic.Devices; using Newtonsoft.Json.Linq; namespace FireWallet diff --git a/FireWalletSetup/FireWalletSetup.vdproj b/FireWalletSetup/FireWalletSetup.vdproj index 1c4b3bd..0b2b2d6 100644 --- a/FireWalletSetup/FireWalletSetup.vdproj +++ b/FireWalletSetup/FireWalletSetup.vdproj @@ -224,15 +224,15 @@ { "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:FireWallet" - "ProductCode" = "8:{E636567F-DDA4-4C6E-89B0-38DC64FD5528}" - "PackageCode" = "8:{AEAF1ABA-01E0-4A71-A8CC-0D6DDA44E907}" + "ProductCode" = "8:{E904E664-B1F3-4DEB-9FB0-91BCBEE3B5A6}" + "PackageCode" = "8:{0B16637D-C5AC-41D4-9D13-D57F3A0AEF25}" "UpgradeCode" = "8:{0C86F725-6B01-4173-AA05-3F0EDF481362}" "AspNetVersion" = "8:" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:TRUE" "DetectNewerInstalledVersion" = "11:TRUE" "InstallAllUsers" = "11:FALSE" - "ProductVersion" = "8:3.4" + "ProductVersion" = "8:4.0" "Manufacturer" = "8:Nathan.Woodburn/" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:https://l.woodburn.au/discord"