Add Tx type column. Display all wallet txs also invalid or unverified

This commit is contained in:
Manfred Karrer 2017-04-21 22:57:45 -05:00
parent 9b4e40aacc
commit 5e3b68453c
9 changed files with 130 additions and 42 deletions

View file

@ -958,6 +958,15 @@ dao.wallet.send.sendFunds.details=Sending: {0}\nTo receiving address: {1}.\nRequ
dao.wallet.bsqFee=BSQ fee payment dao.wallet.bsqFee=BSQ fee payment
dao.wallet.chainHeight=Synced with blockchain height: {0} (chain tip height: {1}) 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? 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?

View file

@ -28,7 +28,6 @@ import io.bisq.core.provider.fee.FeeService;
import io.bisq.core.user.Preferences; import io.bisq.core.user.Preferences;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.*; import org.bitcoinj.core.*;
import org.bitcoinj.script.Script; import org.bitcoinj.script.Script;
@ -54,7 +53,6 @@ public class BsqWalletService extends WalletService {
private final BsqCoinSelector bsqCoinSelector; private final BsqCoinSelector bsqCoinSelector;
private BsqChainState bsqChainState; private BsqChainState bsqChainState;
@Getter
private final ObservableList<Transaction> walletTransactions = FXCollections.observableArrayList(); private final ObservableList<Transaction> walletTransactions = FXCollections.observableArrayList();
private final CopyOnWriteArraySet<BsqBalanceListener> bsqBalanceListeners = new CopyOnWriteArraySet<>(); private final CopyOnWriteArraySet<BsqBalanceListener> bsqBalanceListeners = new CopyOnWriteArraySet<>();
private Coin availableBsqBalance = Coin.ZERO; private Coin availableBsqBalance = Coin.ZERO;
@ -187,8 +185,13 @@ public class BsqWalletService extends WalletService {
// BSQ TransactionOutputs and Transactions // BSQ TransactionOutputs and Transactions
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public ObservableList<Transaction> getWalletTransactions() {
return walletTransactions;
}
private void updateBsqWalletTransactions() { private void updateBsqWalletTransactions() {
walletTransactions.setAll(getBsqWalletTransactions()); walletTransactions.setAll(getTransactions(false));
// walletTransactions.setAll(getBsqWalletTransactions());
updateBsqBalance(); updateBsqBalance();
} }
@ -199,7 +202,7 @@ public class BsqWalletService extends WalletService {
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
public Set<Transaction> getInvalidBsqTransactions() { public Set<Transaction> getUnverifiedBsqTransactions() {
Set<Transaction> bsqWalletTransactions = getBsqWalletTransactions(); Set<Transaction> bsqWalletTransactions = getBsqWalletTransactions();
Set<Transaction> walletTxs = getTransactions(false).stream().collect(Collectors.toSet()); Set<Transaction> walletTxs = getTransactions(false).stream().collect(Collectors.toSet());
checkArgument(walletTxs.size() >= bsqWalletTransactions.size(), checkArgument(walletTxs.size() >= bsqWalletTransactions.size(),
@ -330,7 +333,6 @@ public class BsqWalletService extends WalletService {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public Address getUnusedAddress() { public Address getUnusedAddress() {
//TODO check if current address was used, otherwise get fresh return wallet.currentReceiveAddress();
return wallet.freshReceiveAddress();
} }
} }

View file

@ -29,10 +29,7 @@ import io.bisq.core.app.BisqEnvironment;
import io.bisq.core.btc.BitcoinNetwork; import io.bisq.core.btc.BitcoinNetwork;
import io.bisq.core.dao.DaoOptionKeys; import io.bisq.core.dao.DaoOptionKeys;
import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException; import io.bisq.core.dao.blockchain.exceptions.BlockNotConnectingException;
import io.bisq.core.dao.blockchain.vo.BsqBlock; import io.bisq.core.dao.blockchain.vo.*;
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 lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import javax.inject.Inject; import javax.inject.Inject;
@ -83,8 +80,8 @@ public class BsqChainState implements Persistable {
// block 450000 2017-01-25 // block 450000 2017-01-25
// REG TEST // REG TEST
private static final String REG_TEST_GENESIS_TX_ID = "3bc7bc9484e112ec8ddd1a1c984379819245ac463b9ce40fa8b5bf771c0f9236"; private static final String REG_TEST_GENESIS_TX_ID = "389d631bb48bd2f74fcc88c3506e2b03114b18b4e396c3bd2b8bb7d7ff9ee0d6";
private static final int REG_TEST_GENESIS_BLOCK_HEIGHT = 102; private static final int REG_TEST_GENESIS_BLOCK_HEIGHT = 1441;
// TEST NET // TEST NET
// https://testnet.blockexplorer.com/block/00000000f1cd94c6ccc458a922f2a42c975c3447180f0db1e56322a26ab3f0ec // https://testnet.blockexplorer.com/block/00000000f1cd94c6ccc458a922f2a42c975c3447180f0db1e56322a26ab3f0ec
private static final String TEST_NET_GENESIS_TX_ID = "8853756990acfc1784aac1ee1a50d331c915a46876bb4ad98f260ef2d35da845"; private static final String TEST_NET_GENESIS_TX_ID = "8853756990acfc1784aac1ee1a50d331c915a46876bb4ad98f260ef2d35da845";
@ -305,6 +302,12 @@ public class BsqChainState implements Persistable {
}); });
} }
public Optional<TxType> getTxType(String txId) {
return lock.read(() -> {
return getTx(txId).map(Tx::getTxType);
});
}
public boolean containsTx(String txId) { public boolean containsTx(String txId) {
return lock.read(() -> { return lock.read(() -> {
return getTx(txId).isPresent(); return getTx(txId).isPresent();

View file

@ -218,6 +218,17 @@ bg color of non edit textFields: fafafa
-fx-cursor: hand; -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 * * Tooltip *

View file

@ -83,7 +83,7 @@ public class BsqReceiveView extends ActivatableView<GridPane, Void> {
amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> { amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> {
addressTextField.setAmountAsCoin(bsqFormatter.parseToCoin(t)); addressTextField.setAmountAsCoin(bsqFormatter.parseToCoin(t));
}); });
addressTextField.setAddress(bsqFormatter.getBsqAddressStringFromAddress(bsqWalletService.freshReceiveAddress())); addressTextField.setAddress(bsqFormatter.getBsqAddressStringFromAddress(bsqWalletService.getUnusedAddress()));
} }
@Override @Override

View file

@ -22,10 +22,12 @@ import io.bisq.core.btc.listeners.TxConfidenceListener;
import io.bisq.core.btc.wallet.BsqWalletService; import io.bisq.core.btc.wallet.BsqWalletService;
import io.bisq.core.btc.wallet.BtcWalletService; import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.btc.wallet.WalletUtils; 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.components.indicator.TxConfidenceIndicator;
import io.bisq.gui.util.BsqFormatter; import io.bisq.gui.util.BsqFormatter;
import io.bisq.gui.util.GUIUtil; import io.bisq.gui.util.GUIUtil;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -35,12 +37,14 @@ import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.TransactionOutput; import org.bitcoinj.core.TransactionOutput;
import java.util.Date; import java.util.Date;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ToString @ToString
@Slf4j @Slf4j
@EqualsAndHashCode
class BsqTxListItem { class BsqTxListItem {
@Getter @Getter
private final Transaction transaction; private final Transaction transaction;
@ -61,6 +65,8 @@ class BsqTxListItem {
@Getter @Getter
private boolean received; private boolean received;
@Getter @Getter
private Optional<TxType> txType;
@Getter
private boolean isBurnedBsqTx; private boolean isBurnedBsqTx;
private BsqFormatter bsqFormatter; private BsqFormatter bsqFormatter;
@Getter @Getter
@ -71,11 +77,13 @@ class BsqTxListItem {
public BsqTxListItem(Transaction transaction, public BsqTxListItem(Transaction transaction,
BsqWalletService bsqWalletService, BsqWalletService bsqWalletService,
BtcWalletService btcWalletService, BtcWalletService btcWalletService,
Optional<TxType> txType,
boolean isBurnedBsqTx, boolean isBurnedBsqTx,
BsqFormatter bsqFormatter) { BsqFormatter bsqFormatter) {
this.transaction = transaction; this.transaction = transaction;
this.bsqWalletService = bsqWalletService; this.bsqWalletService = bsqWalletService;
this.btcWalletService = btcWalletService; this.btcWalletService = btcWalletService;
this.txType = txType;
this.isBurnedBsqTx = isBurnedBsqTx; this.isBurnedBsqTx = isBurnedBsqTx;
this.bsqFormatter = bsqFormatter; this.bsqFormatter = bsqFormatter;

View file

@ -17,6 +17,7 @@
package io.bisq.gui.main.dao.wallet.tx; package io.bisq.gui.main.dao.wallet.tx;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bisq.common.locale.Res; import io.bisq.common.locale.Res;
import io.bisq.core.btc.wallet.BsqWalletService; 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.BsqBlockchainManager;
import io.bisq.core.dao.blockchain.BsqChainStateListener; import io.bisq.core.dao.blockchain.BsqChainStateListener;
import io.bisq.core.dao.blockchain.parse.BsqChainState; 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.core.user.Preferences;
import io.bisq.gui.common.view.ActivatableView; import io.bisq.gui.common.view.ActivatableView;
import io.bisq.gui.common.view.FxmlView; import io.bisq.gui.common.view.FxmlView;
import io.bisq.gui.components.AddressWithIconAndDirection; import io.bisq.gui.components.AddressWithIconAndDirection;
import io.bisq.gui.components.HyperlinkWithIcon; import io.bisq.gui.components.HyperlinkWithIcon;
import io.bisq.gui.main.dao.wallet.BsqBalanceUtil; 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.BsqFormatter;
import io.bisq.gui.util.FormBuilder; import io.bisq.gui.util.FormBuilder;
import io.bisq.gui.util.GUIUtil; import io.bisq.gui.util.GUIUtil;
@ -112,6 +112,7 @@ public class BsqTxView extends ActivatableView<GridPane, Void> {
addAddressColumn(); addAddressColumn();
addAmountColumn(); addAmountColumn();
addConfidenceColumn(); addConfidenceColumn();
addTxTypeColumn();
chainHeightLabel = FormBuilder.addLabel(root, ++gridRow, ""); chainHeightLabel = FormBuilder.addLabel(root, ++gridRow, "");
chainHeightLabel.setId("num-offers"); chainHeightLabel.setId("num-offers");
@ -170,37 +171,20 @@ public class BsqTxView extends ActivatableView<GridPane, Void> {
private void updateList() { private void updateList() {
observableList.forEach(BsqTxListItem::cleanup); observableList.forEach(BsqTxListItem::cleanup);
// clone to avoid ConcurrentModificationException // copy list to avoid ConcurrentModificationException
final List<Transaction> walletTransactions = new ArrayList<>(bsqWalletService.getWalletTransactions()); final List<Transaction> walletTransactions = new ArrayList<>(bsqWalletService.getWalletTransactions());
Set<BsqTxListItem> list = walletTransactions.stream() Set<BsqTxListItem> items = walletTransactions.stream()
.map(transaction -> { .map(transaction -> {
// The burned fee is added to all outputs of a tx, so we just ask at index 0
return new BsqTxListItem(transaction, return new BsqTxListItem(transaction,
bsqWalletService, bsqWalletService,
btcWalletService, btcWalletService,
bsqChainState.getTxType(transaction.getHashAsString()),
bsqChainState.hasTxBurntFee(transaction.getHashAsString()), bsqChainState.hasTxBurntFee(transaction.getHashAsString()),
bsqFormatter); bsqFormatter);
} }
) )
.collect(Collectors.toSet()); .collect(Collectors.toSet());
observableList.setAll(list); observableList.setAll(items);
final Set<Transaction> invalidBsqTransactions = bsqWalletService.getInvalidBsqTransactions();
if (!invalidBsqTransactions.isEmpty() && bsqBlockchainManager.isParseBlockchainComplete()) {
Set<String> 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();
}
} }
private void layout() { private void layout() {
@ -327,7 +311,7 @@ public class BsqTxView extends ActivatableView<GridPane, Void> {
private void addAmountColumn() { private void addAmountColumn() {
TableColumn<BsqTxListItem, BsqTxListItem> column = new TableColumn<>(Res.get("shared.amountWithCur", "BSQ")); TableColumn<BsqTxListItem, BsqTxListItem> column = new TableColumn<>(Res.get("shared.amountWithCur", "BSQ"));
column.setMinWidth(130); column.setMinWidth(100);
column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
column.setCellFactory(new Callback<TableColumn<BsqTxListItem, BsqTxListItem>, column.setCellFactory(new Callback<TableColumn<BsqTxListItem, BsqTxListItem>,
TableCell<BsqTxListItem, BsqTxListItem>>() { TableCell<BsqTxListItem, BsqTxListItem>>() {
@ -383,6 +367,80 @@ public class BsqTxView extends ActivatableView<GridPane, Void> {
tableView.getColumns().add(column); tableView.getColumns().add(column);
} }
private void addTxTypeColumn() {
TableColumn<BsqTxListItem, BsqTxListItem> 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<TableColumn<BsqTxListItem, BsqTxListItem>, TableCell<BsqTxListItem,
BsqTxListItem>>() {
@Override
public TableCell<BsqTxListItem, BsqTxListItem> call(TableColumn<BsqTxListItem,
BsqTxListItem> column) {
return new TableCell<BsqTxListItem, BsqTxListItem>() {
@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) { private void openTxInBlockExplorer(BsqTxListItem item) {
if (item.getTxId() != null) if (item.getTxId() != null)
GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + item.getTxId()); GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + item.getTxId());

View file

@ -52,11 +52,9 @@ public class BsqFormatter extends BSFormatter {
} }
public Address getAddressFromBsqAddress(String encoded) { public Address getAddressFromBsqAddress(String encoded) {
if (useBsqAddressFormat) { if (useBsqAddressFormat)
log.error("encoded " + encoded);
encoded = encoded.substring(prefix.length(), encoded.length()); encoded = encoded.substring(prefix.length(), encoded.length());
log.error("encoded " + encoded);
}
try { try {
return Address.fromBase58(WalletUtils.getParameters(), encoded); return Address.fromBase58(WalletUtils.getParameters(), encoded);
} catch (AddressFormatException e) { } catch (AddressFormatException e) {

View file

@ -103,7 +103,6 @@ public class BsqValidator extends NumberValidator {
protected ValidationResult validateIfSufficientAvailableBalance(String input) { protected ValidationResult validateIfSufficientAvailableBalance(String input) {
try { try {
final Coin coin = bsqFormatter.parseToCoin(input); final Coin coin = bsqFormatter.parseToCoin(input);
log.error("coin.compareTo(availableBalance) " + coin.compareTo(availableBalance));
if (availableBalance != null && coin.compareTo(availableBalance) > 0) if (availableBalance != null && coin.compareTo(availableBalance) > 0)
return new ValidationResult(false, Res.get("validation.bsq.insufficientBalance", return new ValidationResult(false, Res.get("validation.bsq.insufficientBalance",
bsqFormatter.formatCoinWithCode(availableBalance))); bsqFormatter.formatCoinWithCode(availableBalance)));