11 Commits

6 changed files with 390 additions and 56 deletions

View File

@@ -1,5 +1,6 @@
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Security.Principal;
@@ -484,9 +485,23 @@ namespace FireWallet
{
AddLog("Error: ");
AddLog(jObject["error"].ToString());
NotifyForm notifyForm = new NotifyForm("Error: \n" + jObject["error"].ToString());
notifyForm.ShowDialog();
notifyForm.Dispose();
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;
}
@@ -621,7 +636,7 @@ namespace FireWallet
private void buttonImport_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "CSV File|*.csv|TXT File|*.txt";
openFileDialog.Filter = "All Files|*.*";
openFileDialog.Title = "Open Batch";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{

View File

@@ -771,9 +771,6 @@ namespace FireWallet
}
else if (state == "REVEAL")
{
decimal bid = Convert.ToDecimal(textBoxBid.Text);
decimal blind = Convert.ToDecimal(textBoxBlind.Text);
decimal lockup = bid + blind;
string content = "{\"method\": \"sendreveal\", \"params\": [\"" + domain + "\"]}";
string response = await APIPost("", true, content);

View File

@@ -59,6 +59,8 @@ namespace FireWallet
buttonNavSend = new Button();
buttonNavPortfolio = new Button();
panelPortfolio = new Panel();
buttonRenewAll = new Button();
buttonRevealAll = new Button();
groupBoxTransactions = new GroupBox();
groupBoxinfo = new GroupBox();
labelPendingCount = new Label();
@@ -87,6 +89,7 @@ namespace FireWallet
textBoxReceiveAddress = new TextBox();
labelReceive1 = new Label();
panelDomains = new Panel();
buttonExportDomains = new Button();
groupBoxDomains = new GroupBox();
panelDomainList = new Panel();
labelDomainSearch = new Label();
@@ -174,6 +177,7 @@ namespace FireWallet
toolStripStatusLabelLedger.Name = "toolStripStatusLabelLedger";
toolStripStatusLabelLedger.Size = new Size(71, 17);
toolStripStatusLabelLedger.Text = "Cold Wallet:";
toolStripStatusLabelLedger.Visible = false;
//
// toolStripSplitButtonlogout
//
@@ -386,15 +390,40 @@ namespace FireWallet
//
// panelPortfolio
//
panelPortfolio.Controls.Add(buttonRevealAll);
panelPortfolio.Controls.Add(groupBoxTransactions);
panelPortfolio.Controls.Add(groupBoxinfo);
panelPortfolio.Controls.Add(groupBoxbalance);
panelPortfolio.Location = new Point(1085, 47);
panelPortfolio.Location = new Point(1065, 80);
panelPortfolio.Name = "panelPortfolio";
panelPortfolio.Size = new Size(956, 538);
panelPortfolio.TabIndex = 7;
panelPortfolio.Visible = false;
//
// buttonRenewAll
//
buttonRenewAll.FlatStyle = FlatStyle.Flat;
buttonRenewAll.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point);
buttonRenewAll.Location = new Point(813, 9);
buttonRenewAll.Name = "buttonRenewAll";
buttonRenewAll.Size = new Size(89, 32);
buttonRenewAll.TabIndex = 10;
buttonRenewAll.Text = "Renew All";
buttonRenewAll.UseVisualStyleBackColor = true;
buttonRenewAll.Click += buttonRenewAll_Click;
//
// buttonRevealAll
//
buttonRevealAll.FlatStyle = FlatStyle.Flat;
buttonRevealAll.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point);
buttonRevealAll.Location = new Point(537, 12);
buttonRevealAll.Name = "buttonRevealAll";
buttonRevealAll.Size = new Size(89, 44);
buttonRevealAll.TabIndex = 9;
buttonRevealAll.Text = "Reveal All";
buttonRevealAll.UseVisualStyleBackColor = true;
buttonRevealAll.Click += buttonRevealAll_Click;
//
// groupBoxTransactions
//
groupBoxTransactions.Dock = DockStyle.Bottom;
@@ -632,7 +661,7 @@ namespace FireWallet
panelRecieve.Controls.Add(labelReceive2);
panelRecieve.Controls.Add(textBoxReceiveAddress);
panelRecieve.Controls.Add(labelReceive1);
panelRecieve.Location = new Point(117, 34);
panelRecieve.Location = new Point(1140, 24);
panelRecieve.Name = "panelRecieve";
panelRecieve.Size = new Size(995, 523);
panelRecieve.TabIndex = 17;
@@ -692,15 +721,29 @@ namespace FireWallet
//
// panelDomains
//
panelDomains.Controls.Add(buttonRenewAll);
panelDomains.Controls.Add(buttonExportDomains);
panelDomains.Controls.Add(groupBoxDomains);
panelDomains.Controls.Add(labelDomainSearch);
panelDomains.Controls.Add(textBoxDomainSearch);
panelDomains.Location = new Point(1129, 22);
panelDomains.Location = new Point(120, 48);
panelDomains.Name = "panelDomains";
panelDomains.Size = new Size(920, 536);
panelDomains.TabIndex = 18;
panelDomains.Visible = false;
//
// buttonExportDomains
//
buttonExportDomains.FlatStyle = FlatStyle.Flat;
buttonExportDomains.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point);
buttonExportDomains.Location = new Point(351, 9);
buttonExportDomains.Name = "buttonExportDomains";
buttonExportDomains.Size = new Size(102, 32);
buttonExportDomains.TabIndex = 3;
buttonExportDomains.Text = "Export";
buttonExportDomains.UseVisualStyleBackColor = true;
buttonExportDomains.Click += export_Click;
//
// groupBoxDomains
//
groupBoxDomains.Controls.Add(panelDomainList);
@@ -749,7 +792,7 @@ namespace FireWallet
panelSettings.Controls.Add(buttonSettingsSave);
panelSettings.Controls.Add(groupBoxSettingsExplorer);
panelSettings.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point);
panelSettings.Location = new Point(121, 21);
panelSettings.Location = new Point(1065, 211);
panelSettings.Name = "panelSettings";
panelSettings.Size = new Size(930, 550);
panelSettings.TabIndex = 19;
@@ -965,13 +1008,13 @@ namespace FireWallet
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1152, 575);
Controls.Add(panelaccount);
Controls.Add(panelPortfolio);
Controls.Add(panelRecieve);
Controls.Add(panelSettings);
Controls.Add(panelDomains);
Controls.Add(panelSend);
Controls.Add(panelPortfolio);
Controls.Add(panelNav);
Controls.Add(statusStripmain);
Controls.Add(panelSettings);
Icon = (Icon)resources.GetObject("$this.Icon");
Name = "MainForm";
Opacity = 0D;
@@ -1091,5 +1134,8 @@ namespace FireWallet
private Button buttonSettingsRescan;
private ToolStripStatusLabel toolStripStatusLabelLedger;
private Button buttonAddressVerify;
private Button buttonRevealAll;
private Button buttonExportDomains;
private Button buttonRenewAll;
}
}

View File

@@ -160,10 +160,10 @@ namespace FireWallet
toolStripStatusLabelNetwork.Text = "Network: Mainnet";
break;
case 1:
toolStripStatusLabelNetwork.Text = "Network: Regtest";
toolStripStatusLabelNetwork.Text = "Network: Regtest (Not Fully Tested)";
break;
case 2:
toolStripStatusLabelNetwork.Text = "Network: Testnet";
toolStripStatusLabelNetwork.Text = "Network: Testnet (Not Implemented)";
break;
}
@@ -218,7 +218,8 @@ namespace FireWallet
hsdProcess.Start();
// Wait for HSD to start
await Task.Delay(2000);
} catch (Exception ex)
}
catch (Exception ex)
{
AddLog("HSD Failed to start");
AddLog(ex.Message);
@@ -556,9 +557,18 @@ namespace FireWallet
path = "wallet/" + account + "";
APIresponse = await APIGet(path, true);
JObject jObject = JObject.Parse(APIresponse);
if (jObject["watchOnly"].ToString() == "True") watchOnly = true;
else watchOnly = false;
toolStripStatusLabelLedger.Text = "Cold Wallet: " + watchOnly.ToString();
if (jObject["watchOnly"].ToString() == "True")
{
watchOnly = true;
toolStripStatusLabelLedger.Text = "Cold Wallet";
toolStripStatusLabelLedger.Visible = true;
}
else
{
watchOnly = false;
toolStripStatusLabelLedger.Visible = false;
}
if (watchOnly)
{
@@ -573,17 +583,36 @@ namespace FireWallet
private async void LoginClick(object sender, EventArgs e)
{
account = comboBoxaccount.Text;
password = textBoxaccountpassword.Text;
bool loggedin = await Login();
if (loggedin)
// If the node isn't connected show a message
try
{
toolStripStatusLabelaccount.Text = "Account: " + account;
textBoxaccountpassword.Text = "";
panelaccount.Visible = false;
toolStripSplitButtonlogout.Visible = true;
panelNav.Visible = true;
buttonNavPortfolio.PerformClick();
if (await APIGet("", false) == "Error")
{
NotifyForm notifyForm = new NotifyForm("Node not connected");
notifyForm.ShowDialog();
notifyForm.Dispose();
return;
}
account = comboBoxaccount.Text;
password = textBoxaccountpassword.Text;
bool loggedin = await Login();
if (loggedin)
{
toolStripStatusLabelaccount.Text = "Account: " + account;
textBoxaccountpassword.Text = "";
panelaccount.Visible = false;
toolStripSplitButtonlogout.Visible = true;
panelNav.Visible = true;
buttonNavPortfolio.PerformClick();
}
}
catch (Exception ex)
{
NotifyForm notifyForm = new NotifyForm("Node not connected\n" + ex.Message);
notifyForm.ShowDialog();
notifyForm.Dispose();
return;
}
}
@@ -604,6 +633,7 @@ namespace FireWallet
{
password = ""; // Clear password from memory as soon as possible
toolStripSplitButtonlogout.Visible = false;
toolStripStatusLabelLedger.Visible = false;
string path = "wallet/" + account + "/lock";
string content = "";
string APIresponse = await APIPost(path, true, content);
@@ -675,6 +705,11 @@ namespace FireWallet
/// <returns></returns>
public async Task<string> APIPost(string path, bool wallet, string content)
{
if (content == "{\"passphrase\": \"\" ,\"timeout\": 60}")
{
// For some reason, the API doesn't like an empty password, so we'll just use a default one
content = "{\"passphrase\": \"password\" ,\"timeout\": 60}";
}
string key = nodeSettings["Key"];
string ip = nodeSettings["IP"];
string port = "1203";
@@ -767,7 +802,11 @@ namespace FireWallet
decimal progress = Convert.ToDecimal(chain["progress"].ToString());
labelSyncPercent.Text = "Sync: " + decimal.Round(progress * 100, 2) + "%";
// Exit if set to 0 TXs
if (userSettings.ContainsKey("portfolio-tx"))
{
if (userSettings["portfolio-tx"] == "0") return;
}
// Get Unconfirmed TX
@@ -852,19 +891,134 @@ namespace FireWallet
};
tmpPanel.Controls.Add(labelHash);
// Count inputs and outputs
// Count inputs
JArray inputs = JArray.Parse(tx["inputs"].ToString());
JArray outputs = JArray.Parse(tx["outputs"].ToString());
int inputCount = inputs.Count;
int outputCount = outputs.Count;
Label labelInputOutput = new Label()
int inputCount = inputs.Count;
Label labelInput = new Label()
{
Text = "Inputs: " + inputCount + " Outputs: " + outputCount,
Text = "Inputs: " + inputCount,
AutoSize = true,
Location = new Point(300, 20)
Location = new Point(200, 5)
};
tmpPanel.Controls.Add(labelInputOutput);
tmpPanel.Controls.Add(labelInput);
AddLog(tx.ToString());
// Get types of outputs
JArray outputs = JArray.Parse(tx["outputs"].ToString());
int outputCount = outputs.Count;
string outputMessage = "";
string tmpOutputMessage = "";
int SendingCount = 0;
decimal SendingAmount = 0;
int OpenCount = 0;
int BidCount = 0;
int RevealCount = 0;
int RedeemCount = 0;
int RegisterCount = 0;
int ClaimCount = 0;
int UpdateCount = 0;
int RenewCount = 0;
int TransferCount = 0;
int FinalizeCount = 0;
int RevokeCount = 0;
foreach (JObject TXOutput in outputs)
{
JObject covenant = JObject.Parse(TXOutput["covenant"].ToString());
string type = covenant["action"].ToString();
if (type == "NONE")
{
if (TXOutput["path"].ToString() != "")
{
if (TXOutput["path"]["change"].ToString().ToLower() == "false")
{
SendingCount++;
SendingAmount += Convert.ToInt64(TXOutput["value"].ToString()) / 1000000;
}
else
{
AddLog(TXOutput.ToString());
}
}
else
{
SendingCount++;
SendingAmount += Convert.ToInt64(TXOutput["value"].ToString()) / 1000000;
}
}
if (type == "OPEN") OpenCount++;
if (type == "BID") BidCount++;
if (type == "REVEAL") RevealCount++;
if (type == "REDEEM") RedeemCount++;
if (type == "REGISTER") RegisterCount++;
if (type == "CLAIM") ClaimCount++;
if (type == "UPDATE") UpdateCount++;
if (type == "RENEW") RenewCount++;
if (type == "TRANSFER") TransferCount++;
if (type == "FINALIZE") FinalizeCount++;
if (type == "REVOKE") RevokeCount++;
}
if (SendingCount > 0)
{
if (SendingCount == 1)
{
tmpOutputMessage += "Sent: " + SendingAmount + " HNS\n";
}
else
{
tmpOutputMessage += "Sent: " + SendingAmount + " HNS (to " + SendingCount + " addresses)\n";
}
}
if (OpenCount > 0) tmpOutputMessage += "Open: " + OpenCount + "\n";
if (BidCount > 0) tmpOutputMessage += "Bid: " + BidCount + "\n";
if (RevealCount > 0) tmpOutputMessage += "Reveal: " + RevealCount + "\n";
if (RedeemCount > 0) tmpOutputMessage += "Redeem: " + RedeemCount + "\n";
if (RegisterCount > 0) tmpOutputMessage += "Register: " + RegisterCount + "\n";
if (ClaimCount > 0) tmpOutputMessage += "Claim: " + ClaimCount + "\n";
if (UpdateCount > 0) tmpOutputMessage += "Update: " + UpdateCount + "\n";
if (RenewCount > 0) tmpOutputMessage += "Renew: " + RenewCount + "\n";
if (TransferCount > 0) tmpOutputMessage += "Transfer: " + TransferCount + "\n";
if (FinalizeCount > 0) tmpOutputMessage += "Finalize: " + FinalizeCount + "\n";
if (RevokeCount > 0) tmpOutputMessage += "Revoke: " + RevokeCount + "\n";
// Split the string into lines to display like this
// Line 1 Line 3 Line 5
// Line 2 Line 4 Line 6
string[] lines = tmpOutputMessage.Split('\n');
int count = lines.Length;
for (int x = 0; x < count; x++)
{
if (x % 2 == 0)
{
outputMessage += lines[x] + " ";
}
else
{
outputMessage += lines[x] + "\n";
}
}
Label labelOutputs = new Label()
{
Text = outputMessage,
AutoSize = true,
Location = new Point(400, 5)
};
tmpPanel.Controls.Add(labelOutputs);
tmpControls[i] = tmpPanel;
@@ -923,7 +1077,7 @@ namespace FireWallet
await UpdateBalance();
GetTXHistory();
labelBalance.Text = "Available: " + balance.ToString() + " HNS";
labelLocked.Text = "Locked: " + balanceLocked.ToString() + " HNS";
labelLocked.Text = "Locked: " + balanceLocked.ToString() + " HNS*";
labelBalanceTotal.Text = "Total: " + (balance + balanceLocked).ToString() + " HNS";
if (theme.ContainsKey("selected-bg") && theme.ContainsKey("selected-fg"))
{
@@ -1415,12 +1569,15 @@ namespace FireWallet
#endregion
#region Domains
public string[] Domains { get; set; }
public string[] DomainsRenewable { get; set; }
private async void UpdateDomains()
{
string response = await APIGet("wallet/" + account + "/name?own=true", true);
JArray names = JArray.Parse(response);
Domains = new string[names.Count];
DomainsRenewable = new string[names.Count];
int i = 0;
int renewable = 0;
panelDomainList.Controls.Clear();
foreach (JObject name in names)
{
@@ -1445,21 +1602,77 @@ namespace FireWallet
domainName.Left = 5;
domainName.AutoSize = true;
JObject stats = JObject.Parse(name["stats"].ToString());
Label expiry = new Label();
expiry.Text = "Expires: " + stats["daysUntilExpire"].ToString() + " days";
expiry.Top = 5;
expiry.AutoSize = true;
expiry.Left = domainTMP.Width - expiry.Width - 100;
domainTMP.Controls.Add(domainName);
domainTMP.Controls.Add(expiry);
if (!name.ContainsKey("stats")) {
AddLog("Domain " + Domains[i] + " does not have stats");
continue;
}
Label expiry = new Label();
JObject stats = JObject.Parse(name["stats"].ToString());
if (stats.ContainsKey("daysUntilExpire"))
{
expiry.Text = "Expires: " + stats["daysUntilExpire"].ToString() + " days";
expiry.Top = 5;
expiry.AutoSize = true;
expiry.Left = domainTMP.Width - expiry.Width - 100;
domainTMP.Controls.Add(expiry);
// Add to domains renewable
DomainsRenewable[renewable] = Domains[i];
renewable++;
}
else
{
expiry.Text = "Expires: Not Registered yet";
expiry.Top = 5;
expiry.AutoSize = true;
expiry.Left = domainTMP.Width - expiry.Width - 100;
domainTMP.Controls.Add(expiry);
}
panelDomainList.Controls.Add(domainTMP);
i++;
}
}
private async void buttonRevealAll_Click(object sender, EventArgs e)
{
string content = "{\"method\": \"sendreveal\"}";
string response = await APIPost("", true, content);
AddLog(response);
if (response == "Error")
{
AddLog("Error sending reveal");
NotifyForm notifyForm = new NotifyForm("Error sending reveal");
notifyForm.ShowDialog();
notifyForm.Dispose();
return;
}
JObject resp = JObject.Parse(response);
if (resp["error"] != null)
{
AddLog("Error sending reveal");
AddLog(resp["error"].ToString());
JObject error = JObject.Parse(resp["error"].ToString());
NotifyForm notifyForm = new NotifyForm("Error sending reveal\n" + error["message"].ToString());
notifyForm.ShowDialog();
notifyForm.Dispose();
return;
}
if (resp.ContainsKey("result"))
{
JObject result = JObject.Parse(resp["result"].ToString());
string hash = result["hash"].ToString();
NotifyForm notifyForm = new NotifyForm("Reveal sent\n" + hash, "Explorer", userSettings["explorer-tx"] + hash);
notifyForm.ShowDialog();
notifyForm.Dispose();
}
}
private void textBoxDomainSearch_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyValue == 13)
@@ -1625,11 +1838,37 @@ 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");
}
}
}
}

View File

@@ -49,6 +49,22 @@
"PrivateKeyFile" = "8:"
"TimeStampServer" = "8:"
"InstallerBootstrapper" = "3:2"
"BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}"
{
"Enabled" = "11:TRUE"
"PromptEnabled" = "11:TRUE"
"PrerequisitesLocation" = "2:1"
"Url" = "8:"
"ComponentsUrl" = "8:"
"Items"
{
"{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2"
{
"Name" = "8:Microsoft .NET Framework 4.7.2 (x86 and x64)"
"ProductCode" = "8:.NETFramework,Version=v4.7.2"
}
}
}
}
"Release"
{
@@ -65,6 +81,22 @@
"PrivateKeyFile" = "8:"
"TimeStampServer" = "8:"
"InstallerBootstrapper" = "3:2"
"BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}"
{
"Enabled" = "11:TRUE"
"PromptEnabled" = "11:TRUE"
"PrerequisitesLocation" = "2:1"
"Url" = "8:"
"ComponentsUrl" = "8:"
"Items"
{
"{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.7.2"
{
"Name" = "8:Microsoft .NET Framework 4.7.2 (x86 and x64)"
"ProductCode" = "8:.NETFramework,Version=v4.7.2"
}
}
}
}
}
"Deployable"
@@ -192,15 +224,15 @@
{
"Name" = "8:Microsoft Visual Studio"
"ProductName" = "8:FireWallet"
"ProductCode" = "8:{F0940C69-189F-4613-8D7C-C3C6EA9CDDA4}"
"PackageCode" = "8:{B433A157-C12E-41BA-B25E-D42F91F186DC}"
"ProductCode" = "8:{EEABCF5F-54F1-4C71-A82F-FA131D9CC8C1}"
"PackageCode" = "8:{7B12D25F-C460-4627-B920-932CD9ACCF9E}"
"UpgradeCode" = "8:{0C86F725-6B01-4173-AA05-3F0EDF481362}"
"AspNetVersion" = "8:"
"RestartWWWService" = "11:FALSE"
"RemovePreviousVersions" = "11:TRUE"
"DetectNewerInstalledVersion" = "11:TRUE"
"InstallAllUsers" = "11:FALSE"
"ProductVersion" = "8:2.0"
"ProductVersion" = "8:2.2"
"Manufacturer" = "8:Nathan.Woodburn/"
"ARPHELPTELEPHONE" = "8:"
"ARPHELPLINK" = "8:https://l.woodburn.au/discord"

View File

@@ -4,6 +4,11 @@ Experimental wallet for Handshake chain
## Installation
### From Releases
You can install the latest release from [here](https://github.com/Nathanwoodburn/FireWallet/releases/).
Here are some videos showing how to install and use FireWallet:
Install with a running Bob wallet: https://cloud.woodburn.au/s/pS8e2oQDidrMJWx
Install with internal HSD: https://cloud.woodburn.au/s/trwd8TyxbDfqaxF (You NEED to have Node, NPM, and git installed to use internal HSD)
### Build from source
You can build from source by cloning this repo.
You will need Visual Studio (recommend a recent version) and .net desktop development tools installed.