From 815934ff5cb4ced1306a5a37c1d0d4822f6075ce Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sat, 29 May 2021 19:43:08 +0100 Subject: [PATCH] Added GET /crosschain/htlc/redeemAll/LITECOIN API This loops through all sell orders and attempts to redeem the LTC from each one. It will return true if at least one was redeemed, or false if none are available to be redeemed. Details are logged to the log.txt file rather than returned in the API response. --- .../api/resource/CrossChainHtlcResource.java | 96 ++++++++++++++++++- 1 file changed, 92 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/qortal/api/resource/CrossChainHtlcResource.java b/src/main/java/org/qortal/api/resource/CrossChainHtlcResource.java index 0442b274..98e9b01d 100644 --- a/src/main/java/org/qortal/api/resource/CrossChainHtlcResource.java +++ b/src/main/java/org/qortal/api/resource/CrossChainHtlcResource.java @@ -20,10 +20,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.bitcoinj.core.*; import org.bitcoinj.script.Script; -import org.qortal.api.ApiError; -import org.qortal.api.ApiErrors; -import org.qortal.api.ApiExceptionFactory; -import org.qortal.api.Security; +import org.qortal.api.*; import org.qortal.api.model.CrossChainBitcoinyHTLCStatus; import org.qortal.crosschain.*; import org.qortal.crypto.Crypto; @@ -279,6 +276,97 @@ public class CrossChainHtlcResource { } } + @GET + @Path("/redeemAll/LITECOIN") + @Operation( + summary = "Redeems HTLC for all applicable ATs in tradebot data", + description = "To be used by a QORT seller (Bob) who needs to redeem LTC proceeds that are stuck in P2SH transactions.
" + + "This requires Bob's trade bot data to be present in the database for any ATs that need redeeming.
" + + "Returns true if at least one trade is redeemed. More detail is available in the log.txt.* file.", + responses = { + @ApiResponse( + content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "boolean")) + ) + } + ) + @ApiErrors({ApiError.INVALID_CRITERIA, ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN}) + public boolean redeemAllHtlc() { + Security.checkApiCallAllowed(request); + boolean success = false; + + try (final Repository repository = RepositoryManager.getRepository()) { + List allTradeBotData = repository.getCrossChainRepository().getAllTradeBotData(); + + for (TradeBotData tradeBotData : allTradeBotData) { + String atAddress = tradeBotData.getAtAddress(); + if (atAddress == null) { + LOGGER.info("Missing AT address in tradebot data", atAddress); + continue; + } + + String tradeState = tradeBotData.getState(); + if (tradeState == null) { + LOGGER.info("Missing trade state for AT {}", atAddress); + continue; + } + + if (tradeState.startsWith("ALICE")) { + LOGGER.info("AT {} isn't redeemable because it is a buy order", atAddress); + continue; + } + + ATData atData = repository.getATRepository().fromATAddress(atAddress); + if (atData == null) { + LOGGER.info("Couldn't find AT with address {}", atAddress); + continue; + } + + ACCT acct = SupportedBlockchain.getAcctByCodeHash(atData.getCodeHash()); + if (acct == null) { + continue; + } + + CrossChainTradeData crossChainTradeData = acct.populateTradeData(repository, atData); + if (crossChainTradeData == null) { + LOGGER.info("Couldn't find crosschain trade data for AT {}", atAddress); + continue; + } + + // Attempt to find secret from the buyer's message to AT + byte[] decodedSecret = LitecoinACCTv1.findSecretA(repository, crossChainTradeData); + if (decodedSecret == null) { + LOGGER.info("Unable to find secret-A from redeem message to AT {}", atAddress); + continue; + } + + // Search for the tradePrivateKey in the tradebot data + byte[] decodedPrivateKey = tradeBotData.getTradePrivateKey(); + + // Search for the litecoin receiving address PKH in the tradebot data + byte[] litecoinReceivingAccountInfo = tradeBotData.getReceivingAccountInfo(); + + try { + LOGGER.info("Attempting to redeem P2SH balance associated with AT {}...", atAddress); + boolean redeemed = this.doRedeemHtlc(atAddress, decodedPrivateKey, decodedSecret, litecoinReceivingAccountInfo); + if (redeemed) { + LOGGER.info("Redeemed P2SH balance associated with AT {}", atAddress); + success = true; + } + else { + LOGGER.info("Couldn't redeem P2SH balance associated with AT {}. Already redeemed?", atAddress); + } + } catch (ApiException e) { + LOGGER.info("Couldn't redeem P2SH balance associated with AT {}. Missing data?", atAddress); + } + } + + } catch (DataException e) { + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); + } + + return success; + } + private boolean doRedeemHtlc(String atAddress, byte[] decodedTradePrivateKey, byte[] decodedSecret, byte[] litecoinReceivingAccountInfo) { try (final Repository repository = RepositoryManager.getRepository()) { ATData atData = repository.getATRepository().fromATAddress(atAddress);