From f69f16df4a92d62f703339869357784d8e3c7d3e Mon Sep 17 00:00:00 2001 From: Nathan Woodburn Date: Mon, 26 Jun 2023 21:21:55 +1000 Subject: [PATCH] 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