From 8a8959c0f57770fab19f92231ee5292c2d0e5de4 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Sat, 21 Dec 2019 11:59:31 +0000 Subject: [PATCH 1/2] Restyled by clang-format --- .../zelcashui/ZelCashZelNodeDialog.java | 1582 +++++----- .../cabecinha84/zelcashui/ZelNodesPanel.java | 1377 ++++---- .../zelcashui/ZelcashRescanDialog.java | 363 +-- .../com/vaklinov/zcashui/SendCashPanel.java | 1960 ++++++------ .../vaklinov/zcashui/WalletOperations.java | 1981 ++++++------ .../vaklinov/zcashui/ZCashClientCaller.java | 2812 ++++++++--------- src/java/com/vaklinov/zcashui/ZCashUI.java | 2086 ++++++------ 7 files changed, 6154 insertions(+), 6007 deletions(-) diff --git a/src/java/com/cabecinha84/zelcashui/ZelCashZelNodeDialog.java b/src/java/com/cabecinha84/zelcashui/ZelCashZelNodeDialog.java index 697faeec..182d1cf2 100644 --- a/src/java/com/cabecinha84/zelcashui/ZelCashZelNodeDialog.java +++ b/src/java/com/cabecinha84/zelcashui/ZelCashZelNodeDialog.java @@ -1,5 +1,16 @@ package com.cabecinha84.zelcashui; +import com.eclipsesource.json.JsonArray; +import com.eclipsesource.json.JsonObject; +import com.vaklinov.zcashui.LabelStorage; +import com.vaklinov.zcashui.LanguageUtil; +import com.vaklinov.zcashui.Log; +import com.vaklinov.zcashui.OSUtil; +import com.vaklinov.zcashui.ZCashClientCaller; +import com.vaklinov.zcashui.ZCashClientCaller.WalletCallException; +import com.vaklinov.zcashui.ZCashInstallationObserver; +import com.vaklinov.zcashui.ZCashInstallationObserver.DAEMON_STATUS; +import com.vaklinov.zcashui.ZCashUI; import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.Dimension; @@ -20,7 +31,6 @@ import java.util.ArrayList; import java.util.Properties; import java.util.Scanner; - import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JComponent; @@ -28,757 +38,841 @@ import javax.swing.JOptionPane; import javax.swing.border.EtchedBorder; -import com.eclipsesource.json.JsonArray; -import com.eclipsesource.json.JsonObject; -import com.vaklinov.zcashui.LabelStorage; -import com.vaklinov.zcashui.LanguageUtil; -import com.vaklinov.zcashui.Log; -import com.vaklinov.zcashui.OSUtil; -import com.vaklinov.zcashui.ZCashClientCaller; -import com.vaklinov.zcashui.ZCashClientCaller.WalletCallException; -import com.vaklinov.zcashui.ZCashInstallationObserver.DAEMON_STATUS; -import com.vaklinov.zcashui.ZCashInstallationObserver; -import com.vaklinov.zcashui.ZCashUI; - /** * Dialog showing the information about a user's identity */ -public class ZelCashZelNodeDialog - extends ZelCashJDialog -{ - protected ZelCashJTextField zelNodeName; - protected ZelCashJTextField zelNodeIP; - protected ZelCashJTextField zelNodeKey; - private ZelCashJComboBox zelNodeOutput; - protected ZelCashJTextField zelNodeOutputText; - protected ZelCashJTextField zelNodeAmount; - protected ZelCashJTextField zelNodeAddress; - protected ZelCashJTextField zelNodeOutputTxid; - protected ZelCashJTextField zelNodeOutputindex; - - private ZCashUI parentFrame; - - private static ZelCashJButton saveButton; - - private ZCashClientCaller clientCaller; - private static ZCashInstallationObserver installationObserver; - private String aliastoEdit; - protected LabelStorage labelStorage; - - final LanguageUtil langUtil = LanguageUtil.instance(); - - public ZelCashZelNodeDialog(ZCashUI parent, final ZCashClientCaller clientCaller, final ZCashInstallationObserver installationObserver, String aliasToEdit, final LabelStorage labelStorage) - throws IOException - { - parentFrame = parent; - this.clientCaller = clientCaller; - this.aliastoEdit = aliasToEdit; - this.installationObserver = installationObserver; - this.labelStorage = labelStorage; - - this.setTitle(langUtil.getString("dialog.zelcashnewzelnode.title")); - this.setSize(900, 650); - this.setLocation(100, 100); - this.setLocationRelativeTo(parent); - this.setModal(true); - this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); - - ZelCashJPanel tempPanel = new ZelCashJPanel(new BorderLayout(0, 0)); - tempPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); - ZelCashJLabel infoLabel = new ZelCashJLabel(langUtil.getString("dialog.zelcashnewzelnode.info")); - tempPanel.add(infoLabel, BorderLayout.CENTER); - this.getContentPane().add(tempPanel, BorderLayout.NORTH); - - - ZelCashJPanel detailsPanel = new ZelCashJPanel(); - detailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.Y_AXIS)); - - addFormField(detailsPanel, langUtil.getString("dialog.zelcashnewzelnode.name"), zelNodeName = new ZelCashJTextField(50)); - addFormField(detailsPanel, langUtil.getString("dialog.zelcashnewzelnode.ip"), zelNodeIP = new ZelCashJTextField(50)); - addFormField(detailsPanel, langUtil.getString("dialog.zelcashnewzelnode.key"), zelNodeKey = new ZelCashJTextField(50)); - addFormField(detailsPanel, langUtil.getString("dialog.zelcashnewzelnode.output"), zelNodeOutput = new ZelCashJComboBox()); - addFormField(detailsPanel, langUtil.getString("dialog.zelcashnewzelnode.output.txid"), zelNodeOutputTxid = new ZelCashJTextField(50)); - addFormField(detailsPanel, langUtil.getString("dialog.zelcashnewzelnode.output.index"), zelNodeOutputindex = new ZelCashJTextField(50)); - addFormField(detailsPanel, langUtil.getString("dialog.zelcashnewzelnode.address"), zelNodeAddress = new ZelCashJTextField(50)); - addFormField(detailsPanel, langUtil.getString("dialog.zelcashnewzelnode.amount"), zelNodeAmount = new ZelCashJTextField(50)); - zelNodeName.setEditable(true); - zelNodeIP.setEditable(true); - zelNodeIP.setText(":16125"); - zelNodeKey.setEditable(false); - zelNodeOutput.setEnabled(false); - zelNodeAmount.setEditable(false); - zelNodeAddress.setEditable(false); - zelNodeOutputTxid.setEditable(false); - zelNodeOutputindex.setEditable(false); - - getZelNodeOutputs(); - if(this.aliastoEdit != null) { - zelNodeName.setText(this.aliastoEdit); - - String blockchainDir = OSUtil.getBlockchainDirectory(); - File zelnodeConf = new File(blockchainDir + File.separator + "zelnode.conf"); - if (!zelnodeConf.exists()) - { - Log.info("Could not find file: {0} !", zelnodeConf.getAbsolutePath()); - } - else { - Log.info("File zelnode.conf found"); - BufferedReader br = new BufferedReader(new FileReader(zelnodeConf)); - String st; - String emptyLine; - while ((st = br.readLine()) != null) { - emptyLine = st.replaceAll(" ", "").replaceAll("(?m)^\\\\s*\\\\r?\\\\n|\\\\r?\\\\n\\\\s*(?!.*\\\\r?\\\\n)", ""); - if(st.startsWith("#") || emptyLine.equals("")) { - continue; - } - else { - String[] zelNodeInfo = st.split("\\s+"); - if(zelNodeInfo[0].equals(this.aliastoEdit)) { - zelNodeIP.setText(zelNodeInfo[1]); - zelNodeKey.setText(zelNodeInfo[2]); - zelNodeOutputTxid.setText(zelNodeInfo[3]); - zelNodeOutputindex.setText(zelNodeInfo[4]); - String output = zelNodeInfo[3] + " " + zelNodeInfo[4]; - zelNodeOutput.setSelectedItem(output); - try { - JsonObject txinfo = clientCaller.getTransactionInfo(zelNodeInfo[3]); - JsonArray details = txinfo.get("details").asArray(); - String vout; - String category; - String detailAmount="0"; - String address =""; - boolean addressFound = false; - for(int i=0; i< details.size(); ++i) { - JsonObject obj = details.get(i).asObject(); - vout = obj.get("vout").toString().replaceAll("[\n\r\"]", ""); - category = obj.get("category").toString().replaceAll("[\n\r\"]", ""); - if(vout.equals(zelNodeInfo[4]) && "send".equals(category)) { - detailAmount = obj.get("amount").toString().replaceAll("[\n\r\"]", "").substring(1); - address = obj.get("address").toString().replaceAll("[\n\r\"]", ""); - addressFound = true; - break; - } - } - if(!addressFound) { - for(int i=0; i< details.size(); ++i) { - JsonObject obj = details.get(i).asObject(); - vout = obj.get("vout").toString().replaceAll("[\n\r\"]", ""); - category = obj.get("category").toString().replaceAll("[\n\r\"]", ""); - - if(vout.equals(zelNodeInfo[4]) && "receive".equals(category)) { - detailAmount = obj.get("amount").toString().replaceAll("[\n\r\"]", ""); - address = obj.get("address").toString().replaceAll("[\n\r\"]", ""); - break; - } - } - } - if ((address != null) && (address.length() > 0)) - { - String label = this.labelStorage.getLabel(address); - if ((label != null) && (label.length() > 0)) - { - address = label + " - " + address; - } - } - - Float floatAmount=Float.parseFloat(detailAmount); - DecimalFormat df = new DecimalFormat("0.00"); - df.setMaximumFractionDigits(2); - zelNodeAmount.setText(df.format(floatAmount)); - zelNodeAddress.setText(address); - } catch (WalletCallException | IOException | InterruptedException e) { - Log.error("Error calling getRawTransactionDetails:"+e.getMessage()); - } - break; - } - } - } - } - } - else { - gelZelNodeKey(); - } - - - detailsPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); - this.getContentPane().add(detailsPanel, BorderLayout.CENTER); - - ZelCashJPanel closePanel = new ZelCashJPanel(); - closePanel.setLayout(new FlowLayout(FlowLayout.CENTER, 3, 3)); - ZelCashJButton closeButon = new ZelCashJButton(langUtil.getString("dialog.about.button.close.text")); - closePanel.add(closeButon); - saveButton = new ZelCashJButton(langUtil.getString("dialog.zelcashuiedit.save")); - closePanel.add(saveButton); - this.getContentPane().add(closePanel, BorderLayout.SOUTH); - - closeButon.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - ZelCashZelNodeDialog.this.parentFrame.repaint(); - ZelCashZelNodeDialog.this.setVisible(false); - ZelCashZelNodeDialog.this.dispose(); - } - }); - - saveButton.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - if("".equals(zelNodeName.getText().replaceAll(" ", "")) || - "".equals(zelNodeIP.getText().replaceAll(" ", "")) || - "".equals(zelNodeKey.getText().replaceAll(" ", "")) || - zelNodeOutput == null || - zelNodeOutput.getItemCount()==0 || - langUtil.getString("dialog.zelcashnewzelnode.select.output").equals(zelNodeOutput.getSelectedItem().toString()) - ) { - JOptionPane.showMessageDialog( - null, - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.missing"), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.missing.title"), - JOptionPane.INFORMATION_MESSAGE); - return; - } - if (!zelNodeName.getText().matches("\\w+")) - { - JOptionPane.showMessageDialog( - null, - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.alias.wrong"), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.error.adding.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - if (!zelNodeIP.getText().endsWith(":16125") && !zelNodeIP.getText().endsWith(":26125")) - { - JOptionPane.showMessageDialog( - null, - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.ip.wrong"), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.error.adding.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - String ip = zelNodeIP.getText().replaceAll(":16125", "").replaceAll(":26125", ""); - if(ip.isEmpty()) { - JOptionPane.showMessageDialog( - null, - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.ip.notset"), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.error.adding.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - else if(!ip.endsWith(".onion")) { - try { - Inet4Address address = (Inet4Address) Inet4Address.getByName(ip); - } - catch (Exception ex1) { - try { - Inet6Address address = (Inet6Address) Inet6Address.getByName(ip); - if(!ip.startsWith("[") || !ip.endsWith("]")) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("parsing.error.zelnodesconf.wrong.ip", ip), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.error.adding.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - } - catch (Exception ex) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("parsing.error.zelnodesconf.wrong.ip", ip), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.error.adding.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - } - } - - saveSettings(); - } - }); - - zelNodeOutput.addItemListener(new ItemListener() { - - @Override - public void itemStateChanged(ItemEvent event) { - if(event.getStateChange() == ItemEvent.SELECTED) { - Object source = event.getSource(); - if (source instanceof ZelCashJComboBox) { - ZelCashJComboBox cb = (ZelCashJComboBox)source; - int index = cb.getSelectedIndex(); - if(index!=0) { - zelNodeAmount.setText(langUtil.getString("zelnodespanel.zelnodes.button.loading")); - zelNodeAddress.setText(langUtil.getString("zelnodespanel.zelnodes.button.loading")); - zelNodeOutputindex.setText(langUtil.getString("zelnodespanel.zelnodes.button.loading")); - zelNodeOutputTxid.setText(langUtil.getString("zelnodespanel.zelnodes.button.loading")); - String[] outputinfo = cb.getSelectedItem().toString().split(" "); - try { - zelNodeOutputTxid.setText(outputinfo[0]); - zelNodeOutputindex.setText(outputinfo[1]); - JsonObject txinfo = clientCaller.getTransactionInfo(outputinfo[0]); - JsonArray details = txinfo.get("details").asArray(); - String vout; - String category; - String detailAmount="0"; - String address =""; - Log.debug("details array size:"+details.size()); - boolean addressFound = false; - for(int i=0; i< details.size(); ++i) { - JsonObject obj = details.get(i).asObject(); - vout = obj.get("vout").toString().replaceAll("[\n\r\"]", ""); - category = obj.get("category").toString().replaceAll("[\n\r\"]", ""); - - if(vout.equals(outputinfo[1]) && "send".equals(category)) { - detailAmount = obj.get("amount").toString().replaceAll("[\n\r\"]", "").substring(1); - address = obj.get("address").toString().replaceAll("[\n\r\"]", ""); - addressFound = true; - break; - } - } - if(!addressFound) { - for(int i=0; i< details.size(); ++i) { - JsonObject obj = details.get(i).asObject(); - vout = obj.get("vout").toString().replaceAll("[\n\r\"]", ""); - category = obj.get("category").toString().replaceAll("[\n\r\"]", ""); - - if(vout.equals(outputinfo[1]) && "receive".equals(category)) { - detailAmount = obj.get("amount").toString().replaceAll("[\n\r\"]", ""); - address = obj.get("address").toString().replaceAll("[\n\r\"]", ""); - break; - } - } - } - if ((address != null) && (address.length() > 0)) - { - String label = labelStorage.getLabel(address); - if ((label != null) && (label.length() > 0)) - { - Log.debug("found the address label:"+label); - address = label + " - " + address; - } - } - Float floatAmount=Float.parseFloat(detailAmount); - DecimalFormat df = new DecimalFormat("0.00"); - df.setMaximumFractionDigits(2); - zelNodeAmount.setText(df.format(floatAmount)); - zelNodeAddress.setText(address); - } catch (WalletCallException | IOException | InterruptedException e) { - Log.error("Error calling getRawTransactionDetails:"+e.getMessage()); - } - } - - } - } - - } - }); - - pack(); - } - - private void gelZelNodeKey() { - Log.info("gelZelNodeKey start"); - try { - zelNodeKey.setText(clientCaller.getZelNodeKey().replaceAll("[\n\r\"]", "")); - } catch (WalletCallException | IOException | InterruptedException e) { - Log.error("Error obtaining gelZelNodeKey. " + e.getMessage()); - } finally { - Log.info("gelZelNodeKey end"); - } - } - - private void getZelNodeOutputs() { - Log.info("getZelNodeOutputs start"); - int outputsCount=0; - try { - JsonArray ja = clientCaller.getZelNodeOutputs(); - outputsCount = ja.size(); - zelNodeOutput.removeAllItems(); - zelNodeOutput.addItem(this.langUtil.getString("dialog.zelcashnewzelnode.select.output")); - for (int i = 0; i < ja.size(); ++i) { - JsonObject obj = ja.get(i).asObject(); - String txhash = obj.get("txhash").toString().replaceAll("[\n\r\"]", ""); - String outputidx = obj.get("outputidx").toString().replaceAll("[\n\r\"]", ""); - zelNodeOutput.addItem(txhash + " " + outputidx); - } - zelNodeOutput.setEnabled(true); - } catch (WalletCallException | IOException | InterruptedException e1) { - Log.error("Error obtaining zelNodeOutputs. " + e1.getMessage()); - } finally { - Log.info("getZelNodeOutputs end - outputscount:" +outputsCount); - } - } - - private void addFormField(ZelCashJPanel detailsPanel, String name, JComponent field, ZelCashJButton button) - { - ZelCashJPanel tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 4, 2)); - ZelCashJLabel tempLabel = new ZelCashJLabel(name, JLabel.RIGHT); - // TODO: hard sizing of labels may not scale! - final int width = new ZelCashJLabel("Sender identification T address:").getPreferredSize().width + 10; - tempLabel.setPreferredSize(new Dimension(width, tempLabel.getPreferredSize().height)); - tempPanel.add(tempLabel); - tempPanel.add(field); - tempPanel.add(button); - detailsPanel.add(tempPanel); - } - - private void addFormField(ZelCashJPanel detailsPanel, String name, JComponent field) - { - ZelCashJPanel tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 4, 2)); - ZelCashJLabel tempLabel = new ZelCashJLabel(name, JLabel.RIGHT); - // TODO: hard sizing of labels may not scale! - final int width = new ZelCashJLabel("Sender identification T address:").getPreferredSize().width + 10; - tempLabel.setPreferredSize(new Dimension(width, tempLabel.getPreferredSize().height)); - tempPanel.add(tempLabel); - tempPanel.add(field); - detailsPanel.add(tempPanel); - } - - - - private void saveSettings() { - Log.info("Starting save zelnode"); - try { - removeEmptyLinesFromNodesConfigurationFile(); - if(this.aliastoEdit!=null) { - removeZelNode(this.aliastoEdit); - } - FileWriter fw = null; - String blockchainDir = OSUtil.getBlockchainDirectory(); - File zelnodeConf = new File(blockchainDir + File.separator + "zelnode.conf"); - if (!zelnodeConf.exists()) - { - Log.info("Could not find file: {0} !", zelnodeConf.getAbsolutePath()); - zelnodeConf.createNewFile(); - Log.info("File zelnodes.conf created"); - } - else { - Log.info("File zelnode.conf found"); - } - - if(this.aliastoEdit==null) { - //checking for duplications - Scanner scanner = new Scanner(zelnodeConf); - - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if(!line.startsWith("#")){ - String[] zelNodeInfo; - zelNodeInfo = line.split("\\s+"); - String alias = zelNodeInfo[0]; - String ip = zelNodeInfo[1]; - String zelnodekey= zelNodeInfo[2]; - String output = zelNodeInfo[3] + " " + zelNodeInfo[4]; - - if(output.equals(zelNodeOutput.getSelectedItem().toString())) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.output.duplicated"), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.error.adding.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - if(alias.toLowerCase().equals(zelNodeName.getText().toLowerCase().replaceAll(" ", "").replaceAll("[\n\r\"]", ""))) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.alias.duplicated"), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.error.adding.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - if(ip.toLowerCase().equals(zelNodeIP.getText().toLowerCase().replaceAll(" ", "").replaceAll("[\n\r\"]", ""))) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.ip.duplicated"), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.error.adding.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - if(zelnodekey.equals(zelNodeKey.getText())) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.key.duplicated"), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.error.adding.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - } - - } - } - - - Log.info("Now adding info needed to run zelNode to zelcash conf file if needed"); - File zelcashConf = new File(blockchainDir + File.separator + "zelcash.conf"); - Properties confProps = new Properties(); - FileInputStream fis = null; - String property = null; - boolean daemonNeedsToBeReindexed = false; - try - { - fis = new FileInputStream(zelcashConf); - fw = new FileWriter(zelcashConf,true); //the true will append the new data - confProps.load(fis); - property = confProps.getProperty("server"); - if(property == null) { - fw.write(System.getProperty("line.separator") + "server=1"); - Log.info("Adding server=1"); - daemonNeedsToBeReindexed = true; - } - property = confProps.getProperty("daemon"); - if(property == null) { - fw.write(System.getProperty("line.separator") + "daemon=1"); - Log.info("Adding server=1"); - daemonNeedsToBeReindexed = true; - } - property = confProps.getProperty("txindex"); - if(property == null) { - fw.write(System.getProperty("line.separator") + "txindex=1"); - Log.info("Adding txindex=1"); - daemonNeedsToBeReindexed = true; - } - property = confProps.getProperty("logtimestamps"); - if(property == null) { - fw.write(System.getProperty("line.separator") + "logtimestamps=1"); - Log.info("Adding logtimestamps=1"); - daemonNeedsToBeReindexed = true; - } - property = confProps.getProperty("maxconnections"); - if(property == null) { - fw.write(System.getProperty("line.separator") + "maxconnections=256"); - Log.info("Adding maxconnections=256"); - daemonNeedsToBeReindexed = true; - } - - } finally - { - if (fw != null) { - fw.close(); - } - if (fis != null) - { - fis.close(); - } - } - - try - { - fw = new FileWriter(zelnodeConf,true); //the true will append the new data - fw.write(zelNodeName.getText().replaceAll(" ", "").replaceAll("[\n\r\"]", "") + " " + zelNodeIP.getText().replaceAll(" ", "").replaceAll("[\n\r\"]", "") + " " + zelNodeKey.getText() + " " + zelNodeOutput.getSelectedItem().toString() +System.getProperty("line.separator"));//appends the string to the file - Log.info("File zelnode.conf saved with new ZelNode: " +zelNodeName); - } finally - { - if (fw != null) { - fw.close(); - } - } - - ZCashInstallationObserver initialInstallationObserver = new ZCashInstallationObserver(OSUtil.getProgramDirectory()); - if(initialInstallationObserver.isOnTestNet()) { - initialInstallationObserver = null; - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("wallet.zelnodes.restart.testnet.message"), - LanguageUtil.instance().getString("wallet.zelnodes.restart.title"), - JOptionPane.INFORMATION_MESSAGE); - } - else { - initialInstallationObserver = null; - Object[] options = { LanguageUtil.instance().getString("ipfs.wrapper.options.yes"), - LanguageUtil.instance().getString("ipfs.wrapper.options.no") }; - if(daemonNeedsToBeReindexed) { - int option = JOptionPane.showOptionDialog(null, - LanguageUtil.instance().getString("wallet.zelnodes.restart.reindex.message"), - LanguageUtil.instance().getString("wallet.zelnodes.restart.title"), - JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); - if (option == 0) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("wallet.reindex.restart.message"), - LanguageUtil.instance().getString("wallet.reindex.restart.title"), - JOptionPane.INFORMATION_MESSAGE); - this.setVisible(false); - ZelCashZelNodeDialog.this.parentFrame.restartDaemon(true, false); - } - } - else { - int option = JOptionPane.showOptionDialog(null, - LanguageUtil.instance().getString("wallet.zelnodes.restart.message"), - LanguageUtil.instance().getString("wallet.zelnodes.restart.title"), - JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); - if (option == 0) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("wallet.restart.message"), - LanguageUtil.instance().getString("wallet.reindex.restart.title"), - JOptionPane.INFORMATION_MESSAGE); - this.setVisible(false); - ZelCashZelNodeDialog.this.parentFrame.restartDaemon(false, false); - } - } - } - - restartUI(); - } - catch (WalletCallException wce) - { - Log.error("Unexpected error: ", wce); - - if ((wce.getMessage().indexOf("{\"code\":-28,\"message\"") != -1) || - (wce.getMessage().indexOf("error code: -28") != -1)) - { - JOptionPane.showMessageDialog( - null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.communication.error.text"), - LanguageUtil.instance().getString("main.frame.option.pane.wallet.communication.error.title"), - JOptionPane.ERROR_MESSAGE); - } else - { +public class ZelCashZelNodeDialog extends ZelCashJDialog { + protected ZelCashJTextField zelNodeName; + protected ZelCashJTextField zelNodeIP; + protected ZelCashJTextField zelNodeKey; + private ZelCashJComboBox zelNodeOutput; + protected ZelCashJTextField zelNodeOutputText; + protected ZelCashJTextField zelNodeAmount; + protected ZelCashJTextField zelNodeAddress; + protected ZelCashJTextField zelNodeOutputTxid; + protected ZelCashJTextField zelNodeOutputindex; + + private ZCashUI parentFrame; + + private static ZelCashJButton saveButton; + + private ZCashClientCaller clientCaller; + private static ZCashInstallationObserver installationObserver; + private String aliastoEdit; + protected LabelStorage labelStorage; + + final LanguageUtil langUtil = LanguageUtil.instance(); + + public ZelCashZelNodeDialog( + ZCashUI parent, final ZCashClientCaller clientCaller, + final ZCashInstallationObserver installationObserver, String aliasToEdit, + final LabelStorage labelStorage) throws IOException { + parentFrame = parent; + this.clientCaller = clientCaller; + this.aliastoEdit = aliasToEdit; + this.installationObserver = installationObserver; + this.labelStorage = labelStorage; + + this.setTitle(langUtil.getString("dialog.zelcashnewzelnode.title")); + this.setSize(900, 650); + this.setLocation(100, 100); + this.setLocationRelativeTo(parent); + this.setModal(true); + this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); + + ZelCashJPanel tempPanel = new ZelCashJPanel(new BorderLayout(0, 0)); + tempPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + ZelCashJLabel infoLabel = + new ZelCashJLabel(langUtil.getString("dialog.zelcashnewzelnode.info")); + tempPanel.add(infoLabel, BorderLayout.CENTER); + this.getContentPane().add(tempPanel, BorderLayout.NORTH); + + ZelCashJPanel detailsPanel = new ZelCashJPanel(); + detailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.Y_AXIS)); + + addFormField(detailsPanel, + langUtil.getString("dialog.zelcashnewzelnode.name"), + zelNodeName = new ZelCashJTextField(50)); + addFormField(detailsPanel, + langUtil.getString("dialog.zelcashnewzelnode.ip"), + zelNodeIP = new ZelCashJTextField(50)); + addFormField(detailsPanel, + langUtil.getString("dialog.zelcashnewzelnode.key"), + zelNodeKey = new ZelCashJTextField(50)); + addFormField(detailsPanel, + langUtil.getString("dialog.zelcashnewzelnode.output"), + zelNodeOutput = new ZelCashJComboBox()); + addFormField(detailsPanel, + langUtil.getString("dialog.zelcashnewzelnode.output.txid"), + zelNodeOutputTxid = new ZelCashJTextField(50)); + addFormField(detailsPanel, + langUtil.getString("dialog.zelcashnewzelnode.output.index"), + zelNodeOutputindex = new ZelCashJTextField(50)); + addFormField(detailsPanel, + langUtil.getString("dialog.zelcashnewzelnode.address"), + zelNodeAddress = new ZelCashJTextField(50)); + addFormField(detailsPanel, + langUtil.getString("dialog.zelcashnewzelnode.amount"), + zelNodeAmount = new ZelCashJTextField(50)); + zelNodeName.setEditable(true); + zelNodeIP.setEditable(true); + zelNodeIP.setText(":16125"); + zelNodeKey.setEditable(false); + zelNodeOutput.setEnabled(false); + zelNodeAmount.setEditable(false); + zelNodeAddress.setEditable(false); + zelNodeOutputTxid.setEditable(false); + zelNodeOutputindex.setEditable(false); + + getZelNodeOutputs(); + if (this.aliastoEdit != null) { + zelNodeName.setText(this.aliastoEdit); + + String blockchainDir = OSUtil.getBlockchainDirectory(); + File zelnodeConf = + new File(blockchainDir + File.separator + "zelnode.conf"); + if (!zelnodeConf.exists()) { + Log.info("Could not find file: {0} !", zelnodeConf.getAbsolutePath()); + } else { + Log.info("File zelnode.conf found"); + BufferedReader br = new BufferedReader(new FileReader(zelnodeConf)); + String st; + String emptyLine; + while ((st = br.readLine()) != null) { + emptyLine = st.replaceAll(" ", "").replaceAll( + "(?m)^\\\\s*\\\\r?\\\\n|\\\\r?\\\\n\\\\s*(?!.*\\\\r?\\\\n)", ""); + if (st.startsWith("#") || emptyLine.equals("")) { + continue; + } else { + String[] zelNodeInfo = st.split("\\s+"); + if (zelNodeInfo[0].equals(this.aliastoEdit)) { + zelNodeIP.setText(zelNodeInfo[1]); + zelNodeKey.setText(zelNodeInfo[2]); + zelNodeOutputTxid.setText(zelNodeInfo[3]); + zelNodeOutputindex.setText(zelNodeInfo[4]); + String output = zelNodeInfo[3] + " " + zelNodeInfo[4]; + zelNodeOutput.setSelectedItem(output); + try { + JsonObject txinfo = + clientCaller.getTransactionInfo(zelNodeInfo[3]); + JsonArray details = txinfo.get("details").asArray(); + String vout; + String category; + String detailAmount = "0"; + String address = ""; + boolean addressFound = false; + for (int i = 0; i < details.size(); ++i) { + JsonObject obj = details.get(i).asObject(); + vout = obj.get("vout").toString().replaceAll("[\n\r\"]", ""); + category = + obj.get("category").toString().replaceAll("[\n\r\"]", ""); + if (vout.equals(zelNodeInfo[4]) && "send".equals(category)) { + detailAmount = obj.get("amount") + .toString() + .replaceAll("[\n\r\"]", "") + .substring(1); + address = obj.get("address").toString().replaceAll( + "[\n\r\"]", ""); + addressFound = true; + break; + } + } + if (!addressFound) { + for (int i = 0; i < details.size(); ++i) { + JsonObject obj = details.get(i).asObject(); + vout = + obj.get("vout").toString().replaceAll("[\n\r\"]", ""); + category = obj.get("category") + .toString() + .replaceAll("[\n\r\"]", ""); + + if (vout.equals(zelNodeInfo[4]) && + "receive".equals(category)) { + detailAmount = obj.get("amount").toString().replaceAll( + "[\n\r\"]", ""); + address = obj.get("address").toString().replaceAll( + "[\n\r\"]", ""); + break; + } + } + } + if ((address != null) && (address.length() > 0)) { + String label = this.labelStorage.getLabel(address); + if ((label != null) && (label.length() > 0)) { + address = label + " - " + address; + } + } + + Float floatAmount = Float.parseFloat(detailAmount); + DecimalFormat df = new DecimalFormat("0.00"); + df.setMaximumFractionDigits(2); + zelNodeAmount.setText(df.format(floatAmount)); + zelNodeAddress.setText(address); + } catch (WalletCallException | IOException | + InterruptedException e) { + Log.error("Error calling getRawTransactionDetails:" + + e.getMessage()); + } + break; + } + } + } + } + } else { + gelZelNodeKey(); + } + + detailsPanel.setBorder( + BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); + this.getContentPane().add(detailsPanel, BorderLayout.CENTER); + + ZelCashJPanel closePanel = new ZelCashJPanel(); + closePanel.setLayout(new FlowLayout(FlowLayout.CENTER, 3, 3)); + ZelCashJButton closeButon = new ZelCashJButton( + langUtil.getString("dialog.about.button.close.text")); + closePanel.add(closeButon); + saveButton = + new ZelCashJButton(langUtil.getString("dialog.zelcashuiedit.save")); + closePanel.add(saveButton); + this.getContentPane().add(closePanel, BorderLayout.SOUTH); + + closeButon.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZelCashZelNodeDialog.this.parentFrame.repaint(); + ZelCashZelNodeDialog.this.setVisible(false); + ZelCashZelNodeDialog.this.dispose(); + } + }); + + saveButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if ("".equals(zelNodeName.getText().replaceAll(" ", "")) || + "".equals(zelNodeIP.getText().replaceAll(" ", "")) || + "".equals(zelNodeKey.getText().replaceAll(" ", "")) || + zelNodeOutput == null || zelNodeOutput.getItemCount() == 0 || + langUtil.getString("dialog.zelcashnewzelnode.select.output") + .equals(zelNodeOutput.getSelectedItem().toString())) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.missing"), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.missing.title"), + JOptionPane.INFORMATION_MESSAGE); + return; + } + if (!zelNodeName.getText().matches("\\w+")) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.alias.wrong"), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.error.adding.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + if (!zelNodeIP.getText().endsWith(":16125") && + !zelNodeIP.getText().endsWith(":26125")) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.ip.wrong"), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.error.adding.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + String ip = zelNodeIP.getText() + .replaceAll(":16125", "") + .replaceAll(":26125", ""); + if (ip.isEmpty()) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.ip.notset"), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.error.adding.title"), + JOptionPane.ERROR_MESSAGE); + return; + } else if (!ip.endsWith(".onion")) { + try { + Inet4Address address = (Inet4Address)Inet4Address.getByName(ip); + } catch (Exception ex1) { + try { + Inet6Address address = (Inet6Address)Inet6Address.getByName(ip); + if (!ip.startsWith("[") || !ip.endsWith("]")) { JOptionPane.showMessageDialog( null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.communication.error.2.text", wce.getMessage()), - LanguageUtil.instance().getString("main.frame.option.pane.wallet.communication.error.2.title"), + LanguageUtil.instance().getString( + "parsing.error.zelnodesconf.wrong.ip", ip), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.error.adding.title"), JOptionPane.ERROR_MESSAGE); + return; + } + } catch (Exception ex) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "parsing.error.zelnodesconf.wrong.ip", ip), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.error.adding.title"), + JOptionPane.ERROR_MESSAGE); + return; } + } + } - System.exit(2); - } catch (Exception ex) - { - Log.error("Unexpected error: ", ex); - JOptionPane.showMessageDialog( - null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.text", ex.getMessage()), - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(3); - } catch (Error err) - { - // Last resort catch for unexpected problems - just to inform the user - err.printStackTrace(); + saveSettings(); + } + }); + + zelNodeOutput.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent event) { + if (event.getStateChange() == ItemEvent.SELECTED) { + Object source = event.getSource(); + if (source instanceof ZelCashJComboBox) { + ZelCashJComboBox cb = (ZelCashJComboBox)source; + int index = cb.getSelectedIndex(); + if (index != 0) { + zelNodeAmount.setText( + langUtil.getString("zelnodespanel.zelnodes.button.loading")); + zelNodeAddress.setText( + langUtil.getString("zelnodespanel.zelnodes.button.loading")); + zelNodeOutputindex.setText( + langUtil.getString("zelnodespanel.zelnodes.button.loading")); + zelNodeOutputTxid.setText( + langUtil.getString("zelnodespanel.zelnodes.button.loading")); + String[] outputinfo = cb.getSelectedItem().toString().split(" "); + try { + zelNodeOutputTxid.setText(outputinfo[0]); + zelNodeOutputindex.setText(outputinfo[1]); + JsonObject txinfo = + clientCaller.getTransactionInfo(outputinfo[0]); + JsonArray details = txinfo.get("details").asArray(); + String vout; + String category; + String detailAmount = "0"; + String address = ""; + Log.debug("details array size:" + details.size()); + boolean addressFound = false; + for (int i = 0; i < details.size(); ++i) { + JsonObject obj = details.get(i).asObject(); + vout = obj.get("vout").toString().replaceAll("[\n\r\"]", ""); + category = + obj.get("category").toString().replaceAll("[\n\r\"]", ""); + + if (vout.equals(outputinfo[1]) && "send".equals(category)) { + detailAmount = obj.get("amount") + .toString() + .replaceAll("[\n\r\"]", "") + .substring(1); + address = obj.get("address").toString().replaceAll( + "[\n\r\"]", ""); + addressFound = true; + break; + } + } + if (!addressFound) { + for (int i = 0; i < details.size(); ++i) { + JsonObject obj = details.get(i).asObject(); + vout = + obj.get("vout").toString().replaceAll("[\n\r\"]", ""); + category = obj.get("category") + .toString() + .replaceAll("[\n\r\"]", ""); + + if (vout.equals(outputinfo[1]) && + "receive".equals(category)) { + detailAmount = obj.get("amount").toString().replaceAll( + "[\n\r\"]", ""); + address = obj.get("address").toString().replaceAll( + "[\n\r\"]", ""); + break; + } + } + } + if ((address != null) && (address.length() > 0)) { + String label = labelStorage.getLabel(address); + if ((label != null) && (label.length() > 0)) { + Log.debug("found the address label:" + label); + address = label + " - " + address; + } + } + Float floatAmount = Float.parseFloat(detailAmount); + DecimalFormat df = new DecimalFormat("0.00"); + df.setMaximumFractionDigits(2); + zelNodeAmount.setText(df.format(floatAmount)); + zelNodeAddress.setText(address); + } catch (WalletCallException | IOException | + InterruptedException e) { + Log.error("Error calling getRawTransactionDetails:" + + e.getMessage()); + } + } + } + } + } + }); + + pack(); + } + + private void gelZelNodeKey() { + Log.info("gelZelNodeKey start"); + try { + zelNodeKey.setText( + clientCaller.getZelNodeKey().replaceAll("[\n\r\"]", "")); + } catch (WalletCallException | IOException | InterruptedException e) { + Log.error("Error obtaining gelZelNodeKey. " + e.getMessage()); + } finally { + Log.info("gelZelNodeKey end"); + } + } + + private void getZelNodeOutputs() { + Log.info("getZelNodeOutputs start"); + int outputsCount = 0; + try { + JsonArray ja = clientCaller.getZelNodeOutputs(); + outputsCount = ja.size(); + zelNodeOutput.removeAllItems(); + zelNodeOutput.addItem( + this.langUtil.getString("dialog.zelcashnewzelnode.select.output")); + for (int i = 0; i < ja.size(); ++i) { + JsonObject obj = ja.get(i).asObject(); + String txhash = obj.get("txhash").toString().replaceAll("[\n\r\"]", ""); + String outputidx = + obj.get("outputidx").toString().replaceAll("[\n\r\"]", ""); + zelNodeOutput.addItem(txhash + " " + outputidx); + } + zelNodeOutput.setEnabled(true); + } catch (WalletCallException | IOException | InterruptedException e1) { + Log.error("Error obtaining zelNodeOutputs. " + e1.getMessage()); + } finally { + Log.info("getZelNodeOutputs end - outputscount:" + outputsCount); + } + } + + private void addFormField(ZelCashJPanel detailsPanel, String name, + JComponent field, ZelCashJButton button) { + ZelCashJPanel tempPanel = + new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 4, 2)); + ZelCashJLabel tempLabel = new ZelCashJLabel(name, JLabel.RIGHT); + // TODO: hard sizing of labels may not scale! + final int width = new ZelCashJLabel("Sender identification T address:") + .getPreferredSize() + .width + + 10; + tempLabel.setPreferredSize( + new Dimension(width, tempLabel.getPreferredSize().height)); + tempPanel.add(tempLabel); + tempPanel.add(field); + tempPanel.add(button); + detailsPanel.add(tempPanel); + } + + private void addFormField(ZelCashJPanel detailsPanel, String name, + JComponent field) { + ZelCashJPanel tempPanel = + new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 4, 2)); + ZelCashJLabel tempLabel = new ZelCashJLabel(name, JLabel.RIGHT); + // TODO: hard sizing of labels may not scale! + final int width = new ZelCashJLabel("Sender identification T address:") + .getPreferredSize() + .width + + 10; + tempLabel.setPreferredSize( + new Dimension(width, tempLabel.getPreferredSize().height)); + tempPanel.add(tempLabel); + tempPanel.add(field); + detailsPanel.add(tempPanel); + } + + private void saveSettings() { + Log.info("Starting save zelnode"); + try { + removeEmptyLinesFromNodesConfigurationFile(); + if (this.aliastoEdit != null) { + removeZelNode(this.aliastoEdit); + } + FileWriter fw = null; + String blockchainDir = OSUtil.getBlockchainDirectory(); + File zelnodeConf = + new File(blockchainDir + File.separator + "zelnode.conf"); + if (!zelnodeConf.exists()) { + Log.info("Could not find file: {0} !", zelnodeConf.getAbsolutePath()); + zelnodeConf.createNewFile(); + Log.info("File zelnodes.conf created"); + } else { + Log.info("File zelnode.conf found"); + } + + if (this.aliastoEdit == null) { + // checking for duplications + Scanner scanner = new Scanner(zelnodeConf); + + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (!line.startsWith("#")) { + String[] zelNodeInfo; + zelNodeInfo = line.split("\\s+"); + String alias = zelNodeInfo[0]; + String ip = zelNodeInfo[1]; + String zelnodekey = zelNodeInfo[2]; + String output = zelNodeInfo[3] + " " + zelNodeInfo[4]; + + if (output.equals(zelNodeOutput.getSelectedItem().toString())) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.output.duplicated"), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.error.adding.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + if (alias.toLowerCase().equals(zelNodeName.getText() + .toLowerCase() + .replaceAll(" ", "") + .replaceAll("[\n\r\"]", ""))) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.alias.duplicated"), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.error.adding.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + if (ip.toLowerCase().equals(zelNodeIP.getText() + .toLowerCase() + .replaceAll(" ", "") + .replaceAll("[\n\r\"]", ""))) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.ip.duplicated"), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.error.adding.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + if (zelnodekey.equals(zelNodeKey.getText())) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.key.duplicated"), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.error.adding.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + } + } + } + + Log.info( + "Now adding info needed to run zelNode to zelcash conf file if needed"); + File zelcashConf = + new File(blockchainDir + File.separator + "zelcash.conf"); + Properties confProps = new Properties(); + FileInputStream fis = null; + String property = null; + boolean daemonNeedsToBeReindexed = false; + try { + fis = new FileInputStream(zelcashConf); + fw = new FileWriter(zelcashConf, + true); // the true will append the new data + confProps.load(fis); + property = confProps.getProperty("server"); + if (property == null) { + fw.write(System.getProperty("line.separator") + "server=1"); + Log.info("Adding server=1"); + daemonNeedsToBeReindexed = true; + } + property = confProps.getProperty("daemon"); + if (property == null) { + fw.write(System.getProperty("line.separator") + "daemon=1"); + Log.info("Adding server=1"); + daemonNeedsToBeReindexed = true; + } + property = confProps.getProperty("txindex"); + if (property == null) { + fw.write(System.getProperty("line.separator") + "txindex=1"); + Log.info("Adding txindex=1"); + daemonNeedsToBeReindexed = true; + } + property = confProps.getProperty("logtimestamps"); + if (property == null) { + fw.write(System.getProperty("line.separator") + "logtimestamps=1"); + Log.info("Adding logtimestamps=1"); + daemonNeedsToBeReindexed = true; + } + property = confProps.getProperty("maxconnections"); + if (property == null) { + fw.write(System.getProperty("line.separator") + "maxconnections=256"); + Log.info("Adding maxconnections=256"); + daemonNeedsToBeReindexed = true; + } + + } finally { + if (fw != null) { + fw.close(); + } + if (fis != null) { + fis.close(); + } + } + + try { + fw = new FileWriter(zelnodeConf, + true); // the true will append the new data + fw.write( + zelNodeName.getText().replaceAll(" ", "").replaceAll("[\n\r\"]", + "") + + " " + + zelNodeIP.getText().replaceAll(" ", "").replaceAll("[\n\r\"]", "") + + " " + zelNodeKey.getText() + " " + + zelNodeOutput.getSelectedItem().toString() + + System.getProperty( + "line.separator")); // appends the string to the file + Log.info("File zelnode.conf saved with new ZelNode: " + zelNodeName); + } finally { + if (fw != null) { + fw.close(); + } + } + + ZCashInstallationObserver initialInstallationObserver = + new ZCashInstallationObserver(OSUtil.getProgramDirectory()); + if (initialInstallationObserver.isOnTestNet()) { + initialInstallationObserver = null; + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "wallet.zelnodes.restart.testnet.message"), + LanguageUtil.instance().getString("wallet.zelnodes.restart.title"), + JOptionPane.INFORMATION_MESSAGE); + } else { + initialInstallationObserver = null; + Object[] options = { + LanguageUtil.instance().getString("ipfs.wrapper.options.yes"), + LanguageUtil.instance().getString("ipfs.wrapper.options.no")}; + if (daemonNeedsToBeReindexed) { + int option = JOptionPane.showOptionDialog( + null, + LanguageUtil.instance().getString( + "wallet.zelnodes.restart.reindex.message"), + LanguageUtil.instance().getString( + "wallet.zelnodes.restart.title"), + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, + options, options[0]); + if (option == 0) { + JOptionPane.showMessageDialog(null, + LanguageUtil.instance().getString( + "wallet.reindex.restart.message"), + LanguageUtil.instance().getString( + "wallet.reindex.restart.title"), + JOptionPane.INFORMATION_MESSAGE); + this.setVisible(false); + ZelCashZelNodeDialog.this.parentFrame.restartDaemon(true, false); + } + } else { + int option = JOptionPane.showOptionDialog( + null, + LanguageUtil.instance().getString( + "wallet.zelnodes.restart.message"), + LanguageUtil.instance().getString( + "wallet.zelnodes.restart.title"), + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, + options, options[0]); + if (option == 0) { JOptionPane.showMessageDialog( null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.2.text", err.getMessage()), - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.2.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(4); + LanguageUtil.instance().getString("wallet.restart.message"), + LanguageUtil.instance().getString( + "wallet.reindex.restart.title"), + JOptionPane.INFORMATION_MESSAGE); + this.setVisible(false); + ZelCashZelNodeDialog.this.parentFrame.restartDaemon(false, false); + } + } + } + + restartUI(); + } catch (WalletCallException wce) { + Log.error("Unexpected error: ", wce); + + if ((wce.getMessage().indexOf("{\"code\":-28,\"message\"") != -1) || + (wce.getMessage().indexOf("error code: -28") != -1)) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.communication.error.text"), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.communication.error.title"), + JOptionPane.ERROR_MESSAGE); + } else { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.communication.error.2.text", + wce.getMessage()), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.communication.error.2.title"), + JOptionPane.ERROR_MESSAGE); + } + + System.exit(2); + } catch (Exception ex) { + Log.error("Unexpected error: ", ex); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.text", + ex.getMessage()), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(3); + } catch (Error err) { + // Last resort catch for unexpected problems - just to inform the user + err.printStackTrace(); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.2.text", + err.getMessage()), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.2.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(4); + } + } + + public void restartUI() + throws IOException, InterruptedException, WalletCallException { + Log.info("Restarting the UI."); + this.parentFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + this.setVisible(false); + ZCashUI z = new ZCashUI(null); + this.parentFrame.setVisible(false); + this.parentFrame.dispose(); + this.parentFrame = z; + this.parentFrame.repaint(); + this.parentFrame.setVisible(true); + this.dispose(); + } + + public void disposeMenu() { + this.setVisible(false); + this.dispose(); + } + + public static void removeZelNode(String zelNodeAlias) { + Log.info("Removing zelnode alias:" + zelNodeAlias); + removeEmptyLinesFromNodesConfigurationFile(); + try { + String blockchainDir = OSUtil.getBlockchainDirectory(); + File zelnodeConf = + new File(blockchainDir + File.separator + "zelnode.conf"); + if (!zelnodeConf.exists()) { + Log.error("Could not find file: {0} !", zelnodeConf.getAbsolutePath()); + return; + } + BufferedReader br = new BufferedReader(new FileReader(zelnodeConf)); + ArrayList coll = new ArrayList(); + try { + + String st = null; + String[] zelNodeInfo; + String emptyLine = null; + while ((st = br.readLine()) != null) { + emptyLine = st.replaceAll(" ", "").replaceAll( + "(?m)^\\\\s*\\\\r?\\\\n|\\\\r?\\\\n\\\\s*(?!.*\\\\r?\\\\n)", ""); + if (st.startsWith("#") || "".equals(emptyLine)) { + coll.add(st); + } else { + zelNodeInfo = st.split("\\s+"); + if (!zelNodeAlias.equals(zelNodeInfo[0])) { + coll.add(st); + } + } + } + } finally { + if (br != null) { + br.close(); + } + } + FileWriter writer = new FileWriter(zelnodeConf); + try { + for (String line : coll) { + writer.write(line + System.getProperty("line.separator")); + } + } finally { + if (writer != null) { + writer.close(); + } + } + } catch (Exception ex) { + Log.error("Error deleting zelnode " + zelNodeAlias + ":" + + ex.getMessage()); + } finally { + Log.info("Zelnode removed from configuration file. Alias removed:" + + zelNodeAlias); + } + } + + public static void removeEmptyLinesFromNodesConfigurationFile() { + try { + String blockchainDir = OSUtil.getBlockchainDirectory(); + File zelnodeConf = + new File(blockchainDir + File.separator + "zelnode.conf"); + if (!zelnodeConf.exists()) { + Log.error("Could not find file: {0} !", zelnodeConf.getAbsolutePath()); + return; + } + BufferedReader br = new BufferedReader(new FileReader(zelnodeConf)); + ArrayList coll = new ArrayList(); + try { + + String st = null; + String[] zelNodeInfo; + String emptyLine = null; + while ((st = br.readLine()) != null) { + emptyLine = st.replaceAll(" ", "").replaceAll( + "(?m)^\\\\s*\\\\r?\\\\n|\\\\r?\\\\n\\\\s*(?!.*\\\\r?\\\\n)", ""); + if (!"".equals(emptyLine)) { + coll.add(st); + } + } + } finally { + if (br != null) { + br.close(); + } + } + FileWriter writer = new FileWriter(zelnodeConf); + try { + for (String line : coll) { + writer.write(line + System.getProperty("line.separator")); + } + } finally { + if (writer != null) { + writer.close(); } - - } - - public void restartUI() throws IOException, InterruptedException, WalletCallException { - Log.info("Restarting the UI."); - this.parentFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - this.setVisible(false); - ZCashUI z = new ZCashUI(null); - this.parentFrame.setVisible(false); - this.parentFrame.dispose(); - this.parentFrame = z; - this.parentFrame.repaint(); - this.parentFrame.setVisible(true); - this.dispose(); - } - - public void disposeMenu() { - this.setVisible(false); - this.dispose(); - } - - public static void removeZelNode(String zelNodeAlias) { - Log.info("Removing zelnode alias:"+zelNodeAlias); - removeEmptyLinesFromNodesConfigurationFile(); - try { - String blockchainDir = OSUtil.getBlockchainDirectory(); - File zelnodeConf = new File(blockchainDir + File.separator + "zelnode.conf"); - if (!zelnodeConf.exists()) { - Log.error("Could not find file: {0} !", zelnodeConf.getAbsolutePath()); - return; - } - BufferedReader br = new BufferedReader(new FileReader(zelnodeConf)); - ArrayList coll = new ArrayList(); - try { - - String st = null; - String[] zelNodeInfo; - String emptyLine = null; - while ((st = br.readLine()) != null) { - emptyLine = st.replaceAll(" ", "").replaceAll("(?m)^\\\\s*\\\\r?\\\\n|\\\\r?\\\\n\\\\s*(?!.*\\\\r?\\\\n)", ""); - if(st.startsWith("#") || "".equals(emptyLine)) { - coll.add(st); - } - else { - zelNodeInfo = st.split("\\s+"); - if(!zelNodeAlias.equals(zelNodeInfo[0])) { - coll.add(st); - } - } - - } - } - finally { - if(br != null) { - br.close(); - } - } - FileWriter writer = new FileWriter(zelnodeConf); - try { - for (String line : coll) { - writer.write(line+System.getProperty("line.separator")); - } - } - finally { - if(writer != null) { - writer.close(); - } - } - } - catch(Exception ex) { - Log.error("Error deleting zelnode "+ zelNodeAlias + ":" + ex.getMessage()); - } - finally { - Log.info("Zelnode removed from configuration file. Alias removed:"+zelNodeAlias); - } - } - - public static void removeEmptyLinesFromNodesConfigurationFile() { - try { - String blockchainDir = OSUtil.getBlockchainDirectory(); - File zelnodeConf = new File(blockchainDir + File.separator + "zelnode.conf"); - if (!zelnodeConf.exists()) { - Log.error("Could not find file: {0} !", zelnodeConf.getAbsolutePath()); - return; - } - BufferedReader br = new BufferedReader(new FileReader(zelnodeConf)); - ArrayList coll = new ArrayList(); - try { - - String st = null; - String[] zelNodeInfo; - String emptyLine = null; - while ((st = br.readLine()) != null) { - emptyLine = st.replaceAll(" ", "").replaceAll("(?m)^\\\\s*\\\\r?\\\\n|\\\\r?\\\\n\\\\s*(?!.*\\\\r?\\\\n)", ""); - if(!"".equals(emptyLine)) { - coll.add(st); - } - } - } - finally { - if(br != null) { - br.close(); - } - } - FileWriter writer = new FileWriter(zelnodeConf); - try { - for (String line : coll) { - writer.write(line+System.getProperty("line.separator")); - } - } - finally { - if(writer != null) { - writer.close(); - } - } - } - catch(Exception ex) { - Log.error("Error deleting empty lines from zelnodes.conf:" + ex.getMessage()); - } - } - -} + } + } catch (Exception ex) { + Log.error("Error deleting empty lines from zelnodes.conf:" + + ex.getMessage()); + } + } +} diff --git a/src/java/com/cabecinha84/zelcashui/ZelNodesPanel.java b/src/java/com/cabecinha84/zelcashui/ZelNodesPanel.java index 6a657f65..8e4b1c56 100644 --- a/src/java/com/cabecinha84/zelcashui/ZelNodesPanel.java +++ b/src/java/com/cabecinha84/zelcashui/ZelNodesPanel.java @@ -1,5 +1,19 @@ package com.cabecinha84.zelcashui; +import com.eclipsesource.json.JsonArray; +import com.eclipsesource.json.JsonObject; +import com.vaklinov.zcashui.LabelStorage; +import com.vaklinov.zcashui.LanguageUtil; +import com.vaklinov.zcashui.Log; +import com.vaklinov.zcashui.OSUtil; +import com.vaklinov.zcashui.PasswordDialog; +import com.vaklinov.zcashui.SendCashPanel; +import com.vaklinov.zcashui.StatusUpdateErrorReporter; +import com.vaklinov.zcashui.WalletTabPanel; +import com.vaklinov.zcashui.ZCashClientCaller; +import com.vaklinov.zcashui.ZCashClientCaller.WalletCallException; +import com.vaklinov.zcashui.ZCashInstallationObserver; +import com.vaklinov.zcashui.ZCashUI; import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.Dimension; @@ -20,7 +34,6 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Vector; - import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JComponent; @@ -31,681 +44,709 @@ import javax.swing.border.EtchedBorder; import javax.swing.table.DefaultTableModel; -import com.eclipsesource.json.JsonArray; -import com.eclipsesource.json.JsonObject; -import com.vaklinov.zcashui.LabelStorage; -import com.vaklinov.zcashui.LanguageUtil; -import com.vaklinov.zcashui.Log; -import com.vaklinov.zcashui.OSUtil; -import com.vaklinov.zcashui.PasswordDialog; -import com.vaklinov.zcashui.SendCashPanel; -import com.vaklinov.zcashui.StatusUpdateErrorReporter; -import com.vaklinov.zcashui.WalletTabPanel; -import com.vaklinov.zcashui.ZCashClientCaller; -import com.vaklinov.zcashui.ZCashClientCaller.WalletCallException; -import com.vaklinov.zcashui.ZCashInstallationObserver; -import com.vaklinov.zcashui.ZCashUI; - /** * Provides the functionality for sending cash */ public class ZelNodesPanel extends WalletTabPanel { - private StatusUpdateErrorReporter errorReporter; - private ZCashInstallationObserver installationObserver; - private LabelStorage labelStorage; - private LanguageUtil langUtil; - private ZCashUI parentFrame; - private ZCashClientCaller clientCaller; - private ZelCashJTable zelNodesTable = null; - private ZelCashJScrollPane zelNodesTablePane = null; - - private ZelCashJTable myZelNodesTable = null; - private ZelCashJScrollPane myZelNodesTablePane = null; - - private ZelCashJLabel zelnodescount = null; - private ZelCashJLabel myzelnodescount = null; - - private ZelCashJPanel myzelnodes = null; - private ZelCashJPanel zelnodes = null; - - protected int lastRow = -1; - protected int lastColumn = -1; - - protected ZelCashJPopupMenu popupMenu; - - ZelCashJButton refresh; - ZelCashJButton collectZelNodeReward; - ZelCashJButton clearCache; - - private int utxoRewardCount = 0; - private static double zelnodeRewardAvailable = 0; - private static int MAX_UTXO_TXN = 100; - - public ZelNodesPanel(ZCashUI parentFrame, ZelCashJTabbedPane parentTabs, ZCashClientCaller clientCaller, - StatusUpdateErrorReporter errorReporter, LabelStorage labelStorage) - throws IOException, InterruptedException, WalletCallException { - this.parentFrame = parentFrame; - this.clientCaller = clientCaller; - this.errorReporter = errorReporter; - this.labelStorage = labelStorage; - this.langUtil = LanguageUtil.instance(); - - // Build content - ZelCashJPanel zelNodesPanel = this; - zelNodesPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); - zelNodesPanel.setLayout(new BorderLayout(0, 0)); - - ZelCashJPanel detailsPanel = new ZelCashJPanel(); - detailsPanel.setLayout(new GridLayout(1,2)); - - myzelnodes = new ZelCashJPanel(); - myzelnodescount = new ZelCashJLabel(); - myzelnodescount.setText(langUtil.getString("zelnodespanel.myzelnodes.count","0")); - myzelnodes.add(myzelnodescount); - myZelNodesTablePane = new ZelCashJScrollPane(); - Vector> dataList = new Vector<>(); - Vector columnNames = new Vector<>(); - columnNames.add(langUtil.getString("zelnodespanel.zelnodes.alias")); - columnNames.add(langUtil.getString("zelnodespanel.zelnodes.ip")); - columnNames.add(langUtil.getString("zelnodespanel.zelnodes.status")); - columnNames.add(langUtil.getString("zelnodespanel.zelnodes.tier")); - columnNames.add(langUtil.getString("zelnodespanel.zelnodes.activetime")); - columnNames.add(langUtil.getString("zelnodespanel.zelnodes.laspaid")); - - myZelNodesTable = new ZelCashJTable(dataList, columnNames); - myZelNodesTable.setAutoCreateRowSorter(true); - - - this.getMyZelNodeList(); - myZelNodesTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); - myzelnodes.add(myZelNodesTablePane = new ZelCashJScrollPane(myZelNodesTable), BorderLayout.CENTER); - - detailsPanel.add(myzelnodes); - - - zelnodes = new ZelCashJPanel(); - zelnodescount = new ZelCashJLabel(); - zelnodescount.setText(langUtil.getString("zelnodespanel.zelnodes.count","0", "0", "0", "0")); - zelnodes.add(zelnodescount); - zelNodesTablePane = new ZelCashJScrollPane(); - - dataList = new Vector<>(); - columnNames = new Vector<>(); - //columnNames.add(langUtil.getString("zelnodespanel.zelnodes.rank")); - columnNames.add(langUtil.getString("zelnodespanel.zelnodes.ip")); - columnNames.add(langUtil.getString("zelnodespanel.zelnodes.status")); - columnNames.add(langUtil.getString("zelnodespanel.zelnodes.tier")); - //columnNames.add(langUtil.getString("zelnodespanel.zelnodes.version")); - columnNames.add(langUtil.getString("zelnodespanel.zelnodes.activetime")); - //columnNames.add(langUtil.getString("zelnodespanel.zelnodes.lastseen")); - //columnNames.add(langUtil.getString("zelnodespanel.zelnodes.laspaid")); - - zelNodesTable = new ZelCashJTable(dataList, columnNames); - zelNodesTable.setAutoCreateRowSorter(true); - - this.gelZelNodeList(); - zelNodesTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); - zelnodes.add(zelNodesTablePane = new ZelCashJScrollPane(zelNodesTable), BorderLayout.CENTER); - - - detailsPanel.add(zelnodes); - - zelNodesPanel.add(detailsPanel,BorderLayout.CENTER); - - // Build panel of buttons - ZelCashJPanel buttonPanel = new ZelCashJPanel(); - buttonPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); - collectZelNodeReward = new ZelCashJButton(langUtil.getString("panel.zelnodespanel.button.colletReward")); - buttonPanel.add(collectZelNodeReward); - refresh = new ZelCashJButton(langUtil.getString("panel.address.button.refresh")); - buttonPanel.add(refresh); - clearCache = new ZelCashJButton(langUtil.getString("panel.address.button.clear.cache")); - buttonPanel.add(clearCache); - zelNodesPanel.add(buttonPanel,BorderLayout.SOUTH); - - refresh.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Cursor oldCursor = ZelNodesPanel.this.getCursor(); - try { - ZelNodesPanel.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - refresh.setText(langUtil.getString("zelnodespanel.zelnodes.button.loading")); - refresh.setEnabled(false); - refreshZelNodesTables(); - refresh.setText(langUtil.getString("panel.address.button.refresh")); - refresh.setEnabled(true); - - } - finally { - ZelNodesPanel.this.setCursor(oldCursor); - } - - - } - }); - - clearCache.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Cursor oldCursor = ZelNodesPanel.this.getCursor(); - try { - ZelNodesPanel.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - clearCache.setText(langUtil.getString("zelnodespanel.zelnodes.button.loading")); - clearCache.setEnabled(false); - String blockchainDir = OSUtil.getBlockchainDirectory(); - File zelnodecache = new File(blockchainDir + File.separator + "zelnodecache.dat"); - if(zelnodecache.exists()) { - zelnodecache.delete(); - ZelNodesPanel.this.parentFrame.restartDaemon(false, false); - try { - restartUI(); - } catch (IOException | InterruptedException | WalletCallException e1) { - Log.error("Error restarting the UI, the wallet will be closed. Error:"+e1.getMessage()); - System.exit(1); - } - } - - } catch (IOException e1) { - Log.error("Error clearing chache:"+e1.getMessage()); - } - finally { - ZelNodesPanel.this.setCursor(oldCursor); - } - - - } - }); - - collectZelNodeReward.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Cursor oldCursor = ZelNodesPanel.this.getCursor(); - try { - ZelNodesPanel.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - getZelNodesRewards(); - } - finally { - ZelNodesPanel.this.setCursor(oldCursor); - } - - - } - }); - } - - private void getZelNodesRewards() { - try { - JsonArray ja = clientCaller.getCollectableZelNodeRewardsInformation(); - utxoRewardCount = 0; - zelnodeRewardAvailable = 0; - boolean generated = false; - boolean spendable = false; - for(int i=0; i < ja.size(); ++i) { - JsonObject jsonObj = ja.get(i).asObject(); - generated = Boolean.parseBoolean(jsonObj.get("generated").toString()); - spendable = Boolean.parseBoolean(jsonObj.get("spendable").toString()); - if(generated && spendable) { - utxoRewardCount++; - zelnodeRewardAvailable+= Double.parseDouble(jsonObj.get("amount").toString().replaceAll("[\n\r\"]", "")); - } - - } - - if(utxoRewardCount == 0) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("panel.zelnodespanel.no.utxo.to.collect"), - LanguageUtil.instance().getString("panel.zelnodespanel.no.utxo.to.collect.title"), - JOptionPane.INFORMATION_MESSAGE); - return; - } - // Z Addresses - they are OK - String[] zAddresses = clientCaller.getWalletZAddresses(); - if(zAddresses.length == 0) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("panel.zelnodespanel.no.zaddress.collect"), - LanguageUtil.instance().getString("panel.zelnodespanel.no.zaddress.collect.title"), - JOptionPane.INFORMATION_MESSAGE); - return; - } - String [] comboBoxItems = new String[zAddresses.length]; - String label; - String address; - for (int i = 0; i < zAddresses.length; i++) - { - label = this.labelStorage.getLabel(zAddresses[i]); - address = zAddresses[i]; - if ((label != null) && (label.length() > 0)) - { - address = label + " - " + address; - } - comboBoxItems[i] = address; - } - - ZelCashJComboBox zAddressesCombo = new ZelCashJComboBox<>(comboBoxItems); - - ZelCashJPanel myPanel = new ZelCashJPanel(); - myPanel.setLayout(new BoxLayout(myPanel, BoxLayout.Y_AXIS)); - - ZelCashJPanel tempPanel = new ZelCashJPanel(new BorderLayout(0, 0)); - tempPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); - ZelCashJLabel infoLabel = new ZelCashJLabel(langUtil.getString("panel.zelnodespanel.collect.zelnodereward.info", new DecimalFormat("########0.00######").format(zelnodeRewardAvailable), utxoRewardCount)); - tempPanel.add(infoLabel, BorderLayout.CENTER); - - ZelCashJPanel detailsPanel = new ZelCashJPanel(); - detailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.Y_AXIS)); - - zAddressesCombo.setPreferredSize(new Dimension(600,zAddressesCombo.getPreferredSize().height)); - detailsPanel.add(zAddressesCombo, BorderLayout.CENTER); - - myPanel.add(tempPanel); - myPanel.add(detailsPanel); - - int result = JOptionPane.showConfirmDialog(ZelNodesPanel.this, - myPanel, - langUtil.getString("panel.zelnodespanel.collect.zelnodereward.info.title"), - JOptionPane.OK_CANCEL_OPTION, JOptionPane.CANCEL_OPTION); - if (result == JOptionPane.CANCEL_OPTION || result == JOptionPane.CLOSED_OPTION) { - return; - } - - String amountShielded; + private StatusUpdateErrorReporter errorReporter; + private ZCashInstallationObserver installationObserver; + private LabelStorage labelStorage; + private LanguageUtil langUtil; + private ZCashUI parentFrame; + private ZCashClientCaller clientCaller; + private ZelCashJTable zelNodesTable = null; + private ZelCashJScrollPane zelNodesTablePane = null; + + private ZelCashJTable myZelNodesTable = null; + private ZelCashJScrollPane myZelNodesTablePane = null; + + private ZelCashJLabel zelnodescount = null; + private ZelCashJLabel myzelnodescount = null; + + private ZelCashJPanel myzelnodes = null; + private ZelCashJPanel zelnodes = null; + + protected int lastRow = -1; + protected int lastColumn = -1; + + protected ZelCashJPopupMenu popupMenu; + + ZelCashJButton refresh; + ZelCashJButton collectZelNodeReward; + ZelCashJButton clearCache; + + private int utxoRewardCount = 0; + private static double zelnodeRewardAvailable = 0; + private static int MAX_UTXO_TXN = 100; + + public ZelNodesPanel(ZCashUI parentFrame, ZelCashJTabbedPane parentTabs, + ZCashClientCaller clientCaller, + StatusUpdateErrorReporter errorReporter, + LabelStorage labelStorage) + throws IOException, InterruptedException, WalletCallException { + this.parentFrame = parentFrame; + this.clientCaller = clientCaller; + this.errorReporter = errorReporter; + this.labelStorage = labelStorage; + this.langUtil = LanguageUtil.instance(); + + // Build content + ZelCashJPanel zelNodesPanel = this; + zelNodesPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); + zelNodesPanel.setLayout(new BorderLayout(0, 0)); + + ZelCashJPanel detailsPanel = new ZelCashJPanel(); + detailsPanel.setLayout(new GridLayout(1, 2)); + + myzelnodes = new ZelCashJPanel(); + myzelnodescount = new ZelCashJLabel(); + myzelnodescount.setText( + langUtil.getString("zelnodespanel.myzelnodes.count", "0")); + myzelnodes.add(myzelnodescount); + myZelNodesTablePane = new ZelCashJScrollPane(); + Vector> dataList = new Vector<>(); + Vector columnNames = new Vector<>(); + columnNames.add(langUtil.getString("zelnodespanel.zelnodes.alias")); + columnNames.add(langUtil.getString("zelnodespanel.zelnodes.ip")); + columnNames.add(langUtil.getString("zelnodespanel.zelnodes.status")); + columnNames.add(langUtil.getString("zelnodespanel.zelnodes.tier")); + columnNames.add(langUtil.getString("zelnodespanel.zelnodes.activetime")); + columnNames.add(langUtil.getString("zelnodespanel.zelnodes.laspaid")); + + myZelNodesTable = new ZelCashJTable(dataList, columnNames); + myZelNodesTable.setAutoCreateRowSorter(true); + + this.getMyZelNodeList(); + myZelNodesTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); + myzelnodes.add(myZelNodesTablePane = + new ZelCashJScrollPane(myZelNodesTable), + BorderLayout.CENTER); + + detailsPanel.add(myzelnodes); + + zelnodes = new ZelCashJPanel(); + zelnodescount = new ZelCashJLabel(); + zelnodescount.setText( + langUtil.getString("zelnodespanel.zelnodes.count", "0", "0", "0", "0")); + zelnodes.add(zelnodescount); + zelNodesTablePane = new ZelCashJScrollPane(); + + dataList = new Vector<>(); + columnNames = new Vector<>(); + // columnNames.add(langUtil.getString("zelnodespanel.zelnodes.rank")); + columnNames.add(langUtil.getString("zelnodespanel.zelnodes.ip")); + columnNames.add(langUtil.getString("zelnodespanel.zelnodes.status")); + columnNames.add(langUtil.getString("zelnodespanel.zelnodes.tier")); + // columnNames.add(langUtil.getString("zelnodespanel.zelnodes.version")); + columnNames.add(langUtil.getString("zelnodespanel.zelnodes.activetime")); + // columnNames.add(langUtil.getString("zelnodespanel.zelnodes.lastseen")); + // columnNames.add(langUtil.getString("zelnodespanel.zelnodes.laspaid")); + + zelNodesTable = new ZelCashJTable(dataList, columnNames); + zelNodesTable.setAutoCreateRowSorter(true); + + this.gelZelNodeList(); + zelNodesTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); + zelnodes.add(zelNodesTablePane = new ZelCashJScrollPane(zelNodesTable), + BorderLayout.CENTER); + + detailsPanel.add(zelnodes); + + zelNodesPanel.add(detailsPanel, BorderLayout.CENTER); + + // Build panel of buttons + ZelCashJPanel buttonPanel = new ZelCashJPanel(); + buttonPanel.setBorder( + BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); + collectZelNodeReward = new ZelCashJButton( + langUtil.getString("panel.zelnodespanel.button.colletReward")); + buttonPanel.add(collectZelNodeReward); + refresh = + new ZelCashJButton(langUtil.getString("panel.address.button.refresh")); + buttonPanel.add(refresh); + clearCache = new ZelCashJButton( + langUtil.getString("panel.address.button.clear.cache")); + buttonPanel.add(clearCache); + zelNodesPanel.add(buttonPanel, BorderLayout.SOUTH); + + refresh.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Cursor oldCursor = ZelNodesPanel.this.getCursor(); + try { + ZelNodesPanel.this.setCursor( + Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + refresh.setText( + langUtil.getString("zelnodespanel.zelnodes.button.loading")); + refresh.setEnabled(false); + refreshZelNodesTables(); + refresh.setText(langUtil.getString("panel.address.button.refresh")); + refresh.setEnabled(true); + + } finally { + ZelNodesPanel.this.setCursor(oldCursor); + } + } + }); + + clearCache.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Cursor oldCursor = ZelNodesPanel.this.getCursor(); + try { + ZelNodesPanel.this.setCursor( + Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + clearCache.setText( + langUtil.getString("zelnodespanel.zelnodes.button.loading")); + clearCache.setEnabled(false); + String blockchainDir = OSUtil.getBlockchainDirectory(); + File zelnodecache = + new File(blockchainDir + File.separator + "zelnodecache.dat"); + if (zelnodecache.exists()) { + zelnodecache.delete(); + ZelNodesPanel.this.parentFrame.restartDaemon(false, false); try { - String zAddressSelected = zAddresses[zAddressesCombo.getSelectedIndex()]; - // Check for encrypted wallet - final boolean bEncryptedWallet = this.clientCaller.isWalletEncrypted(); - if (bEncryptedWallet) - { - boolean passwordOk = false; - int retrys = 0; - while(!passwordOk && retrys<3) { - ++retrys; - PasswordDialog pd = new PasswordDialog((ZelCashJFrame)(ZelNodesPanel.this.getRootPane().getParent())); - pd.setVisible(true); - - if (!pd.isOKPressed()) - { - return; - } - try { - this.clientCaller.unlockWallet(pd.getPassword()); - passwordOk = true; - } - catch (Exception ex) { - Log.error("Error unlocking wallet:"+ex.getMessage()); - JOptionPane.showMessageDialog( - ZelNodesPanel.this.getRootPane().getParent(), - langUtil.getString("encryption.error.unlocking.message", ex.getMessage()), - langUtil.getString("encryption.error.unlocking.title"), - JOptionPane.ERROR_MESSAGE); - } - } - if(!passwordOk) { - Log.info("Failed to enter correct password for third time, wallet will close."); - System.exit(1); - } - } - JsonObject shieldResult = clientCaller.zShieldCoinBase(zAddressSelected, MAX_UTXO_TXN); - amountShielded = shieldResult.get("shieldingValue").toString(); + restartUI(); + } catch (IOException | InterruptedException | + WalletCallException e1) { + Log.error( + "Error restarting the UI, the wallet will be closed. Error:" + + e1.getMessage()); + System.exit(1); } - catch (Exception e) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("panel.zelnodespanel.shielding.error", e.getMessage()), - LanguageUtil.instance().getString("panel.zelnodespanel.shielding.error.title"), - JOptionPane.ERROR_MESSAGE); - throw e; - } - - String message = ""; - if(utxoRewardCount 0)) { + address = label + " - " + address; + } + comboBoxItems[i] = address; + } + + ZelCashJComboBox zAddressesCombo = new ZelCashJComboBox<>(comboBoxItems); + + ZelCashJPanel myPanel = new ZelCashJPanel(); + myPanel.setLayout(new BoxLayout(myPanel, BoxLayout.Y_AXIS)); + + ZelCashJPanel tempPanel = new ZelCashJPanel(new BorderLayout(0, 0)); + tempPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); + ZelCashJLabel infoLabel = new ZelCashJLabel( + langUtil.getString("panel.zelnodespanel.collect.zelnodereward.info", + new DecimalFormat("########0.00######") + .format(zelnodeRewardAvailable), + utxoRewardCount)); + tempPanel.add(infoLabel, BorderLayout.CENTER); + + ZelCashJPanel detailsPanel = new ZelCashJPanel(); + detailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.Y_AXIS)); + + zAddressesCombo.setPreferredSize( + new Dimension(600, zAddressesCombo.getPreferredSize().height)); + detailsPanel.add(zAddressesCombo, BorderLayout.CENTER); + + myPanel.add(tempPanel); + myPanel.add(detailsPanel); + + int result = JOptionPane.showConfirmDialog( + ZelNodesPanel.this, myPanel, + langUtil.getString( + "panel.zelnodespanel.collect.zelnodereward.info.title"), + JOptionPane.OK_CANCEL_OPTION, JOptionPane.CANCEL_OPTION); + if (result == JOptionPane.CANCEL_OPTION || + result == JOptionPane.CLOSED_OPTION) { + return; + } + + String amountShielded; + try { + String zAddressSelected = + zAddresses[zAddressesCombo.getSelectedIndex()]; + // Check for encrypted wallet + final boolean bEncryptedWallet = this.clientCaller.isWalletEncrypted(); + if (bEncryptedWallet) { + boolean passwordOk = false; + int retrys = 0; + while (!passwordOk && retrys < 3) { + ++retrys; + PasswordDialog pd = new PasswordDialog( + (ZelCashJFrame)(ZelNodesPanel.this.getRootPane().getParent())); + pd.setVisible(true); + + if (!pd.isOKPressed()) { + return; } - else { - message = LanguageUtil.instance().getString("panel.zelnodespanel.shielding.success.more", amountShielded, utxoRewardCount - MAX_UTXO_TXN); + try { + this.clientCaller.unlockWallet(pd.getPassword()); + passwordOk = true; + } catch (Exception ex) { + Log.error("Error unlocking wallet:" + ex.getMessage()); + JOptionPane.showMessageDialog( + ZelNodesPanel.this.getRootPane().getParent(), + langUtil.getString("encryption.error.unlocking.message", + ex.getMessage()), + langUtil.getString("encryption.error.unlocking.title"), + JOptionPane.ERROR_MESSAGE); } - JOptionPane.showMessageDialog(null, - message, - LanguageUtil.instance().getString("panel.zelnodespanel.shielding.success.title"), - JOptionPane.INFORMATION_MESSAGE); - - } catch (WalletCallException | IOException | InterruptedException e ) { - Log.error("Error on getZelNodesRewards:" +e.getMessage()); - } - - } - - private void refreshZelNodesTables() { - long start = System.currentTimeMillis(); - getMyZelNodeList(); - gelZelNodeList(); - ZelNodesPanel.this.revalidate(); - ZelNodesPanel.this.repaint(); - long end = System.currentTimeMillis(); - Log.info("refresh ZelNodesTables data done in " + (end - start) + "ms." ); - - } - - private void getMyZelNodeList() { - long start = System.currentTimeMillis(); - ZelCashJTable table = null; - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); - String format = null; - Vector> dataList = new Vector<>(); - int myZelNodeCount = 0; - - - DefaultTableModel dtm = (DefaultTableModel) myZelNodesTable.getModel(); - - try { - Vector data; - dtm.setRowCount(0); - JsonArray ja = clientCaller.getMyZelNodeList(); - //Log.debug(ja.toString()); - //Log.debug(""+ja.size()); - if(ja.size()>0) { - for(int i = 0; i < ja.size(); i++) { - JsonObject jsonObj = ja.get(i).asObject(); - Log.debug(jsonObj.toString()); - ++myZelNodeCount; - data = new Vector<>(); - data.add(jsonObj.get("alias").toString().replaceAll("[\n\r\"]", "")); - data.add(jsonObj.get("address").toString().replaceAll("[\n\r\"]", "")); - data.add(jsonObj.get("status").toString().replaceAll("[\n\r\"]", "")); - data.add(jsonObj.get("tier").toString().replaceAll("[\n\r\"]", "")); - String activeTime = jsonObj.get("activesince").toString().replaceAll("[\n\r\"]", ""); - if(activeTime == null || activeTime.equals("") || activeTime.equals("0")) { - data.add("-1"); - } - else { - Date aux = new Date(Long.parseLong(activeTime) * 1000); - format = formatter.format(aux); - data.add(format); - } - String lastPaid = jsonObj.get("lastpaid").toString().replaceAll("[\n\r\"]", ""); - if(lastPaid == null || lastPaid.equals("") || lastPaid.equals("0")) { - data.add("-1"); - } - else { - Date aux = new Date(Long.parseLong(lastPaid) * 1000); - format = formatter.format(aux); - data.add(format); - } - dtm.addRow(data); - dataList.add(data); - } - } - } - catch (WalletCallException | IOException | InterruptedException e1) { - Log.error("Error obtaining myzelnodelist. Error:" + e1.getMessage()); - return; - } - finally { - dtm.fireTableDataChanged(); - } - - - myzelnodescount.setText(langUtil.getString("zelnodespanel.myzelnodes.count",dataList.size())); - - popupMenu = new ZelCashJPopupMenu(); - int accelaratorKeyMask = Toolkit.getDefaultToolkit ().getMenuShortcutKeyMask(); - - ZelCashJMenuItem startZelnode = new ZelCashJMenuItem(langUtil.getString("zelnodespanel.myzelnodes.mouse.start")); - popupMenu.add(startZelnode); - - startZelnode.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, accelaratorKeyMask)); - startZelnode.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - int row = ZelNodesPanel.this.myZelNodesTable.getSelectedRow(); - String zelNodeALias = ZelNodesPanel.this.myZelNodesTable.getValueAt(row, 0).toString(); - - try { - long start = System.currentTimeMillis(); - JsonObject response = clientCaller.startZelNode(zelNodeALias); - long end = System.currentTimeMillis(); - Log.info("Start zelnodealias: "+zelNodeALias+" done in " + (end - start) + "ms."); - JsonArray detailResponse = response.get("detail").asArray(); - JsonObject jsonObj = detailResponse.get(0).asObject(); - String result = jsonObj.get("result").toString().toUpperCase().replaceAll("[\n\r\"]", ""); - if(result.contains("FAILED")) { - String error = jsonObj.get("errorMessage").toString().replaceAll("[\n\r\"]", ""); - JOptionPane.showMessageDialog( - null, - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.start.error", zelNodeALias, error), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.start.title"), - JOptionPane.ERROR_MESSAGE); - } - else { - JOptionPane.showMessageDialog( - null, - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.start.success", zelNodeALias), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.start.title"), - JOptionPane.INFORMATION_MESSAGE); - } - getMyZelNodeList(); - ZelNodesPanel.this.revalidate(); - ZelNodesPanel.this.repaint(); - } catch (WalletCallException | IOException | InterruptedException e1) { - Log.error("Error calling startZelNode: "+e1.getMessage()); - } - } - }); - - ZelCashJMenuItem edit = new ZelCashJMenuItem(langUtil.getString("zelnodespanel.myzelnodes.mouse.edit")); - popupMenu.add(edit); - - edit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, accelaratorKeyMask)); - edit.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - int row = ZelNodesPanel.this.myZelNodesTable.getSelectedRow(); - String zelNodeAlias = ZelNodesPanel.this.myZelNodesTable.getValueAt(row, 0).toString(); - try { - ZelCashZelNodeDialog ad = new ZelCashZelNodeDialog(ZelNodesPanel.this.parentFrame, clientCaller, installationObserver, zelNodeAlias, labelStorage); - ad.setVisible(true); - getMyZelNodeList(); - ZelNodesPanel.this.revalidate(); - ZelNodesPanel.this.repaint(); - } catch (Exception uee) { - Log.error("Unexpected error: ", uee); - } - - } - }); - - ZelCashJMenuItem delete = new ZelCashJMenuItem(langUtil.getString("zelnodespanel.myzelnodes.mouse.delete")); - popupMenu.add(delete); - - delete.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, accelaratorKeyMask)); - delete.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - int row = ZelNodesPanel.this.myZelNodesTable.getSelectedRow(); - String zelNodeAlias = ZelNodesPanel.this.myZelNodesTable.getValueAt(lastRow, 0).toString(); - Object[] options = - { - langUtil.getString("send.cash.panel.option.pane.confirm.operation.button.yes"), - langUtil.getString("send.cash.panel.option.pane.confirm.operation.button.no") - }; - int option; - option = JOptionPane.showOptionDialog( - ZelNodesPanel.this.getRootPane().getParent(), - langUtil.getString("dialog.zelcashnewzelnode.delete.message", - zelNodeAlias), - langUtil.getString("zelnodespanel.myzelnodes.mouse.delete"), - JOptionPane.DEFAULT_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - options[1]); - - if (option == 0) - { - ZelCashZelNodeDialog.removeZelNode(zelNodeAlias); - getMyZelNodeList(); - ZelNodesPanel.this.revalidate(); - ZelNodesPanel.this.repaint(); - option = JOptionPane.showOptionDialog(null, - LanguageUtil.instance().getString("wallet.zelnodes.delete.restart.message"), - LanguageUtil.instance().getString("wallet.zelnodes.delete.restart.title"), - JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); - if (option == 0) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("wallet.restart.message"), - LanguageUtil.instance().getString("wallet.reindex.restart.title"), - JOptionPane.INFORMATION_MESSAGE); - ZelNodesPanel.this.parentFrame.restartDaemon(false, false); - try { - restartUI(); - } catch (IOException | InterruptedException | WalletCallException e1) { - Log.error("Error restarting the UI, the wallet will be closed. Error:"+e1.getMessage()); - System.exit(1); - } - } - } - } - }); - - - ZelNodesPanel.this.myZelNodesTable.addMouseListener(new MouseAdapter() - { - public void mousePressed(MouseEvent e) - { - if ((!e.isConsumed()) && e.isPopupTrigger()) - { - ZelCashJTable table = (ZelCashJTable)e.getSource(); - lastColumn = table.columnAtPoint(e.getPoint()); - lastRow = table.rowAtPoint(e.getPoint()); - - if (!table.isRowSelected(lastRow)) - { - table.changeSelection(lastRow, lastColumn, false, false); - } - - popupMenu.show(e.getComponent(), e.getPoint().x, e.getPoint().y); - e.consume(); - - } else - { - lastColumn = -1; - lastRow = -1; - } - } - - public void mouseReleased(MouseEvent e) - { - if ((!e.isConsumed()) && e.isPopupTrigger()) - { - mousePressed(e); - } + } + if (!passwordOk) { + Log.info( + "Failed to enter correct password for third time, wallet will close."); + System.exit(1); + } + } + JsonObject shieldResult = + clientCaller.zShieldCoinBase(zAddressSelected, MAX_UTXO_TXN); + amountShielded = shieldResult.get("shieldingValue").toString(); + } catch (Exception e) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "panel.zelnodespanel.shielding.error", e.getMessage()), + LanguageUtil.instance().getString( + "panel.zelnodespanel.shielding.error.title"), + JOptionPane.ERROR_MESSAGE); + throw e; + } + + String message = ""; + if (utxoRewardCount < MAX_UTXO_TXN) { + message = LanguageUtil.instance().getString( + "panel.zelnodespanel.shielding.success"); + } else { + message = LanguageUtil.instance().getString( + "panel.zelnodespanel.shielding.success.more", amountShielded, + utxoRewardCount - MAX_UTXO_TXN); + } + JOptionPane.showMessageDialog( + null, message, + LanguageUtil.instance().getString( + "panel.zelnodespanel.shielding.success.title"), + JOptionPane.INFORMATION_MESSAGE); + + } catch (WalletCallException | IOException | InterruptedException e) { + Log.error("Error on getZelNodesRewards:" + e.getMessage()); + } + } + + private void refreshZelNodesTables() { + long start = System.currentTimeMillis(); + getMyZelNodeList(); + gelZelNodeList(); + ZelNodesPanel.this.revalidate(); + ZelNodesPanel.this.repaint(); + long end = System.currentTimeMillis(); + Log.info("refresh ZelNodesTables data done in " + (end - start) + "ms."); + } + + private void getMyZelNodeList() { + long start = System.currentTimeMillis(); + ZelCashJTable table = null; + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + String format = null; + Vector> dataList = new Vector<>(); + int myZelNodeCount = 0; + + DefaultTableModel dtm = (DefaultTableModel)myZelNodesTable.getModel(); + + try { + Vector data; + dtm.setRowCount(0); + JsonArray ja = clientCaller.getMyZelNodeList(); + // Log.debug(ja.toString()); + // Log.debug(""+ja.size()); + if (ja.size() > 0) { + for (int i = 0; i < ja.size(); i++) { + JsonObject jsonObj = ja.get(i).asObject(); + Log.debug(jsonObj.toString()); + ++myZelNodeCount; + data = new Vector<>(); + data.add(jsonObj.get("alias").toString().replaceAll("[\n\r\"]", "")); + data.add( + jsonObj.get("address").toString().replaceAll("[\n\r\"]", "")); + data.add(jsonObj.get("status").toString().replaceAll("[\n\r\"]", "")); + data.add(jsonObj.get("tier").toString().replaceAll("[\n\r\"]", "")); + String activeTime = + jsonObj.get("activesince").toString().replaceAll("[\n\r\"]", ""); + if (activeTime == null || activeTime.equals("") || + activeTime.equals("0")) { + data.add("-1"); + } else { + Date aux = new Date(Long.parseLong(activeTime) * 1000); + format = formatter.format(aux); + data.add(format); + } + String lastPaid = + jsonObj.get("lastpaid").toString().replaceAll("[\n\r\"]", ""); + if (lastPaid == null || lastPaid.equals("") || lastPaid.equals("0")) { + data.add("-1"); + } else { + Date aux = new Date(Long.parseLong(lastPaid) * 1000); + format = formatter.format(aux); + data.add(format); + } + dtm.addRow(data); + dataList.add(data); + } + } + } catch (WalletCallException | IOException | InterruptedException e1) { + Log.error("Error obtaining myzelnodelist. Error:" + e1.getMessage()); + return; + } finally { + dtm.fireTableDataChanged(); + } + + myzelnodescount.setText( + langUtil.getString("zelnodespanel.myzelnodes.count", dataList.size())); + + popupMenu = new ZelCashJPopupMenu(); + int accelaratorKeyMask = + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + ZelCashJMenuItem startZelnode = new ZelCashJMenuItem( + langUtil.getString("zelnodespanel.myzelnodes.mouse.start")); + popupMenu.add(startZelnode); + + startZelnode.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_S, accelaratorKeyMask)); + startZelnode.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int row = ZelNodesPanel.this.myZelNodesTable.getSelectedRow(); + String zelNodeALias = + ZelNodesPanel.this.myZelNodesTable.getValueAt(row, 0).toString(); + + try { + long start = System.currentTimeMillis(); + JsonObject response = clientCaller.startZelNode(zelNodeALias); + long end = System.currentTimeMillis(); + Log.info("Start zelnodealias: " + zelNodeALias + " done in " + + (end - start) + "ms."); + JsonArray detailResponse = response.get("detail").asArray(); + JsonObject jsonObj = detailResponse.get(0).asObject(); + String result = + jsonObj.get("result").toString().toUpperCase().replaceAll( + "[\n\r\"]", ""); + if (result.contains("FAILED")) { + String error = jsonObj.get("errorMessage") + .toString() + .replaceAll("[\n\r\"]", ""); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.start.error", zelNodeALias, + error), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.start.title"), + JOptionPane.ERROR_MESSAGE); + } else { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.start.success", zelNodeALias), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.start.title"), + JOptionPane.INFORMATION_MESSAGE); + } + getMyZelNodeList(); + ZelNodesPanel.this.revalidate(); + ZelNodesPanel.this.repaint(); + } catch (WalletCallException | IOException | InterruptedException e1) { + Log.error("Error calling startZelNode: " + e1.getMessage()); + } + } + }); + + ZelCashJMenuItem edit = new ZelCashJMenuItem( + langUtil.getString("zelnodespanel.myzelnodes.mouse.edit")); + popupMenu.add(edit); + + edit.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_E, accelaratorKeyMask)); + edit.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int row = ZelNodesPanel.this.myZelNodesTable.getSelectedRow(); + String zelNodeAlias = + ZelNodesPanel.this.myZelNodesTable.getValueAt(row, 0).toString(); + try { + ZelCashZelNodeDialog ad = new ZelCashZelNodeDialog( + ZelNodesPanel.this.parentFrame, clientCaller, + installationObserver, zelNodeAlias, labelStorage); + ad.setVisible(true); + getMyZelNodeList(); + ZelNodesPanel.this.revalidate(); + ZelNodesPanel.this.repaint(); + } catch (Exception uee) { + Log.error("Unexpected error: ", uee); + } + } + }); + + ZelCashJMenuItem delete = new ZelCashJMenuItem( + langUtil.getString("zelnodespanel.myzelnodes.mouse.delete")); + popupMenu.add(delete); + + delete.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_D, accelaratorKeyMask)); + delete.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int row = ZelNodesPanel.this.myZelNodesTable.getSelectedRow(); + String zelNodeAlias = + ZelNodesPanel.this.myZelNodesTable.getValueAt(lastRow, 0) + .toString(); + Object[] options = { + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.button.yes"), + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.button.no")}; + int option; + option = JOptionPane.showOptionDialog( + ZelNodesPanel.this.getRootPane().getParent(), + langUtil.getString("dialog.zelcashnewzelnode.delete.message", + zelNodeAlias), + langUtil.getString("zelnodespanel.myzelnodes.mouse.delete"), + JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, + options, options[1]); + + if (option == 0) { + ZelCashZelNodeDialog.removeZelNode(zelNodeAlias); + getMyZelNodeList(); + ZelNodesPanel.this.revalidate(); + ZelNodesPanel.this.repaint(); + option = JOptionPane.showOptionDialog( + null, + LanguageUtil.instance().getString( + "wallet.zelnodes.delete.restart.message"), + LanguageUtil.instance().getString( + "wallet.zelnodes.delete.restart.title"), + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, + options, options[0]); + if (option == 0) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString("wallet.restart.message"), + LanguageUtil.instance().getString( + "wallet.reindex.restart.title"), + JOptionPane.INFORMATION_MESSAGE); + ZelNodesPanel.this.parentFrame.restartDaemon(false, false); + try { + restartUI(); + } catch (IOException | InterruptedException | + WalletCallException e1) { + Log.error( + "Error restarting the UI, the wallet will be closed. Error:" + + e1.getMessage()); + System.exit(1); } - }); - - long end = System.currentTimeMillis(); - Log.info("refresh MyZelNodeList data done in " + (end - start) + "ms. myZelNodeCount:"+myZelNodeCount); - - } - - private void gelZelNodeList() { - long start = System.currentTimeMillis(); - ZelCashJTable table = null; - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); - String format = null; - DefaultTableModel dtm = (DefaultTableModel) zelNodesTable.getModel(); - int basicCount=0; - int superCount=0; - int bamfCount=0; - int totalNodes=0; - String tier; - try { - JsonArray ja = clientCaller.getZelNodeList(); - Vector> dataList = new Vector<>(); - dtm.setRowCount(0); - for (int i = 0; i < ja.size(); i++) { - - JsonObject jsonObj = ja.get(i).asObject(); - Vector data = new Vector<>(); - - //data.add(jsonObj.get("rank").toString().replaceAll("[\n\r\"]", "")); - data.add(jsonObj.get("ip").toString().replaceAll("[\n\r\"]", "")); - - data.add("CONFIRMED"); - //data.add(jsonObj.get("version").toString().replaceAll("[\n\r\"]", "")); - - tier = jsonObj.get("tier").toString().replaceAll("[\n\r\"]", ""); - data.add(tier); - - String activeTime = jsonObj.get("activesince").toString().replaceAll("[\n\r\"]", ""); - if(activeTime == null || activeTime.equals("") || activeTime.equals("0")) { - data.add("-1"); - } - else { - Date aux = new Date(Long.parseLong(activeTime) * 1000); - format = formatter.format(aux); - data.add(format); - } - - /*String lastpaid = jsonObj.get("lastpaid").toString().replaceAll("[\n\r\"]", ""); - if(lastpaid == null || lastpaid == "" || lastpaid == "0") { - data.add("-1"); - } - else { - Date aux = new Date(Long.parseLong(lastpaid) * 1000); - format = formatter.format(aux); - data.add(format); - }*/ - - dataList.add(data); - dtm.addRow(data); - if("basic".equals(tier.toLowerCase())) { - ++basicCount; - } - else if("super".equals(tier.toLowerCase())) { - ++superCount; - } - else { - ++bamfCount; - } - } - totalNodes = dataList.size(); - zelnodescount.setText(langUtil.getString("zelnodespanel.zelnodes.count",totalNodes, basicCount, superCount, bamfCount)); - - } catch (WalletCallException | IOException | InterruptedException e1) { - Log.error("Error obtaining zelNodeList. " + e1.getMessage()); - } - finally { - dtm.fireTableDataChanged(); - long end = System.currentTimeMillis(); - Log.info("refresh ZelNodeList data done in " + (end - start) + "ms. count:" + totalNodes); - } - } - - private void addFormField(ZelCashJPanel detailsPanel, String name, JComponent field) - { - ZelCashJPanel tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 4, 2)); - ZelCashJLabel tempLabel = new ZelCashJLabel(name, JLabel.RIGHT); - // TODO: hard sizing of labels may not scale! - final int width = new ZelCashJLabel("Sender identification T address:").getPreferredSize().width + 10; - tempLabel.setPreferredSize(new Dimension(width, tempLabel.getPreferredSize().height)); - tempPanel.add(tempLabel); - tempPanel.add(field); - detailsPanel.add(tempPanel); - } - - public void restartUI() throws IOException, InterruptedException, WalletCallException { - Log.info("Restarting the UI."); - ZCashUI z = new ZCashUI(null); - this.parentFrame.setVisible(false); - this.parentFrame.dispose(); - this.parentFrame = z; - this.parentFrame.repaint(); - this.parentFrame.setVisible(true); - this.setVisible(false); - } + } + } + } + }); + + ZelNodesPanel.this.myZelNodesTable.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + if ((!e.isConsumed()) && e.isPopupTrigger()) { + ZelCashJTable table = (ZelCashJTable)e.getSource(); + lastColumn = table.columnAtPoint(e.getPoint()); + lastRow = table.rowAtPoint(e.getPoint()); + + if (!table.isRowSelected(lastRow)) { + table.changeSelection(lastRow, lastColumn, false, false); + } + + popupMenu.show(e.getComponent(), e.getPoint().x, e.getPoint().y); + e.consume(); + + } else { + lastColumn = -1; + lastRow = -1; + } + } + + public void mouseReleased(MouseEvent e) { + if ((!e.isConsumed()) && e.isPopupTrigger()) { + mousePressed(e); + } + } + }); + + long end = System.currentTimeMillis(); + Log.info("refresh MyZelNodeList data done in " + (end - start) + + "ms. myZelNodeCount:" + myZelNodeCount); + } + + private void gelZelNodeList() { + long start = System.currentTimeMillis(); + ZelCashJTable table = null; + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + String format = null; + DefaultTableModel dtm = (DefaultTableModel)zelNodesTable.getModel(); + int basicCount = 0; + int superCount = 0; + int bamfCount = 0; + int totalNodes = 0; + String tier; + try { + JsonArray ja = clientCaller.getZelNodeList(); + Vector> dataList = new Vector<>(); + dtm.setRowCount(0); + for (int i = 0; i < ja.size(); i++) { + + JsonObject jsonObj = ja.get(i).asObject(); + Vector data = new Vector<>(); + + // data.add(jsonObj.get("rank").toString().replaceAll("[\n\r\"]", "")); + data.add(jsonObj.get("ip").toString().replaceAll("[\n\r\"]", "")); + + data.add("CONFIRMED"); + // data.add(jsonObj.get("version").toString().replaceAll("[\n\r\"]", + // "")); + + tier = jsonObj.get("tier").toString().replaceAll("[\n\r\"]", ""); + data.add(tier); + + String activeTime = + jsonObj.get("activesince").toString().replaceAll("[\n\r\"]", ""); + if (activeTime == null || activeTime.equals("") || + activeTime.equals("0")) { + data.add("-1"); + } else { + Date aux = new Date(Long.parseLong(activeTime) * 1000); + format = formatter.format(aux); + data.add(format); + } + + /*String lastpaid = + jsonObj.get("lastpaid").toString().replaceAll("[\n\r\"]", ""); + if(lastpaid == null || lastpaid == "" || lastpaid == "0") { + data.add("-1"); + } + else { + Date aux = new Date(Long.parseLong(lastpaid) * 1000); + format = formatter.format(aux); + data.add(format); + }*/ + + dataList.add(data); + dtm.addRow(data); + if ("basic".equals(tier.toLowerCase())) { + ++basicCount; + } else if ("super".equals(tier.toLowerCase())) { + ++superCount; + } else { + ++bamfCount; + } + } + totalNodes = dataList.size(); + zelnodescount.setText(langUtil.getString("zelnodespanel.zelnodes.count", + totalNodes, basicCount, + superCount, bamfCount)); + + } catch (WalletCallException | IOException | InterruptedException e1) { + Log.error("Error obtaining zelNodeList. " + e1.getMessage()); + } finally { + dtm.fireTableDataChanged(); + long end = System.currentTimeMillis(); + Log.info("refresh ZelNodeList data done in " + (end - start) + + "ms. count:" + totalNodes); + } + } + + private void addFormField(ZelCashJPanel detailsPanel, String name, + JComponent field) { + ZelCashJPanel tempPanel = + new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 4, 2)); + ZelCashJLabel tempLabel = new ZelCashJLabel(name, JLabel.RIGHT); + // TODO: hard sizing of labels may not scale! + final int width = new ZelCashJLabel("Sender identification T address:") + .getPreferredSize() + .width + + 10; + tempLabel.setPreferredSize( + new Dimension(width, tempLabel.getPreferredSize().height)); + tempPanel.add(tempLabel); + tempPanel.add(field); + detailsPanel.add(tempPanel); + } + public void restartUI() + throws IOException, InterruptedException, WalletCallException { + Log.info("Restarting the UI."); + ZCashUI z = new ZCashUI(null); + this.parentFrame.setVisible(false); + this.parentFrame.dispose(); + this.parentFrame = z; + this.parentFrame.repaint(); + this.parentFrame.setVisible(true); + this.setVisible(false); + } } diff --git a/src/java/com/cabecinha84/zelcashui/ZelcashRescanDialog.java b/src/java/com/cabecinha84/zelcashui/ZelcashRescanDialog.java index 478c32e6..c940b731 100644 --- a/src/java/com/cabecinha84/zelcashui/ZelcashRescanDialog.java +++ b/src/java/com/cabecinha84/zelcashui/ZelcashRescanDialog.java @@ -1,4 +1,4 @@ -/************************************************************************************************ +/************************************************************************************************ * Copyright (c) 2019 The ZelCash Developers * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -21,19 +21,6 @@ **********************************************************************************/ package com.cabecinha84.zelcashui; - -import java.awt.BorderLayout; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; - -import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.JOptionPane; - import com.cabecinha84.zelcashui.ZelCashJButton; import com.cabecinha84.zelcashui.ZelCashJDialog; import com.cabecinha84.zelcashui.ZelCashJFrame; @@ -47,183 +34,183 @@ import com.vaklinov.zcashui.Log; import com.vaklinov.zcashui.Util; import com.vaklinov.zcashui.ZCashClientCaller; -import com.vaklinov.zcashui.ZCashUI; import com.vaklinov.zcashui.ZCashClientCaller.WalletCallException; - +import com.vaklinov.zcashui.ZCashUI; +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JOptionPane; /** * Dialog to enter a single private key to import */ -public class ZelcashRescanDialog - extends ZelCashJDialog -{ - protected boolean isOKPressed = false; - - private static final int POLL_PERIOD = 5000; - private static final int STARTUP_ERROR_CODE = -28; - - protected ZelCashJLabel upperLabel; - protected ZelCashJLabel lowerLabel; - - protected ZelCashJProgressBar progress = null; - - protected ZCashClientCaller caller; - - private LanguageUtil langUtil; - - ZelCashJButton okButon; - ZelCashJButton cancelButon; - private ZCashUI parent; - - public ZelcashRescanDialog(ZCashUI parent, ZCashClientCaller caller) - { - super(parent); - this.parent = parent; - this.caller = caller; - langUtil = LanguageUtil.instance(); - this.setTitle(langUtil.getString("wallet.operations.dialog.rescan.title")); - this.setLocation(parent.getLocation().x + 50, parent.getLocation().y + 50); - this.setModal(true); - this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); - - ZelCashJPanel controlsPanel = new ZelCashJPanel(); - controlsPanel.setLayout(new BoxLayout(controlsPanel, BoxLayout.Y_AXIS)); - controlsPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); - - ZelCashJPanel tempPanel = new ZelCashJPanel(new BorderLayout(0, 0)); - tempPanel.add(this.upperLabel = new ZelCashJLabel( - langUtil.getString("wallet.operations.dialog.rescan.message")), - BorderLayout.CENTER); - controlsPanel.add(tempPanel); - - ZelCashJLabel dividerLabel = new ZelCashJLabel(" "); - dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 8)); - controlsPanel.add(dividerLabel); - - tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - tempPanel.add(progress = new ZelCashJProgressBar()); - controlsPanel.add(tempPanel); - - this.getContentPane().setLayout(new BorderLayout(0, 0)); - this.getContentPane().add(controlsPanel, BorderLayout.NORTH); - - // Form buttons - ZelCashJPanel buttonPanel = new ZelCashJPanel(); - buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 3, 3)); - okButon = new ZelCashJButton(langUtil.getString("wallet.operations.dialog.rescan.title")); - buttonPanel.add(okButon); - buttonPanel.add(new ZelCashJLabel(" ")); - cancelButon = new ZelCashJButton(langUtil.getString("single.key.import.dialog.tmp.panel.cancel.button.text")); - buttonPanel.add(cancelButon); - this.getContentPane().add(buttonPanel, BorderLayout.SOUTH); - - okButon.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - ZelcashRescanDialog.this.processOK(); - } - }); - - cancelButon.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - ZelcashRescanDialog.this.setVisible(false); - ZelcashRescanDialog.this.dispose(); - - ZelcashRescanDialog.this.isOKPressed = false; - } - }); - - this.setSize(740, 210); - this.validate(); - this.repaint(); - - this.pack(); - } - - - protected void processOK() - { - ZelcashRescanDialog.this.isOKPressed = true; - - // Start import - this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); - this.progress.setIndeterminate(true); - this.progress.setValue(1); - - this.okButon.setEnabled(false); - this.cancelButon.setEnabled(false); - - new Thread(new Runnable() - { - @Override - public void run() - { - parent.restartDaemon(false, true); - try { - restartAfterRescan(); - } catch (IOException | InterruptedException | InvocationTargetException | WalletCallException e1) { - Log.error("Error restarting the UI, the wallet will be closed. Error:"+e1.getMessage()); - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.2.text", - e1.getMessage()), - LanguageUtil.instance() - .getString("main.frame.option.pane.wallet.critical.error.2.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(1); - } finally - { - ZelcashRescanDialog.this.setVisible(false); - ZelcashRescanDialog.this.dispose(); - } - } - }).start(); - } - - - public boolean isOKPressed() - { - return this.isOKPressed; - } - - - public void restartAfterRescan() throws IOException, InterruptedException, WalletCallException, InvocationTargetException { - Log.info("Waiting for rescan complete."); - while(true) { - Thread.sleep(POLL_PERIOD); - - JsonObject info = null; - - try - { - info = caller.getDaemonRawRuntimeInfo(); - } catch (IOException e) - { - throw e; - } - - JsonValue code = info.get("code"); - Log.debug("clientCaller:"+info.toString()); - if (code == null || (code.asInt() != STARTUP_ERROR_CODE)) - break; - } - Log.info("Rescan complete."); - - JOptionPane.showMessageDialog(this.parent, - langUtil.getString("wallet.operations.dialog.rescan.complete.message"), - langUtil.getString("wallet.operations.dialog.rescan.complete.title"), - JOptionPane.INFORMATION_MESSAGE); - - this.parent.setVisible(false); - this.parent.dispose(); - - ZCashUI z = new ZCashUI(null); - this.parent = z; - this.parent.repaint(); - this.parent.setVisible(true); - } +public class ZelcashRescanDialog extends ZelCashJDialog { + protected boolean isOKPressed = false; + + private static final int POLL_PERIOD = 5000; + private static final int STARTUP_ERROR_CODE = -28; + + protected ZelCashJLabel upperLabel; + protected ZelCashJLabel lowerLabel; + + protected ZelCashJProgressBar progress = null; + + protected ZCashClientCaller caller; + + private LanguageUtil langUtil; + + ZelCashJButton okButon; + ZelCashJButton cancelButon; + private ZCashUI parent; + + public ZelcashRescanDialog(ZCashUI parent, ZCashClientCaller caller) { + super(parent); + this.parent = parent; + this.caller = caller; + langUtil = LanguageUtil.instance(); + this.setTitle(langUtil.getString("wallet.operations.dialog.rescan.title")); + this.setLocation(parent.getLocation().x + 50, parent.getLocation().y + 50); + this.setModal(true); + this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); + + ZelCashJPanel controlsPanel = new ZelCashJPanel(); + controlsPanel.setLayout(new BoxLayout(controlsPanel, BoxLayout.Y_AXIS)); + controlsPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); + + ZelCashJPanel tempPanel = new ZelCashJPanel(new BorderLayout(0, 0)); + tempPanel.add(this.upperLabel = new ZelCashJLabel(langUtil.getString( + "wallet.operations.dialog.rescan.message")), + BorderLayout.CENTER); + controlsPanel.add(tempPanel); + + ZelCashJLabel dividerLabel = new ZelCashJLabel(" "); + dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 8)); + controlsPanel.add(dividerLabel); + + tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + tempPanel.add(progress = new ZelCashJProgressBar()); + controlsPanel.add(tempPanel); + + this.getContentPane().setLayout(new BorderLayout(0, 0)); + this.getContentPane().add(controlsPanel, BorderLayout.NORTH); + + // Form buttons + ZelCashJPanel buttonPanel = new ZelCashJPanel(); + buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 3, 3)); + okButon = new ZelCashJButton( + langUtil.getString("wallet.operations.dialog.rescan.title")); + buttonPanel.add(okButon); + buttonPanel.add(new ZelCashJLabel(" ")); + cancelButon = new ZelCashJButton(langUtil.getString( + "single.key.import.dialog.tmp.panel.cancel.button.text")); + buttonPanel.add(cancelButon); + this.getContentPane().add(buttonPanel, BorderLayout.SOUTH); + + okButon.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZelcashRescanDialog.this.processOK(); + } + }); + + cancelButon.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZelcashRescanDialog.this.setVisible(false); + ZelcashRescanDialog.this.dispose(); + + ZelcashRescanDialog.this.isOKPressed = false; + } + }); + + this.setSize(740, 210); + this.validate(); + this.repaint(); + + this.pack(); + } + + protected void processOK() { + ZelcashRescanDialog.this.isOKPressed = true; + + // Start import + this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + this.progress.setIndeterminate(true); + this.progress.setValue(1); + + this.okButon.setEnabled(false); + this.cancelButon.setEnabled(false); + + new Thread(new Runnable() { + @Override + public void run() { + parent.restartDaemon(false, true); + try { + restartAfterRescan(); + } catch (IOException | InterruptedException | + InvocationTargetException | WalletCallException e1) { + Log.error( + "Error restarting the UI, the wallet will be closed. Error:" + + e1.getMessage()); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.2.text", + e1.getMessage()), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.2.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(1); + } finally { + ZelcashRescanDialog.this.setVisible(false); + ZelcashRescanDialog.this.dispose(); + } + } + }).start(); + } + + public boolean isOKPressed() { return this.isOKPressed; } + + public void restartAfterRescan() throws IOException, InterruptedException, + WalletCallException, + InvocationTargetException { + Log.info("Waiting for rescan complete."); + while (true) { + Thread.sleep(POLL_PERIOD); + + JsonObject info = null; + + try { + info = caller.getDaemonRawRuntimeInfo(); + } catch (IOException e) { + throw e; + } + + JsonValue code = info.get("code"); + Log.debug("clientCaller:" + info.toString()); + if (code == null || (code.asInt() != STARTUP_ERROR_CODE)) + break; + } + Log.info("Rescan complete."); + + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString("wallet.operations.dialog.rescan.complete.message"), + langUtil.getString("wallet.operations.dialog.rescan.complete.title"), + JOptionPane.INFORMATION_MESSAGE); + + this.parent.setVisible(false); + this.parent.dispose(); + + ZCashUI z = new ZCashUI(null); + this.parent = z; + this.parent.repaint(); + this.parent.setVisible(true); + } } diff --git a/src/java/com/vaklinov/zcashui/SendCashPanel.java b/src/java/com/vaklinov/zcashui/SendCashPanel.java index 9bb37bd3..f736f2d3 100644 --- a/src/java/com/vaklinov/zcashui/SendCashPanel.java +++ b/src/java/com/vaklinov/zcashui/SendCashPanel.java @@ -1,11 +1,14 @@ /************************************************************************************************ - * ____________ _ _ _____ _ _____ _ _ _______ __ _ _ _ - * |___ / ____| \ | |/ ____| | | / ____| | | |_ _\ \ / / | | | | | - * / /| |__ | \| | | __ _ ___| |__ | | __| | | | | | \ \ /\ / /_ _| | | ___| |_ - * / / | __| | . ` | | / _` / __| '_ \| | |_ | | | | | | \ \/ \/ / _` | | |/ _ \ __| - * / /__| |____| |\ | |___| (_| \__ \ | | | |__| | |__| |_| |_ \ /\ / (_| | | | __/ |_ - * /_____|______|_| \_|\_____\__,_|___/_| |_|\_____|\____/|_____| \/ \/ \__,_|_|_|\___|\__| - * + * ____________ _ _ _____ _ _____ _ _ _______ __ + *_ _ _ + * |___ / ____| \ | |/ ____| | | / ____| | | |_ _\ \ / / + *| | | | | / /| |__ | \| | | __ _ ___| |__ | | __| | | | | | \ \ + * /\ / /_ _| | | ___| |_ / / | __| | . ` | | / _` / __| '_ \| | |_ | | | + *| | | \ \/ \/ / _` | | |/ _ \ __| / /__| |____| |\ | |___| (_| \__ \ | | | + *|__| | |__| |_| |_ \ /\ / (_| | | | __/ |_ + * /_____|______|_| \_|\_____\__,_|___/_| |_|\_____|\____/|_____| \/ \/ + *\__,_|_|_|\___|\__| + * * Copyright (c) 2016-2018 The ZEN Developers * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -28,7 +31,17 @@ **********************************************************************************/ package com.vaklinov.zcashui; - +import com.cabecinha84.zelcashui.ZelCashJButton; +import com.cabecinha84.zelcashui.ZelCashJCheckBox; +import com.cabecinha84.zelcashui.ZelCashJComboBox; +import com.cabecinha84.zelcashui.ZelCashJFrame; +import com.cabecinha84.zelcashui.ZelCashJLabel; +import com.cabecinha84.zelcashui.ZelCashJMenuItem; +import com.cabecinha84.zelcashui.ZelCashJPanel; +import com.cabecinha84.zelcashui.ZelCashJPopupMenu; +import com.cabecinha84.zelcashui.ZelCashJProgressBar; +import com.cabecinha84.zelcashui.ZelCashJTextField; +import com.vaklinov.zcashui.ZCashClientCaller.WalletCallException; import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.Desktop; @@ -54,7 +67,6 @@ import java.util.HashSet; import java.util.Locale; import java.util.Set; - import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JOptionPane; @@ -62,996 +74,956 @@ import javax.swing.Timer; import javax.swing.border.EtchedBorder; -import com.cabecinha84.zelcashui.ZelCashJButton; -import com.cabecinha84.zelcashui.ZelCashJCheckBox; -import com.cabecinha84.zelcashui.ZelCashJComboBox; -import com.cabecinha84.zelcashui.ZelCashJFrame; -import com.cabecinha84.zelcashui.ZelCashJLabel; -import com.cabecinha84.zelcashui.ZelCashJMenuItem; -import com.cabecinha84.zelcashui.ZelCashJPanel; -import com.cabecinha84.zelcashui.ZelCashJPopupMenu; -import com.cabecinha84.zelcashui.ZelCashJProgressBar; -import com.cabecinha84.zelcashui.ZelCashJTextField; -import com.vaklinov.zcashui.ZCashClientCaller.WalletCallException; - - /** * Provides the functionality for sending cash */ -public class SendCashPanel - extends WalletTabPanel +public class SendCashPanel extends WalletTabPanel { + private ZCashClientCaller clientCaller; + private StatusUpdateErrorReporter errorReporter; + private ZCashInstallationObserver installationObserver; + private BackupTracker backupTracker; + + private ZelCashJComboBox balanceAddressCombo = null; + private ZelCashJPanel comboBoxParentPanel = null; + private String[][] lastAddressBalanceData = null; + private String[] comboBoxItems = null; + private DataGatheringThread addressBalanceGatheringThread = null; + + private ZelCashJTextField destinationAddressField = null; + private ZelCashJTextField destinationAmountField = null; + private ZelCashJTextField destinationMemoField = null; + private ZelCashJTextField transactionFeeField = null; + + private ZelCashJCheckBox sendChangeBackToSourceAddress = null; + + private ZelCashJButton maxAmountButton = null; + private ZelCashJButton sendButton = null; + + private ZelCashJPanel operationStatusPanel = null; + private ZelCashJLabel operationStatusLabel = null; + private ZelCashJProgressBar operationStatusProhgressBar = null; + private Timer operationStatusTimer = null; + private String operationStatusID = null; + private int operationStatusCounter = 0; + private LanguageUtil langUtil; + + public SendCashPanel(ZCashClientCaller clientCaller, + StatusUpdateErrorReporter errorReporter, + ZCashInstallationObserver installationObserver, + BackupTracker backupTracker) + throws IOException, InterruptedException, WalletCallException { + langUtil = LanguageUtil.instance(); + this.timers = new ArrayList(); + this.threads = new ArrayList>(); + + this.clientCaller = clientCaller; + this.errorReporter = errorReporter; + this.installationObserver = installationObserver; + this.backupTracker = backupTracker; + + // Build content + this.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); + this.setLayout(new BorderLayout()); + ZelCashJPanel sendCashPanel = new ZelCashJPanel(); + this.add(sendCashPanel, BorderLayout.NORTH); + sendCashPanel.setLayout(new BoxLayout(sendCashPanel, BoxLayout.Y_AXIS)); + sendCashPanel.setBorder( + BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); + + ZelCashJPanel tempPanel = + new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + tempPanel.add( + new ZelCashJLabel(langUtil.getString("send.cash.panel.label"))); + tempPanel.add( + new ZelCashJLabel(langUtil.getString("send.cash.panel.label.info"))); + sendCashPanel.add(tempPanel); + + balanceAddressCombo = new ZelCashJComboBox<>(new String[] {""}); + comboBoxParentPanel = + new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + comboBoxParentPanel.add(balanceAddressCombo); + sendCashPanel.add(comboBoxParentPanel); + + ZelCashJLabel dividerLabel = new ZelCashJLabel(" "); + dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 3)); + sendCashPanel.add(dividerLabel); + + tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + tempPanel.add(new ZelCashJLabel( + langUtil.getString("send.cash.panel.label.destination.address"))); + sendCashPanel.add(tempPanel); + + destinationAddressField = new ZelCashJTextField(73); + tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + tempPanel.add(destinationAddressField); + sendCashPanel.add(tempPanel); + + dividerLabel = new ZelCashJLabel(" "); + dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 3)); + sendCashPanel.add(dividerLabel); + + tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + tempPanel.add( + new ZelCashJLabel(langUtil.getString("send.cash.panel.label.memo"))); + tempPanel.add(new ZelCashJLabel( + langUtil.getString("send.cash.panel.label.memo.info"))); + sendCashPanel.add(tempPanel); + + destinationMemoField = new ZelCashJTextField(73); + tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + tempPanel.add(destinationMemoField); + sendCashPanel.add(tempPanel); + + dividerLabel = new ZelCashJLabel(" "); + dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 3)); + sendCashPanel.add(dividerLabel); + + // Construct a more complex panel for the amount and transaction fee + ZelCashJPanel amountAndFeePanel = + new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + ZelCashJPanel amountPanel = new ZelCashJPanel(new BorderLayout()); + amountPanel.add( + new ZelCashJLabel(langUtil.getString("send.cash.panel.label.amount")), + BorderLayout.NORTH); + tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + tempPanel.add(destinationAmountField = new ZelCashJTextField(13)); + destinationAmountField.setHorizontalAlignment(SwingConstants.RIGHT); + tempPanel.add(new ZelCashJLabel(" ZEL ")); + tempPanel.add(maxAmountButton = new ZelCashJButton( + langUtil.getString("send.cash.panel.button.maxamount"))); + amountPanel.add(tempPanel, BorderLayout.SOUTH); + + ZelCashJPanel feePanel = new ZelCashJPanel(new BorderLayout()); + feePanel.add( + new ZelCashJLabel(langUtil.getString("send.cash.panel.label.fee")), + BorderLayout.NORTH); + tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + tempPanel.add(transactionFeeField = new ZelCashJTextField(13)); + transactionFeeField.setText("0.0001"); // Default value + transactionFeeField.setHorizontalAlignment(SwingConstants.RIGHT); + tempPanel.add(new ZelCashJLabel(" ZEL")); + feePanel.add(tempPanel, BorderLayout.SOUTH); + + ZelCashJPanel sendChangeBoxPanel = new ZelCashJPanel(new BorderLayout()); + sendChangeBoxPanel.add(new ZelCashJLabel(" "), BorderLayout.NORTH); + tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + tempPanel.add(new ZelCashJLabel(" ")); + tempPanel.add( + sendChangeBackToSourceAddress = new ZelCashJCheckBox( + langUtil.getString("send.cash.panel.checkbox.send.change.back"))); + sendChangeBackToSourceAddress.setSelected(true); + sendChangeBoxPanel.add(tempPanel, BorderLayout.SOUTH); + + amountAndFeePanel.add(amountPanel); + amountAndFeePanel.add(feePanel); + amountAndFeePanel.add(sendChangeBoxPanel); + sendCashPanel.add(amountAndFeePanel); + + dividerLabel = new ZelCashJLabel(" "); + dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 3)); + sendCashPanel.add(dividerLabel); + + tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + tempPanel.add(sendButton = new ZelCashJButton( + langUtil.getString("send.cash.panel.button.send") + + " \u27A4\u27A4\u27A4")); + sendCashPanel.add(tempPanel); + + dividerLabel = new ZelCashJLabel(" "); + dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 5)); + sendCashPanel.add(dividerLabel); + + ZelCashJPanel warningPanel = new ZelCashJPanel(); + warningPanel.setLayout(new BorderLayout(7, 3)); + ZelCashJLabel warningL = new ZelCashJLabel( + langUtil.getString("send.cash.panel.label.send.warning")); + warningPanel.add(warningL, BorderLayout.NORTH); + sendCashPanel.add(warningPanel); + + dividerLabel = new ZelCashJLabel(" "); + dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 15)); + sendCashPanel.add(dividerLabel); + + // Build the operation status panel + operationStatusPanel = new ZelCashJPanel(); + sendCashPanel.add(operationStatusPanel); + operationStatusPanel.setLayout( + new BoxLayout(operationStatusPanel, BoxLayout.Y_AXIS)); + + tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + tempPanel.add(new ZelCashJLabel( + langUtil.getString("send.cash.panel.label.last.operation.status"))); + tempPanel.add(operationStatusLabel = new ZelCashJLabel("N/A")); + operationStatusPanel.add(tempPanel); + + dividerLabel = new ZelCashJLabel(" "); + dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 6)); + operationStatusPanel.add(dividerLabel); + + tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + tempPanel.add(new ZelCashJLabel( + langUtil.getString("send.cash.panel.label.last.operation.progress"))); + tempPanel.add(operationStatusProhgressBar = + new ZelCashJProgressBar(0, 200)); + operationStatusProhgressBar.setPreferredSize(new Dimension(250, 17)); + operationStatusPanel.add(tempPanel); + + dividerLabel = new ZelCashJLabel(" "); + dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 13)); + operationStatusPanel.add(dividerLabel); + + // Wire the buttons + maxAmountButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + if (-1 == balanceAddressCombo.getSelectedIndex()) { + return; + } + String balance = + lastAddressBalanceData[balanceAddressCombo.getSelectedIndex()][0]; + String fee = transactionFeeField.getText(); + if ("".equals(balance) || "".equals(fee)) { + return; + } + String max = new DecimalFormat("########0.00######", + new DecimalFormatSymbols(Locale.US)) + .format(Double.parseDouble(balance) - + Double.parseDouble(fee)); + destinationAmountField.setText(max); + } catch (Exception ex) { + Log.error("Unexpected error: ", ex); + + String errMessage = ""; + if (ex instanceof WalletCallException) { + errMessage = + ((WalletCallException)ex).getMessage().replace(",", ",\n"); + } + + JOptionPane.showMessageDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString("send.cash.panel.option.pane.error.text", + errMessage), + langUtil.getString("send.cash.panel.option.pane.error.title"), + JOptionPane.ERROR_MESSAGE); + } + } + }); + sendButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + SendCashPanel.this.sendCash(); + } catch (Exception ex) { + Log.error("Unexpected error: ", ex); + + String errMessage = ""; + if (ex instanceof WalletCallException) { + errMessage = + ((WalletCallException)ex).getMessage().replace(",", ",\n"); + } + + JOptionPane.showMessageDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString("send.cash.panel.option.pane.error.text", + errMessage), + langUtil.getString("send.cash.panel.option.pane.error.title"), + JOptionPane.ERROR_MESSAGE); + } + } + }); + + // Update the balances via timer and data gathering thread + this.addressBalanceGatheringThread = new DataGatheringThread< + String[][]>(new DataGatheringThread.DataGatherer() { + public String[][] gatherData() throws Exception { + long start = System.currentTimeMillis(); + String[][] data = + SendCashPanel.this.getAddressPositiveBalanceDataFromWallet(); + long end = System.currentTimeMillis(); + Log.info( + "SendCashPanel: Gathering of address/balance table data done in " + + (end - start) + "ms."); + start = System.currentTimeMillis(); + SendCashPanel.this.updateWalletAddressPositiveBalanceComboBox(data); + end = System.currentTimeMillis(); + Log.info("SendCashPanel: Updating from combobox done in " + + (end - start) + "ms."); + return data; + } + }, this.errorReporter, 30000, true); + this.threads.add(addressBalanceGatheringThread); + + // Add a popup menu to the destination address field - for convenience + /*ZelCashJMenuItem paste = new +ZelCashJMenuItem(langUtil.getString("send.cash.panel.menu.item.paste")); final +ZelCashJPopupMenu popupMenu = new ZelCashJPopupMenu(); popupMenu.add(paste); +paste.addActionListener(new ActionListener() { - private ZCashClientCaller clientCaller; - private StatusUpdateErrorReporter errorReporter; - private ZCashInstallationObserver installationObserver; - private BackupTracker backupTracker; - - private ZelCashJComboBox balanceAddressCombo = null; - private ZelCashJPanel comboBoxParentPanel = null; - private String[][] lastAddressBalanceData = null; - private String[] comboBoxItems = null; - private DataGatheringThread addressBalanceGatheringThread = null; - - private ZelCashJTextField destinationAddressField = null; - private ZelCashJTextField destinationAmountField = null; - private ZelCashJTextField destinationMemoField = null; - private ZelCashJTextField transactionFeeField = null; - - private ZelCashJCheckBox sendChangeBackToSourceAddress = null; - - private ZelCashJButton maxAmountButton = null; - private ZelCashJButton sendButton = null; - - private ZelCashJPanel operationStatusPanel = null; - private ZelCashJLabel operationStatusLabel = null; - private ZelCashJProgressBar operationStatusProhgressBar = null; - private Timer operationStatusTimer = null; - private String operationStatusID = null; - private int operationStatusCounter = 0; - private LanguageUtil langUtil; - - public SendCashPanel(ZCashClientCaller clientCaller, - StatusUpdateErrorReporter errorReporter, - ZCashInstallationObserver installationObserver, - BackupTracker backupTracker) - throws IOException, InterruptedException, WalletCallException - { - langUtil = LanguageUtil.instance(); - this.timers = new ArrayList(); - this.threads = new ArrayList>(); - - this.clientCaller = clientCaller; - this.errorReporter = errorReporter; - this.installationObserver = installationObserver; - this.backupTracker = backupTracker; - - // Build content - this.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); - this.setLayout(new BorderLayout()); - ZelCashJPanel sendCashPanel = new ZelCashJPanel(); - this.add(sendCashPanel, BorderLayout.NORTH); - sendCashPanel.setLayout(new BoxLayout(sendCashPanel, BoxLayout.Y_AXIS)); - sendCashPanel.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); - - ZelCashJPanel tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - tempPanel.add(new ZelCashJLabel(langUtil.getString("send.cash.panel.label"))); - tempPanel.add(new ZelCashJLabel(langUtil.getString("send.cash.panel.label.info"))); - sendCashPanel.add(tempPanel); - - balanceAddressCombo = new ZelCashJComboBox<>(new String[] { "" }); - comboBoxParentPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - comboBoxParentPanel.add(balanceAddressCombo); - sendCashPanel.add(comboBoxParentPanel); - - ZelCashJLabel dividerLabel = new ZelCashJLabel(" "); - dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 3)); - sendCashPanel.add(dividerLabel); - - tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - tempPanel.add(new ZelCashJLabel(langUtil.getString("send.cash.panel.label.destination.address"))); - sendCashPanel.add(tempPanel); - - destinationAddressField = new ZelCashJTextField(73); - tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - tempPanel.add(destinationAddressField); - sendCashPanel.add(tempPanel); - - dividerLabel = new ZelCashJLabel(" "); - dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 3)); - sendCashPanel.add(dividerLabel); - - tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - tempPanel.add(new ZelCashJLabel(langUtil.getString("send.cash.panel.label.memo"))); - tempPanel.add(new ZelCashJLabel(langUtil.getString("send.cash.panel.label.memo.info"))); - sendCashPanel.add(tempPanel); - - destinationMemoField = new ZelCashJTextField(73); - tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - tempPanel.add(destinationMemoField); - sendCashPanel.add(tempPanel); - - dividerLabel = new ZelCashJLabel(" "); - dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 3)); - sendCashPanel.add(dividerLabel); - - // Construct a more complex panel for the amount and transaction fee - ZelCashJPanel amountAndFeePanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - ZelCashJPanel amountPanel = new ZelCashJPanel(new BorderLayout()); - amountPanel.add(new ZelCashJLabel(langUtil.getString("send.cash.panel.label.amount")), BorderLayout.NORTH); - tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - tempPanel.add(destinationAmountField = new ZelCashJTextField(13)); - destinationAmountField.setHorizontalAlignment(SwingConstants.RIGHT); - tempPanel.add(new ZelCashJLabel(" ZEL ")); - tempPanel.add(maxAmountButton = new ZelCashJButton(langUtil.getString("send.cash.panel.button.maxamount"))); - amountPanel.add(tempPanel, BorderLayout.SOUTH); - - ZelCashJPanel feePanel = new ZelCashJPanel(new BorderLayout()); - feePanel.add(new ZelCashJLabel(langUtil.getString("send.cash.panel.label.fee")), BorderLayout.NORTH); - tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - tempPanel.add(transactionFeeField = new ZelCashJTextField(13)); - transactionFeeField.setText("0.0001"); // Default value - transactionFeeField.setHorizontalAlignment(SwingConstants.RIGHT); - tempPanel.add(new ZelCashJLabel(" ZEL")); - feePanel.add(tempPanel, BorderLayout.SOUTH); - - ZelCashJPanel sendChangeBoxPanel = new ZelCashJPanel(new BorderLayout()); - sendChangeBoxPanel.add(new ZelCashJLabel(" "), BorderLayout.NORTH); - tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - tempPanel.add(new ZelCashJLabel(" ")); - tempPanel.add(sendChangeBackToSourceAddress = new ZelCashJCheckBox(langUtil.getString("send.cash.panel.checkbox.send.change.back"))); - sendChangeBackToSourceAddress.setSelected(true); - sendChangeBoxPanel.add(tempPanel, BorderLayout.SOUTH); - - amountAndFeePanel.add(amountPanel); - amountAndFeePanel.add(feePanel); - amountAndFeePanel.add(sendChangeBoxPanel); - sendCashPanel.add(amountAndFeePanel); - - dividerLabel = new ZelCashJLabel(" "); - dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 3)); - sendCashPanel.add(dividerLabel); - - tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - tempPanel.add(sendButton = new ZelCashJButton(langUtil.getString("send.cash.panel.button.send") + " \u27A4\u27A4\u27A4")); - sendCashPanel.add(tempPanel); - - dividerLabel = new ZelCashJLabel(" "); - dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 5)); - sendCashPanel.add(dividerLabel); - - ZelCashJPanel warningPanel = new ZelCashJPanel(); - warningPanel.setLayout(new BorderLayout(7, 3)); - ZelCashJLabel warningL = new ZelCashJLabel(langUtil.getString("send.cash.panel.label.send.warning")); - warningPanel.add(warningL, BorderLayout.NORTH); - sendCashPanel.add(warningPanel); - - dividerLabel = new ZelCashJLabel(" "); - dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 15)); - sendCashPanel.add(dividerLabel); - - // Build the operation status panel - operationStatusPanel = new ZelCashJPanel(); - sendCashPanel.add(operationStatusPanel); - operationStatusPanel.setLayout(new BoxLayout(operationStatusPanel, BoxLayout.Y_AXIS)); - - tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - tempPanel.add(new ZelCashJLabel(langUtil.getString("send.cash.panel.label.last.operation.status"))); - tempPanel.add(operationStatusLabel = new ZelCashJLabel("N/A")); - operationStatusPanel.add(tempPanel); - - dividerLabel = new ZelCashJLabel(" "); - dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 6)); - operationStatusPanel.add(dividerLabel); - - tempPanel = new ZelCashJPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - tempPanel.add(new ZelCashJLabel(langUtil.getString("send.cash.panel.label.last.operation.progress"))); - tempPanel.add(operationStatusProhgressBar = new ZelCashJProgressBar(0, 200)); - operationStatusProhgressBar.setPreferredSize(new Dimension(250, 17)); - operationStatusPanel.add(tempPanel); - - dividerLabel = new ZelCashJLabel(" "); - dividerLabel.setFont(new Font("Helvetica", Font.PLAIN, 13)); - operationStatusPanel.add(dividerLabel); - - // Wire the buttons - maxAmountButton.addActionListener(new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - try - { - if(-1 == balanceAddressCombo.getSelectedIndex()) { - return; - } - String balance = lastAddressBalanceData[balanceAddressCombo.getSelectedIndex()][0]; - String fee = transactionFeeField.getText(); - if("".equals(balance) || "".equals(fee)) { - return; - } - String max = new DecimalFormat("########0.00######",new DecimalFormatSymbols(Locale.US)).format(Double.parseDouble(balance) - Double.parseDouble(fee)); - destinationAmountField.setText(max); - } catch (Exception ex) - { - Log.error("Unexpected error: ", ex); - - String errMessage = ""; - if (ex instanceof WalletCallException) - { - errMessage = ((WalletCallException)ex).getMessage().replace(",", ",\n"); - } - - JOptionPane.showMessageDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.option.pane.error.text",errMessage), - langUtil.getString("send.cash.panel.option.pane.error.title"), - JOptionPane.ERROR_MESSAGE); - } - } - }); - sendButton.addActionListener(new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - try - { - SendCashPanel.this.sendCash(); - } catch (Exception ex) - { - Log.error("Unexpected error: ", ex); - - String errMessage = ""; - if (ex instanceof WalletCallException) - { - errMessage = ((WalletCallException)ex).getMessage().replace(",", ",\n"); - } - - JOptionPane.showMessageDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.option.pane.error.text",errMessage), - langUtil.getString("send.cash.panel.option.pane.error.title"), - JOptionPane.ERROR_MESSAGE); - } - } - }); - - // Update the balances via timer and data gathering thread - this.addressBalanceGatheringThread = new DataGatheringThread( - new DataGatheringThread.DataGatherer() - { - public String[][] gatherData() - throws Exception - { - long start = System.currentTimeMillis(); - String[][] data = SendCashPanel.this.getAddressPositiveBalanceDataFromWallet(); - long end = System.currentTimeMillis(); - Log.info("SendCashPanel: Gathering of address/balance table data done in " + (end - start) + "ms." ); - start = System.currentTimeMillis(); - SendCashPanel.this.updateWalletAddressPositiveBalanceComboBox(data); - end = System.currentTimeMillis(); - Log.info("SendCashPanel: Updating from combobox done in " + (end - start) + "ms." ); - return data; - } - }, - this.errorReporter, 30000, true); - this.threads.add(addressBalanceGatheringThread); - - // Add a popup menu to the destination address field - for convenience - /*ZelCashJMenuItem paste = new ZelCashJMenuItem(langUtil.getString("send.cash.panel.menu.item.paste")); - final ZelCashJPopupMenu popupMenu = new ZelCashJPopupMenu(); - popupMenu.add(paste); - paste.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - try - { - String address = (String)Toolkit.getDefaultToolkit().getSystemClipboard(). - getData(DataFlavor.stringFlavor); - if ((address != null) && (address.trim().length() > 0)) - { - SendCashPanel.this.destinationAddressField.setText(address); - } - } catch (Exception ex) - { - Log.error("Unexpected error", ex); - // TODO: clipboard exception handling - do it better - // java.awt.datatransfer.UnsupportedFlavorException: Unicode String - //SendCashPanel.this.errorReporter.reportError(ex); - } - } - }); - - this.destinationAddressField.addMouseListener(new MouseAdapter() - { - public void mousePressed(MouseEvent e) - { - if ((!e.isConsumed()) && e.isPopupTrigger()) - { - popupMenu.show(e.getComponent(), e.getPoint().x, e.getPoint().y); - e.consume(); - }; - } - - public void mouseReleased(MouseEvent e) + @Override + public void actionPerformed(ActionEvent e) { - if ((!e.isConsumed()) && e.isPopupTrigger()) - { - mousePressed(e); - } + try + { + String address = +(String)Toolkit.getDefaultToolkit().getSystemClipboard(). + getData(DataFlavor.stringFlavor); + if ((address != null) && (address.trim().length() > +0)) + { + SendCashPanel.this.destinationAddressField.setText(address); + } + } catch (Exception ex) + { + Log.error("Unexpected error", ex); + // TODO: clipboard exception handling - do it better + // java.awt.datatransfer.UnsupportedFlavorException: +Unicode String + //SendCashPanel.this.errorReporter.reportError(ex); + } } - }); - */ - } - - - private void sendCash() - throws WalletCallException, IOException, InterruptedException - { - if (balanceAddressCombo.getItemCount() <= 0) - { - JOptionPane.showMessageDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.option.pane.no.funds.text"), - langUtil.getString("send.cash.panel.option.pane.no.funds.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - if (this.balanceAddressCombo.getSelectedIndex() < 0) - { - JOptionPane.showMessageDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.option.pane.select.source.text"), - langUtil.getString("send.cash.panel.option.pane.select.source.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - final String sourceAddress = this.lastAddressBalanceData[this.balanceAddressCombo.getSelectedIndex()][1]; - final String destinationAddress = this.destinationAddressField.getText(); - final String memo = this.destinationMemoField.getText(); - final String amount = this.destinationAmountField.getText(); - final String fee = this.transactionFeeField.getText(); - - Log.info("Send button processing: Parameters are: from address: {0}, to address: {1}, " + - "amount: {2}, memo: {3}, transaction fee: {4}", - sourceAddress, destinationAddress, amount, memo, fee); - - // Verify general correctness. - String errorMessage = null; - - if ((sourceAddress == null) || (sourceAddress.trim().length() <= 20)) - { - errorMessage = langUtil.getString("send.cash.panel.option.pane.error.source.address.invalid"); - } else if (sourceAddress.length() > 512) - { - errorMessage = langUtil.getString("send.cash.panel.option.pane.error.source.address.too.long"); - } - - // TODO: full address validation - if ((destinationAddress == null) || (destinationAddress.trim().length() <= 0)) - { - errorMessage = langUtil.getString("send.cash.panel.option.pane.error.destination.address.invalid"); - } else if (destinationAddress.trim().length() <= 20) - { - errorMessage = langUtil.getString("send.cash.panel.option.pane.error.destination.address.too.short"); - } else if (destinationAddress.length() > 512) - { - errorMessage = langUtil.getString("send.cash.panel.option.pane.error.destination.address.too.long"); - } else if (destinationAddress.trim().length() != destinationAddress.length()) - { - errorMessage = langUtil.getString("send.cash.panel.option.pane.error.destination.address.has.spaces"); - } - - if ((amount == null) || (amount.trim().length() <= 0)) - { - errorMessage = langUtil.getString("send.cash.panel.option.pane.error.amount.invalid"); - } else - { - try - { - double d = Double.valueOf(amount); - if (d < 0) - { - errorMessage = langUtil.getString("send.cash.panel.option.pane.error.amount.negative"); - } - } catch (NumberFormatException nfe) - { - errorMessage = langUtil.getString("send.cash.panel.option.pane.error.amount.not.number"); - } - } - - if ((fee == null) || (fee.trim().length() <= 0)) - { - errorMessage = langUtil.getString("send.cash.panel.option.pane.error.fee.invalid"); - } else - { - try - { - double d = Double.valueOf(fee); - if (d < 0) - { - errorMessage = langUtil.getString("send.cash.panel.option.pane.error.fee.negative"); - } - } catch (NumberFormatException nfe) - { - errorMessage = langUtil.getString("send.cash.panel.option.pane.error.fee.not.number"); - } - } - - if (errorMessage != null) - { - JOptionPane.showMessageDialog( - SendCashPanel.this.getRootPane().getParent(), - errorMessage, langUtil.getString("send.cash.panel.option.pane.error.incorrect.sending.parameters"), JOptionPane.ERROR_MESSAGE); - return; - } - - // Prevent accidental sending to non-ZEL addresses (which zelcashd supports) probably because of - // ZClassic compatibility - if (!installationObserver.isOnTestNet()) - { - if (!(destinationAddress.startsWith("zc") || - destinationAddress.startsWith("za") || - destinationAddress.startsWith("t1") || - destinationAddress.startsWith("t3"))) - { - Object[] options = { langUtil.getString("button.option.ok") }; - - JOptionPane.showOptionDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.option.pane.error.destination.address.incorrect.text", destinationAddress), - langUtil.getString("send.cash.panel.option.pane.error.destination.address.incorrect.title"), - JOptionPane.DEFAULT_OPTION, - JOptionPane.ERROR_MESSAGE, - null, - options, - options[0]); - - return; // Do not send anything! - } - } - - // If a memo is specified, make sure the destination is a Z address. - if ((!installationObserver.isOnTestNet()) && - (!Util.stringIsEmpty(memo)) && - (!Util.isZAddress(destinationAddress))) - { - Object[] options = - { - langUtil.getString("button.option.yes"), - langUtil.getString("button.option.no") - }; - int reply = JOptionPane.showOptionDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.option.pane.error.destination.address.notz.text", destinationAddress), - langUtil.getString("send.cash.panel.option.pane.error.destination.address.notz.title"), - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - JOptionPane.NO_OPTION); - - if (reply == JOptionPane.NO_OPTION || reply == JOptionPane.CLOSED_OPTION) - { - return; - } - } - - // Warn the user if there are too many fractional digits in the amount and fee - if (hasExcessiveFractionalDigits(amount)) - { - Object[] options = - { - langUtil.getString("button.option.yes"), - langUtil.getString("button.option.no") - }; - int reply = JOptionPane.showOptionDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.option.pane.error.destination.amount.fractional.digits", amount), - langUtil.getString("send.cash.panel.option.pane.error.destination.fractional.digits.title"), - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - JOptionPane.NO_OPTION); - - if (reply == JOptionPane.NO_OPTION || reply == JOptionPane.CLOSED_OPTION) - { - return; - } - } - - if (hasExcessiveFractionalDigits(fee)) - { - Object[] options = - { - langUtil.getString("button.option.yes"), - langUtil.getString("button.option.no") - }; - int reply = JOptionPane.showOptionDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.option.pane.error.destination.fee.fractional.digits", fee), - langUtil.getString("send.cash.panel.option.pane.error.destination.fractional.digits.title"), - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - JOptionPane.NO_OPTION); - - if (reply == JOptionPane.NO_OPTION || reply == JOptionPane.CLOSED_OPTION) - { - return; - } - } - - // Get a confirmation from the user about the operation - String userDir = OSUtil.getSettingsDirectory(); - File sendCashNotToBeShownFlagFile = new File(userDir + File.separator + "sendCashWarningNotToBeShown.flag"); - if (!sendCashNotToBeShownFlagFile.exists()) - { - Object[] options = - { - langUtil.getString("send.cash.panel.option.pane.confirm.operation.button.yes"), - langUtil.getString("send.cash.panel.option.pane.confirm.operation.button.no"), - langUtil.getString("send.cash.panel.option.pane.confirm.operation.button.not.again") - }; - - int option; - - if (Util.stringIsEmpty(memo)) - { - option = JOptionPane.showOptionDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.option.pane.confirm.operation.text", - amount, sourceAddress, destinationAddress, fee), - langUtil.getString("send.cash.panel.option.pane.confirm.operation.title"), - JOptionPane.DEFAULT_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - options[0]); - } else - { - option = JOptionPane.showOptionDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.option.pane.confirm.operation.text.with.memo", - amount, sourceAddress, destinationAddress, fee, Util.blockWrapString(memo, 50)), - langUtil.getString("send.cash.panel.option.pane.confirm.operation.title"), - JOptionPane.DEFAULT_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - options[0]); - } - - if (option == 2) - { - sendCashNotToBeShownFlagFile.createNewFile(); - } - - if (option == 1 || option == JOptionPane.CLOSED_OPTION) - { - return; - } - - // 4e075d661a12376b13e9bd95831bc6a002824e029ff50059bd1e28662971e055 - } - - boolean bEncryptedWallet = false; - // Backend operations are wrapped inside a wait cursor - Cursor oldCursor = this.getRootPane().getParent().getCursor(); - try - { - // Check for encrypted wallet - bEncryptedWallet = this.clientCaller.isWalletEncrypted(); - if (bEncryptedWallet) - { - boolean passwordOk = false; - int retrys = 0; - while(!passwordOk && retrys<3) { - ++retrys; - PasswordDialog pd = new PasswordDialog((ZelCashJFrame)(SendCashPanel.this.getRootPane().getParent())); - pd.setVisible(true); - - if (!pd.isOKPressed()) - { - return; - } - try { - this.clientCaller.unlockWallet(pd.getPassword()); - passwordOk = true; - } - catch (Exception ex) { - Log.error("Error unlocking wallet:"+ex.getMessage()); - JOptionPane.showMessageDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("encryption.error.unlocking.message", ex.getMessage()), - langUtil.getString("encryption.error.unlocking.title"), - JOptionPane.ERROR_MESSAGE); - } - } - if(!passwordOk) { - Log.info("Failed to enter correct password for third time, wallet will close."); - System.exit(1); - } - } - - boolean sendChangeBackToAddres = this.sendChangeBackToSourceAddress.isSelected(); - Log.info("Change send back flag: {0}", sendChangeBackToAddres); - if (sendChangeBackToAddres) - { - if (this.warnAndCheckConditionsForSendingBackChange(sourceAddress, destinationAddress, amount, memo, fee)) - { - String balance = this.clientCaller.getBalanceForAddress(sourceAddress); - // Call the send method with change going back to source address - operationStatusID = this.clientCaller.sendCashWithReturnOfChange(sourceAddress, destinationAddress, balance, amount, memo, fee); - } else - { - return; // Stop the operation - } - } else - { - // Call the wallet send method - old style - operationStatusID = this.clientCaller.sendCash(sourceAddress, destinationAddress, amount, memo, fee); - } - - // Make sure the keypool has spare addresses - if ((this.backupTracker.getNumTransactionsSinceLastBackup() % 5) == 0) - { - this.clientCaller.keypoolRefill(100); - } - } finally - { - this.getRootPane().getParent().setCursor(oldCursor); - } - - // Disable controls after send - sendChangeBackToSourceAddress.setEnabled(false); - sendButton.setEnabled(false); - balanceAddressCombo.setEnabled(false); - destinationAddressField.setEnabled(false); - destinationAmountField.setEnabled(false); - destinationMemoField.setEnabled(false); - transactionFeeField.setEnabled(false); - - - final boolean bEncryptedWalletForThread = bEncryptedWallet; - // Start a data gathering thread specific to the operation being executed - this is done is a separate - // thread since the server responds more slowly during JoinSplits and this blocks he GUI somewhat. - final DataGatheringThread opFollowingThread = new DataGatheringThread( - new DataGatheringThread.DataGatherer() - { - public Boolean gatherData() - throws Exception - { - long start = System.currentTimeMillis(); - Boolean result = clientCaller.isSendingOperationComplete(operationStatusID); - long end = System.currentTimeMillis(); - Log.info("Checking for operation " + operationStatusID + " status done in " + (end - start) + "ms." ); - - return result; - } - }, - this.errorReporter, 2000, true); - - // Start a timer to update the progress of the operation - operationStatusCounter = 0; - operationStatusTimer = new Timer(2000, new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - try - { - // TODO: Handle errors in case of restarted server while wallet is sending ... - Boolean opComplete = opFollowingThread.getLastData(); - - if ((opComplete != null) && opComplete.booleanValue()) - { - // End the special thread used to follow the operation - opFollowingThread.setSuspended(true); - - SendCashPanel.this.reportCompleteOperationToTheUser( - amount, sourceAddress, destinationAddress); - - // Lock the wallet again - if (bEncryptedWalletForThread) - { - //SendCashPanel.this.clientCaller.lockWallet(); - } - - // Restore controls etc. - operationStatusCounter = 0; - operationStatusID = null; - operationStatusTimer.stop(); - operationStatusTimer = null; - operationStatusProhgressBar.setValue(0); - - sendChangeBackToSourceAddress.setEnabled(true); - sendButton.setEnabled(true); - balanceAddressCombo.setEnabled(true); - destinationAddressField.setEnabled(true); - destinationAmountField.setEnabled(true); - transactionFeeField.setEnabled(true); - destinationMemoField.setEnabled(true); - } else - { - // Update the progress - operationStatusLabel.setText(langUtil.getString("send.cash.panel.operation.status.progress.label")); - operationStatusCounter += 2; - int progress = 0; - if (operationStatusCounter <= 100) - { - progress = operationStatusCounter; - } else - { - progress = 100 + (((operationStatusCounter - 100) * 6) / 10); - } - operationStatusProhgressBar.setValue(progress); - } - - SendCashPanel.this.repaint(); - } catch (Exception ex) - { - Log.error("Unexpected error: ", ex); - SendCashPanel.this.errorReporter.reportError(ex); - } - } - }); - operationStatusTimer.setInitialDelay(0); - operationStatusTimer.start(); - } - - - public void prepareForSending(String address) - { - destinationAddressField.setText(address); - } - - - private void updateWalletAddressPositiveBalanceComboBox(String[][] data) - throws WalletCallException, IOException, InterruptedException - { - String[][] newAddressBalanceData = data; - - // The data may be null if nothing is yet obtained - if (newAddressBalanceData == null) - { - return; - } - - lastAddressBalanceData = newAddressBalanceData; - - comboBoxItems = new String[lastAddressBalanceData.length]; - for (int i = 0; i < lastAddressBalanceData.length; i++) - { - // Do numeric formatting or else we may get 1.1111E-5 - comboBoxItems[i] = - new DecimalFormat("########0.00######").format(Double.valueOf(lastAddressBalanceData[i][0])) + - " - " + lastAddressBalanceData[i][1]; - } - - int selectedIndex = balanceAddressCombo.getSelectedIndex(); - boolean isEnabled = balanceAddressCombo.isEnabled(); - this.comboBoxParentPanel.remove(balanceAddressCombo); - balanceAddressCombo = new ZelCashJComboBox<>(comboBoxItems); - comboBoxParentPanel.add(balanceAddressCombo); - if ((balanceAddressCombo.getItemCount() > 0) && - (selectedIndex >= 0) && - (balanceAddressCombo.getItemCount() > selectedIndex)) - { - balanceAddressCombo.setSelectedIndex(selectedIndex); - } - balanceAddressCombo.setEnabled(isEnabled); - - this.validate(); - this.repaint(); - } - - - private String[][] getAddressPositiveBalanceDataFromWallet() - throws WalletCallException, IOException, InterruptedException - { - // Z Addresses - they are OK - String[] zAddresses = clientCaller.getWalletZAddresses(); - - // T Addresses created inside wallet that may be empty - String[] tAddresses = this.clientCaller.getWalletAllPublicAddresses(); - Set tStoredAddressSet = new HashSet<>(); - for (String address : tAddresses) - { - tStoredAddressSet.add(address); - } - - // T addresses with unspent outputs (even if not GUI created)... - String[] tAddressesWithUnspentOuts = this.clientCaller.getWalletPublicAddressesWithUnspentOutputs(); - Set tAddressSetWithUnspentOuts = new HashSet<>(); - for (String address : tAddressesWithUnspentOuts) - { - tAddressSetWithUnspentOuts.add(address); - } - - // Combine all known T addresses - Set tAddressesCombined = new HashSet<>(); - tAddressesCombined.addAll(tStoredAddressSet); - tAddressesCombined.addAll(tAddressSetWithUnspentOuts); - - String[][] tempAddressBalances = new String[zAddresses.length + tAddressesCombined.size()][]; - - int count = 0; - - for (String address : tAddressesCombined) - { - String balance = this.clientCaller.getBalanceForAddress(address); - if (Double.valueOf(balance) > 0) - { - tempAddressBalances[count++] = new String[] - { - balance, address - }; - } - } - - for (String address : zAddresses) - { - String balance = this.clientCaller.getBalanceForAddress(address); - if (Double.valueOf(balance) > 0) - { - tempAddressBalances[count++] = new String[] - { - balance, address - }; - } - } - - String[][] addressBalances = new String[count][]; - System.arraycopy(tempAddressBalances, 0, addressBalances, 0, count); - - return addressBalances; - } - - - private void reportCompleteOperationToTheUser(String amount, String sourceAddress, String destinationAddress) - throws InterruptedException, WalletCallException, IOException, URISyntaxException - { - if (clientCaller.isCompletedOperationSuccessful(operationStatusID)) - { - operationStatusLabel.setText(langUtil.getString("send.cash.panel.operation.status.success.label")); - String TXID = clientCaller.getSuccessfulOperationTXID(operationStatusID); - - Object[] options = langUtil.getString("send.cash.panel.operation.complete.report").split(":"); - - int option = JOptionPane.showOptionDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.operation.complete.report.success.text", - amount, - sourceAddress, - destinationAddress, - TXID), - langUtil.getString("send.cash.panel.operation.complete.report.success.title"), - JOptionPane.DEFAULT_OPTION, - JOptionPane.INFORMATION_MESSAGE, - null, - options, - options[0]); - - if (option == 1) - { - // Copy the transaction ID to clipboard - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - clipboard.setContents(new StringSelection(TXID), null); - } else if (option == 2) - { - // Open block explorer - Log.info("Transaction ID for block explorer is: " + TXID); - // TODO: code duplication with transactions table - String urlPrefix = "https://explorer.zel.cash/tx/"; - if (installationObserver.isOnTestNet()) - { - urlPrefix = "https://testnet.zel.cash/tx/"; - } - Desktop.getDesktop().browse(new URL(urlPrefix + TXID).toURI()); - } - - // Call the backup tracker - to remind the user - this.backupTracker.handleNewTransaction(); - } else - { - String errorMessage = clientCaller.getOperationFinalErrorMessage(operationStatusID); - operationStatusLabel.setText( - langUtil.getString("send.cash.panel.operation.status.error.label", errorMessage)); - - JOptionPane.showMessageDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.option.pane.error.report.text",errorMessage), - langUtil.getString("send.cash.panel.option.pane.error.report.title"), JOptionPane.ERROR_MESSAGE); - - } - } - - - // Checks if a number has more than 8 fractional digits. This is not normally allowed for ZEL - // Input must be a decimal number! - private boolean hasExcessiveFractionalDigits(String field) - { - BigDecimal num = new BigDecimal(field); - DecimalFormatSymbols decSymbols = new DecimalFormatSymbols(Locale.ROOT); - DecimalFormat longFormat = new DecimalFormat("############################0.00###############################", decSymbols); - String formattedNumber = longFormat.format(num); - String fractionalPart = formattedNumber.substring(formattedNumber.indexOf(".") + 1); - - if (fractionalPart.length() > 8) - { - return true; - } - - return false; - } - - - /** - * Checks the conditions necessary for sending back the change. Also issues a warning to the user on the nature of this operation. - * - * @param sourceAddress - * @param destinationAddress - * @param amount - * @param memo - * @param fee - * - * @return true if all conditions are met and the user has not cancelled the operation - */ - private boolean warnAndCheckConditionsForSendingBackChange(String sourceAddress, String destinationAddress, String amount, String memo, String fee) - throws WalletCallException, InterruptedException, IOException - { - String balance = this.clientCaller.getBalanceForAddress(sourceAddress); - - // Get a confirmation from the user about the operation - general warning - String userDir = OSUtil.getSettingsDirectory(); - File sendChangeBackNotToBeShownFlagFile = new File(userDir + File.separator + "sendBackChangeWarningNotToBeShown.flag"); - if (!sendChangeBackNotToBeShownFlagFile.exists()) - { - Object[] options = - { - langUtil.getString("send.cash.panel.option.pane.confirm.operation.button.yes"), - langUtil.getString("send.cash.panel.option.pane.confirm.operation.button.no"), - langUtil.getString("send.cash.panel.option.pane.confirm.operation.button.not.again") - }; - - int option = JOptionPane.showOptionDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.send.change.back.general.warning"), - langUtil.getString("send.cash.panel.send.change.back.general.warning.title"), - JOptionPane.DEFAULT_OPTION, - JOptionPane.WARNING_MESSAGE, - null, - options, - options[0]); - - if (option == 2) - { - sendChangeBackNotToBeShownFlagFile.createNewFile(); - } - - if (option == 1 || option == JOptionPane.CLOSED_OPTION) - { - return false; - } + }); + +this.destinationAddressField.addMouseListener(new MouseAdapter() +{ + public void mousePressed(MouseEvent e) + { + if ((!e.isConsumed()) && e.isPopupTrigger()) + { + popupMenu.show(e.getComponent(), e.getPoint().x, e.getPoint().y); + e.consume(); + }; + } + +public void mouseReleased(MouseEvent e) +{ + if ((!e.isConsumed()) && e.isPopupTrigger()) + { + mousePressed(e); + } +} +}); + */ + } + + private void sendCash() + throws WalletCallException, IOException, InterruptedException { + if (balanceAddressCombo.getItemCount() <= 0) { + JOptionPane.showMessageDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString("send.cash.panel.option.pane.no.funds.text"), + langUtil.getString("send.cash.panel.option.pane.no.funds.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + if (this.balanceAddressCombo.getSelectedIndex() < 0) { + JOptionPane.showMessageDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString("send.cash.panel.option.pane.select.source.text"), + langUtil.getString("send.cash.panel.option.pane.select.source.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + final String sourceAddress = + this.lastAddressBalanceData[this.balanceAddressCombo.getSelectedIndex()] + [1]; + final String destinationAddress = this.destinationAddressField.getText(); + final String memo = this.destinationMemoField.getText(); + final String amount = this.destinationAmountField.getText(); + final String fee = this.transactionFeeField.getText(); + + Log.info( + "Send button processing: Parameters are: from address: {0}, to address: {1}, " + + "amount: {2}, memo: {3}, transaction fee: {4}", + sourceAddress, destinationAddress, amount, memo, fee); + + // Verify general correctness. + String errorMessage = null; + + if ((sourceAddress == null) || (sourceAddress.trim().length() <= 20)) { + errorMessage = langUtil.getString( + "send.cash.panel.option.pane.error.source.address.invalid"); + } else if (sourceAddress.length() > 512) { + errorMessage = langUtil.getString( + "send.cash.panel.option.pane.error.source.address.too.long"); + } + + // TODO: full address validation + if ((destinationAddress == null) || + (destinationAddress.trim().length() <= 0)) { + errorMessage = langUtil.getString( + "send.cash.panel.option.pane.error.destination.address.invalid"); + } else if (destinationAddress.trim().length() <= 20) { + errorMessage = langUtil.getString( + "send.cash.panel.option.pane.error.destination.address.too.short"); + } else if (destinationAddress.length() > 512) { + errorMessage = langUtil.getString( + "send.cash.panel.option.pane.error.destination.address.too.long"); + } else if (destinationAddress.trim().length() != + destinationAddress.length()) { + errorMessage = langUtil.getString( + "send.cash.panel.option.pane.error.destination.address.has.spaces"); + } + + if ((amount == null) || (amount.trim().length() <= 0)) { + errorMessage = langUtil.getString( + "send.cash.panel.option.pane.error.amount.invalid"); + } else { + try { + double d = Double.valueOf(amount); + if (d < 0) { + errorMessage = langUtil.getString( + "send.cash.panel.option.pane.error.amount.negative"); + } + } catch (NumberFormatException nfe) { + errorMessage = langUtil.getString( + "send.cash.panel.option.pane.error.amount.not.number"); + } + } + + if ((fee == null) || (fee.trim().length() <= 0)) { + errorMessage = + langUtil.getString("send.cash.panel.option.pane.error.fee.invalid"); + } else { + try { + double d = Double.valueOf(fee); + if (d < 0) { + errorMessage = langUtil.getString( + "send.cash.panel.option.pane.error.fee.negative"); + } + } catch (NumberFormatException nfe) { + errorMessage = langUtil.getString( + "send.cash.panel.option.pane.error.fee.not.number"); + } + } + + if (errorMessage != null) { + JOptionPane.showMessageDialog( + SendCashPanel.this.getRootPane().getParent(), errorMessage, + langUtil.getString( + "send.cash.panel.option.pane.error.incorrect.sending.parameters"), + JOptionPane.ERROR_MESSAGE); + return; + } + + // Prevent accidental sending to non-ZEL addresses (which zelcashd supports) + // probably because of ZClassic compatibility + if (!installationObserver.isOnTestNet()) { + if (!(destinationAddress.startsWith("zc") || + destinationAddress.startsWith("za") || + destinationAddress.startsWith("t1") || + destinationAddress.startsWith("t3"))) { + Object[] options = {langUtil.getString("button.option.ok")}; + + JOptionPane.showOptionDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString( + "send.cash.panel.option.pane.error.destination.address.incorrect.text", + destinationAddress), + langUtil.getString( + "send.cash.panel.option.pane.error.destination.address.incorrect.title"), + JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null, + options, options[0]); + + return; // Do not send anything! + } + } + + // If a memo is specified, make sure the destination is a Z address. + if ((!installationObserver.isOnTestNet()) && (!Util.stringIsEmpty(memo)) && + (!Util.isZAddress(destinationAddress))) { + Object[] options = {langUtil.getString("button.option.yes"), + langUtil.getString("button.option.no")}; + int reply = JOptionPane.showOptionDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString( + "send.cash.panel.option.pane.error.destination.address.notz.text", + destinationAddress), + langUtil.getString( + "send.cash.panel.option.pane.error.destination.address.notz.title"), + JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, + options, JOptionPane.NO_OPTION); + + if (reply == JOptionPane.NO_OPTION || + reply == JOptionPane.CLOSED_OPTION) { + return; + } + } + + // Warn the user if there are too many fractional digits in the amount and + // fee + if (hasExcessiveFractionalDigits(amount)) { + Object[] options = {langUtil.getString("button.option.yes"), + langUtil.getString("button.option.no")}; + int reply = JOptionPane.showOptionDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString( + "send.cash.panel.option.pane.error.destination.amount.fractional.digits", + amount), + langUtil.getString( + "send.cash.panel.option.pane.error.destination.fractional.digits.title"), + JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, + options, JOptionPane.NO_OPTION); + + if (reply == JOptionPane.NO_OPTION || + reply == JOptionPane.CLOSED_OPTION) { + return; + } + } + + if (hasExcessiveFractionalDigits(fee)) { + Object[] options = {langUtil.getString("button.option.yes"), + langUtil.getString("button.option.no")}; + int reply = JOptionPane.showOptionDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString( + "send.cash.panel.option.pane.error.destination.fee.fractional.digits", + fee), + langUtil.getString( + "send.cash.panel.option.pane.error.destination.fractional.digits.title"), + JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, + options, JOptionPane.NO_OPTION); + + if (reply == JOptionPane.NO_OPTION || + reply == JOptionPane.CLOSED_OPTION) { + return; + } + } + + // Get a confirmation from the user about the operation + String userDir = OSUtil.getSettingsDirectory(); + File sendCashNotToBeShownFlagFile = + new File(userDir + File.separator + "sendCashWarningNotToBeShown.flag"); + if (!sendCashNotToBeShownFlagFile.exists()) { + Object[] options = { + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.button.yes"), + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.button.no"), + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.button.not.again")}; + + int option; + + if (Util.stringIsEmpty(memo)) { + option = JOptionPane.showOptionDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.text", amount, + sourceAddress, destinationAddress, fee), + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.title"), + JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, + options, options[0]); + } else { + option = JOptionPane.showOptionDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.text.with.memo", + amount, sourceAddress, destinationAddress, fee, + Util.blockWrapString(memo, 50)), + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.title"), + JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, + options, options[0]); + } + + if (option == 2) { + sendCashNotToBeShownFlagFile.createNewFile(); + } + + if (option == 1 || option == JOptionPane.CLOSED_OPTION) { + return; + } + + // 4e075d661a12376b13e9bd95831bc6a002824e029ff50059bd1e28662971e055 + } + + boolean bEncryptedWallet = false; + // Backend operations are wrapped inside a wait cursor + Cursor oldCursor = this.getRootPane().getParent().getCursor(); + try { + // Check for encrypted wallet + bEncryptedWallet = this.clientCaller.isWalletEncrypted(); + if (bEncryptedWallet) { + boolean passwordOk = false; + int retrys = 0; + while (!passwordOk && retrys < 3) { + ++retrys; + PasswordDialog pd = new PasswordDialog( + (ZelCashJFrame)(SendCashPanel.this.getRootPane().getParent())); + pd.setVisible(true); + + if (!pd.isOKPressed()) { + return; + } + try { + this.clientCaller.unlockWallet(pd.getPassword()); + passwordOk = true; + } catch (Exception ex) { + Log.error("Error unlocking wallet:" + ex.getMessage()); + JOptionPane.showMessageDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString("encryption.error.unlocking.message", + ex.getMessage()), + langUtil.getString("encryption.error.unlocking.title"), + JOptionPane.ERROR_MESSAGE); + } + } + if (!passwordOk) { + Log.info( + "Failed to enter correct password for third time, wallet will close."); + System.exit(1); + } + } + + boolean sendChangeBackToAddres = + this.sendChangeBackToSourceAddress.isSelected(); + Log.info("Change send back flag: {0}", sendChangeBackToAddres); + if (sendChangeBackToAddres) { + if (this.warnAndCheckConditionsForSendingBackChange( + sourceAddress, destinationAddress, amount, memo, fee)) { + String balance = + this.clientCaller.getBalanceForAddress(sourceAddress); + // Call the send method with change going back to source address + operationStatusID = this.clientCaller.sendCashWithReturnOfChange( + sourceAddress, destinationAddress, balance, amount, memo, fee); + } else { + return; // Stop the operation + } + } else { + // Call the wallet send method - old style + operationStatusID = this.clientCaller.sendCash( + sourceAddress, destinationAddress, amount, memo, fee); + } + + // Make sure the keypool has spare addresses + if ((this.backupTracker.getNumTransactionsSinceLastBackup() % 5) == 0) { + this.clientCaller.keypoolRefill(100); + } + } finally { + this.getRootPane().getParent().setCursor(oldCursor); + } + + // Disable controls after send + sendChangeBackToSourceAddress.setEnabled(false); + sendButton.setEnabled(false); + balanceAddressCombo.setEnabled(false); + destinationAddressField.setEnabled(false); + destinationAmountField.setEnabled(false); + destinationMemoField.setEnabled(false); + transactionFeeField.setEnabled(false); + + final boolean bEncryptedWalletForThread = bEncryptedWallet; + // Start a data gathering thread specific to the operation being executed - + // this is done is a separate thread since the server responds more slowly + // during JoinSplits and this blocks he GUI somewhat. + final DataGatheringThread opFollowingThread = + new DataGatheringThread( + new DataGatheringThread.DataGatherer() { + public Boolean gatherData() throws Exception { + long start = System.currentTimeMillis(); + Boolean result = + clientCaller.isSendingOperationComplete(operationStatusID); + long end = System.currentTimeMillis(); + Log.info("Checking for operation " + operationStatusID + + " status done in " + (end - start) + "ms."); + + return result; + } + }, + this.errorReporter, 2000, true); + + // Start a timer to update the progress of the operation + operationStatusCounter = 0; + operationStatusTimer = new Timer(2000, new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + // TODO: Handle errors in case of restarted server while wallet is + // sending ... + Boolean opComplete = opFollowingThread.getLastData(); + + if ((opComplete != null) && opComplete.booleanValue()) { + // End the special thread used to follow the operation + opFollowingThread.setSuspended(true); + + SendCashPanel.this.reportCompleteOperationToTheUser( + amount, sourceAddress, destinationAddress); + + // Lock the wallet again + if (bEncryptedWalletForThread) { + // SendCashPanel.this.clientCaller.lockWallet(); + } + + // Restore controls etc. + operationStatusCounter = 0; + operationStatusID = null; + operationStatusTimer.stop(); + operationStatusTimer = null; + operationStatusProhgressBar.setValue(0); + + sendChangeBackToSourceAddress.setEnabled(true); + sendButton.setEnabled(true); + balanceAddressCombo.setEnabled(true); + destinationAddressField.setEnabled(true); + destinationAmountField.setEnabled(true); + transactionFeeField.setEnabled(true); + destinationMemoField.setEnabled(true); + } else { + // Update the progress + operationStatusLabel.setText(langUtil.getString( + "send.cash.panel.operation.status.progress.label")); + operationStatusCounter += 2; + int progress = 0; + if (operationStatusCounter <= 100) { + progress = operationStatusCounter; + } else { + progress = 100 + (((operationStatusCounter - 100) * 6) / 10); + } + operationStatusProhgressBar.setValue(progress); + } + + SendCashPanel.this.repaint(); + } catch (Exception ex) { + Log.error("Unexpected error: ", ex); + SendCashPanel.this.errorReporter.reportError(ex); + } + } + }); + operationStatusTimer.setInitialDelay(0); + operationStatusTimer.start(); + } + + public void prepareForSending(String address) { + destinationAddressField.setText(address); + } + + private void updateWalletAddressPositiveBalanceComboBox(String[][] data) + throws WalletCallException, IOException, InterruptedException { + String[][] newAddressBalanceData = data; + + // The data may be null if nothing is yet obtained + if (newAddressBalanceData == null) { + return; + } + + lastAddressBalanceData = newAddressBalanceData; + + comboBoxItems = new String[lastAddressBalanceData.length]; + for (int i = 0; i < lastAddressBalanceData.length; i++) { + // Do numeric formatting or else we may get 1.1111E-5 + comboBoxItems[i] = + new DecimalFormat("########0.00######") + .format(Double.valueOf(lastAddressBalanceData[i][0])) + + " - " + lastAddressBalanceData[i][1]; + } + + int selectedIndex = balanceAddressCombo.getSelectedIndex(); + boolean isEnabled = balanceAddressCombo.isEnabled(); + this.comboBoxParentPanel.remove(balanceAddressCombo); + balanceAddressCombo = new ZelCashJComboBox<>(comboBoxItems); + comboBoxParentPanel.add(balanceAddressCombo); + if ((balanceAddressCombo.getItemCount() > 0) && (selectedIndex >= 0) && + (balanceAddressCombo.getItemCount() > selectedIndex)) { + balanceAddressCombo.setSelectedIndex(selectedIndex); + } + balanceAddressCombo.setEnabled(isEnabled); + + this.validate(); + this.repaint(); + } + + private String[][] getAddressPositiveBalanceDataFromWallet() + throws WalletCallException, IOException, InterruptedException { + // Z Addresses - they are OK + String[] zAddresses = clientCaller.getWalletZAddresses(); + + // T Addresses created inside wallet that may be empty + String[] tAddresses = this.clientCaller.getWalletAllPublicAddresses(); + Set tStoredAddressSet = new HashSet<>(); + for (String address : tAddresses) { + tStoredAddressSet.add(address); + } + + // T addresses with unspent outputs (even if not GUI created)... + String[] tAddressesWithUnspentOuts = + this.clientCaller.getWalletPublicAddressesWithUnspentOutputs(); + Set tAddressSetWithUnspentOuts = new HashSet<>(); + for (String address : tAddressesWithUnspentOuts) { + tAddressSetWithUnspentOuts.add(address); + } + + // Combine all known T addresses + Set tAddressesCombined = new HashSet<>(); + tAddressesCombined.addAll(tStoredAddressSet); + tAddressesCombined.addAll(tAddressSetWithUnspentOuts); + + String[][] tempAddressBalances = + new String[zAddresses.length + tAddressesCombined.size()][]; + + int count = 0; + + for (String address : tAddressesCombined) { + String balance = this.clientCaller.getBalanceForAddress(address); + if (Double.valueOf(balance) > 0) { + tempAddressBalances[count++] = new String[] {balance, address}; + } + } + + for (String address : zAddresses) { + String balance = this.clientCaller.getBalanceForAddress(address); + if (Double.valueOf(balance) > 0) { + tempAddressBalances[count++] = new String[] {balance, address}; + } + } + + String[][] addressBalances = new String[count][]; + System.arraycopy(tempAddressBalances, 0, addressBalances, 0, count); + + return addressBalances; + } + + private void reportCompleteOperationToTheUser(String amount, + String sourceAddress, + String destinationAddress) + throws InterruptedException, WalletCallException, IOException, + URISyntaxException { + if (clientCaller.isCompletedOperationSuccessful(operationStatusID)) { + operationStatusLabel.setText( + langUtil.getString("send.cash.panel.operation.status.success.label")); + String TXID = clientCaller.getSuccessfulOperationTXID(operationStatusID); + + Object[] options = + langUtil.getString("send.cash.panel.operation.complete.report") + .split(":"); + + int option = JOptionPane.showOptionDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString( + "send.cash.panel.operation.complete.report.success.text", amount, + sourceAddress, destinationAddress, TXID), + langUtil.getString( + "send.cash.panel.operation.complete.report.success.title"), + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, + options, options[0]); + + if (option == 1) { + // Copy the transaction ID to clipboard + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(new StringSelection(TXID), null); + } else if (option == 2) { + // Open block explorer + Log.info("Transaction ID for block explorer is: " + TXID); + // TODO: code duplication with transactions table + String urlPrefix = "https://explorer.zel.cash/tx/"; + if (installationObserver.isOnTestNet()) { + urlPrefix = "https://testnet.zel.cash/tx/"; } - - // Make sure the confirmed balance for the address is sufficient - if (new BigDecimal(balance).subtract(new BigDecimal(amount)).subtract(new BigDecimal(fee)).compareTo(new BigDecimal("0")) < 0) - { - JOptionPane.showMessageDialog( - SendCashPanel.this.getRootPane().getParent(), - langUtil.getString("send.cash.panel.insufficient.balance", sourceAddress, balance, amount, fee), - langUtil.getString("send.cash.panel.insufficient.balance.title"), - JOptionPane.ERROR_MESSAGE); - - return false; - } - - return true; - } + Desktop.getDesktop().browse(new URL(urlPrefix + TXID).toURI()); + } + + // Call the backup tracker - to remind the user + this.backupTracker.handleNewTransaction(); + } else { + String errorMessage = + clientCaller.getOperationFinalErrorMessage(operationStatusID); + operationStatusLabel.setText(langUtil.getString( + "send.cash.panel.operation.status.error.label", errorMessage)); + + JOptionPane.showMessageDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString("send.cash.panel.option.pane.error.report.text", + errorMessage), + langUtil.getString("send.cash.panel.option.pane.error.report.title"), + JOptionPane.ERROR_MESSAGE); + } + } + + // Checks if a number has more than 8 fractional digits. This is not normally + // allowed for ZEL Input must be a decimal number! + private boolean hasExcessiveFractionalDigits(String field) { + BigDecimal num = new BigDecimal(field); + DecimalFormatSymbols decSymbols = new DecimalFormatSymbols(Locale.ROOT); + DecimalFormat longFormat = new DecimalFormat( + "############################0.00###############################", + decSymbols); + String formattedNumber = longFormat.format(num); + String fractionalPart = + formattedNumber.substring(formattedNumber.indexOf(".") + 1); + + if (fractionalPart.length() > 8) { + return true; + } + + return false; + } + + /** + * Checks the conditions necessary for sending back the change. Also issues a + * warning to the user on the nature of this operation. + * + * @param sourceAddress + * @param destinationAddress + * @param amount + * @param memo + * @param fee + * + * @return true if all conditions are met and the user has not cancelled the + * operation + */ + private boolean warnAndCheckConditionsForSendingBackChange( + String sourceAddress, String destinationAddress, String amount, + String memo, String fee) + throws WalletCallException, InterruptedException, IOException { + String balance = this.clientCaller.getBalanceForAddress(sourceAddress); + + // Get a confirmation from the user about the operation - general warning + String userDir = OSUtil.getSettingsDirectory(); + File sendChangeBackNotToBeShownFlagFile = new File( + userDir + File.separator + "sendBackChangeWarningNotToBeShown.flag"); + if (!sendChangeBackNotToBeShownFlagFile.exists()) { + Object[] options = { + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.button.yes"), + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.button.no"), + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.button.not.again")}; + + int option = JOptionPane.showOptionDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString( + "send.cash.panel.send.change.back.general.warning"), + langUtil.getString( + "send.cash.panel.send.change.back.general.warning.title"), + JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null, + options, options[0]); + + if (option == 2) { + sendChangeBackNotToBeShownFlagFile.createNewFile(); + } + + if (option == 1 || option == JOptionPane.CLOSED_OPTION) { + return false; + } + } + + // Make sure the confirmed balance for the address is sufficient + if (new BigDecimal(balance) + .subtract(new BigDecimal(amount)) + .subtract(new BigDecimal(fee)) + .compareTo(new BigDecimal("0")) < 0) { + JOptionPane.showMessageDialog( + SendCashPanel.this.getRootPane().getParent(), + langUtil.getString("send.cash.panel.insufficient.balance", + sourceAddress, balance, amount, fee), + langUtil.getString("send.cash.panel.insufficient.balance.title"), + JOptionPane.ERROR_MESSAGE); + + return false; + } + + return true; + } } diff --git a/src/java/com/vaklinov/zcashui/WalletOperations.java b/src/java/com/vaklinov/zcashui/WalletOperations.java index fd2e629d..64227f8e 100644 --- a/src/java/com/vaklinov/zcashui/WalletOperations.java +++ b/src/java/com/vaklinov/zcashui/WalletOperations.java @@ -1,11 +1,14 @@ /************************************************************************************************ - * ____________ _ _ _____ _ _____ _ _ _______ __ _ _ _ - * |___ / ____| \ | |/ ____| | | / ____| | | |_ _\ \ / / | | | | | - * / /| |__ | \| | | __ _ ___| |__ | | __| | | | | | \ \ /\ / /_ _| | | ___| |_ - * / / | __| | . ` | | / _` / __| '_ \| | |_ | | | | | | \ \/ \/ / _` | | |/ _ \ __| - * / /__| |____| |\ | |___| (_| \__ \ | | | |__| | |__| |_| |_ \ /\ / (_| | | | __/ |_ - * /_____|______|_| \_|\_____\__,_|___/_| |_|\_____|\____/|_____| \/ \/ \__,_|_|_|\___|\__| - * + * ____________ _ _ _____ _ _____ _ _ _______ __ + *_ _ _ + * |___ / ____| \ | |/ ____| | | / ____| | | |_ _\ \ / / + *| | | | | / /| |__ | \| | | __ _ ___| |__ | | __| | | | | | \ \ + * /\ / /_ _| | | ___| |_ / / | __| | . ` | | / _` / __| '_ \| | |_ | | | + *| | | \ \/ \/ / _` | | |/ _ \ __| / /__| |____| |\ | |___| (_| \__ \ | | | + *|__| | |__| |_| |_ \ /\ / (_| | | | __/ |_ + * /_____|______|_| \_|\_____\__,_|___/_| |_|\_____|\____/|_____| \/ \/ + *\__,_|_|_|\___|\__| + * * Copyright (c) 2016-2018 The ZEN Developers * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -14,10 +17,10 @@ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -28,6 +31,23 @@ **********************************************************************************/ package com.vaklinov.zcashui; +import com.cabecinha84.zelcashui.ChangePasswordEncryptionDialog; +import com.cabecinha84.zelcashui.ZelCashJDialog; +import com.cabecinha84.zelcashui.ZelCashJFileChooser; +import com.cabecinha84.zelcashui.ZelCashJFrame; +import com.cabecinha84.zelcashui.ZelCashJLabel; +import com.cabecinha84.zelcashui.ZelCashJProgressBar; +import com.cabecinha84.zelcashui.ZelCashJTabbedPane; +import com.cabecinha84.zelcashui.ZelCashSproutToSaplingDialog; +import com.cabecinha84.zelcashui.ZelCashZelNodeDialog; +import com.cabecinha84.zelcashui.ZelNodesPanel; +import com.cabecinha84.zelcashui.ZelcashRescanDialog; +import com.eclipsesource.json.JsonObject; +import com.eclipsesource.json.JsonValue; +import com.vaklinov.zcashui.ZCashClientCaller.WalletCallException; +import com.vaklinov.zcashui.arizen.models.Address; +import com.vaklinov.zcashui.arizen.repo.ArizenWallet; +import com.vaklinov.zcashui.arizen.repo.WalletRepo; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Cursor; @@ -48,7 +68,6 @@ import java.util.Map; import java.util.Properties; import java.util.Set; - import javax.swing.BorderFactory; import javax.swing.JDialog; import javax.swing.JFileChooser; @@ -58,985 +77,975 @@ import javax.swing.SwingWorker; import javax.swing.filechooser.FileNameExtensionFilter; -import com.cabecinha84.zelcashui.ChangePasswordEncryptionDialog; -import com.cabecinha84.zelcashui.ZelCashJDialog; -import com.cabecinha84.zelcashui.ZelCashJFileChooser; -import com.cabecinha84.zelcashui.ZelCashJFrame; -import com.cabecinha84.zelcashui.ZelCashJLabel; -import com.cabecinha84.zelcashui.ZelCashJProgressBar; -import com.cabecinha84.zelcashui.ZelCashJTabbedPane; -import com.cabecinha84.zelcashui.ZelCashSproutToSaplingDialog; -import com.cabecinha84.zelcashui.ZelCashZelNodeDialog; -import com.cabecinha84.zelcashui.ZelNodesPanel; -import com.cabecinha84.zelcashui.ZelcashRescanDialog; -import com.eclipsesource.json.JsonObject; -import com.eclipsesource.json.JsonValue; -import com.vaklinov.zcashui.ZCashClientCaller.WalletCallException; -import com.vaklinov.zcashui.arizen.models.Address; -import com.vaklinov.zcashui.arizen.repo.ArizenWallet; -import com.vaklinov.zcashui.arizen.repo.WalletRepo; - - /** * Provides miscellaneous operations for the wallet file. */ -public class WalletOperations -{ - private static final int POLL_PERIOD = 5000; - private static final int STARTUP_ERROR_CODE = -28; - - private ZCashUI parent; - private ZelCashJTabbedPane tabs; - private DashboardPanel dashboard; - private SendCashPanel sendCash; - private AddressesPanel addresses; - private LabelStorage labelStorage; - - private ZCashInstallationObserver installationObserver; - private ZCashClientCaller clientCaller; - private StatusUpdateErrorReporter errorReporter; - private BackupTracker backupTracker; - - private LanguageUtil langUtil; - - - public WalletOperations(ZCashUI parent, - ZelCashJTabbedPane tabs, - DashboardPanel dashboard, - AddressesPanel addresses, - SendCashPanel sendCash, - - ZCashInstallationObserver installationObserver, - ZCashClientCaller clientCaller, - StatusUpdateErrorReporter errorReporter, - BackupTracker backupTracker, - LabelStorage labelStorage) - throws IOException, InterruptedException, WalletCallException - { - this.parent = parent; - this.tabs = tabs; - this.dashboard = dashboard; - this.addresses = addresses; - this.sendCash = sendCash; - - this.installationObserver = installationObserver; - this.clientCaller = clientCaller; - this.errorReporter = errorReporter; - - this.backupTracker = backupTracker; - this.langUtil = LanguageUtil.instance(); - this.labelStorage = labelStorage; - } - - - public void changeWalletPassword() - { - try - { - if (!this.clientCaller.isWalletEncrypted()) - { - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.option.pane.wallet.not.encryppted.error.text"), - langUtil.getString("wallet.operations.option.pane.wallet.not.encryppted.error.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - ChangePasswordEncryptionDialog pd = new ChangePasswordEncryptionDialog(this.parent); - pd.setVisible(true); - - if (!pd.isOKPressed()) - { - return; - } - - Cursor oldCursor = this.parent.getCursor(); - try - { - - this.parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - this.parent.stopTimers(); - if(!this.checkExperimentalFeaturesOn()) { - this.parent.restartDaemon(false, false); - } - this.clientCaller.passPhraseChangeWallet(pd.getPassword(), pd.getNewPassword()); - - this.parent.setCursor(oldCursor); - } catch (WalletCallException wce) - { - this.parent.setCursor(oldCursor); - Log.error("Unexpected error: ", wce); - - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("encryption.error.change.password.message", wce.getMessage().replace(",", ",\n")), - langUtil.getString("encryption.error.change.password.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.option.pane.change.password.success.text"), - langUtil.getString("wallet.operations.option.pane.change.password.success.title"), - JOptionPane.INFORMATION_MESSAGE); - - this.parent.exitProgram(); - - } catch (Exception e) - { - this.errorReporter.reportError(e, false); - } - } - - - public void encryptWallet() - { - try - { - if (this.clientCaller.isWalletEncrypted()) - { - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.option.pane.already.encrypted.error.text"), - langUtil.getString("wallet.operations.option.pane.already.encrypted.error.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - PasswordEncryptionDialog pd = new PasswordEncryptionDialog(this.parent); - pd.setVisible(true); - - if (!pd.isOKPressed()) - { - return; - } - - Cursor oldCursor = this.parent.getCursor(); - try - { - - this.parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - this.parent.stopTimers(); - if(!this.checkExperimentalFeaturesOn()) { - this.parent.restartDaemon(false, false); - } - this.clientCaller.encryptWallet(pd.getPassword()); - - this.parent.setCursor(oldCursor); - } catch (WalletCallException wce) - { - this.parent.setCursor(oldCursor); - Log.error("Unexpected error: ", wce); - - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.option.pane.encryption.error.text", wce.getMessage().replace(",", ",\n")), - langUtil.getString("wallet.operations.option.pane.encryption.error.title"), - JOptionPane.ERROR_MESSAGE); - this.parent.exitProgram(); - } - - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.option.pane.encryption.success.text"), - langUtil.getString("wallet.operations.option.pane.encryption.success.title"), - JOptionPane.INFORMATION_MESSAGE); - - this.parent.exitProgram(); - - } catch (Exception e) - { - this.errorReporter.reportError(e, false); - } - } - - private boolean checkExperimentalFeaturesOn() throws IOException { - boolean experimentalFeaturesOn = true; - String blockchainDir = OSUtil.getBlockchainDirectory(); - File zelcashConf = new File(blockchainDir + File.separator + "zelcash.conf"); - Properties confProps = new Properties(); - FileInputStream fis = null; - String property = null; - FileWriter fw = null; - try - { - fis = new FileInputStream(zelcashConf); - fw = new FileWriter(zelcashConf,true); //the true will append the new data - confProps.load(fis); - property = confProps.getProperty("experimentalfeatures"); - if(property == null) { - fw.write(System.getProperty("line.separator") + "experimentalfeatures=1"); - experimentalFeaturesOn = false; - Log.info("Adding experimentalfeatures=1"); - } - property = confProps.getProperty("developerencryptwallet"); - if(property == null) { - fw.write(System.getProperty("line.separator") + "developerencryptwallet=1"); - experimentalFeaturesOn = false; - Log.info("Adding developerencryptwallet=1"); - } - } finally - { - if (fw != null) { - fw.close(); - } - if (fis != null) - { - fis.close(); - } - } - return experimentalFeaturesOn; - - } - public void backupWallet() - { - try - { - this.issueBackupDirectoryWarning(); - - ZelCashJFileChooser fileChooser = new ZelCashJFileChooser(); - fileChooser.setDialogTitle(langUtil.getString("wallet.operations.dialog.backup.wallet.title")); - fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - fileChooser.setCurrentDirectory(OSUtil.getUserHomeDirectory()); - - int result = fileChooser.showSaveDialog(this.parent); - - if (result != JFileChooser.APPROVE_OPTION) - { - return; - } - - File f = fileChooser.getSelectedFile(); - - Cursor oldCursor = this.parent.getCursor(); - String path = null; - try - { - this.parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - path = this.clientCaller.backupWallet(f.getName()); - - this.backupTracker.handleBackup(); - - this.parent.setCursor(oldCursor); - } catch (WalletCallException wce) - { - this.parent.setCursor(oldCursor); - Log.error("Unexpected error: ", wce); - - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.option.pane.backup.wallet.error.text", wce.getMessage().replace(",", ",\n")), - langUtil.getString("wallet.operations.option.pane.backup.wallet.error.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.option.pane.backup.wallet.success.text", f.getName(), path), - - langUtil.getString("wallet.operations.option.pane.backup.wallet.success.title"), JOptionPane.INFORMATION_MESSAGE); - - } catch (Exception e) - { - this.errorReporter.reportError(e, false); - } - } - - - public void exportWalletPrivateKeys() - { - try - { - if (this.clientCaller.isWalletEncrypted()) - { - boolean passwordOk = false; - int retrys = 0; - while(!passwordOk && retrys<3) { - ++retrys; - PasswordDialog pd = new PasswordDialog((ZelCashJFrame)(this.parent)); - pd.setVisible(true); - - if (!pd.isOKPressed()) - { - return; - } - try { - this.clientCaller.unlockWallet(pd.getPassword()); - passwordOk = true; - } - catch (Exception ex) { - Log.error("Error unlocking wallet:"+ex.getMessage()); - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("encryption.error.unlocking.message", ex.getMessage()), - langUtil.getString("encryption.error.unlocking.title"), - JOptionPane.ERROR_MESSAGE); - } - } - if(!passwordOk) { - Log.info("Failed to enter correct password for third time, wallet will close."); - System.exit(1); - } - } - - this.issueBackupDirectoryWarning(); - - ZelCashJFileChooser fileChooser = new ZelCashJFileChooser(); - fileChooser.setDialogTitle(langUtil.getString("wallet.operations.dialog.export.private.keys.title")); - fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - fileChooser.setCurrentDirectory(OSUtil.getUserHomeDirectory()); - - int result = fileChooser.showSaveDialog(this.parent); - - if (result != JFileChooser.APPROVE_OPTION) - { - return; - } - - File f = fileChooser.getSelectedFile(); - - Cursor oldCursor = this.parent.getCursor(); - String path = null; - try - { - this.parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - path = this.clientCaller.exportWallet(f.getName()); - this.backupTracker.handleBackup(); - - this.parent.setCursor(oldCursor); - } catch (WalletCallException wce) - { - this.parent.setCursor(oldCursor); - Log.error("Unexpected error: ", wce); - - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.dialog.export.private.keys.error.text", - "\n" + wce.getMessage().replace(",", ",\n")), - langUtil.getString("wallet.operations.dialog.export.private.keys.error.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.dialog.export.private.keys.success.text", f.getName(), path ), - langUtil.getString("wallet.operations.dialog.export.private.keys.success.title"), - JOptionPane.INFORMATION_MESSAGE); - - } catch (Exception e) - { - this.errorReporter.reportError(e, false); - } - } - - public void reindexWallet() { - Object[] options = - { - langUtil.getString("send.cash.panel.option.pane.confirm.operation.button.yes"), - langUtil.getString("send.cash.panel.option.pane.confirm.operation.button.no") - }; - int option; - option = JOptionPane.showOptionDialog( - this.parent, - langUtil.getString("wallet.operations.dialog.reindex.message"), - langUtil.getString("wallet.operations.dialog.reindex.title"), - JOptionPane.DEFAULT_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - options[1]); - - if (option == 0) - { - this.parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - parent.restartDaemon(true, false); - try { - restartUI(); - } catch (IOException | InterruptedException | WalletCallException e1) { - Log.error("Error restarting the UI, the wallet will be closed. Error:"+e1.getMessage()); - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.2.text", - e1.getMessage()), - LanguageUtil.instance() - .getString("main.frame.option.pane.wallet.critical.error.2.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(1); - } - } - } - - public void rescanWallet() { - try - { - if (this.clientCaller.isWalletEncrypted()) - { - boolean passwordOk = false; - int retrys = 0; - while(!passwordOk && retrys<3) { - ++retrys; - PasswordDialog pd = new PasswordDialog((ZelCashJFrame)(this.parent)); - pd.setVisible(true); - - if (!pd.isOKPressed()) - { - return; - } - try { - this.clientCaller.unlockWallet(pd.getPassword()); - passwordOk = true; - } - catch (Exception ex) { - Log.error("Error unlocking wallet:"+ex.getMessage()); - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("encryption.error.unlocking.message", ex.getMessage()), - langUtil.getString("encryption.error.unlocking.title"), - JOptionPane.ERROR_MESSAGE); - } - } - if(!passwordOk) { - Log.info("Failed to enter correct password for third time, wallet will close."); - System.exit(1); - } - } - ZelcashRescanDialog kd = new ZelcashRescanDialog(this.parent, this.clientCaller); - kd.setVisible(true); - - } catch (Exception ex) - { - this.errorReporter.reportError(ex, false); - } - } - - public void sproutToSaplingMigrationTool() { - try { - String[] zAddresses = this.clientCaller.getWalletZAddresses(); - List listOfSapling = new ArrayList(); - List listOfSaplingWithLabels = new ArrayList(); - if(zAddresses.length == 0) { - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.dialog.sprouttosapling.nosapling.message"), - langUtil.getString("wallet.operations.dialog.sprouttosapling.nosapling.title"), - JOptionPane.INFORMATION_MESSAGE); - return; - } - String label; - String address; - listOfSapling.add(langUtil.getString("dialog.zelcashsprouttosaplingdialog.select")); - listOfSaplingWithLabels.add(langUtil.getString("dialog.zelcashsprouttosaplingdialog.select")); - for(int i = 0; i 0)) - { - address = label + " - " + address; - } - listOfSaplingWithLabels.add(address); - } - } - if(zAddresses.length == 0) { - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.dialog.sprouttosapling.nosapling.message"), - langUtil.getString("wallet.operations.dialog.sprouttosapling.nosapling.title"), - JOptionPane.INFORMATION_MESSAGE); - return; - } - - ZelCashSproutToSaplingDialog ad = new ZelCashSproutToSaplingDialog(this.parent, clientCaller, installationObserver, listOfSapling, listOfSaplingWithLabels); - ad.setVisible(true); - - } catch (HeadlessException | WalletCallException | IOException | InterruptedException e1) { - this.errorReporter.reportError(e1, false); - } - } - - public void importWalletPrivateKeys() - { - try { - if (this.clientCaller.isWalletEncrypted()) - { - boolean passwordOk = false; - int retrys = 0; - while(!passwordOk && retrys<3) { - ++retrys; - PasswordDialog pd = new PasswordDialog((ZelCashJFrame)(this.parent)); - pd.setVisible(true); - - if (!pd.isOKPressed()) - { - return; - } - try { - this.clientCaller.unlockWallet(pd.getPassword()); - passwordOk = true; - } - catch (Exception ex) { - Log.error("Error unlocking wallet:"+ex.getMessage()); - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("encryption.error.unlocking.message", ex.getMessage()), - langUtil.getString("encryption.error.unlocking.title"), - JOptionPane.ERROR_MESSAGE); - } - } - if(!passwordOk) { - Log.info("Failed to enter correct password for third time, wallet will close."); - System.exit(1); - } - } - } catch (HeadlessException | WalletCallException | IOException | InterruptedException e1) { - this.errorReporter.reportError(e1, false); - } - Object[] options = - { - langUtil.getString("button.option.yes"), - langUtil.getString("button.option.no") - }; - int option = JOptionPane.showOptionDialog( - this.parent, - langUtil.getString("wallet.operations.dialog.import.private.keys.notice.text"), - langUtil.getString("wallet.operations.dialog.import.private.keys.notice.title"), - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - JOptionPane.NO_OPTION); - if (option == JOptionPane.NO_OPTION || option == JOptionPane.CLOSED_OPTION) - { - return; - } - - try - { - ZelCashJFileChooser fileChooser = new ZelCashJFileChooser(); - fileChooser.setDialogTitle(langUtil.getString("wallet.operations.file.chooser.import.private.keys.title")); - fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - - int result = fileChooser.showOpenDialog(this.parent); - - if (result != JFileChooser.APPROVE_OPTION) - { - return; - } - - File f = fileChooser.getSelectedFile(); - - Cursor oldCursor = this.parent.getCursor(); - try - { - this.parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - this.clientCaller.importWallet(f.getCanonicalPath()); - - this.parent.setCursor(oldCursor); - } catch (WalletCallException wce) - { - this.parent.setCursor(oldCursor); - Log.error("Unexpected error: ", wce); - - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.dialog.import.private.keys.error.text", wce.getMessage().replace(",", ",\n")), - langUtil.getString("wallet.operations.dialog.import.private.keys.error.title"), - JOptionPane.ERROR_MESSAGE); - return; - } - - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.dialog.import.private.keys.success.text",f.getCanonicalPath()), - langUtil.getString("wallet.operations.dialog.import.private.keys.success.title"), - JOptionPane.INFORMATION_MESSAGE); - - } catch (Exception e) - { - this.errorReporter.reportError(e, false); - } - } - - - public void showPrivateKey() - { - if (this.tabs.getSelectedIndex() != 2) - { - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.option.pane.own.address.view.private.key.text"), - langUtil.getString("wallet.operations.option.pane.own.address.view.private.key.title"), - JOptionPane.INFORMATION_MESSAGE); - this.tabs.setSelectedIndex(2); - return; - } - - String address = this.addresses.getSelectedAddress(); - - if (address == null) - { - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.option.pane.address.table.view.private.key.text"), - langUtil.getString("wallet.operations.option.pane.address.table.view.private.key.title"), - JOptionPane.INFORMATION_MESSAGE); - return; - } - - try - { - // Check for encrypted wallet - final boolean bEncryptedWallet = this.clientCaller.isWalletEncrypted(); - if (bEncryptedWallet) - { - boolean passwordOk = false; - int retrys = 0; - while(!passwordOk && retrys<3) { - ++retrys; - PasswordDialog pd = new PasswordDialog((ZelCashJFrame)(this.parent)); - pd.setVisible(true); - - if (!pd.isOKPressed()) - { - return; - } - try { - this.clientCaller.unlockWallet(pd.getPassword()); - passwordOk = true; - } - catch (Exception ex) { - Log.error("Error unlocking wallet:"+ex.getMessage()); - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("encryption.error.unlocking.message", ex.getMessage()), - langUtil.getString("encryption.error.unlocking.title"), - JOptionPane.ERROR_MESSAGE); - } - } - if(!passwordOk) { - Log.info("Failed to enter correct password for third time, wallet will close."); - System.exit(1); - } - } - - boolean isZAddress = Util.isZAddress(address); - - String privateKey = isZAddress ? - this.clientCaller.getZPrivateKey(address) : this.clientCaller.getTPrivateKey(address); - - // Lock the wallet again - if (bEncryptedWallet) - { - //this.clientCaller.lockWallet(); - } - - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - clipboard.setContents(new StringSelection(privateKey), null); - String adressType = isZAddress ? langUtil.getString("wallet.operations.private.address") - : langUtil.getString("wallet.operations.transparent.address"); - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("wallet.operations.option.pane.address.information.text", adressType, address, privateKey), - langUtil.getString("wallet.operations.option.pane.address.information.title"), JOptionPane.INFORMATION_MESSAGE); - } catch (Exception ex) - { - this.errorReporter.reportError(ex, false); - } - } - - - public void importSinglePrivateKey() - { - try - { - if (this.clientCaller.isWalletEncrypted()) - { - boolean passwordOk = false; - int retrys = 0; - while(!passwordOk && retrys<3) { - ++retrys; - PasswordDialog pd = new PasswordDialog((ZelCashJFrame)(this.parent)); - pd.setVisible(true); - - if (!pd.isOKPressed()) - { - return; - } - try { - this.clientCaller.unlockWallet(pd.getPassword()); - passwordOk = true; - } - catch (Exception ex) { - Log.error("Error unlocking wallet:"+ex.getMessage()); - JOptionPane.showMessageDialog( - this.parent, - langUtil.getString("encryption.error.unlocking.message", ex.getMessage()), - langUtil.getString("encryption.error.unlocking.title"), - JOptionPane.ERROR_MESSAGE); - } - } - if(!passwordOk) { - Log.info("Failed to enter correct password for third time, wallet will close."); - System.exit(1); - } - } - SingleKeyImportDialog kd = new SingleKeyImportDialog(this.parent, this.clientCaller); - kd.setVisible(true); - - } catch (Exception ex) - { - this.errorReporter.reportError(ex, false); - } - } - - - - /** - * export to Arizen wallet - */ - public void exportToArizenWallet() - { - final ZelCashJDialog dialog = new ZelCashJDialog(this.parent, langUtil.getString("wallet.operations.dialog.export.arizen.title")); - final ZelCashJLabel exportLabel = new ZelCashJLabel(); - final WalletRepo arizenWallet = new ArizenWallet(); - try { - ZelCashJFileChooser fileChooser = new ZelCashJFileChooser(); - fileChooser.setFileFilter(new FileNameExtensionFilter(langUtil.getString("wallet.operations.dialog.export.arizen.filechooser.filter"), "uawd")); - fileChooser.setDialogTitle(langUtil.getString("wallet.operations.dialog.export.arizen.filechooser.title")); - fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); - fileChooser.setCurrentDirectory(OSUtil.getUserHomeDirectory()); - int result = fileChooser.showDialog(this.parent, langUtil.getString("wallet.operations.dialog.export.arizen.filechooser.aprove.button")); - - if (result != JFileChooser.APPROVE_OPTION) { - return; - } - - File chooseFile = fileChooser.getSelectedFile(); - String fullPath = chooseFile.getAbsolutePath(); - if (!fullPath.endsWith(".uawd")) - fullPath += ".uawd"; - - final File f = new File(fullPath); - if (f.exists()) { - Object[] options = - { - langUtil.getString("button.option.yes"), - langUtil.getString("button.option.no") - }; - int r = JOptionPane.showOptionDialog((Component) null, - langUtil.getString("wallet.operations.dialog.delete.file.confirmation", f.getName()), - langUtil.getString("wallet.operations.dialog.delete.file.confirmation.title"), - JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - options, - JOptionPane.NO_OPTION); - if (r == 1 || r == JOptionPane.CLOSED_OPTION) { - return; - } - Files.delete(f.toPath()); - } - final String strFullpath = fullPath; - - dialog.setSize(300, 75); - dialog.setLocationRelativeTo(parent); - dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); - dialog.setLayout(new BorderLayout()); - - ZelCashJProgressBar progressBar = new ZelCashJProgressBar(); - progressBar.setIndeterminate(true); - dialog.add(progressBar, BorderLayout.CENTER); - exportLabel.setText(langUtil.getString("wallet.operations.dialog.export.label")); - exportLabel.setHorizontalAlignment(JLabel.CENTER); - exportLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 16, 0)); - - dialog.add(exportLabel, BorderLayout.SOUTH); - dialog.setVisible(true); - - SwingWorker worker = new SwingWorker() { - @Override - public Boolean doInBackground() { - try { - arizenWallet.createWallet(f); - Thread.sleep(750); - updateProgressText(langUtil.getString("wallet.operations.dialog.export.progress.reading.text")); - String[] zaddress = clientCaller.getWalletZAddresses(); - String[] taddress = clientCaller.getWalletAllPublicAddresses(); - String[] tAddressesWithUnspentOuts = clientCaller.getWalletPublicAddressesWithUnspentOutputs(); - - Set
addressPublicSet = new HashSet
(); - Set
addressPrivateSet = new HashSet
(); - - Map tMap = new HashMap(); - Map zMap = new HashMap(); - - for (String straddr : taddress) { - String pk = clientCaller.getTPrivateKey(straddr); - String pkHex = Util.wifToHex(pk); - String balance = clientCaller.getBalanceForAddress(straddr); - Address addr = new Address(Address.ADDRESS_TYPE.TRANSPARENT, straddr, pkHex, balance); - tMap.put(straddr, addr); - } - - for (String straddr : tAddressesWithUnspentOuts) { - String pk = clientCaller.getTPrivateKey(straddr); - String pkHex = Util.wifToHex(pk); - String balance = clientCaller.getBalanceForAddress(straddr); - Address addr = new Address(Address.ADDRESS_TYPE.TRANSPARENT, straddr, pkHex, balance); - tMap.put(straddr, addr); - } - - for (String straddr : zaddress) { - String pk = clientCaller.getZPrivateKey(straddr); - String balance = clientCaller.getBalanceForAddress(straddr); - Address addr = new Address(Address.ADDRESS_TYPE.PRIVATE, straddr, pk, balance); - zMap.put(straddr, addr); - } - addressPublicSet.addAll(tMap.values()); - addressPrivateSet.addAll(zMap.values()); - Thread.sleep(500); - - updateProgressText(langUtil.getString("wallet.operations.dialog.export.progress.writing.text")); - arizenWallet.insertAddressBatch(addressPublicSet); - if (addressPrivateSet.size() > 0) - { - arizenWallet.insertAddressBatch(addressPrivateSet); - } - Thread.sleep(1000); - - updateProgressText(langUtil.getString("wallet.operations.dialog.export.progress.finished.text")); - Thread.sleep(750); - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - dialog.dispose(); - JOptionPane.showConfirmDialog(parent, - langUtil.getString("wallet.operations.option.pane.export.success.info.text", strFullpath), - langUtil.getString("wallet.operations.option.pane.export.success.info.title"), - JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE); - } - }); - - } catch (Exception e) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - dialog.dispose(); - } - }); - errorReporter.reportError(e, false); - } finally { - try { - if (arizenWallet != null && arizenWallet.isOpen()) { - arizenWallet.close(); - } - } catch (Exception ex) { - errorReporter.reportError(ex, false); - } - } - return true; - } - - private void updateProgressText(final String text) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - exportLabel.setText(text); - } - }); - - } - }; - - worker.execute(); - - } catch (Exception ex) { - errorReporter.reportError(ex, false); - } - } - - - - private void issueBackupDirectoryWarning() - throws IOException - { - String userDir = OSUtil.getSettingsDirectory(); - File warningFlagFile = new File(userDir + File.separator + "backupInfoShownNG.flag"); - if (warningFlagFile.exists()) - { +public class WalletOperations { + private static final int POLL_PERIOD = 5000; + private static final int STARTUP_ERROR_CODE = -28; + + private ZCashUI parent; + private ZelCashJTabbedPane tabs; + private DashboardPanel dashboard; + private SendCashPanel sendCash; + private AddressesPanel addresses; + private LabelStorage labelStorage; + + private ZCashInstallationObserver installationObserver; + private ZCashClientCaller clientCaller; + private StatusUpdateErrorReporter errorReporter; + private BackupTracker backupTracker; + + private LanguageUtil langUtil; + + public WalletOperations(ZCashUI parent, ZelCashJTabbedPane tabs, + DashboardPanel dashboard, AddressesPanel addresses, + SendCashPanel sendCash, + + ZCashInstallationObserver installationObserver, + ZCashClientCaller clientCaller, + StatusUpdateErrorReporter errorReporter, + BackupTracker backupTracker, + LabelStorage labelStorage) + throws IOException, InterruptedException, WalletCallException { + this.parent = parent; + this.tabs = tabs; + this.dashboard = dashboard; + this.addresses = addresses; + this.sendCash = sendCash; + + this.installationObserver = installationObserver; + this.clientCaller = clientCaller; + this.errorReporter = errorReporter; + + this.backupTracker = backupTracker; + this.langUtil = LanguageUtil.instance(); + this.labelStorage = labelStorage; + } + + public void changeWalletPassword() { + try { + if (!this.clientCaller.isWalletEncrypted()) { + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.option.pane.wallet.not.encryppted.error.text"), + langUtil.getString( + "wallet.operations.option.pane.wallet.not.encryppted.error.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + ChangePasswordEncryptionDialog pd = + new ChangePasswordEncryptionDialog(this.parent); + pd.setVisible(true); + + if (!pd.isOKPressed()) { + return; + } + + Cursor oldCursor = this.parent.getCursor(); + try { + + this.parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + this.parent.stopTimers(); + if (!this.checkExperimentalFeaturesOn()) { + this.parent.restartDaemon(false, false); + } + this.clientCaller.passPhraseChangeWallet(pd.getPassword(), + pd.getNewPassword()); + + this.parent.setCursor(oldCursor); + } catch (WalletCallException wce) { + this.parent.setCursor(oldCursor); + Log.error("Unexpected error: ", wce); + + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString("encryption.error.change.password.message", + wce.getMessage().replace(",", ",\n")), + langUtil.getString("encryption.error.change.password.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.option.pane.change.password.success.text"), + langUtil.getString( + "wallet.operations.option.pane.change.password.success.title"), + JOptionPane.INFORMATION_MESSAGE); + + this.parent.exitProgram(); + + } catch (Exception e) { + this.errorReporter.reportError(e, false); + } + } + + public void encryptWallet() { + try { + if (this.clientCaller.isWalletEncrypted()) { + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.option.pane.already.encrypted.error.text"), + langUtil.getString( + "wallet.operations.option.pane.already.encrypted.error.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + PasswordEncryptionDialog pd = new PasswordEncryptionDialog(this.parent); + pd.setVisible(true); + + if (!pd.isOKPressed()) { + return; + } + + Cursor oldCursor = this.parent.getCursor(); + try { + + this.parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + this.parent.stopTimers(); + if (!this.checkExperimentalFeaturesOn()) { + this.parent.restartDaemon(false, false); + } + this.clientCaller.encryptWallet(pd.getPassword()); + + this.parent.setCursor(oldCursor); + } catch (WalletCallException wce) { + this.parent.setCursor(oldCursor); + Log.error("Unexpected error: ", wce); + + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.option.pane.encryption.error.text", + wce.getMessage().replace(",", ",\n")), + langUtil.getString( + "wallet.operations.option.pane.encryption.error.title"), + JOptionPane.ERROR_MESSAGE); + this.parent.exitProgram(); + } + + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.option.pane.encryption.success.text"), + langUtil.getString( + "wallet.operations.option.pane.encryption.success.title"), + JOptionPane.INFORMATION_MESSAGE); + + this.parent.exitProgram(); + + } catch (Exception e) { + this.errorReporter.reportError(e, false); + } + } + + private boolean checkExperimentalFeaturesOn() throws IOException { + boolean experimentalFeaturesOn = true; + String blockchainDir = OSUtil.getBlockchainDirectory(); + File zelcashConf = + new File(blockchainDir + File.separator + "zelcash.conf"); + Properties confProps = new Properties(); + FileInputStream fis = null; + String property = null; + FileWriter fw = null; + try { + fis = new FileInputStream(zelcashConf); + fw = new FileWriter(zelcashConf, + true); // the true will append the new data + confProps.load(fis); + property = confProps.getProperty("experimentalfeatures"); + if (property == null) { + fw.write(System.getProperty("line.separator") + + "experimentalfeatures=1"); + experimentalFeaturesOn = false; + Log.info("Adding experimentalfeatures=1"); + } + property = confProps.getProperty("developerencryptwallet"); + if (property == null) { + fw.write(System.getProperty("line.separator") + + "developerencryptwallet=1"); + experimentalFeaturesOn = false; + Log.info("Adding developerencryptwallet=1"); + } + } finally { + if (fw != null) { + fw.close(); + } + if (fis != null) { + fis.close(); + } + } + return experimentalFeaturesOn; + } + public void backupWallet() { + try { + this.issueBackupDirectoryWarning(); + + ZelCashJFileChooser fileChooser = new ZelCashJFileChooser(); + fileChooser.setDialogTitle( + langUtil.getString("wallet.operations.dialog.backup.wallet.title")); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooser.setCurrentDirectory(OSUtil.getUserHomeDirectory()); + + int result = fileChooser.showSaveDialog(this.parent); + + if (result != JFileChooser.APPROVE_OPTION) { + return; + } + + File f = fileChooser.getSelectedFile(); + + Cursor oldCursor = this.parent.getCursor(); + String path = null; + try { + this.parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + path = this.clientCaller.backupWallet(f.getName()); + + this.backupTracker.handleBackup(); + + this.parent.setCursor(oldCursor); + } catch (WalletCallException wce) { + this.parent.setCursor(oldCursor); + Log.error("Unexpected error: ", wce); + + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.option.pane.backup.wallet.error.text", + wce.getMessage().replace(",", ",\n")), + langUtil.getString( + "wallet.operations.option.pane.backup.wallet.error.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.option.pane.backup.wallet.success.text", + f.getName(), path), + + langUtil.getString( + "wallet.operations.option.pane.backup.wallet.success.title"), + JOptionPane.INFORMATION_MESSAGE); + + } catch (Exception e) { + this.errorReporter.reportError(e, false); + } + } + + public void exportWalletPrivateKeys() { + try { + if (this.clientCaller.isWalletEncrypted()) { + boolean passwordOk = false; + int retrys = 0; + while (!passwordOk && retrys < 3) { + ++retrys; + PasswordDialog pd = new PasswordDialog((ZelCashJFrame)(this.parent)); + pd.setVisible(true); + + if (!pd.isOKPressed()) { + return; + } + try { + this.clientCaller.unlockWallet(pd.getPassword()); + passwordOk = true; + } catch (Exception ex) { + Log.error("Error unlocking wallet:" + ex.getMessage()); + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString("encryption.error.unlocking.message", + ex.getMessage()), + langUtil.getString("encryption.error.unlocking.title"), + JOptionPane.ERROR_MESSAGE); + } + } + if (!passwordOk) { + Log.info( + "Failed to enter correct password for third time, wallet will close."); + System.exit(1); + } + } + + this.issueBackupDirectoryWarning(); + + ZelCashJFileChooser fileChooser = new ZelCashJFileChooser(); + fileChooser.setDialogTitle(langUtil.getString( + "wallet.operations.dialog.export.private.keys.title")); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooser.setCurrentDirectory(OSUtil.getUserHomeDirectory()); + + int result = fileChooser.showSaveDialog(this.parent); + + if (result != JFileChooser.APPROVE_OPTION) { + return; + } + + File f = fileChooser.getSelectedFile(); + + Cursor oldCursor = this.parent.getCursor(); + String path = null; + try { + this.parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + path = this.clientCaller.exportWallet(f.getName()); + this.backupTracker.handleBackup(); + + this.parent.setCursor(oldCursor); + } catch (WalletCallException wce) { + this.parent.setCursor(oldCursor); + Log.error("Unexpected error: ", wce); + + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.dialog.export.private.keys.error.text", + "\n" + wce.getMessage().replace(",", ",\n")), + langUtil.getString( + "wallet.operations.dialog.export.private.keys.error.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.dialog.export.private.keys.success.text", + f.getName(), path), + langUtil.getString( + "wallet.operations.dialog.export.private.keys.success.title"), + JOptionPane.INFORMATION_MESSAGE); + + } catch (Exception e) { + this.errorReporter.reportError(e, false); + } + } + + public void reindexWallet() { + Object[] options = { + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.button.yes"), + langUtil.getString( + "send.cash.panel.option.pane.confirm.operation.button.no")}; + int option; + option = JOptionPane.showOptionDialog( + this.parent, + langUtil.getString("wallet.operations.dialog.reindex.message"), + langUtil.getString("wallet.operations.dialog.reindex.title"), + JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, + options[1]); + + if (option == 0) { + this.parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + parent.restartDaemon(true, false); + try { + restartUI(); + } catch (IOException | InterruptedException | WalletCallException e1) { + Log.error("Error restarting the UI, the wallet will be closed. Error:" + + e1.getMessage()); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.2.text", + e1.getMessage()), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.2.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(1); + } + } + } + + public void rescanWallet() { + try { + if (this.clientCaller.isWalletEncrypted()) { + boolean passwordOk = false; + int retrys = 0; + while (!passwordOk && retrys < 3) { + ++retrys; + PasswordDialog pd = new PasswordDialog((ZelCashJFrame)(this.parent)); + pd.setVisible(true); + + if (!pd.isOKPressed()) { + return; + } + try { + this.clientCaller.unlockWallet(pd.getPassword()); + passwordOk = true; + } catch (Exception ex) { + Log.error("Error unlocking wallet:" + ex.getMessage()); + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString("encryption.error.unlocking.message", + ex.getMessage()), + langUtil.getString("encryption.error.unlocking.title"), + JOptionPane.ERROR_MESSAGE); + } + } + if (!passwordOk) { + Log.info( + "Failed to enter correct password for third time, wallet will close."); + System.exit(1); + } + } + ZelcashRescanDialog kd = + new ZelcashRescanDialog(this.parent, this.clientCaller); + kd.setVisible(true); + + } catch (Exception ex) { + this.errorReporter.reportError(ex, false); + } + } + + public void sproutToSaplingMigrationTool() { + try { + String[] zAddresses = this.clientCaller.getWalletZAddresses(); + List listOfSapling = new ArrayList(); + List listOfSaplingWithLabels = new ArrayList(); + if (zAddresses.length == 0) { + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.dialog.sprouttosapling.nosapling.message"), + langUtil.getString( + "wallet.operations.dialog.sprouttosapling.nosapling.title"), + JOptionPane.INFORMATION_MESSAGE); + return; + } + String label; + String address; + listOfSapling.add( + langUtil.getString("dialog.zelcashsprouttosaplingdialog.select")); + listOfSaplingWithLabels.add( + langUtil.getString("dialog.zelcashsprouttosaplingdialog.select")); + for (int i = 0; i < zAddresses.length; ++i) { + if (zAddresses[i].startsWith("za")) { + listOfSapling.add(zAddresses[i]); + label = this.labelStorage.getLabel(zAddresses[i]); + address = zAddresses[i]; + if ((label != null) && (label.length() > 0)) { + address = label + " - " + address; + } + listOfSaplingWithLabels.add(address); + } + } + if (zAddresses.length == 0) { + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.dialog.sprouttosapling.nosapling.message"), + langUtil.getString( + "wallet.operations.dialog.sprouttosapling.nosapling.title"), + JOptionPane.INFORMATION_MESSAGE); + return; + } + + ZelCashSproutToSaplingDialog ad = new ZelCashSproutToSaplingDialog( + this.parent, clientCaller, installationObserver, listOfSapling, + listOfSaplingWithLabels); + ad.setVisible(true); + + } catch (HeadlessException | WalletCallException | IOException | + InterruptedException e1) { + this.errorReporter.reportError(e1, false); + } + } + + public void importWalletPrivateKeys() { + try { + if (this.clientCaller.isWalletEncrypted()) { + boolean passwordOk = false; + int retrys = 0; + while (!passwordOk && retrys < 3) { + ++retrys; + PasswordDialog pd = new PasswordDialog((ZelCashJFrame)(this.parent)); + pd.setVisible(true); + + if (!pd.isOKPressed()) { return; - } - - int reply = JOptionPane.showOptionDialog( + } + try { + this.clientCaller.unlockWallet(pd.getPassword()); + passwordOk = true; + } catch (Exception ex) { + Log.error("Error unlocking wallet:" + ex.getMessage()); + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString("encryption.error.unlocking.message", + ex.getMessage()), + langUtil.getString("encryption.error.unlocking.title"), + JOptionPane.ERROR_MESSAGE); + } + } + if (!passwordOk) { + Log.info( + "Failed to enter correct password for third time, wallet will close."); + System.exit(1); + } + } + } catch (HeadlessException | WalletCallException | IOException | + InterruptedException e1) { + this.errorReporter.reportError(e1, false); + } + Object[] options = {langUtil.getString("button.option.yes"), + langUtil.getString("button.option.no")}; + int option = JOptionPane.showOptionDialog( + this.parent, + langUtil.getString( + "wallet.operations.dialog.import.private.keys.notice.text"), + langUtil.getString( + "wallet.operations.dialog.import.private.keys.notice.title"), + JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, + JOptionPane.NO_OPTION); + if (option == JOptionPane.NO_OPTION || + option == JOptionPane.CLOSED_OPTION) { + return; + } + + try { + ZelCashJFileChooser fileChooser = new ZelCashJFileChooser(); + fileChooser.setDialogTitle(langUtil.getString( + "wallet.operations.file.chooser.import.private.keys.title")); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + + int result = fileChooser.showOpenDialog(this.parent); + + if (result != JFileChooser.APPROVE_OPTION) { + return; + } + + File f = fileChooser.getSelectedFile(); + + Cursor oldCursor = this.parent.getCursor(); + try { + this.parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + this.clientCaller.importWallet(f.getCanonicalPath()); + + this.parent.setCursor(oldCursor); + } catch (WalletCallException wce) { + this.parent.setCursor(oldCursor); + Log.error("Unexpected error: ", wce); + + JOptionPane.showMessageDialog( this.parent, - langUtil.getString("wallet.operations.option.pane.backup.directory.warning.text", - OSUtil.getUserHomeDirectory().getCanonicalPath()), - langUtil.getString("wallet.operations.option.pane.backup.directory.warning.title"), - JOptionPane.YES_NO_OPTION, - JOptionPane.INFORMATION_MESSAGE, - null, new String[] { langUtil.getString("wallet.operations.option.pane.backup.directory.warning.message"), - langUtil.getString("wallet.operations.option.pane.backup.directory.warning.message.ok") }, - JOptionPane.NO_OPTION); - - if (reply == JOptionPane.NO_OPTION || reply == JOptionPane.CLOSED_OPTION) - { - return; - } - - warningFlagFile.createNewFile(); - } - - public void restartUI() throws IOException, InterruptedException, WalletCallException { - Log.info("Restarting the UI."); - ZCashUI z = new ZCashUI(null); - this.parent.setVisible(false); - this.parent.dispose(); - this.parent = z; - this.parent.repaint(); - this.parent.setVisible(true); - } - - public void restartAfterRescan() throws IOException, InterruptedException, WalletCallException, InvocationTargetException { - Log.info("Waiting for rescan complete."); - while(true) { - Thread.sleep(POLL_PERIOD); - - JsonObject info = null; - - try - { - info = clientCaller.getDaemonRawRuntimeInfo(); - } catch (IOException e) - { - throw e; - } - - JsonValue code = info.get("code"); - Log.debug("clientCaller:"+info.toString()); - if (code == null || (code.asInt() != STARTUP_ERROR_CODE)) - break; - } - Log.info("Rescan complete."); - - JOptionPane.showMessageDialog(this.parent, - langUtil.getString("wallet.operations.dialog.rescan.complete.message"), - langUtil.getString("wallet.operations.dialog.rescan.complete.title"), - JOptionPane.INFORMATION_MESSAGE); - - this.parent.setVisible(false); - this.parent.dispose(); - - ZCashUI z = new ZCashUI(null); - this.parent = z; - this.parent.repaint(); - this.parent.setVisible(true); - } + langUtil.getString( + "wallet.operations.dialog.import.private.keys.error.text", + wce.getMessage().replace(",", ",\n")), + langUtil.getString( + "wallet.operations.dialog.import.private.keys.error.title"), + JOptionPane.ERROR_MESSAGE); + return; + } + + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.dialog.import.private.keys.success.text", + f.getCanonicalPath()), + langUtil.getString( + "wallet.operations.dialog.import.private.keys.success.title"), + JOptionPane.INFORMATION_MESSAGE); + + } catch (Exception e) { + this.errorReporter.reportError(e, false); + } + } + + public void showPrivateKey() { + if (this.tabs.getSelectedIndex() != 2) { + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.option.pane.own.address.view.private.key.text"), + langUtil.getString( + "wallet.operations.option.pane.own.address.view.private.key.title"), + JOptionPane.INFORMATION_MESSAGE); + this.tabs.setSelectedIndex(2); + return; + } + + String address = this.addresses.getSelectedAddress(); + + if (address == null) { + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.option.pane.address.table.view.private.key.text"), + langUtil.getString( + "wallet.operations.option.pane.address.table.view.private.key.title"), + JOptionPane.INFORMATION_MESSAGE); + return; + } + + try { + // Check for encrypted wallet + final boolean bEncryptedWallet = this.clientCaller.isWalletEncrypted(); + if (bEncryptedWallet) { + boolean passwordOk = false; + int retrys = 0; + while (!passwordOk && retrys < 3) { + ++retrys; + PasswordDialog pd = new PasswordDialog((ZelCashJFrame)(this.parent)); + pd.setVisible(true); + + if (!pd.isOKPressed()) { + return; + } + try { + this.clientCaller.unlockWallet(pd.getPassword()); + passwordOk = true; + } catch (Exception ex) { + Log.error("Error unlocking wallet:" + ex.getMessage()); + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString("encryption.error.unlocking.message", + ex.getMessage()), + langUtil.getString("encryption.error.unlocking.title"), + JOptionPane.ERROR_MESSAGE); + } + } + if (!passwordOk) { + Log.info( + "Failed to enter correct password for third time, wallet will close."); + System.exit(1); + } + } + + boolean isZAddress = Util.isZAddress(address); + + String privateKey = isZAddress + ? this.clientCaller.getZPrivateKey(address) + : this.clientCaller.getTPrivateKey(address); + + // Lock the wallet again + if (bEncryptedWallet) { + // this.clientCaller.lockWallet(); + } + + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(new StringSelection(privateKey), null); + String adressType = + isZAddress + ? langUtil.getString("wallet.operations.private.address") + : langUtil.getString("wallet.operations.transparent.address"); + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString( + "wallet.operations.option.pane.address.information.text", + adressType, address, privateKey), + langUtil.getString( + "wallet.operations.option.pane.address.information.title"), + JOptionPane.INFORMATION_MESSAGE); + } catch (Exception ex) { + this.errorReporter.reportError(ex, false); + } + } + + public void importSinglePrivateKey() { + try { + if (this.clientCaller.isWalletEncrypted()) { + boolean passwordOk = false; + int retrys = 0; + while (!passwordOk && retrys < 3) { + ++retrys; + PasswordDialog pd = new PasswordDialog((ZelCashJFrame)(this.parent)); + pd.setVisible(true); + + if (!pd.isOKPressed()) { + return; + } + try { + this.clientCaller.unlockWallet(pd.getPassword()); + passwordOk = true; + } catch (Exception ex) { + Log.error("Error unlocking wallet:" + ex.getMessage()); + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString("encryption.error.unlocking.message", + ex.getMessage()), + langUtil.getString("encryption.error.unlocking.title"), + JOptionPane.ERROR_MESSAGE); + } + } + if (!passwordOk) { + Log.info( + "Failed to enter correct password for third time, wallet will close."); + System.exit(1); + } + } + SingleKeyImportDialog kd = + new SingleKeyImportDialog(this.parent, this.clientCaller); + kd.setVisible(true); + + } catch (Exception ex) { + this.errorReporter.reportError(ex, false); + } + } + + /** + * export to Arizen wallet + */ + public void exportToArizenWallet() { + final ZelCashJDialog dialog = new ZelCashJDialog( + this.parent, + langUtil.getString("wallet.operations.dialog.export.arizen.title")); + final ZelCashJLabel exportLabel = new ZelCashJLabel(); + final WalletRepo arizenWallet = new ArizenWallet(); + try { + ZelCashJFileChooser fileChooser = new ZelCashJFileChooser(); + fileChooser.setFileFilter(new FileNameExtensionFilter( + langUtil.getString( + "wallet.operations.dialog.export.arizen.filechooser.filter"), + "uawd")); + fileChooser.setDialogTitle(langUtil.getString( + "wallet.operations.dialog.export.arizen.filechooser.title")); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooser.setCurrentDirectory(OSUtil.getUserHomeDirectory()); + int result = fileChooser.showDialog( + this.parent, + langUtil.getString( + "wallet.operations.dialog.export.arizen.filechooser.aprove.button")); + + if (result != JFileChooser.APPROVE_OPTION) { + return; + } + + File chooseFile = fileChooser.getSelectedFile(); + String fullPath = chooseFile.getAbsolutePath(); + if (!fullPath.endsWith(".uawd")) + fullPath += ".uawd"; + + final File f = new File(fullPath); + if (f.exists()) { + Object[] options = {langUtil.getString("button.option.yes"), + langUtil.getString("button.option.no")}; + int r = JOptionPane.showOptionDialog( + (Component)null, + langUtil.getString( + "wallet.operations.dialog.delete.file.confirmation", + f.getName()), + langUtil.getString( + "wallet.operations.dialog.delete.file.confirmation.title"), + JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, + options, JOptionPane.NO_OPTION); + if (r == 1 || r == JOptionPane.CLOSED_OPTION) { + return; + } + Files.delete(f.toPath()); + } + final String strFullpath = fullPath; + + dialog.setSize(300, 75); + dialog.setLocationRelativeTo(parent); + dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); + dialog.setLayout(new BorderLayout()); + + ZelCashJProgressBar progressBar = new ZelCashJProgressBar(); + progressBar.setIndeterminate(true); + dialog.add(progressBar, BorderLayout.CENTER); + exportLabel.setText( + langUtil.getString("wallet.operations.dialog.export.label")); + exportLabel.setHorizontalAlignment(JLabel.CENTER); + exportLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 16, 0)); + + dialog.add(exportLabel, BorderLayout.SOUTH); + dialog.setVisible(true); + + SwingWorker worker = new SwingWorker() { + @Override + public Boolean doInBackground() { + try { + arizenWallet.createWallet(f); + Thread.sleep(750); + updateProgressText(langUtil.getString( + "wallet.operations.dialog.export.progress.reading.text")); + String[] zaddress = clientCaller.getWalletZAddresses(); + String[] taddress = clientCaller.getWalletAllPublicAddresses(); + String[] tAddressesWithUnspentOuts = + clientCaller.getWalletPublicAddressesWithUnspentOutputs(); + + Set
addressPublicSet = new HashSet
(); + Set
addressPrivateSet = new HashSet
(); + + Map tMap = new HashMap(); + Map zMap = new HashMap(); + + for (String straddr : taddress) { + String pk = clientCaller.getTPrivateKey(straddr); + String pkHex = Util.wifToHex(pk); + String balance = clientCaller.getBalanceForAddress(straddr); + Address addr = new Address(Address.ADDRESS_TYPE.TRANSPARENT, + straddr, pkHex, balance); + tMap.put(straddr, addr); + } + + for (String straddr : tAddressesWithUnspentOuts) { + String pk = clientCaller.getTPrivateKey(straddr); + String pkHex = Util.wifToHex(pk); + String balance = clientCaller.getBalanceForAddress(straddr); + Address addr = new Address(Address.ADDRESS_TYPE.TRANSPARENT, + straddr, pkHex, balance); + tMap.put(straddr, addr); + } + + for (String straddr : zaddress) { + String pk = clientCaller.getZPrivateKey(straddr); + String balance = clientCaller.getBalanceForAddress(straddr); + Address addr = new Address(Address.ADDRESS_TYPE.PRIVATE, straddr, + pk, balance); + zMap.put(straddr, addr); + } + addressPublicSet.addAll(tMap.values()); + addressPrivateSet.addAll(zMap.values()); + Thread.sleep(500); + + updateProgressText(langUtil.getString( + "wallet.operations.dialog.export.progress.writing.text")); + arizenWallet.insertAddressBatch(addressPublicSet); + if (addressPrivateSet.size() > 0) { + arizenWallet.insertAddressBatch(addressPrivateSet); + } + Thread.sleep(1000); + + updateProgressText(langUtil.getString( + "wallet.operations.dialog.export.progress.finished.text")); + Thread.sleep(750); + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + dialog.dispose(); + JOptionPane.showConfirmDialog( + parent, + langUtil.getString( + "wallet.operations.option.pane.export.success.info.text", + strFullpath), + langUtil.getString( + "wallet.operations.option.pane.export.success.info.title"), + JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE); + } + }); + + } catch (Exception e) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + dialog.dispose(); + } + }); + errorReporter.reportError(e, false); + } finally { + try { + if (arizenWallet != null && arizenWallet.isOpen()) { + arizenWallet.close(); + } + } catch (Exception ex) { + errorReporter.reportError(ex, false); + } + } + return true; + } + + private void updateProgressText(final String text) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + exportLabel.setText(text); + } + }); + } + }; + + worker.execute(); + + } catch (Exception ex) { + errorReporter.reportError(ex, false); + } + } + + private void issueBackupDirectoryWarning() throws IOException { + String userDir = OSUtil.getSettingsDirectory(); + File warningFlagFile = + new File(userDir + File.separator + "backupInfoShownNG.flag"); + if (warningFlagFile.exists()) { + return; + } + + int reply = JOptionPane.showOptionDialog( + this.parent, + langUtil.getString( + "wallet.operations.option.pane.backup.directory.warning.text", + OSUtil.getUserHomeDirectory().getCanonicalPath()), + langUtil.getString( + "wallet.operations.option.pane.backup.directory.warning.title"), + JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE, null, + new String[] { + langUtil.getString( + "wallet.operations.option.pane.backup.directory.warning.message"), + langUtil.getString( + "wallet.operations.option.pane.backup.directory.warning.message.ok")}, + JOptionPane.NO_OPTION); + + if (reply == JOptionPane.NO_OPTION || reply == JOptionPane.CLOSED_OPTION) { + return; + } + + warningFlagFile.createNewFile(); + } + + public void restartUI() + throws IOException, InterruptedException, WalletCallException { + Log.info("Restarting the UI."); + ZCashUI z = new ZCashUI(null); + this.parent.setVisible(false); + this.parent.dispose(); + this.parent = z; + this.parent.repaint(); + this.parent.setVisible(true); + } + + public void restartAfterRescan() throws IOException, InterruptedException, + WalletCallException, + InvocationTargetException { + Log.info("Waiting for rescan complete."); + while (true) { + Thread.sleep(POLL_PERIOD); + + JsonObject info = null; + + try { + info = clientCaller.getDaemonRawRuntimeInfo(); + } catch (IOException e) { + throw e; + } + + JsonValue code = info.get("code"); + Log.debug("clientCaller:" + info.toString()); + if (code == null || (code.asInt() != STARTUP_ERROR_CODE)) + break; + } + Log.info("Rescan complete."); + + JOptionPane.showMessageDialog( + this.parent, + langUtil.getString("wallet.operations.dialog.rescan.complete.message"), + langUtil.getString("wallet.operations.dialog.rescan.complete.title"), + JOptionPane.INFORMATION_MESSAGE); + + this.parent.setVisible(false); + this.parent.dispose(); + + ZCashUI z = new ZCashUI(null); + this.parent = z; + this.parent.repaint(); + this.parent.setVisible(true); + } } diff --git a/src/java/com/vaklinov/zcashui/ZCashClientCaller.java b/src/java/com/vaklinov/zcashui/ZCashClientCaller.java index 7f9a460d..13418f24 100644 --- a/src/java/com/vaklinov/zcashui/ZCashClientCaller.java +++ b/src/java/com/vaklinov/zcashui/ZCashClientCaller.java @@ -1,11 +1,14 @@ /************************************************************************************************ - * ____________ _ _ _____ _ _____ _ _ _______ __ _ _ _ - * |___ / ____| \ | |/ ____| | | / ____| | | |_ _\ \ / / | | | | | - * / /| |__ | \| | | __ _ ___| |__ | | __| | | | | | \ \ /\ / /_ _| | | ___| |_ - * / / | __| | . ` | | / _` / __| '_ \| | |_ | | | | | | \ \/ \/ / _` | | |/ _ \ __| - * / /__| |____| |\ | |___| (_| \__ \ | | | |__| | |__| |_| |_ \ /\ / (_| | | | __/ |_ - * /_____|______|_| \_|\_____\__,_|___/_| |_|\_____|\____/|_____| \/ \/ \__,_|_|_|\___|\__| - * + * ____________ _ _ _____ _ _____ _ _ _______ __ + *_ _ _ + * |___ / ____| \ | |/ ____| | | / ____| | | |_ _\ \ / / + *| | | | | / /| |__ | \| | | __ _ ___| |__ | | __| | | | | | \ \ + * /\ / /_ _| | | ___| |_ / / | __| | . ` | | / _` / __| '_ \| | |_ | | | + *| | | \ \/ \/ / _` | | |/ _ \ __| / /__| |____| |\ | |___| (_| \__ \ | | | + *|__| | |__| |_| |_ \ /\ / (_| | | | __/ |_ + * /_____|______|_| \_|\_____\__,_|___/_| |_|\_____|\____/|_____| \/ \/ + *\__,_|_|_|\___|\__| + * * Copyright (c) 2016-2018 The ZEN Developers * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -28,1482 +31,1375 @@ **********************************************************************************/ package com.vaklinov.zcashui; - +import com.eclipsesource.json.Json; +import com.eclipsesource.json.JsonArray; +import com.eclipsesource.json.JsonObject; +import com.eclipsesource.json.JsonValue; +import com.eclipsesource.json.ParseException; +import com.eclipsesource.json.WriterConfig; +import com.vaklinov.zcashui.OSUtil.OS_TYPE; import java.io.File; import java.io.IOException; import java.io.LineNumberReader; import java.io.StringReader; import java.math.BigDecimal; -import java.util.Date; -import java.util.HashMap; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; -import com.eclipsesource.json.Json; -import com.eclipsesource.json.JsonArray; -import com.eclipsesource.json.JsonObject; -import com.eclipsesource.json.JsonValue; -import com.eclipsesource.json.ParseException; -import com.eclipsesource.json.WriterConfig; -import com.vaklinov.zcashui.OSUtil.OS_TYPE; - - /** * Calls zcash-cli */ -public class ZCashClientCaller -{ - public static class WalletBalance - { - public double transparentBalance; - public double privateBalance; - public double totalBalance; - - public double transparentUnconfirmedBalance; - public double privateUnconfirmedBalance; - public double totalUnconfirmedBalance; - } - - - public static class NetworkAndBlockchainInfo - { - public int numConnections; - public int blockNumber; - public String sproutToSaplingEnabled; - public Date lastBlockDate; - } - - - public static class WalletCallException - extends Exception - { - public WalletCallException(String message) - { - super(message); - } - - public WalletCallException(String message, Throwable cause) - { - super(message, cause); - } - } - - - // ZCash client program and daemon - private File zcashcli, zcashd; - - // Table caching the wallet transaction times - to speed up performance - // TXID -> UNIX time as string - private Map transactionTimes = Collections.synchronizedMap( - new HashMap()); - private long lastTransactionTimesAccess = System.currentTimeMillis(); - - // Table caching the wallet transaction confirmations - to speed up performance - // TXID -> confirmations as string - private Map transactionConfirmations = Collections.synchronizedMap( - new HashMap()); - private long lastTransactionConfirmationsAccess = System.currentTimeMillis(); - - private static Boolean walletIsEncrypted = null; - - public ZCashClientCaller(String installDir) - throws IOException - { - // Detect daemon and client tools installation - File dir = new File(installDir); - zcashcli = new File(dir, OSUtil.getZCashCli()); - - if (!zcashcli.exists()) - { - zcashcli = OSUtil.findZCashCommand(OSUtil.getZCashCli()); - } - - if ((zcashcli == null) || (!zcashcli.exists())) - { - throw new IOException( - "The ZelCash installation directory " + installDir + " needs to contain " + - "the command line utilities zelcashd and zelcash-cli. zelcash-cli is missing!"); - } - - zcashd = new File(dir, OSUtil.getZCashd()); - if (!zcashd.exists()) - { - zcashd = OSUtil.findZCashCommand(OSUtil.getZCashd()); - } - - if (zcashd == null || (!zcashd.exists())) - { - throw new IOException( - "The ZelCash command line utility " + zcashcli.getCanonicalPath() + - " was found, but zelcashd was not found!"); - } - } - - /** - * Reindex have more priority over rescan, only one should be true if you need them - */ - public synchronized Process startDaemon(boolean reindex, boolean rescan) - throws IOException, InterruptedException - { - String exportDir = OSUtil.getUserHomeDirectory().getCanonicalPath(); - if (reindex == true) { - CommandExecutor starter = new CommandExecutor( - new String[] - { - zcashd.getCanonicalPath(), - "-exportdir=" + wrapStringParameter(exportDir), - "-reindex" - }); - - return starter.startChildProcess(); - } - else if (rescan == true) { - CommandExecutor starter = new CommandExecutor( - new String[] - { - zcashd.getCanonicalPath(), - "-exportdir=" + wrapStringParameter(exportDir), - "-rescan" - }); - - return starter.startChildProcess(); - } - else { - CommandExecutor starter = new CommandExecutor( - new String[] - { - zcashd.getCanonicalPath(), - "-exportdir=" + wrapStringParameter(exportDir) - }); - - return starter.startChildProcess(); - } - - } - - - public /*synchronized*/ void stopDaemon() - throws IOException,InterruptedException - { - CommandExecutor stopper = new CommandExecutor( - new String[] { zcashcli.getCanonicalPath(), "stop" }); - - String result = stopper.execute(); - Log.info("Stop command issued: " + result); - } - - - public synchronized JsonObject getDaemonRawRuntimeInfo() - throws IOException, InterruptedException, WalletCallException - { - CommandExecutor infoGetter = new CommandExecutor( - new String[] { zcashcli.getCanonicalPath(), "getinfo"} ); - String info = infoGetter.execute(); - - if (info.trim().toLowerCase(Locale.ROOT).startsWith("error: couldn't connect to server")) - { - throw new IOException(info.trim()); - } - - if (info.trim().toLowerCase(Locale.ROOT).startsWith("error: ")) - { - info = info.substring(7); - - try - { - return Json.parse(info).asObject(); - } catch (ParseException pe) - { - Log.error("unexpected daemon info: " + info); - throw new IOException(pe); - } - } else if (info.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) - { - return Util.getJsonErrorMessage(info); - } else - { - try - { - return Json.parse(info).asObject(); - } catch (ParseException pe) - { - Log.info("unexpected daemon info: " + info); - throw new IOException(pe); - } - } - } - - - public synchronized WalletBalance getWalletInfo() - throws WalletCallException, IOException, InterruptedException - { - WalletBalance balance = new WalletBalance(); - - JsonObject objResponse = this.executeCommandAndGetJsonObject("z_gettotalbalance", null); - - balance.transparentBalance = Double.valueOf(objResponse.getString("transparent", "-1")); - balance.privateBalance = Double.valueOf(objResponse.getString("private", "-1")); - balance.totalBalance = Double.valueOf(objResponse.getString("total", "-1")); - - objResponse = this.executeCommandAndGetJsonObject("z_gettotalbalance", "0"); - - balance.transparentUnconfirmedBalance = Double.valueOf(objResponse.getString("transparent", "-1")); - balance.privateUnconfirmedBalance = Double.valueOf(objResponse.getString("private", "-1")); - balance.totalUnconfirmedBalance = Double.valueOf(objResponse.getString("total", "-1")); - - return balance; - } - - - public synchronized String[][] getWalletPublicTransactions() - throws WalletCallException, IOException, InterruptedException - { - String notListed = "\u26D4"; - - OS_TYPE os = OSUtil.getOSType(); - if (os == OS_TYPE.WINDOWS) - { - notListed = " \u25B6"; - } - - JsonArray jsonTransactions = executeCommandAndGetJsonArray( - "listtransactions", wrapStringParameter(""), "100"); - String strTransactions[][] = new String[jsonTransactions.size()][]; - for (int i = 0; i < jsonTransactions.size(); i++) - { - strTransactions[i] = new String[7]; - JsonObject trans = jsonTransactions.get(i).asObject(); - - // Needs to be the same as in getWalletZReceivedTransactions() - // TODO: someday refactor to use object containers - strTransactions[i][0] = "\u2606T (Public)"; - strTransactions[i][1] = trans.getString("category", "ERROR!"); - strTransactions[i][2] = trans.get("confirmations").toString(); - strTransactions[i][3] = trans.get("amount").toString(); - strTransactions[i][4] = trans.get("time").toString(); - strTransactions[i][5] = trans.getString("address", notListed + " (Z Address not listed by wallet!)"); - strTransactions[i][6] = trans.get("txid").toString(); - - } - - return strTransactions; - } - - - public synchronized String[] getWalletZAddresses() - throws WalletCallException, IOException, InterruptedException - { - JsonArray jsonAddresses = executeCommandAndGetJsonArray("z_listaddresses", null); - String strAddresses[] = new String[jsonAddresses.size()]; - for (int i = 0; i < jsonAddresses.size(); i++) - { - strAddresses[i] = jsonAddresses.get(i).asString(); - } - - return strAddresses; - } - - - public synchronized String[][] getWalletZReceivedTransactions() - throws WalletCallException, IOException, InterruptedException - { - String[] zAddresses = this.getWalletZAddresses(); - - List zReceivedTransactions = new ArrayList(); - - for (String zAddress : zAddresses) - { - JsonArray jsonTransactions = executeCommandAndGetJsonArray( - "z_listreceivedbyaddress", wrapStringParameter(zAddress), "0"); - for (int i = 0; i < jsonTransactions.size() && i < 100; i++) - { - String[] currentTransaction = new String[7]; - JsonObject trans = jsonTransactions.get(i).asObject(); - - String txID = trans.getString("txid", "ERROR!"); - // Needs to be the same as in getWalletPublicTransactions() - // TODO: someday refactor to use object containers - currentTransaction[0] = "\u2605Z (Private)"; - currentTransaction[1] = "receive"; - - // Transaction confirmations cached - cleared every 10 min - if ((System.currentTimeMillis() - this.lastTransactionConfirmationsAccess) > (10 * 60 * 1000)) - { - this.lastTransactionConfirmationsAccess = System.currentTimeMillis(); - this.transactionConfirmations.clear(); - } - String confirmations = this.transactionConfirmations.get(txID); - if ((confirmations == null) || confirmations.equals("0")) - { - currentTransaction[2] = this.getWalletTransactionConfirmations(txID); - this.transactionConfirmations.put(txID, currentTransaction[2]); - } else - { - currentTransaction[2] = confirmations; - } - - currentTransaction[3] = trans.get("amount").toString(); - - // Transaction time is cached - cleared every 10 min - if ((System.currentTimeMillis() - this.lastTransactionTimesAccess) > (10 * 60 * 1000)) - { - this.lastTransactionTimesAccess = System.currentTimeMillis(); - this.transactionTimes.clear(); - } - String time = this.transactionTimes.get(txID); - if ((time == null) || (time.equals("-1"))) - { - currentTransaction[4] = this.getWalletTransactionTime(txID); - this.transactionTimes.put(txID, currentTransaction[4]); - } else - { - currentTransaction[4] = time; - } - - currentTransaction[5] = zAddress; - currentTransaction[6] = trans.get("txid").toString(); - - zReceivedTransactions.add(currentTransaction); - } - } - - return zReceivedTransactions.toArray(new String[0][]); - } - - - public synchronized JsonObject[] getTransactionMessagingDataForZaddress(String ZAddress) - throws WalletCallException, IOException, InterruptedException - { - JsonArray jsonTransactions = executeCommandAndGetJsonArray( - "z_listreceivedbyaddress", wrapStringParameter(ZAddress), "0"); - List transactions = new ArrayList(); - for (int i = 0; i < jsonTransactions.size(); i++) - { - JsonObject trans = jsonTransactions.get(i).asObject(); - transactions.add(trans); - } - - return transactions.toArray(new JsonObject[0]); - } - - - // ./src/zcash-cli listunspent only returns T addresses it seems - public synchronized String[] getWalletPublicAddressesWithUnspentOutputs() - throws WalletCallException, IOException, InterruptedException - { - JsonArray jsonUnspentOutputs = executeCommandAndGetJsonArray("listunspent", "0"); - - Set addresses = new HashSet<>(); - for (int i = 0; i < jsonUnspentOutputs.size(); i++) - { - JsonObject outp = jsonUnspentOutputs.get(i).asObject(); - addresses.add(outp.getString("address", "ERROR!")); - } - - return addresses.toArray(new String[0]); - } - - - // ./zcash-cli listreceivedbyaddress 0 true - public synchronized String[] getWalletAllPublicAddresses() - throws WalletCallException, IOException, InterruptedException - { - JsonArray jsonReceivedOutputs = executeCommandAndGetJsonArray("listreceivedbyaddress", "0", "true"); - - Set addresses = new HashSet<>(); - for (int i = 0; i < jsonReceivedOutputs.size(); i++) - { - JsonObject outp = jsonReceivedOutputs.get(i).asObject(); - addresses.add(outp.getString("address", "ERROR!")); - } - - return addresses.toArray(new String[0]); +public class ZCashClientCaller { + public static class WalletBalance { + public double transparentBalance; + public double privateBalance; + public double totalBalance; + + public double transparentUnconfirmedBalance; + public double privateUnconfirmedBalance; + public double totalUnconfirmedBalance; + } + + public static class NetworkAndBlockchainInfo { + public int numConnections; + public int blockNumber; + public String sproutToSaplingEnabled; + public Date lastBlockDate; + } + + public static class WalletCallException extends Exception { + public WalletCallException(String message) { super(message); } + + public WalletCallException(String message, Throwable cause) { + super(message, cause); + } + } + + // ZCash client program and daemon + private File zcashcli, zcashd; + + // Table caching the wallet transaction times - to speed up performance + // TXID -> UNIX time as string + private Map transactionTimes = + Collections.synchronizedMap(new HashMap()); + private long lastTransactionTimesAccess = System.currentTimeMillis(); + + // Table caching the wallet transaction confirmations - to speed up + // performance TXID -> confirmations as string + private Map transactionConfirmations = + Collections.synchronizedMap(new HashMap()); + private long lastTransactionConfirmationsAccess = System.currentTimeMillis(); + + private static Boolean walletIsEncrypted = null; + + public ZCashClientCaller(String installDir) throws IOException { + // Detect daemon and client tools installation + File dir = new File(installDir); + zcashcli = new File(dir, OSUtil.getZCashCli()); + + if (!zcashcli.exists()) { + zcashcli = OSUtil.findZCashCommand(OSUtil.getZCashCli()); + } + + if ((zcashcli == null) || (!zcashcli.exists())) { + throw new IOException( + "The ZelCash installation directory " + installDir + + " needs to contain " + + + "the command line utilities zelcashd and zelcash-cli. zelcash-cli is missing!"); + } + + zcashd = new File(dir, OSUtil.getZCashd()); + if (!zcashd.exists()) { + zcashd = OSUtil.findZCashCommand(OSUtil.getZCashd()); + } + + if (zcashd == null || (!zcashd.exists())) { + throw new IOException("The ZelCash command line utility " + + zcashcli.getCanonicalPath() + + " was found, but zelcashd was not found!"); + } + } + + /** + * Reindex have more priority over rescan, only one should be true if you need + * them + */ + public synchronized Process startDaemon(boolean reindex, boolean rescan) + throws IOException, InterruptedException { + String exportDir = OSUtil.getUserHomeDirectory().getCanonicalPath(); + if (reindex == true) { + CommandExecutor starter = new CommandExecutor(new String[] { + zcashd.getCanonicalPath(), + "-exportdir=" + wrapStringParameter(exportDir), "-reindex"}); + + return starter.startChildProcess(); + } else if (rescan == true) { + CommandExecutor starter = new CommandExecutor(new String[] { + zcashd.getCanonicalPath(), + "-exportdir=" + wrapStringParameter(exportDir), "-rescan"}); + + return starter.startChildProcess(); + } else { + CommandExecutor starter = new CommandExecutor( + new String[] {zcashd.getCanonicalPath(), + "-exportdir=" + wrapStringParameter(exportDir)}); + + return starter.startChildProcess(); + } + } + + public /*synchronized*/ void stopDaemon() + throws IOException, InterruptedException { + CommandExecutor stopper = + new CommandExecutor(new String[] {zcashcli.getCanonicalPath(), "stop"}); + + String result = stopper.execute(); + Log.info("Stop command issued: " + result); + } + + public synchronized JsonObject getDaemonRawRuntimeInfo() + throws IOException, InterruptedException, WalletCallException { + CommandExecutor infoGetter = new CommandExecutor( + new String[] {zcashcli.getCanonicalPath(), "getinfo"}); + String info = infoGetter.execute(); + + if (info.trim() + .toLowerCase(Locale.ROOT) + .startsWith("error: couldn't connect to server")) { + throw new IOException(info.trim()); + } + + if (info.trim().toLowerCase(Locale.ROOT).startsWith("error: ")) { + info = info.substring(7); + + try { + return Json.parse(info).asObject(); + } catch (ParseException pe) { + Log.error("unexpected daemon info: " + info); + throw new IOException(pe); + } + } else if (info.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) { + return Util.getJsonErrorMessage(info); + } else { + try { + return Json.parse(info).asObject(); + } catch (ParseException pe) { + Log.info("unexpected daemon info: " + info); + throw new IOException(pe); + } } + } - - public synchronized Map getRawTransactionDetails(String txID) - throws WalletCallException, IOException, InterruptedException - { - JsonObject jsonTransaction = this.executeCommandAndGetJsonObject( - "gettransaction", wrapStringParameter(txID)); - - Map map = new HashMap(); - - for (String name : jsonTransaction.names()) - { - this.decomposeJSONValue(name, jsonTransaction.get(name), map); - } - - return map; - } - - public synchronized String getMemoField(String acc, String txID) - throws WalletCallException, IOException, InterruptedException - { - JsonArray jsonTransactions = this.executeCommandAndGetJsonArray( - "z_listreceivedbyaddress", wrapStringParameter(acc)); - - for (int i = 0; i < jsonTransactions.size(); i++) - { - if (jsonTransactions.get(i).asObject().getString("txid", "ERROR!").equals(txID)) - { - if (jsonTransactions.get(i).asObject().get("memo") == null) - { - return null; - } - - String memoHex = jsonTransactions.get(i).asObject().getString("memo", "ERROR!"); - String decodedMemo = Util.decodeHexMemo(memoHex); - - // Return only if not null - sometimes multiple incoming transactions have the same ID - // if we have loopback send etc. - if (decodedMemo != null) - { - return decodedMemo; - } - } + public synchronized WalletBalance getWalletInfo() + throws WalletCallException, IOException, InterruptedException { + WalletBalance balance = new WalletBalance(); + + JsonObject objResponse = + this.executeCommandAndGetJsonObject("z_gettotalbalance", null); + + balance.transparentBalance = + Double.valueOf(objResponse.getString("transparent", "-1")); + balance.privateBalance = + Double.valueOf(objResponse.getString("private", "-1")); + balance.totalBalance = Double.valueOf(objResponse.getString("total", "-1")); + + objResponse = this.executeCommandAndGetJsonObject("z_gettotalbalance", "0"); + + balance.transparentUnconfirmedBalance = + Double.valueOf(objResponse.getString("transparent", "-1")); + balance.privateUnconfirmedBalance = + Double.valueOf(objResponse.getString("private", "-1")); + balance.totalUnconfirmedBalance = + Double.valueOf(objResponse.getString("total", "-1")); + + return balance; + } + + public synchronized String[][] getWalletPublicTransactions() + throws WalletCallException, IOException, InterruptedException { + String notListed = "\u26D4"; + + OS_TYPE os = OSUtil.getOSType(); + if (os == OS_TYPE.WINDOWS) { + notListed = " \u25B6"; + } + + JsonArray jsonTransactions = executeCommandAndGetJsonArray( + "listtransactions", wrapStringParameter(""), "100"); + String strTransactions[][] = new String[jsonTransactions.size()][]; + for (int i = 0; i < jsonTransactions.size(); i++) { + strTransactions[i] = new String[7]; + JsonObject trans = jsonTransactions.get(i).asObject(); + + // Needs to be the same as in getWalletZReceivedTransactions() + // TODO: someday refactor to use object containers + strTransactions[i][0] = "\u2606T (Public)"; + strTransactions[i][1] = trans.getString("category", "ERROR!"); + strTransactions[i][2] = trans.get("confirmations").toString(); + strTransactions[i][3] = trans.get("amount").toString(); + strTransactions[i][4] = trans.get("time").toString(); + strTransactions[i][5] = trans.getString( + "address", notListed + " (Z Address not listed by wallet!)"); + strTransactions[i][6] = trans.get("txid").toString(); + } + + return strTransactions; + } + + public synchronized String[] getWalletZAddresses() + throws WalletCallException, IOException, InterruptedException { + JsonArray jsonAddresses = + executeCommandAndGetJsonArray("z_listaddresses", null); + String strAddresses[] = new String[jsonAddresses.size()]; + for (int i = 0; i < jsonAddresses.size(); i++) { + strAddresses[i] = jsonAddresses.get(i).asString(); + } + + return strAddresses; + } + + public synchronized String[][] getWalletZReceivedTransactions() + throws WalletCallException, IOException, InterruptedException { + String[] zAddresses = this.getWalletZAddresses(); + + List zReceivedTransactions = new ArrayList(); + + for (String zAddress : zAddresses) { + JsonArray jsonTransactions = executeCommandAndGetJsonArray( + "z_listreceivedbyaddress", wrapStringParameter(zAddress), "0"); + for (int i = 0; i < jsonTransactions.size() && i < 100; i++) { + String[] currentTransaction = new String[7]; + JsonObject trans = jsonTransactions.get(i).asObject(); + + String txID = trans.getString("txid", "ERROR!"); + // Needs to be the same as in getWalletPublicTransactions() + // TODO: someday refactor to use object containers + currentTransaction[0] = "\u2605Z (Private)"; + currentTransaction[1] = "receive"; + + // Transaction confirmations cached - cleared every 10 min + if ((System.currentTimeMillis() - + this.lastTransactionConfirmationsAccess) > (10 * 60 * 1000)) { + this.lastTransactionConfirmationsAccess = System.currentTimeMillis(); + this.transactionConfirmations.clear(); + } + String confirmations = this.transactionConfirmations.get(txID); + if ((confirmations == null) || confirmations.equals("0")) { + currentTransaction[2] = this.getWalletTransactionConfirmations(txID); + this.transactionConfirmations.put(txID, currentTransaction[2]); + } else { + currentTransaction[2] = confirmations; + } + + currentTransaction[3] = trans.get("amount").toString(); + + // Transaction time is cached - cleared every 10 min + if ((System.currentTimeMillis() - this.lastTransactionTimesAccess) > + (10 * 60 * 1000)) { + this.lastTransactionTimesAccess = System.currentTimeMillis(); + this.transactionTimes.clear(); + } + String time = this.transactionTimes.get(txID); + if ((time == null) || (time.equals("-1"))) { + currentTransaction[4] = this.getWalletTransactionTime(txID); + this.transactionTimes.put(txID, currentTransaction[4]); + } else { + currentTransaction[4] = time; } - return null; - } - - - public synchronized void keypoolRefill(int count) - throws WalletCallException, IOException, InterruptedException - { - String result = this.executeCommandAndGetSingleStringResponse( - "keypoolrefill", String.valueOf(count)); - } - - - public synchronized String getRawTransaction(String txID) - throws WalletCallException, IOException, InterruptedException - { - JsonObject jsonTransaction = this.executeCommandAndGetJsonObject( - "gettransaction", wrapStringParameter(txID)); - - return jsonTransaction.toString(WriterConfig.PRETTY_PRINT); - } - - public synchronized JsonObject getTransactionInfo(String txID) - throws WalletCallException, IOException, InterruptedException - { - return this.executeCommandAndGetJsonObject( - "gettransaction", wrapStringParameter(txID)); - } - - - // return UNIX time as tring - public synchronized String getWalletTransactionTime(String txID) - throws WalletCallException, IOException, InterruptedException - { - JsonObject jsonTransaction = this.executeCommandAndGetJsonObject( - "gettransaction", wrapStringParameter(txID)); - - return String.valueOf(jsonTransaction.getLong("time", -1)); - } - - - public synchronized String getWalletTransactionConfirmations(String txID) - throws WalletCallException, IOException, InterruptedException - { - JsonObject jsonTransaction = this.executeCommandAndGetJsonObject( - "gettransaction", wrapStringParameter(txID)); - - return jsonTransaction.get("confirmations").toString(); - } - - - // Checks if a certain T address is a watch-only address or is otherwise invalid. - public synchronized boolean isWatchOnlyOrInvalidAddress(String address) - throws WalletCallException, IOException, InterruptedException - { - JsonObject response = this.executeCommandAndGetJsonValue("validateaddress", wrapStringParameter(address)).asObject(); - - if (response.getBoolean("isvalid", false)) - { - return response.getBoolean("iswatchonly", true); - } - - return true; - } - - - // Returns confirmed balance only! - public synchronized String getBalanceForAddress(String address) - throws WalletCallException, IOException, InterruptedException - { - JsonValue response = this.executeCommandAndGetJsonValue("z_getbalance", wrapStringParameter(address)); - - return String.valueOf(response.toString()); - } - - - public synchronized String getUnconfirmedBalanceForAddress(String address) - throws WalletCallException, IOException, InterruptedException - { - JsonValue response = this.executeCommandAndGetJsonValue("z_getbalance", wrapStringParameter(address), "0"); - - return String.valueOf(response.toString()); - } - - - public synchronized String createNewAddress(boolean isZAddress, boolean isSapling) - throws WalletCallException, IOException, InterruptedException - { - String strResponse = this.executeCommandAndGetSingleStringResponse((isZAddress ? "z_" : "") + "getnewaddress", (isZAddress ? (isSapling ? "sapling" : "sprout") : "")); - - return strResponse.trim(); - } - - - // Returns OPID - this method is a bit old and could be improved, however it is known to work and would better not - // be changed unless there is a bug. - public synchronized String sendCash(String from, String to, String amount, String memo, String transactionFee) - throws WalletCallException, IOException, InterruptedException - { - Log.info("Starting operation send-cash. Parameters are: from address: {0}, to address: {1}, " + - "amount: {2}, memo: {3}, transaction fee: {4}", - from, to, amount, memo, transactionFee); - - StringBuilder hexMemo = new StringBuilder(); - for (byte c : memo.getBytes("UTF-8")) - { - String hexChar = Integer.toHexString((int)c); - if (hexChar.length() < 2) - { - hexChar = "0" + hexChar; - } - hexMemo.append(hexChar); - } - - JsonObject toArgument = new JsonObject(); - toArgument.set("address", to); - if (hexMemo.length() >= 2) - { - toArgument.set("memo", hexMemo.toString()); - } - - // The JSON Builder has a problem with double values that have no fractional part - // it serializes them as integers that ZCash does not accept. So we do a replacement - // TODO: find a better/cleaner way to format the amount - toArgument.set("amount", "\uFFFF\uFFFF\uFFFF\uFFFF\uFFFF"); - - JsonArray toMany = new JsonArray(); - toMany.add(toArgument); - - String amountPattern = "\"amount\":\"\uFFFF\uFFFF\uFFFF\uFFFF\uFFFF\""; - // Make sure our replacement hack never leads to a mess up - String toManyBeforeReplace = toMany.toString(); - int firstIndex = toManyBeforeReplace.indexOf(amountPattern); - int lastIndex = toManyBeforeReplace.lastIndexOf(amountPattern); - if ((firstIndex == -1) || (firstIndex != lastIndex)) - { - throw new WalletCallException("Error in forming z_sendmany command: " + toManyBeforeReplace); - } - - DecimalFormatSymbols decSymbols = new DecimalFormatSymbols(Locale.ROOT); - - // Properly format teh transaction fee as a number - if ((transactionFee == null) || (transactionFee.trim().length() <= 0)) - { - transactionFee = "0.0001"; // Default value - } else - { - transactionFee = new DecimalFormat( - "########0.00######", decSymbols).format(Double.valueOf(transactionFee)); - } - - // This replacement is a hack to make sure the JSON object amount has double format 0.00 etc. - // TODO: find a better way to format the amount - String toManyArrayStr = toMany.toString().replace( - amountPattern, - "\"amount\":" + new DecimalFormat("########0.00######", decSymbols).format(Double.valueOf(amount))); - - String[] sendCashParameters = new String[] - { - this.zcashcli.getCanonicalPath(), "z_sendmany", wrapStringParameter(from), - wrapStringParameter(toManyArrayStr), - // Default min confirmations for the input transactions is 1 - "1", - // transaction fee - transactionFee - }; - - // Safeguard to make sure the monetary amount does not differ after formatting - BigDecimal bdAmout = new BigDecimal(amount); - JsonArray toManyVerificationArr = Json.parse(toManyArrayStr).asArray(); - BigDecimal bdFinalAmount = - new BigDecimal(toManyVerificationArr.get(0).asObject().getDouble("amount", -1)); - BigDecimal difference = bdAmout.subtract(bdFinalAmount).abs(); - if (difference.compareTo(new BigDecimal("0.000000015")) >= 0) - { - throw new WalletCallException("Error in forming z_sendmany command: Amount differs after formatting: " + - amount + " | " + toManyArrayStr); - } - - Log.info("The following send command will be issued: " + - sendCashParameters[0] + " " + sendCashParameters[1] + " " + - sendCashParameters[2] + " " + sendCashParameters[3] + " " + - sendCashParameters[4] + " " + sendCashParameters[5] + "."); - - // Create caller to send cash - CommandExecutor caller = new CommandExecutor(sendCashParameters); - String strResponse = caller.execute(); - - if (strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error:") || - strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) - { - throw new WalletCallException("Error response from wallet: " + strResponse); - } - - Log.info("Sent cash with the following command: " + - sendCashParameters[0] + " " + sendCashParameters[1] + " " + - sendCashParameters[2] + " " + sendCashParameters[3] + " " + - sendCashParameters[4] + " " + sendCashParameters[5] + "." + - " Got result: [" + strResponse + "]"); - - return strResponse.trim(); - } - - - /** - * Sends ZEL from a source address to a destination address. The change is sent back to the source address. - * The amount of change is calculated based on the existing confirmed balance for the address (parameter). - * This may not be 100% accurate if the blockchain is not synchronized. - * - * @param from source address (T/Z) - * @param to destination address (T/Z) - * @param balance current confirmed balance of the source address - * @param amount ZEL amount to send - * @param memo text memo to include in the transaction - * @param transactionFee transaction see to include - * - * @return a zelcashd operation ID for the send operation - * - * @throws WalletCallException - * @throws IOException - * @throws InterruptedException - */ - public synchronized String sendCashWithReturnOfChange(String from, String to, String balance, - String amount, String memo, String transactionFee) - throws WalletCallException, IOException, InterruptedException - { - Log.info("Starting operation send cash with retrun of change. Parameters are: from address: {0}, to address: {1}, " + - "current balance: {2}, amount: {3}, memo: {4}, transaction fee: {5}", - from, to, balance, amount, memo, transactionFee); - - final DecimalFormatSymbols decSymbols = new DecimalFormatSymbols(Locale.ROOT); - final DecimalFormat sendNumberFormat = new DecimalFormat("########0.00######", decSymbols); - - String hexMemo = Util.encodeHexString(memo); - // Form the main amount to send - JsonObject toDestinationArgument = new JsonObject(); - toDestinationArgument.set("address", to); - if (hexMemo.length() >= 2) - { - toDestinationArgument.set("memo", hexMemo.toString()); - } - String formattedAmountToSend = sendNumberFormat.format(new BigDecimal(amount)); - String formattedTransactionFee = sendNumberFormat.format(new BigDecimal(transactionFee)); - toDestinationArgument.set("amount", formattedAmountToSend); - // Form the return amount - based on the balance - JsonObject backToSourceArgument = new JsonObject(); - backToSourceArgument.set("address", from); - BigDecimal changeToSendBackBD = new BigDecimal(balance). - subtract(new BigDecimal(formattedAmountToSend)).subtract(new BigDecimal(formattedTransactionFee)); - boolean changeIsZero = false; - if (changeToSendBackBD.compareTo(new BigDecimal("0")) < 0) - { - throw new WalletCallException("Error: change was calculated negative"); - } else if (changeToSendBackBD.compareTo(new BigDecimal("0")) == 0) - { - Log.info("Change was calculated exactly zero - will not be sent back!"); - changeIsZero = true; - } - - String formattedChangeToReturn = sendNumberFormat.format(changeToSendBackBD); - backToSourceArgument.set("amount", formattedChangeToReturn); - - // Array of two addresses to send to - JsonArray toMany = new JsonArray(); - toMany.add(toDestinationArgument); - if (!changeIsZero) - { - toMany.add(backToSourceArgument); - } - - String toManyArrayStr = toMany.toString(WriterConfig.MINIMAL); - String[] sendCashParameters = new String[] - { - this.zcashcli.getCanonicalPath(), "z_sendmany", wrapStringParameter(from), - wrapStringParameter(toManyArrayStr), - // Default min confirmations for the input transactions is 1 - "1", - // transaction fee - formattedTransactionFee - }; - - // Safeguard to make sure the monetary amount does not differ after formatting - BigDecimal bdAmout = new BigDecimal(amount); // original amount used for check - JsonArray toManyVerificationArr = Json.parse(toManyArrayStr).asArray(); - BigDecimal bdFinalAmount = - new BigDecimal(toManyVerificationArr.get(0).asObject().getString("amount", "-1")); - BigDecimal amountCheckDifference = bdAmout.subtract(bdFinalAmount).abs(); - if (amountCheckDifference.compareTo(new BigDecimal("0.0")) > 0) // MUST be exact - { - throw new WalletCallException("Error in forming z_sendmany command: Main amount differs after formatting: " + - formattedAmountToSend + " | \n" + toManyArrayStr); - } - // Amount + change + fee = balance // This must also match - BigDecimal bdFinalChange = changeIsZero ? - new BigDecimal("0") : new BigDecimal(toManyVerificationArr.get(1).asObject().getString("amount", "-1")); - amountCheckDifference = bdFinalChange.add(bdFinalAmount).add(new BigDecimal(formattedTransactionFee)). - subtract(new BigDecimal(balance)).abs(); // Original balance used after formatting - if (amountCheckDifference.compareTo(new BigDecimal("0.0")) > 0) // MUST be exact - { - throw new WalletCallException("Error in forming z_sendmany command: Sum differs after formatting: " + - formattedAmountToSend + " | \n" + toManyArrayStr); - } - - Log.info("The following send command (with change retrun) will be issued: " + - sendCashParameters[0] + " " + sendCashParameters[1] + " " + - sendCashParameters[2] + " " + sendCashParameters[3] + " " + - sendCashParameters[4] + " " + sendCashParameters[5] + "."); - - // Create caller to send cash - CommandExecutor caller = new CommandExecutor(sendCashParameters); - String strResponse = caller.execute(); - - if (strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error:") || - strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) - { - throw new WalletCallException("Error response from wallet: " + strResponse); - } - - Log.info("Sent cash (with change back) with the following command: " + - sendCashParameters[0] + " " + sendCashParameters[1] + " " + - sendCashParameters[2] + " " + sendCashParameters[3] + " " + - sendCashParameters[4] + " " + sendCashParameters[5] + "." + - " Got result: [" + strResponse + "]"); - - return strResponse.trim(); - } - - - // Returns OPID - public synchronized String sendMessage(String from, String to, double amount, double fee, String memo) - throws WalletCallException, IOException, InterruptedException - { - String hexMemo = Util.encodeHexString(memo); - JsonObject toArgument = new JsonObject(); - toArgument.set("address", to); - if (hexMemo.length() >= 2) - { - toArgument.set("memo", hexMemo.toString()); - } - - DecimalFormatSymbols decSymbols = new DecimalFormatSymbols(Locale.ROOT); - - // TODO: The JSON Builder has a problem with double values that have no fractional part - // it serializes them as integers that ZCash does not accept. This will work with the - // fractional amounts always used for messaging - toArgument.set("amount", new DecimalFormat("########0.00######", decSymbols).format(amount)); - - JsonArray toMany = new JsonArray(); - toMany.add(toArgument); - - String toManyArrayStr = toMany.toString(); - String[] sendCashParameters = new String[] - { - this.zcashcli.getCanonicalPath(), "z_sendmany", wrapStringParameter(from), - wrapStringParameter(toManyArrayStr), - // Default min confirmations for the input transactions is 1 - "1", - // transaction fee - new DecimalFormat("########0.00######", decSymbols).format(fee) - }; - - // Create caller to send cash - CommandExecutor caller = new CommandExecutor(sendCashParameters); - String strResponse = caller.execute(); - - if (strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error:") || - strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) - { - throw new WalletCallException("Error response from wallet: " + strResponse); - } - - Log.info("Sending cash message with the following command: " + - sendCashParameters[0] + " " + sendCashParameters[1] + " " + - sendCashParameters[2] + " " + sendCashParameters[3] + " " + - sendCashParameters[4] + " " + sendCashParameters[5] + "." + - " Got result: [" + strResponse + "]"); - - return strResponse.trim(); - } - - - // Returns the message signature - public synchronized String signMessage(String address, String message) - throws WalletCallException, IOException, InterruptedException - { - String response = this.executeCommandAndGetSingleStringResponse( - "signmessage", wrapStringParameter(address), wrapStringParameter(message)); - - return response.trim(); - } - - - // Verifies a message - true if OK - public synchronized boolean verifyMessage(String address, String signature, String message) - throws WalletCallException, IOException, InterruptedException - { - String response = this.executeCommandAndGetSingleStringResponse( - "verifymessage", - wrapStringParameter(address), - wrapStringParameter(signature), - wrapStringParameter(message)); - - return response.trim().equalsIgnoreCase("true"); - } - - - public synchronized boolean isSendingOperationComplete(String opID) - throws WalletCallException, IOException, InterruptedException - { - JsonArray response = this.executeCommandAndGetJsonArray( - "z_getoperationstatus", wrapStringParameter("[\"" + opID + "\"]")); - JsonObject jsonStatus = response.get(0).asObject(); - - String status = jsonStatus.getString("status", "ERROR"); - - Log.info("Operation " + opID + " status is " + response + "."); - - if (status.equalsIgnoreCase("success") || - status.equalsIgnoreCase("error") || - status.equalsIgnoreCase("failed")) - { - return true; - } else if (status.equalsIgnoreCase("executing") || status.equalsIgnoreCase("queued")) - { - return false; - } else - { - throw new WalletCallException("Unexpected status response from wallet: " + response.toString()); - } - } - - - public synchronized boolean isCompletedOperationSuccessful(String opID) - throws WalletCallException, IOException, InterruptedException - { - JsonArray response = this.executeCommandAndGetJsonArray( - "z_getoperationstatus", wrapStringParameter("[\"" + opID + "\"]")); - JsonObject jsonStatus = response.get(0).asObject(); - - String status = jsonStatus.getString("status", "ERROR"); - - Log.info("Operation " + opID + " status is " + response + "."); - - if (status.equalsIgnoreCase("success")) - { - return true; - } else if (status.equalsIgnoreCase("error") || status.equalsIgnoreCase("failed")) - { - return false; - } else - { - throw new WalletCallException("Unexpected final operation status response from wallet: " + response.toString()); - } - } - - - public synchronized String getSuccessfulOperationTXID(String opID) - throws WalletCallException, IOException, InterruptedException - { - String TXID = null; - JsonArray response = this.executeCommandAndGetJsonArray( - "z_getoperationstatus", wrapStringParameter("[\"" + opID + "\"]")); - JsonObject jsonStatus = response.get(0).asObject(); - JsonValue opResultValue = jsonStatus.get("result"); - - if (opResultValue != null) - { - JsonObject opResult = opResultValue.asObject(); - if (opResult.get("txid") != null) - { - TXID = opResult.get("txid").asString(); - } - } - - return TXID; - } - - - // May only be called for already failed operations - public synchronized String getOperationFinalErrorMessage(String opID) - throws WalletCallException, IOException, InterruptedException - { - JsonArray response = this.executeCommandAndGetJsonArray( - "z_getoperationstatus", wrapStringParameter("[\"" + opID + "\"]")); - JsonObject jsonStatus = response.get(0).asObject(); - - JsonObject jsonError = jsonStatus.get("error").asObject(); - return jsonError.getString("message", "ERROR!"); - } - - - public synchronized NetworkAndBlockchainInfo getNetworkAndBlockchainInfo() - throws WalletCallException, IOException, InterruptedException - { - NetworkAndBlockchainInfo info = new NetworkAndBlockchainInfo(); - - String strNumCons = this.executeCommandAndGetSingleStringResponse("getconnectioncount"); - info.numConnections = Integer.valueOf(strNumCons.trim()); - - String strBlockCount = this.executeCommandAndGetSingleStringResponse("getblockcount"); - String lastBlockHash = this.executeCommandAndGetSingleStringResponse("getblockhash", strBlockCount.trim()); - JsonObject lastBlock = this.executeCommandAndGetJsonObject("getblock", wrapStringParameter(lastBlockHash.trim())); - info.lastBlockDate = new Date(Long.valueOf(lastBlock.getLong("time", -1) * 1000L)); - info.blockNumber = Integer.valueOf(strBlockCount.trim()); - - JsonObject migrationInfo = getMigrationInfo(); - String status = migrationInfo.get("enabled").toString().toUpperCase().replaceAll("[\n\r\"]", ""); - info.sproutToSaplingEnabled = status; - - return info; - } - - - /*public synchronized void lockWallet() - throws WalletCallException, IOException, InterruptedException - { - String response = this.executeCommandAndGetSingleStringResponse("walletlock"); - - // Response is expected to be empty - if (response.trim().length() > 0) - { - throw new WalletCallException("Unexpected response from wallet: " + response); - } - }*/ - - - // Unlocks the wallet for 3 years! - public synchronized void unlockWallet(String password) - throws WalletCallException, IOException, InterruptedException - { - - String response = this.executeCommandAndGetSingleStringResponse( - "walletpassphrase", wrapStringParameter(password), "100000000"); - - // Response is expected to be empty - if (response.trim().length() > 0) - { - throw new WalletCallException("Unexpected response from wallet: " + response); - } - } - - - // Wallet locks check - an unencrypted wallet will give an error - // zcash-cli walletlock - // error: {"code":-15,"message":"Error: running with an unencrypted wallet, but walletlock was called."} - public synchronized boolean isWalletEncrypted() - throws WalletCallException, IOException, InterruptedException + currentTransaction[5] = zAddress; + currentTransaction[6] = trans.get("txid").toString(); + + zReceivedTransactions.add(currentTransaction); + } + } + + return zReceivedTransactions.toArray(new String[0][]); + } + + public synchronized + JsonObject[] getTransactionMessagingDataForZaddress(String ZAddress) + throws WalletCallException, IOException, InterruptedException { + JsonArray jsonTransactions = executeCommandAndGetJsonArray( + "z_listreceivedbyaddress", wrapStringParameter(ZAddress), "0"); + List transactions = new ArrayList(); + for (int i = 0; i < jsonTransactions.size(); i++) { + JsonObject trans = jsonTransactions.get(i).asObject(); + transactions.add(trans); + } + + return transactions.toArray(new JsonObject[0]); + } + + // ./src/zcash-cli listunspent only returns T addresses it seems + public synchronized String[] getWalletPublicAddressesWithUnspentOutputs() + throws WalletCallException, IOException, InterruptedException { + JsonArray jsonUnspentOutputs = + executeCommandAndGetJsonArray("listunspent", "0"); + + Set addresses = new HashSet<>(); + for (int i = 0; i < jsonUnspentOutputs.size(); i++) { + JsonObject outp = jsonUnspentOutputs.get(i).asObject(); + addresses.add(outp.getString("address", "ERROR!")); + } + + return addresses.toArray(new String[0]); + } + + // ./zcash-cli listreceivedbyaddress 0 true + public synchronized String[] getWalletAllPublicAddresses() + throws WalletCallException, IOException, InterruptedException { + JsonArray jsonReceivedOutputs = + executeCommandAndGetJsonArray("listreceivedbyaddress", "0", "true"); + + Set addresses = new HashSet<>(); + for (int i = 0; i < jsonReceivedOutputs.size(); i++) { + JsonObject outp = jsonReceivedOutputs.get(i).asObject(); + addresses.add(outp.getString("address", "ERROR!")); + } + + return addresses.toArray(new String[0]); + } + + public synchronized Map getRawTransactionDetails(String txID) + throws WalletCallException, IOException, InterruptedException { + JsonObject jsonTransaction = this.executeCommandAndGetJsonObject( + "gettransaction", wrapStringParameter(txID)); + + Map map = new HashMap(); + + for (String name : jsonTransaction.names()) { + this.decomposeJSONValue(name, jsonTransaction.get(name), map); + } + + return map; + } + + public synchronized String getMemoField(String acc, String txID) + throws WalletCallException, IOException, InterruptedException { + JsonArray jsonTransactions = this.executeCommandAndGetJsonArray( + "z_listreceivedbyaddress", wrapStringParameter(acc)); + + for (int i = 0; i < jsonTransactions.size(); i++) { + if (jsonTransactions.get(i) + .asObject() + .getString("txid", "ERROR!") + .equals(txID)) { + if (jsonTransactions.get(i).asObject().get("memo") == null) { + return null; + } + + String memoHex = + jsonTransactions.get(i).asObject().getString("memo", "ERROR!"); + String decodedMemo = Util.decodeHexMemo(memoHex); + + // Return only if not null - sometimes multiple incoming transactions + // have the same ID if we have loopback send etc. + if (decodedMemo != null) { + return decodedMemo; + } + } + } + + return null; + } + + public synchronized void keypoolRefill(int count) + throws WalletCallException, IOException, InterruptedException { + String result = this.executeCommandAndGetSingleStringResponse( + "keypoolrefill", String.valueOf(count)); + } + + public synchronized String getRawTransaction(String txID) + throws WalletCallException, IOException, InterruptedException { + JsonObject jsonTransaction = this.executeCommandAndGetJsonObject( + "gettransaction", wrapStringParameter(txID)); + + return jsonTransaction.toString(WriterConfig.PRETTY_PRINT); + } + + public synchronized JsonObject getTransactionInfo(String txID) + throws WalletCallException, IOException, InterruptedException { + return this.executeCommandAndGetJsonObject("gettransaction", + wrapStringParameter(txID)); + } + + // return UNIX time as tring + public synchronized String getWalletTransactionTime(String txID) + throws WalletCallException, IOException, InterruptedException { + JsonObject jsonTransaction = this.executeCommandAndGetJsonObject( + "gettransaction", wrapStringParameter(txID)); + + return String.valueOf(jsonTransaction.getLong("time", -1)); + } + + public synchronized String getWalletTransactionConfirmations(String txID) + throws WalletCallException, IOException, InterruptedException { + JsonObject jsonTransaction = this.executeCommandAndGetJsonObject( + "gettransaction", wrapStringParameter(txID)); + + return jsonTransaction.get("confirmations").toString(); + } + + // Checks if a certain T address is a watch-only address or is otherwise + // invalid. + public synchronized boolean isWatchOnlyOrInvalidAddress(String address) + throws WalletCallException, IOException, InterruptedException { + JsonObject response = + this.executeCommandAndGetJsonValue("validateaddress", + wrapStringParameter(address)) + .asObject(); + + if (response.getBoolean("isvalid", false)) { + return response.getBoolean("iswatchonly", true); + } + + return true; + } + + // Returns confirmed balance only! + public synchronized String getBalanceForAddress(String address) + throws WalletCallException, IOException, InterruptedException { + JsonValue response = this.executeCommandAndGetJsonValue( + "z_getbalance", wrapStringParameter(address)); + + return String.valueOf(response.toString()); + } + + public synchronized String getUnconfirmedBalanceForAddress(String address) + throws WalletCallException, IOException, InterruptedException { + JsonValue response = this.executeCommandAndGetJsonValue( + "z_getbalance", wrapStringParameter(address), "0"); + + return String.valueOf(response.toString()); + } + + public synchronized String createNewAddress(boolean isZAddress, + boolean isSapling) + throws WalletCallException, IOException, InterruptedException { + String strResponse = this.executeCommandAndGetSingleStringResponse( + (isZAddress ? "z_" : "") + "getnewaddress", + (isZAddress ? (isSapling ? "sapling" : "sprout") : "")); + + return strResponse.trim(); + } + + // Returns OPID - this method is a bit old and could be improved, however it + // is known to work and would better not be changed unless there is a bug. + public synchronized String sendCash(String from, String to, String amount, + String memo, String transactionFee) + throws WalletCallException, IOException, InterruptedException { + Log.info( + "Starting operation send-cash. Parameters are: from address: {0}, to address: {1}, " + + "amount: {2}, memo: {3}, transaction fee: {4}", + from, to, amount, memo, transactionFee); + + StringBuilder hexMemo = new StringBuilder(); + for (byte c : memo.getBytes("UTF-8")) { + String hexChar = Integer.toHexString((int)c); + if (hexChar.length() < 2) { + hexChar = "0" + hexChar; + } + hexMemo.append(hexChar); + } + + JsonObject toArgument = new JsonObject(); + toArgument.set("address", to); + if (hexMemo.length() >= 2) { + toArgument.set("memo", hexMemo.toString()); + } + + // The JSON Builder has a problem with double values that have no fractional + // part it serializes them as integers that ZCash does not accept. So we do + // a replacement + // TODO: find a better/cleaner way to format the amount + toArgument.set("amount", "\uFFFF\uFFFF\uFFFF\uFFFF\uFFFF"); + + JsonArray toMany = new JsonArray(); + toMany.add(toArgument); + + String amountPattern = "\"amount\":\"\uFFFF\uFFFF\uFFFF\uFFFF\uFFFF\""; + // Make sure our replacement hack never leads to a mess up + String toManyBeforeReplace = toMany.toString(); + int firstIndex = toManyBeforeReplace.indexOf(amountPattern); + int lastIndex = toManyBeforeReplace.lastIndexOf(amountPattern); + if ((firstIndex == -1) || (firstIndex != lastIndex)) { + throw new WalletCallException("Error in forming z_sendmany command: " + + toManyBeforeReplace); + } + + DecimalFormatSymbols decSymbols = new DecimalFormatSymbols(Locale.ROOT); + + // Properly format teh transaction fee as a number + if ((transactionFee == null) || (transactionFee.trim().length() <= 0)) { + transactionFee = "0.0001"; // Default value + } else { + transactionFee = new DecimalFormat("########0.00######", decSymbols) + .format(Double.valueOf(transactionFee)); + } + + // This replacement is a hack to make sure the JSON object amount has double + // format 0.00 etc. + // TODO: find a better way to format the amount + String toManyArrayStr = toMany.toString().replace( + amountPattern, + "\"amount\":" + new DecimalFormat("########0.00######", decSymbols) + .format(Double.valueOf(amount))); + + String[] sendCashParameters = new String[] { + this.zcashcli.getCanonicalPath(), "z_sendmany", + wrapStringParameter(from), wrapStringParameter(toManyArrayStr), + // Default min confirmations for the input transactions is 1 + "1", + // transaction fee + transactionFee}; + + // Safeguard to make sure the monetary amount does not differ after + // formatting + BigDecimal bdAmout = new BigDecimal(amount); + JsonArray toManyVerificationArr = Json.parse(toManyArrayStr).asArray(); + BigDecimal bdFinalAmount = new BigDecimal( + toManyVerificationArr.get(0).asObject().getDouble("amount", -1)); + BigDecimal difference = bdAmout.subtract(bdFinalAmount).abs(); + if (difference.compareTo(new BigDecimal("0.000000015")) >= 0) { + throw new WalletCallException( + "Error in forming z_sendmany command: Amount differs after formatting: " + + amount + " | " + toManyArrayStr); + } + + Log.info("The following send command will be issued: " + + sendCashParameters[0] + " " + sendCashParameters[1] + " " + + sendCashParameters[2] + " " + sendCashParameters[3] + " " + + sendCashParameters[4] + " " + sendCashParameters[5] + "."); + + // Create caller to send cash + CommandExecutor caller = new CommandExecutor(sendCashParameters); + String strResponse = caller.execute(); + + if (strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error:") || + strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) { + throw new WalletCallException("Error response from wallet: " + + strResponse); + } + + Log.info("Sent cash with the following command: " + sendCashParameters[0] + + " " + sendCashParameters[1] + " " + sendCashParameters[2] + " " + + sendCashParameters[3] + " " + sendCashParameters[4] + " " + + sendCashParameters[5] + "." + + " Got result: [" + strResponse + "]"); + + return strResponse.trim(); + } + + /** + * Sends ZEL from a source address to a destination address. The change is + * sent back to the source address. The amount of change is calculated based + * on the existing confirmed balance for the address (parameter). This may not + * be 100% accurate if the blockchain is not synchronized. + * + * @param from source address (T/Z) + * @param to destination address (T/Z) + * @param balance current confirmed balance of the source address + * @param amount ZEL amount to send + * @param memo text memo to include in the transaction + * @param transactionFee transaction see to include + * + * @return a zelcashd operation ID for the send operation + * + * @throws WalletCallException + * @throws IOException + * @throws InterruptedException + */ + public synchronized String sendCashWithReturnOfChange(String from, String to, + String balance, + String amount, + String memo, + String transactionFee) + throws WalletCallException, IOException, InterruptedException { + Log.info( + "Starting operation send cash with retrun of change. Parameters are: from address: {0}, to address: {1}, " + + + "current balance: {2}, amount: {3}, memo: {4}, transaction fee: {5}", + from, to, balance, amount, memo, transactionFee); + + final DecimalFormatSymbols decSymbols = + new DecimalFormatSymbols(Locale.ROOT); + final DecimalFormat sendNumberFormat = + new DecimalFormat("########0.00######", decSymbols); + + String hexMemo = Util.encodeHexString(memo); + // Form the main amount to send + JsonObject toDestinationArgument = new JsonObject(); + toDestinationArgument.set("address", to); + if (hexMemo.length() >= 2) { + toDestinationArgument.set("memo", hexMemo.toString()); + } + String formattedAmountToSend = + sendNumberFormat.format(new BigDecimal(amount)); + String formattedTransactionFee = + sendNumberFormat.format(new BigDecimal(transactionFee)); + toDestinationArgument.set("amount", formattedAmountToSend); + // Form the return amount - based on the balance + JsonObject backToSourceArgument = new JsonObject(); + backToSourceArgument.set("address", from); + BigDecimal changeToSendBackBD = + new BigDecimal(balance) + .subtract(new BigDecimal(formattedAmountToSend)) + .subtract(new BigDecimal(formattedTransactionFee)); + boolean changeIsZero = false; + if (changeToSendBackBD.compareTo(new BigDecimal("0")) < 0) { + throw new WalletCallException("Error: change was calculated negative"); + } else if (changeToSendBackBD.compareTo(new BigDecimal("0")) == 0) { + Log.info("Change was calculated exactly zero - will not be sent back!"); + changeIsZero = true; + } + + String formattedChangeToReturn = + sendNumberFormat.format(changeToSendBackBD); + backToSourceArgument.set("amount", formattedChangeToReturn); + + // Array of two addresses to send to + JsonArray toMany = new JsonArray(); + toMany.add(toDestinationArgument); + if (!changeIsZero) { + toMany.add(backToSourceArgument); + } + + String toManyArrayStr = toMany.toString(WriterConfig.MINIMAL); + String[] sendCashParameters = new String[] { + this.zcashcli.getCanonicalPath(), "z_sendmany", + wrapStringParameter(from), wrapStringParameter(toManyArrayStr), + // Default min confirmations for the input transactions is 1 + "1", + // transaction fee + formattedTransactionFee}; + + // Safeguard to make sure the monetary amount does not differ after + // formatting + BigDecimal bdAmout = + new BigDecimal(amount); // original amount used for check + JsonArray toManyVerificationArr = Json.parse(toManyArrayStr).asArray(); + BigDecimal bdFinalAmount = new BigDecimal( + toManyVerificationArr.get(0).asObject().getString("amount", "-1")); + BigDecimal amountCheckDifference = bdAmout.subtract(bdFinalAmount).abs(); + if (amountCheckDifference.compareTo(new BigDecimal("0.0")) > + 0) // MUST be exact { - if (ZCashClientCaller.walletIsEncrypted == null) { - String[] params = new String[] { this.zcashcli.getCanonicalPath(), "walletlock" }; - CommandExecutor caller = new CommandExecutor(params); - String strResult = caller.execute(); - - if (strResult.trim().length() <= 0) - { - // If it could be locked with no result - obviously encrypted - ZCashClientCaller.walletIsEncrypted = true; - } else if (strResult.trim().toLowerCase(Locale.ROOT).startsWith("error:")) - { - // Expecting an error of an unencrypted wallet - String jsonPart = strResult.substring(strResult.indexOf("{")); - JsonValue response = null; - try - { - response = Json.parse(jsonPart); - } catch (ParseException pe) - { - throw new WalletCallException(jsonPart + "\n" + pe.getMessage() + "\n", pe); - } - - JsonObject respObject = response.asObject(); - if ((respObject.getDouble("code", -1) == -15) && - (respObject.getString("message", "ERR").indexOf("unencrypted wallet") != -1)) - { - // Obviously unencrupted - ZCashClientCaller.walletIsEncrypted = false; - } else - { - throw new WalletCallException("Unexpected response from wallet: " + strResult); - } - } else if (strResult.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) - { - JsonObject respObject = Util.getJsonErrorMessage(strResult); - if ((respObject.getDouble("code", -1) == -15) && - (respObject.getString("message", "ERR").indexOf("unencrypted wallet") != -1)) - { - // Obviously unencrupted - ZCashClientCaller.walletIsEncrypted = false; - } else - { - throw new WalletCallException("Unexpected response from wallet: " + strResult); - } - } else - { - throw new WalletCallException("Unexpected response from wallet: " + strResult); - } - } - return ZCashClientCaller.walletIsEncrypted; - + throw new WalletCallException( + "Error in forming z_sendmany command: Main amount differs after formatting: " + + formattedAmountToSend + " | \n" + toManyArrayStr); + } + // Amount + change + fee = balance // This must also match + BigDecimal bdFinalChange = + changeIsZero + ? new BigDecimal("0") + : new BigDecimal(toManyVerificationArr.get(1).asObject().getString( + "amount", "-1")); + amountCheckDifference = + bdFinalChange.add(bdFinalAmount) + .add(new BigDecimal(formattedTransactionFee)) + .subtract(new BigDecimal(balance)) + .abs(); // Original balance used after formatting + if (amountCheckDifference.compareTo(new BigDecimal("0.0")) > + 0) // MUST be exact + { + throw new WalletCallException( + "Error in forming z_sendmany command: Sum differs after formatting: " + + formattedAmountToSend + " | \n" + toManyArrayStr); + } + + Log.info( + "The following send command (with change retrun) will be issued: " + + sendCashParameters[0] + " " + sendCashParameters[1] + " " + + sendCashParameters[2] + " " + sendCashParameters[3] + " " + + sendCashParameters[4] + " " + sendCashParameters[5] + "."); + + // Create caller to send cash + CommandExecutor caller = new CommandExecutor(sendCashParameters); + String strResponse = caller.execute(); + + if (strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error:") || + strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) { + throw new WalletCallException("Error response from wallet: " + + strResponse); + } + + Log.info("Sent cash (with change back) with the following command: " + + sendCashParameters[0] + " " + sendCashParameters[1] + " " + + sendCashParameters[2] + " " + sendCashParameters[3] + " " + + sendCashParameters[4] + " " + sendCashParameters[5] + "." + + " Got result: [" + strResponse + "]"); + + return strResponse.trim(); + } + + // Returns OPID + public synchronized String sendMessage(String from, String to, double amount, + double fee, String memo) + throws WalletCallException, IOException, InterruptedException { + String hexMemo = Util.encodeHexString(memo); + JsonObject toArgument = new JsonObject(); + toArgument.set("address", to); + if (hexMemo.length() >= 2) { + toArgument.set("memo", hexMemo.toString()); + } + + DecimalFormatSymbols decSymbols = new DecimalFormatSymbols(Locale.ROOT); + + // TODO: The JSON Builder has a problem with double values that have no + // fractional part it serializes them as integers that ZCash does not + // accept. This will work with the fractional amounts always used for + // messaging + toArgument.set( + "amount", + new DecimalFormat("########0.00######", decSymbols).format(amount)); + + JsonArray toMany = new JsonArray(); + toMany.add(toArgument); + + String toManyArrayStr = toMany.toString(); + String[] sendCashParameters = new String[] { + this.zcashcli.getCanonicalPath(), "z_sendmany", + wrapStringParameter(from), wrapStringParameter(toManyArrayStr), + // Default min confirmations for the input transactions is 1 + "1", + // transaction fee + new DecimalFormat("########0.00######", decSymbols).format(fee)}; + + // Create caller to send cash + CommandExecutor caller = new CommandExecutor(sendCashParameters); + String strResponse = caller.execute(); + + if (strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error:") || + strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) { + throw new WalletCallException("Error response from wallet: " + + strResponse); } + Log.info("Sending cash message with the following command: " + + sendCashParameters[0] + " " + sendCashParameters[1] + " " + + sendCashParameters[2] + " " + sendCashParameters[3] + " " + + sendCashParameters[4] + " " + sendCashParameters[5] + "." + + " Got result: [" + strResponse + "]"); + + return strResponse.trim(); + } + + // Returns the message signature + public synchronized String signMessage(String address, String message) + throws WalletCallException, IOException, InterruptedException { + String response = this.executeCommandAndGetSingleStringResponse( + "signmessage", wrapStringParameter(address), + wrapStringParameter(message)); + + return response.trim(); + } + + // Verifies a message - true if OK + public synchronized boolean verifyMessage(String address, String signature, + String message) + throws WalletCallException, IOException, InterruptedException { + String response = this.executeCommandAndGetSingleStringResponse( + "verifymessage", wrapStringParameter(address), + wrapStringParameter(signature), wrapStringParameter(message)); + + return response.trim().equalsIgnoreCase("true"); + } + + public synchronized boolean isSendingOperationComplete(String opID) + throws WalletCallException, IOException, InterruptedException { + JsonArray response = this.executeCommandAndGetJsonArray( + "z_getoperationstatus", wrapStringParameter("[\"" + opID + "\"]")); + JsonObject jsonStatus = response.get(0).asObject(); + + String status = jsonStatus.getString("status", "ERROR"); + + Log.info("Operation " + opID + " status is " + response + "."); + + if (status.equalsIgnoreCase("success") || + status.equalsIgnoreCase("error") || status.equalsIgnoreCase("failed")) { + return true; + } else if (status.equalsIgnoreCase("executing") || + status.equalsIgnoreCase("queued")) { + return false; + } else { + throw new WalletCallException("Unexpected status response from wallet: " + + response.toString()); + } + } + + public synchronized boolean isCompletedOperationSuccessful(String opID) + throws WalletCallException, IOException, InterruptedException { + JsonArray response = this.executeCommandAndGetJsonArray( + "z_getoperationstatus", wrapStringParameter("[\"" + opID + "\"]")); + JsonObject jsonStatus = response.get(0).asObject(); + + String status = jsonStatus.getString("status", "ERROR"); + + Log.info("Operation " + opID + " status is " + response + "."); + + if (status.equalsIgnoreCase("success")) { + return true; + } else if (status.equalsIgnoreCase("error") || + status.equalsIgnoreCase("failed")) { + return false; + } else { + throw new WalletCallException( + "Unexpected final operation status response from wallet: " + + response.toString()); + } + } + + public synchronized String getSuccessfulOperationTXID(String opID) + throws WalletCallException, IOException, InterruptedException { + String TXID = null; + JsonArray response = this.executeCommandAndGetJsonArray( + "z_getoperationstatus", wrapStringParameter("[\"" + opID + "\"]")); + JsonObject jsonStatus = response.get(0).asObject(); + JsonValue opResultValue = jsonStatus.get("result"); + + if (opResultValue != null) { + JsonObject opResult = opResultValue.asObject(); + if (opResult.get("txid") != null) { + TXID = opResult.get("txid").asString(); + } + } + + return TXID; + } + + // May only be called for already failed operations + public synchronized String getOperationFinalErrorMessage(String opID) + throws WalletCallException, IOException, InterruptedException { + JsonArray response = this.executeCommandAndGetJsonArray( + "z_getoperationstatus", wrapStringParameter("[\"" + opID + "\"]")); + JsonObject jsonStatus = response.get(0).asObject(); + + JsonObject jsonError = jsonStatus.get("error").asObject(); + return jsonError.getString("message", "ERROR!"); + } + + public synchronized NetworkAndBlockchainInfo getNetworkAndBlockchainInfo() + throws WalletCallException, IOException, InterruptedException { + NetworkAndBlockchainInfo info = new NetworkAndBlockchainInfo(); + + String strNumCons = + this.executeCommandAndGetSingleStringResponse("getconnectioncount"); + info.numConnections = Integer.valueOf(strNumCons.trim()); + + String strBlockCount = + this.executeCommandAndGetSingleStringResponse("getblockcount"); + String lastBlockHash = this.executeCommandAndGetSingleStringResponse( + "getblockhash", strBlockCount.trim()); + JsonObject lastBlock = this.executeCommandAndGetJsonObject( + "getblock", wrapStringParameter(lastBlockHash.trim())); + info.lastBlockDate = + new Date(Long.valueOf(lastBlock.getLong("time", -1) * 1000L)); + info.blockNumber = Integer.valueOf(strBlockCount.trim()); + + JsonObject migrationInfo = getMigrationInfo(); + String status = + migrationInfo.get("enabled").toString().toUpperCase().replaceAll( + "[\n\r\"]", ""); + info.sproutToSaplingEnabled = status; + + return info; + } + + /*public synchronized void lockWallet() + throws WalletCallException, IOException, InterruptedException + { + String response = + this.executeCommandAndGetSingleStringResponse("walletlock"); + + // Response is expected to be empty + if (response.trim().length() > 0) + { + throw new WalletCallException("Unexpected response from + wallet: " + response); + } + }*/ + + // Unlocks the wallet for 3 years! + public synchronized void unlockWallet(String password) + throws WalletCallException, IOException, InterruptedException { + + String response = this.executeCommandAndGetSingleStringResponse( + "walletpassphrase", wrapStringParameter(password), "100000000"); + + // Response is expected to be empty + if (response.trim().length() > 0) { + throw new WalletCallException("Unexpected response from wallet: " + + response); + } + } + + // Wallet locks check - an unencrypted wallet will give an error + // zcash-cli walletlock + // error: {"code":-15,"message":"Error: running with an unencrypted wallet, + // but walletlock was called."} + public synchronized boolean isWalletEncrypted() + throws WalletCallException, IOException, InterruptedException { + if (ZCashClientCaller.walletIsEncrypted == null) { + String[] params = + new String[] {this.zcashcli.getCanonicalPath(), "walletlock"}; + CommandExecutor caller = new CommandExecutor(params); + String strResult = caller.execute(); + + if (strResult.trim().length() <= 0) { + // If it could be locked with no result - obviously encrypted + ZCashClientCaller.walletIsEncrypted = true; + } else if (strResult.trim() + .toLowerCase(Locale.ROOT) + .startsWith("error:")) { + // Expecting an error of an unencrypted wallet + String jsonPart = strResult.substring(strResult.indexOf("{")); + JsonValue response = null; + try { + response = Json.parse(jsonPart); + } catch (ParseException pe) { + throw new WalletCallException( + jsonPart + "\n" + pe.getMessage() + "\n", pe); + } + + JsonObject respObject = response.asObject(); + if ((respObject.getDouble("code", -1) == -15) && + (respObject.getString("message", "ERR") + .indexOf("unencrypted wallet") != -1)) { + // Obviously unencrupted + ZCashClientCaller.walletIsEncrypted = false; + } else { + throw new WalletCallException("Unexpected response from wallet: " + + strResult); + } + } else if (strResult.trim() + .toLowerCase(Locale.ROOT) + .startsWith("error code:")) { + JsonObject respObject = Util.getJsonErrorMessage(strResult); + if ((respObject.getDouble("code", -1) == -15) && + (respObject.getString("message", "ERR") + .indexOf("unencrypted wallet") != -1)) { + // Obviously unencrupted + ZCashClientCaller.walletIsEncrypted = false; + } else { + throw new WalletCallException("Unexpected response from wallet: " + + strResult); + } + } else { + throw new WalletCallException("Unexpected response from wallet: " + + strResult); + } + } + return ZCashClientCaller.walletIsEncrypted; + } + + /** + * Encrypts the wallet. Typical success/error use cases are: + * + * ./zcash-cli encryptwallet "1234" + * wallet encrypted; Bitcoin server stopping, restart to run with encrypted + * wallet. The keypool has been flushed, you need to make a new backup. + * + * ./zcash-cli encryptwallet "1234" + * error: {"code":-15,"message":"Error: running with an encrypted wallet, but + * encryptwallet was called."} + * + * @param password + */ + public synchronized void encryptWallet(String password) + throws WalletCallException, IOException, InterruptedException { + String response = this.executeCommandAndGetSingleStringResponse( + "encryptwallet", wrapStringParameter(password)); + Log.info("Result of wallet encryption is: \n" + response); + // If no exception - obviously successful + } + + public synchronized void passPhraseChangeWallet(String oldPassword, + String newPassword) + throws WalletCallException, IOException, InterruptedException { + String response = this.executeCommandAndGetSingleStringResponse( + "walletpassphrasechange", wrapStringParameter(oldPassword), + wrapStringParameter(newPassword)); + Log.info("Result of encryption password change is: \n" + response); + // If no exception - obviously successful + } + + public synchronized String backupWallet(String fileName) + throws WalletCallException, IOException, InterruptedException { + Log.info("Backup up wallet to location: " + fileName); + String response = this.executeCommandAndGetSingleStringResponse( + "backupwallet", wrapStringParameter(fileName)); + // If no exception - obviously successful + return response; + } + + public synchronized String exportWallet(String fileName) + throws WalletCallException, IOException, InterruptedException { + Log.info("Export wallet keys to location: " + fileName); + String response = this.executeCommandAndGetSingleStringResponse( + "z_exportwallet", wrapStringParameter(fileName)); + // If no exception - obviously successful + return response; + } + + public synchronized void importWallet(String fileName) + throws WalletCallException, IOException, InterruptedException { + Log.info("Import wallet keys from location: " + fileName); + String response = this.executeCommandAndGetSingleStringResponse( + "z_importwallet", wrapStringParameter(fileName)); + // If no exception - obviously successful + } + + public synchronized String getTPrivateKey(String address) + throws WalletCallException, IOException, InterruptedException { + String response = this.executeCommandAndGetSingleStringResponse( + "dumpprivkey", wrapStringParameter(address)); + + return response.trim(); + } + + public synchronized String getZPrivateKey(String address) + throws WalletCallException, IOException, InterruptedException { + String response = this.executeCommandAndGetSingleStringResponse( + "z_exportkey", wrapStringParameter(address)); + + return response.trim(); + } + + // Imports a private key - tries both possibilities T/Z + public synchronized String importPrivateKey(String key) + throws WalletCallException, IOException, InterruptedException { + // First try a Z key + String[] params = new String[] {this.zcashcli.getCanonicalPath(), + "-rpcclienttimeout=5000", "z_importkey", + wrapStringParameter(key)}; + CommandExecutor caller = new CommandExecutor(params); + String strResult = caller.execute(); + + if (Util.stringIsEmpty(strResult) || + (!strResult.trim().toLowerCase(Locale.ROOT).contains("error"))) { + return strResult == null ? "" : strResult.trim(); + } + + // Obviously we have an error trying to import a Z key + if (strResult.trim().toLowerCase(Locale.ROOT).startsWith("error") && + (strResult.indexOf("{") != -1)) { + // Expecting an error of a T address key + String jsonPart = strResult.substring(strResult.indexOf("{")); + JsonValue response = null; + try { + response = Json.parse(jsonPart); + } catch (ParseException pe) { + throw new WalletCallException(jsonPart + "\n" + pe.getMessage() + "\n", + pe); + } + + JsonObject respObject = response.asObject(); + if ((respObject.getDouble("code", +123) == -1) && + (respObject.getString("message", "ERR") + .indexOf("wrong network type") != -1)) { + // Obviously T address - do nothing here + } else { + throw new WalletCallException("Unexpected response from wallet: " + + strResult); + } + } else if (strResult.trim() + .toLowerCase(Locale.ROOT) + .startsWith("error code:")) { + JsonObject respObject = Util.getJsonErrorMessage(strResult); + if (((respObject.getDouble("code", +123) == -1) && + (respObject.getString("message", "ERR") + .indexOf("wrong network type") != -1)) || + strResult.trim() + .toLowerCase(Locale.ROOT) + .indexOf("invalid spending key") != -1) { + // Obviously T address - do nothing here + } else { + throw new WalletCallException("Unexpected response from wallet: " + + strResult); + } + } else { + throw new WalletCallException("Unexpected response from wallet: " + + strResult); + } + + // Second try a T key + strResult = this.executeCommandAndGetSingleStringResponse( + "-rpcclienttimeout=5000", "importprivkey", wrapStringParameter(key)); + + if (Util.stringIsEmpty(strResult) || + (!strResult.trim().toLowerCase(Locale.ROOT).contains("error"))) { + return strResult == null ? "" : strResult.trim(); + } + + // Obviously an error + throw new WalletCallException("Unexpected response from wallet: " + + strResult); + } + + public synchronized String getZelNodeKey() + throws WalletCallException, IOException, InterruptedException { + + String objResponse = + this.executeCommandAndGetSingleStringResponse("createzelnodekey"); + return objResponse; + } + + public synchronized JsonObject startZelNode(String zelNodeAlias) + throws WalletCallException, IOException, InterruptedException { + + String objResponse = this.executeCommandAndGetSingleStringResponse( + "startdeterministiczelnode", zelNodeAlias, "false"); + JsonValue response = null; + try { + response = Json.parse(objResponse); + } catch (ParseException pe) { + throw new WalletCallException(objResponse + "\n" + pe.getMessage() + "\n", + pe); + } + + if (response.isObject()) { + return response.asObject(); + } else { + throw new WalletCallException( + "Unexpected non-object response from wallet: " + response.toString()); + } + } + + public synchronized JsonArray getZelNodeOutputs() + throws WalletCallException, IOException, InterruptedException { + + JsonArray objResponse = + this.executeCommandAndGetJsonArray("getzelnodeoutputs", null); + return objResponse; + } + + public synchronized JsonArray getZelNodeList() + throws WalletCallException, IOException, InterruptedException { + + JsonArray objResponse = this.executeCommandAndGetJsonArray( + "viewdeterministiczelnodelist", null); + return objResponse; + } + + public synchronized JsonArray getMyZelNodeList() + throws WalletCallException, IOException, InterruptedException { + + JsonArray objResponse = + this.executeCommandAndGetJsonArray("listzelnodeconf", null); + return objResponse; + } + + public synchronized JsonArray getCollectableZelNodeRewardsInformation() + throws WalletCallException, IOException, InterruptedException { + return executeCommandAndGetJsonArray("listunspent", "0"); + } + + public synchronized JsonObject zShieldCoinBase(String to, int utxoSize) + throws WalletCallException, IOException, InterruptedException { + + String[] zShieldCoinBaseParameters = + new String[] {this.zcashcli.getCanonicalPath(), + "z_shieldcoinbase", + "*", + wrapStringParameter(to), + "0.0001", + String.valueOf(utxoSize)}; + + Log.info("The following zSchieldCoinBase command will be issued: " + + zShieldCoinBaseParameters[0] + " " + zShieldCoinBaseParameters[1] + + " " + zShieldCoinBaseParameters[2] + " " + + zShieldCoinBaseParameters[3] + " " + zShieldCoinBaseParameters[4] + + "."); + + // Create caller to send cash + CommandExecutor caller = new CommandExecutor(zShieldCoinBaseParameters); + String strResponse = caller.execute(); + + if (strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error:") || + strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) { + throw new WalletCallException("Error response from wallet: " + + strResponse); + } + + Log.info("zSchieldCoinBase with the following command: " + + zShieldCoinBaseParameters[0] + " " + zShieldCoinBaseParameters[1] + + " " + zShieldCoinBaseParameters[2] + " " + + zShieldCoinBaseParameters[3] + " " + zShieldCoinBaseParameters[4] + + " " + zShieldCoinBaseParameters[5] + "." + + " Got result: [" + strResponse + "]"); + + return Json.parse(strResponse).asObject(); + } + + public synchronized JsonObject getMigrationInfo() + throws WalletCallException, IOException, InterruptedException { + + String objResponse = + this.executeCommandAndGetSingleStringResponse("z_getmigrationstatus"); + JsonValue response = null; + try { + response = Json.parse(objResponse); + } catch (ParseException pe) { + throw new WalletCallException(objResponse + "\n" + pe.getMessage() + "\n", + pe); + } + + if (response.isObject()) { + return response.asObject(); + } else { + throw new WalletCallException( + "Unexpected non-object response from wallet: " + response.toString()); + } + } + + private JsonObject executeCommandAndGetJsonObject(String command1, + String command2) + throws WalletCallException, IOException, InterruptedException { + JsonValue response = this.executeCommandAndGetJsonValue(command1, command2); + + if (response.isObject()) { + return response.asObject(); + } else { + throw new WalletCallException( + "Unexpected non-object response from wallet: " + response.toString()); + } + } + + private JsonArray executeCommandAndGetJsonArray(String command1, + String command2) + throws WalletCallException, IOException, InterruptedException { + return this.executeCommandAndGetJsonArray(command1, command2, null); + } + + private JsonArray executeCommandAndGetJsonArray(String command1, + String command2, + String command3) + throws WalletCallException, IOException, InterruptedException { + JsonValue response = + this.executeCommandAndGetJsonValue(command1, command2, command3); + + if (response.isArray()) { + return response.asArray(); + } else { + throw new WalletCallException( + "Unexpected non-array response from wallet: " + response.toString()); + } + } + + private JsonValue executeCommandAndGetJsonValue(String command1, + String command2) + throws WalletCallException, IOException, InterruptedException { + return this.executeCommandAndGetJsonValue(command1, command2, null); + } + + private JsonValue executeCommandAndGetJsonValue(String command1, + String command2, + String command3) + throws WalletCallException, IOException, InterruptedException { + String strResponse = this.executeCommandAndGetSingleStringResponse( + command1, command2, command3); + + JsonValue response = null; + try { + response = Json.parse(strResponse); + } catch (ParseException pe) { + throw new WalletCallException(strResponse + "\n" + pe.getMessage() + "\n", + pe); + } - /** - * Encrypts the wallet. Typical success/error use cases are: - * - * ./zcash-cli encryptwallet "1234" - * wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. - * The keypool has been flushed, you need to make a new backup. - * - * ./zcash-cli encryptwallet "1234" - * error: {"code":-15,"message":"Error: running with an encrypted wallet, but encryptwallet was called."} - * - * @param password - */ - public synchronized void encryptWallet(String password) - throws WalletCallException, IOException, InterruptedException - { - String response = this.executeCommandAndGetSingleStringResponse( - "encryptwallet", wrapStringParameter(password)); - Log.info("Result of wallet encryption is: \n" + response); - // If no exception - obviously successful - } - - public synchronized void passPhraseChangeWallet(String oldPassword, String newPassword) - throws WalletCallException, IOException, InterruptedException - { - String response = this.executeCommandAndGetSingleStringResponse( - "walletpassphrasechange", wrapStringParameter(oldPassword), wrapStringParameter(newPassword)); - Log.info("Result of encryption password change is: \n" + response); - // If no exception - obviously successful - } - - - public synchronized String backupWallet(String fileName) - throws WalletCallException, IOException, InterruptedException - { - Log.info("Backup up wallet to location: " + fileName); - String response = this.executeCommandAndGetSingleStringResponse( - "backupwallet", wrapStringParameter(fileName)); - // If no exception - obviously successful - return response; - } - - - public synchronized String exportWallet(String fileName) - throws WalletCallException, IOException, InterruptedException - { - Log.info("Export wallet keys to location: " + fileName); - String response = this.executeCommandAndGetSingleStringResponse( - "z_exportwallet", wrapStringParameter(fileName)); - // If no exception - obviously successful - return response; - } - - - public synchronized void importWallet(String fileName) - throws WalletCallException, IOException, InterruptedException - { - Log.info("Import wallet keys from location: " + fileName); - String response = this.executeCommandAndGetSingleStringResponse( - "z_importwallet", wrapStringParameter(fileName)); - // If no exception - obviously successful - } - - - public synchronized String getTPrivateKey(String address) - throws WalletCallException, IOException, InterruptedException - { - String response = this.executeCommandAndGetSingleStringResponse( - "dumpprivkey", wrapStringParameter(address)); - - return response.trim(); - } - - - public synchronized String getZPrivateKey(String address) - throws WalletCallException, IOException, InterruptedException - { - String response = this.executeCommandAndGetSingleStringResponse( - "z_exportkey", wrapStringParameter(address)); - - return response.trim(); - } - - - // Imports a private key - tries both possibilities T/Z - public synchronized String importPrivateKey(String key) - throws WalletCallException, IOException, InterruptedException - { - // First try a Z key - String[] params = new String[] - { - this.zcashcli.getCanonicalPath(), - "-rpcclienttimeout=5000", - "z_importkey", - wrapStringParameter(key) - }; - CommandExecutor caller = new CommandExecutor(params); - String strResult = caller.execute(); - - if (Util.stringIsEmpty(strResult) || - (!strResult.trim().toLowerCase(Locale.ROOT).contains("error"))) - { - return strResult == null ? "" : strResult.trim(); - } - - // Obviously we have an error trying to import a Z key - if (strResult.trim().toLowerCase(Locale.ROOT).startsWith("error") && - (strResult.indexOf("{") != -1)) - { - // Expecting an error of a T address key - String jsonPart = strResult.substring(strResult.indexOf("{")); - JsonValue response = null; - try - { - response = Json.parse(jsonPart); - } catch (ParseException pe) - { - throw new WalletCallException(jsonPart + "\n" + pe.getMessage() + "\n", pe); - } - - JsonObject respObject = response.asObject(); - if ((respObject.getDouble("code", +123) == -1) && - (respObject.getString("message", "ERR").indexOf("wrong network type") != -1)) - { - // Obviously T address - do nothing here - } else - { - throw new WalletCallException("Unexpected response from wallet: " + strResult); - } - } else if (strResult.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) - { - JsonObject respObject = Util.getJsonErrorMessage(strResult); - if (((respObject.getDouble("code", +123) == -1) && (respObject.getString("message", "ERR").indexOf("wrong network type") != -1)) || strResult.trim().toLowerCase(Locale.ROOT).indexOf("invalid spending key") != -1) - { - // Obviously T address - do nothing here - } else - { - throw new WalletCallException("Unexpected response from wallet: " + strResult); - } - } else - { - throw new WalletCallException("Unexpected response from wallet: " + strResult); - } - - // Second try a T key - strResult = this.executeCommandAndGetSingleStringResponse( - "-rpcclienttimeout=5000", "importprivkey", wrapStringParameter(key)); - - if (Util.stringIsEmpty(strResult) || - (!strResult.trim().toLowerCase(Locale.ROOT).contains("error"))) - { - return strResult == null ? "" : strResult.trim(); - } - - // Obviously an error - throw new WalletCallException("Unexpected response from wallet: " + strResult); - } - - public synchronized String getZelNodeKey() - throws WalletCallException, IOException, InterruptedException - { - - String objResponse = this.executeCommandAndGetSingleStringResponse("createzelnodekey"); - return objResponse; - } - - public synchronized JsonObject startZelNode(String zelNodeAlias) - throws WalletCallException, IOException, InterruptedException - { - - String objResponse = this.executeCommandAndGetSingleStringResponse("startdeterministiczelnode", zelNodeAlias, "false"); - JsonValue response = null; - try - { - response = Json.parse(objResponse); - } catch (ParseException pe) - { - throw new WalletCallException(objResponse + "\n" + pe.getMessage() + "\n", pe); - } - - if (response.isObject()) - { - return response.asObject(); - } else - { - throw new WalletCallException("Unexpected non-object response from wallet: " + response.toString()); - } - } - - - public synchronized JsonArray getZelNodeOutputs() - throws WalletCallException, IOException, InterruptedException - { - - JsonArray objResponse = this.executeCommandAndGetJsonArray("getzelnodeoutputs", null); - return objResponse; - } - - public synchronized JsonArray getZelNodeList() - throws WalletCallException, IOException, InterruptedException - { - - JsonArray objResponse = this.executeCommandAndGetJsonArray("viewdeterministiczelnodelist", null); - return objResponse; - } - - public synchronized JsonArray getMyZelNodeList() - throws WalletCallException, IOException, InterruptedException - { - - JsonArray objResponse = this.executeCommandAndGetJsonArray("listzelnodeconf", null); - return objResponse; - } - - public synchronized JsonArray getCollectableZelNodeRewardsInformation() - throws WalletCallException, IOException, InterruptedException - { - return executeCommandAndGetJsonArray("listunspent", "0"); - - } - - public synchronized JsonObject zShieldCoinBase(String to, int utxoSize) - throws WalletCallException, IOException, InterruptedException - { - - String[] zShieldCoinBaseParameters = new String[] - { - this.zcashcli.getCanonicalPath(), - "z_shieldcoinbase", - "*", - wrapStringParameter(to), - "0.0001", - String.valueOf(utxoSize) - }; - - Log.info("The following zSchieldCoinBase command will be issued: " + - zShieldCoinBaseParameters[0] + " " + zShieldCoinBaseParameters[1] + " " + - zShieldCoinBaseParameters[2] + " " + zShieldCoinBaseParameters[3] + " " + - zShieldCoinBaseParameters[4] + "."); - - // Create caller to send cash - CommandExecutor caller = new CommandExecutor(zShieldCoinBaseParameters); - String strResponse = caller.execute(); - - if (strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error:") || - strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) - { - throw new WalletCallException("Error response from wallet: " + strResponse); - } - - Log.info("zSchieldCoinBase with the following command: " + - zShieldCoinBaseParameters[0] + " " + zShieldCoinBaseParameters[1] + " " + - zShieldCoinBaseParameters[2] + " " + zShieldCoinBaseParameters[3] + " " + - zShieldCoinBaseParameters[4] + " " + zShieldCoinBaseParameters[5] + "." + - " Got result: [" + strResponse + "]"); - - return Json.parse(strResponse).asObject(); - } - - public synchronized JsonObject getMigrationInfo() - throws WalletCallException, IOException, InterruptedException - { - - String objResponse = this.executeCommandAndGetSingleStringResponse("z_getmigrationstatus"); - JsonValue response = null; - try - { - response = Json.parse(objResponse); - } catch (ParseException pe) - { - throw new WalletCallException(objResponse + "\n" + pe.getMessage() + "\n", pe); - } - - if (response.isObject()) - { - return response.asObject(); - } else - { - throw new WalletCallException("Unexpected non-object response from wallet: " + response.toString()); - } - } - - private JsonObject executeCommandAndGetJsonObject(String command1, String command2) - throws WalletCallException, IOException, InterruptedException - { - JsonValue response = this.executeCommandAndGetJsonValue(command1, command2); - - if (response.isObject()) - { - return response.asObject(); - } else - { - throw new WalletCallException("Unexpected non-object response from wallet: " + response.toString()); - } - - } - - - private JsonArray executeCommandAndGetJsonArray(String command1, String command2) - throws WalletCallException, IOException, InterruptedException - { - return this.executeCommandAndGetJsonArray(command1, command2, null); - } - - - private JsonArray executeCommandAndGetJsonArray(String command1, String command2, String command3) - throws WalletCallException, IOException, InterruptedException - { - JsonValue response = this.executeCommandAndGetJsonValue(command1, command2, command3); - - if (response.isArray()) - { - return response.asArray(); - } else - { - throw new WalletCallException("Unexpected non-array response from wallet: " + response.toString()); - } - } - - - private JsonValue executeCommandAndGetJsonValue(String command1, String command2) - throws WalletCallException, IOException, InterruptedException - { - return this.executeCommandAndGetJsonValue(command1, command2, null); - } - - - private JsonValue executeCommandAndGetJsonValue(String command1, String command2, String command3) - throws WalletCallException, IOException, InterruptedException - { - String strResponse = this.executeCommandAndGetSingleStringResponse(command1, command2, command3); - - JsonValue response = null; - try - { - response = Json.parse(strResponse); - } catch (ParseException pe) - { - throw new WalletCallException(strResponse + "\n" + pe.getMessage() + "\n", pe); - } - - return response; - } - - - private String executeCommandAndGetSingleStringResponse(String command1) - throws WalletCallException, IOException, InterruptedException - { - return this.executeCommandAndGetSingleStringResponse(command1, null); - } - - - private String executeCommandAndGetSingleStringResponse(String command1, String command2) - throws WalletCallException, IOException, InterruptedException - { - return this.executeCommandAndGetSingleStringResponse(command1, command2, null); - } - - - private String executeCommandAndGetSingleStringResponse(String command1, String command2, String command3) - throws WalletCallException, IOException, InterruptedException - { - return executeCommandAndGetSingleStringResponse(command1, command2, command3, null); - } - - - private String executeCommandAndGetSingleStringResponse( - String command1, String command2, String command3, String command4) - throws WalletCallException, IOException, InterruptedException - { - String[] params; - if (command4 != null) - { - params = new String[] { this.zcashcli.getCanonicalPath(), command1, command2, command3, command4 }; - } else if (command3 != null) - { - params = new String[] { this.zcashcli.getCanonicalPath(), command1, command2, command3 }; - } else if (command2 != null) - { - params = new String[] { this.zcashcli.getCanonicalPath(), command1, command2 }; - } else - { - params = new String[] { this.zcashcli.getCanonicalPath(), command1 }; - } - - CommandExecutor caller = new CommandExecutor(params); - - String strResponse = caller.execute(); - if (strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error:") || - strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) - { - throw new WalletCallException("Error response from wallet: " + strResponse); - } - - return strResponse; - } - - - // Used to wrap string parameters on the command line - not doing so causes problems on Windows. - public static String wrapStringParameter(String param) - { - OS_TYPE os = OSUtil.getOSType(); - - // Fix is made for Windows only - if (os == OS_TYPE.WINDOWS) - { - param = "\"" + param.replace("\"", "\\\"") + "\""; - } - - return param; - } - - - private void decomposeJSONValue(String name, JsonValue val, Map map) - { - if (val.isObject()) - { - JsonObject obj = val.asObject(); - for (String memberName : obj.names()) - { - this.decomposeJSONValue(name + "." + memberName, obj.get(memberName), map); - } - } else if (val.isArray()) - { - JsonArray arr = val.asArray(); - for (int i = 0; i < arr.size(); i++) - { - this.decomposeJSONValue(name + "[" + i + "]", arr.get(i), map); - } - } else - { - map.put(name, val.toString()); - } - } + return response; + } + + private String executeCommandAndGetSingleStringResponse(String command1) + throws WalletCallException, IOException, InterruptedException { + return this.executeCommandAndGetSingleStringResponse(command1, null); + } + + private String executeCommandAndGetSingleStringResponse(String command1, + String command2) + throws WalletCallException, IOException, InterruptedException { + return this.executeCommandAndGetSingleStringResponse(command1, command2, + null); + } + + private String executeCommandAndGetSingleStringResponse(String command1, + String command2, + String command3) + throws WalletCallException, IOException, InterruptedException { + return executeCommandAndGetSingleStringResponse(command1, command2, + command3, null); + } + + private String + executeCommandAndGetSingleStringResponse(String command1, String command2, + String command3, String command4) + throws WalletCallException, IOException, InterruptedException { + String[] params; + if (command4 != null) { + params = new String[] {this.zcashcli.getCanonicalPath(), command1, + command2, command3, command4}; + } else if (command3 != null) { + params = new String[] {this.zcashcli.getCanonicalPath(), command1, + command2, command3}; + } else if (command2 != null) { + params = + new String[] {this.zcashcli.getCanonicalPath(), command1, command2}; + } else { + params = new String[] {this.zcashcli.getCanonicalPath(), command1}; + } + + CommandExecutor caller = new CommandExecutor(params); + + String strResponse = caller.execute(); + if (strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error:") || + strResponse.trim().toLowerCase(Locale.ROOT).startsWith("error code:")) { + throw new WalletCallException("Error response from wallet: " + + strResponse); + } + + return strResponse; + } + + // Used to wrap string parameters on the command line - not doing so causes + // problems on Windows. + public static String wrapStringParameter(String param) { + OS_TYPE os = OSUtil.getOSType(); + + // Fix is made for Windows only + if (os == OS_TYPE.WINDOWS) { + param = "\"" + param.replace("\"", "\\\"") + "\""; + } + + return param; + } + + private void decomposeJSONValue(String name, JsonValue val, + Map map) { + if (val.isObject()) { + JsonObject obj = val.asObject(); + for (String memberName : obj.names()) { + this.decomposeJSONValue(name + "." + memberName, obj.get(memberName), + map); + } + } else if (val.isArray()) { + JsonArray arr = val.asArray(); + for (int i = 0; i < arr.size(); i++) { + this.decomposeJSONValue(name + "[" + i + "]", arr.get(i), map); + } + } else { + map.put(name, val.toString()); + } + } } diff --git a/src/java/com/vaklinov/zcashui/ZCashUI.java b/src/java/com/vaklinov/zcashui/ZCashUI.java index 4fdfeddc..3ac3b3e2 100644 --- a/src/java/com/vaklinov/zcashui/ZCashUI.java +++ b/src/java/com/vaklinov/zcashui/ZCashUI.java @@ -1,11 +1,14 @@ /************************************************************************************************ - * ____________ _ _ _____ _ _____ _ _ _______ __ _ _ _ - * |___ / ____| \ | |/ ____| | | / ____| | | |_ _\ \ / / | | | | | - * / /| |__ | \| | | __ _ ___| |__ | | __| | | | | | \ \ /\ / /_ _| | | ___| |_ - * / / | __| | . ` | | / _` / __| '_ \| | |_ | | | | | | \ \/ \/ / _` | | |/ _ \ __| - * / /__| |____| |\ | |___| (_| \__ \ | | | |__| | |__| |_| |_ \ /\ / (_| | | | __/ |_ - * /_____|______|_| \_|\_____\__,_|___/_| |_|\_____|\____/|_____| \/ \/ \__,_|_|_|\___|\__| - * + * ____________ _ _ _____ _ _____ _ _ _______ __ + *_ _ _ + * |___ / ____| \ | |/ ____| | | / ____| | | |_ _\ \ / / + *| | | | | / /| |__ | \| | | __ _ ___| |__ | | __| | | | | | \ \ + * /\ / /_ _| | | ___| |_ / / | __| | . ` | | / _` / __| '_ \| | |_ | | | + *| | | \ \/ \/ / _` | | |/ _ \ __| / /__| |____| |\ | |___| (_| \__ \ | | | + *|__| | |__| |_| |_ \ /\ / (_| | | | __/ |_ + * /_____|______|_| \_|\_____\__,_|___/_| |_|\_____|\____/|_____| \/ \/ + *\__,_|_|_|\___|\__| + * * Copyright (c) 2016-2018 The ZEN Developers * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -28,6 +31,23 @@ **********************************************************************************/ package com.vaklinov.zcashui; +import com.cabecinha84.zelcashui.AppLock; +import com.cabecinha84.zelcashui.ZelCashJFrame; +import com.cabecinha84.zelcashui.ZelCashJMenu; +import com.cabecinha84.zelcashui.ZelCashJMenuBar; +import com.cabecinha84.zelcashui.ZelCashJMenuItem; +import com.cabecinha84.zelcashui.ZelCashJTabbedPane; +import com.cabecinha84.zelcashui.ZelCashUI; +import com.cabecinha84.zelcashui.ZelCashUIEditDialog; +import com.cabecinha84.zelcashui.ZelCashZelNodeDialog; +import com.cabecinha84.zelcashui.ZelNodesPanel; +import com.vaklinov.zcashui.OSUtil.OS_TYPE; +import com.vaklinov.zcashui.ZCashClientCaller.NetworkAndBlockchainInfo; +import com.vaklinov.zcashui.ZCashClientCaller.WalletCallException; +import com.vaklinov.zcashui.ZCashInstallationObserver.DAEMON_STATUS; +import com.vaklinov.zcashui.ZCashInstallationObserver.DaemonInfo; +import com.vaklinov.zcashui.ZCashInstallationObserver.InstallationDetectionException; +import com.vaklinov.zcashui.msg.MessagingPanel; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; @@ -57,7 +77,6 @@ import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JOptionPane; @@ -68,969 +87,1098 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import com.cabecinha84.zelcashui.AppLock; -import com.cabecinha84.zelcashui.ZelCashJFrame; -import com.cabecinha84.zelcashui.ZelCashJMenu; -import com.cabecinha84.zelcashui.ZelCashJMenuBar; -import com.cabecinha84.zelcashui.ZelCashJMenuItem; -import com.cabecinha84.zelcashui.ZelCashJTabbedPane; -import com.cabecinha84.zelcashui.ZelCashZelNodeDialog; -import com.cabecinha84.zelcashui.ZelCashUI; -import com.cabecinha84.zelcashui.ZelCashUIEditDialog; -import com.cabecinha84.zelcashui.ZelNodesPanel; -import com.vaklinov.zcashui.OSUtil.OS_TYPE; -import com.vaklinov.zcashui.ZCashClientCaller.NetworkAndBlockchainInfo; -import com.vaklinov.zcashui.ZCashClientCaller.WalletCallException; -import com.vaklinov.zcashui.ZCashInstallationObserver.DAEMON_STATUS; -import com.vaklinov.zcashui.ZCashInstallationObserver.DaemonInfo; -import com.vaklinov.zcashui.ZCashInstallationObserver.InstallationDetectionException; -import com.vaklinov.zcashui.msg.MessagingPanel; - /** * Main ZelCash Window. */ public class ZCashUI extends ZelCashJFrame { - public static final long THREAD_WAIT_1_SECOND = 1000; - public static final long THREAD_WAIT_5_SECONDS = 5000; - private static final String REGEXIPV4IPV6 = "/((^\\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\\s*$)|(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$))/"; - private ZCashInstallationObserver installationObserver; - private ZCashClientCaller clientCaller; - private LabelStorage labelStorage; - private StatusUpdateErrorReporter errorReporter; - - private WalletOperations walletOps; - - private ZelCashJMenuItem menuItemExit; - private ZelCashJMenuItem menuItemAbout; - private ZelCashJMenuItem menuItemZelcashUI; - private ZelCashJMenuItem menuItemEncrypt; - private ZelCashJMenuItem menuItemChangePassword; - private ZelCashJMenuItem menuItemBackup; - private ZelCashJMenuItem menuItemReindex; - private ZelCashJMenuItem menuItemRescan; - private ZelCashJMenuItem menuItemSproutToSapling; - private ZelCashJMenuItem menuItemExportKeys; - private ZelCashJMenuItem menuItemImportKeys; - private ZelCashJMenuItem menuItemShowPrivateKey; - private ZelCashJMenuItem menuItemImportOnePrivateKey; - private ZelCashJMenuItem menuItemOwnIdentity; - private ZelCashJMenuItem menuItemExportOwnIdentity; - private ZelCashJMenuItem menuItemImportContactIdentity; - private ZelCashJMenuItem menuItemAddMessagingGroup; - private ZelCashJMenuItem menuItemRemoveContactIdentity; - private ZelCashJMenuItem menuItemMessagingOptions; - private ZelCashJMenuItem menuItemShareFileViaIPFS; - private ZelCashJMenuItem menuItemExportToArizen; - private ZelCashJMenuItem menuItemNewZelnode; - - public DashboardPanel dashboard; - public TransactionsDetailPanel transactionDetailsPanel; - public AddressesPanel addresses; - public SendCashPanel sendPanel; - public AddressBookPanel addressBookPanel; - public MessagingPanel messagingPanel; - public ZelNodesPanel zelNodesPanel; - - private LanguageUtil langUtil; - - private static File walletLock; - private static FileChannel channel; - private static FileLock lock; - - ZelCashJTabbedPane tabs; - - public ZCashUI(StartupProgressDialog progressDialog) throws IOException, InterruptedException, WalletCallException { - langUtil = LanguageUtil.instance(); - - this.setTitle(langUtil.getString("main.frame.title")); - - if (progressDialog != null) { - progressDialog.setProgressText(langUtil.getString("main.frame.progressbar")); - } - - ClassLoader cl = this.getClass().getClassLoader(); - - this.setIconImage(new ImageIcon(cl.getResource("images/ZelCash-yellow.orange-logo.png")).getImage()); - - Container contentPane = this.getContentPane(); - contentPane.setBackground(ZelCashUI.container); - errorReporter = new StatusUpdateErrorReporter(this); - installationObserver = new ZCashInstallationObserver(OSUtil.getProgramDirectory()); - clientCaller = new ZCashClientCaller(OSUtil.getProgramDirectory()); - - if (installationObserver.isOnTestNet()) { - this.setTitle(this.getTitle() + langUtil.getString("main.frame.title.testnet")); - } - - // Build content - tabs = new ZelCashJTabbedPane(); - Font oldTabFont = tabs.getFont(); - Font newTabFont = new Font(oldTabFont.getName(), Font.BOLD | Font.ITALIC, oldTabFont.getSize() * 57 / 50); - tabs.setFont(newTabFont); - BackupTracker backupTracker = new BackupTracker(this); - labelStorage = new LabelStorage(); - tabs.addTab(langUtil.getString("main.frame.tab.overview.title"), - new ImageIcon(cl.getResource("zelcashImages/overview.png")), dashboard = new DashboardPanel(this, - installationObserver, clientCaller, errorReporter, backupTracker, labelStorage)); - tabs.addTab(langUtil.getString("main.frame.tab.transactions.title"), - new ImageIcon(cl.getResource("zelcashImages/transactions.png")), - transactionDetailsPanel = new TransactionsDetailPanel(this, tabs, installationObserver, clientCaller, - errorReporter, dashboard.getTransactionGatheringThread(), labelStorage)); - this.dashboard.setDetailsPanelForSelection(this.transactionDetailsPanel); - tabs.addTab(langUtil.getString("main.frame.tab.own.address.title"), - new ImageIcon(cl.getResource("zelcashImages/own-addresses.png")), - addresses = new AddressesPanel(this, clientCaller, errorReporter, labelStorage, installationObserver)); - tabs.addTab(langUtil.getString("main.frame.tab.send.cash.title"), - new ImageIcon(cl.getResource("zelcashImages/send.png")), - sendPanel = new SendCashPanel(clientCaller, errorReporter, installationObserver, backupTracker)); - tabs.addTab(langUtil.getString("main.frame.tab.address.book.title"), - new ImageIcon(cl.getResource("zelcashImages/address-book.png")), - addressBookPanel = new AddressBookPanel(sendPanel, tabs, labelStorage)); - tabs.addTab(langUtil.getString("main.frame.tab.messaging.title"), - new ImageIcon(cl.getResource("zelcashImages/messaging.png")), - messagingPanel = new MessagingPanel(this, sendPanel, tabs, clientCaller, errorReporter, labelStorage)); - tabs.addTab(langUtil.getString("main.frame.tab.zelnodes.title"), - new ImageIcon(cl.getResource("zelcashImages/zelNodes.png")), - zelNodesPanel = new ZelNodesPanel(this, tabs, clientCaller, errorReporter, labelStorage)); - contentPane.add(tabs); - - this.walletOps = new WalletOperations(this, tabs, dashboard, addresses, sendPanel, installationObserver, - clientCaller, errorReporter, backupTracker, labelStorage); - - // Build menu - ZelCashJMenuBar mb = new ZelCashJMenuBar(); - ZelCashJMenu file = new ZelCashJMenu(langUtil.getString("menu.label.main")); - file.setMnemonic(KeyEvent.VK_M); - int accelaratorKeyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); - file.add(menuItemZelcashUI = new ZelCashJMenuItem(langUtil.getString("menu.label.zelcashui"), KeyEvent.VK_U)); - menuItemZelcashUI.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U, accelaratorKeyMask)); - file.add(menuItemAbout = new ZelCashJMenuItem(langUtil.getString("menu.label.about"), KeyEvent.VK_T)); - menuItemAbout.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, accelaratorKeyMask)); - file.addSeparator(); - file.add(menuItemExit = new ZelCashJMenuItem(langUtil.getString("menu.label.quit"), KeyEvent.VK_Q)); - menuItemExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, accelaratorKeyMask)); - mb.add(file); - - ZelCashJMenu wallet = new ZelCashJMenu(langUtil.getString("menu.label.wallet")); - wallet.setMnemonic(KeyEvent.VK_W); - wallet.add(menuItemBackup = new ZelCashJMenuItem(langUtil.getString("menu.label.backup"), KeyEvent.VK_B)); - menuItemBackup.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, accelaratorKeyMask)); - wallet.add(menuItemEncrypt = new - ZelCashJMenuItem(langUtil.getString("menu.label.encrypt"), KeyEvent.VK_E)); - menuItemEncrypt.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, - accelaratorKeyMask)); - wallet.add(menuItemChangePassword = new - ZelCashJMenuItem(langUtil.getString("menu.label.changepassword"), KeyEvent.VK_J)); - menuItemChangePassword.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_J, - accelaratorKeyMask)); - wallet.add(menuItemExportKeys = new ZelCashJMenuItem(langUtil.getString("menu.label.export.private.keys"), - KeyEvent.VK_K)); - menuItemExportKeys.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_K, accelaratorKeyMask)); - wallet.add(menuItemImportKeys = new ZelCashJMenuItem(langUtil.getString("menu.label.import.private.keys"), - KeyEvent.VK_I)); - menuItemImportKeys.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, accelaratorKeyMask)); - wallet.add(menuItemShowPrivateKey = new ZelCashJMenuItem(langUtil.getString("menu.label.show.private.key"), - KeyEvent.VK_P)); - menuItemShowPrivateKey.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, accelaratorKeyMask)); - wallet.add(menuItemImportOnePrivateKey = new ZelCashJMenuItem( - langUtil.getString("menu.label.import.one.private.key"), KeyEvent.VK_N)); - menuItemImportOnePrivateKey.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, accelaratorKeyMask)); - wallet.add(menuItemReindex = new ZelCashJMenuItem(langUtil.getString("menu.label.reindex"), KeyEvent.VK_1)); - menuItemReindex.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, accelaratorKeyMask)); - wallet.add(menuItemRescan = new ZelCashJMenuItem(langUtil.getString("menu.label.rescan"), KeyEvent.VK_2)); - menuItemRescan.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, accelaratorKeyMask)); - wallet.add(menuItemSproutToSapling = new ZelCashJMenuItem(langUtil.getString("menu.label.sprouttosapling"), KeyEvent.VK_3)); - menuItemSproutToSapling.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_3, accelaratorKeyMask)); - // wallet.add(menuItemExportToArizen = new - // ZelCashJMenuItem(langUtil.getString("menu.label.export.to.arizen"), - // KeyEvent.VK_A)); - // menuItemExportToArizen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, - // accelaratorKeyMask)); - mb.add(wallet); - - ZelCashJMenu messaging = new ZelCashJMenu(langUtil.getString("menu.label.messaging")); - messaging.setMnemonic(KeyEvent.VK_S); - messaging.add(menuItemOwnIdentity = new ZelCashJMenuItem(langUtil.getString("menu.label.own.identity"), - KeyEvent.VK_D)); - menuItemOwnIdentity.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, accelaratorKeyMask)); - messaging.add(menuItemExportOwnIdentity = new ZelCashJMenuItem( - langUtil.getString("menu.label.export.own.identity"), KeyEvent.VK_L)); - menuItemExportOwnIdentity.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L, accelaratorKeyMask)); - messaging.add(menuItemAddMessagingGroup = new ZelCashJMenuItem( - langUtil.getString("menu.label.add.messaging.group"), KeyEvent.VK_G)); - menuItemAddMessagingGroup.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_G, accelaratorKeyMask)); - messaging.add(menuItemImportContactIdentity = new ZelCashJMenuItem( - langUtil.getString("menu.label.import.contact.identity"), KeyEvent.VK_Y)); - menuItemImportContactIdentity.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, accelaratorKeyMask)); - messaging.add(menuItemRemoveContactIdentity = new ZelCashJMenuItem( - langUtil.getString("menu.label.remove.contact"), KeyEvent.VK_R)); - menuItemRemoveContactIdentity.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, accelaratorKeyMask)); - messaging.add(menuItemMessagingOptions = new ZelCashJMenuItem(langUtil.getString("menu.label.options"), - KeyEvent.VK_O)); - menuItemMessagingOptions.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, accelaratorKeyMask)); - - ZelCashJMenu shareFileVia = new ZelCashJMenu(langUtil.getString("menu.label.share.file")); - shareFileVia.setMnemonic(KeyEvent.VK_V); - // TODO: uncomment this for IPFS integration - // messaging.add(shareFileVia); - shareFileVia.add( - menuItemShareFileViaIPFS = new ZelCashJMenuItem(langUtil.getString("menu.label.ipfs"), KeyEvent.VK_F)); - menuItemShareFileViaIPFS.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, accelaratorKeyMask)); - - mb.add(messaging); - - ZelCashJMenu zelNodes = new ZelCashJMenu(langUtil.getString("menu.label.zelnodes")); - zelNodes.setMnemonic(KeyEvent.VK_Z); - zelNodes.add(menuItemNewZelnode = new ZelCashJMenuItem(langUtil.getString("menu.label.zelnodes.new"), - KeyEvent.VK_Z)); - menuItemNewZelnode.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, accelaratorKeyMask)); - - mb.add(zelNodes); - - ActionListener languageSelectionAction = new ActionListener() { - public void actionPerformed(ActionEvent e) { - try { - Log.info("Action [" + e.getActionCommand() + "] performed"); - LanguageMenuItem item = (LanguageMenuItem) e.getSource(); - langUtil.updatePreferredLanguage(item.getLocale()); - JOptionPane.showMessageDialog(ZCashUI.this.getRootPane().getParent(), - langUtil.getString("dialog.message.language.prefs.update"), - langUtil.getString("dialog.message.language.prefs.update.title"), - JOptionPane.INFORMATION_MESSAGE); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - }; - ZelCashJMenu languageMenu = new ZelCashJMenu(langUtil.getString("menu.label.language")); - // only english translation available - /* - * LanguageMenuItem italian = new - * LanguageMenuItem(langUtil.getString("menu.label.language.italian"), new - * ImageIcon(cl.getResource("images/italian.png")), Locale.ITALY); - * italian.setHorizontalTextPosition(ZelCashJMenuItem.RIGHT); - * - * italian.addActionListener(languageSelectionAction); - */ - - LanguageMenuItem english = new LanguageMenuItem(langUtil.getString("menu.label.language.english"), - new ImageIcon(cl.getResource("images/uk.png")), Locale.US); - english.setHorizontalTextPosition(ZelCashJMenuItem.RIGHT); - - english.addActionListener(languageSelectionAction); - - ButtonGroup group = new ButtonGroup(); - // group.add(italian); - group.add(english); - - // languageMenu.add(italian); - languageMenu.add(english); - - // Temporarily disabled till translations are completed - // mb.add(languageMenu); - - this.setJMenuBar(mb); - - // Add listeners etc. - menuItemExit.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.exitProgram(); - } - }); - - menuItemAbout.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - try { - AboutDialog ad = new AboutDialog(ZCashUI.this); - ad.setVisible(true); - } catch (UnsupportedEncodingException uee) { - Log.error("Unexpected error: ", uee); - ZCashUI.this.errorReporter.reportError(uee); - } - } - }); - - menuItemZelcashUI.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - try { - ZelCashUIEditDialog ad = new ZelCashUIEditDialog(ZCashUI.this); - ad.setVisible(true); - } catch (UnsupportedEncodingException uee) { - Log.error("Unexpected error: ", uee); - ZCashUI.this.errorReporter.reportError(uee); - } - } - }); - - menuItemNewZelnode.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - try { - ZelCashZelNodeDialog ad = new ZelCashZelNodeDialog(ZCashUI.this, clientCaller, installationObserver, null, labelStorage); - ad.setVisible(true); - } catch (Exception uee) { - Log.error("Unexpected error: ", uee); - ZCashUI.this.errorReporter.reportError(uee); - } - } - }); - - menuItemBackup.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.walletOps.backupWallet(); - } - }); - - - menuItemEncrypt.addActionListener( new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.walletOps.encryptWallet(); } } ); - - menuItemChangePassword.addActionListener( new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.walletOps.changeWalletPassword(); } } ); - - menuItemExportKeys.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.walletOps.exportWalletPrivateKeys(); - } - }); - - menuItemImportKeys.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.walletOps.importWalletPrivateKeys(); - } - }); - - menuItemShowPrivateKey.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.walletOps.showPrivateKey(); - } - }); - - menuItemImportOnePrivateKey.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.walletOps.importSinglePrivateKey(); - } - }); - - - menuItemReindex.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.walletOps.reindexWallet(); - } - }); - - menuItemRescan.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.walletOps.rescanWallet(); - } - }); - - menuItemSproutToSapling.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.walletOps.sproutToSaplingMigrationTool(); - } - }); - - menuItemOwnIdentity.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.messagingPanel.openOwnIdentityDialog(); - } - }); - - menuItemExportOwnIdentity.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.messagingPanel.exportOwnIdentity(); - } - }); - - menuItemImportContactIdentity.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.messagingPanel.importContactIdentity(); - } - }); - - menuItemAddMessagingGroup.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.messagingPanel.addMessagingGroup(); - } - }); - - menuItemRemoveContactIdentity.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.messagingPanel.removeSelectedContact(); - } - }); - - menuItemMessagingOptions.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.messagingPanel.openOptionsDialog(); - } - }); - - menuItemShareFileViaIPFS.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - ZCashUI.this.messagingPanel.shareFileViaIPFS(); - } - }); - /* - * menuItemExportToArizen.addActionListener( new ActionListener() { - * - * @Override public void actionPerformed(ActionEvent e) { - * ZCashUI.this.walletOps.exportToArizenWallet(); } } ); - */ - // Close operation - this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); - this.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent e) { - ZCashUI.this.exitProgram(); - } - }); - - // Show initial message - SwingUtilities.invokeLater(new Runnable() { - public void run() { - try { - String userDir = OSUtil.getSettingsDirectory(); - File warningFlagFile = new File(userDir + File.separator + "initialInfoShown_0.82.flag"); - if (warningFlagFile.exists()) { - return; - } - ; - - Object[] options = { langUtil.getString("main.frame.disclaimer.button.agree"), - langUtil.getString("main.frame.disclaimer.button.disagree") }; - - int option = JOptionPane.showOptionDialog(ZCashUI.this.getRootPane().getParent(), - langUtil.getString("main.frame.disclaimer.text"), - langUtil.getString("main.frame.disclaimer.title"), JOptionPane.DEFAULT_OPTION, - JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); - - if (option == 0) { - warningFlagFile.createNewFile(); - } else { - ZCashUI.this.exitProgram(); - } - - } catch (IOException ioe) { - /* TODO: report exceptions to the user */ - Log.error("Unexpected error: ", ioe); - } - } - }); - - // Finally dispose of the progress dialog - if (progressDialog != null) { - progressDialog.doDispose(); - } - - // Notify the messaging TAB that it is being selected - every time - tabs.addChangeListener(new ChangeListener() { - @Override - public void stateChanged(ChangeEvent e) { - ZelCashJTabbedPane tabs = (ZelCashJTabbedPane) e.getSource(); - if (tabs.getSelectedIndex() == 5) { - ZCashUI.this.messagingPanel.tabSelected(); - } - } - }); - - this.validate(); - this.repaint(); - - this.pack(); - Dimension currentSize = this.getSize(); - - OS_TYPE os = OSUtil.getOSType(); - int width = 1040; - if (os == OS_TYPE.MAC_OS) { - width += 100; // Needs to be wider on macOS - } - - this.setSize(new Dimension(width, currentSize.height)); - this.validate(); - this.repaint(); - } - - public void exitProgram() { - Log.info("Exiting ..."); - - this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - this.dashboard.stopThreadsAndTimers(); - this.transactionDetailsPanel.stopThreadsAndTimers(); - this.addresses.stopThreadsAndTimers(); - this.sendPanel.stopThreadsAndTimers(); - this.messagingPanel.stopThreadsAndTimers(); - - ZCashUI.this.setVisible(false); - ZCashUI.this.dispose(); - - System.exit(0); - } - - public void stopTimers() { - Log.info("stopTimers ..."); - - this.dashboard.stopThreadsAndTimers(); - this.transactionDetailsPanel.stopThreadsAndTimers(); - this.addresses.stopThreadsAndTimers(); - this.sendPanel.stopThreadsAndTimers(); - this.messagingPanel.stopThreadsAndTimers(); - - } - - /** - * if both are true only reindex will be executed. - * @param reindex - * @param rescan - */ - public void restartDaemon(boolean reindex, boolean rescan) { - Log.info("restartDaemon ..."); - - this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - try { - this.dashboard.stopThreadsAndTimers(); - this.transactionDetailsPanel.stopThreadsAndTimers(); - this.addresses.stopThreadsAndTimers(); - this.sendPanel.stopThreadsAndTimers(); - this.messagingPanel.stopThreadsAndTimers(); - Thread.sleep(ZCashUI.THREAD_WAIT_1_SECOND); - - this.clientCaller.stopDaemon(); - ZCashInstallationObserver initialInstallationObserver; - DaemonInfo zcashdInfo; - for (int i = 0; i < 10; ++i) { - Log.info("Check if Daemon is stopped"); - initialInstallationObserver = new ZCashInstallationObserver(OSUtil.getProgramDirectory()); - zcashdInfo = initialInstallationObserver.getDaemonInfo(); - initialInstallationObserver = null; - if (zcashdInfo.status != DAEMON_STATUS.RUNNING) { - Log.info("Daemon stopped."); - break; - } - Thread.sleep(ZCashUI.THREAD_WAIT_1_SECOND); - } - this.clientCaller.startDaemon(reindex, rescan); - for (int i = 0; i < 10; ++i) { - Log.info("Check if Daemon is running"); - initialInstallationObserver = new ZCashInstallationObserver(OSUtil.getProgramDirectory()); - zcashdInfo = initialInstallationObserver.getDaemonInfo(); - initialInstallationObserver = null; - if (zcashdInfo.status == DAEMON_STATUS.RUNNING) { - Log.info("Daemon running."); - break; - } - Thread.sleep(ZCashUI.THREAD_WAIT_1_SECOND); - } - for (int i = 0; i < 30; ++i) { - Log.info("Checking if Daemon is ready for gui wallet."); - try { - clientCaller.getNetworkAndBlockchainInfo(); - Log.info("Daemon is ready."); - } - catch(Exception e) { - Log.info("Daemon not ready."); - Thread.sleep(ZCashUI.THREAD_WAIT_1_SECOND); - } - } - Log.info("restartDaemon finished ..."); - } - catch (Exception e) { - Log.error("Error on restartDaemon: "+e.getMessage()); - } - - } - - public static void main(String argv[]) throws IOException { - ZCashUI ui = null; - StartupProgressDialog startupBar = null; - ZCashClientCaller initialClientCaller = null; - ZCashInstallationObserver initialInstallationObserver = null; - DaemonInfo zcashdInfo = null; - boolean reindex = false; - for (int i = 0; i < argv.length; i++) { - if("reindex".equals(argv[i])) { - reindex = true; - } - } - try { - new ZelCashUI(); - OS_TYPE os = OSUtil.getOSType(); - - if ((os == OS_TYPE.WINDOWS) || (os == OS_TYPE.MAC_OS) || (os == OS_TYPE.LINUX)) { - possiblyCreateZelCashConfigFile(); - } - - LanguageUtil langUtil = LanguageUtil.instance(); - - Log.info("Starting ZelCash Swing Wallet ..."); - Log.info("OS: " + System.getProperty("os.name") + " = " + os); - Log.info("Current directory: " + new File(".").getCanonicalPath()); - Log.info("Class path: " + System.getProperty("java.class.path")); - Log.info("Environment PATH: " + System.getenv("PATH")); - - // Look and feel settings - a custom OS-look and feel is set for Windows - if (os == OS_TYPE.WINDOWS) { - // Custom Windows L&F and font settings - UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); - - // This font looks good but on Windows 7 it misses some chars like the stars... - // FontUIResource font = new FontUIResource("Lucida Sans Unicode", Font.PLAIN, - // 11); - // UIManager.put("Table.font", font); - } else if (os == OS_TYPE.MAC_OS) { - // The MacOS L&F is active by default - the property sets the menu bar Mac style - System.setProperty("apple.laf.useScreenMenuBar", "true"); - System.setProperty("com.apple.mrj.application.apple.menu.about.name", - LanguageUtil.instance().getString("apple.menu.about.name")); - } else { - for (LookAndFeelInfo lf : UIManager.getInstalledLookAndFeels()) { - Log.info("Available look and feel: " + lf.getName() + " " + lf.getClassName()); - if (lf.getName().equals("Nimbus")) { - Log.info("Setting look and feel: {0}", lf.getClassName()); - UIManager.setLookAndFeel(lf.getClassName()); - break; - } - ; - } - } - - Log.info("Checking if zelnodes.conf exists and is properly set..."); - String blockchainDir = OSUtil.getBlockchainDirectory(); - File zelnodeConf = new File(blockchainDir + File.separator + "zelnode.conf"); - if (!zelnodeConf.exists()) - { - Log.info("Could not find file: {0} !", zelnodeConf.getAbsolutePath()); - } - else { - BufferedReader br = new BufferedReader(new FileReader(zelnodeConf)); - String emptyLine; - String st; - String[] zelNodeInfo; - while ((st = br.readLine()) != null) { - emptyLine = st.replaceAll(" ", "").replaceAll("(?m)^\\\\s*\\\\r?\\\\n|\\\\r?\\\\n\\\\s*(?!.*\\\\r?\\\\n)", ""); - if(st.startsWith("#") || emptyLine.equals("")) { - continue; - } - else { - zelNodeInfo = st.split("\\s+"); - if(zelNodeInfo.length != 5) { - Log.error("Zelnode.conf file not ok. One of the lines doesn`t have 5 strings."); - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("parsing.error.zelnodesconf.wrong.number"), - LanguageUtil.instance().getString("parsing.error.zelnodesconf.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(1); - } - else { - - if(!zelNodeInfo[1].endsWith(":16125") && !zelNodeInfo[1].endsWith(":26125")) { - Log.error("Zelnode.conf file not ok. ip not ending with correct port: "+zelNodeInfo[1]); - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("parsing.error.zelnodesconf.wrong.port", zelNodeInfo[1]), - LanguageUtil.instance().getString("parsing.error.zelnodesconf.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(1); - } - - String ip = zelNodeInfo[1].replaceAll(":16125", "").replaceAll(":26125", ""); - if(ip.isEmpty()) { - JOptionPane.showMessageDialog( - null, - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.ip.notset"), - LanguageUtil.instance().getString("dialog.zelcashnewzelnode.fields.error.adding.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(1); - } - else if(!ip.endsWith(".onion")) { - try { - Inet4Address address = (Inet4Address) Inet4Address.getByName(ip); - } - catch (Exception e) { - try { - Inet6Address address = (Inet6Address) Inet6Address.getByName(ip); - if(!ip.startsWith("[") || !ip.endsWith("]")) { - Log.error("Zelnode.conf file not ok. ip not valid for ipv4, ipv6 and onion:"+ip); - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("parsing.error.zelnodesconf.wrong.ip", ip), - LanguageUtil.instance().getString("parsing.error.zelnodesconf.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(1); - } - } - catch (Exception ex) { - Log.error("Zelnode.conf file not ok. ip not valid for ipv4, ipv6 and onion:"+ip); - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("parsing.error.zelnodesconf.wrong.ip", ip), - LanguageUtil.instance().getString("parsing.error.zelnodesconf.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(1); - } - } - } - - } - } - } - Log.info("zelnodes.conf exists and is properly set..."); - } - - // If zelcashd is currently not running, do a startup of the daemon as a child - // process - // It may be started but not ready - then also show dialog - initialInstallationObserver = new ZCashInstallationObserver(OSUtil.getProgramDirectory()); - zcashdInfo = initialInstallationObserver.getDaemonInfo(); - initialInstallationObserver = null; - - initialClientCaller = new ZCashClientCaller(OSUtil.getProgramDirectory()); - boolean daemonStartInProgress = false; - try { - if (zcashdInfo.status == DAEMON_STATUS.RUNNING) { - NetworkAndBlockchainInfo info = initialClientCaller.getNetworkAndBlockchainInfo(); - // If more than 20 minutes behind in the blockchain - startup in progress - if ((System.currentTimeMillis() - info.lastBlockDate.getTime()) > (20 * 60 * 1000)) { - Log.info( - "Current blockchain synchronization date is " + new Date(info.lastBlockDate.getTime())); - daemonStartInProgress = true; - } - } - } catch (WalletCallException wce) { - if ((wce.getMessage().indexOf("{\"code\":-28") != -1) || // Started but not ready - (wce.getMessage().indexOf("error code: -28") != -1)) { - Log.info("zelcashd is currently starting..."); - daemonStartInProgress = true; - } - } - if (false == AppLock.lock()) { - throw new Exception(LanguageUtil.instance().getString("duplicate.instante.detected")); - } - installShutdownHook(); - - if ((zcashdInfo.status != DAEMON_STATUS.RUNNING) || (daemonStartInProgress)) { - Log.info( - "zelcashd is not running at the moment or has not started/synchronized 100% - showing splash..."); - startupBar = new StartupProgressDialog(initialClientCaller); - startupBar.setVisible(true); - startupBar.waitForStartup(reindex); - } - initialClientCaller = null; - - // Main GUI is created here - ui = new ZCashUI(startupBar); - ui.setVisible(true); - - } catch (InstallationDetectionException ide) { - Log.error("Unexpected error: ", ide); - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("main.frame.option.pane.installation.error.text", - OSUtil.getProgramDirectory(), ide.getMessage()), - LanguageUtil.instance().getString("main.frame.option.pane.installation.error.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(1); - } catch (WalletCallException wce) { - Log.error("Unexpected error: ", wce); - - if ((wce.getMessage().indexOf("{\"code\":-28,\"message\"") != -1) - || (wce.getMessage().indexOf("error code: -28") != -1)) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.communication.error.text"), - LanguageUtil.instance().getString("main.frame.option.pane.wallet.communication.error.title"), - JOptionPane.ERROR_MESSAGE); - } else { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.communication.error.2.text", - wce.getMessage()), - LanguageUtil.instance().getString("main.frame.option.pane.wallet.communication.error.2.title"), - JOptionPane.ERROR_MESSAGE); - } - - System.exit(2); - } catch (Exception e) { - Log.error("Unexpected error: ", e); - if (e.getMessage().equals(LanguageUtil.instance().getString("duplicate.instante.detected"))) { - JOptionPane.showMessageDialog(null, e.getMessage(), - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(3); - } - else if(e.getMessage().contains("(code 1)")) { - Object[] options = { LanguageUtil.instance().getString("main.frame.reindex.button.agree"), - LanguageUtil.instance().getString("main.frame.reindex.button.disagree") }; - - int option = JOptionPane.showOptionDialog(null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.code1.text", - e.getMessage()), - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.title"), - JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); - if (option == 0) { - try { - - if (initialClientCaller == null) { - initialClientCaller = new ZCashClientCaller(OSUtil.getProgramDirectory()); - } - initialClientCaller.stopDaemon(); - - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("wallet.reindex.restart.message"), - LanguageUtil.instance().getString("wallet.reindex.restart.title"), - JOptionPane.INFORMATION_MESSAGE); - - if (startupBar != null) { - startupBar.dispose(); - } - for (int i = 0; i < 5; ++i) { - Log.info("Check if Daemon is stopped"); - initialInstallationObserver = new ZCashInstallationObserver(OSUtil.getProgramDirectory()); - zcashdInfo = initialInstallationObserver.getDaemonInfo(); - initialInstallationObserver = null; - if (zcashdInfo.status != DAEMON_STATUS.RUNNING) { - Log.info("Daemon stopped."); - break; - } - Thread.sleep(ZCashUI.THREAD_WAIT_1_SECOND); - } - Log.info("Restarting the wallet."); - String args[] = {"reindex"}; - ZCashUI.main(args); - } catch (Exception errr) { - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.2.text", - errr.getMessage()), - LanguageUtil.instance() - .getString("main.frame.option.pane.wallet.critical.error.2.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(5); - } catch (Error errX) { - // Last resort catch for unexpected problems - just to inform the user - errX.printStackTrace(); - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.2.text", - errX.getMessage()), - LanguageUtil.instance() - .getString("main.frame.option.pane.wallet.critical.error.2.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(6); - } - } else { - System.exit(3); - } - } - else { - Log.error("Unexpected error: ", e); - JOptionPane.showMessageDialog( - null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.text", e.getMessage()), - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(3); - } - - } catch (Error err) { - // Last resort catch for unexpected problems - just to inform the user - err.printStackTrace(); - JOptionPane.showMessageDialog(null, - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.2.text", - err.getMessage()), - LanguageUtil.instance().getString("main.frame.option.pane.wallet.critical.error.2.title"), - JOptionPane.ERROR_MESSAGE); - System.exit(4); - } - } - - public static void possiblyCreateZelCashConfigFile() throws IOException { - String blockchainDir = OSUtil.getBlockchainDirectory(); - File dir = new File(blockchainDir); - - if (!dir.exists()) { - if (!dir.mkdirs()) { - Log.error("ERROR: Could not create settings directory: " + dir.getCanonicalPath()); - throw new IOException("Could not create settings directory: " + dir.getCanonicalPath()); - } - } - - File zelcashConfigFile = new File(dir, "zelcash.conf"); - - if (!zelcashConfigFile.exists()) { - Log.info("ZelCash configuration file " + zelcashConfigFile.getCanonicalPath() - + " does not exist. It will be created with default settings."); - - Random r = new Random(System.currentTimeMillis()); - - PrintStream configOut = new PrintStream(new FileOutputStream(zelcashConfigFile)); - - configOut.println("#############################################################################"); - configOut.println("# ZelCash configuration file #"); - configOut.println("#############################################################################"); - configOut.println("# This file has been automatically generated by the ZelCash GUI wallet with #"); - configOut.println("# default settings. It may be further cutsomized by hand only. #"); - configOut.println("#############################################################################"); - configOut.println("# Creation date: " + new Date().toString()); - configOut.println("#############################################################################"); - configOut.println(""); - configOut.println("rpcallowip=127.0.0.1"); - configOut.println("server=1"); - configOut.println("daemon=1"); - configOut.println("txindex=1"); - configOut.println("logtimestamps=1"); - configOut.println("maxconnections=256"); - configOut.println("addnode=node.zel.cash"); - configOut.println("addnode=explorer.zel.cash"); - configOut.println("addnode=explorer2.zel.cash"); - configOut.println("addnode=explorer-asia.zel.cash"); - configOut.println("addnode=explorer.zelcash.online"); - configOut.println("addnode=explorer.zel.zelcore.io"); - configOut.println("# The rpcuser/rpcpassword are used for the local call to zelcashd"); - configOut.println("rpcuser=User" + Math.abs(r.nextInt())); - configOut.println("rpcpassword=Pass" + Math.abs(r.nextInt()) + "" + Math.abs(r.nextInt()) + "" - + Math.abs(r.nextInt())); - /* - * This is not necessary as of release: - * https://github.com/ZencashOfficial/zen/releases/tag/v2.0.9-3-b8d2ebf - * configOut. - * println("# Well-known nodes to connect to - to speed up acquiring initial connections" - * ); configOut.println("addnode=zpool.blockoperations.com"); - * configOut.println("addnode=luckpool.org:8333"); - * configOut.println("addnode=zencash.cloud"); - * configOut.println("addnode=zen.suprnova.cc"); - * configOut.println("addnode=zen.bitfire.one"); - * configOut.println("addnode=zenmine.pro"); - */ - - configOut.close(); - } - } - - private static void installShutdownHook() { - - Runnable runner = new Runnable() { - @Override - public void run() { - AppLock.unlock(); - } - }; - Runtime.getRuntime().addShutdownHook(new Thread(runner, "Window Prefs Hook")); - } + public static final long THREAD_WAIT_1_SECOND = 1000; + public static final long THREAD_WAIT_5_SECONDS = 5000; + private static final String REGEXIPV4IPV6 = + "/((^\\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\\s*$)|(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$))/"; + private ZCashInstallationObserver installationObserver; + private ZCashClientCaller clientCaller; + private LabelStorage labelStorage; + private StatusUpdateErrorReporter errorReporter; + + private WalletOperations walletOps; + + private ZelCashJMenuItem menuItemExit; + private ZelCashJMenuItem menuItemAbout; + private ZelCashJMenuItem menuItemZelcashUI; + private ZelCashJMenuItem menuItemEncrypt; + private ZelCashJMenuItem menuItemChangePassword; + private ZelCashJMenuItem menuItemBackup; + private ZelCashJMenuItem menuItemReindex; + private ZelCashJMenuItem menuItemRescan; + private ZelCashJMenuItem menuItemSproutToSapling; + private ZelCashJMenuItem menuItemExportKeys; + private ZelCashJMenuItem menuItemImportKeys; + private ZelCashJMenuItem menuItemShowPrivateKey; + private ZelCashJMenuItem menuItemImportOnePrivateKey; + private ZelCashJMenuItem menuItemOwnIdentity; + private ZelCashJMenuItem menuItemExportOwnIdentity; + private ZelCashJMenuItem menuItemImportContactIdentity; + private ZelCashJMenuItem menuItemAddMessagingGroup; + private ZelCashJMenuItem menuItemRemoveContactIdentity; + private ZelCashJMenuItem menuItemMessagingOptions; + private ZelCashJMenuItem menuItemShareFileViaIPFS; + private ZelCashJMenuItem menuItemExportToArizen; + private ZelCashJMenuItem menuItemNewZelnode; + + public DashboardPanel dashboard; + public TransactionsDetailPanel transactionDetailsPanel; + public AddressesPanel addresses; + public SendCashPanel sendPanel; + public AddressBookPanel addressBookPanel; + public MessagingPanel messagingPanel; + public ZelNodesPanel zelNodesPanel; + + private LanguageUtil langUtil; + + private static File walletLock; + private static FileChannel channel; + private static FileLock lock; + + ZelCashJTabbedPane tabs; + + public ZCashUI(StartupProgressDialog progressDialog) + throws IOException, InterruptedException, WalletCallException { + langUtil = LanguageUtil.instance(); + + this.setTitle(langUtil.getString("main.frame.title")); + + if (progressDialog != null) { + progressDialog.setProgressText( + langUtil.getString("main.frame.progressbar")); + } + + ClassLoader cl = this.getClass().getClassLoader(); + + this.setIconImage( + new ImageIcon(cl.getResource("images/ZelCash-yellow.orange-logo.png")) + .getImage()); + + Container contentPane = this.getContentPane(); + contentPane.setBackground(ZelCashUI.container); + errorReporter = new StatusUpdateErrorReporter(this); + installationObserver = + new ZCashInstallationObserver(OSUtil.getProgramDirectory()); + clientCaller = new ZCashClientCaller(OSUtil.getProgramDirectory()); + + if (installationObserver.isOnTestNet()) { + this.setTitle(this.getTitle() + + langUtil.getString("main.frame.title.testnet")); + } + + // Build content + tabs = new ZelCashJTabbedPane(); + Font oldTabFont = tabs.getFont(); + Font newTabFont = new Font(oldTabFont.getName(), Font.BOLD | Font.ITALIC, + oldTabFont.getSize() * 57 / 50); + tabs.setFont(newTabFont); + BackupTracker backupTracker = new BackupTracker(this); + labelStorage = new LabelStorage(); + tabs.addTab(langUtil.getString("main.frame.tab.overview.title"), + new ImageIcon(cl.getResource("zelcashImages/overview.png")), + dashboard = new DashboardPanel(this, installationObserver, + clientCaller, errorReporter, + backupTracker, labelStorage)); + tabs.addTab(langUtil.getString("main.frame.tab.transactions.title"), + new ImageIcon(cl.getResource("zelcashImages/transactions.png")), + transactionDetailsPanel = new TransactionsDetailPanel( + this, tabs, installationObserver, clientCaller, + errorReporter, dashboard.getTransactionGatheringThread(), + labelStorage)); + this.dashboard.setDetailsPanelForSelection(this.transactionDetailsPanel); + tabs.addTab( + langUtil.getString("main.frame.tab.own.address.title"), + new ImageIcon(cl.getResource("zelcashImages/own-addresses.png")), + addresses = new AddressesPanel(this, clientCaller, errorReporter, + labelStorage, installationObserver)); + tabs.addTab(langUtil.getString("main.frame.tab.send.cash.title"), + new ImageIcon(cl.getResource("zelcashImages/send.png")), + sendPanel = + new SendCashPanel(clientCaller, errorReporter, + installationObserver, backupTracker)); + tabs.addTab(langUtil.getString("main.frame.tab.address.book.title"), + new ImageIcon(cl.getResource("zelcashImages/address-book.png")), + addressBookPanel = + new AddressBookPanel(sendPanel, tabs, labelStorage)); + tabs.addTab(langUtil.getString("main.frame.tab.messaging.title"), + new ImageIcon(cl.getResource("zelcashImages/messaging.png")), + messagingPanel = + new MessagingPanel(this, sendPanel, tabs, clientCaller, + errorReporter, labelStorage)); + tabs.addTab(langUtil.getString("main.frame.tab.zelnodes.title"), + new ImageIcon(cl.getResource("zelcashImages/zelNodes.png")), + zelNodesPanel = new ZelNodesPanel(this, tabs, clientCaller, + errorReporter, labelStorage)); + contentPane.add(tabs); + + this.walletOps = new WalletOperations( + this, tabs, dashboard, addresses, sendPanel, installationObserver, + clientCaller, errorReporter, backupTracker, labelStorage); + + // Build menu + ZelCashJMenuBar mb = new ZelCashJMenuBar(); + ZelCashJMenu file = new ZelCashJMenu(langUtil.getString("menu.label.main")); + file.setMnemonic(KeyEvent.VK_M); + int accelaratorKeyMask = + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + file.add(menuItemZelcashUI = new ZelCashJMenuItem( + langUtil.getString("menu.label.zelcashui"), KeyEvent.VK_U)); + menuItemZelcashUI.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_U, accelaratorKeyMask)); + file.add(menuItemAbout = new ZelCashJMenuItem( + langUtil.getString("menu.label.about"), KeyEvent.VK_T)); + menuItemAbout.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_T, accelaratorKeyMask)); + file.addSeparator(); + file.add(menuItemExit = new ZelCashJMenuItem( + langUtil.getString("menu.label.quit"), KeyEvent.VK_Q)); + menuItemExit.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_Q, accelaratorKeyMask)); + mb.add(file); + + ZelCashJMenu wallet = + new ZelCashJMenu(langUtil.getString("menu.label.wallet")); + wallet.setMnemonic(KeyEvent.VK_W); + wallet.add(menuItemBackup = new ZelCashJMenuItem( + langUtil.getString("menu.label.backup"), KeyEvent.VK_B)); + menuItemBackup.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_B, accelaratorKeyMask)); + wallet.add(menuItemEncrypt = new ZelCashJMenuItem( + langUtil.getString("menu.label.encrypt"), KeyEvent.VK_E)); + menuItemEncrypt.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_E, accelaratorKeyMask)); + wallet.add( + menuItemChangePassword = new ZelCashJMenuItem( + langUtil.getString("menu.label.changepassword"), KeyEvent.VK_J)); + menuItemChangePassword.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_J, accelaratorKeyMask)); + wallet.add(menuItemExportKeys = new ZelCashJMenuItem( + langUtil.getString("menu.label.export.private.keys"), + KeyEvent.VK_K)); + menuItemExportKeys.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_K, accelaratorKeyMask)); + wallet.add(menuItemImportKeys = new ZelCashJMenuItem( + langUtil.getString("menu.label.import.private.keys"), + KeyEvent.VK_I)); + menuItemImportKeys.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_I, accelaratorKeyMask)); + wallet.add( + menuItemShowPrivateKey = new ZelCashJMenuItem( + langUtil.getString("menu.label.show.private.key"), KeyEvent.VK_P)); + menuItemShowPrivateKey.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_P, accelaratorKeyMask)); + wallet.add(menuItemImportOnePrivateKey = new ZelCashJMenuItem( + langUtil.getString("menu.label.import.one.private.key"), + KeyEvent.VK_N)); + menuItemImportOnePrivateKey.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_N, accelaratorKeyMask)); + wallet.add(menuItemReindex = new ZelCashJMenuItem( + langUtil.getString("menu.label.reindex"), KeyEvent.VK_1)); + menuItemReindex.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_1, accelaratorKeyMask)); + wallet.add(menuItemRescan = new ZelCashJMenuItem( + langUtil.getString("menu.label.rescan"), KeyEvent.VK_2)); + menuItemRescan.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_2, accelaratorKeyMask)); + wallet.add( + menuItemSproutToSapling = new ZelCashJMenuItem( + langUtil.getString("menu.label.sprouttosapling"), KeyEvent.VK_3)); + menuItemSproutToSapling.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_3, accelaratorKeyMask)); + // wallet.add(menuItemExportToArizen = new + // ZelCashJMenuItem(langUtil.getString("menu.label.export.to.arizen"), + // KeyEvent.VK_A)); + // menuItemExportToArizen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, + // accelaratorKeyMask)); + mb.add(wallet); + + ZelCashJMenu messaging = + new ZelCashJMenu(langUtil.getString("menu.label.messaging")); + messaging.setMnemonic(KeyEvent.VK_S); + messaging.add( + menuItemOwnIdentity = new ZelCashJMenuItem( + langUtil.getString("menu.label.own.identity"), KeyEvent.VK_D)); + menuItemOwnIdentity.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_D, accelaratorKeyMask)); + messaging.add(menuItemExportOwnIdentity = new ZelCashJMenuItem( + langUtil.getString("menu.label.export.own.identity"), + KeyEvent.VK_L)); + menuItemExportOwnIdentity.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_L, accelaratorKeyMask)); + messaging.add(menuItemAddMessagingGroup = new ZelCashJMenuItem( + langUtil.getString("menu.label.add.messaging.group"), + KeyEvent.VK_G)); + menuItemAddMessagingGroup.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_G, accelaratorKeyMask)); + messaging.add(menuItemImportContactIdentity = new ZelCashJMenuItem( + langUtil.getString("menu.label.import.contact.identity"), + KeyEvent.VK_Y)); + menuItemImportContactIdentity.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_Y, accelaratorKeyMask)); + messaging.add( + menuItemRemoveContactIdentity = new ZelCashJMenuItem( + langUtil.getString("menu.label.remove.contact"), KeyEvent.VK_R)); + menuItemRemoveContactIdentity.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_R, accelaratorKeyMask)); + messaging.add(menuItemMessagingOptions = new ZelCashJMenuItem( + langUtil.getString("menu.label.options"), KeyEvent.VK_O)); + menuItemMessagingOptions.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_O, accelaratorKeyMask)); + + ZelCashJMenu shareFileVia = + new ZelCashJMenu(langUtil.getString("menu.label.share.file")); + shareFileVia.setMnemonic(KeyEvent.VK_V); + // TODO: uncomment this for IPFS integration + // messaging.add(shareFileVia); + shareFileVia.add(menuItemShareFileViaIPFS = new ZelCashJMenuItem( + langUtil.getString("menu.label.ipfs"), KeyEvent.VK_F)); + menuItemShareFileViaIPFS.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_F, accelaratorKeyMask)); + + mb.add(messaging); + + ZelCashJMenu zelNodes = + new ZelCashJMenu(langUtil.getString("menu.label.zelnodes")); + zelNodes.setMnemonic(KeyEvent.VK_Z); + zelNodes.add( + menuItemNewZelnode = new ZelCashJMenuItem( + langUtil.getString("menu.label.zelnodes.new"), KeyEvent.VK_Z)); + menuItemNewZelnode.setAccelerator( + KeyStroke.getKeyStroke(KeyEvent.VK_Z, accelaratorKeyMask)); + + mb.add(zelNodes); + + ActionListener languageSelectionAction = new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + Log.info("Action [" + e.getActionCommand() + "] performed"); + LanguageMenuItem item = (LanguageMenuItem)e.getSource(); + langUtil.updatePreferredLanguage(item.getLocale()); + JOptionPane.showMessageDialog( + ZCashUI.this.getRootPane().getParent(), + langUtil.getString("dialog.message.language.prefs.update"), + langUtil.getString("dialog.message.language.prefs.update.title"), + JOptionPane.INFORMATION_MESSAGE); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }; + ZelCashJMenu languageMenu = + new ZelCashJMenu(langUtil.getString("menu.label.language")); + // only english translation available + /* + * LanguageMenuItem italian = new + * LanguageMenuItem(langUtil.getString("menu.label.language.italian"), new + * ImageIcon(cl.getResource("images/italian.png")), Locale.ITALY); + * italian.setHorizontalTextPosition(ZelCashJMenuItem.RIGHT); + * + * italian.addActionListener(languageSelectionAction); + */ + + LanguageMenuItem english = new LanguageMenuItem( + langUtil.getString("menu.label.language.english"), + new ImageIcon(cl.getResource("images/uk.png")), Locale.US); + english.setHorizontalTextPosition(ZelCashJMenuItem.RIGHT); + + english.addActionListener(languageSelectionAction); + + ButtonGroup group = new ButtonGroup(); + // group.add(italian); + group.add(english); + + // languageMenu.add(italian); + languageMenu.add(english); + + // Temporarily disabled till translations are completed + // mb.add(languageMenu); + + this.setJMenuBar(mb); + + // Add listeners etc. + menuItemExit.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.exitProgram(); + } + }); + + menuItemAbout.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + AboutDialog ad = new AboutDialog(ZCashUI.this); + ad.setVisible(true); + } catch (UnsupportedEncodingException uee) { + Log.error("Unexpected error: ", uee); + ZCashUI.this.errorReporter.reportError(uee); + } + } + }); + + menuItemZelcashUI.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + ZelCashUIEditDialog ad = new ZelCashUIEditDialog(ZCashUI.this); + ad.setVisible(true); + } catch (UnsupportedEncodingException uee) { + Log.error("Unexpected error: ", uee); + ZCashUI.this.errorReporter.reportError(uee); + } + } + }); + + menuItemNewZelnode.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + ZelCashZelNodeDialog ad = new ZelCashZelNodeDialog( + ZCashUI.this, clientCaller, installationObserver, null, + labelStorage); + ad.setVisible(true); + } catch (Exception uee) { + Log.error("Unexpected error: ", uee); + ZCashUI.this.errorReporter.reportError(uee); + } + } + }); + + menuItemBackup.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.walletOps.backupWallet(); + } + }); + + menuItemEncrypt.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.walletOps.encryptWallet(); + } + }); + + menuItemChangePassword.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.walletOps.changeWalletPassword(); + } + }); + + menuItemExportKeys.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.walletOps.exportWalletPrivateKeys(); + } + }); + + menuItemImportKeys.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.walletOps.importWalletPrivateKeys(); + } + }); + + menuItemShowPrivateKey.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.walletOps.showPrivateKey(); + } + }); + + menuItemImportOnePrivateKey.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.walletOps.importSinglePrivateKey(); + } + }); + + menuItemReindex.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.walletOps.reindexWallet(); + } + }); + + menuItemRescan.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.walletOps.rescanWallet(); + } + }); + + menuItemSproutToSapling.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.walletOps.sproutToSaplingMigrationTool(); + } + }); + + menuItemOwnIdentity.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.messagingPanel.openOwnIdentityDialog(); + } + }); + + menuItemExportOwnIdentity.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.messagingPanel.exportOwnIdentity(); + } + }); + + menuItemImportContactIdentity.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.messagingPanel.importContactIdentity(); + } + }); + + menuItemAddMessagingGroup.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.messagingPanel.addMessagingGroup(); + } + }); + + menuItemRemoveContactIdentity.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.messagingPanel.removeSelectedContact(); + } + }); + + menuItemMessagingOptions.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.messagingPanel.openOptionsDialog(); + } + }); + + menuItemShareFileViaIPFS.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ZCashUI.this.messagingPanel.shareFileViaIPFS(); + } + }); + /* + * menuItemExportToArizen.addActionListener( new ActionListener() { + * + * @Override public void actionPerformed(ActionEvent e) { + * ZCashUI.this.walletOps.exportToArizenWallet(); } } ); + */ + // Close operation + this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + this.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + ZCashUI.this.exitProgram(); + } + }); + + // Show initial message + SwingUtilities.invokeLater(new Runnable() { + public void run() { + try { + String userDir = OSUtil.getSettingsDirectory(); + File warningFlagFile = + new File(userDir + File.separator + "initialInfoShown_0.82.flag"); + if (warningFlagFile.exists()) { + return; + }; + + Object[] options = { + langUtil.getString("main.frame.disclaimer.button.agree"), + langUtil.getString("main.frame.disclaimer.button.disagree")}; + + int option = JOptionPane.showOptionDialog( + ZCashUI.this.getRootPane().getParent(), + langUtil.getString("main.frame.disclaimer.text"), + langUtil.getString("main.frame.disclaimer.title"), + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, + options, options[0]); + + if (option == 0) { + warningFlagFile.createNewFile(); + } else { + ZCashUI.this.exitProgram(); + } + + } catch (IOException ioe) { + /* TODO: report exceptions to the user */ + Log.error("Unexpected error: ", ioe); + } + } + }); + + // Finally dispose of the progress dialog + if (progressDialog != null) { + progressDialog.doDispose(); + } + + // Notify the messaging TAB that it is being selected - every time + tabs.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + ZelCashJTabbedPane tabs = (ZelCashJTabbedPane)e.getSource(); + if (tabs.getSelectedIndex() == 5) { + ZCashUI.this.messagingPanel.tabSelected(); + } + } + }); + + this.validate(); + this.repaint(); + + this.pack(); + Dimension currentSize = this.getSize(); + + OS_TYPE os = OSUtil.getOSType(); + int width = 1040; + if (os == OS_TYPE.MAC_OS) { + width += 100; // Needs to be wider on macOS + } + + this.setSize(new Dimension(width, currentSize.height)); + this.validate(); + this.repaint(); + } + + public void exitProgram() { + Log.info("Exiting ..."); + + this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + this.dashboard.stopThreadsAndTimers(); + this.transactionDetailsPanel.stopThreadsAndTimers(); + this.addresses.stopThreadsAndTimers(); + this.sendPanel.stopThreadsAndTimers(); + this.messagingPanel.stopThreadsAndTimers(); + + ZCashUI.this.setVisible(false); + ZCashUI.this.dispose(); + + System.exit(0); + } + + public void stopTimers() { + Log.info("stopTimers ..."); + + this.dashboard.stopThreadsAndTimers(); + this.transactionDetailsPanel.stopThreadsAndTimers(); + this.addresses.stopThreadsAndTimers(); + this.sendPanel.stopThreadsAndTimers(); + this.messagingPanel.stopThreadsAndTimers(); + } + + /** + * if both are true only reindex will be executed. + * @param reindex + * @param rescan + */ + public void restartDaemon(boolean reindex, boolean rescan) { + Log.info("restartDaemon ..."); + + this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + this.dashboard.stopThreadsAndTimers(); + this.transactionDetailsPanel.stopThreadsAndTimers(); + this.addresses.stopThreadsAndTimers(); + this.sendPanel.stopThreadsAndTimers(); + this.messagingPanel.stopThreadsAndTimers(); + Thread.sleep(ZCashUI.THREAD_WAIT_1_SECOND); + + this.clientCaller.stopDaemon(); + ZCashInstallationObserver initialInstallationObserver; + DaemonInfo zcashdInfo; + for (int i = 0; i < 10; ++i) { + Log.info("Check if Daemon is stopped"); + initialInstallationObserver = + new ZCashInstallationObserver(OSUtil.getProgramDirectory()); + zcashdInfo = initialInstallationObserver.getDaemonInfo(); + initialInstallationObserver = null; + if (zcashdInfo.status != DAEMON_STATUS.RUNNING) { + Log.info("Daemon stopped."); + break; + } + Thread.sleep(ZCashUI.THREAD_WAIT_1_SECOND); + } + this.clientCaller.startDaemon(reindex, rescan); + for (int i = 0; i < 10; ++i) { + Log.info("Check if Daemon is running"); + initialInstallationObserver = + new ZCashInstallationObserver(OSUtil.getProgramDirectory()); + zcashdInfo = initialInstallationObserver.getDaemonInfo(); + initialInstallationObserver = null; + if (zcashdInfo.status == DAEMON_STATUS.RUNNING) { + Log.info("Daemon running."); + break; + } + Thread.sleep(ZCashUI.THREAD_WAIT_1_SECOND); + } + for (int i = 0; i < 30; ++i) { + Log.info("Checking if Daemon is ready for gui wallet."); + try { + clientCaller.getNetworkAndBlockchainInfo(); + Log.info("Daemon is ready."); + } catch (Exception e) { + Log.info("Daemon not ready."); + Thread.sleep(ZCashUI.THREAD_WAIT_1_SECOND); + } + } + Log.info("restartDaemon finished ..."); + } catch (Exception e) { + Log.error("Error on restartDaemon: " + e.getMessage()); + } + } + + public static void main(String argv[]) throws IOException { + ZCashUI ui = null; + StartupProgressDialog startupBar = null; + ZCashClientCaller initialClientCaller = null; + ZCashInstallationObserver initialInstallationObserver = null; + DaemonInfo zcashdInfo = null; + boolean reindex = false; + for (int i = 0; i < argv.length; i++) { + if ("reindex".equals(argv[i])) { + reindex = true; + } + } + try { + new ZelCashUI(); + OS_TYPE os = OSUtil.getOSType(); + + if ((os == OS_TYPE.WINDOWS) || (os == OS_TYPE.MAC_OS) || + (os == OS_TYPE.LINUX)) { + possiblyCreateZelCashConfigFile(); + } + + LanguageUtil langUtil = LanguageUtil.instance(); + + Log.info("Starting ZelCash Swing Wallet ..."); + Log.info("OS: " + System.getProperty("os.name") + " = " + os); + Log.info("Current directory: " + new File(".").getCanonicalPath()); + Log.info("Class path: " + System.getProperty("java.class.path")); + Log.info("Environment PATH: " + System.getenv("PATH")); + + // Look and feel settings - a custom OS-look and feel is set for Windows + if (os == OS_TYPE.WINDOWS) { + // Custom Windows L&F and font settings + UIManager.setLookAndFeel( + "com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + + // This font looks good but on Windows 7 it misses some chars like the + // stars... FontUIResource font = new FontUIResource("Lucida Sans + // Unicode", Font.PLAIN, 11); UIManager.put("Table.font", font); + } else if (os == OS_TYPE.MAC_OS) { + // The MacOS L&F is active by default - the property sets the menu bar + // Mac style + System.setProperty("apple.laf.useScreenMenuBar", "true"); + System.setProperty( + "com.apple.mrj.application.apple.menu.about.name", + LanguageUtil.instance().getString("apple.menu.about.name")); + } else { + for (LookAndFeelInfo lf : UIManager.getInstalledLookAndFeels()) { + Log.info("Available look and feel: " + lf.getName() + " " + + lf.getClassName()); + if (lf.getName().equals("Nimbus")) { + Log.info("Setting look and feel: {0}", lf.getClassName()); + UIManager.setLookAndFeel(lf.getClassName()); + break; + }; + } + } + + Log.info("Checking if zelnodes.conf exists and is properly set..."); + String blockchainDir = OSUtil.getBlockchainDirectory(); + File zelnodeConf = + new File(blockchainDir + File.separator + "zelnode.conf"); + if (!zelnodeConf.exists()) { + Log.info("Could not find file: {0} !", zelnodeConf.getAbsolutePath()); + } else { + BufferedReader br = new BufferedReader(new FileReader(zelnodeConf)); + String emptyLine; + String st; + String[] zelNodeInfo; + while ((st = br.readLine()) != null) { + emptyLine = st.replaceAll(" ", "").replaceAll( + "(?m)^\\\\s*\\\\r?\\\\n|\\\\r?\\\\n\\\\s*(?!.*\\\\r?\\\\n)", ""); + if (st.startsWith("#") || emptyLine.equals("")) { + continue; + } else { + zelNodeInfo = st.split("\\s+"); + if (zelNodeInfo.length != 5) { + Log.error( + "Zelnode.conf file not ok. One of the lines doesn`t have 5 strings."); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "parsing.error.zelnodesconf.wrong.number"), + LanguageUtil.instance().getString( + "parsing.error.zelnodesconf.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(1); + } else { + + if (!zelNodeInfo[1].endsWith(":16125") && + !zelNodeInfo[1].endsWith(":26125")) { + Log.error( + "Zelnode.conf file not ok. ip not ending with correct port: " + + zelNodeInfo[1]); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "parsing.error.zelnodesconf.wrong.port", + zelNodeInfo[1]), + LanguageUtil.instance().getString( + "parsing.error.zelnodesconf.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(1); + } + + String ip = zelNodeInfo[1] + .replaceAll(":16125", "") + .replaceAll(":26125", ""); + if (ip.isEmpty()) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.ip.notset"), + LanguageUtil.instance().getString( + "dialog.zelcashnewzelnode.fields.error.adding.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(1); + } else if (!ip.endsWith(".onion")) { + try { + Inet4Address address = + (Inet4Address)Inet4Address.getByName(ip); + } catch (Exception e) { + try { + Inet6Address address = + (Inet6Address)Inet6Address.getByName(ip); + if (!ip.startsWith("[") || !ip.endsWith("]")) { + Log.error( + "Zelnode.conf file not ok. ip not valid for ipv4, ipv6 and onion:" + + ip); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "parsing.error.zelnodesconf.wrong.ip", ip), + LanguageUtil.instance().getString( + "parsing.error.zelnodesconf.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(1); + } + } catch (Exception ex) { + Log.error( + "Zelnode.conf file not ok. ip not valid for ipv4, ipv6 and onion:" + + ip); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "parsing.error.zelnodesconf.wrong.ip", ip), + LanguageUtil.instance().getString( + "parsing.error.zelnodesconf.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(1); + } + } + } + } + } + } + Log.info("zelnodes.conf exists and is properly set..."); + } + + // If zelcashd is currently not running, do a startup of the daemon as a + // child process It may be started but not ready - then also show dialog + initialInstallationObserver = + new ZCashInstallationObserver(OSUtil.getProgramDirectory()); + zcashdInfo = initialInstallationObserver.getDaemonInfo(); + initialInstallationObserver = null; + + initialClientCaller = new ZCashClientCaller(OSUtil.getProgramDirectory()); + boolean daemonStartInProgress = false; + try { + if (zcashdInfo.status == DAEMON_STATUS.RUNNING) { + NetworkAndBlockchainInfo info = + initialClientCaller.getNetworkAndBlockchainInfo(); + // If more than 20 minutes behind in the blockchain - startup in + // progress + if ((System.currentTimeMillis() - info.lastBlockDate.getTime()) > + (20 * 60 * 1000)) { + Log.info("Current blockchain synchronization date is " + + new Date(info.lastBlockDate.getTime())); + daemonStartInProgress = true; + } + } + } catch (WalletCallException wce) { + if ((wce.getMessage().indexOf("{\"code\":-28") != + -1) || // Started but not ready + (wce.getMessage().indexOf("error code: -28") != -1)) { + Log.info("zelcashd is currently starting..."); + daemonStartInProgress = true; + } + } + if (false == AppLock.lock()) { + throw new Exception( + LanguageUtil.instance().getString("duplicate.instante.detected")); + } + installShutdownHook(); + + if ((zcashdInfo.status != DAEMON_STATUS.RUNNING) || + (daemonStartInProgress)) { + Log.info( + "zelcashd is not running at the moment or has not started/synchronized 100% - showing splash..."); + startupBar = new StartupProgressDialog(initialClientCaller); + startupBar.setVisible(true); + startupBar.waitForStartup(reindex); + } + initialClientCaller = null; + + // Main GUI is created here + ui = new ZCashUI(startupBar); + ui.setVisible(true); + + } catch (InstallationDetectionException ide) { + Log.error("Unexpected error: ", ide); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.installation.error.text", + OSUtil.getProgramDirectory(), ide.getMessage()), + LanguageUtil.instance().getString( + "main.frame.option.pane.installation.error.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(1); + } catch (WalletCallException wce) { + Log.error("Unexpected error: ", wce); + + if ((wce.getMessage().indexOf("{\"code\":-28,\"message\"") != -1) || + (wce.getMessage().indexOf("error code: -28") != -1)) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.communication.error.text"), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.communication.error.title"), + JOptionPane.ERROR_MESSAGE); + } else { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.communication.error.2.text", + wce.getMessage()), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.communication.error.2.title"), + JOptionPane.ERROR_MESSAGE); + } + + System.exit(2); + } catch (Exception e) { + Log.error("Unexpected error: ", e); + if (e.getMessage().equals(LanguageUtil.instance().getString( + "duplicate.instante.detected"))) { + JOptionPane.showMessageDialog( + null, e.getMessage(), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(3); + } else if (e.getMessage().contains("(code 1)")) { + Object[] options = {LanguageUtil.instance().getString( + "main.frame.reindex.button.agree"), + LanguageUtil.instance().getString( + "main.frame.reindex.button.disagree")}; + + int option = JOptionPane.showOptionDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.code1.text", + e.getMessage()), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.title"), + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, + options, options[0]); + if (option == 0) { + try { + + if (initialClientCaller == null) { + initialClientCaller = + new ZCashClientCaller(OSUtil.getProgramDirectory()); + } + initialClientCaller.stopDaemon(); + + JOptionPane.showMessageDialog(null, + LanguageUtil.instance().getString( + "wallet.reindex.restart.message"), + LanguageUtil.instance().getString( + "wallet.reindex.restart.title"), + JOptionPane.INFORMATION_MESSAGE); + + if (startupBar != null) { + startupBar.dispose(); + } + for (int i = 0; i < 5; ++i) { + Log.info("Check if Daemon is stopped"); + initialInstallationObserver = + new ZCashInstallationObserver(OSUtil.getProgramDirectory()); + zcashdInfo = initialInstallationObserver.getDaemonInfo(); + initialInstallationObserver = null; + if (zcashdInfo.status != DAEMON_STATUS.RUNNING) { + Log.info("Daemon stopped."); + break; + } + Thread.sleep(ZCashUI.THREAD_WAIT_1_SECOND); + } + Log.info("Restarting the wallet."); + String args[] = {"reindex"}; + ZCashUI.main(args); + } catch (Exception errr) { + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.2.text", + errr.getMessage()), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.2.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(5); + } catch (Error errX) { + // Last resort catch for unexpected problems - just to inform the + // user + errX.printStackTrace(); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.2.text", + errX.getMessage()), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.2.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(6); + } + } else { + System.exit(3); + } + } else { + Log.error("Unexpected error: ", e); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.text", + e.getMessage()), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(3); + } + + } catch (Error err) { + // Last resort catch for unexpected problems - just to inform the user + err.printStackTrace(); + JOptionPane.showMessageDialog( + null, + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.2.text", + err.getMessage()), + LanguageUtil.instance().getString( + "main.frame.option.pane.wallet.critical.error.2.title"), + JOptionPane.ERROR_MESSAGE); + System.exit(4); + } + } + + public static void possiblyCreateZelCashConfigFile() throws IOException { + String blockchainDir = OSUtil.getBlockchainDirectory(); + File dir = new File(blockchainDir); + + if (!dir.exists()) { + if (!dir.mkdirs()) { + Log.error("ERROR: Could not create settings directory: " + + dir.getCanonicalPath()); + throw new IOException("Could not create settings directory: " + + dir.getCanonicalPath()); + } + } + + File zelcashConfigFile = new File(dir, "zelcash.conf"); + + if (!zelcashConfigFile.exists()) { + Log.info("ZelCash configuration file " + + zelcashConfigFile.getCanonicalPath() + + " does not exist. It will be created with default settings."); + + Random r = new Random(System.currentTimeMillis()); + + PrintStream configOut = + new PrintStream(new FileOutputStream(zelcashConfigFile)); + + configOut.println( + "#############################################################################"); + configOut.println( + "# ZelCash configuration file #"); + configOut.println( + "#############################################################################"); + configOut.println( + "# This file has been automatically generated by the ZelCash GUI wallet with #"); + configOut.println( + "# default settings. It may be further cutsomized by hand only. #"); + configOut.println( + "#############################################################################"); + configOut.println("# Creation date: " + new Date().toString()); + configOut.println( + "#############################################################################"); + configOut.println(""); + configOut.println("rpcallowip=127.0.0.1"); + configOut.println("server=1"); + configOut.println("daemon=1"); + configOut.println("txindex=1"); + configOut.println("logtimestamps=1"); + configOut.println("maxconnections=256"); + configOut.println("addnode=node.zel.cash"); + configOut.println("addnode=explorer.zel.cash"); + configOut.println("addnode=explorer2.zel.cash"); + configOut.println("addnode=explorer-asia.zel.cash"); + configOut.println("addnode=explorer.zelcash.online"); + configOut.println("addnode=explorer.zel.zelcore.io"); + configOut.println( + "# The rpcuser/rpcpassword are used for the local call to zelcashd"); + configOut.println("rpcuser=User" + Math.abs(r.nextInt())); + configOut.println("rpcpassword=Pass" + Math.abs(r.nextInt()) + "" + + Math.abs(r.nextInt()) + "" + Math.abs(r.nextInt())); + /* + * This is not necessary as of release: + * https://github.com/ZencashOfficial/zen/releases/tag/v2.0.9-3-b8d2ebf + * configOut. + * println("# Well-known nodes to connect to - to speed up acquiring + * initial connections" + * ); configOut.println("addnode=zpool.blockoperations.com"); + * configOut.println("addnode=luckpool.org:8333"); + * configOut.println("addnode=zencash.cloud"); + * configOut.println("addnode=zen.suprnova.cc"); + * configOut.println("addnode=zen.bitfire.one"); + * configOut.println("addnode=zenmine.pro"); + */ + + configOut.close(); + } + } + + private static void installShutdownHook() { + + Runnable runner = new Runnable() { + @Override + public void run() { + AppLock.unlock(); + } + }; + Runtime.getRuntime().addShutdownHook( + new Thread(runner, "Window Prefs Hook")); + } } From a8abf9669548f9ca92d8fbf6eba40e7488aff2d4 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Sat, 21 Dec 2019 11:59:32 +0000 Subject: [PATCH 2/2] Restyled by whitespace --- src/resources/messages/zelcash_en.properties | 90 ++++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/src/resources/messages/zelcash_en.properties b/src/resources/messages/zelcash_en.properties index e7fc8572..13e0bde6 100644 --- a/src/resources/messages/zelcash_en.properties +++ b/src/resources/messages/zelcash_en.properties @@ -40,12 +40,12 @@ main.frame.option.pane.wallet.communication.error.text=It appears that zelcashd start the ZELmate wallet later... main.frame.option.pane.wallet.communication.error.title=Wallet communication error main.frame.option.pane.wallet.communication.error.2.text=There was a problem communicating with the ZelCash daemon/wallet. \n \ - Please ensure that the ZelCash server zelcashd is started (e.g. via \n \ + Please ensure that the ZelCash server zelcashd is started (e.g. via \n \ command \"zelcashd --daemon\"). Error message is: \n {0} \ See the console/logfile output for more detailed error information! main.frame.option.pane.wallet.communication.error.2.title=Wallet communication error main.frame.option.pane.wallet.critical.error.text=A general unexpected critical error has occurred: \n {0} \n \ - See the console/logfile output for more detailed error information! + See the console/logfile output for more detailed error information! main.frame.option.pane.wallet.critical.error.title=Error main.frame.option.pane.wallet.critical.error.2.text=A general unexpected critical/unrecoverable error has occurred: \n {0} \n \ See the console/logfile output for more detailed error information! @@ -200,21 +200,21 @@ panel.address.message.backup=\n\nIt is necessary to back up the wallet after cre loss on the current PC. To back up the wallet, use menu option: Wallet >> Backup\n panel.address.option.pane.text=A new {0} address has been created successfully:\n\ {1} {2} -panel.address.option.pane.title=Address created. -panel.address.table.create.address.header=Label:Balance:Confirmed?:Address +panel.address.option.pane.title=Address created. +panel.address.table.create.address.header=Label:Balance:Confirmed?:Address panel.address.option.pane.validation.error.text=An invalid or watch-only address exists in the wallet: \n \ {0} \n\n \ The ZELmate wallet software cannot operate properly with addresses that are invalid or\n \ exist in the wallet as watch-only addresses. Do NOT use this address as a destination\n \ - address for payment operations! -panel.address.option.pane.validation.error.title=Error: invalid or watch-only address exists! + address for payment operations! +panel.address.option.pane.validation.error.title=Error: invalid or watch-only address exists! panel.address.option.pane.yes=Yes\u0020{0} panel.address.option.pane.no=No\u0020\u0020{0} panel.address.label.input.text=Please enter a label for the newly created address: panel.address.label.input.title=Label of the address... table.address.option.obtain.private.key.label=Obtain private key... -table.address.option.pane.text.private=Z (Private) -table.address.option.pane.text.transparent=T (Transparent) +table.address.option.pane.text.private=Z (Private) +table.address.option.pane.text.transparent=T (Transparent) table.address.option.pane.text.rest=address:\n{0} \nhas private key:\n{1}\n\n\ The private key has also been copied to the clipboard. table.address.option.pane.title=Private key information @@ -259,7 +259,7 @@ panel.dashboard.transactions.out=OUT panel.dashboard.transactions.private=PrivatE panel.dashboard.marketcap.column.exchange.info=Exchange Information panel.dashboard.marketcap.column.exchange.value=Values -panel.dashboard.marketcap.price.currency=Current price in +panel.dashboard.marketcap.price.currency=Current price in panel.dashboard.marketcap.price.btc=Current price in BTC: panel.dashboard.marketcap.capitalisation=ZEL capitalization (USD): panel.dashboard.marketcap.daily.change=Daily change (USD price): @@ -317,7 +317,7 @@ dialog.password.option.pane.process.title=Password empty dialog.password.encryption.upper.label.text=This function is experimental, use it at your own risk. \ Please backup your private keys before encrypt your wallet. \ The wallet.dat file will be encrypted with a password and won`t be compatible with ZelCore Full Node mode. -dialog.password.encryption.confirmation.label.text=Confirmation:\u0020 +dialog.password.encryption.confirmation.label.text=Confirmation:\u0020 dialog.password.encryption.option.pane.mismatch.text=The password and the confirmation do not match! dialog.password.encryption.option.pane.mismatch.title=Password mismatch... @@ -336,9 +336,9 @@ sapling.spend.fetcher.option.pane.verify.key.text=Verifying downloaded Sapling S sapling.spend.fetcher.option.pane.verify.key.failed.text=Failed to download Sapling Spend parameter properly. Cannot continue! send.cash.panel.label=Send cash from:\u0020\u0020\u0020\u0020\u0020\u0020\u0020 send.cash.panel.label.info= \ - * Only addresses with a confirmed balance are shown as sources for sending! + * Only addresses with a confirmed balance are shown as sources for sending! send.cash.panel.label.destination.address=Destination address: -send.cash.panel.label.memo=Memo (optional):\u0020\u0020\u0020\u0020\u0020 +send.cash.panel.label.memo=Memo (optional):\u0020\u0020\u0020\u0020\u0020 send.cash.panel.label.memo.info= \ * Memo may be specified only if the destination is a Z (Private) address! send.cash.panel.label.amount=Amount to send: @@ -352,16 +352,16 @@ send.cash.panel.label.send.warning= \ transaction is confirmed. The address is temporarily removed from the list! Freshly mined coins may only \ be sent to a Z (Private) address. \ -send.cash.panel.label.last.operation.status=Last operation status:\u0020 -send.cash.panel.label.last.operation.progress=Progress:\u0020 +send.cash.panel.label.last.operation.status=Last operation status:\u0020 +send.cash.panel.label.last.operation.progress=Progress:\u0020 send.cash.panel.option.pane.error.text=An unexpected error occurred when sending cash!\n \ Please ensure that the ZelCash daemon is running and\n \ - parameters are correct. You may try again later...\n {0} + parameters are correct. You may try again later...\n {0} send.cash.panel.option.pane.error.title=Error in sending cash -send.cash.panel.menu.item.paste=Paste address -send.cash.panel.option.pane.no.funds.text=There are no addresses with a positive balance to send\n cash from! +send.cash.panel.menu.item.paste=Paste address +send.cash.panel.option.pane.no.funds.text=There are no addresses with a positive balance to send\n cash from! send.cash.panel.option.pane.no.funds.title=No funds available -send.cash.panel.option.pane.select.source.text=Please select a source address with a current positive\n balance to send cash from! +send.cash.panel.option.pane.select.source.text=Please select a source address with a current positive\n balance to send cash from! send.cash.panel.option.pane.select.source.title=Please select source address send.cash.panel.option.pane.error.source.address.invalid=Source address is invalid; it is too short or missing. send.cash.panel.option.pane.error.source.address.too.long=Source address is invalid; it is too long. @@ -370,7 +370,7 @@ send.cash.panel.option.pane.error.destination.address.too.short=Destination addr send.cash.panel.option.pane.error.destination.address.too.long=Destination address is invalid; it is too long. send.cash.panel.option.pane.error.destination.address.has.spaces=Destination address is invalid; it has leading or trailing white-space characters. send.cash.panel.option.pane.error.destination.address.incorrect.text=The destination address to send ZEL to: \n {0} \n\ - does not appear to be a valid ZEL address. ZEL addresses start with zc, za, t1 or t3! + does not appear to be a valid ZEL address. ZEL addresses start with zc, za, t1 or t3! send.cash.panel.option.pane.error.destination.address.incorrect.title=Destination address is incorrect... send.cash.panel.option.pane.error.destination.address.notz.text=The destination address to send ZEL to: \n {0} \n\ does not appear to be a Z address. However a text memo is specified to be sent. A text memo may\nbe included in a \ @@ -390,7 +390,7 @@ send.cash.panel.option.pane.confirm.operation.text.with.memo=Please confirm that send.cash.panel.option.pane.confirm.operation.title=Confirm sending cash... send.cash.panel.option.pane.confirm.operation.button.yes=Yes (I confirm) send.cash.panel.option.pane.confirm.operation.button.no=No -send.cash.panel.option.pane.confirm.operation.button.not.again=Yes, do not show this message again +send.cash.panel.option.pane.confirm.operation.button.not.again=Yes, do not show this message again send.cash.panel.option.pane.error.destination.amount.fractional.digits=The amount to send specified: {0} has more than 8 fractional decimal digits.\n\ It cannot be accurately represented in a ZEL transaction and will be rounded off. The actual amount\n\ sent will thus differ from what is specified. Are you sure you want to proceed? @@ -408,11 +408,11 @@ send.cash.panel.option.pane.error.incorrect.sending.parameters=Sending parameter send.cash.panel.operation.status.progress.label=IN PROGRESS send.cash.panel.operation.status.success.label=SUCCESSFUL send.cash.panel.operation.complete.report=OK:Copy transaction ID:View on the blockchain -send.cash.panel.operation.complete.report.success.text=Successfully sent {0} ZEL from address: \n{1}\nto address: \n{2}\n\nTransaction ID: {3} +send.cash.panel.operation.complete.report.success.text=Successfully sent {0} ZEL from address: \n{1}\nto address: \n{2}\n\nTransaction ID: {3} send.cash.panel.operation.complete.report.success.title=Cash sent successfully send.cash.panel.operation.status.error.label=ERROR: {0} send.cash.panel.option.pane.error.report.text=An error occurred when sending cash. Error message is:\n {0}\n\n \ - Please ensure that sending parameters are correct. You may try again later...\n + Please ensure that sending parameters are correct. You may try again later...\n send.cash.panel.option.pane.error.report.title=Error in sending cash send.cash.panel.checkbox.send.change.back=Send remaining balance back to source address (explicitly) send.cash.panel.insufficient.balance=The address: {0}\n\ @@ -465,7 +465,7 @@ transactions.table.memo.unavailable.text=The selected transaction does not have about this transaction is available. transactions.table.memo.unavailable.title=Memo information is unavailable transactions.table.memo.clipboard.text=The memo contained in the transaction is: \n{0}\n\n\ - (The memo has also been copied to the clipboard.) + (The memo has also been copied to the clipboard.) transactions.table.memo.clipboard.title=Memo transactions.table.memo.field.missing.text=The selected transaction does not contain a memo field. transactions.table.memo.field.missing.title=Memo field is not available... @@ -520,7 +520,7 @@ wallet.operations.option.pane.address.table.view.private.key.title=Please select wallet.operations.private.address=Z (Private) wallet.operations.transparent.address=T (Transparent) wallet.operations.option.pane.address.information.text={0} address:\n{1}\nhas private key:\n{2} \n\n\ - The private key has also been copied to the clipboard. + The private key has also been copied to the clipboard. wallet.operations.option.pane.address.information.title=Private key information wallet.operations.dialog.export.arizen.title=Exporting Arizen wallet wallet.operations.dialog.export.arizen.filechooser.filter=Arizen wallet file @@ -565,7 +565,7 @@ create.group.dialog.group.added=The messaging group with key phrase:\n\ has been added successfully. All messages sent by individual users to the group will be sent to Z address:\n\ {1} \n\n\ IMPORTANT: Do NOT send any ZEL to this address except in cases of messaging transactions. Any\n\ - funds sent to this address may be spent by any user who has access to the group key phrase! + funds sent to this address may be spent by any user who has access to the group key phrase! create.group.dialog.group.added.successfully=Group added successfully... create.group.dialog.group.already=The messaging group with key phrase:\n\ {0} \n\ @@ -576,9 +576,9 @@ ipfs.wrapper.file.shared=The file {0} has been shared successfully via IPFS. It reached by other users (who have a local IPFS server running) via IPFS link: \n\ http://localhost:8080/ipfs/ {1} \n\n\ The link has been added to the messaging text box and also copied to the clipboard.\n -ipfs.wrapper.file.shared.successfuly=File shared successfully -ipfs.wrapper.file.shared.unexpected.error=An unexpected error occurred while sharing file via IPFS!\n {0} -ipfs.wrapper.file.error.importing.private.keys=Error in importing wallet private keys... +ipfs.wrapper.file.shared.successfuly=File shared successfully +ipfs.wrapper.file.shared.unexpected.error=An unexpected error occurred while sharing file via IPFS!\n {0} +ipfs.wrapper.file.error.importing.private.keys=Error in importing wallet private keys... ipfs.wrapper.file.not.found=The IPFS executables are expected to be found in directory:\n\ {0} \n\ However this directory is missing! IPFS cannot be started! @@ -661,12 +661,12 @@ messaging.panel.message.spamming=If you believe this user is spamming the group messaging.panel.message.spamming.ignore=Possibly ignore user messages? messaging.panel.message.spamming.ignore.yes=Ignore user's messages messaging.panel.message.spamming.ignore.cancel=Cancel & Close -messaging.panel.special.identity=Special identity carrying message; Contains details of contact: -messaging.panel.warning.unverified=[WARNING: Message signature is unverified.] -messaging.panel.error.invalid=[ERROR: Message signature is invalid! Message may be forged!] -messaging.panel.anonymous=[Anonymous] -messaging.panel.conversation.group=Conversation in group: -messaging.panel.conversation.with=Conversation with: +messaging.panel.special.identity=Special identity carrying message; Contains details of contact: +messaging.panel.warning.unverified=[WARNING: Message signature is unverified.] +messaging.panel.error.invalid=[ERROR: Message signature is invalid! Message may be forged!] +messaging.panel.anonymous=[Anonymous] +messaging.panel.conversation.group=Conversation in group: +messaging.panel.conversation.with=Conversation with: messaging.panel.welcome.message=Welcome to ZelCash messaging. As a start you will need to create a new messaging\n\ identity for yourself. As a part of this messaging identity a pair of T+Z addresses\n\ will be created. The T address is to be used for identifying you to other users.\n\ @@ -823,17 +823,17 @@ messaging.send.text.exceeding.message=The text of the message you have written i version of the ZEL messaging protocol supports approximately 330\n\ characters per message (number is not exact - depends on character\n\ encoding specifics). -messaging.send.text.exceeding=Message size exceeds currently supported limits... +messaging.send.text.exceeding=Message size exceeds currently supported limits... messaging.send.status.error=Send status:  \ - ERROR! + ERROR! messaging.send.error.wce.message=An error occurred when sending message to contact: {0}. \n\ "Error message is: {1} \n\ - "If the problem persists, you may need technical support :( ...\n -messaging.send.error.in.sending=Error in sending message + "If the problem persists, you may need technical support :( ...\n +messaging.send.error.in.sending=Error in sending message messaging.send.status.success=Send status:  \ - SUCCESSFUL + SUCCESSFUL messaging.send.status.inprogress=Send status:  \ - IN PROGRESS + IN PROGRESS messaging.collect.identity.not.found.message=The messaging identity send/receive address: \n\ {0} \n\ is not found in the wallet.dat. The reason may be that after a messaging identity\n\ @@ -855,7 +855,7 @@ messaging.send.identity.large.message=The size of your messaging identity is unf as a message. Your contact will have to import your messaging identity\n\ manually from a json file... messaging.send.identity.large=Messaging identity size is too large! -ownidentity.title=Own messaging identity - edit... +ownidentity.title=Own messaging identity - edit... ownidentity.infolabel=\ The fields below make up your messaging identity. This information is meant to be \ shared with other users.
The only mandatory field is the \"Nick name\".\ @@ -877,8 +877,8 @@ main.frame.option.pane.wallet.critical.error.code1.text=The error happening can - Local copy of blockchain is corrupted. Reindexing the blockchain may fix the issue.\n \ - Daemon is not starting. Check AntiVirus and Firewall; \n \ - There is something wrong with your zelcash.conf or zelnode.conf. \n \ - Do you want to try to reindex to fix the problem? Please backup your wallet.dat before reindexing. -dialog.zelcashqrcode.title=ZelCash QR Code + Do you want to try to reindex to fix the problem? Please backup your wallet.dat before reindexing. +dialog.zelcashqrcode.title=ZelCash QR Code dialog.zelcashnewzelnode.title=Create ZelNode dialog.zelcashnewzelnode.info=Please make sure you have sent the collateral needed for your ZelNode type in one single transaction and that is confirmed.
\ All information will be stored in zelnodes.conf file. @@ -996,7 +996,7 @@ dialog.zelcashsprouttosaplingdialog.info=The Sprout-to-Sapling migra When enabled it will automatically transfer your Sprout funds to the selected Sapling address without exposing your funds. Each migration transaction
\ will be sent when height is multiple of 500. The amount sent in each transaction is automatically calculated with a 0.0001 fee.
\ When it`s enabled don`t forget to leave your ZELmate running or the migration won`t execute.
\ - The migration will end once the wallet Sprout balance is below .01 ZEL + The migration will end once the wallet Sprout balance is below .01 ZEL dialog.zelcashsprouttosaplingdialog.enabled=Enabled: dialog.zelcashsprouttosaplingdialog.enabled.true=True dialog.zelcashsprouttosaplingdialog.enabled.false=False @@ -1009,7 +1009,7 @@ dialog.zelcashsprouttosaplingdialog.time_started=Time Started: dialog.zelcashsprouttosaplingdialog.migration_txids=Migration transactions id: dialog.zelcashsprouttosaplingdialog.enable=Enable & Restart dialog.zelcashsprouttosaplingdialog.disable=Disable & Restart -dialog.zelcashsprouttosaplingdialog.select=Select.. +dialog.zelcashsprouttosaplingdialog.select=Select.. dialog.zelcashsprouttosaplingdialog.noaddress.title=Select destination Address dialog.zelcashsprouttosaplingdialog.noaddress.message=You need to select your sapling destination address to enable the migration. zelcashsprouttosapling.enabled=Enabled