From ef97daac8998da2ae72ef6a6ac209c33a4c2223a Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Thu, 15 Oct 2020 12:52:18 -0300 Subject: [PATCH 01/10] Use bitcoinj 0.15.8 (commit fcec3da) --- build.gradle | 2 +- core/src/main/java/bisq/core/app/WalletAppSetup.java | 2 +- gradle/witness/gradle-witness.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index b8e2519e20b..fd827538fba 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,7 @@ configure(subprojects) { ext { // in alphabetical order bcVersion = '1.63' - bitcoinjVersion = 'a733034' + bitcoinjVersion = 'fcec3da' btcdCli4jVersion = '27b94333' codecVersion = '1.13' easybindVersion = '1.0.3' diff --git a/core/src/main/java/bisq/core/app/WalletAppSetup.java b/core/src/main/java/bisq/core/app/WalletAppSetup.java index c5d8b839f6f..bccd033ee0e 100644 --- a/core/src/main/java/bisq/core/app/WalletAppSetup.java +++ b/core/src/main/java/bisq/core/app/WalletAppSetup.java @@ -107,7 +107,7 @@ void init(@Nullable Consumer chainFileLockedExceptionHandler, Runnable downloadCompleteHandler, Runnable walletInitializedHandler) { log.info("Initialize WalletAppSetup with BitcoinJ version {} and hash of BitcoinJ commit {}", - VersionMessage.BITCOINJ_VERSION, "a733034"); + VersionMessage.BITCOINJ_VERSION, "fcec3da"); ObjectProperty walletServiceException = new SimpleObjectProperty<>(); btcInfoBinding = EasyBind.combine(walletsSetup.downloadPercentageProperty(), diff --git a/gradle/witness/gradle-witness.gradle b/gradle/witness/gradle-witness.gradle index 8aff7277f1e..cecaf6c8b74 100644 --- a/gradle/witness/gradle-witness.gradle +++ b/gradle/witness/gradle-witness.gradle @@ -23,7 +23,7 @@ dependencyVerification { 'com.github.bisq-network.netlayer:tor.external:a3606a592d6b6caa6a2fb7db224eaf43c6874c6730da4815bd37ad686b283dcb', 'com.github.bisq-network.netlayer:tor.native:b15aba7fe987185037791c7ec7c529cb001b90d723d047d54aab87aceb3b3d45', 'com.github.bisq-network.netlayer:tor:a974190aa3a031067ccd1dda28a3ae58cad14060792299d86ea38a05fb21afc5', - 'com.github.bisq-network:bitcoinj:b8b6e4b8010f2b8d4aac7141c0809dea6d102c3ff3c06ceba78c2626d531b0af', + 'com.github.bisq-network.bitcoinj:bitcoinj-core:8af7faa2155feff5afd1fa0fcea6fe7f7fa0d7ee977bdc648d1e73f3dcf2c754', 'com.github.cd2357.tor-binary:tor-binary-geoip:ae27b6aca1a3a50a046eb11e38202b6d21c2fcd2b8643bbeb5ea85e065fbc1be', 'com.github.cd2357.tor-binary:tor-binary-linux32:7b5d6770aa442ef6d235e8a9bfbaa7c62560690f9fe69ff03c7a752eae84f7dc', 'com.github.cd2357.tor-binary:tor-binary-linux64:24111fa35027599a750b0176392dc1e9417d919414396d1b221ac2e707eaba76', From 161e220a4f5464f263639e93fdbb89f468f0106f Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Wed, 30 Sep 2020 14:58:15 -0300 Subject: [PATCH 02/10] BtcWalletService: Use segwit addresses --- .../main/java/bisq/core/btc/wallet/BtcWalletService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index 27bb49a5b6c..375b23dd7a4 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -27,6 +27,7 @@ import bisq.core.provider.fee.FeeService; import bisq.core.user.Preferences; +import bisq.common.config.Config; import bisq.common.handlers.ErrorMessageHandler; import org.bitcoinj.core.Address; @@ -579,18 +580,17 @@ public AddressEntry getOrCreateAddressEntry(String offerId, AddressEntry.Context if (addressEntry.isPresent()) { return addressEntry.get(); } else { - // We still use non-segwit addresses for the trade protocol. // We try to use available and not yet used entries Optional emptyAvailableAddressEntry = getAddressEntryListAsImmutableList().stream() .filter(e -> AddressEntry.Context.AVAILABLE == e.getContext()) .filter(e -> isAddressUnused(e.getAddress())) - .filter(e -> Script.ScriptType.P2PKH.equals(e.getAddress().getOutputScriptType())) + .filter(e -> Script.ScriptType.P2WPKH.equals(e.getAddress().getOutputScriptType())) .findAny(); if (emptyAvailableAddressEntry.isPresent()) { return addressEntryList.swapAvailableToAddressEntryWithOfferId(emptyAvailableAddressEntry.get(), context, offerId); } else { - DeterministicKey key = (DeterministicKey) wallet.findKeyFromAddress(wallet.freshReceiveAddress(Script.ScriptType.P2PKH)); - AddressEntry entry = new AddressEntry(key, context, offerId, false); + DeterministicKey key = (DeterministicKey) wallet.findKeyFromAddress(wallet.freshReceiveAddress(Script.ScriptType.P2WPKH)); + AddressEntry entry = new AddressEntry(key, context, offerId, true); addressEntryList.addAddressEntry(entry); return entry; } From 06e5091f79024e5739e8b496f2aad79cf5795483 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Tue, 22 Sep 2020 18:28:54 -0300 Subject: [PATCH 03/10] TradeWalletService use P2WSH --- .../core/btc/wallet/TradeWalletService.java | 163 +++++++++++++----- .../buyer/BuyerSignsDelayedPayoutTx.java | 7 + .../seller/SellerSignsDelayedPayoutTx.java | 6 + .../windows/ManualPayoutTxWindow.java | 8 + 4 files changed, 144 insertions(+), 40 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index eb7b7485c10..840048c9cbb 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -502,12 +502,12 @@ private PreparedDepositTxAndMakerInputs makerCreatesDepositTx(boolean makerIsBuy // Add MultiSig output - Script p2SHMultiSigOutputScript = get2of2MultiSigOutputScript(buyerPubKey, sellerPubKey); + Script hashedMultiSigOutputScript = get2of2MultiSigOutputScript(buyerPubKey, sellerPubKey, false); // Tx fee for deposit tx will be paid by buyer. - TransactionOutput p2SHMultiSigOutput = new TransactionOutput(params, preparedDepositTx, msOutputAmount, - p2SHMultiSigOutputScript.getProgram()); - preparedDepositTx.addOutput(p2SHMultiSigOutput); + TransactionOutput hashedMultiSigOutput = new TransactionOutput(params, preparedDepositTx, msOutputAmount, + hashedMultiSigOutputScript.getProgram()); + preparedDepositTx.addOutput(hashedMultiSigOutput); // We add the hash ot OP_RETURN with a 0 amount output TransactionOutput contractHashOutput = new TransactionOutput(params, preparedDepositTx, Coin.ZERO, @@ -587,9 +587,9 @@ public Transaction takerSignsDepositTx(boolean takerIsSeller, checkArgument(!sellerInputs.isEmpty()); // Check if maker's MultiSig script is identical to the takers - Script p2SHMultiSigOutputScript = get2of2MultiSigOutputScript(buyerPubKey, sellerPubKey); - if (!makersDepositTx.getOutput(0).getScriptPubKey().equals(p2SHMultiSigOutputScript)) { - throw new TransactionVerificationException("Maker's p2SHMultiSigOutputScript does not match to takers p2SHMultiSigOutputScript"); + Script hashedMultiSigOutputScript = get2of2MultiSigOutputScript(buyerPubKey, sellerPubKey, false); + if (!makersDepositTx.getOutput(0).getScriptPubKey().equals(hashedMultiSigOutputScript)) { + throw new TransactionVerificationException("Maker's hashedMultiSigOutputScript does not match to takers hashedMultiSigOutputScript"); } // The outpoints are not available from the serialized makersDepositTx, so we cannot use that tx directly, but we use it to construct a new @@ -692,11 +692,11 @@ public Transaction createDelayedUnsignedPayoutTx(Transaction depositTx, Coin minerFee, long lockTime) throws AddressFormatException, TransactionVerificationException { - TransactionOutput p2SHMultiSigOutput = depositTx.getOutput(0); + TransactionOutput hashedMultiSigOutput = depositTx.getOutput(0); Transaction delayedPayoutTx = new Transaction(params); - delayedPayoutTx.addInput(p2SHMultiSigOutput); + delayedPayoutTx.addInput(hashedMultiSigOutput); applyLockTime(lockTime, delayedPayoutTx); - Coin outputAmount = p2SHMultiSigOutput.getValue().subtract(minerFee); + Coin outputAmount = hashedMultiSigOutput.getValue().subtract(minerFee); delayedPayoutTx.addOutput(outputAmount, Address.fromString(params, donationAddressString)); WalletService.printTx("Unsigned delayedPayoutTx ToDonationAddress", delayedPayoutTx); WalletService.verifyTransaction(delayedPayoutTx); @@ -704,13 +704,17 @@ public Transaction createDelayedUnsignedPayoutTx(Transaction depositTx, } public byte[] signDelayedPayoutTx(Transaction delayedPayoutTx, + Transaction preparedDepositTx, DeterministicKey myMultiSigKeyPair, byte[] buyerPubKey, byte[] sellerPubKey) throws AddressFormatException, TransactionVerificationException { Script redeemScript = get2of2MultiSigRedeemScript(buyerPubKey, sellerPubKey); - Sha256Hash sigHash = delayedPayoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); + Sha256Hash sigHash; + Coin delayedPayoutTxInputValue = preparedDepositTx.getOutput(0).getValue(); + sigHash = delayedPayoutTx.hashForWitnessSignature(0, redeemScript, + delayedPayoutTxInputValue, Transaction.SigHash.ALL, false); checkNotNull(myMultiSigKeyPair, "myMultiSigKeyPair must not be null"); if (myMultiSigKeyPair.isEncrypted()) { checkNotNull(aesKey); @@ -733,9 +737,10 @@ public Transaction finalizeDelayedPayoutTx(Transaction delayedPayoutTx, ECKey.ECDSASignature sellerECDSASignature = ECKey.ECDSASignature.decodeFromDER(sellerSignature); TransactionSignature buyerTxSig = new TransactionSignature(buyerECDSASignature, Transaction.SigHash.ALL, false); TransactionSignature sellerTxSig = new TransactionSignature(sellerECDSASignature, Transaction.SigHash.ALL, false); - Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(sellerTxSig, buyerTxSig), redeemScript); TransactionInput input = delayedPayoutTx.getInput(0); - input.setScriptSig(inputScript); + input.setScriptSig(ScriptBuilder.createEmpty()); + TransactionWitness witness = TransactionWitness.redeemP2WSH(redeemScript, sellerTxSig, buyerTxSig); + input.setWitness(witness); WalletService.printTx("finalizeDelayedPayoutTx", delayedPayoutTx); WalletService.verifyTransaction(delayedPayoutTx); WalletService.checkWalletConsistency(wallet); @@ -779,7 +784,15 @@ public byte[] buyerSignsPayoutTx(Transaction depositTx, // MS redeemScript Script redeemScript = get2of2MultiSigRedeemScript(buyerPubKey, sellerPubKey); // MS output from prev. tx is index 0 - Sha256Hash sigHash = preparedPayoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); + Sha256Hash sigHash; + TransactionOutput hashedMultiSigOutput = depositTx.getOutput(0); + if (ScriptPattern.isP2SH(hashedMultiSigOutput.getScriptPubKey())) { + sigHash = preparedPayoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); + } else { + Coin inputValue = hashedMultiSigOutput.getValue(); + sigHash = preparedPayoutTx.hashForWitnessSignature(0, redeemScript, + inputValue, Transaction.SigHash.ALL, false); + } checkNotNull(multiSigKeyPair, "multiSigKeyPair must not be null"); if (multiSigKeyPair.isEncrypted()) { checkNotNull(aesKey); @@ -822,7 +835,16 @@ public Transaction sellerSignsAndFinalizesPayoutTx(Transaction depositTx, // MS redeemScript Script redeemScript = get2of2MultiSigRedeemScript(buyerPubKey, sellerPubKey); // MS output from prev. tx is index 0 - Sha256Hash sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); + TransactionOutput hashedMultiSigOutput = depositTx.getOutput(0); + boolean hashedMultiSigOutputIsLegacy = ScriptPattern.isP2SH(hashedMultiSigOutput.getScriptPubKey()); + Sha256Hash sigHash; + if (hashedMultiSigOutputIsLegacy) { + sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); + } else { + Coin inputValue = hashedMultiSigOutput.getValue(); + sigHash = payoutTx.hashForWitnessSignature(0, redeemScript, + inputValue, Transaction.SigHash.ALL, false); + } checkNotNull(multiSigKeyPair, "multiSigKeyPair must not be null"); if (multiSigKeyPair.isEncrypted()) { checkNotNull(aesKey); @@ -832,10 +854,16 @@ public Transaction sellerSignsAndFinalizesPayoutTx(Transaction depositTx, Transaction.SigHash.ALL, false); TransactionSignature sellerTxSig = new TransactionSignature(sellerSignature, Transaction.SigHash.ALL, false); // Take care of order of signatures. Need to be reversed here. See comment below at getMultiSigRedeemScript (seller, buyer) - Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(sellerTxSig, buyerTxSig), - redeemScript); TransactionInput input = payoutTx.getInput(0); - input.setScriptSig(inputScript); + if (hashedMultiSigOutputIsLegacy) { + Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(sellerTxSig, buyerTxSig), + redeemScript); + input.setScriptSig(inputScript); + } else { + input.setScriptSig(ScriptBuilder.createEmpty()); + TransactionWitness witness = TransactionWitness.redeemP2WSH(redeemScript, sellerTxSig, buyerTxSig); + input.setWitness(witness); + } WalletService.printTx("payoutTx", payoutTx); WalletService.verifyTransaction(payoutTx); WalletService.checkWalletConsistency(wallet); @@ -863,7 +891,16 @@ public byte[] signMediatedPayoutTx(Transaction depositTx, // MS redeemScript Script redeemScript = get2of2MultiSigRedeemScript(buyerPubKey, sellerPubKey); // MS output from prev. tx is index 0 - Sha256Hash sigHash = preparedPayoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); + TransactionOutput hashedMultiSigOutput = depositTx.getOutput(0); + boolean hashedMultiSigOutputIsLegacy = ScriptPattern.isP2SH(hashedMultiSigOutput.getScriptPubKey()); + Sha256Hash sigHash; + if (hashedMultiSigOutputIsLegacy) { + sigHash = preparedPayoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); + } else { + Coin inputValue = hashedMultiSigOutput.getValue(); + sigHash = preparedPayoutTx.hashForWitnessSignature(0, redeemScript, + inputValue, Transaction.SigHash.ALL, false); + } checkNotNull(myMultiSigKeyPair, "myMultiSigKeyPair must not be null"); if (myMultiSigKeyPair.isEncrypted()) { checkNotNull(aesKey); @@ -895,9 +932,18 @@ public Transaction finalizeMediatedPayoutTx(Transaction depositTx, TransactionSignature sellerTxSig = new TransactionSignature(ECKey.ECDSASignature.decodeFromDER(sellerSignature), Transaction.SigHash.ALL, false); // Take care of order of signatures. Need to be reversed here. See comment below at getMultiSigRedeemScript (seller, buyer) - Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(sellerTxSig, buyerTxSig), redeemScript); + TransactionOutput hashedMultiSigOutput = depositTx.getOutput(0); + boolean hashedMultiSigOutputIsLegacy = ScriptPattern.isP2SH(hashedMultiSigOutput.getScriptPubKey()); TransactionInput input = payoutTx.getInput(0); - input.setScriptSig(inputScript); + if (hashedMultiSigOutputIsLegacy) { + Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(sellerTxSig, buyerTxSig), + redeemScript); + input.setScriptSig(inputScript); + } else { + input.setScriptSig(ScriptBuilder.createEmpty()); + TransactionWitness witness = TransactionWitness.redeemP2WSH(redeemScript, sellerTxSig, buyerTxSig); + input.setWitness(witness); + } WalletService.printTx("mediated payoutTx", payoutTx); WalletService.verifyTransaction(payoutTx); WalletService.checkWalletConsistency(wallet); @@ -945,9 +991,9 @@ public Transaction traderSignAndFinalizeDisputedPayoutTx(byte[] depositTxSeriali byte[] arbitratorPubKey) throws AddressFormatException, TransactionVerificationException, WalletException, SignatureDecodeException { Transaction depositTx = new Transaction(params, depositTxSerialized); - TransactionOutput p2SHMultiSigOutput = depositTx.getOutput(0); + TransactionOutput hashedMultiSigOutput = depositTx.getOutput(0); Transaction payoutTx = new Transaction(params); - payoutTx.addInput(p2SHMultiSigOutput); + payoutTx.addInput(hashedMultiSigOutput); if (buyerPayoutAmount.isPositive()) { payoutTx.addOutput(buyerPayoutAmount, Address.fromString(params, buyerAddressString)); } @@ -957,7 +1003,15 @@ public Transaction traderSignAndFinalizeDisputedPayoutTx(byte[] depositTxSeriali // take care of sorting! Script redeemScript = get2of3MultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey); - Sha256Hash sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); + Sha256Hash sigHash; + boolean hashedMultiSigOutputIsLegacy = !ScriptPattern.isP2SH(hashedMultiSigOutput.getScriptPubKey()); + if (hashedMultiSigOutputIsLegacy) { + sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); + } else { + Coin inputValue = hashedMultiSigOutput.getValue(); + sigHash = payoutTx.hashForWitnessSignature(0, redeemScript, + inputValue, Transaction.SigHash.ALL, false); + } checkNotNull(tradersMultiSigKeyPair, "tradersMultiSigKeyPair must not be null"); if (tradersMultiSigKeyPair.isEncrypted()) { checkNotNull(aesKey); @@ -966,11 +1020,18 @@ public Transaction traderSignAndFinalizeDisputedPayoutTx(byte[] depositTxSeriali TransactionSignature tradersTxSig = new TransactionSignature(tradersSignature, Transaction.SigHash.ALL, false); TransactionSignature arbitratorTxSig = new TransactionSignature(ECKey.ECDSASignature.decodeFromDER(arbitratorSignature), Transaction.SigHash.ALL, false); - // Take care of order of signatures. See comment below at getMultiSigRedeemScript (sort order needed here: arbitrator, seller, buyer) - Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(arbitratorTxSig, tradersTxSig), - redeemScript); TransactionInput input = payoutTx.getInput(0); - input.setScriptSig(inputScript); + // Take care of order of signatures. See comment below at getMultiSigRedeemScript (sort order needed here: arbitrator, seller, buyer) + if (hashedMultiSigOutputIsLegacy) { + Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript( + ImmutableList.of(arbitratorTxSig, tradersTxSig), + redeemScript); + input.setScriptSig(inputScript); + } else { + input.setScriptSig(ScriptBuilder.createEmpty()); + TransactionWitness witness = TransactionWitness.redeemP2WSH(redeemScript, arbitratorTxSig, tradersTxSig); + input.setWitness(witness); + } WalletService.printTx("disputed payoutTx", payoutTx); WalletService.verifyTransaction(payoutTx); WalletService.checkWalletConsistency(wallet); @@ -995,21 +1056,23 @@ public void emergencySignAndPublishPayoutTxFrom2of2MultiSig(String depositTxHex, String sellerPrivateKeyAsHex, String buyerPubKeyAsHex, String sellerPubKeyAsHex, + boolean hashedMultiSigOutputIsLegacy, TxBroadcaster.Callback callback) throws AddressFormatException, TransactionVerificationException, WalletException { byte[] buyerPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(buyerPubKeyAsHex)).getPubKey(); byte[] sellerPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(sellerPubKeyAsHex)).getPubKey(); - Script p2SHMultiSigOutputScript = get2of2MultiSigOutputScript(buyerPubKey, sellerPubKey); + Script hashedMultiSigOutputScript = get2of2MultiSigOutputScript(buyerPubKey, sellerPubKey, + hashedMultiSigOutputIsLegacy); - Coin msOutput = buyerPayoutAmount.add(sellerPayoutAmount).add(txFee); - TransactionOutput p2SHMultiSigOutput = new TransactionOutput(params, null, msOutput, p2SHMultiSigOutputScript.getProgram()); + Coin msOutputValue = buyerPayoutAmount.add(sellerPayoutAmount).add(txFee); + TransactionOutput hashedMultiSigOutput = new TransactionOutput(params, null, msOutputValue, hashedMultiSigOutputScript.getProgram()); Transaction depositTx = new Transaction(params); - depositTx.addOutput(p2SHMultiSigOutput); + depositTx.addOutput(hashedMultiSigOutput); Transaction payoutTx = new Transaction(params); Sha256Hash spendTxHash = Sha256Hash.wrap(depositTxHex); - payoutTx.addInput(new TransactionInput(params, depositTx, p2SHMultiSigOutputScript.getProgram(), new TransactionOutPoint(params, 0, spendTxHash), msOutput)); + payoutTx.addInput(new TransactionInput(params, depositTx, null, new TransactionOutPoint(params, 0, spendTxHash), msOutputValue)); if (buyerPayoutAmount.isPositive()) { payoutTx.addOutput(buyerPayoutAmount, Address.fromString(params, buyerAddressString)); @@ -1020,7 +1083,14 @@ public void emergencySignAndPublishPayoutTxFrom2of2MultiSig(String depositTxHex, // take care of sorting! Script redeemScript = get2of2MultiSigRedeemScript(buyerPubKey, sellerPubKey); - Sha256Hash sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); + Sha256Hash sigHash; + if (hashedMultiSigOutputIsLegacy) { + sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); + } else { + Coin inputValue = msOutputValue; + sigHash = payoutTx.hashForWitnessSignature(0, redeemScript, + inputValue, Transaction.SigHash.ALL, false); + } ECKey buyerPrivateKey = ECKey.fromPrivate(Utils.HEX.decode(buyerPrivateKeyAsHex)); checkNotNull(buyerPrivateKey, "key must not be null"); @@ -1032,10 +1102,18 @@ public void emergencySignAndPublishPayoutTxFrom2of2MultiSig(String depositTxHex, TransactionSignature buyerTxSig = new TransactionSignature(buyerECDSASignature, Transaction.SigHash.ALL, false); TransactionSignature sellerTxSig = new TransactionSignature(sellerECDSASignature, Transaction.SigHash.ALL, false); - Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(sellerTxSig, buyerTxSig), redeemScript); TransactionInput input = payoutTx.getInput(0); - input.setScriptSig(inputScript); + if (hashedMultiSigOutputIsLegacy) { + Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(sellerTxSig, buyerTxSig), + redeemScript); + input.setScriptSig(inputScript); + } else { + input.setScriptSig(ScriptBuilder.createEmpty()); + TransactionWitness witness = TransactionWitness.redeemP2WSH(redeemScript, sellerTxSig, buyerTxSig); + input.setWitness(witness); + } + WalletService.printTx("payoutTx", payoutTx); WalletService.verifyTransaction(payoutTx); WalletService.checkWalletConsistency(wallet); @@ -1144,8 +1222,13 @@ private Script get2of2MultiSigRedeemScript(byte[] buyerPubKey, byte[] sellerPubK return ScriptBuilder.createMultiSigOutputScript(2, keys); } - private Script get2of2MultiSigOutputScript(byte[] buyerPubKey, byte[] sellerPubKey) { - return ScriptBuilder.createP2SHOutputScript(get2of2MultiSigRedeemScript(buyerPubKey, sellerPubKey)); + private Script get2of2MultiSigOutputScript(byte[] buyerPubKey, byte[] sellerPubKey, boolean legacy) { + Script redeemScript = get2of2MultiSigRedeemScript(buyerPubKey, sellerPubKey); + if (legacy) { + return ScriptBuilder.createP2SHOutputScript(redeemScript); + } else { + return ScriptBuilder.createP2WSHOutputScript(redeemScript); + } } private Transaction createPayoutTx(Transaction depositTx, @@ -1153,9 +1236,9 @@ private Transaction createPayoutTx(Transaction depositTx, Coin sellerPayoutAmount, String buyerAddressString, String sellerAddressString) throws AddressFormatException { - TransactionOutput p2SHMultiSigOutput = depositTx.getOutput(0); + TransactionOutput hashedMultiSigOutput = depositTx.getOutput(0); Transaction transaction = new Transaction(params); - transaction.addInput(p2SHMultiSigOutput); + transaction.addInput(hashedMultiSigOutput); if (buyerPayoutAmount.isPositive()) { transaction.addOutput(buyerPayoutAmount, Address.fromString(params, buyerAddressString)); } diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSignsDelayedPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSignsDelayedPayoutTx.java index 2008b92b40a..d56cf2e8217 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSignsDelayedPayoutTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSignsDelayedPayoutTx.java @@ -24,6 +24,8 @@ import bisq.common.taskrunner.TaskRunner; +import org.bitcoinj.core.Coin; +import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Transaction; import org.bitcoinj.crypto.DeterministicKey; @@ -46,7 +48,11 @@ protected void run() { runInterceptHook(); Transaction preparedDelayedPayoutTx = checkNotNull(processModel.getPreparedDelayedPayoutTx()); + BtcWalletService btcWalletService = processModel.getBtcWalletService(); + NetworkParameters params = btcWalletService.getParams(); + Transaction preparedDepositTx = new Transaction(params, processModel.getPreparedDepositTx()); + String id = processModel.getOffer().getId(); byte[] buyerMultiSigPubKey = processModel.getMyMultiSigPubKey(); @@ -58,6 +64,7 @@ protected void run() { byte[] sellerMultiSigPubKey = processModel.getTradingPeer().getMultiSigPubKey(); byte[] delayedPayoutTxSignature = processModel.getTradeWalletService().signDelayedPayoutTx( preparedDelayedPayoutTx, + preparedDepositTx, myMultiSigKeyPair, buyerMultiSigPubKey, sellerMultiSigPubKey); diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignsDelayedPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignsDelayedPayoutTx.java index 0ae1a159201..4d110fba390 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignsDelayedPayoutTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignsDelayedPayoutTx.java @@ -24,6 +24,8 @@ import bisq.common.taskrunner.TaskRunner; +import org.bitcoinj.core.Coin; +import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Transaction; import org.bitcoinj.crypto.DeterministicKey; @@ -47,6 +49,9 @@ protected void run() { Transaction preparedDelayedPayoutTx = checkNotNull(processModel.getPreparedDelayedPayoutTx()); BtcWalletService btcWalletService = processModel.getBtcWalletService(); + NetworkParameters params = btcWalletService.getParams(); + Transaction preparedDepositTx = new Transaction(params, processModel.getPreparedDepositTx()); + String id = processModel.getOffer().getId(); byte[] sellerMultiSigPubKey = processModel.getMyMultiSigPubKey(); @@ -59,6 +64,7 @@ protected void run() { byte[] delayedPayoutTxSignature = processModel.getTradeWalletService().signDelayedPayoutTx( preparedDelayedPayoutTx, + preparedDepositTx, myMultiSigKeyPair, buyerMultiSigPubKey, sellerMultiSigPubKey); diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/ManualPayoutTxWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/ManualPayoutTxWindow.java index 6b89048980b..609c526fdec 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/ManualPayoutTxWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/ManualPayoutTxWindow.java @@ -40,6 +40,7 @@ import javax.inject.Inject; import javafx.scene.Scene; +import javafx.scene.control.CheckBox; import javafx.scene.input.KeyCode; import org.slf4j.Logger; @@ -47,6 +48,7 @@ import javax.annotation.Nullable; +import static bisq.desktop.util.FormBuilder.addCheckBox; import static bisq.desktop.util.FormBuilder.addInputTextField; // We don't translate here as it is for dev only purpose @@ -116,6 +118,8 @@ private void addContent() { InputTextField buyerPubKeyAsHex = addInputTextField(gridPane, ++rowIndex, "buyerPubKeyAsHex"); InputTextField sellerPubKeyAsHex = addInputTextField(gridPane, ++rowIndex, "sellerPubKeyAsHex"); + CheckBox depositTxLegacy = addCheckBox(gridPane, ++rowIndex, "depositTxLegacy"); + // Notes: // Open with alt+g // Priv key is only visible if pw protection is removed (wallet details data (alt+j)) @@ -136,6 +140,9 @@ private void addContent() { sellerPubKeyAsHex.setText(""); sellerPrivateKeyAsHex.setText(""); + depositTxLegacy.setAllowIndeterminate(false); + depositTxLegacy.setSelected(false); + actionButtonText("Sign and publish transaction"); TxBroadcaster.Callback callback = new TxBroadcaster.Callback() { @@ -167,6 +174,7 @@ public void onFailure(TxBroadcastException exception) { sellerPrivateKeyAsHex.getText(), buyerPubKeyAsHex.getText(), sellerPubKeyAsHex.getText(), + depositTxLegacy.isSelected(), callback); } catch (AddressFormatException | WalletException | TransactionVerificationException e) { log.error(e.toString()); From 4a05b6d6d5ef1175f4a3918ed500b580c186ef9e Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Tue, 6 Oct 2020 15:22:22 -0300 Subject: [PATCH 04/10] Revert "Construct dummy outputs with LegacyAddress" This reverts commit b8f5c6e970fc29b705478aac8a655a73bed52a7e. --- .../main/java/bisq/core/btc/wallet/TradeWalletService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index 840048c9cbb..7f6c28007b7 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -38,6 +38,7 @@ import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.SegwitAddress; import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.SignatureDecodeException; import org.bitcoinj.core.Transaction; @@ -336,7 +337,7 @@ OUT[0] dummyOutputAmount (inputAmount - tx fee) Transaction dummyTX = new Transaction(params); // The output is just used to get the right inputs and change outputs, so we use an anonymous ECKey, as it will never be used for anything. // We don't care about fee calculation differences between the real tx and that dummy tx as we use a static tx fee. - TransactionOutput dummyOutput = new TransactionOutput(params, dummyTX, dummyOutputAmount, LegacyAddress.fromKey(params, new ECKey())); + TransactionOutput dummyOutput = new TransactionOutput(params, dummyTX, dummyOutputAmount, SegwitAddress.fromKey(params, new ECKey())); dummyTX.addOutput(dummyOutput); // Find the needed inputs to pay the output, optionally add 1 change output. @@ -455,7 +456,7 @@ private PreparedDepositTxAndMakerInputs makerCreatesDepositTx(boolean makerIsBuy // First we construct a dummy TX to get the inputs and outputs we want to use for the real deposit tx. // Similar to the way we did in the createTakerDepositTxInputs method. Transaction dummyTx = new Transaction(params); - TransactionOutput dummyOutput = new TransactionOutput(params, dummyTx, makerInputAmount, LegacyAddress.fromKey(params, new ECKey())); + TransactionOutput dummyOutput = new TransactionOutput(params, dummyTx, makerInputAmount, SegwitAddress.fromKey(params, new ECKey())); dummyTx.addOutput(dummyOutput); addAvailableInputsAndChangeOutputs(dummyTx, makerAddress, makerChangeAddress); // Normally we have only 1 input but we support multiple inputs if the user has paid in with several transactions. From 22ba9a898b5ad4db64f4cc1f3089347afbb5a6c0 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Tue, 6 Oct 2020 16:31:20 -0300 Subject: [PATCH 05/10] Explain why bitcoinSerialize(false) is used --- .../main/java/bisq/core/btc/wallet/TradeWalletService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index 7f6c28007b7..0c0f4284be8 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -1171,6 +1171,10 @@ private RawTransactionInput getRawInputFromTransactionInput(@NotNull Transaction "input.getConnectedOutput().getParentTransaction() must not be null"); checkNotNull(input.getValue(), "input.getValue() must not be null"); + // bitcoinSerialize(false) is used just in case the serialized tx is parsed by a bisq node still using + // bitcoinj 0.14. This is not supposed to happen ever since Version.TRADE_PROTOCOL_VERSION was set to 3, + // but it costs nothing to be on the safe side. + // The serialized tx is just used to obtain its hash, so the witness data is not relevant. return new RawTransactionInput(input.getOutpoint().getIndex(), input.getConnectedOutput().getParentTransaction().bitcoinSerialize(false), input.getValue().value); From 3585dc95fcda1ef27ade1bd832ee35b7ab0b222c Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Tue, 6 Oct 2020 20:26:09 -0300 Subject: [PATCH 06/10] Create the scriptCode the right way --- .../main/java/bisq/core/btc/wallet/TradeWalletService.java | 7 ++----- core/src/main/java/bisq/core/btc/wallet/WalletService.java | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index 0c0f4284be8..ef1f8eca856 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -1275,11 +1275,8 @@ private void signInput(Transaction transaction, TransactionInput input, int inpu input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey)); } } else if (ScriptPattern.isP2WPKH(scriptPubKey)) { - // TODO: Consider using this alternative way to build the scriptCode (taken from bitcoinj master) - // Script scriptCode = ScriptBuilder.createP2PKHOutputScript(sigKey) - Script scriptCode = new ScriptBuilder().data( - ScriptBuilder.createOutputScript(LegacyAddress.fromKey(transaction.getParams(), sigKey)).getProgram()) - .build(); + // scriptCode is expected to have the format of a legacy P2PKH output script + Script scriptCode = ScriptBuilder.createP2PKHOutputScript(sigKey); Coin value = input.getValue(); TransactionSignature txSig = transaction.calculateWitnessSignature(inputIndex, sigKey, scriptCode, value, Transaction.SigHash.ALL, false); 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 251aac97172..e468a6a956b 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -325,11 +325,8 @@ public static void signTransactionInput(Wallet wallet, } } else if (ScriptPattern.isP2WPKH(scriptPubKey)) { try { - // 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(); + // scriptCode is expected to have the format of a legacy P2PKH output script + Script scriptCode = ScriptBuilder.createP2PKHOutputScript(key); Coin value = txIn.getValue(); TransactionSignature txSig = tx.calculateWitnessSignature(index, key, scriptCode, value, Transaction.SigHash.ALL, false); From 953a5f0bb547c8c390af49c6c8dd74a55aa62bd6 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Tue, 6 Oct 2020 21:15:07 -0300 Subject: [PATCH 07/10] Deal with P2WPKH has empty scriptSig --- .../bisq/core/btc/wallet/TradeWalletService.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index ef1f8eca856..d0861b12b76 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -602,7 +602,10 @@ public Transaction takerSignsDepositTx(boolean takerIsSeller, // We grab the signature from the makersDepositTx and apply it to the new tx input for (int i = 0; i < buyerInputs.size(); i++) { TransactionInput makersInput = makersDepositTx.getInputs().get(i); - byte[] makersScriptSigProgram = getMakersScriptSigProgram(makersInput); + byte[] makersScriptSigProgram = makersInput.getScriptSig().getProgram(); + if (makersScriptSigProgram.length == 0 && TransactionWitness.EMPTY.equals(makersInput.getWitness())) { + throw new TransactionVerificationException("Inputs from maker not signed."); + } TransactionInput input = getTransactionInput(depositTx, makersScriptSigProgram, buyerInputs.get(i)); if (!TransactionWitness.EMPTY.equals(makersInput.getWitness())) { input.setWitness(makersInput.getWitness()); @@ -1180,15 +1183,6 @@ private RawTransactionInput getRawInputFromTransactionInput(@NotNull Transaction input.getValue().value); } - private byte[] getMakersScriptSigProgram(TransactionInput transactionInput) throws TransactionVerificationException { - byte[] scriptProgram = transactionInput.getScriptSig().getProgram(); - if (scriptProgram.length == 0) { - throw new TransactionVerificationException("Inputs from maker not signed."); - } - - return scriptProgram; - } - private TransactionInput getTransactionInput(Transaction depositTx, byte[] scriptProgram, RawTransactionInput rawTransactionInput) { From d1620c4fd76f7024d40a503c2b1eabea8a786134 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Tue, 6 Oct 2020 15:20:24 -0300 Subject: [PATCH 08/10] Revert "Validate AddressEntry.segwit" This reverts commit e49c56527825a443b794ab74cee24b12d5b9eb45. --- core/src/main/java/bisq/core/btc/model/AddressEntry.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/model/AddressEntry.java b/core/src/main/java/bisq/core/btc/model/AddressEntry.java index a3a0387be2a..5348950b391 100644 --- a/core/src/main/java/bisq/core/btc/model/AddressEntry.java +++ b/core/src/main/java/bisq/core/btc/model/AddressEntry.java @@ -97,10 +97,6 @@ public AddressEntry(@NotNull DeterministicKey keyPair, Context context, @Nullable String offerId, boolean segwit) { - if (segwit && (!Context.AVAILABLE.equals(context) || offerId != null)) { - throw new IllegalArgumentException("Segwit addresses are only allowed for " + - "AVAILABLE entries without an offerId"); - } this.keyPair = keyPair; this.context = context; this.offerId = offerId; From a3d7c71410f0ad0f6aa7476e6d2deba9c29dda5b Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Thu, 15 Oct 2020 19:19:17 -0300 Subject: [PATCH 09/10] Set TRADE_PROTOCOL_VERSION to 3 --- common/src/main/java/bisq/common/app/Version.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/bisq/common/app/Version.java b/common/src/main/java/bisq/common/app/Version.java index 33efa828030..1b4ad6339b4 100644 --- a/common/src/main/java/bisq/common/app/Version.java +++ b/common/src/main/java/bisq/common/app/Version.java @@ -92,10 +92,13 @@ private static int getSubVersion(String version, int index) { // The version no. of the current protocol. The offer holds that version. // A taker will check the version of the offers to see if his version is compatible. - // Offers created with the old version will become invalid and have to be canceled. + // For the switch to version 2, offers created with the old version will become invalid and have to be canceled. + // For the switch to version 3, offers created with the old version can be migrated to version 3 just by opening + // the Bisq app. // VERSION = 0.5.0 -> TRADE_PROTOCOL_VERSION = 1 // Version 1.2.2 -> TRADE_PROTOCOL_VERSION = 2 - public static final int TRADE_PROTOCOL_VERSION = 2; + // Version 1.5.0 -> TRADE_PROTOCOL_VERSION = 3 + public static final int TRADE_PROTOCOL_VERSION = 3; private static int p2pMessageVersion; public static final String BSQ_TX_VERSION = "1"; From dffa251e1dc160b7b1bfe334b759d2b08de2c3d9 Mon Sep 17 00:00:00 2001 From: Oscar Guindzberg Date: Fri, 9 Oct 2020 16:52:20 -0300 Subject: [PATCH 10/10] Remove unused imports --- core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java | 1 - core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java | 1 - core/src/main/java/bisq/core/btc/wallet/WalletService.java | 1 - .../trade/protocol/tasks/buyer/BuyerSignsDelayedPayoutTx.java | 1 - .../trade/protocol/tasks/seller/SellerSignsDelayedPayoutTx.java | 1 - 5 files changed, 5 deletions(-) diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index 375b23dd7a4..2ab2e64e715 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -27,7 +27,6 @@ import bisq.core.provider.fee.FeeService; import bisq.core.user.Preferences; -import bisq.common.config.Config; import bisq.common.handlers.ErrorMessageHandler; import org.bitcoinj.core.Address; diff --git a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java index d0861b12b76..46d9cea8bb4 100644 --- a/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/TradeWalletService.java @@ -36,7 +36,6 @@ import org.bitcoinj.core.Coin; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.SegwitAddress; import org.bitcoinj.core.Sha256Hash; 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 e468a6a956b..fa285a3e557 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -37,7 +37,6 @@ 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; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSignsDelayedPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSignsDelayedPayoutTx.java index d56cf2e8217..32748a319c3 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSignsDelayedPayoutTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/buyer/BuyerSignsDelayedPayoutTx.java @@ -24,7 +24,6 @@ import bisq.common.taskrunner.TaskRunner; -import org.bitcoinj.core.Coin; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Transaction; import org.bitcoinj.crypto.DeterministicKey; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignsDelayedPayoutTx.java b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignsDelayedPayoutTx.java index 4d110fba390..556e556b418 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignsDelayedPayoutTx.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/seller/SellerSignsDelayedPayoutTx.java @@ -24,7 +24,6 @@ import bisq.common.taskrunner.TaskRunner; -import org.bitcoinj.core.Coin; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Transaction; import org.bitcoinj.crypto.DeterministicKey;