From 1f34b3197e33e58dbc1b657b239c0034568bf12a Mon Sep 17 00:00:00 2001 From: Ben Kaufman Date: Fri, 15 Mar 2019 20:05:06 +0200 Subject: [PATCH 1/2] Added mining fee to un/lock bond popup. --- .../resources/i18n/displayStrings.properties | 4 +- .../main/dao/bonding/BondingViewUtils.java | 46 +++++++++++++------ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 36bb8f3d77a..34d26b22f84 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1435,9 +1435,9 @@ dao.bond.reputation.salt=Salt dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=Lockup dao.bond.reputation.lockup.headline=Confirm lockup transaction -dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nAre you sure you want to proceed? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\nMining fee: {3} ({4} Satoshis/byte)\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=Confirm unlock transaction -dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\nAre you sure you want to proceed? +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\nMining fee: {3} ({4} Satoshis/byte)\n\nAre you sure you want to proceed? dao.bond.allBonds.header=All bonds diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java index 59c22a6ba6d..38c27993237 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java @@ -24,6 +24,8 @@ import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.util.GUIUtil; +import bisq.core.btc.exceptions.TransactionVerificationException; +import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.setup.WalletsSetup; import bisq.core.dao.DaoFacade; import bisq.core.dao.governance.bond.lockup.LockupReason; @@ -36,10 +38,12 @@ import bisq.core.locale.Res; import bisq.core.util.BSFormatter; import bisq.core.util.BsqFormatter; +import bisq.core.util.CoinUtil; import bisq.network.p2p.P2PService; import bisq.common.app.DevEnv; +import bisq.common.util.Tuple2; import org.bitcoinj.core.Coin; import org.bitcoinj.core.InsufficientMoneyException; @@ -101,18 +105,29 @@ private void lockupBond(byte[] hash, Coin lockupAmount, int lockupTime, LockupRe Consumer resultHandler) { if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) { if (!DevEnv.isDevMode()) { - BSFormatter formatter = new BSFormatter(); - String duration = formatter.formatDurationAsWords(lockupTime * 10 * 60 * 1000L, false, false); - new Popup<>().headLine(Res.get("dao.bond.reputation.lockup.headline")) - .confirmation(Res.get("dao.bond.reputation.lockup.details", - bsqFormatter.formatCoinWithCode(lockupAmount), - lockupTime, - duration - )) - .actionButtonText(Res.get("shared.yes")) - .onAction(() -> publishLockupTx(hash, lockupAmount, lockupTime, lockupReason, resultHandler)) - .closeButtonText(Res.get("shared.cancel")) - .show(); + try { + Tuple2 miningFeeAndTxSize = daoFacade.getMiningFeeAndTxSize(lockupAmount); + Coin miningFee = miningFeeAndTxSize.first; + int txSize = miningFeeAndTxSize.second; + BSFormatter formatter = new BSFormatter(); + String duration = formatter.formatDurationAsWords(lockupTime * 10 * 60 * 1000L, false, false); + new Popup<>().headLine(Res.get("dao.bond.reputation.lockup.headline")) + .confirmation(Res.get("dao.bond.reputation.lockup.details", + bsqFormatter.formatCoinWithCode(lockupAmount), + lockupTime, + duration, + formatter.formatCoinWithCode(miningFee), + CoinUtil.getFeePerByte(miningFee, txSize) + )) + .actionButtonText(Res.get("shared.yes")) + .onAction(() -> publishLockupTx(hash, lockupAmount, lockupTime, lockupReason, resultHandler)) + .closeButtonText(Res.get("shared.cancel")) + .show(); + } catch (WalletException | InsufficientMoneyException | TransactionVerificationException e) { + log.error(e.toString()); + e.printStackTrace(); + new Popup<>().warning(e.getMessage()).show(); + } } else { publishLockupTx(hash, lockupAmount, lockupTime, lockupReason, resultHandler); } @@ -147,13 +162,18 @@ public void unLock(String lockupTxId, Consumer resultHandler) { try { if (!DevEnv.isDevMode()) { + Tuple2 miningFeeAndTxSize = daoFacade.getMiningFeeAndTxSize(unlockAmount); + Coin miningFee = miningFeeAndTxSize.first; + int txSize = miningFeeAndTxSize.second; BSFormatter formatter = new BSFormatter(); String duration = formatter.formatDurationAsWords(lockTime * 10 * 60 * 1000L, false, false); new Popup<>().headLine(Res.get("dao.bond.reputation.unlock.headline")) .confirmation(Res.get("dao.bond.reputation.unlock.details", bsqFormatter.formatCoinWithCode(unlockAmount), lockTime, - duration + duration, + formatter.formatCoinWithCode(miningFee), + CoinUtil.getFeePerByte(miningFee, txSize) )) .actionButtonText(Res.get("shared.yes")) .onAction(() -> publishUnlockTx(lockupTxId, resultHandler)) From 7c97a90f4a8c97547ece2f75536ea4bc152f39e1 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sat, 23 Mar 2019 17:06:28 -0500 Subject: [PATCH 2/2] Complete pull request Use lockup and unlock tx for miner fee and tx size display. The getMiningFeeAndTxSize in daoFacade was used for blind vote. Got renamed in the meantime to make it more clear. --- .../main/java/bisq/core/dao/DaoFacade.java | 12 +++++++++++ .../bond/lockup/LockupTxService.java | 18 ++++++++++++----- .../bond/unlock/UnlockTxService.java | 20 ++++++++++++++----- .../resources/i18n/displayStrings.properties | 6 ++++-- .../main/dao/bonding/BondingViewUtils.java | 20 +++++++++---------- 5 files changed, 54 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/DaoFacade.java b/core/src/main/java/bisq/core/dao/DaoFacade.java index 253c344ba77..2a273f1fa3f 100644 --- a/core/src/main/java/bisq/core/dao/DaoFacade.java +++ b/core/src/main/java/bisq/core/dao/DaoFacade.java @@ -90,6 +90,8 @@ import javafx.collections.ObservableList; import javafx.collections.transformation.FilteredList; +import java.io.IOException; + import java.util.List; import java.util.Optional; import java.util.Set; @@ -509,11 +511,21 @@ public void publishLockupTx(Coin lockupAmount, int lockTime, LockupReason lockup lockupTxService.publishLockupTx(lockupAmount, lockTime, lockupReason, hash, resultHandler, exceptionHandler); } + public Tuple2 getLockupTxMiningFeeAndTxSize(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash) + throws InsufficientMoneyException, IOException, TransactionVerificationException, WalletException { + return lockupTxService.getMiningFeeAndTxSize(lockupAmount, lockTime, lockupReason, hash); + } + public void publishUnlockTx(String lockupTxId, Consumer resultHandler, ExceptionHandler exceptionHandler) { unlockTxService.publishUnlockTx(lockupTxId, resultHandler, exceptionHandler); } + public Tuple2 getUnlockTxMiningFeeAndTxSize(String lockupTxId) + throws InsufficientMoneyException, TransactionVerificationException, WalletException { + return unlockTxService.getMiningFeeAndTxSize(lockupTxId); + } + public long getTotalLockupAmount() { return daoStateService.getTotalLockupAmount(); } diff --git a/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupTxService.java b/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupTxService.java index f78ba1b2d77..0a8884df35e 100644 --- a/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupTxService.java +++ b/core/src/main/java/bisq/core/dao/governance/bond/lockup/LockupTxService.java @@ -28,6 +28,7 @@ import bisq.core.dao.state.model.blockchain.TxType; import bisq.common.handlers.ExceptionHandler; +import bisq.common.util.Tuple2; import org.bitcoinj.core.Coin; import org.bitcoinj.core.InsufficientMoneyException; @@ -71,9 +72,7 @@ public void publishLockupTx(Coin lockupAmount, int lockTime, LockupReason lockup checkArgument(lockTime <= BondConsensus.getMaxLockTime() && lockTime >= BondConsensus.getMinLockTime(), "lockTime not in range"); try { - byte[] opReturnData = BondConsensus.getLockupOpReturnData(lockTime, lockupReason, hash); - Transaction lockupTx = createLockupTx(lockupAmount, opReturnData); - + Transaction lockupTx = getLockupTx(lockupAmount, lockTime, lockupReason, hash); walletsManager.publishAndCommitBsqTx(lockupTx, TxType.LOCKUP, new TxBroadcaster.Callback() { @Override public void onSuccess(Transaction transaction) { @@ -92,8 +91,17 @@ public void onFailure(TxBroadcastException exception) { } } - private Transaction createLockupTx(Coin lockupAmount, byte[] opReturnData) - throws InsufficientMoneyException, WalletException, TransactionVerificationException { + public Tuple2 getMiningFeeAndTxSize(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash) + throws InsufficientMoneyException, WalletException, TransactionVerificationException, IOException { + Transaction tx = getLockupTx(lockupAmount, lockTime, lockupReason, hash); + Coin miningFee = tx.getFee(); + int txSize = tx.bitcoinSerialize().length; + return new Tuple2<>(miningFee, txSize); + } + + private Transaction getLockupTx(Coin lockupAmount, int lockTime, LockupReason lockupReason, byte[] hash) + throws InsufficientMoneyException, WalletException, TransactionVerificationException, IOException { + byte[] opReturnData = BondConsensus.getLockupOpReturnData(lockTime, lockupReason, hash); Transaction preparedTx = bsqWalletService.getPreparedLockupTx(lockupAmount); Transaction txWithBtcFee = btcWalletService.completePreparedBsqTx(preparedTx, true, opReturnData); Transaction transaction = bsqWalletService.signTx(txWithBtcFee); diff --git a/core/src/main/java/bisq/core/dao/governance/bond/unlock/UnlockTxService.java b/core/src/main/java/bisq/core/dao/governance/bond/unlock/UnlockTxService.java index c07bffe4a85..9bde263d360 100644 --- a/core/src/main/java/bisq/core/dao/governance/bond/unlock/UnlockTxService.java +++ b/core/src/main/java/bisq/core/dao/governance/bond/unlock/UnlockTxService.java @@ -29,7 +29,9 @@ import bisq.core.dao.state.model.blockchain.TxType; import bisq.common.handlers.ExceptionHandler; +import bisq.common.util.Tuple2; +import org.bitcoinj.core.Coin; import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.Transaction; @@ -70,10 +72,7 @@ public UnlockTxService(WalletsManager walletsManager, public void publishUnlockTx(String lockupTxId, Consumer resultHandler, ExceptionHandler exceptionHandler) { try { - Optional optionalLockupTxOutput = daoStateService.getLockupTxOutput(lockupTxId); - checkArgument(optionalLockupTxOutput.isPresent(), "lockupTxOutput must be present"); - TxOutput lockupTxOutput = optionalLockupTxOutput.get(); - Transaction unlockTx = getUnlockTx(lockupTxOutput); + Transaction unlockTx = getUnlockTx(lockupTxId); walletsManager.publishAndCommitBsqTx(unlockTx, TxType.UNLOCK, new TxBroadcaster.Callback() { @Override public void onSuccess(Transaction transaction) { @@ -90,8 +89,19 @@ public void onFailure(TxBroadcastException exception) { } } - private Transaction getUnlockTx(TxOutput lockupTxOutput) + public Tuple2 getMiningFeeAndTxSize(String lockupTxId) throws InsufficientMoneyException, WalletException, TransactionVerificationException { + Transaction tx = getUnlockTx(lockupTxId); + Coin miningFee = tx.getFee(); + int txSize = tx.bitcoinSerialize().length; + return new Tuple2<>(miningFee, txSize); + } + + private Transaction getUnlockTx(String lockupTxId) + throws InsufficientMoneyException, WalletException, TransactionVerificationException { + Optional optionalLockupTxOutput = daoStateService.getLockupTxOutput(lockupTxId); + checkArgument(optionalLockupTxOutput.isPresent(), "lockupTxOutput must be present"); + TxOutput lockupTxOutput = optionalLockupTxOutput.get(); Transaction preparedTx = bsqWalletService.getPreparedUnlockTx(lockupTxOutput); Transaction txWithBtcFee = btcWalletService.completePreparedBsqTx(preparedTx, true, null); Transaction transaction = bsqWalletService.signTx(txWithBtcFee); diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index b766f04c913..53524d1eb16 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1460,9 +1460,11 @@ dao.bond.reputation.salt=Salt dao.bond.reputation.hash=Hash dao.bond.reputation.lockupButton=Lockup dao.bond.reputation.lockup.headline=Confirm lockup transaction -dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\nMining fee: {3} ({4} Satoshis/byte)\n\nAre you sure you want to proceed? +dao.bond.reputation.lockup.details=Lockup amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\n\ + Mining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.reputation.unlock.headline=Confirm unlock transaction -dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\nMining fee: {3} ({4} Satoshis/byte)\n\nAre you sure you want to proceed? +dao.bond.reputation.unlock.details=Unlock amount: {0}\nUnlock time: {1} block(s) (≈{2})\n\n\ + Mining fee: {3} ({4} Satoshis/byte)\nTransaction size: {5} Kb\n\nAre you sure you want to proceed? dao.bond.allBonds.header=All bonds diff --git a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java b/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java index 12f2a164df0..80d22115b90 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/bonding/BondingViewUtils.java @@ -24,8 +24,6 @@ import bisq.desktop.main.overlays.popups.Popup; import bisq.desktop.util.GUIUtil; -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.setup.WalletsSetup; import bisq.core.dao.DaoFacade; import bisq.core.dao.governance.bond.lockup.LockupReason; @@ -109,7 +107,7 @@ private void lockupBond(byte[] hash, Coin lockupAmount, int lockupTime, LockupRe if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) { if (!DevEnv.isDevMode()) { try { - Tuple2 miningFeeAndTxSize = daoFacade.getMiningFeeAndTxSize(lockupAmount); + Tuple2 miningFeeAndTxSize = daoFacade.getLockupTxMiningFeeAndTxSize(lockupAmount, lockupTime, lockupReason, hash); Coin miningFee = miningFeeAndTxSize.first; int txSize = miningFeeAndTxSize.second; BSFormatter formatter = new BSFormatter(); @@ -120,26 +118,27 @@ private void lockupBond(byte[] hash, Coin lockupAmount, int lockupTime, LockupRe lockupTime, duration, formatter.formatCoinWithCode(miningFee), - CoinUtil.getFeePerByte(miningFee, txSize) + CoinUtil.getFeePerByte(miningFee, txSize), + txSize )) .actionButtonText(Res.get("shared.yes")) - .onAction(() -> publishLockupTx(hash, lockupAmount, lockupTime, lockupReason, resultHandler)) + .onAction(() -> publishLockupTx(lockupAmount, lockupTime, lockupReason, hash, resultHandler)) .closeButtonText(Res.get("shared.cancel")) .show(); - } catch (WalletException | InsufficientMoneyException | TransactionVerificationException e) { + } catch (Throwable e) { log.error(e.toString()); e.printStackTrace(); new Popup<>().warning(e.getMessage()).show(); } } else { - publishLockupTx(hash, lockupAmount, lockupTime, lockupReason, resultHandler); + publishLockupTx(lockupAmount, lockupTime, lockupReason, hash, resultHandler); } } else { GUIUtil.showNotReadyForTxBroadcastPopups(p2PService, walletsSetup); } } - private void publishLockupTx(byte[] hash, Coin lockupAmount, int lockupTime, LockupReason lockupReason, Consumer resultHandler) { + private void publishLockupTx(Coin lockupAmount, int lockupTime, LockupReason lockupReason, byte[] hash, Consumer resultHandler) { daoFacade.publishLockupTx(lockupAmount, lockupTime, lockupReason, @@ -169,7 +168,7 @@ public void unLock(String lockupTxId, Consumer resultHandler) { try { if (!DevEnv.isDevMode()) { - Tuple2 miningFeeAndTxSize = daoFacade.getMiningFeeAndTxSize(unlockAmount); + Tuple2 miningFeeAndTxSize = daoFacade.getUnlockTxMiningFeeAndTxSize(lockupTxId); Coin miningFee = miningFeeAndTxSize.first; int txSize = miningFeeAndTxSize.second; BSFormatter formatter = new BSFormatter(); @@ -180,7 +179,8 @@ public void unLock(String lockupTxId, Consumer resultHandler) { lockTime, duration, formatter.formatCoinWithCode(miningFee), - CoinUtil.getFeePerByte(miningFee, txSize) + CoinUtil.getFeePerByte(miningFee, txSize), + txSize )) .actionButtonText(Res.get("shared.yes")) .onAction(() -> publishUnlockTx(lockupTxId, resultHandler))