multisig: Added multisig from batching

This commit is contained in:
Nathan Woodburn 2023-06-26 21:21:55 +10:00
parent db5934bf4a
commit f69f16df4a
Signed by: nathanwoodburn
GPG Key ID: 203B000478AD0EF1
3 changed files with 168 additions and 22 deletions

View File

@ -29,6 +29,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DnsClient" Version="1.7.0" /> <PackageReference Include="DnsClient" Version="1.7.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageReference Include="QRCoder" Version="1.4.3" /> <PackageReference Include="QRCoder" Version="1.4.3" />
</ItemGroup> </ItemGroup>

View File

@ -1,4 +1,5 @@
using Newtonsoft.Json.Linq; using System.Windows.Forms.VisualStyles;
using Newtonsoft.Json.Linq;
namespace FireWallet namespace FireWallet
{ {
@ -10,15 +11,25 @@ namespace FireWallet
int reqSigs; int reqSigs;
int sigs; int sigs;
string signedTX; string signedTX;
string[] domains;
public ImportTXForm(MainForm mainForm) public ImportTXForm(MainForm mainForm)
{ {
InitializeComponent(); InitializeComponent();
this.mainForm = mainForm; 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) private void ImportTXForm_Load(object sender, EventArgs e)
{ {
// Default variables // Default variables
domains = new string[0];
signedTX = ""; signedTX = "";
totalSigs = 3; totalSigs = 3;
reqSigs = 2; reqSigs = 2;
@ -31,25 +42,28 @@ namespace FireWallet
{ {
mainForm.ThemeControl(c); mainForm.ThemeControl(c);
} }
OpenFileDialog openFileDialog = new OpenFileDialog(); if (tx == null)
openFileDialog.Filter = "Transaction files (*.json)|*.json";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{ {
string tx = System.IO.File.ReadAllText(openFileDialog.FileName); OpenFileDialog openFileDialog = new OpenFileDialog();
try openFileDialog.Filter = "Transaction files (*.json)|*.json";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{ {
JObject txObj = JObject.Parse(tx); string tx = System.IO.File.ReadAllText(openFileDialog.FileName);
this.tx = txObj; try
ParseTX(); {
} JObject txObj = JObject.Parse(tx);
catch this.tx = txObj;
{ }
NotifyForm notifyForm = new NotifyForm("Invalid transaction file."); catch
notifyForm.ShowDialog(); {
notifyForm.Dispose(); NotifyForm notifyForm = new NotifyForm("Invalid transaction file.");
this.Close(); notifyForm.ShowDialog();
notifyForm.Dispose();
this.Close();
}
} }
} }
ParseTX();
} }
private void Cancelbutton2_Click(object sender, EventArgs e) private void Cancelbutton2_Click(object sender, EventArgs e)
@ -230,9 +244,6 @@ namespace FireWallet
{ {
if (metaOutput.ContainsKey("name")) if (metaOutput.ContainsKey("name"))
{ {
Label covenantLabel = new Label(); Label covenantLabel = new Label();
string name = metaOutput["name"].ToString(); string name = metaOutput["name"].ToString();
@ -240,6 +251,14 @@ namespace FireWallet
covenantLabel.Location = new Point(5, 25); covenantLabel.Location = new Point(5, 25);
covenantLabel.AutoSize = true; covenantLabel.AutoSize = true;
PanelOutput.Controls.Add(covenantLabel); 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; labelSigsSigned.Width = (labelSigsTotal.Width / totalSigs) * sigs;
labelSigInfo.Text = "Signed: " + sigs + "\nReq: " + reqSigs + " of " + totalSigs; labelSigInfo.Text = "Signed: " + sigs + "\nReq: " + reqSigs + " of " + totalSigs;
signedTX = response; signedTX = response;
} else {
} }
} }
private void buttonExport_Click(object sender, EventArgs e) private void buttonExport_Click(object sender, EventArgs e)
{ {
mainForm.ExportTransaction(signedTX); mainForm.ExportTransaction(signedTX,domains);
} }
private async void buttonSend_Click(object sender, EventArgs e) private async void buttonSend_Click(object sender, EventArgs e)

View File

@ -6,6 +6,7 @@ using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Windows.Forms.VisualStyles;
using DnsClient; using DnsClient;
using DnsClient.Protocol; using DnsClient.Protocol;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@ -1982,6 +1983,26 @@ namespace FireWallet
} }
ExportTransaction(output); 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; BatchMode = true;
} }
BatchForm.AddBatch(domain, operation); BatchForm.AddBatch(domain, operation);
BatchForm.UpdateTheme();
} }
public void AddBatch(string domain, string operation, decimal bid, decimal lockup) public void AddBatch(string domain, string operation, decimal bid, decimal lockup)
@ -2533,6 +2555,7 @@ namespace FireWallet
BatchMode = true; BatchMode = true;
} }
BatchForm.AddBatch(domain, operation, bid, lockup); BatchForm.AddBatch(domain, operation, bid, lockup);
BatchForm.UpdateTheme();
} }
public void AddBatch(string domain, string operation, DNS[] updateRecords) public void AddBatch(string domain, string operation, DNS[] updateRecords)
@ -2544,6 +2567,7 @@ namespace FireWallet
BatchMode = true; BatchMode = true;
} }
BatchForm.AddBatch(domain, operation, updateRecords); BatchForm.AddBatch(domain, operation, updateRecords);
BatchForm.UpdateTheme();
} }
public void AddBatch(string domain, string operation, string address) public void AddBatch(string domain, string operation, string address)
{ {
@ -2554,6 +2578,7 @@ namespace FireWallet
BatchMode = true; BatchMode = true;
} }
BatchForm.AddBatch(domain, operation, address); BatchForm.AddBatch(domain, operation, address);
BatchForm.UpdateTheme();
} }
public void FinishBatch() public void FinishBatch()
{ {
@ -2699,7 +2724,11 @@ namespace FireWallet
#region Multi #region Multi
public void ExportTransaction(string rawTX) public async Task<string> ExportTransaction(string rawTX)
{
return await ExportTransaction(rawTX, new string[0]);
}
public async Task<string> ExportTransaction(string rawTX, string[] domains)
{ {
JObject tx = JObject.Parse(rawTX); JObject tx = JObject.Parse(rawTX);
JObject toExport = new JObject(); JObject toExport = new JObject();
@ -2709,6 +2738,14 @@ namespace FireWallet
JArray outputsParsed = new JArray(); JArray outputsParsed = new JArray();
JArray inputs = JArray.Parse(tx["inputs"].ToString()); JArray inputs = JArray.Parse(tx["inputs"].ToString());
JArray outputs = JArray.Parse(tx["outputs"].ToString()); JArray outputs = JArray.Parse(tx["outputs"].ToString());
Dictionary<string,string> hashes = new Dictionary<string, string>();
foreach (string domain in domains)
{
// sha3 hash of domain
hashes.Add(CalculateSHA3Hash(domain),domain);
}
foreach (JObject input in inputs) foreach (JObject input in inputs)
{ {
JObject coin = JObject.Parse(input["coin"].ToString()); JObject coin = JObject.Parse(input["coin"].ToString());
@ -2722,6 +2759,7 @@ namespace FireWallet
else else
{ {
AddLog("Not supported yet"); AddLog("Not supported yet");
return "Error";
} }
} }
foreach (JObject output in outputs) foreach (JObject output in outputs)
@ -2735,7 +2773,20 @@ namespace FireWallet
} }
else 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(); JObject metadata = new JObject();
@ -2753,8 +2804,81 @@ namespace FireWallet
StreamWriter sw = new StreamWriter(saveFileDialog.FileName); StreamWriter sw = new StreamWriter(saveFileDialog.FileName);
sw.Write(toExport.ToString()); sw.Write(toExport.ToString());
sw.Dispose(); 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 #endregion
} }
} }