From 5e3b68453c5aeeec5b5d61683b0280d355eb025f Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 21 Apr 2017 22:57:45 -0500 Subject: [PATCH] Add Tx type column. Display all wallet txs also invalid or unverified --- .../resources/i18n/displayStrings.properties | 9 ++ .../core/btc/wallet/BsqWalletService.java | 14 ++- .../dao/blockchain/parse/BsqChainState.java | 15 ++- gui/src/main/java/io/bisq/gui/bisq.css | 11 ++ .../dao/wallet/receive/BsqReceiveView.java | 2 +- .../gui/main/dao/wallet/tx/BsqTxListItem.java | 8 ++ .../gui/main/dao/wallet/tx/BsqTxView.java | 106 ++++++++++++++---- .../java/io/bisq/gui/util/BsqFormatter.java | 6 +- .../gui/util/validation/BsqValidator.java | 1 - 9 files changed, 130 insertions(+), 42 deletions(-) diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index f3460d5aba..d6febf2096 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -958,6 +958,15 @@ dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequ dao.wallet.bsqFee=BSQ fee payment dao.wallet.chainHeight=Synced with blockchain height: {0} (chain tip height: {1}) +dao.wallet.tx.type=Type +dao.tx.type.enum.UNDEFINED=Unverified BSQ transaction +dao.tx.type.enum.GENESIS=Genesis transaction +dao.tx.type.enum.SEND_BSQ=Send BSQ transaction +dao.tx.type.enum.PAY_TRADE_FEE=Pay trading fee transaction +dao.tx.type.enum.COMPENSATION_REQUEST=Compensation request transaction +dao.tx.type.enum.VOTE=Voting transaction +dao.tx.type.enum.ISSUANCE=Issuance transaction + dao.compensation.create.confirm.info=Compensation request fee: {0}\nMining fee: {1} ({2} Satoshis/byte)\nTransaction size: {3} Kb\n\nAre you sure you want to publish the compensation request? diff --git a/core/src/main/java/io/bisq/core/btc/wallet/BsqWalletService.java b/core/src/main/java/io/bisq/core/btc/wallet/BsqWalletService.java index 8ae1639929..4ea8d63da0 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/BsqWalletService.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/BsqWalletService.java @@ -28,7 +28,6 @@ import io.bisq.core.provider.fee.FeeService; import io.bisq.core.user.Preferences; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.bitcoinj.core.*; import org.bitcoinj.script.Script; @@ -54,7 +53,6 @@ public class BsqWalletService extends WalletService { private final BsqCoinSelector bsqCoinSelector; private BsqChainState bsqChainState; - @Getter private final ObservableList walletTransactions = FXCollections.observableArrayList(); private final CopyOnWriteArraySet bsqBalanceListeners = new CopyOnWriteArraySet<>(); private Coin availableBsqBalance = Coin.ZERO; @@ -187,8 +185,13 @@ public class BsqWalletService extends WalletService { // BSQ TransactionOutputs and Transactions /////////////////////////////////////////////////////////////////////////////////////////// + public ObservableList getWalletTransactions() { + return walletTransactions; + } + private void updateBsqWalletTransactions() { - walletTransactions.setAll(getBsqWalletTransactions()); + walletTransactions.setAll(getTransactions(false)); + // walletTransactions.setAll(getBsqWalletTransactions()); updateBsqBalance(); } @@ -199,7 +202,7 @@ public class BsqWalletService extends WalletService { .collect(Collectors.toSet()); } - public Set getInvalidBsqTransactions() { + public Set getUnverifiedBsqTransactions() { Set bsqWalletTransactions = getBsqWalletTransactions(); Set walletTxs = getTransactions(false).stream().collect(Collectors.toSet()); checkArgument(walletTxs.size() >= bsqWalletTransactions.size(), @@ -330,7 +333,6 @@ public class BsqWalletService extends WalletService { /////////////////////////////////////////////////////////////////////////////////////////// public Address getUnusedAddress() { - //TODO check if current address was used, otherwise get fresh - return wallet.freshReceiveAddress(); + return wallet.currentReceiveAddress(); } } diff --git a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java index ce77f7634e..c3079eb5d2 100644 --- a/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java +++ b/core/src/main/java/io/bisq/core/dao/blockchain/parse/BsqChainState.java @@ -29,10 +29,7 @@ import io.bisq.core.app.BisqEnvironment; import io.bisq.core.btc.BitcoinNetwork; import io.bisq.core.dao.DaoOptionKeys; import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException; -import io.bisq.core.dao.blockchain.vo.BsqBlock; -import io.bisq.core.dao.blockchain.vo.Tx; -import io.bisq.core.dao.blockchain.vo.TxIdIndexTuple; -import io.bisq.core.dao.blockchain.vo.TxOutput; +import io.bisq.core.dao.blockchain.vo.*; import lombok.extern.slf4j.Slf4j; import javax.inject.Inject; @@ -83,8 +80,8 @@ public class BsqChainState implements Persistable { // block 450000 2017-01-25 // REG TEST - private static final String REG_TEST_GENESIS_TX_ID = "3bc7bc9484e112ec8ddd1a1c984379819245ac463b9ce40fa8b5bf771c0f9236"; - private static final int REG_TEST_GENESIS_BLOCK_HEIGHT = 102; + private static final String REG_TEST_GENESIS_TX_ID = "389d631bb48bd2f74fcc88c3506e2b03114b18b4e396c3bd2b8bb7d7ff9ee0d6"; + private static final int REG_TEST_GENESIS_BLOCK_HEIGHT = 1441; // TEST NET // https://testnet.blockexplorer.com/block/00000000f1cd94c6ccc458a922f2a42c975c3447180f0db1e56322a26ab3f0ec private static final String TEST_NET_GENESIS_TX_ID = "8853756990acfc1784aac1ee1a50d331c915a46876bb4ad98f260ef2d35da845"; @@ -305,6 +302,12 @@ public class BsqChainState implements Persistable { }); } + public Optional getTxType(String txId) { + return lock.read(() -> { + return getTx(txId).map(Tx::getTxType); + }); + } + public boolean containsTx(String txId) { return lock.read(() -> { return getTx(txId).isPresent(); diff --git a/gui/src/main/java/io/bisq/gui/bisq.css b/gui/src/main/java/io/bisq/gui/bisq.css index f8d549b7ba..7cd47e3406 100644 --- a/gui/src/main/java/io/bisq/gui/bisq.css +++ b/gui/src/main/java/io/bisq/gui/bisq.css @@ -218,6 +218,17 @@ bg color of non edit textFields: fafafa -fx-cursor: hand; } +.dao-tx-type-invalid-icon { + -fx-text-fill: -bs-error-red; + -fx-cursor: hand; +} + +.dao-tx-type-default-icon { + -fx-text-fill: #666; + -fx-cursor: hand; +} + + /******************************************************************************* * * * Tooltip * diff --git a/gui/src/main/java/io/bisq/gui/main/dao/wallet/receive/BsqReceiveView.java b/gui/src/main/java/io/bisq/gui/main/dao/wallet/receive/BsqReceiveView.java index f12e83f748..3fb90e60d3 100644 --- a/gui/src/main/java/io/bisq/gui/main/dao/wallet/receive/BsqReceiveView.java +++ b/gui/src/main/java/io/bisq/gui/main/dao/wallet/receive/BsqReceiveView.java @@ -83,7 +83,7 @@ public class BsqReceiveView extends ActivatableView { amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> { addressTextField.setAmountAsCoin(bsqFormatter.parseToCoin(t)); }); - addressTextField.setAddress(bsqFormatter.getBsqAddressStringFromAddress(bsqWalletService.freshReceiveAddress())); + addressTextField.setAddress(bsqFormatter.getBsqAddressStringFromAddress(bsqWalletService.getUnusedAddress())); } @Override diff --git a/gui/src/main/java/io/bisq/gui/main/dao/wallet/tx/BsqTxListItem.java b/gui/src/main/java/io/bisq/gui/main/dao/wallet/tx/BsqTxListItem.java index 601a8db940..2f41c327bd 100644 --- a/gui/src/main/java/io/bisq/gui/main/dao/wallet/tx/BsqTxListItem.java +++ b/gui/src/main/java/io/bisq/gui/main/dao/wallet/tx/BsqTxListItem.java @@ -22,10 +22,12 @@ import io.bisq.core.btc.listeners.TxConfidenceListener; import io.bisq.core.btc.wallet.BsqWalletService; import io.bisq.core.btc.wallet.BtcWalletService; import io.bisq.core.btc.wallet.WalletUtils; +import io.bisq.core.dao.blockchain.vo.TxType; import io.bisq.gui.components.indicator.TxConfidenceIndicator; import io.bisq.gui.util.BsqFormatter; import io.bisq.gui.util.GUIUtil; import javafx.scene.control.Tooltip; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; @@ -35,12 +37,14 @@ import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.core.TransactionOutput; import java.util.Date; +import java.util.Optional; import static com.google.common.base.Preconditions.checkNotNull; @ToString @Slf4j +@EqualsAndHashCode class BsqTxListItem { @Getter private final Transaction transaction; @@ -61,6 +65,8 @@ class BsqTxListItem { @Getter private boolean received; @Getter + private Optional txType; + @Getter private boolean isBurnedBsqTx; private BsqFormatter bsqFormatter; @Getter @@ -71,11 +77,13 @@ class BsqTxListItem { public BsqTxListItem(Transaction transaction, BsqWalletService bsqWalletService, BtcWalletService btcWalletService, + Optional txType, boolean isBurnedBsqTx, BsqFormatter bsqFormatter) { this.transaction = transaction; this.bsqWalletService = bsqWalletService; this.btcWalletService = btcWalletService; + this.txType = txType; this.isBurnedBsqTx = isBurnedBsqTx; this.bsqFormatter = bsqFormatter; diff --git a/gui/src/main/java/io/bisq/gui/main/dao/wallet/tx/BsqTxView.java b/gui/src/main/java/io/bisq/gui/main/dao/wallet/tx/BsqTxView.java index f947710dbb..d24886a1fb 100644 --- a/gui/src/main/java/io/bisq/gui/main/dao/wallet/tx/BsqTxView.java +++ b/gui/src/main/java/io/bisq/gui/main/dao/wallet/tx/BsqTxView.java @@ -17,6 +17,7 @@ package io.bisq.gui.main.dao.wallet.tx; +import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; import io.bisq.common.locale.Res; import io.bisq.core.btc.wallet.BsqWalletService; @@ -24,14 +25,13 @@ import io.bisq.core.btc.wallet.BtcWalletService; import io.bisq.core.dao.blockchain.BsqBlockchainManager; import io.bisq.core.dao.blockchain.BsqChainStateListener; import io.bisq.core.dao.blockchain.parse.BsqChainState; -import io.bisq.core.user.DontShowAgainLookup; +import io.bisq.core.dao.blockchain.vo.TxType; import io.bisq.core.user.Preferences; import io.bisq.gui.common.view.ActivatableView; import io.bisq.gui.common.view.FxmlView; import io.bisq.gui.components.AddressWithIconAndDirection; import io.bisq.gui.components.HyperlinkWithIcon; import io.bisq.gui.main.dao.wallet.BsqBalanceUtil; -import io.bisq.gui.main.overlays.popups.Popup; import io.bisq.gui.util.BsqFormatter; import io.bisq.gui.util.FormBuilder; import io.bisq.gui.util.GUIUtil; @@ -112,6 +112,7 @@ public class BsqTxView extends ActivatableView { addAddressColumn(); addAmountColumn(); addConfidenceColumn(); + addTxTypeColumn(); chainHeightLabel = FormBuilder.addLabel(root, ++gridRow, ""); chainHeightLabel.setId("num-offers"); @@ -170,37 +171,20 @@ public class BsqTxView extends ActivatableView { private void updateList() { observableList.forEach(BsqTxListItem::cleanup); - // clone to avoid ConcurrentModificationException + // copy list to avoid ConcurrentModificationException final List walletTransactions = new ArrayList<>(bsqWalletService.getWalletTransactions()); - Set list = walletTransactions.stream() + Set items = walletTransactions.stream() .map(transaction -> { - // The burned fee is added to all outputs of a tx, so we just ask at index 0 return new BsqTxListItem(transaction, bsqWalletService, btcWalletService, + bsqChainState.getTxType(transaction.getHashAsString()), bsqChainState.hasTxBurntFee(transaction.getHashAsString()), bsqFormatter); } ) .collect(Collectors.toSet()); - observableList.setAll(list); - - final Set invalidBsqTransactions = bsqWalletService.getInvalidBsqTransactions(); - if (!invalidBsqTransactions.isEmpty() && bsqBlockchainManager.isParseBlockchainComplete()) { - Set txIds = invalidBsqTransactions.stream() - .filter(t -> t != null) - .map(Transaction::getHashAsString).collect(Collectors.toSet()); - - log.error("invalidBsqTransactions " + txIds); - String key = "invalidBsqTransactionsWarning_" + txIds; - if (DontShowAgainLookup.showAgain(key)) - new Popup<>().warning("We detected invalid Bsq transactions.\n" + - "This must not happen if you used the bisq application only to send or receive BSQ.\n\n" + - "invalidBsqTransactionIds=" + txIds.toString()) - .width(800) - .dontShowAgainId(key) - .show(); - } + observableList.setAll(items); } private void layout() { @@ -327,7 +311,7 @@ public class BsqTxView extends ActivatableView { private void addAmountColumn() { TableColumn column = new TableColumn<>(Res.get("shared.amountWithCur", "BSQ")); - column.setMinWidth(130); + column.setMinWidth(100); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory(new Callback, TableCell>() { @@ -383,6 +367,80 @@ public class BsqTxView extends ActivatableView { tableView.getColumns().add(column); } + private void addTxTypeColumn() { + TableColumn column = new TableColumn<>(Res.get("dao.wallet.tx.type")); + column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); + column.setMinWidth(70); + column.setMaxWidth(column.getMinWidth()); + column.setCellFactory( + new Callback, TableCell>() { + + @Override + public TableCell call(TableColumn column) { + return new TableCell() { + + @Override + public void updateItem(final BsqTxListItem item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty) { + String style = "dao-tx-type-invalid-icon"; + TxType txType = TxType.UNDEFINED; + AwesomeIcon awesomeIcon = AwesomeIcon.QUESTION; + if (item.getTxType().isPresent()) { + txType = item.getTxType().get(); + switch (txType) { + case UNDEFINED: + awesomeIcon = AwesomeIcon.QUESTION; + style = "dao-tx-type-invalid-icon"; + break; + case GENESIS: + awesomeIcon = AwesomeIcon.ROCKET; + style = "dao-tx-type-default-icon"; + break; + case SEND_BSQ: + awesomeIcon = AwesomeIcon.RANDOM;//EXCHANGE + style = "dao-tx-type-default-icon"; + break; + case PAY_TRADE_FEE: + awesomeIcon = AwesomeIcon.TICKET; + style = "dao-tx-type-default-icon"; + break; + case COMPENSATION_REQUEST: + awesomeIcon = AwesomeIcon.UMBRELLA; + style = "dao-tx-type-default-icon"; + break; + case VOTE: + awesomeIcon = AwesomeIcon.THUMBS_UP; + style = "dao-tx-type-default-icon"; + break; + case ISSUANCE: + awesomeIcon = AwesomeIcon.TINT; + style = "dao-tx-type-default-icon"; + break; + default: + awesomeIcon = AwesomeIcon.SIGNIN; + style = "dao-tx-type-default-icon"; + break; + } + } + Label label = AwesomeDude.createIconLabel(awesomeIcon); + label.getStyleClass().add(style); + label.setTooltip(new Tooltip(Res.get("dao.tx.type.enum." + txType.name()))); + setGraphic(label); + } else { + setGraphic(null); + } + } + }; + } + }); + + tableView.getColumns().add(column); + } + private void openTxInBlockExplorer(BsqTxListItem item) { if (item.getTxId() != null) GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + item.getTxId()); diff --git a/gui/src/main/java/io/bisq/gui/util/BsqFormatter.java b/gui/src/main/java/io/bisq/gui/util/BsqFormatter.java index 7854375515..193beed747 100644 --- a/gui/src/main/java/io/bisq/gui/util/BsqFormatter.java +++ b/gui/src/main/java/io/bisq/gui/util/BsqFormatter.java @@ -52,11 +52,9 @@ public class BsqFormatter extends BSFormatter { } public Address getAddressFromBsqAddress(String encoded) { - if (useBsqAddressFormat) { - log.error("encoded " + encoded); + if (useBsqAddressFormat) encoded = encoded.substring(prefix.length(), encoded.length()); - log.error("encoded " + encoded); - } + try { return Address.fromBase58(WalletUtils.getParameters(), encoded); } catch (AddressFormatException e) { diff --git a/gui/src/main/java/io/bisq/gui/util/validation/BsqValidator.java b/gui/src/main/java/io/bisq/gui/util/validation/BsqValidator.java index e71dce5b42..5435ec793c 100644 --- a/gui/src/main/java/io/bisq/gui/util/validation/BsqValidator.java +++ b/gui/src/main/java/io/bisq/gui/util/validation/BsqValidator.java @@ -103,7 +103,6 @@ public class BsqValidator extends NumberValidator { protected ValidationResult validateIfSufficientAvailableBalance(String input) { try { final Coin coin = bsqFormatter.parseToCoin(input); - log.error("coin.compareTo(availableBalance) " + coin.compareTo(availableBalance)); if (availableBalance != null && coin.compareTo(availableBalance) > 0) return new ValidationResult(false, Res.get("validation.bsq.insufficientBalance", bsqFormatter.formatCoinWithCode(availableBalance)));