Add API functions to get wallet transfers and withdraw funds

This commit is contained in:
l0nelyc0w 2021-12-30 21:59:48 +02:00 committed by woodser
parent 8dc4f63ca8
commit c1699795e7
9 changed files with 663 additions and 16 deletions

View File

@ -54,6 +54,11 @@ import java.util.function.Consumer;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; 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. * Provides high level interface to functionality of core Bisq features.
* E.g. useful for different APIs to access data of different domains of Bisq. * E.g. useful for different APIs to access data of different domains of Bisq.
@ -295,6 +300,18 @@ public class CoreApi {
return walletsService.getNewDepositSubaddress(); return walletsService.getNewDepositSubaddress();
} }
public List<MoneroTxWallet> getXmrTxs() {
return walletsService.getXmrTxs();
}
public MoneroTxWallet createXmrTx(List<MoneroDestination> destinations) {
return walletsService.createXmrTx(destinations);
}
public String relayXmrTx(String metadata) {
return walletsService.relayXmrTx(metadata);
}
public long getAddressBalance(String addressString) { public long getAddressBalance(String addressString) {
return walletsService.getAddressBalance(addressString); return walletsService.getAddressBalance(addressString);
} }

View File

@ -26,12 +26,9 @@ import bisq.core.app.AppStartupState;
import bisq.core.btc.Balances; import bisq.core.btc.Balances;
import bisq.core.btc.exceptions.AddressEntryException; import bisq.core.btc.exceptions.AddressEntryException;
import bisq.core.btc.exceptions.InsufficientFundsException; 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.model.AddressEntry;
import bisq.core.btc.setup.WalletsSetup; import bisq.core.btc.setup.WalletsSetup;
import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.TxBroadcaster;
import bisq.core.btc.wallet.WalletsManager; import bisq.core.btc.wallet.WalletsManager;
import bisq.core.btc.wallet.XmrWalletService; import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.provider.fee.FeeService; import bisq.core.provider.fee.FeeService;
@ -47,11 +44,9 @@ import bisq.common.util.Utilities;
import org.bitcoinj.core.Address; import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Transaction; import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.crypto.KeyCrypterScrypt; import org.bitcoinj.crypto.KeyCrypterScrypt;
import javax.inject.Inject; import javax.inject.Inject;
@ -73,7 +68,6 @@ import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j; 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.lang.String.format;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import monero.wallet.model.MoneroDestination;
import monero.wallet.model.MoneroTxWallet;
@Singleton @Singleton
@Slf4j @Slf4j
class CoreWalletsService { class CoreWalletsService {
@ -157,11 +156,37 @@ class CoreWalletsService {
return new BalancesInfo(getBtcBalances(), getXmrBalances()); return new BalancesInfo(getBtcBalances(), getXmrBalances());
} }
} }
String getNewDepositSubaddress() { String getNewDepositSubaddress() {
return xmrWalletService.getWallet().createSubaddress(0).getAddress(); return xmrWalletService.getWallet().createSubaddress(0).getAddress();
} }
List<MoneroTxWallet> getXmrTxs(){
return xmrWalletService.getWallet().getTxs();
}
MoneroTxWallet createXmrTx(List<MoneroDestination> 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) { long getAddressBalance(String addressString) {
Address address = getAddressEntry(addressString).getAddress(); Address address = getAddressEntry(addressString).getAddress();
return btcWalletService.getBalanceForAddress(address).value; return btcWalletService.getBalanceForAddress(address).value;
@ -215,7 +240,6 @@ class CoreWalletsService {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
void sendBtc(String address, void sendBtc(String address,
String amount, String amount,
String txFeeRate, String txFeeRate,
@ -530,15 +554,15 @@ class CoreWalletsService {
var availableBalance = balances.getAvailableBalance().get(); var availableBalance = balances.getAvailableBalance().get();
if (availableBalance == null) if (availableBalance == null)
throw new IllegalStateException("available balance is not yet available"); throw new IllegalStateException("available balance is not yet available");
var lockedBalance = balances.getLockedBalance().get(); var lockedBalance = balances.getLockedBalance().get();
if (lockedBalance == null) if (lockedBalance == null)
throw new IllegalStateException("locked balance is not yet available"); throw new IllegalStateException("locked balance is not yet available");
var reservedOfferBalance = balances.getReservedOfferBalance().get(); var reservedOfferBalance = balances.getReservedOfferBalance().get();
if (reservedOfferBalance == null) if (reservedOfferBalance == null)
throw new IllegalStateException("reserved offer balance is not yet available"); throw new IllegalStateException("reserved offer balance is not yet available");
var reservedTradeBalance = balances.getReservedTradeBalance().get(); var reservedTradeBalance = balances.getReservedTradeBalance().get();
if (reservedTradeBalance == null) if (reservedTradeBalance == null)
throw new IllegalStateException("reserved trade balance is not yet available"); throw new IllegalStateException("reserved trade balance is not yet available");

View File

@ -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 +
'}';
}
}

View File

@ -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 +
'}';
}
}

View File

@ -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<Integer> subaddressIndices;
@Nullable
private final List<XmrDestination> 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<XmrDestination> 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<XmrDestination> 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<Integer> subaddressIndices;
private List<XmrDestination> 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<Integer> subaddressIndices) {
this.subaddressIndices = subaddressIndices;
return this;
}
public XmrOutgoingTransferBuilder withDestinations(List<XmrDestination> 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 +
'}';
}
}

View File

@ -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<XmrIncomingTransfer> 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<XmrIncomingTransfer> 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<XmrIncomingTransfer> 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<XmrIncomingTransfer> 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 +
'}';
}
}

View File

@ -34,6 +34,7 @@ import lombok.Getter;
import monero.daemon.MoneroDaemon; import monero.daemon.MoneroDaemon;
import monero.wallet.MoneroWallet; import monero.wallet.MoneroWallet;
import monero.wallet.model.MoneroDestination;
import monero.wallet.model.MoneroOutputWallet; import monero.wallet.model.MoneroOutputWallet;
import monero.wallet.model.MoneroSubaddress; import monero.wallet.model.MoneroSubaddress;
import monero.wallet.model.MoneroTxConfig; import monero.wallet.model.MoneroTxConfig;
@ -239,7 +240,7 @@ public class XmrWalletService {
} }
public Coin getBalanceForSubaddress(int subaddressIndex) { public Coin getBalanceForSubaddress(int subaddressIndex) {
// get subaddress balance // get subaddress balance
BigInteger balance = wallet.getBalance(0, subaddressIndex); BigInteger balance = wallet.getBalance(0, subaddressIndex);
@ -358,6 +359,25 @@ public class XmrWalletService {
// return sendResult.tx.getTxId().toString(); // return sendResult.tx.getTxId().toString();
// } // }
///////////////////////////////////////////////////////////////////////////////////////////
// Create Tx
///////////////////////////////////////////////////////////////////////////////////////////
public MoneroTxWallet createTx(List<MoneroDestination> 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 // Util
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -414,7 +434,7 @@ public class XmrWalletService {
} }
}); });
} }
@Override @Override
public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) { public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) {
UserThread.execute(new Runnable() { UserThread.execute(new Runnable() {

View File

@ -20,8 +20,6 @@ package bisq.daemon.grpc;
import bisq.core.api.CoreApi; import bisq.core.api.CoreApi;
import bisq.core.api.model.AddressBalanceInfo; import bisq.core.api.model.AddressBalanceInfo;
import bisq.core.api.model.TxFeeRateInfo; 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.GetAddressBalanceReply;
import bisq.proto.grpc.GetAddressBalanceRequest; import bisq.proto.grpc.GetAddressBalanceRequest;
@ -31,6 +29,12 @@ import bisq.proto.grpc.GetFundingAddressesReply;
import bisq.proto.grpc.GetFundingAddressesRequest; import bisq.proto.grpc.GetFundingAddressesRequest;
import bisq.proto.grpc.GetNewDepositSubaddressRequest; import bisq.proto.grpc.GetNewDepositSubaddressRequest;
import bisq.proto.grpc.GetNewDepositSubaddressReply; 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.GetTransactionReply;
import bisq.proto.grpc.GetTransactionRequest; import bisq.proto.grpc.GetTransactionRequest;
import bisq.proto.grpc.GetTxFeeRateReply; import bisq.proto.grpc.GetTxFeeRateReply;
@ -59,6 +63,8 @@ import javax.inject.Inject;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import java.math.BigInteger;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -69,6 +75,7 @@ import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import static bisq.core.api.model.TxInfo.toTxInfo; 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.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
import static bisq.proto.grpc.WalletsGrpc.*; import static bisq.proto.grpc.WalletsGrpc.*;
import static java.util.concurrent.TimeUnit.MINUTES; 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.CallRateMeteringInterceptor;
import bisq.daemon.grpc.interceptor.GrpcCallRateMeter; import bisq.daemon.grpc.interceptor.GrpcCallRateMeter;
import monero.wallet.model.MoneroDestination;
import monero.wallet.model.MoneroTxWallet;
@Slf4j @Slf4j
class GrpcWalletsService extends WalletsImplBase { class GrpcWalletsService extends WalletsImplBase {
@ -104,7 +113,7 @@ class GrpcWalletsService extends WalletsImplBase {
exceptionHandler.handleException(log, cause, responseObserver); exceptionHandler.handleException(log, cause, responseObserver);
} }
} }
@Override @Override
public void getNewDepositSubaddress(GetNewDepositSubaddressRequest req, public void getNewDepositSubaddress(GetNewDepositSubaddressRequest req,
StreamObserver<GetNewDepositSubaddressReply> responseObserver) { StreamObserver<GetNewDepositSubaddressReply> responseObserver) {
@ -120,6 +129,59 @@ class GrpcWalletsService extends WalletsImplBase {
} }
} }
@Override
public void getXmrTxs(GetXmrTxsRequest req, StreamObserver<GetXmrTxsReply> responseObserver) {
try {
List<MoneroTxWallet> 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<CreateXmrTxReply> 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<RelayXmrTxReply> 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 @Override
public void getAddressBalance(GetAddressBalanceRequest req, public void getAddressBalance(GetAddressBalanceRequest req,
StreamObserver<GetAddressBalanceReply> responseObserver) { StreamObserver<GetAddressBalanceReply> responseObserver) {

View File

@ -453,6 +453,12 @@ service Wallets {
} }
rpc GetNewDepositSubaddress (GetNewDepositSubaddressRequest) returns (GetNewDepositSubaddressReply) { 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 GetAddressBalance (GetAddressBalanceRequest) returns (GetAddressBalanceReply) {
} }
rpc SendBtc (SendBtcRequest) returns (SendBtcReply) { rpc SendBtc (SendBtcRequest) returns (SendBtcReply) {
@ -492,6 +498,61 @@ message GetNewDepositSubaddressReply {
string subaddress = 1; 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 { message GetAddressBalanceRequest {
string address = 1; string address = 1;
} }