Skip to content

Commit

Permalink
Add new api method gettransaction
Browse files Browse the repository at this point in the history
This change was prompted by the recent changes in the main branch to
allow a tx memo field to be set from the UI and API.

This and the prior PR address the API's need to be able to fetch a
tx (with a memo).  The API can now get a completed trade's withdrawal
txid and pass it as a gettransaction parameter.

See previous PR "Append nullable withdrawalTxId field to Trade".

	bisq-network#4937

A summary of changes by file:

grpc.proto

- Added withdrawalTxId field to existing TradeInfo proto & wrapper.
- Reordered fields in TradeInfo proto.
- Added new fields to be displayed by TxInfo proto in CLI.
- Fixed typo: unsetTxFeeRatePreference -> UnsetTxFeeRatePreference.
- Added new GetTransaction rpc.

GrpcWalletsService - Added new getTransaction gRPC boilerplate.

CoreWalletsService - Added new getTransaction implementation.

TxInfo - Added the new fields for displaying a tx summary from CLI.
This is not intended to be more than a brief summary;  a block explorer
or bitcoin-core client should be used to see the complete definition.

TradeInfo - Added the new withdrawalTxId field defined in grpc.proto.

CliMain - Added new 'case gettransaction'.

TransactionFormat - Formats a TxInfo sent from the server to CLI.

ColumnHeaderConstants - Added console headers used by TransactionFormat.

TradeFormat - Displays a completed trade's WithdrawalTxId if present.

Apitest - Adjusted affected tests: assert tx memo is persisted and
test gettransaction.
  • Loading branch information
ghubstan committed Dec 14, 2020
1 parent 6aa385e commit 5522d0c
Show file tree
Hide file tree
Showing 15 changed files with 341 additions and 61 deletions.
6 changes: 6 additions & 0 deletions apitest/src/test/java/bisq/apitest/method/MethodTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import bisq.proto.grpc.GetPaymentAccountsRequest;
import bisq.proto.grpc.GetPaymentMethodsRequest;
import bisq.proto.grpc.GetTradeRequest;
import bisq.proto.grpc.GetTransactionRequest;
import bisq.proto.grpc.GetTxFeeRateRequest;
import bisq.proto.grpc.GetUnusedBsqAddressRequest;
import bisq.proto.grpc.KeepFundsRequest;
Expand Down Expand Up @@ -432,6 +433,11 @@ protected final TxFeeRateInfo unsetTxFeeRate(BisqAppConfig bisqAppConfig) {
grpcStubs(bisqAppConfig).walletsService.unsetTxFeeRatePreference(req).getTxFeeRateInfo());
}

protected final TxInfo getTransaction(BisqAppConfig bisqAppConfig, String txId) {
var req = GetTransactionRequest.newBuilder().setTxId(txId).build();
return grpcStubs(bisqAppConfig).walletsService.getTransaction(req).getTxInfo();
}

// Static conveniences for test methods and test case fixture setups.

protected static RegisterDisputeAgentRequest createRegisterDisputeAgentRequest(String disputeAgentType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,12 @@ public void testCreateTransferwiseAccount(TestInfo testInfo) {
String jsonString = getCompletedFormAsJsonString();
TransferwiseAccount paymentAccount = (TransferwiseAccount) createPaymentAccount(alicedaemon, jsonString);
verifyUserPayloadHasPaymentAccountWithId(paymentAccount.getId());
verifyAccountTradeCurrencies(getAllTransferwiseCurrencies(), paymentAccount);
verifyUserPayloadHasPaymentAccountWithId(paymentAccount.getId());
// As per commit 88f26f93241af698ae689bf081205d0f9dc929fa
// Do not autofill all currencies by default but keep all unselected.
// verifyAccountTradeCurrencies(getAllTransferwiseCurrencies(), paymentAccount);
// TODO uncomment after master/merge
// assertEquals(0, paymentAccount.getTradeCurrencies().size());
verifyCommonFormEntries(paymentAccount);
assertEquals(COMPLETED_FORM_MAP.get(PROPERTY_NAME_EMAIL), paymentAccount.getEmail());
log.debug("Deserialized {}: {}", paymentAccount.getClass().getSimpleName(), paymentAccount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

import lombok.extern.slf4j.Slf4j;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
Expand All @@ -33,6 +32,7 @@
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.apitest.config.BisqAppConfig.bobdaemon;
import static bisq.cli.CurrencyFormat.formatSatoshis;
import static bisq.cli.TransactionFormat.format;
import static bisq.core.trade.Trade.Phase.*;
import static bisq.core.trade.Trade.State.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
Expand All @@ -42,7 +42,7 @@
import static protobuf.Offer.State.OFFER_FEE_PAID;
import static protobuf.OpenOffer.State.AVAILABLE;

@Disabled
// @Disabled
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TakeSellBTCOfferTest extends AbstractTradeTest {
Expand All @@ -52,6 +52,8 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
// Maker and Taker fees are in BTC.
private static final String TRADE_FEE_CURRENCY_CODE = "btc";

private static final String WITHDRAWAL_TX_MEMO = "Bob's trade withdrawal";

@Test
@Order(1)
public void testTakeAlicesSellOffer(final TestInfo testInfo) {
Expand Down Expand Up @@ -147,7 +149,7 @@ public void testBobsBtcWithdrawalToExternalAddress(final TestInfo testInfo) {
logTrade(log, testInfo, "Bob's view before withdrawing funds to external wallet", trade);

String toAddress = bitcoinCli.getNewBtcAddress();
withdrawFunds(bobdaemon, tradeId, toAddress, "to whom it may concern");
withdrawFunds(bobdaemon, tradeId, toAddress, WITHDRAWAL_TX_MEMO);

genBtcBlocksThenWait(1, 2250);

Expand All @@ -162,4 +164,19 @@ public void testBobsBtcWithdrawalToExternalAddress(final TestInfo testInfo) {
testName(testInfo),
formatSatoshis(currentBalance.getAvailableBalance()));
}

@Test
@Order(5)
public void testGetTradeWithdrawalTx(final TestInfo testInfo) {
var trade = getTrade(bobdaemon, tradeId);
var withdrawalTxId = trade.getWithdrawalTxId();
assertNotNull(withdrawalTxId);

var txInfo = getTransaction(bobdaemon, withdrawalTxId);
assertEquals(WITHDRAWAL_TX_MEMO, txInfo.getMemo());

log.debug("{} Trade withdrawal Tx:\n{}",
testName(testInfo),
format(txInfo));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bisq.apitest.method.wallet;

import bisq.proto.grpc.BtcBalanceInfo;
import bisq.proto.grpc.TxInfo;

import lombok.extern.slf4j.Slf4j;

Expand All @@ -20,6 +21,7 @@
import static bisq.cli.TableFormat.formatBtcBalanceInfoTbl;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;

Expand All @@ -32,6 +34,8 @@
@TestMethodOrder(OrderAnnotation.class)
public class BtcWalletTest extends MethodTest {

private static final String TX_MEMO = "tx memo";

// All api tests depend on the DAO / regtest environment, and Bob & Alice's wallets
// are initialized with 10 BTC during the scaffolding setup.
private static final bisq.core.api.model.BtcBalanceInfo INITIAL_BTC_BALANCES =
Expand Down Expand Up @@ -99,15 +103,23 @@ public void testAliceSendBTCToBob(TestInfo testInfo) {
String bobsBtcAddress = getUnusedBtcAddress(bobdaemon);
log.debug("Sending 5.5 BTC From Alice to Bob @ {}", bobsBtcAddress);

sendBtc(alicedaemon,
TxInfo txInfo = sendBtc(alicedaemon,
bobsBtcAddress,
"5.50",
"100",
"to whom it may concern");
TX_MEMO);
assertTrue(txInfo.getIsPending());

// Note that the memo is not set on the tx yet.
assertTrue(txInfo.getMemo().isEmpty());
genBtcBlocksThenWait(1, 3000);

BtcBalanceInfo alicesBalances = getBtcBalances(alicedaemon);
// Fetch the tx and check for confirmation and memo.
txInfo = getTransaction(alicedaemon, txInfo.getTxId());
assertFalse(txInfo.getIsPending());
assertEquals(TX_MEMO, txInfo.getMemo());

BtcBalanceInfo alicesBalances = getBtcBalances(alicedaemon);
log.debug("{} Alice's BTC Balances:\n{}",
testName(testInfo),
formatBtcBalanceInfoTbl(alicesBalances));
Expand Down
1 change: 1 addition & 0 deletions apitest/src/test/java/bisq/apitest/scenario/TradeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,6 @@ public void testTakeSellBTCOffer(final TestInfo testInfo) {
test.testBobsConfirmPaymentStarted(testInfo);
test.testAlicesConfirmPaymentReceived(testInfo);
test.testBobsBtcWithdrawalToExternalAddress(testInfo);
test.testGetTradeWithdrawalTx(testInfo);
}
}
27 changes: 24 additions & 3 deletions cli/src/main/java/bisq/cli/CliMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import bisq.proto.grpc.GetPaymentAccountsRequest;
import bisq.proto.grpc.GetPaymentMethodsRequest;
import bisq.proto.grpc.GetTradeRequest;
import bisq.proto.grpc.GetTransactionRequest;
import bisq.proto.grpc.GetTxFeeRateRequest;
import bisq.proto.grpc.GetUnusedBsqAddressRequest;
import bisq.proto.grpc.GetVersionRequest;
Expand Down Expand Up @@ -116,6 +117,7 @@ private enum Method {
gettxfeerate,
settxfeerate,
unsettxfeerate,
gettransaction,
lockwallet,
unlockwallet,
removewalletpassword,
Expand Down Expand Up @@ -275,7 +277,10 @@ public static void run(String[] args) {
.build();
var reply = walletsService.sendBsq(request);
TxInfo txInfo = reply.getTxInfo();
out.printf("%s bsq sent to %s in tx %s%n", amount, address, txInfo.getId());
out.printf("%s bsq sent to %s in tx %s%n",
amount,
address,
txInfo.getTxId());
return;
}
case sendbtc: {
Expand Down Expand Up @@ -305,7 +310,10 @@ public static void run(String[] args) {
.build();
var reply = walletsService.sendBtc(request);
TxInfo txInfo = reply.getTxInfo();
out.printf("%s btc sent to %s in tx %s%n", amount, address, txInfo.getId());
out.printf("%s btc sent to %s in tx %s%n",
amount,
address,
txInfo.getTxId());
return;
}
case gettxfeerate: {
Expand All @@ -332,6 +340,18 @@ public static void run(String[] args) {
out.println(formatTxFeeRateInfo(reply.getTxFeeRateInfo()));
return;
}
case gettransaction: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("no tx id specified");

var txId = nonOptionArgs.get(1);
var request = GetTransactionRequest.newBuilder()
.setTxId(txId)
.build();
var reply = walletsService.getTransaction(request);
out.println(TransactionFormat.format(reply.getTxInfo()));
return;
}
case createoffer: {
if (nonOptionArgs.size() < 9)
throw new IllegalArgumentException("incorrect parameter count,"
Expand Down Expand Up @@ -441,7 +461,7 @@ public static void run(String[] args) {
return;
}
case gettrade: {
// TODO make short-id a valid argument
// TODO make short-id a valid argument?
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("incorrect parameter count, "
+ " expecting trade id [,showcontract = true|false]");
Expand Down Expand Up @@ -720,6 +740,7 @@ private static void printHelp(OptionParser parser, PrintStream stream) {
stream.format(rowFormat, "gettxfeerate", "", "Get current tx fee rate in sats/byte");
stream.format(rowFormat, "settxfeerate", "satoshis (per byte)", "Set custom tx fee rate in sats/byte");
stream.format(rowFormat, "unsettxfeerate", "", "Unset custom tx fee rate");
stream.format(rowFormat, "gettransaction", "transaction id", "Get transaction with id");
stream.format(rowFormat, "createoffer", "payment acct id, buy | sell, currency code, \\", "Create and place an offer");
stream.format(rowFormat, "", "amount (btc), min amount, use mkt based price, \\", "");
stream.format(rowFormat, "", "fixed price (btc) | mkt price margin (%), security deposit (%) \\", "");
Expand Down
10 changes: 10 additions & 0 deletions cli/src/main/java/bisq/cli/ColumnHeaderConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ class ColumnHeaderConstants {
static final String COL_HEADER_TRADE_SHORT_ID = "ID";
static final String COL_HEADER_TRADE_TX_FEE = "Tx Fee(%-3s)";
static final String COL_HEADER_TRADE_TAKER_FEE = "Taker Fee(%-3s)";
static final String COL_HEADER_TRADE_WITHDRAWAL_TX_ID = "Withdrawal TX ID";

static final String COL_HEADER_TX_ID = "Tx ID";
static final String COL_HEADER_TX_INPUT_SUM = "Tx Inputs (BTC)";
static final String COL_HEADER_TX_OUTPUT_SUM = "Tx Outputs (BTC)";
static final String COL_HEADER_TX_FEE = "Tx Fee (BTC)";
static final String COL_HEADER_TX_SIZE = "Tx Size (Bytes)";
static final String COL_HEADER_TX_IS_CONFIRMED = "Is Confirmed";
static final String COL_HEADER_TX_MEMO = "Memo";

static final String COL_HEADER_VOLUME = padEnd("%-3s(min - max)", 15, ' ');
static final String COL_HEADER_UUID = padEnd("ID", 52, ' ');
}
33 changes: 19 additions & 14 deletions cli/src/main/java/bisq/cli/TradeFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public static String format(TradeInfo tradeInfo) {
+ COL_HEADER_TRADE_FIAT_RECEIVED + COL_HEADER_DELIMITER
+ COL_HEADER_TRADE_PAYOUT_PUBLISHED + COL_HEADER_DELIMITER
+ COL_HEADER_TRADE_WITHDRAWN + COL_HEADER_DELIMITER
+ (tradeInfo.getIsWithdrawn() ? COL_HEADER_TRADE_WITHDRAWAL_TX_ID + COL_HEADER_DELIMITER : "")
+ "%n";

String counterCurrencyCode = tradeInfo.getOffer().getCounterCurrencyCode();
Expand All @@ -66,18 +67,20 @@ public static String format(TradeInfo tradeInfo) {
? String.format(headersFormat, counterCurrencyCode, baseCurrencyCode, baseCurrencyCode, baseCurrencyCode)
: String.format(headersFormat, counterCurrencyCode, baseCurrencyCode, baseCurrencyCode);

String colDataFormat = "%-" + shortIdColWidth + "s" // left justify
+ " %-" + (roleColWidth + COL_HEADER_DELIMITER.length()) + "s" // left justify
+ "%" + (COL_HEADER_PRICE.length() - 1) + "s" // right justify
+ "%" + (COL_HEADER_TRADE_AMOUNT.length() + 1) + "s" // right justify
+ "%" + (COL_HEADER_TRADE_TX_FEE.length() + 1) + "s" // right justify
+ takerFeeHeader.get() // right justify
+ " %-" + COL_HEADER_TRADE_DEPOSIT_PUBLISHED.length() + "s" // left justify
+ " %-" + COL_HEADER_TRADE_DEPOSIT_CONFIRMED.length() + "s" // left justify
+ " %-" + COL_HEADER_TRADE_FIAT_SENT.length() + "s" // left justify
+ " %-" + COL_HEADER_TRADE_FIAT_RECEIVED.length() + "s" // left justify
+ " %-" + COL_HEADER_TRADE_PAYOUT_PUBLISHED.length() + "s" // left justify
+ " %-" + COL_HEADER_TRADE_WITHDRAWN.length() + "s"; // left justify

String colDataFormat = "%-" + shortIdColWidth + "s" // lt justify
+ " %-" + (roleColWidth + COL_HEADER_DELIMITER.length()) + "s" // left
+ "%" + (COL_HEADER_PRICE.length() - 1) + "s" // rt justify
+ "%" + (COL_HEADER_TRADE_AMOUNT.length() + 1) + "s" // rt justify
+ "%" + (COL_HEADER_TRADE_TX_FEE.length() + 1) + "s" // rt justify
+ takerFeeHeader.get() // rt justify
+ " %-" + COL_HEADER_TRADE_DEPOSIT_PUBLISHED.length() + "s" // lt justify
+ " %-" + COL_HEADER_TRADE_DEPOSIT_CONFIRMED.length() + "s" // lt justify
+ " %-" + COL_HEADER_TRADE_FIAT_SENT.length() + "s" // lt justify
+ " %-" + COL_HEADER_TRADE_FIAT_RECEIVED.length() + "s" // lt justify
+ " %-" + COL_HEADER_TRADE_PAYOUT_PUBLISHED.length() + "s" // lt justify
+ " %-" + COL_HEADER_TRADE_WITHDRAWN.length() + "s" // lt justify
+ " %-" + COL_HEADER_TRADE_WITHDRAWAL_TX_ID.length() + "s"; // left

return headerLine +
(isTaker
Expand All @@ -97,7 +100,8 @@ private static String formatTradeForMaker(String format, TradeInfo tradeInfo) {
tradeInfo.getIsFiatSent() ? "YES" : "NO",
tradeInfo.getIsFiatReceived() ? "YES" : "NO",
tradeInfo.getIsPayoutPublished() ? "YES" : "NO",
tradeInfo.getIsWithdrawn() ? "YES" : "NO");
tradeInfo.getIsWithdrawn() ? "YES" : "NO",
tradeInfo.getIsWithdrawn() ? tradeInfo.getWithdrawalTxId() : "");
}

private static String formatTradeForTaker(String format, TradeInfo tradeInfo) {
Expand All @@ -113,6 +117,7 @@ private static String formatTradeForTaker(String format, TradeInfo tradeInfo) {
tradeInfo.getIsFiatSent() ? "YES" : "NO",
tradeInfo.getIsFiatReceived() ? "YES" : "NO",
tradeInfo.getIsPayoutPublished() ? "YES" : "NO",
tradeInfo.getIsWithdrawn() ? "YES" : "NO");
tradeInfo.getIsWithdrawn() ? "YES" : "NO",
tradeInfo.getIsWithdrawn() ? tradeInfo.getWithdrawalTxId() : "");
}
}
59 changes: 59 additions & 0 deletions cli/src/main/java/bisq/cli/TransactionFormat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.cli;

import bisq.proto.grpc.TxInfo;

import com.google.common.annotations.VisibleForTesting;

import static bisq.cli.ColumnHeaderConstants.*;
import static bisq.cli.CurrencyFormat.formatSatoshis;
import static com.google.common.base.Strings.padEnd;

@VisibleForTesting
public class TransactionFormat {

public static String format(TxInfo txInfo) {
String headerLine = padEnd(COL_HEADER_TX_ID, txInfo.getTxId().length(), ' ') + COL_HEADER_DELIMITER
+ COL_HEADER_TX_IS_CONFIRMED + COL_HEADER_DELIMITER
+ COL_HEADER_TX_INPUT_SUM + COL_HEADER_DELIMITER
+ COL_HEADER_TX_OUTPUT_SUM + COL_HEADER_DELIMITER
+ COL_HEADER_TX_FEE + COL_HEADER_DELIMITER
+ COL_HEADER_TX_SIZE + COL_HEADER_DELIMITER
+ (txInfo.getMemo().isEmpty() ? "" : COL_HEADER_TX_MEMO + COL_HEADER_DELIMITER)
+ "\n";

String colDataFormat = "%-" + txInfo.getTxId().length() + "s"
+ " %" + COL_HEADER_TX_IS_CONFIRMED.length() + "s"
+ " %" + COL_HEADER_TX_INPUT_SUM.length() + "s"
+ " %" + COL_HEADER_TX_OUTPUT_SUM.length() + "s"
+ " %" + COL_HEADER_TX_FEE.length() + "s"
+ " %" + COL_HEADER_TX_SIZE.length() + "s"
+ " %s";

return headerLine
+ String.format(colDataFormat,
txInfo.getTxId(),
txInfo.getIsPending() ? "NO" : "YES", // pending=true means not confirmed
formatSatoshis(txInfo.getInputSum()),
formatSatoshis(txInfo.getOutputSum()),
formatSatoshis(txInfo.getFee()),
txInfo.getSize(),
txInfo.getMemo().isEmpty() ? "" : txInfo.getMemo());
}
}
4 changes: 4 additions & 0 deletions core/src/main/java/bisq/core/api/CoreApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,10 @@ public TxFeeRateInfo getMostRecentTxFeeRateInfo() {
return walletsService.getMostRecentTxFeeRateInfo();
}

public Transaction getTransaction(String txId) {
return walletsService.getTransaction(txId);
}

public void setWalletPassword(String password, String newPassword) {
walletsService.setWalletPassword(password, newPassword);
}
Expand Down
Loading

0 comments on commit 5522d0c

Please sign in to comment.