mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-11-02 18:26:43 +00:00
allow psbt signature verification to be deferred
This commit is contained in:
parent
567294a4b0
commit
42ffeb9565
3 changed files with 64 additions and 18 deletions
|
@ -16,7 +16,7 @@ import java.nio.ByteBuffer;
|
|||
import java.util.*;
|
||||
|
||||
import static com.sparrowwallet.drongo.psbt.PSBTEntry.*;
|
||||
import static com.sparrowwallet.drongo.psbt.PSBTInput.PSBT_IN_BIP32_DERIVATION;
|
||||
import static com.sparrowwallet.drongo.psbt.PSBTInput.*;
|
||||
import static com.sparrowwallet.drongo.psbt.PSBTOutput.*;
|
||||
|
||||
public class PSBT {
|
||||
|
@ -169,11 +169,15 @@ public class PSBT {
|
|||
}
|
||||
|
||||
public PSBT(byte[] psbt) throws PSBTParseException {
|
||||
this.psbtBytes = psbt;
|
||||
parse();
|
||||
this(psbt, true);
|
||||
}
|
||||
|
||||
private void parse() throws PSBTParseException {
|
||||
public PSBT(byte[] psbt, boolean verifySignatures) throws PSBTParseException {
|
||||
this.psbtBytes = psbt;
|
||||
parse(verifySignatures);
|
||||
}
|
||||
|
||||
private void parse(boolean verifySignatures) throws PSBTParseException {
|
||||
int seenInputs = 0;
|
||||
int seenOutputs = 0;
|
||||
|
||||
|
@ -214,7 +218,7 @@ public class PSBT {
|
|||
seenInputs++;
|
||||
if (seenInputs == inputs) {
|
||||
currentState = STATE_OUTPUTS;
|
||||
parseInputEntries(inputEntryLists);
|
||||
parseInputEntries(inputEntryLists, verifySignatures);
|
||||
}
|
||||
break;
|
||||
case STATE_OUTPUTS:
|
||||
|
@ -315,7 +319,7 @@ public class PSBT {
|
|||
}
|
||||
}
|
||||
|
||||
private void parseInputEntries(List<List<PSBTEntry>> inputEntryLists) throws PSBTParseException {
|
||||
private void parseInputEntries(List<List<PSBTEntry>> inputEntryLists, boolean verifySignatures) throws PSBTParseException {
|
||||
for(List<PSBTEntry> inputEntries : inputEntryLists) {
|
||||
PSBTEntry duplicate = findDuplicateKey(inputEntries);
|
||||
if(duplicate != null) {
|
||||
|
@ -325,9 +329,11 @@ public class PSBT {
|
|||
int inputIndex = this.psbtInputs.size();
|
||||
PSBTInput input = new PSBTInput(inputEntries, transaction, inputIndex);
|
||||
|
||||
boolean verified = input.verifySignatures();
|
||||
if(!verified && input.getPartialSignatures().size() > 0) {
|
||||
throw new PSBTParseException("Unverifiable partial signatures provided");
|
||||
if(verifySignatures) {
|
||||
boolean verified = input.verifySignatures();
|
||||
if(!verified && input.getPartialSignatures().size() > 0) {
|
||||
throw new PSBTSignatureException("Unverifiable partial signatures provided");
|
||||
}
|
||||
}
|
||||
|
||||
this.psbtInputs.add(input);
|
||||
|
@ -379,6 +385,19 @@ public class PSBT {
|
|||
return fee;
|
||||
}
|
||||
|
||||
public void verifySignatures() throws PSBTSignatureException {
|
||||
for(PSBTInput input : getPsbtInputs()) {
|
||||
boolean verified = input.verifySignatures();
|
||||
if(!verified) {
|
||||
if(input.getPartialSignatures().size() > 0) {
|
||||
throw new PSBTSignatureException("Unverifiable partial signatures provided");
|
||||
}
|
||||
|
||||
throw new PSBTSignatureException("No UTXO data provided");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSignatures() {
|
||||
for(PSBTInput psbtInput : getPsbtInputs()) {
|
||||
if(!psbtInput.getPartialSignatures().isEmpty() || psbtInput.getFinalScriptSig() != null || psbtInput.getFinalScriptWitness() != null) {
|
||||
|
@ -630,6 +649,10 @@ public class PSBT {
|
|||
}
|
||||
|
||||
public static PSBT fromString(String strPSBT) throws PSBTParseException {
|
||||
return fromString(strPSBT, true);
|
||||
}
|
||||
|
||||
public static PSBT fromString(String strPSBT, boolean verifySignatures) throws PSBTParseException {
|
||||
if (!isPSBT(strPSBT)) {
|
||||
throw new PSBTParseException("Provided string is not a PSBT");
|
||||
}
|
||||
|
@ -639,6 +662,6 @@ public class PSBT {
|
|||
}
|
||||
|
||||
byte[] psbtBytes = Utils.hexToBytes(strPSBT);
|
||||
return new PSBT(psbtBytes);
|
||||
return new PSBT(psbtBytes, verifySignatures);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,11 +136,15 @@ public class PSBTInput {
|
|||
throw new PSBTParseException("Witness UTXO provided but redeem script is not P2WPKH or P2WSH");
|
||||
}
|
||||
}
|
||||
if(scriptPubKey == null || !P2SH.isScriptType(scriptPubKey)) {
|
||||
throw new PSBTParseException("PSBT provided a redeem script for a transaction output that does not need one");
|
||||
}
|
||||
if(!Arrays.equals(Utils.sha256hash160(redeemScript.getProgram()), scriptPubKey.getPubKeyHash())) {
|
||||
throw new PSBTParseException("Redeem script hash does not match transaction output script pubkey hash " + Utils.bytesToHex(scriptPubKey.getPubKeyHash()));
|
||||
if(scriptPubKey == null) {
|
||||
log.warn("PSBT provided a redeem script for a transaction output that was not provided");
|
||||
} else {
|
||||
if(!P2SH.isScriptType(scriptPubKey)) {
|
||||
throw new PSBTParseException("PSBT provided a redeem script for a transaction output that does not need one");
|
||||
}
|
||||
if(!Arrays.equals(Utils.sha256hash160(redeemScript.getProgram()), scriptPubKey.getPubKeyHash())) {
|
||||
throw new PSBTParseException("Redeem script hash does not match transaction output script pubkey hash " + Utils.bytesToHex(scriptPubKey.getPubKeyHash()));
|
||||
}
|
||||
}
|
||||
|
||||
this.redeemScript = redeemScript;
|
||||
|
@ -156,7 +160,7 @@ public class PSBTInput {
|
|||
pubKeyHash = this.witnessUtxo.getScript().getPubKeyHash();
|
||||
}
|
||||
if(pubKeyHash == null) {
|
||||
throw new PSBTParseException("Witness script provided without P2WSH witness utxo or P2SH redeem script");
|
||||
log.warn("Witness script provided without P2WSH witness utxo or P2SH redeem script");
|
||||
} else if(!Arrays.equals(Sha256Hash.hash(witnessScript.getProgram()), pubKeyHash)) {
|
||||
throw new PSBTParseException("Witness script hash does not match provided pay to script hash " + Utils.bytesToHex(pubKeyHash));
|
||||
}
|
||||
|
@ -428,7 +432,7 @@ public class PSBTInput {
|
|||
return false;
|
||||
}
|
||||
|
||||
boolean verifySignatures() throws PSBTParseException {
|
||||
boolean verifySignatures() throws PSBTSignatureException {
|
||||
SigHash localSigHash = getSigHash();
|
||||
if(localSigHash == null) {
|
||||
//Assume SigHash.ALL
|
||||
|
@ -443,7 +447,7 @@ public class PSBTInput {
|
|||
for(ECKey sigPublicKey : getPartialSignatures().keySet()) {
|
||||
TransactionSignature signature = getPartialSignature(sigPublicKey);
|
||||
if(!sigPublicKey.verify(hash, signature)) {
|
||||
throw new PSBTParseException("Partial signature does not verify against provided public key");
|
||||
throw new PSBTSignatureException("Partial signature does not verify against provided public key");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package com.sparrowwallet.drongo.psbt;
|
||||
|
||||
public class PSBTSignatureException extends PSBTParseException {
|
||||
public PSBTSignatureException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public PSBTSignatureException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public PSBTSignatureException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public PSBTSignatureException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue