Search in sources :

Example 11 with PSBTInput

use of com.sparrowwallet.drongo.psbt.PSBTInput in project sparrow by sparrowwallet.

the class TransactionController method createInputTreeItem.

private TreeItem<TransactionForm> createInputTreeItem(int inputIndex) {
    TransactionInput txInput = getTransaction().getInputs().get(inputIndex);
    PSBTInput psbtInput = null;
    if (getPSBT() != null && getPSBT().getPsbtInputs().size() > txInput.getIndex()) {
        psbtInput = getPSBT().getPsbtInputs().get(txInput.getIndex());
    }
    InputForm inputForm = (getPSBT() != null ? new InputForm(txdata, psbtInput) : new InputForm(txdata, txInput));
    return new TreeItem<>(inputForm);
}
Also used : TreeItem(javafx.scene.control.TreeItem) PSBTInput(com.sparrowwallet.drongo.psbt.PSBTInput)

Example 12 with PSBTInput

use of com.sparrowwallet.drongo.psbt.PSBTInput 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 13 with PSBTInput

use of com.sparrowwallet.drongo.psbt.PSBTInput in project drongo by sparrowwallet.

the class Wallet method getSignedKeystores.

/**
 * Determines which keystores have signed a PSBT
 *
 * @param psbt The partially signed or finalized PSBT
 * @return A map keyed with the PSBTInput mapped to a map of the signatures and associated keystores that signed it
 */
public Map<PSBTInput, Map<TransactionSignature, Keystore>> getSignedKeystores(PSBT psbt) {
    Map<PSBTInput, WalletNode> signingNodes = getSigningNodes(psbt);
    Map<PSBTInput, Map<TransactionSignature, Keystore>> signedKeystores = new LinkedHashMap<>();
    for (PSBTInput psbtInput : signingNodes.keySet()) {
        WalletNode walletNode = signingNodes.get(psbtInput);
        Wallet signingWallet = walletNode.getWallet();
        Map<ECKey, Keystore> keystoreKeysForNode = signingWallet.getKeystores().stream().collect(Collectors.toMap(keystore -> signingWallet.getScriptType().getOutputKey(keystore.getPubKey(walletNode)), Function.identity(), (u, v) -> {
            throw new IllegalStateException("Duplicate keys from different keystores for node " + walletNode);
        }, LinkedHashMap::new));
        Map<ECKey, TransactionSignature> keySignatureMap;
        if (psbt.isFinalized() || psbtInput.isTaproot()) {
            keySignatureMap = psbtInput.getSigningKeys(keystoreKeysForNode.keySet());
        } else {
            keySignatureMap = psbtInput.getPartialSignatures();
        }
        keystoreKeysForNode.keySet().retainAll(keySignatureMap.keySet());
        Map<TransactionSignature, Keystore> inputSignatureKeystores = new LinkedHashMap<>();
        for (ECKey signingKey : keystoreKeysForNode.keySet()) {
            inputSignatureKeystores.put(keySignatureMap.get(signingKey), keystoreKeysForNode.get(signingKey));
        }
        signedKeystores.put(psbtInput, inputSignatureKeystores);
    }
    return signedKeystores;
}
Also used : Address(com.sparrowwallet.drongo.address.Address) java.util(java.util) PSBT(com.sparrowwallet.drongo.psbt.PSBT) ChildNumber(com.sparrowwallet.drongo.crypto.ChildNumber) Key(com.sparrowwallet.drongo.crypto.Key) PolicyType(com.sparrowwallet.drongo.policy.PolicyType) Function(java.util.function.Function) Collectors(java.util.stream.Collectors) StandardCharsets(java.nio.charset.StandardCharsets) Policy(com.sparrowwallet.drongo.policy.Policy) PSBTInput(com.sparrowwallet.drongo.psbt.PSBTInput) PSBTOutput(com.sparrowwallet.drongo.psbt.PSBTOutput) com.sparrowwallet.drongo.protocol(com.sparrowwallet.drongo.protocol) ECKey(com.sparrowwallet.drongo.crypto.ECKey) DeterministicKey(com.sparrowwallet.drongo.crypto.DeterministicKey) com.sparrowwallet.drongo(com.sparrowwallet.drongo) ScriptType(com.sparrowwallet.drongo.protocol.ScriptType) PaymentCode(com.sparrowwallet.drongo.bip47.PaymentCode) WITNESS_SCALE_FACTOR(com.sparrowwallet.drongo.protocol.Transaction.WITNESS_SCALE_FACTOR) ECKey(com.sparrowwallet.drongo.crypto.ECKey) PSBTInput(com.sparrowwallet.drongo.psbt.PSBTInput)

Example 14 with PSBTInput

use of com.sparrowwallet.drongo.psbt.PSBTInput in project drongo by sparrowwallet.

the class Wallet method getSigningNodes.

/**
 * Determines which nodes in this wallet can sign which inputs in the provided PSBT
 *
 * @param psbt The PSBT to be signed
 * @return A map if the PSBT inputs and the nodes that can sign them
 */
public Map<PSBTInput, WalletNode> getSigningNodes(PSBT psbt) {
    Map<PSBTInput, WalletNode> signingNodes = new LinkedHashMap<>();
    Map<Script, WalletNode> walletOutputScripts = getWalletOutputScripts();
    for (PSBTInput psbtInput : psbt.getPsbtInputs()) {
        TransactionOutput utxo = psbtInput.getUtxo();
        if (utxo != null) {
            Script scriptPubKey = utxo.getScript();
            WalletNode signingNode = walletOutputScripts.get(scriptPubKey);
            if (signingNode != null) {
                signingNodes.put(psbtInput, signingNode);
            }
        }
    }
    return signingNodes;
}
Also used : PSBTInput(com.sparrowwallet.drongo.psbt.PSBTInput)

Example 15 with PSBTInput

use of com.sparrowwallet.drongo.psbt.PSBTInput in project drongo by sparrowwallet.

the class PSBT method serialize.

public byte[] serialize(boolean includeXpubs) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    baos.writeBytes(Utils.hexToBytes(PSBT_MAGIC_HEX));
    baos.writeBytes(new byte[] { (byte) 0xff });
    List<PSBTEntry> globalEntries = getGlobalEntries();
    for (PSBTEntry entry : globalEntries) {
        if (includeXpubs || (entry.getKeyType() != PSBT_GLOBAL_BIP32_PUBKEY && entry.getKeyType() != PSBT_GLOBAL_PROPRIETARY)) {
            entry.serializeToStream(baos);
        }
    }
    baos.writeBytes(new byte[] { (byte) 0x00 });
    for (PSBTInput psbtInput : getPsbtInputs()) {
        List<PSBTEntry> inputEntries = psbtInput.getInputEntries();
        for (PSBTEntry entry : inputEntries) {
            if (includeXpubs || (entry.getKeyType() != PSBT_IN_BIP32_DERIVATION && entry.getKeyType() != PSBT_IN_PROPRIETARY && entry.getKeyType() != PSBT_IN_TAP_INTERNAL_KEY && entry.getKeyType() != PSBT_IN_TAP_BIP32_DERIVATION)) {
                entry.serializeToStream(baos);
            }
        }
        baos.writeBytes(new byte[] { (byte) 0x00 });
    }
    for (PSBTOutput psbtOutput : getPsbtOutputs()) {
        List<PSBTEntry> outputEntries = psbtOutput.getOutputEntries();
        for (PSBTEntry entry : outputEntries) {
            if (includeXpubs || (entry.getKeyType() != PSBT_OUT_REDEEM_SCRIPT && entry.getKeyType() != PSBT_OUT_WITNESS_SCRIPT && entry.getKeyType() != PSBT_OUT_BIP32_DERIVATION && entry.getKeyType() != PSBT_OUT_PROPRIETARY && entry.getKeyType() != PSBT_OUT_TAP_INTERNAL_KEY && entry.getKeyType() != PSBT_OUT_TAP_BIP32_DERIVATION)) {
                entry.serializeToStream(baos);
            }
        }
        baos.writeBytes(new byte[] { (byte) 0x00 });
    }
    return baos.toByteArray();
}
Also used : PSBTOutput(com.sparrowwallet.drongo.psbt.PSBTOutput) PSBTEntry(com.sparrowwallet.drongo.psbt.PSBTEntry) PSBTInput(com.sparrowwallet.drongo.psbt.PSBTInput)

Aggregations

PSBTInput (com.sparrowwallet.drongo.psbt.PSBTInput)17 PSBTOutput (com.sparrowwallet.drongo.psbt.PSBTOutput)6 ECKey (com.sparrowwallet.drongo.crypto.ECKey)5 Address (com.sparrowwallet.drongo.address.Address)3 com.sparrowwallet.drongo.protocol (com.sparrowwallet.drongo.protocol)3 PSBT (com.sparrowwallet.drongo.psbt.PSBT)3 Subscribe (com.google.common.eventbus.Subscribe)2 PSBTEntry (com.sparrowwallet.drongo.psbt.PSBTEntry)2 EventManager (com.sparrowwallet.sparrow.EventManager)2 com.sparrowwallet.sparrow.control (com.sparrowwallet.sparrow.control)2 com.sparrowwallet.sparrow.event (com.sparrowwallet.sparrow.event)2 StandardCharsets (java.nio.charset.StandardCharsets)2 DateTimeFormatter (java.time.format.DateTimeFormatter)2 FXML (javafx.fxml.FXML)2 Initializable (javafx.fxml.Initializable)2 javafx.scene.control (javafx.scene.control)2 Field (tornadofx.control.Field)2 Fieldset (tornadofx.control.Fieldset)2 ImmutableMap (com.google.common.collect.ImmutableMap)1 com.sparrowwallet.drongo (com.sparrowwallet.drongo)1