mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 15:10:44 +01:00
Add Tx type column. Display all wallet txs also invalid or unverified
This commit is contained in:
parent
9b4e40aacc
commit
5e3b68453c
9 changed files with 130 additions and 42 deletions
|
@ -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?
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)));
|
||||
|
|
Loading…
Add table
Reference in a new issue