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