mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-12-26 18:16:45 +00:00
support for better combining and finalising psbts
This commit is contained in:
parent
3ce2394813
commit
4b4a980a9b
4 changed files with 116 additions and 10 deletions
|
@ -153,6 +153,16 @@ public class Transaction extends ChildMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasScriptSigs() {
|
||||||
|
for(TransactionInput in : inputs) {
|
||||||
|
if(in.getScriptBytes().length > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasWitnesses() {
|
public boolean hasWitnesses() {
|
||||||
for(TransactionInput in : inputs) {
|
for(TransactionInput in : inputs) {
|
||||||
if(in.hasWitness()) {
|
if(in.hasWitness()) {
|
||||||
|
|
|
@ -393,6 +393,16 @@ public class PSBT {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isFinalized() {
|
||||||
|
for(PSBTInput psbtInput : getPsbtInputs()) {
|
||||||
|
if(psbtInput.getFinalScriptSig() != null || psbtInput.getFinalScriptWitness() != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private List<PSBTEntry> getGlobalEntries() {
|
private List<PSBTEntry> getGlobalEntries() {
|
||||||
List<PSBTEntry> entries = new ArrayList<>();
|
List<PSBTEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -462,10 +472,8 @@ public class PSBT {
|
||||||
throw new IllegalArgumentException("Provided PSBT does contain a matching global transaction");
|
throw new IllegalArgumentException("Provided PSBT does contain a matching global transaction");
|
||||||
}
|
}
|
||||||
|
|
||||||
for(PSBTInput psbtInput : psbt.getPsbtInputs()) {
|
if(isFinalized() || psbt.isFinalized()) {
|
||||||
if(psbtInput.getFinalScriptSig() != null || psbtInput.getFinalScriptWitness() != null) {
|
throw new IllegalArgumentException("Cannot combine an already finalised PSBT");
|
||||||
throw new IllegalArgumentException("Cannot combine an already finalised PSBT");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(psbt.getVersion() != null) {
|
if(psbt.getVersion() != null) {
|
||||||
|
@ -499,12 +507,14 @@ public class PSBT {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(hasWitness && !transaction.isSegwit()) {
|
Transaction finalTransaction = new Transaction(transaction.bitcoinSerialize());
|
||||||
transaction.setSegwitVersion(1);
|
|
||||||
|
if(hasWitness && !finalTransaction.isSegwit()) {
|
||||||
|
finalTransaction.setSegwitVersion(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < transaction.getInputs().size(); i++) {
|
for(int i = 0; i < finalTransaction.getInputs().size(); i++) {
|
||||||
TransactionInput txInput = transaction.getInputs().get(i);
|
TransactionInput txInput = finalTransaction.getInputs().get(i);
|
||||||
PSBTInput psbtInput = getPsbtInputs().get(i);
|
PSBTInput psbtInput = getPsbtInputs().get(i);
|
||||||
txInput.setScriptBytes(psbtInput.getFinalScriptSig().getProgram());
|
txInput.setScriptBytes(psbtInput.getFinalScriptSig().getProgram());
|
||||||
|
|
||||||
|
@ -512,12 +522,12 @@ public class PSBT {
|
||||||
if(psbtInput.getFinalScriptWitness() != null) {
|
if(psbtInput.getFinalScriptWitness() != null) {
|
||||||
txInput.setWitness(psbtInput.getFinalScriptWitness());
|
txInput.setWitness(psbtInput.getFinalScriptWitness());
|
||||||
} else {
|
} else {
|
||||||
txInput.setWitness(new TransactionWitness(transaction));
|
txInput.setWitness(new TransactionWitness(finalTransaction));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return transaction;
|
return finalTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PSBTInput> getPsbtInputs() {
|
public List<PSBTInput> getPsbtInputs() {
|
||||||
|
|
|
@ -395,6 +395,16 @@ public class PSBTInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<TransactionSignature> getSignatures() {
|
||||||
|
if(getFinalScriptWitness() != null) {
|
||||||
|
return getFinalScriptWitness().getSignatures();
|
||||||
|
} else if(getFinalScriptSig() != null) {
|
||||||
|
return getFinalScriptSig().getSignatures();
|
||||||
|
} else {
|
||||||
|
return getPartialSignatures().values();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean sign(ECKey privKey) {
|
public boolean sign(ECKey privKey) {
|
||||||
SigHash localSigHash = getSigHash();
|
SigHash localSigHash = getSigHash();
|
||||||
if(localSigHash == null) {
|
if(localSigHash == null) {
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package com.sparrowwallet.drongo.wallet;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||||
|
import com.sparrowwallet.drongo.policy.Miniscript;
|
||||||
|
import com.sparrowwallet.drongo.policy.Policy;
|
||||||
|
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
import com.sparrowwallet.drongo.protocol.TransactionSignature;
|
||||||
|
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||||
|
import com.sparrowwallet.drongo.psbt.PSBTInput;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a special wallet that is used solely to finalize a fully signed PSBT by reading from the partial signatures and UTXO scriptPubKey
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FinalizingPSBTWallet extends Wallet {
|
||||||
|
private final Map<PSBTInput, WalletNode> signedInputNodes = new LinkedHashMap<>();
|
||||||
|
private final Map<WalletNode, List<ECKey>> signedNodeKeys = new LinkedHashMap<>();
|
||||||
|
private int numSignatures;
|
||||||
|
|
||||||
|
public FinalizingPSBTWallet(PSBT psbt) {
|
||||||
|
super("Finalizing PSBT Wallet");
|
||||||
|
|
||||||
|
Map<PSBTInput, WalletNode> signingNodes = new LinkedHashMap<>();
|
||||||
|
for(int i = 0; i < psbt.getPsbtInputs().size(); i++) {
|
||||||
|
PSBTInput psbtInput = psbt.getPsbtInputs().get(i);
|
||||||
|
Set<ECKey> keys = psbtInput.getPartialSignatures().keySet();
|
||||||
|
WalletNode signedNode = new WalletNode("m/" + i);
|
||||||
|
signedInputNodes.put(psbtInput, signedNode);
|
||||||
|
signedNodeKeys.put(signedNode, new ArrayList<>(keys));
|
||||||
|
numSignatures = keys.size();
|
||||||
|
setScriptType(ScriptType.getType(psbtInput.getUtxo().getScript()));
|
||||||
|
}
|
||||||
|
|
||||||
|
setPolicyType(numSignatures == 1 ? PolicyType.SINGLE : PolicyType.MULTI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<PSBTInput, List<Keystore>> getSignedKeystores(PSBT psbt) {
|
||||||
|
Map<PSBTInput, List<Keystore>> signedKeystores = new LinkedHashMap<>();
|
||||||
|
for(PSBTInput psbtInput : psbt.getPsbtInputs()) {
|
||||||
|
Collection<TransactionSignature> signatures = psbtInput.getSignatures();
|
||||||
|
signedKeystores.put(psbtInput, IntStream.range(1, signatures.size() + 1).mapToObj(i -> new Keystore("Keystore " + i)).collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
return signedKeystores;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Policy getDefaultPolicy() {
|
||||||
|
return new Policy(new Miniscript("")) {
|
||||||
|
@Override
|
||||||
|
public int getNumSignaturesRequired() {
|
||||||
|
return numSignatures;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<PSBTInput, WalletNode> getSigningNodes(PSBT psbt) {
|
||||||
|
return signedInputNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ECKey getPubKey(WalletNode node) {
|
||||||
|
return signedNodeKeys.get(node).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ECKey> getPubKeys(WalletNode node) {
|
||||||
|
return signedNodeKeys.get(node);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue