add sp support for bip322 message signing
This commit is contained in:
parent
698f8b08a1
commit
dfd947cb69
@ -1,5 +1,6 @@
|
||||
package com.sparrowwallet.drongo.crypto;
|
||||
|
||||
import com.sparrowwallet.drongo.KeyDerivation;
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.address.Address;
|
||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||
@ -43,6 +44,46 @@ public class Bip322 {
|
||||
return psbt;
|
||||
}
|
||||
|
||||
public static PSBT getBip322PsbtSp(Address address, String message, byte[] silentPaymentsTweak, Map<ECKey, KeyDerivation> spendDerivations) {
|
||||
if(silentPaymentsTweak == null) {
|
||||
throw new IllegalArgumentException("Silent payments tweak is required");
|
||||
}
|
||||
|
||||
PSBT psbt = getBip322Psbt(P2TR, address, message);
|
||||
PSBTInput psbtInput = psbt.getPsbtInputs().getFirst();
|
||||
psbtInput.setSilentPaymentsTweak(silentPaymentsTweak);
|
||||
if(spendDerivations != null && !spendDerivations.isEmpty()) {
|
||||
psbtInput.getSilentPaymentsSpendDerivations().putAll(spendDerivations);
|
||||
}
|
||||
|
||||
return psbt;
|
||||
}
|
||||
|
||||
public static String signMessageBip322Sp(Address address, String message, ECKey spendPrivKey, byte[] silentPaymentsTweak) {
|
||||
PSBT psbt = getBip322PsbtSp(address, message, silentPaymentsTweak, Collections.emptyMap());
|
||||
PSBTInput psbtInput = psbt.getPsbtInputs().getFirst();
|
||||
|
||||
if(!psbtInput.signSilentPayments(spendPrivKey)) {
|
||||
throw new IllegalStateException("Failed to sign BIP322 PSBT with silent payments tweak");
|
||||
}
|
||||
|
||||
return getBip322SignatureFromPsbtSp(psbt);
|
||||
}
|
||||
|
||||
public static String getBip322SignatureFromPsbtSp(PSBT signedPsbt) {
|
||||
PSBTInput psbtInput = signedPsbt.getPsbtInputs().getFirst();
|
||||
TransactionSignature signature = psbtInput.getTapKeyPathSignature();
|
||||
if(signature == null) {
|
||||
throw new IllegalArgumentException("PSBT does not contain a taproot keypath signature");
|
||||
}
|
||||
|
||||
Transaction finalizeTransaction = new Transaction();
|
||||
TransactionWitness witness = new TransactionWitness(finalizeTransaction, signature);
|
||||
finalizeTransaction.addInput(Sha256Hash.ZERO_HASH, 0, new Script(new byte[0]), witness);
|
||||
|
||||
return Base64.getEncoder().encodeToString(witness.toByteArray());
|
||||
}
|
||||
|
||||
public static String getBip322SignatureFromPsbt(ScriptType scriptType, PSBT signedPsbt, ECKey pubKey) {
|
||||
checkScriptType(scriptType);
|
||||
|
||||
|
||||
@ -167,6 +167,48 @@ public class Bip322Test {
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> Bip322.getBip322SignatureFromPsbt(ScriptType.P2WPKH, psbt, pubKey));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signMessageBip322Sp() throws SignatureException {
|
||||
ECKey spendPrivKey = DumpedPrivateKey.fromBase58("L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k").getKey();
|
||||
byte[] tweak = Utils.hexToBytes("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
|
||||
|
||||
ECKey spendPubKey = ECKey.fromPublicOnly(spendPrivKey);
|
||||
ECKey tweakPoint = ECKey.fromPublicOnly(ECKey.fromPrivate(tweak));
|
||||
ECKey outputKey = spendPubKey.add(tweakPoint, true);
|
||||
Address address = ScriptType.P2TR.getAddress(PolicyType.SINGLE_SP, outputKey);
|
||||
|
||||
String signature = Bip322.signMessageBip322Sp(address, "Hello World", spendPrivKey, tweak);
|
||||
Assertions.assertTrue(Bip322.verifyMessageBip322(ScriptType.P2TR, address, "Hello World", signature));
|
||||
|
||||
//An SP signature for the same key but a different tweak must not verify against the first address
|
||||
byte[] tweak2 = Utils.hexToBytes("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210");
|
||||
ECKey tweakPoint2 = ECKey.fromPublicOnly(ECKey.fromPrivate(tweak2));
|
||||
ECKey outputKey2 = spendPubKey.add(tweakPoint2, true);
|
||||
Address address2 = ScriptType.P2TR.getAddress(PolicyType.SINGLE_SP, outputKey2);
|
||||
String signature2 = Bip322.signMessageBip322Sp(address2, "Hello World", spendPrivKey, tweak2);
|
||||
Assertions.assertTrue(Bip322.verifyMessageBip322(ScriptType.P2TR, address2, "Hello World", signature2));
|
||||
Assertions.assertFalse(Bip322.verifyMessageBip322(ScriptType.P2TR, address, "Hello World", signature2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBip322PsbtSp() {
|
||||
ECKey spendPrivKey = DumpedPrivateKey.fromBase58("L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k").getKey();
|
||||
byte[] tweak = Utils.hexToBytes("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
|
||||
|
||||
ECKey spendPubKey = ECKey.fromPublicOnly(spendPrivKey);
|
||||
ECKey tweakPoint = ECKey.fromPublicOnly(ECKey.fromPrivate(tweak));
|
||||
ECKey outputKey = spendPubKey.add(tweakPoint, true);
|
||||
Address address = ScriptType.P2TR.getAddress(PolicyType.SINGLE_SP, outputKey);
|
||||
|
||||
PSBT psbt = Bip322.getBip322PsbtSp(address, "Hello World", tweak, java.util.Collections.emptyMap());
|
||||
Assertions.assertEquals(1, psbt.getPsbtInputs().size());
|
||||
PSBTInput psbtInput = psbt.getPsbtInputs().get(0);
|
||||
Assertions.assertNotNull(psbtInput.getWitnessUtxo());
|
||||
Assertions.assertArrayEquals(tweak, psbtInput.getSilentPaymentsTweak());
|
||||
Assertions.assertNull(psbtInput.getTapInternalKey());
|
||||
Assertions.assertTrue(psbtInput.getTapDerivedPublicKeys().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyMessageBip322Multisig() throws SignatureException, InvalidAddressException {
|
||||
Address address = Address.fromString("bc1ppv609nr0vr25u07u95waq5lucwfm6tde4nydujnu8npg4q75mr5sxq8lt3");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user