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.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?

View file

@ -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<Transaction> walletTransactions = FXCollections.observableArrayList();
private final CopyOnWriteArraySet<BsqBalanceListener> bsqBalanceListeners = new CopyOnWriteArraySet<>();
private Coin availableBsqBalance = Coin.ZERO;
@ -187,8 +185,13 @@ public class BsqWalletService extends WalletService {
// BSQ TransactionOutputs and Transactions
///////////////////////////////////////////////////////////////////////////////////////////
public ObservableList<Transaction> 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<Transaction> getInvalidBsqTransactions() {
public Set<Transaction> getUnverifiedBsqTransactions() {
Set<Transaction> bsqWalletTransactions = getBsqWalletTransactions();
Set<Transaction> 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();
}
}

View file

@ -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<TxType> getTxType(String txId) {
return lock.read(() -> {
return getTx(txId).map(Tx::getTxType);
});
}
public boolean containsTx(String txId) {
return lock.read(() -> {
return getTx(txId).isPresent();

View file

@ -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 *

View file

@ -83,7 +83,7 @@ public class BsqReceiveView extends ActivatableView<GridPane, Void> {
amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> {
addressTextField.setAmountAsCoin(bsqFormatter.parseToCoin(t));
});
addressTextField.setAddress(bsqFormatter.getBsqAddressStringFromAddress(bsqWalletService.freshReceiveAddress()));
addressTextField.setAddress(bsqFormatter.getBsqAddressStringFromAddress(bsqWalletService.getUnusedAddress()));
}
@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.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> 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> txType,
boolean isBurnedBsqTx,
BsqFormatter bsqFormatter) {
this.transaction = transaction;
this.bsqWalletService = bsqWalletService;
this.btcWalletService = btcWalletService;
this.txType = txType;
this.isBurnedBsqTx = isBurnedBsqTx;
this.bsqFormatter = bsqFormatter;

View file

@ -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<GridPane, Void> {
addAddressColumn();
addAmountColumn();
addConfidenceColumn();
addTxTypeColumn();
chainHeightLabel = FormBuilder.addLabel(root, ++gridRow, "");
chainHeightLabel.setId("num-offers");
@ -170,37 +171,20 @@ public class BsqTxView extends ActivatableView<GridPane, Void> {
private void updateList() {
observableList.forEach(BsqTxListItem::cleanup);
// clone to avoid ConcurrentModificationException
// copy list to avoid ConcurrentModificationException
final List<Transaction> walletTransactions = new ArrayList<>(bsqWalletService.getWalletTransactions());
Set<BsqTxListItem> list = walletTransactions.stream()
Set<BsqTxListItem> 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<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();
}
observableList.setAll(items);
}
private void layout() {
@ -327,7 +311,7 @@ public class BsqTxView extends ActivatableView<GridPane, Void> {
private void addAmountColumn() {
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.setCellFactory(new Callback<TableColumn<BsqTxListItem, BsqTxListItem>,
TableCell<BsqTxListItem, BsqTxListItem>>() {
@ -383,6 +367,80 @@ public class BsqTxView extends ActivatableView<GridPane, Void> {
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) {
if (item.getTxId() != null)
GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + item.getTxId());

View file

@ -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) {

View file

@ -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)));