mirror of
https://github.com/sparrowwallet/drongo.git
synced 2025-11-05 11:56:38 +00:00
use map of scriptpubkeys instead of transaction outputs for tweak computation
This commit is contained in:
parent
a4d86f9ee3
commit
da736c8cef
2 changed files with 29 additions and 26 deletions
|
|
@ -18,35 +18,35 @@ public class SilentPaymentUtils {
|
||||||
|
|
||||||
private static final List<ScriptType> SCRIPT_TYPES = List.of(ScriptType.P2TR, ScriptType.P2WPKH, ScriptType.P2SH_P2WPKH, ScriptType.P2PKH);
|
private static final List<ScriptType> SCRIPT_TYPES = List.of(ScriptType.P2TR, ScriptType.P2WPKH, ScriptType.P2SH_P2WPKH, ScriptType.P2PKH);
|
||||||
|
|
||||||
public static boolean isEligible(Transaction tx, Map<HashIndex, TransactionOutput> spentOutputs) {
|
public static boolean isEligible(Transaction tx, Map<HashIndex, Script> spentScriptPubKeys) {
|
||||||
if(!containsTaprootOutput(tx)) {
|
if(!containsTaprootOutput(tx)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(getInputPubKeys(tx, spentOutputs).isEmpty()) {
|
if(getInputPubKeys(tx, spentScriptPubKeys).isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(spendsInvalidSegwitOutput(tx, spentOutputs)) {
|
if(spendsInvalidSegwitOutput(tx, spentScriptPubKeys)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<ECKey> getInputPubKeys(Transaction tx, Map<HashIndex, TransactionOutput> spentOutputs) {
|
public static List<ECKey> getInputPubKeys(Transaction tx, Map<HashIndex, Script> spentScriptPubKeys) {
|
||||||
List<ECKey> keys = new ArrayList<>();
|
List<ECKey> keys = new ArrayList<>();
|
||||||
for(TransactionInput input : tx.getInputs()) {
|
for(TransactionInput input : tx.getInputs()) {
|
||||||
HashIndex hashIndex = new HashIndex(input.getOutpoint().getHash(), input.getOutpoint().getIndex());
|
HashIndex hashIndex = new HashIndex(input.getOutpoint().getHash(), input.getOutpoint().getIndex());
|
||||||
TransactionOutput output = spentOutputs.get(hashIndex);
|
Script scriptPubKey = spentScriptPubKeys.get(hashIndex);
|
||||||
if(output == null) {
|
if(scriptPubKey == null) {
|
||||||
throw new IllegalStateException("No output found for input " + input.getOutpoint());
|
throw new IllegalStateException("No scriptPubKey found for input " + input.getOutpoint().getHash() + ":" + input.getOutpoint().getIndex());
|
||||||
}
|
}
|
||||||
for(ScriptType scriptType : SCRIPT_TYPES) {
|
for(ScriptType scriptType : SCRIPT_TYPES) {
|
||||||
if(scriptType.isScriptType(output.getScript())) {
|
if(scriptType.isScriptType(scriptPubKey)) {
|
||||||
switch(scriptType) {
|
switch(scriptType) {
|
||||||
case P2TR:
|
case P2TR:
|
||||||
keys.add(ScriptType.P2TR.getPublicKeyFromScript(output.getScript()));
|
keys.add(ScriptType.P2TR.getPublicKeyFromScript(scriptPubKey));
|
||||||
break;
|
break;
|
||||||
case P2WPKH:
|
case P2WPKH:
|
||||||
case P2SH_P2WPKH:
|
case P2SH_P2WPKH:
|
||||||
|
|
@ -84,10 +84,13 @@ public class SilentPaymentUtils {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean spendsInvalidSegwitOutput(Transaction tx, Map<HashIndex, TransactionOutput> spentOutputs) {
|
public static boolean spendsInvalidSegwitOutput(Transaction tx, Map<HashIndex, Script> spentScriptPubKeys) {
|
||||||
for(TransactionInput input : tx.getInputs()) {
|
for(TransactionInput input : tx.getInputs()) {
|
||||||
HashIndex hashIndex = new HashIndex(input.getOutpoint().getHash(), input.getOutpoint().getIndex());
|
HashIndex hashIndex = new HashIndex(input.getOutpoint().getHash(), input.getOutpoint().getIndex());
|
||||||
Script scriptPubKey = spentOutputs.get(hashIndex).getScript();
|
Script scriptPubKey = spentScriptPubKeys.get(hashIndex);
|
||||||
|
if(scriptPubKey == null) {
|
||||||
|
throw new IllegalStateException("No scriptPubKey found for input " + input.getOutpoint().getHash() + ":" + input.getOutpoint().getIndex());
|
||||||
|
}
|
||||||
List<ScriptChunk> chunks = scriptPubKey.getChunks();
|
List<ScriptChunk> chunks = scriptPubKey.getChunks();
|
||||||
if(chunks.size() == 2 && chunks.getFirst().isOpCode() && chunks.get(1).getData() != null
|
if(chunks.size() == 2 && chunks.getFirst().isOpCode() && chunks.get(1).getData() != null
|
||||||
&& chunks.getFirst().getOpcode() >= ScriptOpCodes.OP_2 && chunks.getFirst().getOpcode() <= ScriptOpCodes.OP_16) {
|
&& chunks.getFirst().getOpcode() >= ScriptOpCodes.OP_2 && chunks.getFirst().getOpcode() <= ScriptOpCodes.OP_16) {
|
||||||
|
|
@ -98,16 +101,16 @@ public class SilentPaymentUtils {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] getTweak(Transaction tx, Map<HashIndex, TransactionOutput> spentOutputs) {
|
public static byte[] getTweak(Transaction tx, Map<HashIndex, Script> spentScriptPubKeys) {
|
||||||
if(tx.getOutputs().stream().noneMatch(output -> ScriptType.P2TR.isScriptType(output.getScript()))) {
|
if(tx.getOutputs().stream().noneMatch(output -> ScriptType.P2TR.isScriptType(output.getScript()))) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(spendsInvalidSegwitOutput(tx, spentOutputs)) {
|
if(spendsInvalidSegwitOutput(tx, spentScriptPubKeys)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ECKey> inputKeys = getInputPubKeys(tx, spentOutputs);
|
List<ECKey> inputKeys = getInputPubKeys(tx, spentScriptPubKeys);
|
||||||
if(inputKeys.isEmpty()) {
|
if(inputKeys.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,15 @@ public class SilentPaymentUtilsTest {
|
||||||
transaction.addInput(Sha256Hash.wrap("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d"), 0, new Script(Utils.hexToBytes("48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792")));
|
transaction.addInput(Sha256Hash.wrap("a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d"), 0, new Script(Utils.hexToBytes("48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792")));
|
||||||
transaction.addOutput(1000L, ScriptType.P2TR.getOutputScript(ECKey.fromPublicOnly(Utils.hexToBytes("3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1"))));
|
transaction.addOutput(1000L, ScriptType.P2TR.getOutputScript(ECKey.fromPublicOnly(Utils.hexToBytes("3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1"))));
|
||||||
|
|
||||||
Map<HashIndex, TransactionOutput> spentOutputs = new HashMap<>();
|
Map<HashIndex, Script> spentScriptPubKeys = new HashMap<>();
|
||||||
HashIndex hashIndex0 = new HashIndex(transaction.getInputs().getFirst().getOutpoint().getHash(), transaction.getInputs().getFirst().getOutpoint().getIndex());
|
HashIndex hashIndex0 = new HashIndex(transaction.getInputs().getFirst().getOutpoint().getHash(), transaction.getInputs().getFirst().getOutpoint().getIndex());
|
||||||
TransactionOutput spentOutput0 = new TransactionOutput(new Transaction(), 400L, new Script(Utils.hexToBytes("76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac")));
|
Script spentScriptPubKey0 = new Script(Utils.hexToBytes("76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"));
|
||||||
HashIndex hashIndex1 = new HashIndex(transaction.getInputs().getLast().getOutpoint().getHash(), transaction.getInputs().getLast().getOutpoint().getIndex());
|
HashIndex hashIndex1 = new HashIndex(transaction.getInputs().getLast().getOutpoint().getHash(), transaction.getInputs().getLast().getOutpoint().getIndex());
|
||||||
TransactionOutput spentOutput1 = new TransactionOutput(new Transaction(), 400L, new Script(Utils.hexToBytes("76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac")));
|
Script spentScriptPubKey1 = new Script(Utils.hexToBytes("76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac"));
|
||||||
spentOutputs.put(hashIndex0, spentOutput0);
|
spentScriptPubKeys.put(hashIndex0, spentScriptPubKey0);
|
||||||
spentOutputs.put(hashIndex1, spentOutput1);
|
spentScriptPubKeys.put(hashIndex1, spentScriptPubKey1);
|
||||||
|
|
||||||
byte[] tweak = SilentPaymentUtils.getTweak(transaction, spentOutputs);
|
byte[] tweak = SilentPaymentUtils.getTweak(transaction, spentScriptPubKeys);
|
||||||
Assertions.assertNotNull(tweak);
|
Assertions.assertNotNull(tweak);
|
||||||
Assertions.assertEquals("024ac253c216532e961988e2a8ce266a447c894c781e52ef6cee902361db960004", Utils.bytesToHex(tweak));
|
Assertions.assertEquals("024ac253c216532e961988e2a8ce266a447c894c781e52ef6cee902361db960004", Utils.bytesToHex(tweak));
|
||||||
}
|
}
|
||||||
|
|
@ -37,15 +37,15 @@ public class SilentPaymentUtilsTest {
|
||||||
transaction.addInput(Sha256Hash.wrap("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16"), 0, new Script(Utils.hexToBytes("48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792")));
|
transaction.addInput(Sha256Hash.wrap("f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16"), 0, new Script(Utils.hexToBytes("48304602210086783ded73e961037e77d49d9deee4edc2b23136e9728d56e4491c80015c3a63022100fda4c0f21ea18de29edbce57f7134d613e044ee150a89e2e64700de2d4e83d4e2103bd85685d03d111699b15d046319febe77f8de5286e9e512703cdee1bf3be3792")));
|
||||||
transaction.addOutput(1000L, ScriptType.P2TR.getOutputScript(ECKey.fromPublicOnly(Utils.hexToBytes("3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1"))));
|
transaction.addOutput(1000L, ScriptType.P2TR.getOutputScript(ECKey.fromPublicOnly(Utils.hexToBytes("3e9fce73d4e77a4809908e3c3a2e54ee147b9312dc5044a193d1fc85de46e3c1"))));
|
||||||
|
|
||||||
Map<HashIndex, TransactionOutput> spentOutputs = new HashMap<>();
|
Map<HashIndex, Script> spentScriptPubKeys = new HashMap<>();
|
||||||
HashIndex hashIndex0 = new HashIndex(transaction.getInputs().getFirst().getOutpoint().getHash(), transaction.getInputs().getFirst().getOutpoint().getIndex());
|
HashIndex hashIndex0 = new HashIndex(transaction.getInputs().getFirst().getOutpoint().getHash(), transaction.getInputs().getFirst().getOutpoint().getIndex());
|
||||||
TransactionOutput spentOutput0 = new TransactionOutput(new Transaction(), 400L, new Script(Utils.hexToBytes("76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac")));
|
Script spentScriptPubKey0 = new Script(Utils.hexToBytes("76a91419c2f3ae0ca3b642bd3e49598b8da89f50c1416188ac"));
|
||||||
HashIndex hashIndex1 = new HashIndex(transaction.getInputs().getLast().getOutpoint().getHash(), transaction.getInputs().getLast().getOutpoint().getIndex());
|
HashIndex hashIndex1 = new HashIndex(transaction.getInputs().getLast().getOutpoint().getHash(), transaction.getInputs().getLast().getOutpoint().getIndex());
|
||||||
TransactionOutput spentOutput1 = new TransactionOutput(new Transaction(), 400L, new Script(Utils.hexToBytes("76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac")));
|
Script spentScriptPubKey1 = new Script(Utils.hexToBytes("76a914d9317c66f54ff0a152ec50b1d19c25be50c8e15988ac"));
|
||||||
spentOutputs.put(hashIndex0, spentOutput0);
|
spentScriptPubKeys.put(hashIndex0, spentScriptPubKey0);
|
||||||
spentOutputs.put(hashIndex1, spentOutput1);
|
spentScriptPubKeys.put(hashIndex1, spentScriptPubKey1);
|
||||||
|
|
||||||
byte[] tweak = SilentPaymentUtils.getTweak(transaction, spentOutputs);
|
byte[] tweak = SilentPaymentUtils.getTweak(transaction, spentScriptPubKeys);
|
||||||
Assertions.assertNotNull(tweak);
|
Assertions.assertNotNull(tweak);
|
||||||
Assertions.assertEquals("024ac253c216532e961988e2a8ce266a447c894c781e52ef6cee902361db960004", Utils.bytesToHex(tweak));
|
Assertions.assertEquals("024ac253c216532e961988e2a8ce266a447c894c781e52ef6cee902361db960004", Utils.bytesToHex(tweak));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue