mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-12-27 02:26:44 +00:00
fix transaction signature ordering bug
This commit is contained in:
parent
fff658a3ab
commit
1a38b8e662
4 changed files with 101 additions and 46 deletions
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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};
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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"));
|
||||||
|
|
Loading…
Reference in a new issue