Search in sources :

Example 1 with KeyPurpose

use of com.sparrowwallet.drongo.KeyPurpose in project sparrow by sparrowwallet.

the class AddressesController method exportAddresses.

private void exportAddresses(KeyPurpose keyPurpose) {
    Stage window = new Stage();
    FileChooser fileChooser = new FileChooser();
    fileChooser.setTitle("Export Addresses to CSV");
    fileChooser.setInitialFileName(getWalletForm().getWallet().getFullName() + "-" + keyPurpose.name().toLowerCase() + "-addresses.csv");
    Wallet copy = getWalletForm().getWallet().copy();
    WalletNode purposeNode = copy.getNode(keyPurpose);
    purposeNode.fillToIndex(Math.max(purposeNode.getChildren().size(), DEFAULT_EXPORT_ADDRESSES_LENGTH));
    AppServices.moveToActiveWindowScreen(window, 800, 450);
    File file = fileChooser.showSaveDialog(window);
    if (file != null) {
        try (FileOutputStream outputStream = new FileOutputStream(file)) {
            CsvWriter writer = new CsvWriter(outputStream, ',', StandardCharsets.UTF_8);
            writer.writeRecord(new String[] { "Index", "Payment Address", "Derivation", "Label" });
            for (WalletNode indexNode : purposeNode.getChildren()) {
                writer.write(Integer.toString(indexNode.getIndex()));
                writer.write(indexNode.getAddress().toString());
                writer.write(getDerivationPath(indexNode));
                Optional<Entry> optLabelEntry = getWalletForm().getNodeEntry(keyPurpose).getChildren().stream().filter(entry -> ((NodeEntry) entry).getNode().getIndex() == indexNode.getIndex()).findFirst();
                writer.write(optLabelEntry.isPresent() ? optLabelEntry.get().getLabel() : "");
                writer.endRecord();
            }
            writer.close();
        } catch (IOException e) {
            log.error("Error exporting addresses as CSV", e);
            AppServices.showErrorDialog("Error exporting addresses as CSV", e.getMessage());
        }
    }
}
Also used : Button(javafx.scene.control.Button) Initializable(javafx.fxml.Initializable) Logger(org.slf4j.Logger) Wallet(com.sparrowwallet.drongo.wallet.Wallet) URL(java.net.URL) com.sparrowwallet.sparrow.event(com.sparrowwallet.sparrow.event) LoggerFactory(org.slf4j.LoggerFactory) AddressTreeTable(com.sparrowwallet.sparrow.control.AddressTreeTable) StandardCharsets(java.nio.charset.StandardCharsets) FXML(javafx.fxml.FXML) FileChooser(javafx.stage.FileChooser) ActionEvent(javafx.event.ActionEvent) List(java.util.List) AppServices(com.sparrowwallet.sparrow.AppServices) KeyPurpose(com.sparrowwallet.drongo.KeyPurpose) Stage(javafx.stage.Stage) java.io(java.io) ResourceBundle(java.util.ResourceBundle) CsvWriter(com.csvreader.CsvWriter) EventManager(com.sparrowwallet.sparrow.EventManager) Optional(java.util.Optional) Subscribe(com.google.common.eventbus.Subscribe) WalletNode(com.sparrowwallet.drongo.wallet.WalletNode) PayNymAddressesDialog(com.sparrowwallet.sparrow.paynym.PayNymAddressesDialog) CsvWriter(com.csvreader.CsvWriter) Wallet(com.sparrowwallet.drongo.wallet.Wallet) FileChooser(javafx.stage.FileChooser) Stage(javafx.stage.Stage) WalletNode(com.sparrowwallet.drongo.wallet.WalletNode)

Example 2 with KeyPurpose

use of com.sparrowwallet.drongo.KeyPurpose in project sparrow by sparrowwallet.

the class WalletTransactionsEntry method getWalletTransactions.

private static void getWalletTransactions(Wallet wallet, Map<BlockTransaction, WalletTransaction> walletTransactionMap, WalletNode purposeNode) {
    KeyPurpose keyPurpose = purposeNode.getKeyPurpose();
    List<WalletNode> childNodes = new ArrayList<>(purposeNode.getChildren());
    for (WalletNode addressNode : childNodes) {
        for (BlockTransactionHashIndex hashIndex : addressNode.getTransactionOutputs()) {
            BlockTransaction inputTx = wallet.getWalletTransaction(hashIndex.getHash());
            // A null inputTx here means the wallet is still updating - ignore as the WalletHistoryChangedEvent will run this again
            if (inputTx != null) {
                WalletTransaction inputWalletTx = walletTransactionMap.get(inputTx);
                if (inputWalletTx == null) {
                    inputWalletTx = new WalletTransaction(wallet, inputTx);
                    walletTransactionMap.put(inputTx, inputWalletTx);
                }
                inputWalletTx.incoming.put(hashIndex, keyPurpose);
                if (hashIndex.getSpentBy() != null) {
                    BlockTransaction outputTx = wallet.getWalletTransaction(hashIndex.getSpentBy().getHash());
                    if (outputTx != null) {
                        WalletTransaction outputWalletTx = walletTransactionMap.get(outputTx);
                        if (outputWalletTx == null) {
                            outputWalletTx = new WalletTransaction(wallet, outputTx);
                            walletTransactionMap.put(outputTx, outputWalletTx);
                        }
                        outputWalletTx.outgoing.put(hashIndex.getSpentBy(), keyPurpose);
                    }
                }
            }
        }
    }
}
Also used : BlockTransactionHashIndex(com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex) KeyPurpose(com.sparrowwallet.drongo.KeyPurpose) BlockTransaction(com.sparrowwallet.drongo.wallet.BlockTransaction) WalletNode(com.sparrowwallet.drongo.wallet.WalletNode)

Example 3 with KeyPurpose

use of com.sparrowwallet.drongo.KeyPurpose in project sparrow by sparrowwallet.

the class WalletTransactionsEntry method getWalletTransactions.

private static Collection<WalletTransaction> getWalletTransactions(Wallet wallet) {
    Map<BlockTransaction, WalletTransaction> walletTransactionMap = new HashMap<>(wallet.getTransactions().size());
    for (KeyPurpose keyPurpose : wallet.getWalletKeyPurposes()) {
        getWalletTransactions(wallet, walletTransactionMap, wallet.getNode(keyPurpose));
    }
    for (Wallet childWallet : wallet.getChildWallets()) {
        if (childWallet.isNested()) {
            for (KeyPurpose keyPurpose : childWallet.getWalletKeyPurposes()) {
                getWalletTransactions(childWallet, walletTransactionMap, childWallet.getNode(keyPurpose));
            }
        }
    }
    List<WalletTransaction> walletTransactions = new ArrayList<>(walletTransactionMap.values());
    Collections.sort(walletTransactions);
    return walletTransactions;
}
Also used : Wallet(com.sparrowwallet.drongo.wallet.Wallet) KeyPurpose(com.sparrowwallet.drongo.KeyPurpose) BlockTransaction(com.sparrowwallet.drongo.wallet.BlockTransaction)

Example 4 with KeyPurpose

use of com.sparrowwallet.drongo.KeyPurpose in project sparrow by sparrowwallet.

the class ElectrumServer method getHistory.

public Map<WalletNode, Set<BlockTransactionHash>> getHistory(Wallet wallet, Collection<WalletNode> nodes) throws ServerException {
    Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap = new TreeMap<>();
    Set<WalletNode> historyNodes = new HashSet<>(nodes);
    // Add any nodes with mempool transactions in case these have been replaced
    Set<WalletNode> mempoolNodes = wallet.getWalletTxos().entrySet().stream().filter(entry -> entry.getKey().getHeight() <= 0 || (entry.getKey().getSpentBy() != null && entry.getKey().getSpentBy().getHeight() <= 0)).map(Map.Entry::getValue).collect(Collectors.toSet());
    historyNodes.addAll(mempoolNodes);
    subscribeWalletNodes(wallet, historyNodes, nodeTransactionMap, 0);
    getReferences(wallet, nodeTransactionMap.keySet(), nodeTransactionMap, 0);
    Set<BlockTransactionHash> newReferences = nodeTransactionMap.values().stream().flatMap(Collection::stream).filter(ref -> !wallet.getTransactions().containsKey(ref.getHash())).collect(Collectors.toSet());
    getReferencedTransactions(wallet, nodeTransactionMap);
    // Subscribe and retrieve transaction history from child nodes if necessary to maintain gap limit
    Set<KeyPurpose> keyPurposes = nodes.stream().map(WalletNode::getKeyPurpose).collect(Collectors.toUnmodifiableSet());
    for (KeyPurpose keyPurpose : keyPurposes) {
        WalletNode purposeNode = wallet.getNode(keyPurpose);
        getHistoryToGapLimit(wallet, nodeTransactionMap, purposeNode);
    }
    log.debug("Fetched nodes history for: " + nodeTransactionMap.keySet());
    if (!newReferences.isEmpty()) {
        // Look for additional nodes to fetch history for by considering the inputs and outputs of new transactions found
        log.debug(wallet.getFullName() + " found new transactions: " + newReferences);
        Set<WalletNode> additionalNodes = new HashSet<>();
        Map<String, WalletNode> walletScriptHashes = getAllScriptHashes(wallet);
        for (BlockTransactionHash reference : newReferences) {
            BlockTransaction blockTransaction = wallet.getTransactions().get(reference.getHash());
            for (TransactionOutput txOutput : blockTransaction.getTransaction().getOutputs()) {
                WalletNode node = walletScriptHashes.get(getScriptHash(txOutput));
                if (node != null && !historyNodes.contains(node)) {
                    additionalNodes.add(node);
                }
            }
            for (TransactionInput txInput : blockTransaction.getTransaction().getInputs()) {
                BlockTransaction inputBlockTransaction = wallet.getTransactions().get(txInput.getOutpoint().getHash());
                if (inputBlockTransaction != null) {
                    TransactionOutput txOutput = inputBlockTransaction.getTransaction().getOutputs().get((int) txInput.getOutpoint().getIndex());
                    WalletNode node = walletScriptHashes.get(getScriptHash(txOutput));
                    if (node != null && !historyNodes.contains(node)) {
                        additionalNodes.add(node);
                    }
                }
            }
        }
        if (!additionalNodes.isEmpty()) {
            log.debug("Found additional nodes: " + additionalNodes);
            subscribeWalletNodes(wallet, additionalNodes, nodeTransactionMap, 0);
            getReferences(wallet, additionalNodes, nodeTransactionMap, 0);
            getReferencedTransactions(wallet, nodeTransactionMap);
        }
    }
    return nodeTransactionMap;
}
Also used : Address(com.sparrowwallet.drongo.address.Address) java.util(java.util) PayNym(com.sparrowwallet.sparrow.paynym.PayNym) com.sparrowwallet.drongo.wallet(com.sparrowwallet.drongo.wallet) LoggerFactory(org.slf4j.LoggerFactory) Config(com.sparrowwallet.sparrow.io.Config) IntegerProperty(javafx.beans.property.IntegerProperty) Task(javafx.concurrent.Task) Matcher(java.util.regex.Matcher) Utils(com.sparrowwallet.drongo.Utils) SimpleIntegerProperty(javafx.beans.property.SimpleIntegerProperty) com.sparrowwallet.drongo.protocol(com.sparrowwallet.drongo.protocol) Subscribe(com.google.common.eventbus.Subscribe) Logger(org.slf4j.Logger) ReentrantLock(java.util.concurrent.locks.ReentrantLock) ScheduledService(javafx.concurrent.ScheduledService) com.sparrowwallet.sparrow.event(com.sparrowwallet.sparrow.event) InvalidPaymentCodeException(com.sparrowwallet.drongo.bip47.InvalidPaymentCodeException) Service(javafx.concurrent.Service) HostAndPort(com.google.common.net.HostAndPort) Collectors(java.util.stream.Collectors) StandardCharsets(java.nio.charset.StandardCharsets) Platform(javafx.application.Platform) Transport(com.github.arteam.simplejsonrpc.client.Transport) Condition(java.util.concurrent.locks.Condition) AppServices(com.sparrowwallet.sparrow.AppServices) KeyPurpose(com.sparrowwallet.drongo.KeyPurpose) Network(com.sparrowwallet.drongo.Network) java.io(java.io) EventManager(com.sparrowwallet.sparrow.EventManager) Pattern(java.util.regex.Pattern) PaymentCode(com.sparrowwallet.drongo.bip47.PaymentCode) KeyPurpose(com.sparrowwallet.drongo.KeyPurpose)

Example 5 with KeyPurpose

use of com.sparrowwallet.drongo.KeyPurpose in project sparrow by sparrowwallet.

the class Electrum method importWallet.

@Override
public Wallet importWallet(InputStream inputStream, String password) throws ImportException {
    Reader reader;
    if (password != null) {
        ECKey decryptionKey = Pbkdf2KeyDeriver.DEFAULT_INSTANCE.deriveECKey(password);
        reader = new InputStreamReader(new InflaterInputStream(new ECIESInputStream(inputStream, decryptionKey)), StandardCharsets.UTF_8);
    } else {
        reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
    }
    try {
        Gson gson = new Gson();
        Type stringStringMap = new TypeToken<Map<String, JsonElement>>() {
        }.getType();
        Map<String, JsonElement> map = gson.fromJson(reader, stringStringMap);
        ElectrumJsonWallet ew = new ElectrumJsonWallet();
        if (map.get("wallet_type") == null) {
            throw new ImportException("File was not a valid Electrum wallet");
        }
        ew.wallet_type = map.get("wallet_type").getAsString();
        for (String key : map.keySet()) {
            if (key.startsWith("x") || key.equals("keystore")) {
                ElectrumKeystore ek = gson.fromJson(map.get(key), ElectrumKeystore.class);
                if (ek.root_fingerprint == null && ek.ckcc_xfp != null) {
                    byte[] le = new byte[4];
                    Utils.uint32ToByteArrayLE(Long.parseLong(ek.ckcc_xfp), le, 0);
                    ek.root_fingerprint = Utils.bytesToHex(le).toUpperCase();
                }
                ew.keystores.put(key, ek);
            }
            if (key.equals("labels")) {
                JsonObject jsonObject = (JsonObject) map.get(key);
                for (String labelKey : jsonObject.keySet()) {
                    ew.labels.put(labelKey, jsonObject.get(labelKey).getAsString());
                }
            }
            if (key.equals("addresses")) {
                ew.addresses = gson.fromJson(map.get(key), ElectrumAddresses.class);
            }
            if (key.equals("verified_tx3")) {
                JsonObject jsonObject = (JsonObject) map.get(key);
                for (String txKey : jsonObject.keySet()) {
                    Sha256Hash txHash = Sha256Hash.wrap(txKey);
                    JsonArray array = jsonObject.getAsJsonArray(txKey);
                    if (array != null && array.size() > 3) {
                        int height = array.get(0).getAsInt();
                        Date date = new Date(array.get(1).getAsLong() * 1000);
                        long fee = array.get(2).getAsLong();
                        Sha256Hash blockHash = Sha256Hash.wrap(array.get(3).getAsString());
                        JsonObject transactions = (JsonObject) map.get("transactions");
                        if (transactions != null) {
                            String txhex = transactions.get(txKey).getAsString();
                            if (txhex != null) {
                                Transaction transaction = new Transaction(Utils.hexToBytes(txhex));
                                BlockTransaction blockTransaction = new BlockTransaction(txHash, height, date, fee, transaction, blockHash);
                                ew.transactions.put(txHash, blockTransaction);
                            }
                        }
                    }
                }
            }
        }
        Wallet wallet = new Wallet();
        ScriptType scriptType = null;
        for (ElectrumKeystore ek : ew.keystores.values()) {
            Keystore keystore = new Keystore();
            ExtendedKey xPub = ExtendedKey.fromDescriptor(ek.xpub);
            String derivationPath = ek.derivation;
            if (derivationPath == null) {
                derivationPath = "m/0";
            }
            String masterFingerprint = ek.root_fingerprint;
            if (masterFingerprint == null) {
                masterFingerprint = Utils.bytesToHex(xPub.getParentFingerprint());
            }
            if ("hardware".equals(ek.type)) {
                keystore.setSource(KeystoreSource.HW_USB);
                keystore.setWalletModel(WalletModel.fromType(ek.hw_type));
                if (keystore.getWalletModel() == null) {
                    throw new ImportException("Wallet has keystore of unknown hardware wallet type \"" + ek.hw_type + "\".");
                }
                if (keystore.getWalletModel().equals(WalletModel.TREZOR_1)) {
                    keystore.setWalletModel(WalletModel.TREZOR_T);
                }
            } else if ("bip32".equals(ek.type)) {
                if (ek.xprv != null && ek.seed == null) {
                    throw new ImportException("Electrum does not support exporting BIP39 derived seeds, as it does not store the mnemonic words. Only seeds created with its native Electrum Seed Version System are exportable. " + "If you have the mnemonic words, create a new wallet with a BIP39 keystore.");
                } else if (ek.seed != null) {
                    keystore.setSource(KeystoreSource.SW_SEED);
                    String mnemonic = ek.seed;
                    String passphrase = ek.passphrase;
                    if (password != null) {
                        mnemonic = decrypt(mnemonic, password);
                        passphrase = decrypt(passphrase, password);
                    }
                    keystore.setSeed(new DeterministicSeed(mnemonic, passphrase, 0, DeterministicSeed.Type.ELECTRUM));
                    // Ensure the derivation path from the seed matches the provided xpub
                    String[] possibleDerivations = { "m", "m/0", "m/0'" };
                    for (String possibleDerivation : possibleDerivations) {
                        List<ChildNumber> derivation = KeyDerivation.parsePath(possibleDerivation);
                        DeterministicKey derivedKey = keystore.getExtendedMasterPrivateKey().getKey(derivation);
                        DeterministicKey derivedKeyPublicOnly = derivedKey.dropPrivateBytes().dropParent();
                        ExtendedKey xpub = new ExtendedKey(derivedKeyPublicOnly, derivedKey.getParentFingerprint(), derivation.isEmpty() ? ChildNumber.ZERO : derivation.get(derivation.size() - 1));
                        if (xpub.equals(xPub)) {
                            derivationPath = possibleDerivation;
                            break;
                        }
                    }
                } else {
                    keystore.setSource(KeystoreSource.SW_WATCH);
                }
                keystore.setWalletModel(WalletModel.ELECTRUM);
            }
            keystore.setKeyDerivation(new KeyDerivation(masterFingerprint, derivationPath));
            keystore.setExtendedPublicKey(xPub);
            keystore.setLabel(ek.label != null ? ek.label : "Electrum");
            if (keystore.getLabel().length() > Keystore.MAX_LABEL_LENGTH) {
                keystore.setLabel(keystore.getLabel().substring(0, Keystore.MAX_LABEL_LENGTH));
            }
            wallet.getKeystores().add(keystore);
            ExtendedKey.Header xpubHeader = ExtendedKey.Header.fromExtendedKey(ek.xpub);
            scriptType = xpubHeader.getDefaultScriptType();
        }
        wallet.setScriptType(scriptType);
        if (ew.wallet_type.equals("standard")) {
            wallet.setPolicyType(PolicyType.SINGLE);
            wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, scriptType, wallet.getKeystores(), 1));
        } else if (ew.wallet_type.contains("of")) {
            wallet.setPolicyType(PolicyType.MULTI);
            String[] mOfn = ew.wallet_type.split("of");
            int threshold = Integer.parseInt(mOfn[0]);
            wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.MULTI, scriptType, wallet.getKeystores(), threshold));
        } else {
            throw new ImportException("Unknown Electrum wallet type of " + ew.wallet_type);
        }
        for (String key : ew.labels.keySet()) {
            try {
                Sha256Hash txHash = Sha256Hash.wrap(key);
                BlockTransaction blockTransaction = ew.transactions.get(txHash);
                if (blockTransaction != null) {
                    blockTransaction.setLabel(ew.labels.get(key));
                }
            } catch (Exception e) {
                // not a tx - try an address
                if (ew.addresses != null) {
                    try {
                        Address address = Address.fromString(key);
                        Map<KeyPurpose, List<String>> keyPurposes = Map.of(KeyPurpose.RECEIVE, ew.addresses.receiving, KeyPurpose.CHANGE, ew.addresses.change);
                        for (KeyPurpose keyPurpose : keyPurposes.keySet()) {
                            WalletNode purposeNode = wallet.getNode(keyPurpose);
                            purposeNode.fillToIndex(keyPurposes.get(keyPurpose).size() - 1);
                            for (WalletNode addressNode : purposeNode.getChildren()) {
                                if (address.equals(addressNode.getAddress())) {
                                    addressNode.setLabel(ew.labels.get(key));
                                }
                            }
                        }
                        for (BlockTransaction blkTx : ew.transactions.values()) {
                            if (blkTx.getLabel() == null) {
                                Transaction tx = blkTx.getTransaction();
                                for (TransactionOutput txOutput : tx.getOutputs()) {
                                    try {
                                        Address[] addresses = txOutput.getScript().getToAddresses();
                                        if (Arrays.asList(addresses).contains(address)) {
                                            blkTx.setLabel(ew.labels.get(key));
                                        }
                                    } catch (NonStandardScriptException ex) {
                                    // ignore
                                    }
                                }
                            }
                        }
                    } catch (Exception ex) {
                    // not an address
                    }
                }
            }
        }
        wallet.updateTransactions(ew.transactions);
        try {
            wallet.checkWallet();
        } catch (InvalidWalletException e) {
            throw new IllegalStateException("Imported Electrum wallet was invalid: " + e.getMessage());
        }
        return wallet;
    } catch (Exception e) {
        throw new ImportException("Error importing Electrum Wallet", e);
    }
}
Also used : Address(com.sparrowwallet.drongo.address.Address) KeyPurpose(com.sparrowwallet.drongo.KeyPurpose) InflaterInputStream(java.util.zip.InflaterInputStream) KeyDerivation(com.sparrowwallet.drongo.KeyDerivation) PolicyType(com.sparrowwallet.drongo.policy.PolicyType) Type(java.lang.reflect.Type) ExtendedKey(com.sparrowwallet.drongo.ExtendedKey)

Aggregations

KeyPurpose (com.sparrowwallet.drongo.KeyPurpose)5 Subscribe (com.google.common.eventbus.Subscribe)2 Address (com.sparrowwallet.drongo.address.Address)2 BlockTransaction (com.sparrowwallet.drongo.wallet.BlockTransaction)2 Wallet (com.sparrowwallet.drongo.wallet.Wallet)2 WalletNode (com.sparrowwallet.drongo.wallet.WalletNode)2 AppServices (com.sparrowwallet.sparrow.AppServices)2 EventManager (com.sparrowwallet.sparrow.EventManager)2 com.sparrowwallet.sparrow.event (com.sparrowwallet.sparrow.event)2 java.io (java.io)2 StandardCharsets (java.nio.charset.StandardCharsets)2 Logger (org.slf4j.Logger)2 LoggerFactory (org.slf4j.LoggerFactory)2 CsvWriter (com.csvreader.CsvWriter)1 Transport (com.github.arteam.simplejsonrpc.client.Transport)1 HostAndPort (com.google.common.net.HostAndPort)1 ExtendedKey (com.sparrowwallet.drongo.ExtendedKey)1 KeyDerivation (com.sparrowwallet.drongo.KeyDerivation)1 Network (com.sparrowwallet.drongo.Network)1 Utils (com.sparrowwallet.drongo.Utils)1