Search in sources :

Example 26 with ECKey

use of com.sparrowwallet.drongo.crypto.ECKey in project sparrow by sparrowwallet.

the class SendController method broadcastNotification.

public void broadcastNotification(Wallet decryptedWallet) {
    try {
        PaymentCode paymentCode = decryptedWallet.getPaymentCode();
        PaymentCode externalPaymentCode = paymentCodeProperty.get();
        WalletTransaction walletTransaction = walletTransactionProperty.get();
        WalletNode input0Node = walletTransaction.getSelectedUtxos().entrySet().iterator().next().getValue();
        Keystore keystore = input0Node.getWallet().isNested() ? decryptedWallet.getChildWallet(input0Node.getWallet().getName()).getKeystores().get(0) : decryptedWallet.getKeystores().get(0);
        ECKey input0Key = keystore.getKey(input0Node);
        TransactionOutPoint input0Outpoint = walletTransaction.getTransaction().getInputs().iterator().next().getOutpoint();
        SecretPoint secretPoint = new SecretPoint(input0Key.getPrivKeyBytes(), externalPaymentCode.getNotificationKey().getPubKey());
        byte[] blindingMask = PaymentCode.getMask(secretPoint.ECDHSecretAsBytes(), input0Outpoint.bitcoinSerialize());
        byte[] blindedPaymentCode = PaymentCode.blind(paymentCode.getPayload(), blindingMask);
        List<UtxoSelector> utxoSelectors = List.of(new PresetUtxoSelector(walletTransaction.getSelectedUtxos().keySet(), true));
        Long userFee = userFeeSet.get() ? getFeeValueSats() : null;
        double feeRate = getUserFeeRate();
        Integer currentBlockHeight = AppServices.getCurrentBlockHeight();
        boolean groupByAddress = Config.get().isGroupByAddress();
        boolean includeMempoolOutputs = Config.get().isIncludeMempoolOutputs();
        boolean includeSpentMempoolOutputs = includeSpentMempoolOutputsProperty.get();
        WalletTransaction finalWalletTx = decryptedWallet.createWalletTransaction(utxoSelectors, getUtxoFilters(), walletTransaction.getPayments(), List.of(blindedPaymentCode), excludedChangeNodes, feeRate, getMinimumFeeRate(), userFee, currentBlockHeight, groupByAddress, includeMempoolOutputs, includeSpentMempoolOutputs);
        PSBT psbt = finalWalletTx.createPSBT();
        decryptedWallet.sign(psbt);
        decryptedWallet.finalise(psbt);
        Transaction transaction = psbt.extractTransaction();
        ServiceProgressDialog.ProxyWorker proxyWorker = new ServiceProgressDialog.ProxyWorker();
        ElectrumServer.BroadcastTransactionService broadcastTransactionService = new ElectrumServer.BroadcastTransactionService(transaction);
        broadcastTransactionService.setOnSucceeded(successEvent -> {
            ElectrumServer.TransactionMempoolService transactionMempoolService = new ElectrumServer.TransactionMempoolService(walletTransaction.getWallet(), transaction.getTxId(), new HashSet<>(walletTransaction.getSelectedUtxos().values()));
            transactionMempoolService.setDelay(Duration.seconds(2));
            transactionMempoolService.setPeriod(Duration.seconds(5));
            transactionMempoolService.setRestartOnFailure(false);
            transactionMempoolService.setOnSucceeded(mempoolWorkerStateEvent -> {
                Set<String> scriptHashes = transactionMempoolService.getValue();
                if (!scriptHashes.isEmpty()) {
                    transactionMempoolService.cancel();
                    clear(null);
                    if (Config.get().isUsePayNym()) {
                        proxyWorker.setMessage("Finding PayNym...");
                        AppServices.getPayNymService().getPayNym(externalPaymentCode.toString()).subscribe(payNym -> {
                            proxyWorker.end();
                            addChildWallets(walletTransaction.getWallet(), externalPaymentCode, transaction, payNym);
                        }, error -> {
                            proxyWorker.end();
                            addChildWallets(walletTransaction.getWallet(), externalPaymentCode, transaction, null);
                        });
                    } else {
                        proxyWorker.end();
                        addChildWallets(walletTransaction.getWallet(), externalPaymentCode, transaction, null);
                    }
                }
                if (transactionMempoolService.getIterationCount() > 5 && transactionMempoolService.isRunning()) {
                    transactionMempoolService.cancel();
                    proxyWorker.end();
                    log.error("Timeout searching for broadcasted notification transaction");
                    AppServices.showErrorDialog("Timeout searching for broadcasted transaction", "The transaction was broadcast but the server did not register it in the mempool. It is safe to try broadcasting again.");
                }
            });
            transactionMempoolService.setOnFailed(mempoolWorkerStateEvent -> {
                transactionMempoolService.cancel();
                proxyWorker.end();
                log.error("Error searching for broadcasted notification transaction", mempoolWorkerStateEvent.getSource().getException());
                AppServices.showErrorDialog("Timeout searching for broadcasted transaction", "The transaction was broadcast but the server did not register it in the mempool. It is safe to try broadcasting again.");
            });
            proxyWorker.setMessage("Receiving notification transaction...");
            transactionMempoolService.start();
        });
        broadcastTransactionService.setOnFailed(failedEvent -> {
            proxyWorker.end();
            log.error("Error broadcasting notification transaction", failedEvent.getSource().getException());
            AppServices.showErrorDialog("Error broadcasting notification transaction", failedEvent.getSource().getException().getMessage());
        });
        ServiceProgressDialog progressDialog = new ServiceProgressDialog("Broadcast", "Broadcast Notification Transaction", "/image/paynym.png", proxyWorker);
        AppServices.moveToActiveWindowScreen(progressDialog);
        proxyWorker.setMessage("Broadcasting notification transaction...");
        proxyWorker.start();
        broadcastTransactionService.start();
    } catch (Exception e) {
        log.error("Error creating notification transaction", e);
        AppServices.showErrorDialog("Error creating notification transaction", e.getMessage());
    }
}
Also used : ECKey(com.sparrowwallet.drongo.crypto.ECKey) SecureString(com.sparrowwallet.drongo.SecureString) SecretPoint(com.sparrowwallet.drongo.bip47.SecretPoint) PaymentCode(com.sparrowwallet.drongo.bip47.PaymentCode) InvalidAddressException(com.sparrowwallet.drongo.address.InvalidAddressException) IOException(java.io.IOException) PSBT(com.sparrowwallet.drongo.psbt.PSBT) Transaction(com.sparrowwallet.drongo.protocol.Transaction) TransactionOutPoint(com.sparrowwallet.drongo.protocol.TransactionOutPoint)

Example 27 with ECKey

use of com.sparrowwallet.drongo.crypto.ECKey in project sparrow by sparrowwallet.

the class DbPersistence method loadChildWallets.

private Map<WalletAndKey, Storage> loadChildWallets(Storage storage, Wallet masterWallet, ECKey encryptionKey) throws StorageException {
    Jdbi jdbi = getJdbi(storage, getFilePassword(encryptionKey));
    List<String> schemas = jdbi.withHandle(handle -> {
        return handle.createQuery("show schemas").mapTo(String.class).list();
    });
    List<String> childSchemas = schemas.stream().filter(schema -> schema.startsWith(WALLET_SCHEMA_PREFIX) && !schema.equals(MASTER_SCHEMA)).collect(Collectors.toList());
    Map<WalletAndKey, Storage> childWallets = new TreeMap<>();
    for (String schema : childSchemas) {
        migrate(storage, schema, encryptionKey);
        Jdbi childJdbi = getJdbi(storage, getFilePassword(encryptionKey));
        Wallet wallet = childJdbi.withHandle(handle -> {
            WalletDao walletDao = handle.attach(WalletDao.class);
            Wallet childWallet = walletDao.getMainWallet(schema);
            childWallet.setName(schema.substring(WALLET_SCHEMA_PREFIX.length()));
            childWallet.setMasterWallet(masterWallet);
            return childWallet;
        });
        childWallets.put(new WalletAndKey(wallet, encryptionKey, keyDeriver, Collections.emptyMap()), storage);
    }
    return childWallets;
}
Also used : java.util(java.util) FlywayException(org.flywaydb.core.api.FlywayException) AsymmetricKeyDeriver(com.sparrowwallet.drongo.crypto.AsymmetricKeyDeriver) com.sparrowwallet.drongo.wallet(com.sparrowwallet.drongo.wallet) LoggerFactory(org.slf4j.LoggerFactory) ByteBuffer(java.nio.ByteBuffer) StandardCopyOption(java.nio.file.StandardCopyOption) SecureRandom(java.security.SecureRandom) Files(com.google.common.io.Files) Utils(com.sparrowwallet.drongo.Utils) InvalidPasswordException(com.sparrowwallet.drongo.crypto.InvalidPasswordException) Subscribe(com.google.common.eventbus.Subscribe) H2DatabasePlugin(org.jdbi.v3.core.h2.H2DatabasePlugin) ExecutorService(java.util.concurrent.ExecutorService) com.sparrowwallet.sparrow.wallet(com.sparrowwallet.sparrow.wallet) Jdbi(org.jdbi.v3.core.Jdbi) Argon2KeyDeriver(com.sparrowwallet.drongo.crypto.Argon2KeyDeriver) SqlObjectPlugin(org.jdbi.v3.sqlobject.SqlObjectPlugin) Logger(org.slf4j.Logger) com.sparrowwallet.sparrow.event(com.sparrowwallet.sparrow.event) BasicThreadFactory(org.apache.commons.lang3.concurrent.BasicThreadFactory) Collectors(java.util.stream.Collectors) StandardCharsets(java.nio.charset.StandardCharsets) Executors(java.util.concurrent.Executors) ChangeFileEncryption(org.h2.tools.ChangeFileEncryption) TimeUnit(java.util.concurrent.TimeUnit) HikariConfig(com.zaxxer.hikari.HikariConfig) Stream(java.util.stream.Stream) java.io(java.io) com.sparrowwallet.sparrow.io(com.sparrowwallet.sparrow.io) HikariDataSource(com.zaxxer.hikari.HikariDataSource) EventManager(com.sparrowwallet.sparrow.EventManager) ECKey(com.sparrowwallet.drongo.crypto.ECKey) Flyway(org.flywaydb.core.Flyway) Sha256Hash(com.sparrowwallet.drongo.protocol.Sha256Hash) HikariPool(com.zaxxer.hikari.pool.HikariPool) FlywayValidateException(org.flywaydb.core.api.exception.FlywayValidateException) FileChannel(java.nio.channels.FileChannel) Jdbi(org.jdbi.v3.core.Jdbi)

Example 28 with ECKey

use of com.sparrowwallet.drongo.crypto.ECKey in project sparrow by sparrowwallet.

the class JsonPersistence method loadWallet.

@Override
public WalletAndKey loadWallet(Storage storage, CharSequence password, ECKey alreadyDerivedKey) throws IOException, StorageException {
    Wallet wallet;
    ECKey encryptionKey;
    try (InputStream fileStream = new FileInputStream(storage.getWalletFile())) {
        encryptionKey = getEncryptionKey(password, fileStream, alreadyDerivedKey);
        Reader reader = new InputStreamReader(new InflaterInputStream(new ECIESInputStream(fileStream, encryptionKey, getEncryptionMagic())), StandardCharsets.UTF_8);
        wallet = gson.fromJson(reader, Wallet.class);
        wallet.getPurposeNodes().forEach(purposeNode -> purposeNode.setWallet(wallet));
    }
    Map<WalletAndKey, Storage> childWallets = loadChildWallets(storage, wallet, encryptionKey);
    wallet.setChildWallets(childWallets.keySet().stream().map(WalletAndKey::getWallet).collect(Collectors.toList()));
    return new WalletAndKey(wallet, encryptionKey, keyDeriver, childWallets);
}
Also used : Wallet(com.sparrowwallet.drongo.wallet.Wallet) InflaterInputStream(java.util.zip.InflaterInputStream) InflaterInputStream(java.util.zip.InflaterInputStream) ECKey(com.sparrowwallet.drongo.crypto.ECKey)

Example 29 with ECKey

use of com.sparrowwallet.drongo.crypto.ECKey in project drongo by sparrowwallet.

the class Wallet method finalise.

public void finalise(PSBT psbt) {
    int threshold = getDefaultPolicy().getNumSignaturesRequired();
    Map<PSBTInput, WalletNode> signingNodes = getSigningNodes(psbt);
    for (int i = 0; i < psbt.getTransaction().getInputs().size(); i++) {
        TransactionInput txInput = psbt.getTransaction().getInputs().get(i);
        PSBTInput psbtInput = psbt.getPsbtInputs().get(i);
        if (psbtInput.isFinalized()) {
            continue;
        }
        WalletNode signingNode = signingNodes.get(psbtInput);
        // Transaction parent on PSBT utxo might be null in a witness tx, so get utxo tx hash and utxo index from PSBT tx input
        TransactionOutput utxo = new TransactionOutput(null, psbtInput.getUtxo().getValue(), psbtInput.getUtxo().getScript()) {

            @Override
            public Sha256Hash getHash() {
                return txInput.getOutpoint().getHash();
            }

            @Override
            public int getIndex() {
                return (int) txInput.getOutpoint().getIndex();
            }
        };
        // TODO: Handle taproot scriptpath spending
        int signaturesAvailable = psbtInput.isTaproot() ? (psbtInput.getTapKeyPathSignature() != null ? 1 : 0) : psbtInput.getPartialSignatures().size();
        if (signaturesAvailable >= threshold && signingNode != null) {
            Transaction transaction = new Transaction();
            TransactionInput finalizedTxInput;
            if (getPolicyType().equals(PolicyType.SINGLE)) {
                ECKey pubKey = signingNode.getPubKey();
                TransactionSignature transactionSignature = psbtInput.isTaproot() ? psbtInput.getTapKeyPathSignature() : psbtInput.getPartialSignature(pubKey);
                if (transactionSignature == null) {
                    throw new IllegalArgumentException("Pubkey of partial signature does not match wallet pubkey");
                }
                finalizedTxInput = signingNode.getWallet().getScriptType().addSpendingInput(transaction, utxo, pubKey, transactionSignature);
            } else if (getPolicyType().equals(PolicyType.MULTI)) {
                List<ECKey> pubKeys = signingNode.getPubKeys();
                Map<ECKey, TransactionSignature> pubKeySignatures = new TreeMap<>(new ECKey.LexicographicECKeyComparator());
                for (ECKey pubKey : pubKeys) {
                    pubKeySignatures.put(pubKey, psbtInput.getPartialSignature(pubKey));
                }
                List<TransactionSignature> signatures = pubKeySignatures.values().stream().filter(Objects::nonNull).collect(Collectors.toList());
                if (signatures.size() < threshold) {
                    throw new IllegalArgumentException("Pubkeys of partial signatures do not match wallet pubkeys");
                }
                finalizedTxInput = signingNode.getWallet().getScriptType().addMultisigSpendingInput(transaction, utxo, threshold, pubKeySignatures);
            } else {
                throw new UnsupportedOperationException("Cannot finalise PSBT for policy type " + getPolicyType());
            }
            psbtInput.setFinalScriptSig(finalizedTxInput.getScriptSig());
            psbtInput.setFinalScriptWitness(finalizedTxInput.getWitness());
            psbtInput.clearNonFinalFields();
        }
    }
    psbt.getPsbtOutputs().forEach(PSBTOutput::clearNonFinalFields);
}
Also used : PSBTOutput(com.sparrowwallet.drongo.psbt.PSBTOutput) ECKey(com.sparrowwallet.drongo.crypto.ECKey) PSBTInput(com.sparrowwallet.drongo.psbt.PSBTInput)

Example 30 with ECKey

use of com.sparrowwallet.drongo.crypto.ECKey in project drongo by sparrowwallet.

the class Wallet method getInputWeightUnits.

/**
 * Return the number of weight units required for an input created by this wallet.
 *
 * @return the number of weight units (WU)
 */
public int getInputWeightUnits() {
    // Estimate assuming an input spending from a fresh receive node - it does not matter this node has no real utxos
    WalletNode receiveNode = getFreshNode(KeyPurpose.RECEIVE);
    Transaction transaction = new Transaction();
    TransactionOutput prevTxOut = transaction.addOutput(1L, receiveNode.getAddress());
    TransactionInput txInput = null;
    if (getPolicyType().equals(PolicyType.SINGLE)) {
        ECKey pubKey = receiveNode.getPubKey();
        TransactionSignature signature = TransactionSignature.dummy(getScriptType().getSignatureType());
        txInput = getScriptType().addSpendingInput(transaction, prevTxOut, pubKey, signature);
    } else if (getPolicyType().equals(PolicyType.MULTI)) {
        List<ECKey> pubKeys = receiveNode.getPubKeys();
        int threshold = getDefaultPolicy().getNumSignaturesRequired();
        Map<ECKey, TransactionSignature> pubKeySignatures = new TreeMap<>(new ECKey.LexicographicECKeyComparator());
        for (int i = 0; i < pubKeys.size(); i++) {
            pubKeySignatures.put(pubKeys.get(i), i < threshold ? TransactionSignature.dummy(getScriptType().getSignatureType()) : null);
        }
        txInput = getScriptType().addMultisigSpendingInput(transaction, prevTxOut, threshold, pubKeySignatures);
    }
    assert txInput != null;
    int wu = txInput.getLength() * WITNESS_SCALE_FACTOR;
    if (txInput.hasWitness()) {
        wu += txInput.getWitness().getLength();
    }
    return wu;
}
Also used : ECKey(com.sparrowwallet.drongo.crypto.ECKey)

Aggregations

ECKey (com.sparrowwallet.drongo.crypto.ECKey)45 Test (org.junit.Test)22 ByteArrayOutputStream (java.io.ByteArrayOutputStream)7 Address (com.sparrowwallet.drongo.address.Address)6 ChildNumber (com.sparrowwallet.drongo.crypto.ChildNumber)6 PaymentCode (com.sparrowwallet.drongo.bip47.PaymentCode)5 PSBT (com.sparrowwallet.drongo.psbt.PSBT)5 PSBTInput (com.sparrowwallet.drongo.psbt.PSBTInput)5 InvalidAddressException (com.sparrowwallet.drongo.address.InvalidAddressException)4 ScriptType (com.sparrowwallet.drongo.protocol.ScriptType)4 SecureString (com.sparrowwallet.drongo.SecureString)3 SecretPoint (com.sparrowwallet.drongo.bip47.SecretPoint)3 DeterministicKey (com.sparrowwallet.drongo.crypto.DeterministicKey)3 SchnorrSignature (com.sparrowwallet.drongo.crypto.SchnorrSignature)3 PSBTOutput (com.sparrowwallet.drongo.psbt.PSBTOutput)3 StandardCharsets (java.nio.charset.StandardCharsets)3 java.util (java.util)3 Collectors (java.util.stream.Collectors)3 Subscribe (com.google.common.eventbus.Subscribe)2 Files (com.google.common.io.Files)2