mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-12-26 10:06:45 +00:00
signing fixes
This commit is contained in:
parent
4b4a980a9b
commit
4c5166a6ea
4 changed files with 82 additions and 11 deletions
|
@ -24,8 +24,9 @@ public class TransactionWitness extends ChildMessage {
|
|||
public TransactionWitness(Transaction transaction, List<TransactionSignature> signatures, Script witnessScript) {
|
||||
setParent(transaction);
|
||||
this.pushes = new ArrayList<>();
|
||||
//If a multisig witness script, add a zero byte witness element to handle the multisig off by one bug
|
||||
if(ScriptType.MULTISIG.isScriptType(witnessScript)) {
|
||||
pushes.add(new byte[] { ScriptOpCodes.OP_0 });
|
||||
pushes.add(new byte[0]);
|
||||
}
|
||||
for(TransactionSignature signature : signatures) {
|
||||
pushes.add(signature.encodeToBitcoin());
|
||||
|
@ -95,7 +96,7 @@ public class TransactionWitness extends ChildMessage {
|
|||
|
||||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
stream.write(new VarInt(pushes.size()).encode());
|
||||
for (int i = 0; i < pushes.size(); i++) {
|
||||
for(int i = 0; i < pushes.size(); i++) {
|
||||
byte[] push = pushes.get(i);
|
||||
if(push.length == 1 && push[0] == 0) {
|
||||
stream.write(push);
|
||||
|
|
|
@ -386,7 +386,7 @@ public class PSBTInput {
|
|||
//All partial sigs are already verified
|
||||
int reqSigs = getSigningScript().getNumRequiredSignatures();
|
||||
int sigs = getPartialSignatures().size();
|
||||
return sigs == reqSigs;
|
||||
return sigs >= reqSigs;
|
||||
} catch(NonStandardScriptException e) {
|
||||
return false;
|
||||
}
|
||||
|
@ -457,6 +457,31 @@ public class PSBTInput {
|
|||
return false;
|
||||
}
|
||||
|
||||
public ScriptType getScriptType() {
|
||||
Script signingScript = getUtxo().getScript();
|
||||
|
||||
boolean p2sh = false;
|
||||
if(P2SH.isScriptType(signingScript)) {
|
||||
p2sh = true;
|
||||
|
||||
if(getRedeemScript() != null) {
|
||||
signingScript = getRedeemScript();
|
||||
} else if(getFinalScriptSig() != null) {
|
||||
signingScript = getFinalScriptSig().getFirstNestedScript();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if(P2WPKH.isScriptType(signingScript)) {
|
||||
return p2sh ? P2SH_P2WPKH : P2WPKH;
|
||||
} else if(P2WSH.isScriptType(signingScript)) {
|
||||
return p2sh ? P2SH_P2WSH : P2WSH;
|
||||
}
|
||||
|
||||
return ScriptType.getType(signingScript);
|
||||
}
|
||||
|
||||
public Script getSigningScript() {
|
||||
Script signingScript = getUtxo().getScript();
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package com.sparrowwallet.drongo.wallet;
|
||||
|
||||
import com.sparrowwallet.drongo.KeyPurpose;
|
||||
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.NonStandardScriptException;
|
||||
import com.sparrowwallet.drongo.protocol.Script;
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||
import com.sparrowwallet.drongo.protocol.TransactionSignature;
|
||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||
|
@ -15,7 +18,7 @@ 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
|
||||
*
|
||||
* It is used when the normal wallet is not available.
|
||||
*/
|
||||
public class FinalizingPSBTWallet extends Wallet {
|
||||
private final Map<PSBTInput, WalletNode> signedInputNodes = new LinkedHashMap<>();
|
||||
|
@ -25,15 +28,43 @@ public class FinalizingPSBTWallet extends Wallet {
|
|||
public FinalizingPSBTWallet(PSBT psbt) {
|
||||
super("Finalizing PSBT Wallet");
|
||||
|
||||
Map<PSBTInput, WalletNode> signingNodes = new LinkedHashMap<>();
|
||||
if(!psbt.isSigned() || psbt.isFinalized()) {
|
||||
throw new IllegalArgumentException("Only a fully signed, unfinalized PSBT can be used");
|
||||
}
|
||||
|
||||
WalletNode purposeNode = getNode(KeyPurpose.RECEIVE);
|
||||
List<WalletNode> signedNodes = new ArrayList<>(purposeNode.getChildren());
|
||||
|
||||
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);
|
||||
|
||||
WalletNode signedNode = signedNodes.get(i);
|
||||
signedInputNodes.put(psbtInput, signedNode);
|
||||
signedNodeKeys.put(signedNode, new ArrayList<>(keys));
|
||||
numSignatures = keys.size();
|
||||
setScriptType(ScriptType.getType(psbtInput.getUtxo().getScript()));
|
||||
|
||||
ScriptType scriptType = psbtInput.getScriptType();
|
||||
if(scriptType == null || (getScriptType() != null && scriptType.equals(getScriptType()))) {
|
||||
throw new IllegalArgumentException("Cannot determine a single script type from the PSBT");
|
||||
} else {
|
||||
setScriptType(scriptType);
|
||||
}
|
||||
|
||||
try {
|
||||
Script signingScript = psbtInput.getSigningScript();
|
||||
int sigsRequired = signingScript.getNumRequiredSignatures();
|
||||
if(numSignatures > 0 && sigsRequired != numSignatures) {
|
||||
throw new IllegalArgumentException("Different number of signatures required in PSBT inputs");
|
||||
} else {
|
||||
numSignatures = sigsRequired;
|
||||
}
|
||||
|
||||
if(ScriptType.MULTISIG.isScriptType(signingScript)) {
|
||||
signedNodeKeys.put(signedNode, Arrays.asList(ScriptType.MULTISIG.getPublicKeysFromScript(signingScript)));
|
||||
}
|
||||
} catch(NonStandardScriptException e) {
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
setPolicyType(numSignatures == 1 ? PolicyType.SINGLE : PolicyType.MULTI);
|
||||
|
@ -73,4 +104,15 @@ public class FinalizingPSBTWallet extends Wallet {
|
|||
public List<ECKey> getPubKeys(WalletNode node) {
|
||||
return signedNodeKeys.get(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Script getOutputScript(WalletNode node) {
|
||||
for(Map.Entry<PSBTInput, WalletNode> entry : signedInputNodes.entrySet()) {
|
||||
if(node.equals(entry.getValue())) {
|
||||
return entry.getKey().getUtxo().getScript();
|
||||
}
|
||||
}
|
||||
|
||||
return new Script(new byte[10]);
|
||||
}
|
||||
}
|
|
@ -637,7 +637,10 @@ public class Wallet {
|
|||
for(Map.Entry<PSBTInput, WalletNode> signingEntry : signingNodes.entrySet()) {
|
||||
ECKey privKey = keystore.getKey(signingEntry.getValue());
|
||||
PSBTInput psbtInput = signingEntry.getKey();
|
||||
psbtInput.sign(privKey);
|
||||
|
||||
if(!psbtInput.isSigned()) {
|
||||
psbtInput.sign(privKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -680,8 +683,8 @@ public class Wallet {
|
|||
finalizedTxInput = getScriptType().addSpendingInput(transaction, utxo, pubKey, transactionSignature);
|
||||
} else if(getPolicyType().equals(PolicyType.MULTI)) {
|
||||
List<ECKey> pubKeys = getPubKeys(signingNode);
|
||||
List<TransactionSignature> signatures = pubKeys.stream().map(psbtInput::getPartialSignature).collect(Collectors.toList());
|
||||
if(pubKeys.size() != signatures.size()) {
|
||||
List<TransactionSignature> signatures = pubKeys.stream().map(psbtInput::getPartialSignature).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
if(signatures.size() < threshold) {
|
||||
throw new IllegalArgumentException("Pubkeys of partial signatures do not match wallet pubkeys");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue