refactor bip322 implementation for qr and file signing support
This commit is contained in:
parent
53c999a01b
commit
af031d9425
@ -6,7 +6,6 @@ import com.sparrowwallet.drongo.policy.PolicyType;
|
||||
import com.sparrowwallet.drongo.protocol.*;
|
||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||
import com.sparrowwallet.drongo.psbt.PSBTInput;
|
||||
import com.sparrowwallet.drongo.psbt.PSBTInputSigner;
|
||||
import com.sparrowwallet.drongo.psbt.PSBTSignatureException;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@ -22,19 +21,39 @@ public class Bip322 {
|
||||
ECKey pubKey = ECKey.fromPublicOnly(privKey);
|
||||
Address address = scriptType.getAddress(pubKey);
|
||||
|
||||
Transaction toSpend = getBip322ToSpend(address, message);
|
||||
Transaction toSign = getBip322ToSign(toSpend);
|
||||
|
||||
TransactionOutput utxoOutput = toSpend.getOutputs().get(0);
|
||||
|
||||
PSBT psbt = new PSBT(toSign);
|
||||
PSBTInput psbtInput = psbt.getPsbtInputs().get(0);
|
||||
psbtInput.setWitnessUtxo(utxoOutput);
|
||||
psbtInput.setSigHash(SigHash.ALL);
|
||||
PSBT psbt = getBip322Psbt(scriptType, address, message);
|
||||
PSBTInput psbtInput = psbt.getPsbtInputs().getFirst();
|
||||
psbtInput.sign(scriptType.getOutputKey(privKey));
|
||||
|
||||
return getBip322SignatureFromPsbt(scriptType, psbt, pubKey);
|
||||
}
|
||||
|
||||
public static PSBT getBip322Psbt(ScriptType scriptType, Address address, String message) {
|
||||
checkScriptType(scriptType);
|
||||
|
||||
Transaction toSpend = getBip322ToSpend(address, message);
|
||||
Transaction toSign = getBip322ToSign(toSpend);
|
||||
TransactionOutput utxoOutput = toSpend.getOutputs().getFirst();
|
||||
|
||||
PSBT psbt = new PSBT(toSign);
|
||||
PSBTInput psbtInput = psbt.getPsbtInputs().getFirst();
|
||||
psbtInput.setWitnessUtxo(utxoOutput);
|
||||
psbtInput.setSigHash(SigHash.ALL);
|
||||
|
||||
return psbt;
|
||||
}
|
||||
|
||||
public static String getBip322SignatureFromPsbt(ScriptType scriptType, PSBT signedPsbt, ECKey pubKey) {
|
||||
checkScriptType(scriptType);
|
||||
|
||||
PSBTInput psbtInput = signedPsbt.getPsbtInputs().getFirst();
|
||||
TransactionSignature signature = psbtInput.isTaproot() ? psbtInput.getTapKeyPathSignature() : psbtInput.getPartialSignature(pubKey);
|
||||
|
||||
if(signature == null) {
|
||||
throw new IllegalArgumentException("PSBT does not contain a signature");
|
||||
}
|
||||
|
||||
TransactionOutput utxoOutput = psbtInput.getWitnessUtxo();
|
||||
Transaction finalizeTransaction = new Transaction();
|
||||
TransactionInput finalizedTxInput = scriptType.addSpendingInput(finalizeTransaction, utxoOutput, pubKey, signature);
|
||||
|
||||
@ -74,7 +93,7 @@ public class Bip322 {
|
||||
}
|
||||
|
||||
if(scriptType == ScriptType.P2WPKH) {
|
||||
signature = witness.getSignatures().get(0);
|
||||
signature = witness.getSignatures().getFirst();
|
||||
if(witness.getPushes().size() <= 1) {
|
||||
throw new SignatureException("BIP322 simple signature for P2WPKH script type does not contain a pubkey.");
|
||||
}
|
||||
@ -84,7 +103,7 @@ public class Bip322 {
|
||||
throw new SignatureException("Provided address does not match pubkey in signature");
|
||||
}
|
||||
} else if(scriptType == ScriptType.P2TR) {
|
||||
signature = witness.getSignatures().get(0);
|
||||
signature = witness.getSignatures().getFirst();
|
||||
pubKey = P2TR.getPublicKeyFromScript(address.getOutputScript());
|
||||
} else {
|
||||
throw new SignatureException(scriptType + " addresses are not supported");
|
||||
@ -94,8 +113,8 @@ public class Bip322 {
|
||||
Transaction toSign = getBip322ToSign(toSpend);
|
||||
|
||||
PSBT psbt = new PSBT(toSign);
|
||||
PSBTInput psbtInput = psbt.getPsbtInputs().get(0);
|
||||
psbtInput.setWitnessUtxo(toSpend.getOutputs().get(0));
|
||||
PSBTInput psbtInput = psbt.getPsbtInputs().getFirst();
|
||||
psbtInput.setWitnessUtxo(toSpend.getOutputs().getFirst());
|
||||
psbtInput.setSigHash(SigHash.ALL);
|
||||
|
||||
if(scriptType == ScriptType.P2TR) {
|
||||
@ -141,7 +160,7 @@ public class Bip322 {
|
||||
scriptSigChunks.add(ScriptChunk.fromData(getBip322MessageHash(message)));
|
||||
Script scriptSig = new Script(scriptSigChunks);
|
||||
toSpend.addInput(Sha256Hash.ZERO_HASH, 0xFFFFFFFFL, scriptSig, new TransactionWitness(toSpend, Collections.emptyList()));
|
||||
toSpend.getInputs().get(0).setSequenceNumber(0L);
|
||||
toSpend.getInputs().getFirst().setSequenceNumber(0L);
|
||||
toSpend.addOutput(0L, address.getOutputScript());
|
||||
|
||||
return toSpend;
|
||||
@ -154,7 +173,7 @@ public class Bip322 {
|
||||
|
||||
TransactionWitness witness = new TransactionWitness(toSign);
|
||||
toSign.addInput(toSpend.getTxId(), 0L, new Script(new byte[0]), witness);
|
||||
toSign.getInputs().get(0).setSequenceNumber(0L);
|
||||
toSign.getInputs().getFirst().setSequenceNumber(0L);
|
||||
toSign.addOutput(0, new Script(List.of(ScriptChunk.fromOpcode(ScriptOpCodes.OP_RETURN))));
|
||||
|
||||
return toSign;
|
||||
|
||||
@ -4,6 +4,9 @@ import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.address.Address;
|
||||
import com.sparrowwallet.drongo.address.InvalidAddressException;
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||
import com.sparrowwallet.drongo.protocol.SigHash;
|
||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||
import com.sparrowwallet.drongo.psbt.PSBTInput;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@ -100,6 +103,69 @@ public class Bip322Test {
|
||||
Assertions.assertThrows(UnsupportedOperationException.class, () -> Bip322.verifyMessageBip322(ScriptType.P2SH_P2WPKH, address, message1, signature1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBip322Psbt() {
|
||||
ECKey privKey = DumpedPrivateKey.fromBase58("L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k").getKey();
|
||||
Address address = ScriptType.P2WPKH.getAddress(privKey);
|
||||
|
||||
PSBT psbt = Bip322.getBip322Psbt(ScriptType.P2WPKH, address, "Hello World");
|
||||
Assertions.assertEquals(1, psbt.getPsbtInputs().size());
|
||||
PSBTInput psbtInput = psbt.getPsbtInputs().get(0);
|
||||
Assertions.assertNotNull(psbtInput.getWitnessUtxo());
|
||||
Assertions.assertEquals(SigHash.ALL, psbtInput.getSigHash());
|
||||
Assertions.assertEquals(0, psbt.getTransaction().getVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBip322PsbtTaproot() {
|
||||
ECKey privKey = DumpedPrivateKey.fromBase58("L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k").getKey();
|
||||
Address address = ScriptType.P2TR.getAddress(privKey);
|
||||
|
||||
PSBT psbt = Bip322.getBip322Psbt(ScriptType.P2TR, address, "Hello World");
|
||||
Assertions.assertEquals(1, psbt.getPsbtInputs().size());
|
||||
PSBTInput psbtInput = psbt.getPsbtInputs().get(0);
|
||||
Assertions.assertNotNull(psbtInput.getWitnessUtxo());
|
||||
Assertions.assertEquals(SigHash.ALL, psbtInput.getSigHash());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBip322SignatureFromPsbt() {
|
||||
ECKey privKey = DumpedPrivateKey.fromBase58("L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k").getKey();
|
||||
ECKey pubKey = ECKey.fromPublicOnly(privKey);
|
||||
Address address = ScriptType.P2WPKH.getAddress(privKey);
|
||||
|
||||
PSBT psbt = Bip322.getBip322Psbt(ScriptType.P2WPKH, address, "Hello World");
|
||||
psbt.getPsbtInputs().get(0).sign(ScriptType.P2WPKH.getOutputKey(privKey));
|
||||
|
||||
String sigFromPsbt = Bip322.getBip322SignatureFromPsbt(ScriptType.P2WPKH, psbt, pubKey);
|
||||
String sigDirect = Bip322.signMessageBip322(ScriptType.P2WPKH, "Hello World", privKey);
|
||||
Assertions.assertEquals(sigDirect, sigFromPsbt);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBip322SignatureFromPsbtTaproot() {
|
||||
ECKey privKey = DumpedPrivateKey.fromBase58("L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k").getKey();
|
||||
ECKey pubKey = ECKey.fromPublicOnly(privKey);
|
||||
Address address = ScriptType.P2TR.getAddress(privKey);
|
||||
|
||||
PSBT psbt = Bip322.getBip322Psbt(ScriptType.P2TR, address, "Hello World");
|
||||
psbt.getPsbtInputs().get(0).sign(ScriptType.P2TR.getOutputKey(privKey));
|
||||
|
||||
String sigFromPsbt = Bip322.getBip322SignatureFromPsbt(ScriptType.P2TR, psbt, pubKey);
|
||||
String sigDirect = Bip322.signMessageBip322(ScriptType.P2TR, "Hello World", privKey);
|
||||
Assertions.assertEquals(sigDirect, sigFromPsbt);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBip322SignatureFromUnsignedPsbt() {
|
||||
ECKey privKey = DumpedPrivateKey.fromBase58("L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k").getKey();
|
||||
ECKey pubKey = ECKey.fromPublicOnly(privKey);
|
||||
Address address = ScriptType.P2WPKH.getAddress(privKey);
|
||||
|
||||
PSBT psbt = Bip322.getBip322Psbt(ScriptType.P2WPKH, address, "Hello World");
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> Bip322.getBip322SignatureFromPsbt(ScriptType.P2WPKH, psbt, pubKey));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyMessageBip322Multisig() throws SignatureException, InvalidAddressException {
|
||||
Address address = Address.fromString("bc1ppv609nr0vr25u07u95waq5lucwfm6tde4nydujnu8npg4q75mr5sxq8lt3");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user