fix transaction signature ordering bug

This commit is contained in:
Craig Raw 2020-08-10 13:19:24 +02:00
parent fff658a3ab
commit 1a38b8e662
4 changed files with 101 additions and 46 deletions

View file

@ -909,4 +909,26 @@ public class ECKey implements EncryptableItem {
public String toString() { public String toString() {
return pub.toString(); return pub.toString();
} }
public static class LexicographicECKeyComparator implements Comparator<ECKey> {
@Override
public int compare(ECKey leftKey, ECKey rightKey) {
byte[] left = leftKey.getPubKey();
byte[] right = rightKey.getPubKey();
int minLength = Math.min(left.length, right.length);
for (int i = 0; i < minLength; i++) {
int result = compare(left[i], right[i]);
if (result != 0) {
return result;
}
}
return left.length - right.length;
}
public static int compare(byte a, byte b) {
return Byte.toUnsignedInt(a) - Byte.toUnsignedInt(b);
}
}
} }

View file

@ -111,12 +111,12 @@ public enum ScriptType {
} }
@Override @Override
public Script getMultisigScriptSig(Script scriptPubKey, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public Script getMultisigScriptSig(Script scriptPubKey, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
throw new ProtocolException(getName() + " is not a multisig script type"); throw new ProtocolException(getName() + " is not a multisig script type");
} }
@Override @Override
public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
throw new ProtocolException(getName() + " is not a multisig script type"); throw new ProtocolException(getName() + " is not a multisig script type");
} }
@ -219,12 +219,12 @@ public enum ScriptType {
} }
@Override @Override
public Script getMultisigScriptSig(Script scriptPubKey, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public Script getMultisigScriptSig(Script scriptPubKey, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
throw new ProtocolException(getName() + " is not a multisig script type"); throw new ProtocolException(getName() + " is not a multisig script type");
} }
@Override @Override
public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
throw new ProtocolException(getName() + " is not a multisig script type"); throw new ProtocolException(getName() + " is not a multisig script type");
} }
@ -274,7 +274,7 @@ public enum ScriptType {
} }
@Override @Override
public Script getOutputScript(int threshold, List<ECKey> pubKeys) { public Script getOutputScript(int threshold, Collection<ECKey> pubKeys) {
if(threshold > pubKeys.size()) { if(threshold > pubKeys.size()) {
throw new ProtocolException("Threshold of " + threshold + " is greater than number of pubKeys provided (" + pubKeys.size() + ")"); throw new ProtocolException("Threshold of " + threshold + " is greater than number of pubKeys provided (" + pubKeys.size() + ")");
} }
@ -382,11 +382,13 @@ public enum ScriptType {
} }
@Override @Override
public Script getMultisigScriptSig(Script scriptPubKey, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public Script getMultisigScriptSig(Script scriptPubKey, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
if(!isScriptType(scriptPubKey)) { if(!isScriptType(scriptPubKey)) {
throw new ProtocolException("Provided scriptPubKey is not a " + getName() + " script"); throw new ProtocolException("Provided scriptPubKey is not a " + getName() + " script");
} }
if(threshold != signatures.size()) {
List<TransactionSignature> signatures = pubKeySignatures.values().stream().filter(Objects::nonNull).collect(Collectors.toList());
if(signatures.size() < threshold) {
throw new ProtocolException("Only " + signatures.size() + " signatures provided to meet a multisig threshold of " + threshold); throw new ProtocolException("Only " + signatures.size() + " signatures provided to meet a multisig threshold of " + threshold);
} }
@ -402,8 +404,8 @@ public enum ScriptType {
} }
@Override @Override
public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
Script scriptSig = getMultisigScriptSig(prevOutput.getScript(), threshold, pubKeys, signatures); Script scriptSig = getMultisigScriptSig(prevOutput.getScript(), threshold, pubKeySignatures);
return transaction.addInput(prevOutput.getHash(), prevOutput.getIndex(), scriptSig); return transaction.addInput(prevOutput.getHash(), prevOutput.getIndex(), scriptSig);
} }
@ -503,17 +505,17 @@ public enum ScriptType {
} }
@Override @Override
public Script getMultisigScriptSig(Script scriptPubKey, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public Script getMultisigScriptSig(Script scriptPubKey, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
if(!isScriptType(scriptPubKey)) { if(!isScriptType(scriptPubKey)) {
throw new ProtocolException("Provided scriptPubKey is not a " + getName() + " script"); throw new ProtocolException("Provided scriptPubKey is not a " + getName() + " script");
} }
Script redeemScript = MULTISIG.getOutputScript(threshold, pubKeys); Script redeemScript = MULTISIG.getOutputScript(threshold, pubKeySignatures.keySet());
if(!scriptPubKey.equals(getOutputScript(redeemScript))) { if(!scriptPubKey.equals(getOutputScript(redeemScript))) {
throw new ProtocolException("P2SH scriptPubKey hash does not match constructed redeem script hash"); throw new ProtocolException("P2SH scriptPubKey hash does not match constructed redeem script hash");
} }
Script multisigScript = MULTISIG.getMultisigScriptSig(redeemScript, threshold, pubKeys, signatures); Script multisigScript = MULTISIG.getMultisigScriptSig(redeemScript, threshold, pubKeySignatures);
List<ScriptChunk> chunks = new ArrayList<>(multisigScript.getChunks()); List<ScriptChunk> chunks = new ArrayList<>(multisigScript.getChunks());
ScriptChunk redeemScriptChunk = ScriptChunk.fromData(redeemScript.getProgram()); ScriptChunk redeemScriptChunk = ScriptChunk.fromData(redeemScript.getProgram());
chunks.add(redeemScriptChunk); chunks.add(redeemScriptChunk);
@ -522,8 +524,8 @@ public enum ScriptType {
} }
@Override @Override
public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
Script scriptSig = getMultisigScriptSig(prevOutput.getScript(), threshold, pubKeys, signatures); Script scriptSig = getMultisigScriptSig(prevOutput.getScript(), threshold, pubKeySignatures);
return transaction.addInput(prevOutput.getHash(), prevOutput.getIndex(), scriptSig); return transaction.addInput(prevOutput.getHash(), prevOutput.getIndex(), scriptSig);
} }
@ -616,12 +618,12 @@ public enum ScriptType {
} }
@Override @Override
public Script getMultisigScriptSig(Script scriptPubKey, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public Script getMultisigScriptSig(Script scriptPubKey, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
throw new ProtocolException(getName() + " is not a multisig script type"); throw new ProtocolException(getName() + " is not a multisig script type");
} }
@Override @Override
public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
throw new ProtocolException(getName() + " is not a multisig script type"); throw new ProtocolException(getName() + " is not a multisig script type");
} }
@ -698,12 +700,12 @@ public enum ScriptType {
} }
@Override @Override
public Script getMultisigScriptSig(Script scriptPubKey, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public Script getMultisigScriptSig(Script scriptPubKey, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
if(!isScriptType(scriptPubKey)) { if(!isScriptType(scriptPubKey)) {
throw new ProtocolException("Provided scriptPubKey is not a " + getName() + " script"); throw new ProtocolException("Provided scriptPubKey is not a " + getName() + " script");
} }
Script witnessScript = MULTISIG.getOutputScript(threshold, pubKeys); Script witnessScript = MULTISIG.getOutputScript(threshold, pubKeySignatures.keySet());
Script redeemScript = P2WSH.getOutputScript(witnessScript); Script redeemScript = P2WSH.getOutputScript(witnessScript);
if(!scriptPubKey.equals(P2SH.getOutputScript(redeemScript))) { if(!scriptPubKey.equals(P2SH.getOutputScript(redeemScript))) {
throw new ProtocolException("P2SH scriptPubKey hash does not match constructed redeem script hash"); throw new ProtocolException("P2SH scriptPubKey hash does not match constructed redeem script hash");
@ -714,10 +716,10 @@ public enum ScriptType {
} }
@Override @Override
public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
Script scriptSig = getMultisigScriptSig(prevOutput.getScript(), threshold, pubKeys, signatures); Script scriptSig = getMultisigScriptSig(prevOutput.getScript(), threshold, pubKeySignatures);
Script witnessScript = MULTISIG.getOutputScript(threshold, pubKeys); Script witnessScript = MULTISIG.getOutputScript(threshold, pubKeySignatures.keySet());
TransactionWitness witness = new TransactionWitness(transaction, signatures, witnessScript); TransactionWitness witness = new TransactionWitness(transaction, pubKeySignatures.values().stream().filter(Objects::nonNull).collect(Collectors.toList()), witnessScript);
return transaction.addInput(prevOutput.getHash(), prevOutput.getIndex(), scriptSig, witness); return transaction.addInput(prevOutput.getHash(), prevOutput.getIndex(), scriptSig, witness);
} }
@ -812,12 +814,12 @@ public enum ScriptType {
} }
@Override @Override
public Script getMultisigScriptSig(Script scriptPubKey, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public Script getMultisigScriptSig(Script scriptPubKey, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
throw new ProtocolException(getName() + " is not a multisig script type"); throw new ProtocolException(getName() + " is not a multisig script type");
} }
@Override @Override
public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
throw new ProtocolException(getName() + " is not a multisig script type"); throw new ProtocolException(getName() + " is not a multisig script type");
} }
@ -906,12 +908,12 @@ public enum ScriptType {
} }
@Override @Override
public Script getMultisigScriptSig(Script scriptPubKey, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public Script getMultisigScriptSig(Script scriptPubKey, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
if(!isScriptType(scriptPubKey)) { if(!isScriptType(scriptPubKey)) {
throw new ProtocolException("Provided scriptPubKey is not a " + getName() + " script"); throw new ProtocolException("Provided scriptPubKey is not a " + getName() + " script");
} }
Script witnessScript = MULTISIG.getOutputScript(threshold, pubKeys); Script witnessScript = MULTISIG.getOutputScript(threshold, pubKeySignatures.keySet());
if(!scriptPubKey.equals(P2WSH.getOutputScript(witnessScript))) { if(!scriptPubKey.equals(P2WSH.getOutputScript(witnessScript))) {
throw new ProtocolException("P2WSH scriptPubKey hash does not match constructed witness script hash"); throw new ProtocolException("P2WSH scriptPubKey hash does not match constructed witness script hash");
} }
@ -920,10 +922,10 @@ public enum ScriptType {
} }
@Override @Override
public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures) { public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
Script scriptSig = getMultisigScriptSig(prevOutput.getScript(), threshold, pubKeys, signatures); Script scriptSig = getMultisigScriptSig(prevOutput.getScript(), threshold, pubKeySignatures);
Script witnessScript = MULTISIG.getOutputScript(threshold, pubKeys); Script witnessScript = MULTISIG.getOutputScript(threshold, pubKeySignatures.keySet());
TransactionWitness witness = new TransactionWitness(transaction, signatures, witnessScript); TransactionWitness witness = new TransactionWitness(transaction, pubKeySignatures.values().stream().filter(Objects::nonNull).collect(Collectors.toList()), witnessScript);
return transaction.addInput(prevOutput.getHash(), prevOutput.getIndex(), scriptSig, witness); return transaction.addInput(prevOutput.getHash(), prevOutput.getIndex(), scriptSig, witness);
} }
@ -993,7 +995,7 @@ public enum ScriptType {
public abstract Script getOutputScript(Script script); public abstract Script getOutputScript(Script script);
public Script getOutputScript(int threshold, List<ECKey> pubKeys) { public Script getOutputScript(int threshold, Collection<ECKey> pubKeys) {
throw new UnsupportedOperationException("Only defined for MULTISIG script type"); throw new UnsupportedOperationException("Only defined for MULTISIG script type");
} }
@ -1025,9 +1027,9 @@ public enum ScriptType {
public abstract TransactionInput addSpendingInput(Transaction transaction, TransactionOutput prevOutput, ECKey pubKey, TransactionSignature signature); public abstract TransactionInput addSpendingInput(Transaction transaction, TransactionOutput prevOutput, ECKey pubKey, TransactionSignature signature);
public abstract Script getMultisigScriptSig(Script scriptPubKey, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures); public abstract Script getMultisigScriptSig(Script scriptPubKey, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures);
public abstract TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, List<ECKey> pubKeys, List<TransactionSignature> signatures); public abstract TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures);
public static final ScriptType[] SINGLE_HASH_TYPES = {P2PKH, P2SH, P2SH_P2WPKH, P2SH_P2WSH, P2WPKH, P2WSH}; public static final ScriptType[] SINGLE_HASH_TYPES = {P2PKH, P2SH, P2SH_P2WPKH, P2SH_P2WSH, P2WPKH, P2WSH};

View file

@ -2,6 +2,7 @@ package com.sparrowwallet.drongo.wallet;
import com.sparrowwallet.drongo.BitcoinUnit; import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.crypto.ECKey; import com.sparrowwallet.drongo.crypto.ECKey;
import com.sparrowwallet.drongo.crypto.Key; import com.sparrowwallet.drongo.crypto.Key;
@ -14,7 +15,6 @@ import com.sparrowwallet.drongo.psbt.PSBTInput;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static com.sparrowwallet.drongo.protocol.Transaction.WITNESS_SCALE_FACTOR; import static com.sparrowwallet.drongo.protocol.Transaction.WITNESS_SCALE_FACTOR;
@ -390,11 +390,11 @@ public class Wallet {
} else if(getPolicyType().equals(PolicyType.MULTI)) { } else if(getPolicyType().equals(PolicyType.MULTI)) {
List<ECKey> pubKeys = getPubKeys(receiveNode); List<ECKey> pubKeys = getPubKeys(receiveNode);
int threshold = getDefaultPolicy().getNumSignaturesRequired(); int threshold = getDefaultPolicy().getNumSignaturesRequired();
List<TransactionSignature> signatures = new ArrayList<>(threshold); Map<ECKey, TransactionSignature> pubKeySignatures = new TreeMap<>(new ECKey.LexicographicECKeyComparator());
for(int i = 0; i < threshold; i++) { for(int i = 0; i < pubKeys.size(); i++) {
signatures.add(TransactionSignature.dummy()); pubKeySignatures.put(pubKeys.get(i), i < threshold ? TransactionSignature.dummy() : null);
} }
txInput = getScriptType().addMultisigSpendingInput(transaction, prevTxOut, threshold, pubKeys, signatures); txInput = getScriptType().addMultisigSpendingInput(transaction, prevTxOut, threshold, pubKeySignatures);
} }
assert txInput != null; assert txInput != null;
@ -491,8 +491,11 @@ public class Wallet {
} else if(getPolicyType().equals(PolicyType.MULTI)) { } else if(getPolicyType().equals(PolicyType.MULTI)) {
List<ECKey> pubKeys = getPubKeys(walletNode); List<ECKey> pubKeys = getPubKeys(walletNode);
int threshold = getDefaultPolicy().getNumSignaturesRequired(); int threshold = getDefaultPolicy().getNumSignaturesRequired();
List<TransactionSignature> signatures = IntStream.range(0, threshold).mapToObj(i -> TransactionSignature.dummy()).collect(Collectors.toList()); Map<ECKey, TransactionSignature> pubKeySignatures = new TreeMap<>(new ECKey.LexicographicECKeyComparator());
return getScriptType().addMultisigSpendingInput(transaction, prevTxOut, threshold, pubKeys, signatures); for(int i = 0; i < pubKeys.size(); i++) {
pubKeySignatures.put(pubKeys.get(i), i < threshold ? TransactionSignature.dummy() : null);
}
return getScriptType().addMultisigSpendingInput(transaction, prevTxOut, threshold, pubKeySignatures);
} else { } else {
throw new UnsupportedOperationException("Cannot create transaction for policy type " + getPolicyType()); throw new UnsupportedOperationException("Cannot create transaction for policy type " + getPolicyType());
} }
@ -647,6 +650,9 @@ public class Wallet {
} }
public void finalise(PSBT psbt) { public void finalise(PSBT psbt) {
System.out.println("Before finalise: ");
System.out.println(Utils.bytesToHex(psbt.serialize()));
int threshold = getDefaultPolicy().getNumSignaturesRequired(); int threshold = getDefaultPolicy().getNumSignaturesRequired();
Map<PSBTInput, WalletNode> signingNodes = getSigningNodes(psbt); Map<PSBTInput, WalletNode> signingNodes = getSigningNodes(psbt);
@ -683,12 +689,18 @@ public class Wallet {
finalizedTxInput = getScriptType().addSpendingInput(transaction, utxo, pubKey, transactionSignature); finalizedTxInput = getScriptType().addSpendingInput(transaction, utxo, pubKey, transactionSignature);
} else if(getPolicyType().equals(PolicyType.MULTI)) { } else if(getPolicyType().equals(PolicyType.MULTI)) {
List<ECKey> pubKeys = getPubKeys(signingNode); List<ECKey> pubKeys = getPubKeys(signingNode);
List<TransactionSignature> signatures = pubKeys.stream().map(psbtInput::getPartialSignature).filter(Objects::nonNull).collect(Collectors.toList());
Map<ECKey, TransactionSignature> pubKeySignatures = new TreeMap<>(new ECKey.LexicographicECKeyComparator());
for(ECKey pubKey : pubKeys) {
pubKeySignatures.put(pubKey, psbtInput.getPartialSignature(pubKey));
}
List<TransactionSignature> signatures = pubKeySignatures.values().stream().filter(Objects::nonNull).collect(Collectors.toList());
if(signatures.size() < threshold) { if(signatures.size() < threshold) {
throw new IllegalArgumentException("Pubkeys of partial signatures do not match wallet pubkeys"); throw new IllegalArgumentException("Pubkeys of partial signatures do not match wallet pubkeys");
} }
finalizedTxInput = getScriptType().addMultisigSpendingInput(transaction, utxo, threshold, pubKeys, signatures); finalizedTxInput = getScriptType().addMultisigSpendingInput(transaction, utxo, threshold, pubKeySignatures);
} else { } else {
throw new UnsupportedOperationException("Cannot finalise PSBT for policy type " + getPolicyType()); throw new UnsupportedOperationException("Cannot finalise PSBT for policy type " + getPolicyType());
} }
@ -698,6 +710,9 @@ public class Wallet {
psbtInput.clearFinalised(); psbtInput.clearFinalised();
} }
} }
System.out.println("After finalise: ");
System.out.println(Utils.bytesToHex(psbt.serialize()));
} }
public BitcoinUnit getAutoUnit() { public BitcoinUnit getAutoUnit() {

View file

@ -9,7 +9,8 @@ import org.junit.Test;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.Map;
import java.util.TreeMap;
public class TransactionTest { public class TransactionTest {
@Test @Test
@ -302,8 +303,13 @@ public class TransactionTest {
ECKey key1 = redeemScript.getChunks().get(2).getPubKey(); ECKey key1 = redeemScript.getChunks().get(2).getPubKey();
ECKey key2 = redeemScript.getChunks().get(3).getPubKey(); ECKey key2 = redeemScript.getChunks().get(3).getPubKey();
Map<ECKey, TransactionSignature> pubKeySignatures = new TreeMap<>(new ECKey.LexicographicECKeyComparator());
pubKeySignatures.put(key0, signature0);
pubKeySignatures.put(key1, signature1);
pubKeySignatures.put(key2, null);
Transaction transaction = new Transaction(); Transaction transaction = new Transaction();
spent0ScriptType.addMultisigSpendingInput(transaction, spent0Output, 2, List.of(key0, key1, key2), List.of(signature0, signature1)); spent0ScriptType.addMultisigSpendingInput(transaction, spent0Output, 2, pubKeySignatures);
transaction.addOutput(833300, Address.fromString("31mKrRn3xQoGppLY5dU92Dbm4kN4ddkknE")); transaction.addOutput(833300, Address.fromString("31mKrRn3xQoGppLY5dU92Dbm4kN4ddkknE"));
transaction.addOutput(1222480000, Address.fromString("1CL9kj1seXif6agPfeh6vpKkzc2Hxq1UpM")); transaction.addOutput(1222480000, Address.fromString("1CL9kj1seXif6agPfeh6vpKkzc2Hxq1UpM"));
@ -376,9 +382,14 @@ public class TransactionTest {
ECKey key1 = witnessScript.getChunks().get(2).getPubKey(); ECKey key1 = witnessScript.getChunks().get(2).getPubKey();
ECKey key2 = witnessScript.getChunks().get(3).getPubKey(); ECKey key2 = witnessScript.getChunks().get(3).getPubKey();
Map<ECKey, TransactionSignature> pubKeySignatures = new TreeMap<>(new ECKey.LexicographicECKeyComparator());
pubKeySignatures.put(key0, signature0);
pubKeySignatures.put(key1, signature1);
pubKeySignatures.put(key2, null);
Transaction transaction = new Transaction(); Transaction transaction = new Transaction();
transaction.setSegwitVersion(1); transaction.setSegwitVersion(1);
TransactionInput input = ScriptType.P2SH_P2WSH.addMultisigSpendingInput(transaction, spent0Output, 2, List.of(key0, key1, key2), List.of(signature0, signature1)); TransactionInput input = ScriptType.P2SH_P2WSH.addMultisigSpendingInput(transaction, spent0Output, 2, pubKeySignatures);
transaction.addOutput(59287429, Address.fromString("3PBjKH4FRuEKy4sD3NfL7tqfZTG5K42owu")); transaction.addOutput(59287429, Address.fromString("3PBjKH4FRuEKy4sD3NfL7tqfZTG5K42owu"));
transaction.addOutput(212571, Address.fromString("3KRUgU4XGuErXkjBtFhksPzTGJ4AMwF4jB")); transaction.addOutput(212571, Address.fromString("3KRUgU4XGuErXkjBtFhksPzTGJ4AMwF4jB"));
@ -447,9 +458,14 @@ public class TransactionTest {
ECKey key1 = witnessScript.getChunks().get(2).getPubKey(); ECKey key1 = witnessScript.getChunks().get(2).getPubKey();
ECKey key2 = witnessScript.getChunks().get(3).getPubKey(); ECKey key2 = witnessScript.getChunks().get(3).getPubKey();
Map<ECKey, TransactionSignature> pubKeySignatures = new TreeMap<>(new ECKey.LexicographicECKeyComparator());
pubKeySignatures.put(key0, signature0);
pubKeySignatures.put(key1, signature1);
pubKeySignatures.put(key2, null);
Transaction transaction = new Transaction(); Transaction transaction = new Transaction();
transaction.setSegwitVersion(1); transaction.setSegwitVersion(1);
spent0ScriptType.addMultisigSpendingInput(transaction, spent0Output, 2, List.of(key0, key1, key2), List.of(signature0, signature1)); spent0ScriptType.addMultisigSpendingInput(transaction, spent0Output, 2, pubKeySignatures);
transaction.addOutput(10900000, Address.fromString("3Dt17mpd8FDXBjP56rCD7a4Sx7wpL91uhn")); transaction.addOutput(10900000, Address.fromString("3Dt17mpd8FDXBjP56rCD7a4Sx7wpL91uhn"));
transaction.addOutput(332500000, Address.fromString("1K6igqzm36x8jxRTavPhgWXLVcVZVDTGc9")); transaction.addOutput(332500000, Address.fromString("1K6igqzm36x8jxRTavPhgWXLVcVZVDTGc9"));