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());
}
}
}
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);
}
}
}
}
}
}
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;
}
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;
}
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);
}
}
Aggregations