mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-22 22:45:21 +01:00
Add memo field to withdrawal transaction
- "Memo" field is modeled as property of the new object Transaction which is stored in persitant storage. - Transaction object is modeled in a way that allows extension in the furure for more persisted attributes.
This commit is contained in:
parent
5e5d7d1577
commit
8d5f42f122
10 changed files with 201 additions and 10 deletions
|
@ -39,6 +39,7 @@ import bisq.core.support.dispute.mediation.MediationDisputeList;
|
|||
import bisq.core.support.dispute.refund.RefundDisputeList;
|
||||
import bisq.core.trade.TradableList;
|
||||
import bisq.core.trade.statistics.TradeStatistics2Store;
|
||||
import bisq.core.transaction.TransactionsPayload;
|
||||
import bisq.core.user.PreferencesPayload;
|
||||
import bisq.core.user.UserPayload;
|
||||
|
||||
|
@ -148,6 +149,8 @@ public class CorePersistenceProtoResolver extends CoreProtoResolver implements P
|
|||
return UnconfirmedBsqChangeOutputList.fromProto(proto.getUnconfirmedBsqChangeOutputList());
|
||||
case SIGNED_WITNESS_STORE:
|
||||
return SignedWitnessStore.fromProto(proto.getSignedWitnessStore());
|
||||
case TRANSACTIONS_PAYLOAD:
|
||||
return TransactionsPayload.fromProto(proto.getTransactionsPayload());
|
||||
|
||||
default:
|
||||
throw new ProtobufferRuntimeException("Unknown proto message case(PB.PersistableEnvelope). " +
|
||||
|
|
29
core/src/main/java/bisq/core/transaction/Transaction.java
Normal file
29
core/src/main/java/bisq/core/transaction/Transaction.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
package bisq.core.transaction;
|
||||
|
||||
import bisq.common.Payload;
|
||||
|
||||
public class Transaction implements Payload {
|
||||
private final String txId;
|
||||
private final String memo;
|
||||
|
||||
public Transaction(String txId, String memo) {
|
||||
this.txId = txId;
|
||||
this.memo = memo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public protobuf.Transaction toProtoMessage() {
|
||||
final protobuf.Transaction.Builder builder = protobuf.Transaction.newBuilder()
|
||||
.setMemo(memo)
|
||||
.setTxId(txId);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static Transaction fromProto(protobuf.Transaction protobufTransaction) {
|
||||
return new Transaction(protobufTransaction.getTxId(), protobufTransaction.getMemo());
|
||||
}
|
||||
|
||||
public String getTxId() { return txId; }
|
||||
|
||||
public String getMemo() { return memo; }
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package bisq.core.transaction;
|
||||
|
||||
import bisq.common.proto.persistable.PersistableEnvelope;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Slf4j
|
||||
@Singleton
|
||||
public class TransactionsPayload implements PersistableEnvelope {
|
||||
private final Map<String, Transaction> transactions;
|
||||
|
||||
public TransactionsPayload(Map<String, Transaction> transactions) {
|
||||
this.transactions = transactions;
|
||||
}
|
||||
|
||||
public Transaction findTransactionById(String txId) {
|
||||
@Nullable
|
||||
Transaction result = this.transactions.get(txId);
|
||||
|
||||
return result != null ? result : new Transaction(txId, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
protobuf.TransactionsPayload.Builder builder = protobuf.TransactionsPayload.newBuilder();
|
||||
|
||||
Optional.ofNullable(transactions)
|
||||
.ifPresent(transactions -> builder.addAllTransactions(
|
||||
transactions
|
||||
.values()
|
||||
.stream()
|
||||
.map(Transaction::toProtoMessage)
|
||||
.collect(Collectors.toList())
|
||||
)
|
||||
);
|
||||
|
||||
return protobuf.PersistableEnvelope.newBuilder().setTransactionsPayload(builder).build();
|
||||
}
|
||||
|
||||
public void addTransaction(Transaction transaction) {
|
||||
this.transactions.put(transaction.getTxId(), transaction);
|
||||
}
|
||||
|
||||
public static TransactionsPayload fromProto(protobuf.TransactionsPayload proto) {
|
||||
Map<String, Transaction> map = new HashMap<>();
|
||||
proto.getTransactionsList().forEach(
|
||||
protoTransaction -> map.put(protoTransaction.getTxId(), Transaction.fromProto(protoTransaction))
|
||||
);
|
||||
|
||||
return new TransactionsPayload(map);
|
||||
}
|
||||
}
|
|
@ -899,6 +899,8 @@ funds.withdrawal.feeExcluded=Amount excludes mining fee
|
|||
funds.withdrawal.feeIncluded=Amount includes mining fee
|
||||
funds.withdrawal.fromLabel=Withdraw from address
|
||||
funds.withdrawal.toLabel=Withdraw to address
|
||||
funds.withdrawal.memoLabel=Withdrawal memo
|
||||
funds.withdrawal.memo=Optional text as memo for the withdrawal
|
||||
funds.withdrawal.withdrawButton=Withdraw selected
|
||||
funds.withdrawal.noFundsAvailable=No funds are available for withdrawal
|
||||
funds.withdrawal.confirmWithdrawalRequest=Confirm withdrawal request
|
||||
|
@ -936,6 +938,7 @@ funds.tx.noFundsFromDispute=No refund from dispute
|
|||
funds.tx.receivedFunds=Received funds
|
||||
funds.tx.withdrawnFromWallet=Withdrawn from wallet
|
||||
funds.tx.withdrawnFromBSQWallet=BTC withdrawn from BSQ wallet
|
||||
funds.tx.memo=Memo
|
||||
funds.tx.noTxAvailable=No transactions available
|
||||
funds.tx.revert=Revert
|
||||
funds.tx.txSent=Transaction successfully sent to a new address in the local Bisq wallet.
|
||||
|
|
|
@ -20,16 +20,21 @@ package bisq.desktop.main.funds.transactions;
|
|||
import bisq.core.btc.wallet.BsqWalletService;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.dao.DaoFacade;
|
||||
import bisq.core.transaction.TransactionsPayload;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
import bisq.common.storage.Storage;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
||||
|
@ -40,27 +45,38 @@ public class TransactionListItemFactory {
|
|||
private final DaoFacade daoFacade;
|
||||
private final CoinFormatter formatter;
|
||||
private final Preferences preferences;
|
||||
private final Storage<TransactionsPayload> storage;
|
||||
|
||||
@Inject
|
||||
TransactionListItemFactory(BtcWalletService btcWalletService,
|
||||
BsqWalletService bsqWalletService,
|
||||
DaoFacade daoFacade,
|
||||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
|
||||
Preferences preferences) {
|
||||
Preferences preferences,
|
||||
Storage<TransactionsPayload> storage
|
||||
) {
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.bsqWalletService = bsqWalletService;
|
||||
this.daoFacade = daoFacade;
|
||||
this.formatter = formatter;
|
||||
this.preferences = preferences;
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
TransactionsListItem create(Transaction transaction, @Nullable TransactionAwareTradable tradable) {
|
||||
TransactionsPayload persisted = storage.initAndGetPersistedWithFileName("TransactionPayload", 100);
|
||||
if (persisted == null) {
|
||||
persisted = new TransactionsPayload(new HashMap<>());
|
||||
}
|
||||
|
||||
return new TransactionsListItem(transaction,
|
||||
btcWalletService,
|
||||
bsqWalletService,
|
||||
tradable,
|
||||
daoFacade,
|
||||
formatter,
|
||||
preferences.getIgnoreDustThreshold());
|
||||
preferences.getIgnoreDustThreshold(),
|
||||
persisted.findTransactionById(transaction.getHashAsString())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ class TransactionsListItem {
|
|||
private boolean received;
|
||||
private boolean detailsAvailable;
|
||||
private Coin amountAsCoin = Coin.ZERO;
|
||||
private String memo = "";
|
||||
private int confirmations = 0;
|
||||
@Getter
|
||||
private final boolean isDustAttackTx;
|
||||
|
@ -88,9 +89,12 @@ class TransactionsListItem {
|
|||
TransactionAwareTradable transactionAwareTradable,
|
||||
DaoFacade daoFacade,
|
||||
CoinFormatter formatter,
|
||||
long ignoreDustThreshold) {
|
||||
long ignoreDustThreshold,
|
||||
bisq.core.transaction.Transaction storedTransaction
|
||||
) {
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.formatter = formatter;
|
||||
this.memo = storedTransaction.getMemo();
|
||||
|
||||
txId = transaction.getHashAsString();
|
||||
|
||||
|
@ -340,5 +344,7 @@ class TransactionsListItem {
|
|||
public String getNumConfirmations() {
|
||||
return String.valueOf(confirmations);
|
||||
}
|
||||
|
||||
public String getMemo() { return memo; }
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
<TableColumn fx:id="addressColumn" minWidth="260"/>
|
||||
<TableColumn fx:id="transactionColumn" minWidth="180"/>
|
||||
<TableColumn fx:id="amountColumn" minWidth="130" maxWidth="130"/>
|
||||
<TableColumn fx:id="memoColumn" minWidth="50" maxWidth="50"/>
|
||||
<TableColumn fx:id="confidenceColumn" minWidth="130" maxWidth="130"/>
|
||||
<TableColumn fx:id="revertTxColumn" sortable="false" minWidth="110" maxWidth="110" visible="false"/>
|
||||
</columns>
|
||||
|
|
|
@ -89,7 +89,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
@FXML
|
||||
TableView<TransactionsListItem> tableView;
|
||||
@FXML
|
||||
TableColumn<TransactionsListItem, TransactionsListItem> dateColumn, detailsColumn, addressColumn, transactionColumn, amountColumn, confidenceColumn, revertTxColumn;
|
||||
TableColumn<TransactionsListItem, TransactionsListItem> dateColumn, detailsColumn, addressColumn, transactionColumn, amountColumn, memoColumn, confidenceColumn, revertTxColumn;
|
||||
@FXML
|
||||
AutoTooltipButton exportButton;
|
||||
|
||||
|
@ -136,6 +136,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address")));
|
||||
transactionColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.txId", Res.getBaseCurrencyCode())));
|
||||
amountColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode())));
|
||||
memoColumn.setGraphic(new AutoTooltipLabel(Res.get("funds.tx.memo")));
|
||||
confidenceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.confirmations", Res.getBaseCurrencyCode())));
|
||||
revertTxColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.revert", Res.getBaseCurrencyCode())));
|
||||
|
||||
|
@ -147,6 +148,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
setAddressColumnCellFactory();
|
||||
setTransactionColumnCellFactory();
|
||||
setAmountColumnCellFactory();
|
||||
setMemoColumnCellFactory();
|
||||
setConfidenceColumnCellFactory();
|
||||
setRevertTxColumnCellFactory();
|
||||
|
||||
|
@ -237,13 +239,14 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
return columns;
|
||||
};
|
||||
CSVEntryConverter<TransactionsListItem> contentConverter = item -> {
|
||||
String[] columns = new String[6];
|
||||
String[] columns = new String[7];
|
||||
columns[0] = item.getDateString();
|
||||
columns[1] = item.getDetails();
|
||||
columns[2] = item.getDirection() + " " + item.getAddressString();
|
||||
columns[3] = item.getTxId();
|
||||
columns[4] = item.getAmount();
|
||||
columns[5] = item.getNumConfirmations();
|
||||
columns[5] = item.getMemo();
|
||||
columns[6] = item.getNumConfirmations();
|
||||
return columns;
|
||||
};
|
||||
|
||||
|
@ -453,6 +456,33 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
});
|
||||
}
|
||||
|
||||
private void setMemoColumnCellFactory() {
|
||||
memoColumn.setCellValueFactory((addressListItem) ->
|
||||
new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
|
||||
memoColumn.setCellFactory(
|
||||
new Callback<>() {
|
||||
|
||||
@Override
|
||||
public TableCell<TransactionsListItem, TransactionsListItem> call(TableColumn<TransactionsListItem,
|
||||
TransactionsListItem> column) {
|
||||
return new TableCell<>() {
|
||||
|
||||
@Override
|
||||
public void updateItem(final TransactionsListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
setGraphic(new AutoTooltipLabel(item.getMemo()));
|
||||
} else {
|
||||
setGraphic(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setConfidenceColumnCellFactory() {
|
||||
confidenceColumn.getStyleClass().add("last-column");
|
||||
confidenceColumn.setCellValueFactory((addressListItem) ->
|
||||
|
|
|
@ -39,6 +39,7 @@ import bisq.core.btc.wallet.Restrictions;
|
|||
import bisq.core.locale.Res;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.TradeManager;
|
||||
import bisq.core.transaction.TransactionsPayload;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
@ -49,6 +50,7 @@ import bisq.core.util.validation.BtcAddressValidator;
|
|||
import bisq.network.p2p.P2PService;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.storage.Storage;
|
||||
import bisq.common.util.Tuple3;
|
||||
import bisq.common.util.Tuple4;
|
||||
|
||||
|
@ -98,10 +100,9 @@ import javafx.util.Callback;
|
|||
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -125,7 +126,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
|
||||
private RadioButton useAllInputsRadioButton, useCustomInputsRadioButton, feeExcludedRadioButton;
|
||||
private Label amountLabel;
|
||||
private TextField amountTextField, withdrawFromTextField, withdrawToTextField;
|
||||
private TextField amountTextField, withdrawFromTextField, withdrawToTextField, withdrawMemoTextField;
|
||||
|
||||
private final BtcWalletService walletService;
|
||||
private final TradeManager tradeManager;
|
||||
|
@ -135,6 +136,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
private final Preferences preferences;
|
||||
private final BtcAddressValidator btcAddressValidator;
|
||||
private final WalletPasswordWindow walletPasswordWindow;
|
||||
private final Storage<TransactionsPayload> transactionPayloadStorage;
|
||||
private final ObservableList<WithdrawalListItem> observableList = FXCollections.observableArrayList();
|
||||
private final SortedList<WithdrawalListItem> sortedList = new SortedList<>(observableList);
|
||||
private Set<WithdrawalListItem> selectedItems = new HashSet<>();
|
||||
|
@ -164,7 +166,9 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
|
||||
Preferences preferences,
|
||||
BtcAddressValidator btcAddressValidator,
|
||||
WalletPasswordWindow walletPasswordWindow) {
|
||||
WalletPasswordWindow walletPasswordWindow,
|
||||
Storage<TransactionsPayload> transactionPayloadStorage
|
||||
) {
|
||||
this.walletService = walletService;
|
||||
this.tradeManager = tradeManager;
|
||||
this.p2PService = p2PService;
|
||||
|
@ -173,6 +177,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
this.preferences = preferences;
|
||||
this.btcAddressValidator = btcAddressValidator;
|
||||
this.walletPasswordWindow = walletPasswordWindow;
|
||||
this.transactionPayloadStorage = transactionPayloadStorage;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -220,6 +225,10 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
Res.get("funds.withdrawal.toLabel", Res.getBaseCurrencyCode())).second;
|
||||
withdrawToTextField.setMaxWidth(380);
|
||||
|
||||
withdrawMemoTextField = addTopLabelInputTextField(gridPane, ++rowIndex,
|
||||
Res.get("funds.withdrawal.memoLabel", Res.getBaseCurrencyCode())).second;
|
||||
withdrawMemoTextField.setMaxWidth(380);
|
||||
|
||||
final Button withdrawButton = addButton(gridPane, ++rowIndex, Res.get("funds.withdrawal.withdrawButton"), 15);
|
||||
|
||||
withdrawButton.setOnAction(event -> onWithdraw());
|
||||
|
@ -385,6 +394,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
|
||||
if (transaction != null) {
|
||||
log.debug("onWithdraw onSuccess tx ID:{}", transaction.getHashAsString());
|
||||
storeTransaction(transaction);
|
||||
} else {
|
||||
log.error("onWithdraw transaction is null");
|
||||
}
|
||||
|
@ -420,6 +430,19 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
}
|
||||
}
|
||||
|
||||
private void storeTransaction(@NotNull Transaction transaction) {
|
||||
bisq.core.transaction.Transaction transactionToBeStored = new bisq.core.transaction.Transaction(
|
||||
transaction.getHashAsString(),
|
||||
withdrawMemoTextField.getText()
|
||||
);
|
||||
TransactionsPayload transactionsPayload = transactionPayloadStorage.initAndGetPersistedWithFileName("TransactionPayload", 100);
|
||||
if (transactionsPayload == null) {
|
||||
transactionsPayload = new TransactionsPayload(new HashMap<>());
|
||||
}
|
||||
transactionsPayload.addTransaction(transactionToBeStored);
|
||||
transactionPayloadStorage.queueUpForSave(transactionsPayload);
|
||||
}
|
||||
|
||||
private void selectForWithdrawal(WithdrawalListItem item) {
|
||||
if (item.isSelected())
|
||||
selectedItems.add(item);
|
||||
|
@ -528,6 +551,9 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
withdrawToTextField.setText("");
|
||||
withdrawToTextField.setPromptText(Res.get("funds.withdrawal.fillDestAddress"));
|
||||
|
||||
withdrawMemoTextField.setText("");
|
||||
withdrawMemoTextField.setPromptText(Res.get("funds.withdrawal.memo"));
|
||||
|
||||
selectedItems.clear();
|
||||
tableView.getSelectionModel().clearSelection();
|
||||
}
|
||||
|
|
|
@ -1145,6 +1145,7 @@ message PersistableEnvelope {
|
|||
SignedWitnessStore signed_witness_store = 28;
|
||||
MediationDisputeList mediation_dispute_list = 29;
|
||||
RefundDisputeList refund_dispute_list = 30;
|
||||
TransactionsPayload transactions_payload = 31;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2069,3 +2070,16 @@ message MockPayload {
|
|||
string message_version = 1;
|
||||
string message = 2;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Transaction
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
message Transaction {
|
||||
string txId = 1;
|
||||
string memo = 2;
|
||||
}
|
||||
|
||||
message TransactionsPayload {
|
||||
repeated Transaction transactions = 1;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue