diff --git a/core/src/main/java/haveno/core/api/CoreApi.java b/core/src/main/java/haveno/core/api/CoreApi.java index e8e83978eb..5162bfdb33 100644 --- a/core/src/main/java/haveno/core/api/CoreApi.java +++ b/core/src/main/java/haveno/core/api/CoreApi.java @@ -299,8 +299,12 @@ public class CoreApi { return walletsService.createXmrTx(destinations); } - public String relayXmrTx(String metadata) { - return walletsService.relayXmrTx(metadata); + public List createXmrSweepTxs(String address) { + return walletsService.createXmrSweepTxs(address); + } + + public List relayXmrTxs(List metadatas) { + return walletsService.relayXmrTxs(metadatas); } public long getAddressBalance(String addressString) { diff --git a/core/src/main/java/haveno/core/api/CoreWalletsService.java b/core/src/main/java/haveno/core/api/CoreWalletsService.java index 68ec8c13ea..0433a8e994 100644 --- a/core/src/main/java/haveno/core/api/CoreWalletsService.java +++ b/core/src/main/java/haveno/core/api/CoreWalletsService.java @@ -173,12 +173,24 @@ class CoreWalletsService { } } - String relayXmrTx(String metadata) { + List createXmrSweepTxs(String address) { accountService.checkAccountOpen(); verifyWalletsAreAvailable(); verifyEncryptedWalletIsUnlocked(); try { - return xmrWalletService.relayTx(metadata); + return xmrWalletService.createSweepTxs(address); + } catch (Exception ex) { + log.error("", ex); + throw new IllegalStateException(ex); + } + } + + List relayXmrTxs(List metadatas) { + accountService.checkAccountOpen(); + verifyWalletsAreAvailable(); + verifyEncryptedWalletIsUnlocked(); + try { + return xmrWalletService.relayTxs(metadatas); } catch (Exception ex) { log.error("", ex); throw new IllegalStateException(ex); diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 65cd28d3d8..b2c4ce90e2 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -441,6 +441,12 @@ public class XmrWalletService extends XmrWalletBase { if (name.contains(File.separator)) throw new IllegalArgumentException("Path not expected: " + name); } + public MoneroTxWallet createTx(List destinations) { + MoneroTxWallet tx = createTx(new MoneroTxConfig().setAccountIndex(0).setDestinations(destinations).setRelay(false).setCanSplit(false)); + //printTxs("XmrWalletService.createTx", tx); + return tx; + } + public MoneroTxWallet createTx(MoneroTxConfig txConfig) { synchronized (walletLock) { synchronized (HavenoUtils.getWalletFunctionLock()) { @@ -455,18 +461,30 @@ public class XmrWalletService extends XmrWalletBase { } } - public String relayTx(String metadata) { + public List createSweepTxs(String address) { + return createSweepTxs(new MoneroTxConfig().setAccountIndex(0).setAddress(address).setRelay(false)); + } + + public List createSweepTxs(MoneroTxConfig txConfig) { synchronized (walletLock) { - String txId = wallet.relayTx(metadata); - requestSaveWallet(); - return txId; + synchronized (HavenoUtils.getWalletFunctionLock()) { + List txs = wallet.sweepUnlocked(txConfig); + if (Boolean.TRUE.equals(txConfig.getRelay())) { + for (MoneroTxWallet tx : txs) cachedTxs.addFirst(tx); + cacheWalletInfo(); + requestSaveWallet(); + } + return txs; + } } } - public MoneroTxWallet createTx(List destinations) { - MoneroTxWallet tx = createTx(new MoneroTxConfig().setAccountIndex(0).setDestinations(destinations).setRelay(false).setCanSplit(false)); - //printTxs("XmrWalletService.createTx", tx); - return tx; + public List relayTxs(List metadatas) { + synchronized (walletLock) { + List txIds = wallet.relayTxs(metadatas); + requestSaveWallet(); + return txIds; + } } /** diff --git a/daemon/src/main/java/haveno/daemon/grpc/GrpcWalletsService.java b/daemon/src/main/java/haveno/daemon/grpc/GrpcWalletsService.java index 7c3ca22e3b..9fbfa02089 100644 --- a/daemon/src/main/java/haveno/daemon/grpc/GrpcWalletsService.java +++ b/daemon/src/main/java/haveno/daemon/grpc/GrpcWalletsService.java @@ -43,6 +43,8 @@ import static haveno.core.api.model.XmrTx.toXmrTx; import haveno.daemon.grpc.interceptor.CallRateMeteringInterceptor; import haveno.daemon.grpc.interceptor.GrpcCallRateMeter; import static haveno.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor; +import haveno.proto.grpc.CreateXmrSweepTxsReply; +import haveno.proto.grpc.CreateXmrSweepTxsRequest; import haveno.proto.grpc.CreateXmrTxReply; import haveno.proto.grpc.CreateXmrTxRequest; import haveno.proto.grpc.GetAddressBalanceReply; @@ -61,8 +63,8 @@ import haveno.proto.grpc.GetXmrTxsReply; import haveno.proto.grpc.GetXmrTxsRequest; import haveno.proto.grpc.LockWalletReply; import haveno.proto.grpc.LockWalletRequest; -import haveno.proto.grpc.RelayXmrTxReply; -import haveno.proto.grpc.RelayXmrTxRequest; +import haveno.proto.grpc.RelayXmrTxsReply; +import haveno.proto.grpc.RelayXmrTxsRequest; import haveno.proto.grpc.RemoveWalletPasswordReply; import haveno.proto.grpc.RemoveWalletPasswordRequest; import haveno.proto.grpc.SetWalletPasswordReply; @@ -185,7 +187,7 @@ class GrpcWalletsService extends WalletsImplBase { .stream() .map(s -> new MoneroDestination(s.getAddress(), new BigInteger(s.getAmount()))) .collect(Collectors.toList())); - log.info("Successfully created XMR tx: hash {}", tx.getHash()); + log.info("Successfully created XMR tx, hash: {}", tx.getHash()); var reply = CreateXmrTxReply.newBuilder() .setTx(toXmrTx(tx).toProtoMessage()) .build(); @@ -197,12 +199,30 @@ class GrpcWalletsService extends WalletsImplBase { } @Override - public void relayXmrTx(RelayXmrTxRequest req, - StreamObserver responseObserver) { + public void createXmrSweepTxs(CreateXmrSweepTxsRequest req, + StreamObserver responseObserver) { try { - String txHash = coreApi.relayXmrTx(req.getMetadata()); - var reply = RelayXmrTxReply.newBuilder() - .setHash(txHash) + List xmrTxs = coreApi.createXmrSweepTxs(req.getAddress()); + log.info("Successfully created XMR sweep txs, hashes: {}", xmrTxs.stream().map(MoneroTxWallet::getHash).collect(Collectors.toList())); + var reply = CreateXmrSweepTxsReply.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 relayXmrTxs(RelayXmrTxsRequest req, + StreamObserver responseObserver) { + try { + List txHashes = coreApi.relayXmrTxs(req.getMetadatasList()); + var reply = RelayXmrTxsReply.newBuilder() + .addAllHashes(txHashes) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index b9615b5bcb..b2af139042 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -944,7 +944,9 @@ service Wallets { } rpc CreateXmrTx (CreateXmrTxRequest) returns (CreateXmrTxReply) { } - rpc relayXmrTx (RelayXmrTxRequest) returns (RelayXmrTxReply) { + rpc CreateXmrSweepTxs (CreateXmrSweepTxsRequest) returns (CreateXmrSweepTxsReply) { + } + rpc RelayXmrTxs (RelayXmrTxsRequest) returns (RelayXmrTxsReply) { } rpc GetAddressBalance (GetAddressBalanceRequest) returns (GetAddressBalanceReply) { } @@ -1036,12 +1038,20 @@ message CreateXmrTxReply { XmrTx tx = 1; } -message RelayXmrTxRequest { - string metadata = 1; +message CreateXmrSweepTxsRequest { + string address = 1; } -message RelayXmrTxReply { - string hash = 1; +message CreateXmrSweepTxsReply { + repeated XmrTx txs = 1; +} + +message RelayXmrTxsRequest { + repeated string metadatas = 1; +} + +message RelayXmrTxsReply { + repeated string hashes = 2; } message GetAddressBalanceRequest {