11 Commits

Author SHA1 Message Date
f371f3da49 main: Attempted to add seed phrase decryption 2023-06-16 15:36:58 +10:00
a024ce7afc readme: Added installation dependencies
- Added instructions for installing .net desktop
- Added instructions for installing Node, NPM, and git
2023-06-16 11:20:48 +10:00
88ee50f4a6 main: Dependencies install links 2023-06-15 22:56:35 +10:00
23cbace1ea package: Increased version 2023-06-15 22:28:51 +10:00
6894e9c079 main: Fixed installation of hsd 2023-06-15 22:21:12 +10:00
95d0498672 main: cleaned up code 2023-06-15 22:01:32 +10:00
88c6b5afe0 README.md: Added instructions for using HIP-02
- Added instructions for using HIP-02 to send HNS to Handshake addresses or domains.
- To use HIP-02, a user needs to have an HSD resolver listening on port 5350.
- A domain must be prefixed with `@` to use HIP-02 (e.g., `@nathan.woodburn`).
2023-06-15 17:38:05 +10:00
f06bc5b711 mainForm.cs: Added domain sorting feature and updated UI
- Added comboBoxDomainSort_DropDownClosed method to update domains
- Added comboBoxDomainSort control to panelDomains
- Added labelDomainSort control to MainForm.Designer.cs
- Updated UpdateDomains method to sort domains based on selected option
2023-06-15 16:47:59 +10:00
42536e47bb README.md: Added a note about BID import syntax
- Added a note about the BID import syntax being BID,LOCKUP where LOCKUP is (BID+BLIND)
2023-06-15 16:43:51 +10:00
404a47ec79 batchForm.cs: Added try-catch block to handle file in use exceptions
- Added try-catch block to handle exceptions when importing batch
- Displayed error message and notification form if an exception occurs
2023-06-15 16:21:13 +10:00
c3abd0b4de README.md: Added ability to send HNS using HIP-02
- Added option to send HNS to Handshake addresses or domains
- Implemented [HIP-02](https://github.com/handshake-org/HIPs/blob/master/HIP-0002.md) for sending HNS
2023-06-15 14:07:26 +10:00
6 changed files with 295 additions and 123 deletions

View File

@@ -637,6 +637,8 @@ namespace FireWallet
openFileDialog.Filter = "All Files|*.*";
openFileDialog.Title = "Open Batch";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
try
{
StreamReader sr = new StreamReader(openFileDialog.FileName);
string line;
@@ -718,6 +720,13 @@ namespace FireWallet
}
}
sr.Dispose();
} catch (Exception ex)
{
AddLog("Error importing batch: " + ex.Message);
NotifyForm notifyForm = new NotifyForm("Error importing batch\nMake sure the file is in not in use");
notifyForm.ShowDialog();
notifyForm.Dispose();
}
}
}

View File

@@ -12,7 +12,7 @@
<PackageIcon>HSDBatcher.png</PackageIcon>
<RepositoryUrl>https://github.com/Nathanwoodburn/FireWallet</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<Version>3.0</Version>
<Version>3.1</Version>
</PropertyGroup>
<ItemGroup>

View File

@@ -95,6 +95,7 @@ namespace FireWallet
textBoxReceiveAddress = new TextBox();
labelReceive1 = new Label();
panelDomains = new Panel();
comboBoxDomainSort = new ComboBox();
buttonExportDomains = new Button();
groupBoxDomains = new GroupBox();
panelDomainList = new Panel();
@@ -122,6 +123,7 @@ namespace FireWallet
textBoxExAddr = new TextBox();
labelSettings4 = new Label();
textBoxExTX = new TextBox();
labelDomainSort = new Label();
statusStripmain.SuspendLayout();
panelaccount.SuspendLayout();
groupBoxaccount.SuspendLayout();
@@ -783,6 +785,8 @@ namespace FireWallet
//
// panelDomains
//
panelDomains.Controls.Add(labelDomainSort);
panelDomains.Controls.Add(comboBoxDomainSort);
panelDomains.Controls.Add(buttonRenewAll);
panelDomains.Controls.Add(buttonExportDomains);
panelDomains.Controls.Add(groupBoxDomains);
@@ -794,6 +798,19 @@ namespace FireWallet
panelDomains.TabIndex = 18;
panelDomains.Visible = false;
//
// comboBoxDomainSort
//
comboBoxDomainSort.DropDownStyle = ComboBoxStyle.DropDownList;
comboBoxDomainSort.FlatStyle = FlatStyle.Flat;
comboBoxDomainSort.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point);
comboBoxDomainSort.FormattingEnabled = true;
comboBoxDomainSort.Items.AddRange(new object[] { "Default", "Alphabetical", "Expiring", "Value" });
comboBoxDomainSort.Location = new Point(686, 12);
comboBoxDomainSort.Name = "comboBoxDomainSort";
comboBoxDomainSort.Size = new Size(121, 29);
comboBoxDomainSort.TabIndex = 11;
comboBoxDomainSort.DropDownClosed += comboBoxDomainSort_DropDownClosed;
//
// buttonExportDomains
//
buttonExportDomains.FlatStyle = FlatStyle.Flat;
@@ -1064,17 +1081,27 @@ namespace FireWallet
textBoxExTX.Size = new Size(307, 29);
textBoxExTX.TabIndex = 1;
//
// labelDomainSort
//
labelDomainSort.AutoSize = true;
labelDomainSort.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point);
labelDomainSort.Location = new Point(638, 15);
labelDomainSort.Name = "labelDomainSort";
labelDomainSort.Size = new Size(42, 21);
labelDomainSort.TabIndex = 12;
labelDomainSort.Text = "Sort:";
//
// MainForm
//
AutoScaleDimensions = new SizeF(7F, 15F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1152, 575);
Controls.Add(panelDomains);
Controls.Add(panelSend);
Controls.Add(panelSettings);
Controls.Add(panelaccount);
Controls.Add(panelPortfolio);
Controls.Add(panelRecieve);
Controls.Add(panelDomains);
Controls.Add(panelNav);
Controls.Add(statusStripmain);
Icon = (Icon)resources.GetObject("$this.Icon");
@@ -1205,5 +1232,7 @@ namespace FireWallet
private ToolStripMenuItem supportDiscordServerToolStripMenuItem;
private Label labelHIPArrow;
private Label labelSendingHIPAddress;
private ComboBox comboBoxDomainSort;
private Label labelDomainSort;
}
}

View File

@@ -14,6 +14,7 @@ using DnsClient;
using DnsClient.Protocol;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using System.Numerics;
namespace FireWallet
{
@@ -221,6 +222,28 @@ namespace FireWallet
return false;
}
}
} else
{
if (!Directory.Exists(dir + "hsd"))
{
NotifyForm Notifyinstall = new NotifyForm("Installing hsd\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.git";
string destinationPath = dir + "hsd";
CloneRepository(repositoryUrl, destinationPath);
Notifyinstall.CloseNotification();
Notifyinstall.Dispose();
}
if (!Directory.Exists(dir + "hsd\\node_modules"))
{
AddLog("HSD install failed");
this.Close();
return false;
}
}
@@ -664,6 +687,7 @@ namespace FireWallet
toolStripStatusLabelLedger.Text = "Cold Wallet";
toolStripStatusLabelLedger.Visible = true;
buttonRevealAll.Visible = false;
buttonSeed.Enabled = false;
}
else
@@ -671,6 +695,7 @@ namespace FireWallet
watchOnly = false;
toolStripStatusLabelLedger.Visible = false;
buttonRevealAll.Visible = true;
buttonSeed.Enabled = true;
}
@@ -842,7 +867,6 @@ namespace FireWallet
{
AddLog("Post Error: " + ex.Message);
AddLog(await resp.Content.ReadAsStringAsync());
AddLog("Content: " + content);
return "Error";
}
@@ -1216,6 +1240,8 @@ namespace FireWallet
groupBoxDomains.Width = panelDomains.Width - 20;
groupBoxDomains.Left = 10;
groupBoxDomains.Height = panelDomains.Height - groupBoxDomains.Top - 10;
comboBoxDomainSort.SelectedIndex = 0;
UpdateDomains();
}
@@ -1414,27 +1440,6 @@ namespace FireWallet
return hex.ToString();
}
private static string GetHash(HashAlgorithm hashAlgorithm, byte[] input)
{
// Convert the input string to a byte array and compute the hash.
byte[] data = hashAlgorithm.ComputeHash(input);
// Create a new Stringbuilder to collect the bytes
// and create a string.
var sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
// Return the hexadecimal string.
return sBuilder.ToString();
}
private void textBoxSendingAmount_Leave(object sender, EventArgs e)
{
decimal amount = 0;
@@ -1647,7 +1652,7 @@ namespace FireWallet
if (!outputInstalled.Contains("git version"))
{
AddLog("Git is not installed");
NotifyForm notifyForm = new NotifyForm("Git is not installed\nPlease install it to install HSD dependencies");
NotifyForm notifyForm = new NotifyForm("Git is not installed\nPlease install it to install HSD dependencies","Install", "https://git-scm.com/download/win");
notifyForm.ShowDialog();
notifyForm.Dispose();
this.Close();
@@ -1669,7 +1674,7 @@ namespace FireWallet
if (!outputInstalled.Contains("v"))
{
AddLog("Node is not installed");
NotifyForm notifyForm = new NotifyForm("Node is not installed\nPlease install it to install HSD dependencies");
NotifyForm notifyForm = new NotifyForm("Node is not installed\nPlease install it to install HSD dependencies","Install", "https://nodejs.org/en/download");
notifyForm.ShowDialog();
notifyForm.Dispose();
this.Close();
@@ -1695,7 +1700,7 @@ namespace FireWallet
{
AddLog("NPM is not installed");
AddLog(outputInstalled);
NotifyForm notifyForm = new NotifyForm("NPM is not installed\nPlease install it to install HSD dependencies");
NotifyForm notifyForm = new NotifyForm("NPM is not installed\nPlease install it to install HSD dependencies","Install", "https://docs.npmjs.com/downloading-and-installing-node-js-and-npm");
notifyForm.ShowDialog();
notifyForm.Dispose();
this.Close();
@@ -1881,6 +1886,33 @@ namespace FireWallet
int i = 0;
int renewable = 0;
panelDomainList.Controls.Clear();
// Sort the domains
switch (comboBoxDomainSort.Text)
{
case "Default":
break;
case "Alphabetical":
names = new JArray(names.OrderBy(obj => (string)obj["name"]));
break;
case "Expiring":
names = new JArray(names.OrderBy(obj =>
{
JToken daysUntilExpireToken = obj["stats"]?["daysUntilExpire"];
return (int)(daysUntilExpireToken ?? int.MaxValue);
}));
break;
case "Value":
// Sort by most valuable first
names = new JArray(names.OrderByDescending(obj =>
{
JToken valueToken = obj?["value"];
return (int)(valueToken ?? 0);
}));
break;
}
foreach (JObject name in names)
{
Domains[i] = name["name"].ToString();
@@ -2002,6 +2034,43 @@ namespace FireWallet
domainSearch = Regex.Replace(textBoxDomainSearch.Text, "[^a-zA-Z0-9-_]", "");
textBoxDomainSearch.Text = domainSearch;
}
private void export_Click(object sender, EventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Filter = "CSV file (*.csv)|*.csv";
saveFileDialog.Title = "Export";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
StreamWriter sw = new StreamWriter(saveFileDialog.FileName);
foreach (string domain in DomainsRenewable)
{
if (domain == null) break;
sw.WriteLine(domain);
}
sw.Dispose();
}
}
private void buttonRenewAll_Click(object sender, EventArgs e)
{
if (DomainsRenewable == null)
{
NotifyForm notifyForm = new NotifyForm("No renewable domains found");
notifyForm.ShowDialog();
notifyForm.Dispose();
return;
}
foreach (string domain in DomainsRenewable)
{
if (domain == null) break;
AddBatch(domain, "RENEW");
}
}
private void comboBoxDomainSort_DropDownClosed(object sender, EventArgs e)
{
UpdateDomains();
}
#endregion
#region Batching
@@ -2108,6 +2177,8 @@ namespace FireWallet
try
{
AddLog("Decrypting seed...");
AddLog(resp.ToString());
string iv = resp["iv"].ToString();
string ciphertext = resp["ciphertext"].ToString();
string tmpn = resp["n"].ToString();
@@ -2116,9 +2187,26 @@ namespace FireWallet
int n = int.Parse(tmpn);
int p = int.Parse(tmpp);
int r = int.Parse(tmpr);
int iterations = n;
byte[] decripted = await Decrypt_Seed(algorithm, ciphertext, iv, n,r,p);
// This is returning garbled text
AddLog("Seed decrypted");
string phrase = Encoding.UTF8.GetString(decripted);
AddLog("Your seed phrase is:\n" + phrase);
phrase = Encoding.ASCII.GetString(decripted);
AddLog("Your seed phrase is:\n" + phrase);
phrase = Encoding.Unicode.GetString(decripted);
AddLog("Your seed phrase is:\n" + phrase);
}
catch (Exception ex)
@@ -2133,7 +2221,71 @@ namespace FireWallet
}
}
private async Task<byte[]> Decrypt_Seed(string algorithm, string ciphertext, string iv, int n,int r, int p)
{
byte[] salt = Encoding.ASCII.GetBytes("hsd");
using (AesManaged aes = new AesManaged())
{
aes.Key = DeriveKey(algorithm, password, salt, n, r, p);
aes.IV = HexStringToByteArray(iv);
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
byte[] cipher = HexStringToByteArray(ciphertext);
if (cipher.Length % 16 != 0)
{
AddLog("Invalid cipher length");
return null;
}
using (ICryptoTransform decryptor = aes.CreateDecryptor())
{
byte[] decrypted = decryptor.TransformFinalBlock(cipher, 0, cipher.Length);
return decrypted;
}
}
}
static byte[] HexStringToByteArray(string hex)
{
int numberChars = hex.Length / 2;
byte[] bytes = new byte[numberChars];
for (int i = 0; i < numberChars; i++)
{
bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
}
return bytes;
}
byte[] DeriveKey(string algorithm, string passphrase, byte[] salt, int n, int r, int p)
{
byte[] passwordBytes = Encoding.UTF8.GetBytes(passphrase);
switch (algorithm)
{
case "pbkdf2":
return Pbkdf2DeriveKey(passwordBytes, salt, n, 32);
case "scrypt":
return ScryptDeriveKey(passwordBytes, salt, n, r, p, 32);
default:
throw new Exception($"Unknown algorithm: {algorithm}.");
}
}
static byte[] Pbkdf2DeriveKey(byte[] password, byte[] salt, int iterations, int derivedKeyLength)
{
using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations))
{
return pbkdf2.GetBytes(derivedKeyLength);
}
}
static byte[] ScryptDeriveKey(byte[] password, byte[] salt, int costParameterN, int costParameterR, int costParameterP, int derivedKeyLength)
{
using (var rfc2898 = new Rfc2898DeriveBytes(password, salt, costParameterN, HashAlgorithmName.SHA256))
{
return rfc2898.GetBytes(derivedKeyLength);
}
}
private async void Rescan_Click(object sender, EventArgs e)
{
string content = "{\"height\": 0}";
@@ -2149,39 +2301,7 @@ namespace FireWallet
}
#endregion
private void export_Click(object sender, EventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Filter = "CSV file (*.csv)|*.csv";
saveFileDialog.Title = "Export";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
StreamWriter sw = new StreamWriter(saveFileDialog.FileName);
foreach (string domain in DomainsRenewable)
{
if (domain == null) break;
sw.WriteLine(domain);
}
sw.Dispose();
}
}
private void buttonRenewAll_Click(object sender, EventArgs e)
{
if (DomainsRenewable == null)
{
NotifyForm notifyForm = new NotifyForm("No renewable domains found");
notifyForm.ShowDialog();
notifyForm.Dispose();
return;
}
foreach (string domain in DomainsRenewable)
{
if (domain == null) break;
AddBatch(domain, "RENEW");
}
}
#region Help Menu
private void githubToolStripMenuItem_Click(object sender, EventArgs e)
{
// Open the GitHub page
@@ -2212,5 +2332,6 @@ namespace FireWallet
};
Process.Start(psi);
}
#endregion
}
}

View File

@@ -224,15 +224,15 @@
{
"Name" = "8:Microsoft Visual Studio"
"ProductName" = "8:FireWallet"
"ProductCode" = "8:{007B0A5E-57B9-4DB7-AABE-5A3631A89BEB}"
"PackageCode" = "8:{546D4209-3E58-4144-A9DC-E659A2482DD4}"
"ProductCode" = "8:{460D8F86-4FE9-4547-9B17-7E01ACBF9194}"
"PackageCode" = "8:{A6678F97-9CE8-4005-82AC-AB2D09A9DA27}"
"UpgradeCode" = "8:{0C86F725-6B01-4173-AA05-3F0EDF481362}"
"AspNetVersion" = "8:"
"RestartWWWService" = "11:FALSE"
"RemovePreviousVersions" = "11:TRUE"
"DetectNewerInstalledVersion" = "11:TRUE"
"InstallAllUsers" = "11:FALSE"
"ProductVersion" = "8:3.0"
"ProductVersion" = "8:3.1"
"Manufacturer" = "8:Nathan.Woodburn/"
"ARPHELPTELEPHONE" = "8:"
"ARPHELPLINK" = "8:https://l.woodburn.au/discord"

View File

@@ -2,6 +2,14 @@
Experimental wallet for Handshake chain
## Installation
### Dependencies
You will need .net desktop installed. You can download it from [here](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.18-windows-x64-installer).
You will also need Node, NPM, and git installed if you want to use the internal HSD or Ledger wallets.
[Git](https://git-scm.com/downloads)
[Node](https://nodejs.org/en/download/)
[NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
### From Releases
You can install the latest release from [here](https://github.com/Nathanwoodburn/FireWallet/releases/).
@@ -60,6 +68,9 @@ You can change the number of transactions shown in the `portfolio-tx:` settings.
<br><br>
## Sending HNS
![Send](assets/send_hns.png)
This page lets you send HNS to Handshake addresses or domains using [HIP-02](https://github.com/handshake-org/HIPs/blob/master/HIP-0002.md).
To use HIP-02 you need to have HSD resolver (or any HNS compatible DNS resolver) listening on port 5350 (default HSD port).
To enter a domain to use HIP-02 you need to prefix the domain with `@` (eg. `@nathan.woodburn`).
## Receiving HNS or Domains
The receive page shows your current HNS address.
@@ -104,6 +115,8 @@ The "CANCEL" transaction type is used to cancel an transfer.
At the momemt "UPDATE" or coin only transactions are not supported.
Please not that the import syntax for BIDs is BID,LOCKUP where LOCKUP is (BID+BLIND)
![Batch Import](assets/batch_import.png)
## Exporting