diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/bisq/core/btc/wallet/WalletService.java index b92cdc2410..f17122ac3b 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -17,6 +17,7 @@ package bisq.core.btc.wallet; +import bisq.core.btc.exceptions.SigningException; import bisq.core.btc.exceptions.TransactionVerificationException; import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.listeners.AddressConfidenceListener; @@ -37,12 +38,14 @@ import org.bitcoinj.core.Coin; import org.bitcoinj.core.Context; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.InsufficientMoneyException; +import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.core.TransactionInput; import org.bitcoinj.core.TransactionOutput; +import org.bitcoinj.core.TransactionWitness; import org.bitcoinj.core.VerificationException; import org.bitcoinj.core.listeners.NewBestBlockListener; import org.bitcoinj.core.listeners.TransactionConfidenceEventListener; @@ -51,6 +54,7 @@ import org.bitcoinj.crypto.KeyCrypter; import org.bitcoinj.crypto.KeyCrypterScrypt; import org.bitcoinj.crypto.TransactionSignature; import org.bitcoinj.script.Script; +import org.bitcoinj.script.ScriptBuilder; import org.bitcoinj.script.ScriptChunk; import org.bitcoinj.script.ScriptException; import org.bitcoinj.script.ScriptPattern; @@ -241,7 +245,7 @@ public abstract class WalletService { int inputIndex) throws TransactionVerificationException { try { checkNotNull(input.getConnectedOutput(), "input.getConnectedOutput() must not be null"); - input.getScriptSig().correctlySpends(transaction, inputIndex, input.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); + input.getScriptSig().correctlySpends(transaction, inputIndex, input.getWitness(), input.getValue(), input.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); } catch (Throwable t) { t.printStackTrace(); log.error(t.getMessage()); @@ -265,7 +269,7 @@ public abstract class WalletService { // We assume if it's already signed, it's hopefully got a SIGHASH type that will not invalidate when // we sign missing pieces (to check this would require either assuming any signatures are signing // standard output types or a way to get processed signatures out of script execution) - txIn.getScriptSig().correctlySpends(tx, index, txIn.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); + txIn.getScriptSig().correctlySpends(tx, index, txIn.getWitness(), txIn.getValue(), txIn.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", index); return; } catch (ScriptException e) { @@ -288,7 +292,7 @@ public abstract class WalletService { // We assume if it's already signed, it's hopefully got a SIGHASH type that will not invalidate when // we sign missing pieces (to check this would require either assuming any signatures are signing // standard output types or a way to get processed signatures out of script execution) - txIn.getScriptSig().correctlySpends(tx, index, txIn.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); + txIn.getScriptSig().correctlySpends(tx, index, txIn.getWitness(), txIn.getValue(), txIn.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS); log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", index); return; } catch (ScriptException e) { @@ -312,14 +316,31 @@ public abstract class WalletService { Script inputScript = txIn.getScriptSig(); byte[] script = redeemData.redeemScript.getProgram(); - try { - TransactionSignature signature = partialTx.calculateSignature(index, key, script, Transaction.SigHash.ALL, false); - inputScript = scriptPubKey.getScriptSigWithSignature(inputScript, signature.encodeToBitcoin(), 0); - txIn.setScriptSig(inputScript); - } catch (ECKey.KeyIsEncryptedException e1) { - throw e1; - } catch (ECKey.MissingPrivateKeyException e1) { - log.warn("No private key in keypair for input {}", index); + + if (ScriptPattern.isP2PK(scriptPubKey) || ScriptPattern.isP2PKH(scriptPubKey)) { + try { + TransactionSignature signature = partialTx.calculateSignature(index, key, script, Transaction.SigHash.ALL, false); + inputScript = scriptPubKey.getScriptSigWithSignature(inputScript, signature.encodeToBitcoin(), 0); + txIn.setScriptSig(inputScript); + } catch (ECKey.KeyIsEncryptedException e1) { + throw e1; + } catch (ECKey.MissingPrivateKeyException e1) { + log.warn("No private key in keypair for input {}", index); + } + } else if (ScriptPattern.isP2WPKH(scriptPubKey)) { + // TODO: Consider using this alternative way to build the scriptCode (taken from bitcoinj master) + // Script scriptCode = ScriptBuilder.createP2PKHOutputScript(key); + Script scriptCode = new ScriptBuilder().data( + ScriptBuilder.createOutputScript(LegacyAddress.fromKey(tx.getParams(), key)).getProgram()) + .build(); + Coin value = txIn.getValue(); + TransactionSignature txSig = tx.calculateWitnessSignature(index, key, scriptCode, value, + Transaction.SigHash.ALL, false); + txIn.setScriptSig(ScriptBuilder.createEmpty()); + txIn.setWitness(TransactionWitness.redeemP2WPKH(txSig, key)); + } else { + // log.error("Unexpected script type."); + throw new RuntimeException("Unexpected script type."); } } else { log.warn("Missing connected output, assuming input {} is already signed.", index); @@ -592,7 +613,7 @@ public abstract class WalletService { @Nullable public DeterministicKey findKeyFromPubKey(byte[] pubKey) { - return wallet.getActiveKeyChain().findKeyFromPubKey(pubKey); + return (DeterministicKey) wallet.findKeyFromPubKey(pubKey); } public boolean isEncrypted() {