org.multibit.viewsystem.swing.action.CreateRemoteWalletSubmitAction.java Source code

Java tutorial

Introduction

Here is the source code for org.multibit.viewsystem.swing.action.CreateRemoteWalletSubmitAction.java

Source

/**
 * Copyright 2011 multibit.org
 *
 * Licensed under the MIT license (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://opensource.org/licenses/mit-license.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.multibit.viewsystem.swing.action;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.EnumMap;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.Border;
import javax.xml.bind.DatatypeConverter;

import org.multibit.controller.Controller;
import org.multibit.controller.bitcoin.BitcoinController;
import org.multibit.file.BackupManager;
import org.multibit.file.FileHandler;
import org.multibit.file.WalletLoadException;
import org.multibit.file.WalletSaveException;
import org.multibit.message.Message;
import org.multibit.message.MessageManager;
import org.multibit.model.bitcoin.BitcoinModel;
import org.multibit.model.bitcoin.WalletData;
import org.multibit.model.bitcoin.WalletInfoData;
import org.multibit.store.MultiBitWalletVersion;
import org.multibit.store.WalletVersionException;
import org.multibit.viewsystem.swing.MultiBitFrame;
import org.multibit.viewsystem.swing.view.WalletFileFilter;
import org.multibit.viewsystem.swing.view.components.FontSizer;
import org.multibit.viewsystem.swing.view.panels.HelpContentsPanel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.bouncycastle.util.Arrays;

import threshold.mr04.Alice;
import threshold.mr04.SignatureTest;
import threshold.mr04.data.PublicParameters;

import com.google.bitcoin.core.MakeCertificate;
import com.google.bitcoin.core.RemoteECKey;
import com.google.bitcoin.core.Utils;
import com.google.bitcoin.core.Wallet;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.EncodeHintType;

/**
 * This {@link Action} creates a new wallet.
 */
public class CreateRemoteWalletSubmitAction extends AbstractAction {

    private static final Logger log = LoggerFactory.getLogger(CreateRemoteWalletSubmitAction.class);

    private static final long serialVersionUID = 1923492460523457765L;

    private static final int TAM_QRCODE = 300;

    private static String KEYSTORE_FILENAME = "mykeystore.bks";
    private static String KEYSTORE_PASSWORD = "password";

    private final Controller controller;
    private final BitcoinController bitcoinController;

    private MultiBitFrame mainFrame;

    private Font adjustedFont;

    /**
     * Creates a new {@link CreateRemoteWalletSubmitAction}.
     */
    public CreateRemoteWalletSubmitAction(BitcoinController bitcoinController, ImageIcon icon,
            MultiBitFrame mainFrame) {
        super(bitcoinController.getLocaliser().getString("createNewRemoteWalletAction.text"), icon);

        this.bitcoinController = bitcoinController;
        this.controller = this.bitcoinController;
        this.mainFrame = mainFrame;

        MnemonicUtil mnemonicUtil = new MnemonicUtil(controller.getLocaliser());
        putValue(SHORT_DESCRIPTION, HelpContentsPanel.createTooltipTextForMenuItem(
                controller.getLocaliser().getString("createNewWalletAction.tooltip")));
        putValue(MNEMONIC_KEY, mnemonicUtil.getMnemonic("createNewWalletAction.text"));
    }

    /**
     * Create new wallet.
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        if (mainFrame != null) {
            mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        }
        setEnabled(false);

        try {
            // Create a file save dialog.

            JFileChooser.setDefaultLocale(controller.getLocaliser().getLocale());
            JFileChooser fileChooser = new JFileChooser();
            fileChooser.setLocale(controller.getLocaliser().getLocale());
            fileChooser.setDialogTitle(controller.getLocaliser().getString("createNewWalletAction.tooltip"));

            adjustedFont = FontSizer.INSTANCE.getAdjustedDefaultFont();
            if (adjustedFont != null) {
                setFileChooserFont(new Container[] { fileChooser });
            }
            fileChooser.applyComponentOrientation(
                    ComponentOrientation.getOrientation(controller.getLocaliser().getLocale()));
            if (this.bitcoinController.getModel().getActiveWalletFilename() != null) {
                fileChooser
                        .setCurrentDirectory(new File(this.bitcoinController.getModel().getActiveWalletFilename()));
            }
            fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            fileChooser.setFileFilter(new WalletFileFilter(controller));
            String defaultFileName = fileChooser.getCurrentDirectory().getAbsoluteFile() + File.separator
                    + controller.getLocaliser().getString("saveWalletAsView.untitled") + "."
                    + BitcoinModel.WALLET_FILE_EXTENSION;
            fileChooser.setSelectedFile(new File(defaultFileName));

            fileChooser.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            int returnVal = fileChooser.showSaveDialog(mainFrame);

            String newWalletFilename = null;
            if (returnVal == JFileChooser.APPROVE_OPTION) {
                File file = fileChooser.getSelectedFile();
                if (file != null) {
                    newWalletFilename = file.getAbsolutePath();
                    createNewWallet(newWalletFilename);
                }
            }
        } finally {
            setEnabled(true);
            if (mainFrame != null) {
                mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            }
        }
    }

    public void createNewWallet(String newWalletFilename) {
        String message;
        if (new File(newWalletFilename).isDirectory()) {
            message = controller.getLocaliser().getString("createNewWalletAction.walletFileIsADirectory",
                    new Object[] { newWalletFilename });
            log.debug(message);
            MessageManager.INSTANCE.addMessage(new Message(message));
            return;
        }

        // If the filename has no extension, put on the wallet extension.
        if (!newWalletFilename.contains(".")) {
            newWalletFilename = newWalletFilename + "." + BitcoinModel.WALLET_FILE_EXTENSION;
        }

        File newWalletFile = new File(newWalletFilename);

        boolean theWalletWasNotOpenedSuccessfully = false;

        try {
            // If file exists, load the existing wallet.
            if (newWalletFile.exists()) {
                WalletData perWalletModelData = this.bitcoinController.getFileHandler().loadFromFile(newWalletFile);
                if (perWalletModelData != null) {
                    // Use the existing wallet.
                    this.bitcoinController.addWalletFromFilename(newWalletFile.getAbsolutePath());
                    this.bitcoinController.getModel().setActiveWalletByFilename(newWalletFilename);
                    controller.getModel().setUserPreference(BitcoinModel.GRAB_FOCUS_FOR_ACTIVE_WALLET, "true");
                    controller.fireRecreateAllViews(true);
                    controller.fireDataChangedUpdateNow();
                }
            } else {
                // Create a new wallet - protobuf.2 initially for backwards compatibility.
                Wallet newWallet = new Wallet(this.bitcoinController.getModel().getNetworkParameters());

                SecureRandom prGen = new SecureRandom();
                byte[] oneTimePass = new byte[256];
                prGen.nextBytes(oneTimePass);

                SignatureTest t = new SignatureTest(2);
                PublicParameters params = new PublicParameters(SignatureTest.CURVE, t.nHat, t.kPrime, t.h1, t.h2,
                        t.alicesPallierPubKey, t.otherPallierPubKey);
                Alice alice = new Alice(t.aliceShare, t.publicKey, new SecureRandom(), t.paillier, params);

                File keystore = new File(KEYSTORE_FILENAME);

                Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
                KeyStore ks = KeyStore.getInstance("BKS");

                if (!keystore.exists()) {
                    MakeCertificate.generateSelfSignedCertificate("compTLSCert", keystore, KEYSTORE_PASSWORD);
                }

                ks.load(new FileInputStream(keystore), KEYSTORE_PASSWORD.toCharArray());

                X509Certificate cert = (X509Certificate) ks.getCertificate("compTLSCert");
                byte[] certBytes = cert.getEncoded();
                System.out.println("cert is " + certBytes.length + " bytes");

                byte[] fullBytes = Arrays.copyOf(oneTimePass, oneTimePass.length + certBytes.length);
                System.arraycopy(certBytes, 0, fullBytes, oneTimePass.length, certBytes.length);
                String fullString = DatatypeConverter.printBase64Binary(fullBytes);

                QRCodeWriter writer = new QRCodeWriter();
                Map<EncodeHintType, Object> hints = new EnumMap<EncodeHintType, Object>(EncodeHintType.class);
                hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
                hints.put(EncodeHintType.MARGIN, 0);
                BitMatrix matrix = writer.encode(fullString, BarcodeFormat.QR_CODE, 300, 300, hints);

                BufferedImage image = new BufferedImage(TAM_QRCODE, TAM_QRCODE, BufferedImage.TYPE_INT_RGB);
                image.createGraphics();

                Graphics2D graphics = (Graphics2D) image.getGraphics();
                graphics.setColor(Color.WHITE);
                graphics.fillRect(0, 0, TAM_QRCODE, TAM_QRCODE);
                graphics.setColor(Color.BLACK);

                for (int i = matrix.getTopLeftOnBit()[0]; i < TAM_QRCODE; i++) {
                    for (int j = matrix.getTopLeftOnBit()[1]; j < TAM_QRCODE; j++) {
                        if (matrix.get(i, j)) {
                            graphics.fillRect(i, j, 1, 1);
                        }
                    }
                }

                ByteArrayOutputStream os = new ByteArrayOutputStream();
                ImageIO.write(image, "png", os);

                Icon icon = new ImageIcon(image);

                JLabel iconLabel = new JLabel(icon);
                JPanel iconPanel = new JPanel(new GridBagLayout());
                iconPanel.add(iconLabel);

                JPanel mainPanel = new JPanel(new BorderLayout());
                JLabel label = new JLabel(
                        "Capture this image to pair with phone. Warning: This will never be shown again");
                Border paddingBorder = BorderFactory.createEmptyBorder(30, 10, 10, 10);
                label.setBorder(paddingBorder);
                mainPanel.add(label);
                mainPanel.add(iconPanel, BorderLayout.NORTH);
                log.debug("Showing wallet qr panel");
                JOptionPane.showMessageDialog(null, mainPanel, "Two Factor", JOptionPane.PLAIN_MESSAGE);
                log.debug("Showed wallet qr panel");

                log.debug("Trying to create ECKey");
                RemoteECKey newKey = new RemoteECKey(alice, params, t.bobShare, t.publicKey, oneTimePass, keystore,
                        KEYSTORE_PASSWORD);
                log.debug("Finished trying to create ECKey");
                String filename = Utils.bytesToHexString(newKey.getPubKeyHash());

                FileOutputStream fileOut = new FileOutputStream("/Users/hkalodner/btfa_work/" + filename + ".key");
                ObjectOutputStream out = new ObjectOutputStream(fileOut);
                out.writeObject(newKey);
                out.close();
                fileOut.close();
                log.debug("Created ECKey");

                newWallet.addKey(newKey);
                WalletData perWalletModelData = new WalletData();
                perWalletModelData.setWalletInfo(
                        new WalletInfoData(newWalletFilename, newWallet, MultiBitWalletVersion.PROTOBUF));
                perWalletModelData.setWallet(newWallet);
                perWalletModelData.setWalletFilename(newWalletFilename);
                perWalletModelData.setWalletDescription(
                        controller.getLocaliser().getString("createNewWalletSubmitAction.defaultDescription"));
                this.bitcoinController.getFileHandler().savePerWalletModelData(perWalletModelData, true);

                // Start using the new file as the wallet.
                this.bitcoinController.addWalletFromFilename(newWalletFile.getAbsolutePath());
                this.bitcoinController.getModel().setActiveWalletByFilename(newWalletFilename);
                controller.getModel().setUserPreference(BitcoinModel.GRAB_FOCUS_FOR_ACTIVE_WALLET, "true");

                // Save the user properties to disk.
                FileHandler.writeUserPreferences(this.bitcoinController);
                log.debug("User preferences with new wallet written successfully");

                // Backup the wallet and wallet info.
                BackupManager.INSTANCE.backupPerWalletModelData(bitcoinController.getFileHandler(),
                        perWalletModelData);

                controller.fireRecreateAllViews(true);
                controller.fireDataChangedUpdateNow();
            }
        } catch (WalletLoadException e) {
            e.printStackTrace();
            message = controller.getLocaliser().getString("createNewWalletAction.walletCouldNotBeCreated",
                    new Object[] { newWalletFilename, e.getMessage() });
            log.error(message);
            MessageManager.INSTANCE.addMessage(new Message(message));
            theWalletWasNotOpenedSuccessfully = true;
        } catch (WalletSaveException e) {
            e.printStackTrace();
            message = controller.getLocaliser().getString("createNewWalletAction.walletCouldNotBeCreated",
                    new Object[] { newWalletFilename, e.getMessage() });
            log.error(message);
            MessageManager.INSTANCE.addMessage(new Message(message));
            theWalletWasNotOpenedSuccessfully = true;
        } catch (WalletVersionException e) {
            e.printStackTrace();
            message = controller.getLocaliser().getString("createNewWalletAction.walletCouldNotBeCreated",
                    new Object[] { newWalletFilename, e.getMessage() });
            log.error(message);
            MessageManager.INSTANCE.addMessage(new Message(message));
            theWalletWasNotOpenedSuccessfully = true;
        } catch (IOException e) {
            e.printStackTrace();
            message = controller.getLocaliser().getString("createNewWalletAction.walletCouldNotBeCreated",
                    new Object[] { newWalletFilename, e.getMessage() });
            log.error(message);
            MessageManager.INSTANCE.addMessage(new Message(message));
            theWalletWasNotOpenedSuccessfully = true;
        } catch (KeyStoreException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (CertificateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (WriterException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        if (theWalletWasNotOpenedSuccessfully) {
            WalletData loopData = this.bitcoinController.getModel()
                    .getPerWalletModelDataByWalletFilename(newWalletFilename);
            if (loopData != null) {
                // Clear the backup wallet filename - this prevents it being automatically overwritten.
                if (loopData.getWalletInfo() != null) {
                    loopData.getWalletInfo().put(BitcoinModel.WALLET_BACKUP_FILE, "");
                }
            }
        }
    }

    private void setFileChooserFont(Component[] comp) {
        for (int x = 0; x < comp.length; x++) {
            if (comp[x] instanceof Container)
                setFileChooserFont(((Container) comp[x]).getComponents());
            try {
                comp[x].setFont(adjustedFont);
            } catch (Exception e) {
            } // do nothing
        }
    }

}