From c1699795e790cead87dfc6771711a9d9544e1c8a Mon Sep 17 00:00:00 2001 From: l0nelyc0w Date: Thu, 30 Dec 2021 21:59:48 +0200 Subject: [PATCH] Add API functions to get wallet transfers and withdraw funds --- core/src/main/java/bisq/core/api/CoreApi.java | 17 ++ .../bisq/core/api/CoreWalletsService.java | 46 +++-- .../bisq/core/api/model/XmrDestination.java | 69 +++++++ .../core/api/model/XmrIncomingTransfer.java | 107 +++++++++++ .../core/api/model/XmrOutgoingTransfer.java | 111 +++++++++++ .../main/java/bisq/core/api/model/XmrTx.java | 176 ++++++++++++++++++ .../core/btc/wallet/XmrWalletService.java | 24 ++- .../bisq/daemon/grpc/GrpcWalletsService.java | 68 ++++++- proto/src/main/proto/grpc.proto | 61 ++++++ 9 files changed, 663 insertions(+), 16 deletions(-) create mode 100644 core/src/main/java/bisq/core/api/model/XmrDestination.java create mode 100644 core/src/main/java/bisq/core/api/model/XmrIncomingTransfer.java create mode 100644 core/src/main/java/bisq/core/api/model/XmrOutgoingTransfer.java create mode 100644 core/src/main/java/bisq/core/api/model/XmrTx.java diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index c99d0dda17..5767847598 100644 --- a/core/src/main/java/bisq/core/api/CoreApi.java +++ b/core/src/main/java/bisq/core/api/CoreApi.java @@ -54,6 +54,11 @@ import java.util.function.Consumer; import lombok.Getter; import lombok.extern.slf4j.Slf4j; + + +import monero.wallet.model.MoneroDestination; +import monero.wallet.model.MoneroTxWallet; + /** * Provides high level interface to functionality of core Bisq features. * E.g. useful for different APIs to access data of different domains of Bisq. @@ -295,6 +300,18 @@ public class CoreApi { return walletsService.getNewDepositSubaddress(); } + public List getXmrTxs() { + return walletsService.getXmrTxs(); + } + + public MoneroTxWallet createXmrTx(List destinations) { + return walletsService.createXmrTx(destinations); + } + + public String relayXmrTx(String metadata) { + return walletsService.relayXmrTx(metadata); + } + public long getAddressBalance(String addressString) { return walletsService.getAddressBalance(addressString); } diff --git a/core/src/main/java/bisq/core/api/CoreWalletsService.java b/core/src/main/java/bisq/core/api/CoreWalletsService.java index 7491b2623d..347fa72bb6 100644 --- a/core/src/main/java/bisq/core/api/CoreWalletsService.java +++ b/core/src/main/java/bisq/core/api/CoreWalletsService.java @@ -26,12 +26,9 @@ import bisq.core.app.AppStartupState; import bisq.core.btc.Balances; import bisq.core.btc.exceptions.AddressEntryException; import bisq.core.btc.exceptions.InsufficientFundsException; -import bisq.core.btc.exceptions.TransactionVerificationException; -import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.model.AddressEntry; import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.wallet.BtcWalletService; -import bisq.core.btc.wallet.TxBroadcaster; import bisq.core.btc.wallet.WalletsManager; import bisq.core.btc.wallet.XmrWalletService; import bisq.core.provider.fee.FeeService; @@ -47,11 +44,9 @@ import bisq.common.util.Utilities; import org.bitcoinj.core.Address; import org.bitcoinj.core.Coin; import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.LegacyAddress; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionConfidence; -import org.bitcoinj.core.TransactionOutput; import org.bitcoinj.crypto.KeyCrypterScrypt; import javax.inject.Inject; @@ -73,7 +68,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Function; -import java.util.function.Predicate; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @@ -85,6 +79,11 @@ import static bisq.core.util.ParsingUtils.parseToCoin; import static java.lang.String.format; import static java.util.concurrent.TimeUnit.SECONDS; + + +import monero.wallet.model.MoneroDestination; +import monero.wallet.model.MoneroTxWallet; + @Singleton @Slf4j class CoreWalletsService { @@ -157,11 +156,37 @@ class CoreWalletsService { return new BalancesInfo(getBtcBalances(), getXmrBalances()); } } - + String getNewDepositSubaddress() { return xmrWalletService.getWallet().createSubaddress(0).getAddress(); } + List getXmrTxs(){ + return xmrWalletService.getWallet().getTxs(); + } + + MoneroTxWallet createXmrTx(List destinations) { + verifyWalletsAreAvailable(); + verifyEncryptedWalletIsUnlocked(); + try { + return xmrWalletService.createTx(destinations); + } catch (Exception ex) { + log.error("", ex); + throw new IllegalStateException(ex); + } + } + + String relayXmrTx(String metadata) { + verifyWalletsAreAvailable(); + verifyEncryptedWalletIsUnlocked(); + try { + return xmrWalletService.getWallet().relayTx(metadata); + } catch (Exception ex) { + log.error("", ex); + throw new IllegalStateException(ex); + } + } + long getAddressBalance(String addressString) { Address address = getAddressEntry(addressString).getAddress(); return btcWalletService.getBalanceForAddress(address).value; @@ -215,7 +240,6 @@ class CoreWalletsService { .collect(Collectors.toList()); } - void sendBtc(String address, String amount, String txFeeRate, @@ -530,15 +554,15 @@ class CoreWalletsService { var availableBalance = balances.getAvailableBalance().get(); if (availableBalance == null) throw new IllegalStateException("available balance is not yet available"); - + var lockedBalance = balances.getLockedBalance().get(); if (lockedBalance == null) throw new IllegalStateException("locked balance is not yet available"); - + var reservedOfferBalance = balances.getReservedOfferBalance().get(); if (reservedOfferBalance == null) throw new IllegalStateException("reserved offer balance is not yet available"); - + var reservedTradeBalance = balances.getReservedTradeBalance().get(); if (reservedTradeBalance == null) throw new IllegalStateException("reserved trade balance is not yet available"); diff --git a/core/src/main/java/bisq/core/api/model/XmrDestination.java b/core/src/main/java/bisq/core/api/model/XmrDestination.java new file mode 100644 index 0000000000..e118d47c4b --- /dev/null +++ b/core/src/main/java/bisq/core/api/model/XmrDestination.java @@ -0,0 +1,69 @@ +package bisq.core.api.model; + +import bisq.common.Payload; +import java.math.BigInteger; +import lombok.Getter; +import monero.wallet.model.MoneroDestination; + +@Getter +public class XmrDestination implements Payload { + + private final String address; + private final BigInteger amount; + + public XmrDestination(XmrDestinationBuilder builder) { + this.address = builder.address; + this.amount = builder.amount; + } + + public static XmrDestination toXmrDestination(MoneroDestination dst) { + return new XmrDestinationBuilder() + .withAddress(dst.getAddress()) + .withAmount(dst.getAmount()) + .build(); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public bisq.proto.grpc.XmrDestination toProtoMessage() { + return bisq.proto.grpc.XmrDestination.newBuilder() + .setAddress(address) + .setAmount(amount.toString()) + .build(); + } + + public static XmrDestination fromProto(bisq.proto.grpc.XmrDestination proto) { + return new XmrDestinationBuilder() + .withAddress(proto.getAddress()) + .withAmount(new BigInteger(proto.getAmount())) + .build(); + } + + public static class XmrDestinationBuilder { + private String address; + private BigInteger amount; + + public XmrDestinationBuilder withAddress(String address) { + this.address = address; + return this; + } + + public XmrDestinationBuilder withAmount(BigInteger amount) { + this.amount = amount; + return this; + } + + public XmrDestination build() { return new XmrDestination(this); } + } + + @Override + public String toString() { + return "XmrDestination{" + + "address=" + address + + ", amount" + amount + + '}'; + } +} diff --git a/core/src/main/java/bisq/core/api/model/XmrIncomingTransfer.java b/core/src/main/java/bisq/core/api/model/XmrIncomingTransfer.java new file mode 100644 index 0000000000..6a5316f870 --- /dev/null +++ b/core/src/main/java/bisq/core/api/model/XmrIncomingTransfer.java @@ -0,0 +1,107 @@ +package bisq.core.api.model; + +import bisq.common.Payload; +import java.math.BigInteger; +import lombok.Getter; +import monero.wallet.model.MoneroIncomingTransfer; + +@Getter +public class XmrIncomingTransfer implements Payload { + + private final BigInteger amount; + private final Integer accountIndex; + private final Integer subaddressIndex; + private final String address; + private final Long numSuggestedConfirmations; + + public XmrIncomingTransfer(XmrIncomingTransferBuilder builder) { + this.amount = builder.amount; + this.accountIndex = builder.accountIndex; + this.subaddressIndex = builder.subaddressIndex; + this.address = builder.address; + this.numSuggestedConfirmations = builder.numSuggestedConfirmations; + } + + public static XmrIncomingTransfer toXmrIncomingTransfer(MoneroIncomingTransfer transfer) { + return new XmrIncomingTransferBuilder() + .withAmount(transfer.getAmount()) + .withAccountIndex(transfer.getAccountIndex()) + .withSubaddressIndex(transfer.getSubaddressIndex()) + .withAddress(transfer.getAddress()) + .withNumSuggestedConfirmations(transfer.getNumSuggestedConfirmations()) + .build(); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public bisq.proto.grpc.XmrIncomingTransfer toProtoMessage() { + return bisq.proto.grpc.XmrIncomingTransfer.newBuilder() + .setAmount(amount.toString()) + .setAccountIndex(accountIndex) + .setSubaddressIndex(subaddressIndex) + .setAddress(address) + .setNumSuggestedConfirmations(numSuggestedConfirmations) + .build(); + } + + public static XmrIncomingTransfer fromProto(bisq.proto.grpc.XmrIncomingTransfer proto) { + return new XmrIncomingTransferBuilder() + .withAmount(new BigInteger(proto.getAmount())) + .withAccountIndex(proto.getAccountIndex()) + .withSubaddressIndex(proto.getSubaddressIndex()) + .withAddress(proto.getAddress()) + .withNumSuggestedConfirmations(proto.getNumSuggestedConfirmations()) + .build(); + } + + public static class XmrIncomingTransferBuilder { + private BigInteger amount; + private Integer accountIndex; + private Integer subaddressIndex; + private String address; + private Long numSuggestedConfirmations; + + public XmrIncomingTransferBuilder withAmount(BigInteger amount) { + this.amount = amount; + return this; + } + + public XmrIncomingTransferBuilder withAccountIndex(Integer accountIndex) { + this.accountIndex = accountIndex; + return this; + } + + public XmrIncomingTransferBuilder withSubaddressIndex(Integer subaddressIndex) { + this.subaddressIndex = subaddressIndex; + return this; + } + + public XmrIncomingTransferBuilder withAddress(String address) { + this.address = address; + return this; + } + + public XmrIncomingTransferBuilder withNumSuggestedConfirmations(Long numSuggestedConfirmations) { + this.numSuggestedConfirmations = numSuggestedConfirmations; + return this; + } + + public XmrIncomingTransfer build() { + return new XmrIncomingTransfer(this); + } + } + + @Override + public String toString() { + return "XmrIncomingTransfer{" + + "amount=" + amount + + ", accountIndex=" + accountIndex + + ", subaddressIndex=" + subaddressIndex + + ", address=" + address + + ", numSuggestedConfirmations=" + numSuggestedConfirmations + + '}'; + } +} diff --git a/core/src/main/java/bisq/core/api/model/XmrOutgoingTransfer.java b/core/src/main/java/bisq/core/api/model/XmrOutgoingTransfer.java new file mode 100644 index 0000000000..587772d984 --- /dev/null +++ b/core/src/main/java/bisq/core/api/model/XmrOutgoingTransfer.java @@ -0,0 +1,111 @@ +package bisq.core.api.model; + +import static bisq.core.api.model.XmrDestination.toXmrDestination; + +import bisq.common.Payload; +import bisq.common.proto.ProtoUtil; +import java.math.BigInteger; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import lombok.Getter; +import monero.wallet.model.MoneroOutgoingTransfer; + +@Getter +public class XmrOutgoingTransfer implements Payload { + + private final BigInteger amount; + private final Integer accountIndex; + @Nullable + private final List subaddressIndices; + @Nullable + private final List destinations; + + public XmrOutgoingTransfer(XmrOutgoingTransferBuilder builder) { + this.amount = builder.amount; + this.accountIndex = builder.accountIndex; + this.subaddressIndices = builder.subaddressIndices; + this.destinations = builder.destinations; + } + + public static XmrOutgoingTransfer toXmrOutgoingTransfer(MoneroOutgoingTransfer transfer) { + List destinations = transfer.getDestinations() == null ? null : + transfer.getDestinations().stream() + .map(s -> toXmrDestination(s)) + .collect(Collectors.toList()); + XmrOutgoingTransferBuilder builder = new XmrOutgoingTransferBuilder() + .withAmount(transfer.getAmount()) + .withAccountIndex(transfer.getAccountIndex()) + .withSubaddressIndices(transfer.getSubaddressIndices()) + .withDestinations(destinations); + return builder.build(); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public bisq.proto.grpc.XmrOutgoingTransfer toProtoMessage() { + var builder = bisq.proto.grpc.XmrOutgoingTransfer.newBuilder() + .setAmount(amount.toString()) + .setAccountIndex(accountIndex); + Optional.ofNullable(subaddressIndices).ifPresent(e -> builder.addAllSubaddressIndices(subaddressIndices)); + Optional.ofNullable(destinations).ifPresent(e -> builder.addAllDestinations(ProtoUtil.collectionToProto(destinations, bisq.proto.grpc.XmrDestination.class))); + return builder.build(); + } + + public static XmrOutgoingTransfer fromProto(bisq.proto.grpc.XmrOutgoingTransfer proto) { + List destinations = proto.getDestinationsList().isEmpty() ? + null : proto.getDestinationsList().stream() + .map(XmrDestination::fromProto).collect(Collectors.toList()); + return new XmrOutgoingTransferBuilder() + .withAmount(new BigInteger(proto.getAmount())) + .withAccountIndex(proto.getAccountIndex()) + .withSubaddressIndices(proto.getSubaddressIndicesList()) + .withDestinations(destinations) + .build(); + } + + public static class XmrOutgoingTransferBuilder { + private BigInteger amount; + private Integer accountIndex; + private List subaddressIndices; + private List destinations; + + public XmrOutgoingTransferBuilder withAmount(BigInteger amount) { + this.amount = amount; + return this; + } + + public XmrOutgoingTransferBuilder withAccountIndex(Integer accountIndex) { + this.accountIndex = accountIndex; + return this; + } + + public XmrOutgoingTransferBuilder withSubaddressIndices(List subaddressIndices) { + this.subaddressIndices = subaddressIndices; + return this; + } + + public XmrOutgoingTransferBuilder withDestinations(List destinations) { + this.destinations = destinations; + return this; + } + + public XmrOutgoingTransfer build() { + return new XmrOutgoingTransfer(this); + } + } + + @Override + public String toString() { + return "XmrOutgoingTransfer{" + + "amount=" + amount + + ", accountIndex=" + accountIndex + + ", subaddressIndices=" + subaddressIndices + + ", destinations=" + destinations + + '}'; + } +} diff --git a/core/src/main/java/bisq/core/api/model/XmrTx.java b/core/src/main/java/bisq/core/api/model/XmrTx.java new file mode 100644 index 0000000000..e888eec881 --- /dev/null +++ b/core/src/main/java/bisq/core/api/model/XmrTx.java @@ -0,0 +1,176 @@ +package bisq.core.api.model; + +import static bisq.core.api.model.XmrIncomingTransfer.toXmrIncomingTransfer; +import static bisq.core.api.model.XmrOutgoingTransfer.toXmrOutgoingTransfer; + +import bisq.common.Payload; +import bisq.common.proto.ProtoUtil; +import java.math.BigInteger; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import lombok.Getter; +import monero.wallet.model.MoneroTxWallet; + +@Getter +public class XmrTx implements Payload { + + private final String hash; + private final BigInteger fee; + private final boolean isConfirmed; + private final boolean isLocked; + @Nullable + private final Long height; + @Nullable + private final Long timestamp; + @Nullable + private final List incomingTransfers; + @Nullable + private final XmrOutgoingTransfer outgoingTransfer; + @Nullable + private final String metadata; + + public XmrTx(XmrTxBuilder builder) { + this.hash = builder.hash; + this.fee = builder.fee; + this.isConfirmed = builder.isConfirmed; + this.isLocked = builder.isLocked; + this.height = builder.height; + this.timestamp = builder.timestamp; + this.incomingTransfers = builder.incomingTransfers; + this.outgoingTransfer = builder.outgoingTransfer; + this.metadata = builder.metadata; + } + + public static XmrTx toXmrTx(MoneroTxWallet tx){ + Long timestamp = tx.getBlock() == null ? null : tx.getBlock().getTimestamp(); + List incomingTransfers = tx.getIncomingTransfers() == null ? null : + tx.getIncomingTransfers().stream() + .map(s -> toXmrIncomingTransfer(s)) + .collect(Collectors.toList()); + XmrOutgoingTransfer outgoingTransfer = tx.getOutgoingTransfer() == null ? null : + toXmrOutgoingTransfer(tx.getOutgoingTransfer()); + XmrTxBuilder builder = new XmrTxBuilder() + .withHash(tx.getHash()) + .withFee(tx.getFee()) + .withIsConfirmed(tx.isConfirmed()) + .withIsLocked(tx.isLocked()); + Optional.ofNullable(tx.getHeight()).ifPresent(e ->builder.withHeight(tx.getHeight())); + Optional.ofNullable(timestamp).ifPresent(e ->builder.withTimestamp(timestamp)); + Optional.ofNullable(outgoingTransfer).ifPresent(e ->builder.withOutgoingTransfer(outgoingTransfer)); + Optional.ofNullable(incomingTransfers).ifPresent(e ->builder.withIncomingTransfers(incomingTransfers)); + Optional.ofNullable(tx.getMetadata()).ifPresent(e ->builder.withMetadata(tx.getMetadata())); + return builder.build(); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public bisq.proto.grpc.XmrTx toProtoMessage() { + bisq.proto.grpc.XmrTx.Builder builder = bisq.proto.grpc.XmrTx.newBuilder() + .setHash(hash) + .setFee(fee.toString()) + .setIsConfirmed(isConfirmed) + .setIsLocked(isLocked); + Optional.ofNullable(height).ifPresent(e -> builder.setHeight(height)); + Optional.ofNullable(timestamp).ifPresent(e -> builder.setTimestamp(timestamp)); + Optional.ofNullable(outgoingTransfer).ifPresent(e -> builder.setOutgoingTransfer(outgoingTransfer.toProtoMessage())); + Optional.ofNullable(incomingTransfers).ifPresent(e -> builder.addAllIncomingTransfers(ProtoUtil.collectionToProto(incomingTransfers, bisq.proto.grpc.XmrIncomingTransfer.class))); + Optional.ofNullable(metadata).ifPresent(e -> builder.setMetadata(metadata)); + return builder.build(); + } + + public static XmrTx fromProto(bisq.proto.grpc.XmrTx proto) { + return new XmrTxBuilder() + .withHash(proto.getHash()) + .withFee(new BigInteger(proto.getFee())) + .withIsConfirmed(proto.getIsConfirmed()) + .withIsLocked(proto.getIsLocked()) + .withHeight(proto.getHeight()) + .withTimestamp(proto.getTimestamp()) + .withIncomingTransfers( + proto.getIncomingTransfersList().stream() + .map(XmrIncomingTransfer::fromProto) + .collect(Collectors.toList())) + .withOutgoingTransfer(XmrOutgoingTransfer.fromProto(proto.getOutgoingTransfer())) + .withMetadata(proto.getMetadata()) + .build(); + } + + public static class XmrTxBuilder { + private String hash; + private BigInteger fee; + private boolean isConfirmed; + private boolean isLocked; + private Long height; + private Long timestamp; + private List incomingTransfers; + private XmrOutgoingTransfer outgoingTransfer; + private String metadata; + + public XmrTxBuilder withHash(String hash) { + this.hash = hash; + return this; + } + + public XmrTxBuilder withFee(BigInteger fee) { + this.fee = fee; + return this; + } + + public XmrTxBuilder withIsConfirmed(boolean isConfirmed) { + this.isConfirmed = isConfirmed; + return this; + } + + public XmrTxBuilder withIsLocked(boolean isLocked) { + this.isLocked = isLocked; + return this; + } + + public XmrTxBuilder withHeight(Long height) { + this.height = height; + return this; + } + + public XmrTxBuilder withTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } + + public XmrTxBuilder withIncomingTransfers(List incomingTransfers) { + this.incomingTransfers = incomingTransfers; + return this; + } + + public XmrTxBuilder withOutgoingTransfer(XmrOutgoingTransfer outgoingTransfer) { + this.outgoingTransfer = outgoingTransfer; + return this; + } + + public XmrTxBuilder withMetadata(String metadata) { + this.metadata = metadata; + return this; + } + + public XmrTx build() { return new XmrTx(this); } + } + + @Override + public String toString() { + return "XmrTx{" + + "hash=" + hash + + ", fee=" + timestamp + + ", isConfirmed=" + isConfirmed + + ", isLocked=" + isLocked + + ", height=" + height + + ", timestamp=" + timestamp + + ", incomingTransfers=" + incomingTransfers + + ", outgoingTransfer=" + outgoingTransfer + + ", metadata=" + metadata + + '}'; + } +} diff --git a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java index 1d35c409e1..0dcd1331bd 100644 --- a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java @@ -34,6 +34,7 @@ import lombok.Getter; import monero.daemon.MoneroDaemon; import monero.wallet.MoneroWallet; +import monero.wallet.model.MoneroDestination; import monero.wallet.model.MoneroOutputWallet; import monero.wallet.model.MoneroSubaddress; import monero.wallet.model.MoneroTxConfig; @@ -239,7 +240,7 @@ public class XmrWalletService { } public Coin getBalanceForSubaddress(int subaddressIndex) { - + // get subaddress balance BigInteger balance = wallet.getBalance(0, subaddressIndex); @@ -358,6 +359,25 @@ public class XmrWalletService { // return sendResult.tx.getTxId().toString(); // } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Create Tx + /////////////////////////////////////////////////////////////////////////////////////////// + + public MoneroTxWallet createTx(List destinations) { + try { + MoneroTxWallet tx = wallet.createTx(new MoneroTxConfig() + .setAccountIndex(0) + .setDestinations(destinations) + .setRelay(false) + .setCanSplit(false)); + printTxs("XmrWalletService.createTx", tx); + return tx; + } catch (Exception e) { + throw e; + } + } + /////////////////////////////////////////////////////////////////////////////////////////// // Util /////////////////////////////////////////////////////////////////////////////////////////// @@ -414,7 +434,7 @@ public class XmrWalletService { } }); } - + @Override public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) { UserThread.execute(new Runnable() { diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcWalletsService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcWalletsService.java index d07d70a901..8fbd194b6d 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcWalletsService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcWalletsService.java @@ -20,8 +20,6 @@ package bisq.daemon.grpc; import bisq.core.api.CoreApi; import bisq.core.api.model.AddressBalanceInfo; import bisq.core.api.model.TxFeeRateInfo; -import bisq.core.btc.exceptions.TxBroadcastException; -import bisq.core.btc.wallet.TxBroadcaster; import bisq.proto.grpc.GetAddressBalanceReply; import bisq.proto.grpc.GetAddressBalanceRequest; @@ -31,6 +29,12 @@ import bisq.proto.grpc.GetFundingAddressesReply; import bisq.proto.grpc.GetFundingAddressesRequest; import bisq.proto.grpc.GetNewDepositSubaddressRequest; import bisq.proto.grpc.GetNewDepositSubaddressReply; +import bisq.proto.grpc.GetXmrTxsRequest; +import bisq.proto.grpc.GetXmrTxsReply; +import bisq.proto.grpc.CreateXmrTxRequest; +import bisq.proto.grpc.CreateXmrTxReply; +import bisq.proto.grpc.RelayXmrTxRequest; +import bisq.proto.grpc.RelayXmrTxReply; import bisq.proto.grpc.GetTransactionReply; import bisq.proto.grpc.GetTransactionRequest; import bisq.proto.grpc.GetTxFeeRateReply; @@ -59,6 +63,8 @@ import javax.inject.Inject; import com.google.common.util.concurrent.FutureCallback; +import java.math.BigInteger; + import java.util.HashMap; import java.util.List; import java.util.Optional; @@ -69,6 +75,7 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import static bisq.core.api.model.TxInfo.toTxInfo; +import static bisq.core.api.model.XmrTx.toXmrTx; import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor; import static bisq.proto.grpc.WalletsGrpc.*; import static java.util.concurrent.TimeUnit.MINUTES; @@ -78,6 +85,8 @@ import static java.util.concurrent.TimeUnit.SECONDS; import bisq.daemon.grpc.interceptor.CallRateMeteringInterceptor; import bisq.daemon.grpc.interceptor.GrpcCallRateMeter; +import monero.wallet.model.MoneroDestination; +import monero.wallet.model.MoneroTxWallet; @Slf4j class GrpcWalletsService extends WalletsImplBase { @@ -104,7 +113,7 @@ class GrpcWalletsService extends WalletsImplBase { exceptionHandler.handleException(log, cause, responseObserver); } } - + @Override public void getNewDepositSubaddress(GetNewDepositSubaddressRequest req, StreamObserver responseObserver) { @@ -120,6 +129,59 @@ class GrpcWalletsService extends WalletsImplBase { } } + @Override + public void getXmrTxs(GetXmrTxsRequest req, StreamObserver responseObserver) { + try { + List xmrTxs = coreApi.getXmrTxs(); + var reply = GetXmrTxsReply.newBuilder() + .addAllTxs(xmrTxs.stream() + .map(s -> toXmrTx(s).toProtoMessage()) + .collect(Collectors.toList())) + .build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (Throwable cause) { + exceptionHandler.handleException(log, cause, responseObserver); + } + } + + @Override + public void createXmrTx(CreateXmrTxRequest req, + StreamObserver responseObserver) { + try { + MoneroTxWallet tx = coreApi.createXmrTx( + req.getDestinationsList() + .stream() + .map(s -> new MoneroDestination(s.getAddress(), new BigInteger(s.getAmount()))) + .collect(Collectors.toList())); + log.info("Successfully created XMR tx: hash {}, metadata {}", + tx.getHash(), + tx.getMetadata()); + var reply = CreateXmrTxReply.newBuilder() + .setTx(toXmrTx(tx).toProtoMessage()) + .build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (Throwable cause) { + exceptionHandler.handleException(log, cause, responseObserver); + } + } + + @Override + public void relayXmrTx(RelayXmrTxRequest req, + StreamObserver responseObserver) { + try { + String txHash = coreApi.relayXmrTx(req.getMetadata()); + var reply = RelayXmrTxReply.newBuilder() + .setHash(txHash) + .build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (Throwable cause) { + exceptionHandler.handleException(log, cause, responseObserver); + } + } + @Override public void getAddressBalance(GetAddressBalanceRequest req, StreamObserver responseObserver) { diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index 4d4aef15a3..f3607ae88f 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -453,6 +453,12 @@ service Wallets { } rpc GetNewDepositSubaddress (GetNewDepositSubaddressRequest) returns (GetNewDepositSubaddressReply) { } + rpc GetXmrTxs (GetXmrTxsRequest) returns (GetXmrTxsReply) { + } + rpc CreateXmrTx (CreateXmrTxRequest) returns (CreateXmrTxReply) { + } + rpc relayXmrTx (RelayXmrTxRequest) returns (RelayXmrTxReply) { + } rpc GetAddressBalance (GetAddressBalanceRequest) returns (GetAddressBalanceReply) { } rpc SendBtc (SendBtcRequest) returns (SendBtcReply) { @@ -492,6 +498,61 @@ message GetNewDepositSubaddressReply { string subaddress = 1; } +message GetXmrTxsRequest { +} + +message GetXmrTxsReply { + repeated XmrTx txs = 1; +} + +message XmrTx { + string hash = 1; + string fee = 2; + bool is_confirmed = 3; + bool is_locked = 4; + uint64 height = 5; + uint64 timestamp = 6; + repeated XmrIncomingTransfer incoming_transfers = 7; + XmrOutgoingTransfer outgoing_transfer = 8; + string metadata = 9; +} + +message XmrDestination { + string address = 1; + string amount = 2; +} + +message XmrIncomingTransfer { + string amount = 1; + int32 account_index = 2; + int32 subaddress_index = 3; + string address = 4; + uint64 num_suggested_confirmations = 5; +} + +message XmrOutgoingTransfer { + string amount = 1; + int32 account_index = 2; + repeated int32 subaddress_indices = 3; + repeated XmrDestination destinations = 4; +} + +message CreateXmrTxRequest { + repeated XmrDestination destinations = 1; +} + +message CreateXmrTxReply { + XmrTx tx = 1; +} + +message RelayXmrTxRequest { + string metadata = 1; +} + +message RelayXmrTxReply { + string hash = 1; +} + message GetAddressBalanceRequest { string address = 1; }