diff --git a/.github/workflows/sub_extended_tests.yml b/.github/workflows/sub_extended_tests.yml index 72fb740531..821484e061 100644 --- a/.github/workflows/sub_extended_tests.yml +++ b/.github/workflows/sub_extended_tests.yml @@ -64,30 +64,6 @@ jobs: - name: Run Kafka, Postgres, and Sep24 UI with docker compose run: docker compose -f /home/runner/anchor-platform/service-runner/src/main/resources/docker-compose-test.yaml up -d --build - # `custody` Tests - - name: Start `custody` configuration - env: - TEST_PROFILE_NAME: custody - KT_REFERENCE_SERVER_CONFIG: /home/runner/anchor-platform/service-runner/src/main/resources/config/reference-config.yaml - run: | - cd /home/runner/anchor-platform - ./gradlew startServersWithTestProfile & - echo "PID=$!" >> $GITHUB_ENV - - - name: Wait for the sep server to start and get ready - uses: mydea/action-wait-for-api@v1 - with: - url: "http://localhost:8080/.well-known/stellar.toml" - interval: "1" - - - name: Run `custody` configuration tests - env: - TEST_PROFILE_NAME: custody - run: | - cd /home/runner/anchor-platform - ./gradlew :service-runner:clean :extended-tests:clean :extended-tests:test --tests org.stellar.anchor.platform.suite.CustodyTestSuite - kill -9 $PID - # `rpc` Tests - name: Start `rpc` configuration env: @@ -112,54 +88,6 @@ jobs: ./gradlew :extended-tests:test --tests org.stellar.anchor.platform.suite.RpcTestSuite kill -9 $PID - # `auth-apikey-custody` Tests - - name: Start `auth-apikey-custody` configuration - env: - TEST_PROFILE_NAME: auth-apikey-custody - KT_REFERENCE_SERVER_CONFIG: /home/runner/anchor-platform/service-runner/src/main/resources/config/reference-config.yaml - run: | - cd /home/runner/anchor-platform - ./gradlew startServersWithTestProfile & - echo "PID=$!" >> $GITHUB_ENV - - - name: Wait for the custody server to start and get ready - uses: mydea/action-wait-for-api@v1 - with: - url: "http://localhost:8086/health" - interval: "1" - - - name: Run `auth-apikey-custody` configuration tests - env: - TEST_PROFILE_NAME: auth-apikey-custody - run: | - cd /home/runner/anchor-platform - ./gradlew :extended-tests:test --tests org.stellar.anchor.platform.suite.AuthApikeyCustodyTestSuite - kill -9 $PID - - # `auth-jwt-custody` Tests - - name: Start `auth-jwt-custody` configuration - env: - TEST_PROFILE_NAME: auth-jwt-custody - KT_REFERENCE_SERVER_CONFIG: /home/runner/anchor-platform/service-runner/src/main/resources/config/reference-config.yaml - run: | - cd /home/runner/anchor-platform - ./gradlew startServersWithTestProfile & - echo "PID=$!" >> $GITHUB_ENV - - - name: Wait for the custody server to start and get ready - uses: mydea/action-wait-for-api@v1 - with: - url: "http://localhost:8086/health" - interval: "1" - - - name: Run `auth-jwt-custody` configuration tests - env: - TEST_PROFILE_NAME: auth-jwt-custody - run: | - cd /home/runner/anchor-platform - ./gradlew :extended-tests:test --tests org.stellar.anchor.platform.suite.AuthJwtCustodyTestSuite - kill -9 $PID - # `auth-apikey-platform` Tests - name: Start `auth-apikey-platform` configuration env: diff --git a/.run/Custody Server_ custody.run.xml b/.run/Custody Server_ custody.run.xml deleted file mode 100644 index 72db2c0a7c..0000000000 --- a/.run/Custody Server_ custody.run.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - \ No newline at end of file diff --git a/.run/Test Profile_ auth-apikey-custody.run.xml b/.run/Test Profile_ auth-apikey-custody.run.xml deleted file mode 100644 index 549362e2d6..0000000000 --- a/.run/Test Profile_ auth-apikey-custody.run.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.run/Test Profile_ auth-jwt-custody.run.xml b/.run/Test Profile_ auth-jwt-custody.run.xml deleted file mode 100644 index 1497b837d7..0000000000 --- a/.run/Test Profile_ auth-jwt-custody.run.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.run/Test Profile_ custody.run.xml b/.run/Test Profile_ custody.run.xml deleted file mode 100644 index 2b48aa20a4..0000000000 --- a/.run/Test Profile_ custody.run.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/CreateCustodyTransactionRequest.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/CreateCustodyTransactionRequest.java deleted file mode 100644 index 579de18dd9..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/CreateCustodyTransactionRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.stellar.anchor.api.custody; - -import lombok.Builder; -import lombok.Data; - -@Data -@Builder -public class CreateCustodyTransactionRequest { - - private String id; - private String memo; - private String memoType; - private String protocol; - private String fromAccount; - private String toAccount; - private String amount; - private String amountFee; - private String asset; - private String kind; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/CreateTransactionPaymentResponse.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/CreateTransactionPaymentResponse.java deleted file mode 100644 index bcdfded67c..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/CreateTransactionPaymentResponse.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.stellar.anchor.api.custody; - -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class CreateTransactionPaymentResponse { - private String id; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/CreateTransactionRefundRequest.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/CreateTransactionRefundRequest.java deleted file mode 100644 index 63af493e17..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/CreateTransactionRefundRequest.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.stellar.anchor.api.custody; - -import lombok.Builder; -import lombok.Data; - -@Data -@Builder -public class CreateTransactionRefundRequest { - private String memo; - private String memoType; - private String amount; - private String amountFee; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/CustodyExceptionResponse.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/CustodyExceptionResponse.java deleted file mode 100644 index 41172375cd..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/CustodyExceptionResponse.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.stellar.anchor.api.custody; - -import lombok.Data; - -@Data -public class CustodyExceptionResponse { - String rawErrorMessage; - - public CustodyExceptionResponse(String rawErrorMessage) { - this.rawErrorMessage = rawErrorMessage; - } -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/GenerateDepositAddressResponse.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/GenerateDepositAddressResponse.java deleted file mode 100644 index a24c5f1402..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/GenerateDepositAddressResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.stellar.anchor.api.custody; - -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class GenerateDepositAddressResponse { - - private String address; - private String memo; - private String memoType; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/AmlScreeningResult.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/AmlScreeningResult.java deleted file mode 100644 index 53e2f409c4..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/AmlScreeningResult.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Data; - -@Data -public class AmlScreeningResult { - private String provider; - private String payload; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/AmountInfo.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/AmountInfo.java deleted file mode 100644 index e58cbb0201..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/AmountInfo.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Data; - -@Data -public class AmountInfo { - private String amount; - private String requestedAmount; - private String netAmount; - private String amountUSD; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/AuthorizationGroup.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/AuthorizationGroup.java deleted file mode 100644 index e8325ae065..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/AuthorizationGroup.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import java.util.List; -import lombok.Data; - -@Data -public class AuthorizationGroup { - private Long th; - private List users; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/AuthorizationInfo.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/AuthorizationInfo.java deleted file mode 100644 index bdef5c5afc..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/AuthorizationInfo.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import java.util.List; -import lombok.Data; - -@Data -public class AuthorizationInfo { - private Boolean allowOperatorAsAuthorizer; - private String logic; - private List groups; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/BlockInfo.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/BlockInfo.java deleted file mode 100644 index a3bb254dd5..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/BlockInfo.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Data; - -@Data -public class BlockInfo { - private String blockHeight; - private String blockHash; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/CreateAddressRequest.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/CreateAddressRequest.java deleted file mode 100644 index 606aad36fe..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/CreateAddressRequest.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Data; - -@Data -public class CreateAddressRequest { - - private String description; - private String customerRefId; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/CreateAddressResponse.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/CreateAddressResponse.java deleted file mode 100644 index 4474f15f2f..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/CreateAddressResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.AllArgsConstructor; -import lombok.Data; - -@Data -@AllArgsConstructor -public class CreateAddressResponse { - - private String address; - private String legacyAddress; - private String enterpriseAddress; - private String tag; - private Integer bip44AddressIndex; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/CreateTransactionRequest.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/CreateTransactionRequest.java deleted file mode 100644 index 176cabc294..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/CreateTransactionRequest.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; - -@Data -@Builder -public class CreateTransactionRequest { - private String assetId; - private TransferPeerPath source; - private DestinationTransferPeerPath destination; - private TransactionRequestDestination[] destinations; - private String amount; - private Boolean treatAsGrossAmount; - private String fee; - private String gasPrice; - private String gasLimit; - private String networkFee; - private String priorityFee; - private FeeLevel feeLevel; - private String maxFee; - private Boolean failOnLowFee; - private Boolean forceSweep; - private String note; - private OperationType operation; - private String customerRefId; - private String replaceTxByHash; - private String externalTxId; - private Object extraParameters; - - @Data - @AllArgsConstructor - public static class TransferPeerPath { - private TransferPeerPathType type; - private String id; - } - - @Data - public static class DestinationTransferPeerPath { - - public DestinationTransferPeerPath( - DestinationTransferPeerPathType type, OneTimeAddress oneTimeAddress) { - this.type = type; - this.oneTimeAddress = oneTimeAddress; - } - - private DestinationTransferPeerPathType type; - private String id; - private OneTimeAddress oneTimeAddress; - } - - @Data - @AllArgsConstructor - public static class OneTimeAddress { - private String address; - private String tag; - } - - @Data - @AllArgsConstructor - public static class TransactionRequestDestination { - private String amount; - private DestinationTransferPeerPath destination; - } - - public enum OperationType { - BURN, - CONTRACT_CALL, - MINT, - RAW, - REDEEM_FROM_COMPOUND, - SUPPLY_TO_COMPOUND, - TRANSFER, - TYPED_MESSAGE - } - - public enum TransferPeerPathType { - VAULT_ACCOUNT, - EXCHANGE_ACCOUNT, - FIAT_ACCOUNT, - GAS_STATION - } - - public enum DestinationTransferPeerPathType { - VAULT_ACCOUNT, - EXCHANGE_ACCOUNT, - INTERNAL_WALLET, - EXTERNAL_WALLET, - ONE_TIME_ADDRESS, - NETWORK_CONNECTION, - FIAT_ACCOUNT, - COMPOUND - } - - public enum FeeLevel { - LOW, - MEDIUM, - HIGH - } -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/CreateTransactionResponse.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/CreateTransactionResponse.java deleted file mode 100644 index a4114a68bf..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/CreateTransactionResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import java.util.List; -import lombok.Builder; -import lombok.Data; - -@Data -@Builder -public class CreateTransactionResponse { - private String id; - private TransactionStatus status; - private List systemMessages; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/DestinationsResponse.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/DestinationsResponse.java deleted file mode 100644 index 6d5338668b..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/DestinationsResponse.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Data; - -@Data -public class DestinationsResponse { - private String amount; - private TransferPeerPathResponse destination; - private Float amountUSD; - private String destinationAddress; - private String destinationAddressDescription; - private AmlScreeningResult amlScreeningResult; - private String customerRefId; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/EventType.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/EventType.java deleted file mode 100644 index 54c5e95518..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/EventType.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -public enum EventType { - TRANSACTION_CREATED, - TRANSACTION_STATUS_UPDATED, - TRANSACTION_APPROVAL_STATUS_UPDATED, - VAULT_ACCOUNT_ADDED, - VAULT_ACCOUNT_ASSET_ADDED, - INTERNAL_WALLET_ASSET_ADDED, - EXTERNAL_WALLET_ASSET_ADDED, - EXCHANGE_ACCOUNT_ADDED, - FIAT_ACCOUNT_ADDED, - NETWORK_CONNECTION_ADDED -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/FeeInfo.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/FeeInfo.java deleted file mode 100644 index 651563e19f..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/FeeInfo.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Data; - -@Data -public class FeeInfo { - private String networkFee; - private String serviceFee; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/FireblocksEventObject.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/FireblocksEventObject.java deleted file mode 100644 index 7cf4a4b5e9..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/FireblocksEventObject.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Data; - -@Data -public class FireblocksEventObject { - private EventType type; - private String tenantId; - private Long timestamp; - private TransactionDetails data; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/NetworkRecord.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/NetworkRecord.java deleted file mode 100644 index 5e131f7354..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/NetworkRecord.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Data; - -@Data -public class NetworkRecord { - private TransferPeerPathResponse source; - private TransferPeerPathResponse destination; - private String txHash; - private Float networkFee; - private String assetId; - private Float netAmount; - private NetworkStatus status; - private String type; - private String destinationAddress; - private String sourceAddress; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/NetworkStatus.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/NetworkStatus.java deleted file mode 100644 index 70dd1b9903..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/NetworkStatus.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -public enum NetworkStatus { - DROPPED, - BROADCASTING, - CONFIRMING, - FAILED, - CONFIRMED -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/RewardsInfo.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/RewardsInfo.java deleted file mode 100644 index 942c2b9e5f..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/RewardsInfo.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Data; - -@Data -public class RewardsInfo { - private String srcRewards; - private String destRewards; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/SignedMessage.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/SignedMessage.java deleted file mode 100644 index fc523e5ee6..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/SignedMessage.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Data; - -@Data -public class SignedMessage { - private String content; - private String algorithm; - private Long[] derivationPath; - private Object signature; - private String publicKey; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/SystemMessageInfo.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/SystemMessageInfo.java deleted file mode 100644 index ec2b27f228..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/SystemMessageInfo.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Data; - -@Data -public class SystemMessageInfo { - private SystemMessageInfoType type; - private String message; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/SystemMessageInfoType.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/SystemMessageInfoType.java deleted file mode 100644 index f02c37901f..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/SystemMessageInfoType.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -public enum SystemMessageInfoType { - WARN, - BLOCK -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransactionDetails.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransactionDetails.java deleted file mode 100644 index 5ef2834ff8..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransactionDetails.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Builder; -import lombok.Data; - -@Data -@Builder -public class TransactionDetails { - private String id; - private String assetId; - private TransferPeerPathResponse source; - private TransferPeerPathResponse destination; - private Float requestedAmount; - private AmountInfo amountInfo; - private Float fee; - private FeeInfo feeInfo; - private Float amount; - private Float netAmount; - private Float amountUSD; - private Float serviceFee; - private Boolean treatAsGrossAmount; - private Float networkFee; - private Long createdAt; - private Long lastUpdated; - private TransactionStatus status; - private String txHash; - private Long index; - private TransactionSubStatus subStatus; - private String sourceAddress; - private String destinationAddress; - private String destinationAddressDescription; - private String destinationTag; - private String[] signedBy; - private String createdBy; - private String rejectedBy; - private String addressType; - private String note; - private String exchangeTxId; - private String feeCurrency; - private String operation; - private AmlScreeningResult amlScreeningResult; - private String customerRefId; - private Long numOfConfirmations; - private NetworkRecord[] networkRecords; - private String replacedTxHash; - private String externalTxId; - private DestinationsResponse[] destinations; - private BlockInfo blockInfo; - private RewardsInfo rewardsInfo; - private AuthorizationInfo authorizationInfo; - private SignedMessage[] signedMessages; - private Object extraParameters; - private SystemMessageInfo systemMessages; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransactionStatus.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransactionStatus.java deleted file mode 100644 index bfff3b701e..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransactionStatus.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import java.util.Set; - -public enum TransactionStatus { - SUBMITTED, - QUEUED, - PENDING_AUTHORIZATION, - PENDING_SIGNATURE, - BROADCASTING, - PENDING_3RD_PARTY_MANUAL_APPROVAL, - PENDING_3RD_PARTY, - CONFIRMING, - PARTIALLY_COMPLETED, - PENDING_AML_SCREENING, - COMPLETED, - CANCELLED, - REJECTED, - BLOCKED, - FAILED; - - public boolean isCompleted() { - return Set.of(COMPLETED, CONFIRMING).contains(this); - } - - public boolean isObservable() { - return Set.of(FAILED, CANCELLED, BLOCKED, CONFIRMING, COMPLETED).contains(this); - } - - // CONFIRMING webhook status means the transaction is complete on Stellar. - // That's why events with COMPLETED status are ignored - public boolean isObservableByWebhook() { - return Set.of(FAILED, CANCELLED, BLOCKED, CONFIRMING).contains(this); - } -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransactionSubStatus.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransactionSubStatus.java deleted file mode 100644 index 164ca8ac07..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransactionSubStatus.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import com.google.gson.annotations.SerializedName; - -public enum TransactionSubStatus { - INSUFFICIENT_FUNDS, - AMOUNT_TOO_SMALL, - UNSUPPORTED_ASSET, - UNAUTHORISED__MISSING_PERMISSION, - INVALID_SIGNATURE, - API_INVALID_SIGNATURE, - UNAUTHORISED__MISSING_CREDENTIALS, - UNAUTHORISED__USER, - UNAUTHORISED__DEVICE, - INVALID_UNMANAGED_WALLET, - INVALID_EXCHANGE_ACCOUNT, - INSUFFICIENT_FUNDS_FOR_FEE, - INVALID_ADDRESS, - WITHDRAW_LIMIT, - API_CALL_LIMIT, - ADDRESS_NOT_WHITELISTED, - TIMEOUT, - CONNECTIVITY_ERROR, - THIRD_PARTY_INTERNAL_ERROR, - CANCELLED_EXTERNALLY, - INVALID_THIRD_PARTY_RESPONSE, - VAULT_WALLET_NOT_READY, - MISSING_DEPOSIT_ADDRESS, - ONE_TIME_ADDRESS_DISABLED, - INTERNAL_ERROR, - UNKNOWN_ERROR, - AUTHORIZER_NOT_FOUND, - INSUFFICIENT_RESERVED_FUNDING, - MANUAL_DEPOSIT_ADDRESS_REQUIRED, - INVALID_FEE, - ERROR_UNSUPPORTED_TRANSACTION_TYPE, - UNSUPPORTED_OPERATION, - @SerializedName("3RD_PARTY_PROCESSING") - THIRD_PARTY_PROCESSING, - PENDING_BLOCKCHAIN_CONFIRMATIONS, - @SerializedName("3RD_PARTY_CONFIRMING") - THIRD_PARTY_CONFIRMING, - CONFIRMED, - @SerializedName("3RD_PARTY_COMPLETED") - THIRD_PARTY_COMPLETED, - REJECTED_BY_USER, - CANCELLED_BY_USER, - @SerializedName("3RD_PARTY_CANCELLED") - THIRD_PARTY_CANCELLED, - @SerializedName("3RD_PARTY_REJECTED") - THIRD_PARTY_REJECTED, - REJECTED_AML_SCREENING, - BLOCKED_BY_POLICY, - FAILED_AML_SCREENING, - PARTIALLY_FAILED, - @SerializedName("3RD_PARTY_FAILED") - THIRD_PARTY_FAILED, - DROPPED_BY_BLOCKCHAIN, - REJECTED_BY_BLOCKCHAIN, - INVALID_FEE_PARAMS, - MISSING_TAG_OR_MEMO, - SIGNING_ERROR, - GAS_LIMIT_TOO_LOW, - TOO_MANY_INPUTS, - MAX_FEE_EXCEEDED, - ACTUAL_FEE_TOO_HIGH, - INVALID_CONTRACT_CALL_DATA, - INVALID_NONCE_TOO_LOW, - INVALID_NONCE_TOO_HIGH, - INVALID_NONCE_FOR_RBF, - FAIL_ON_LOW_FEE, - TOO_LONG_MEMPOOL_CHAIN, - TX_OUTDATED, - INCOMPLETE_USER_SETUP, - SIGNER_NOT_FOUND, - INVALID_TAG_OR_MEMO, - ZERO_BALANCE_IN_PERMANENT_ADDRESS, - NEED_MORE_TO_CREATE_DESTINATION, - NON_EXISTING_ACCOUNT_NAME, - ENV_UNSUPPORTED_ASSET -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransferPeerPathResponse.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransferPeerPathResponse.java deleted file mode 100644 index f2e5a0e5d4..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransferPeerPathResponse.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -import lombok.Data; - -@Data -public class TransferPeerPathResponse { - private TransferPeerPathResponseType type; - private String id; - private String name; - private String subType; -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransferPeerPathResponseType.java b/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransferPeerPathResponseType.java deleted file mode 100644 index 7c522a10b6..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/custody/fireblocks/TransferPeerPathResponseType.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.stellar.anchor.api.custody.fireblocks; - -public enum TransferPeerPathResponseType { - VAULT_ACCOUNT, - EXCHANGE_ACCOUNT, - INTERNAL_WALLET, - EXTERNAL_WALLET, - ONE_TIME_ADDRESS, - NETWORK_CONNECTION, - FIAT_ACCOUNT, - COMPOUND, - UNKNOWN -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/exception/CustodyException.java b/api-schema/src/main/java/org/stellar/anchor/api/exception/CustodyException.java deleted file mode 100644 index 28aa3e6a84..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/exception/CustodyException.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.stellar.anchor.api.exception; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import org.apache.hc.core5.http.HttpStatus; - -@EqualsAndHashCode(callSuper = false) -public class CustodyException extends AnchorException { - - @Getter private int statusCode; - @Getter private String rawMessage; - - public CustodyException(String message) { - super(message); - } - - public CustodyException(String rawMessage, int statusCode) { - this( - String.format( - "Custody API returned an error. HTTP status[%d], response[%s]", statusCode, rawMessage), - rawMessage, - statusCode); - } - - public CustodyException(String message, String rawMessage, int statusCode) { - super(message); - this.statusCode = statusCode; - this.rawMessage = rawMessage; - } - - public CustodyException(Exception cause) { - super("Exception occurred during request to Custody API", cause); - this.rawMessage = cause.getMessage(); - this.statusCode = HttpStatus.SC_SERVICE_UNAVAILABLE; - } - - public CustodyException(String message, String rawMessage, Exception cause) { - super(message, cause); - this.rawMessage = rawMessage; - this.statusCode = HttpStatus.SC_SERVICE_UNAVAILABLE; - } -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/exception/FireblocksException.java b/api-schema/src/main/java/org/stellar/anchor/api/exception/FireblocksException.java deleted file mode 100644 index 337b70402b..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/exception/FireblocksException.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.stellar.anchor.api.exception; - -import lombok.EqualsAndHashCode; - -@EqualsAndHashCode(callSuper = false) -public class FireblocksException extends CustodyException { - - public FireblocksException(String rawMessage, int statusCode) { - super( - String.format( - "Fireblocks API returned an error. HTTP status[%d], response[%s]", - statusCode, rawMessage), - rawMessage, - statusCode); - } - - public FireblocksException(Exception cause) { - super("Exception occurred during request to Fireblocks API", cause.getMessage(), cause); - } -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/exception/custody/CustodyBadRequestException.java b/api-schema/src/main/java/org/stellar/anchor/api/exception/custody/CustodyBadRequestException.java deleted file mode 100644 index 308fe8df22..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/exception/custody/CustodyBadRequestException.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.stellar.anchor.api.exception.custody; - -import lombok.EqualsAndHashCode; -import org.stellar.anchor.api.exception.AnchorException; - -@EqualsAndHashCode(callSuper = false) -public class CustodyBadRequestException extends AnchorException { - public CustodyBadRequestException(String message) { - super(message); - } -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/exception/custody/CustodyNotFoundException.java b/api-schema/src/main/java/org/stellar/anchor/api/exception/custody/CustodyNotFoundException.java deleted file mode 100644 index 9f3137f13f..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/exception/custody/CustodyNotFoundException.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.stellar.anchor.api.exception.custody; - -import lombok.EqualsAndHashCode; -import org.stellar.anchor.api.exception.AnchorException; - -@EqualsAndHashCode(callSuper = false) -public class CustodyNotFoundException extends AnchorException { - public CustodyNotFoundException(String message) { - super(message); - } -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/exception/custody/CustodyServiceUnavailableException.java b/api-schema/src/main/java/org/stellar/anchor/api/exception/custody/CustodyServiceUnavailableException.java deleted file mode 100644 index 4990d8dfda..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/exception/custody/CustodyServiceUnavailableException.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.stellar.anchor.api.exception.custody; - -import lombok.EqualsAndHashCode; -import org.stellar.anchor.api.exception.AnchorException; - -@EqualsAndHashCode(callSuper = false) -public class CustodyServiceUnavailableException extends AnchorException { - public CustodyServiceUnavailableException(String message) { - super(message); - } -} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/exception/custody/CustodyTooManyRequestsException.java b/api-schema/src/main/java/org/stellar/anchor/api/exception/custody/CustodyTooManyRequestsException.java deleted file mode 100644 index ebd29a8a62..0000000000 --- a/api-schema/src/main/java/org/stellar/anchor/api/exception/custody/CustodyTooManyRequestsException.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.stellar.anchor.api.exception.custody; - -import lombok.EqualsAndHashCode; -import org.stellar.anchor.api.exception.AnchorException; - -@EqualsAndHashCode(callSuper = false) -public class CustodyTooManyRequestsException extends AnchorException { - public CustodyTooManyRequestsException(String message) { - super(message); - } -} diff --git a/core/src/main/java/org/stellar/anchor/auth/AuthHelper.java b/core/src/main/java/org/stellar/anchor/auth/AuthHelper.java index 8ba951a7f2..7d47c6dc3c 100644 --- a/core/src/main/java/org/stellar/anchor/auth/AuthHelper.java +++ b/core/src/main/java/org/stellar/anchor/auth/AuthHelper.java @@ -4,7 +4,6 @@ import lombok.SneakyThrows; import org.stellar.anchor.api.exception.InvalidConfigException; import org.stellar.anchor.auth.ApiAuthJwt.CallbackAuthJwt; -import org.stellar.anchor.auth.ApiAuthJwt.CustodyAuthJwt; import org.stellar.anchor.auth.ApiAuthJwt.PlatformAuthJwt; import org.stellar.anchor.util.AuthHeader; @@ -113,9 +112,6 @@ private String createJwt() { } else if (jwtClass == PlatformAuthJwt.class) { PlatformAuthJwt token = new PlatformAuthJwt(issuedAt, expirationTime); return jwtService.encode(token); - } else if (jwtClass == CustodyAuthJwt.class) { - CustodyAuthJwt token = new CustodyAuthJwt(issuedAt, expirationTime); - return jwtService.encode(token); } else { throw new InvalidConfigException("Invalid JWT class: " + jwtClass); } diff --git a/core/src/main/java/org/stellar/anchor/auth/JwtService.java b/core/src/main/java/org/stellar/anchor/auth/JwtService.java index 3a65ed76f5..544b5ea35e 100644 --- a/core/src/main/java/org/stellar/anchor/auth/JwtService.java +++ b/core/src/main/java/org/stellar/anchor/auth/JwtService.java @@ -26,7 +26,6 @@ import org.stellar.anchor.auth.ApiAuthJwt.PlatformAuthJwt; import org.stellar.anchor.auth.MoreInfoUrlJwt.Sep24MoreInfoUrlJwt; import org.stellar.anchor.auth.MoreInfoUrlJwt.Sep6MoreInfoUrlJwt; -import org.stellar.anchor.config.CustodySecretConfig; import org.stellar.anchor.config.SecretConfig; import org.stellar.anchor.util.KeyUtil; import org.stellar.anchor.util.Log; @@ -47,10 +46,8 @@ public class JwtService { String sep24MoreInfoUrlJwtSecret; String callbackAuthSecret; String platformAuthSecret; - String custodyAuthSecret; - public JwtService(SecretConfig secretConfig, CustodySecretConfig custodySecretConfig) - throws NotSupportedException { + public JwtService(SecretConfig secretConfig) throws NotSupportedException { this( secretConfig.getSep6MoreInfoUrlJwtSecret(), secretConfig.getSep10JwtSecretKey(), @@ -58,8 +55,7 @@ public JwtService(SecretConfig secretConfig, CustodySecretConfig custodySecretCo secretConfig.getSep24InteractiveUrlJwtSecret(), secretConfig.getSep24MoreInfoUrlJwtSecret(), secretConfig.getCallbackAuthSecret(), - secretConfig.getPlatformAuthSecret(), - custodySecretConfig.getCustodyAuthSecret()); + secretConfig.getPlatformAuthSecret()); } public JwtService( @@ -69,8 +65,7 @@ public JwtService( String sep24InteractiveUrlJwtSecret, String sep24MoreInfoUrlJwtSecret, String callbackAuthSecret, - String platformAuthSecret, - String custodyAuthSecret) { + String platformAuthSecret) { this.sep6MoreInfoUrlJwtSecret = sep6MoreInfoUrlJwtSecret; this.sep10JwtSecret = sep10JwtSecret; this.sep45JwtSecret = sep45JwtSecret; @@ -78,7 +73,6 @@ public JwtService( this.sep24MoreInfoUrlJwtSecret = sep24MoreInfoUrlJwtSecret; this.callbackAuthSecret = callbackAuthSecret; this.platformAuthSecret = platformAuthSecret; - this.custodyAuthSecret = custodyAuthSecret; // Required for Ed25519 keys Security.addProvider(new BouncyCastleProvider()); @@ -163,10 +157,6 @@ public String encode(PlatformAuthJwt token) throws InvalidConfigException { return encode(token, platformAuthSecret); } - public String encode(CustodyAuthJwt token) throws InvalidConfigException { - return encode(token, custodyAuthSecret); - } - private String encode(ApiAuthJwt token, String secret) throws InvalidConfigException { if (secret == null) { throw new InvalidConfigException( @@ -202,8 +192,6 @@ public T decode(String cipher, Class cls) secret = callbackAuthSecret; } else if (cls.equals(PlatformAuthJwt.class)) { secret = platformAuthSecret; - } else if (cls.equals(CustodyAuthJwt.class)) { - secret = custodyAuthSecret; } else { throw new NotSupportedException( String.format("The Jwt class:[%s] is not supported", cls.getName())); diff --git a/core/src/main/java/org/stellar/anchor/config/CustodyConfig.java b/core/src/main/java/org/stellar/anchor/config/CustodyConfig.java deleted file mode 100644 index 5dc93aeb34..0000000000 --- a/core/src/main/java/org/stellar/anchor/config/CustodyConfig.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.stellar.anchor.config; - -import static org.stellar.anchor.config.CustodyConfig.CustodyType.NONE; - -public interface CustodyConfig { - - default boolean isCustodyIntegrationEnabled() { - return NONE != getType(); - } - - CustodyType getType(); - - enum CustodyType { - NONE("none"), - FIREBLOCKS("fireblocks"); - - private final String type; - - CustodyType(String type) { - this.type = type; - } - - public String toString() { - return type; - } - } -} diff --git a/core/src/main/java/org/stellar/anchor/config/CustodySecretConfig.java b/core/src/main/java/org/stellar/anchor/config/CustodySecretConfig.java deleted file mode 100644 index 4c72e1425a..0000000000 --- a/core/src/main/java/org/stellar/anchor/config/CustodySecretConfig.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.stellar.anchor.config; - -public interface CustodySecretConfig { - String getFireblocksApiKey(); - - String getFireblocksSecretKey(); - - String getCustodyAuthSecret(); -} diff --git a/core/src/main/java/org/stellar/anchor/config/Sep24Config.java b/core/src/main/java/org/stellar/anchor/config/Sep24Config.java index b69168616f..b7c69d8c64 100644 --- a/core/src/main/java/org/stellar/anchor/config/Sep24Config.java +++ b/core/src/main/java/org/stellar/anchor/config/Sep24Config.java @@ -25,7 +25,6 @@ class Features { enum DepositInfoGeneratorType { SELF, - CUSTODY, NONE } } diff --git a/core/src/main/java/org/stellar/anchor/config/Sep31Config.java b/core/src/main/java/org/stellar/anchor/config/Sep31Config.java index 5877d0d6e6..8030b5db7d 100644 --- a/core/src/main/java/org/stellar/anchor/config/Sep31Config.java +++ b/core/src/main/java/org/stellar/anchor/config/Sep31Config.java @@ -14,7 +14,6 @@ enum PaymentType { enum DepositInfoGeneratorType { SELF, - CUSTODY, NONE } } diff --git a/core/src/main/java/org/stellar/anchor/config/Sep6Config.java b/core/src/main/java/org/stellar/anchor/config/Sep6Config.java index efea2c5da5..bcd2a5f63d 100644 --- a/core/src/main/java/org/stellar/anchor/config/Sep6Config.java +++ b/core/src/main/java/org/stellar/anchor/config/Sep6Config.java @@ -31,7 +31,6 @@ class Features { enum DepositInfoGeneratorType { SELF, - CUSTODY, NONE } } diff --git a/core/src/main/java/org/stellar/anchor/custody/CustodyService.java b/core/src/main/java/org/stellar/anchor/custody/CustodyService.java deleted file mode 100644 index 2b3e5c1105..0000000000 --- a/core/src/main/java/org/stellar/anchor/custody/CustodyService.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.stellar.anchor.custody; - -import org.stellar.anchor.api.custody.CreateTransactionPaymentResponse; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.api.rpc.method.DoStellarRefundRequest; -import org.stellar.anchor.sep24.Sep24Transaction; -import org.stellar.anchor.sep31.Sep31Transaction; -import org.stellar.anchor.sep6.Sep6Transaction; - -public interface CustodyService { - - /** - * Create custody transaction for SEP6 transaction - * - * @param txn SEP6 transaction - * @throws AnchorException if error happens - */ - void createTransaction(Sep6Transaction txn) throws AnchorException; - - /** - * Create custody transaction for SEP24 transaction - * - * @param txn SEP24 transaction - * @throws AnchorException if error happens - */ - void createTransaction(Sep24Transaction txn) throws AnchorException; - - /** - * Create custody transaction for SEP31 transaction - * - * @param txn SEP31 transaction - * @throws AnchorException if error happens - */ - void createTransaction(Sep31Transaction txn) throws AnchorException; - - /** - * Create custody transaction payment - * - * @param txnId transaction ID - * @param requestBody request body - * @return {@link CreateTransactionPaymentResponse} object - * @throws AnchorException if error happens - */ - CreateTransactionPaymentResponse createTransactionPayment(String txnId, String requestBody) - throws AnchorException; - - /** - * Create custody transaction refund - * - * @param refundRequest {@link DoStellarRefundRequest} object - * @param memo Refund memo - * @param memoType Refund memo type - * @return {@link CreateTransactionPaymentResponse} object - * @throws AnchorException if error happens - */ - CreateTransactionPaymentResponse createTransactionRefund( - DoStellarRefundRequest refundRequest, String memo, String memoType) throws AnchorException; -} diff --git a/core/src/main/java/org/stellar/anchor/filter/CustodyAuthJwtFilter.java b/core/src/main/java/org/stellar/anchor/filter/CustodyAuthJwtFilter.java deleted file mode 100644 index f1506666a5..0000000000 --- a/core/src/main/java/org/stellar/anchor/filter/CustodyAuthJwtFilter.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.stellar.anchor.filter; - -import jakarta.servlet.ServletResponse; -import jakarta.servlet.http.HttpServletRequest; -import lombok.NonNull; -import org.stellar.anchor.api.exception.SepValidationException; -import org.stellar.anchor.auth.ApiAuthJwt.CustodyAuthJwt; -import org.stellar.anchor.auth.JwtService; - -public class CustodyAuthJwtFilter extends AbstractJwtFilter { - - public CustodyAuthJwtFilter(JwtService jwtService, String authorizationHeader) { - super(jwtService, authorizationHeader); - } - - @Override - public void check(String jwtCipher, HttpServletRequest request, ServletResponse servletResponse) - throws Exception { - @NonNull CustodyAuthJwt token = jwtService.decode(jwtCipher, CustodyAuthJwt.class); - if (token == null) { - throw new SepValidationException("JwtToken should not be null"); - } - - request.setAttribute(JWT_TOKEN, token); - } -} diff --git a/core/src/main/java/org/stellar/anchor/sep24/Sep24Service.java b/core/src/main/java/org/stellar/anchor/sep24/Sep24Service.java index 7d8a6230bd..c583a77e1e 100644 --- a/core/src/main/java/org/stellar/anchor/sep24/Sep24Service.java +++ b/core/src/main/java/org/stellar/anchor/sep24/Sep24Service.java @@ -48,7 +48,6 @@ import org.stellar.anchor.client.ClientService; import org.stellar.anchor.client.CustodialClient; import org.stellar.anchor.config.AppConfig; -import org.stellar.anchor.config.CustodyConfig; import org.stellar.anchor.config.Sep24Config; import org.stellar.anchor.event.EventService; import org.stellar.anchor.sep38.Sep38Quote; @@ -73,7 +72,6 @@ public class Sep24Service { final EventService.Session eventSession; final InteractiveUrlConstructor interactiveUrlConstructor; final MoreInfoUrlConstructor moreInfoUrlConstructor; - final CustodyConfig custodyConfig; final ExchangeAmountsCalculator exchangeAmountsCalculator; final Counter sep24TransactionRequestedCounter = counter(MetricConstants.SEP24_TRANSACTION_REQUESTED); @@ -101,7 +99,6 @@ public Sep24Service( EventService eventService, InteractiveUrlConstructor interactiveUrlConstructor, MoreInfoUrlConstructor moreInfoUrlConstructor, - CustodyConfig custodyConfig, ExchangeAmountsCalculator exchangeAmountsCalculator) { debug("appConfig:", appConfig); debug("sep24Config:", sep24Config); @@ -116,7 +113,6 @@ public Sep24Service( this.eventSession = eventService.createSession(this.getClass().getName(), TRANSACTION); this.interactiveUrlConstructor = interactiveUrlConstructor; this.moreInfoUrlConstructor = moreInfoUrlConstructor; - this.custodyConfig = custodyConfig; this.exchangeAmountsCalculator = exchangeAmountsCalculator; info("Sep24Service initialized."); } @@ -216,30 +212,12 @@ public InteractiveTransactionResponse withdraw( if (memo != null) { debug("transaction memo detected.", memo); - - if (!CustodyUtils.isMemoTypeSupported( - custodyConfig.getType(), memoTypeString(memoType(memo)))) { - throw new SepValidationException( - String.format( - "Memo type[%s] is not supported for custody type[%s]", - memoTypeString(memoType(memo)), custodyConfig.getType())); - } - builder.memo(memo.toString()); builder.memoType(memoTypeString(memoType(memo))); } if (refundMemo != null) { debug("refund memo detected.", refundMemo); - - if (!CustodyUtils.isMemoTypeSupported( - custodyConfig.getType(), memoTypeString(memoType(refundMemo)))) { - throw new SepValidationException( - String.format( - "Refund memo type[%s] is not supported for custody type[%s]", - memoTypeString(memoType(refundMemo)), custodyConfig.getType())); - } - builder.refundMemo(refundMemo.toString()); builder.refundMemoType(memoTypeString(memoType(refundMemo))); } @@ -404,15 +382,6 @@ public InteractiveTransactionResponse deposit( if (memo != null) { debug("transaction memo detected.", memo); - - if (!CustodyUtils.isMemoTypeSupported( - custodyConfig.getType(), memoTypeString(memoType(memo)))) { - throw new SepValidationException( - String.format( - "Memo type[%s] is not supported for custody type[%s]", - memoTypeString(memoType(memo)), custodyConfig.getType())); - } - builder.memo(memo.toString()); builder.memoType(memoTypeString(memoType(memo))); } diff --git a/core/src/main/java/org/stellar/anchor/util/CustodyUtils.java b/core/src/main/java/org/stellar/anchor/util/CustodyUtils.java deleted file mode 100644 index ce68bde9f7..0000000000 --- a/core/src/main/java/org/stellar/anchor/util/CustodyUtils.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.stellar.anchor.util; - -import static org.stellar.sdk.xdr.MemoType.MEMO_HASH; - -import org.stellar.anchor.config.CustodyConfig.CustodyType; - -public class CustodyUtils { - - public static boolean isMemoTypeSupported(CustodyType custodyType, String memoType) { - switch (custodyType) { - case FIREBLOCKS: - return !MemoHelper.memoTypeAsString(MEMO_HASH).equals(memoType); - default: - return true; - } - } -} diff --git a/core/src/main/java/org/stellar/anchor/util/TransactionMapper.java b/core/src/main/java/org/stellar/anchor/util/TransactionMapper.java index bfcec321a8..40a3a3680c 100644 --- a/core/src/main/java/org/stellar/anchor/util/TransactionMapper.java +++ b/core/src/main/java/org/stellar/anchor/util/TransactionMapper.java @@ -2,11 +2,8 @@ import static org.stellar.anchor.api.platform.PlatformTransactionData.Kind.*; -import com.google.common.collect.ImmutableSet; import jakarta.annotation.Nullable; -import java.util.Optional; import org.stellar.anchor.api.asset.AssetInfo; -import org.stellar.anchor.api.custody.CreateCustodyTransactionRequest; import org.stellar.anchor.api.platform.GetTransactionResponse; import org.stellar.anchor.api.platform.PlatformTransactionData; import org.stellar.anchor.api.sep.SepTransactionStatus; @@ -20,70 +17,6 @@ import org.stellar.anchor.sep6.Sep6Transaction; public class TransactionMapper { - - public static CreateCustodyTransactionRequest toCustodyTransaction(Sep6Transaction txn) { - PlatformTransactionData.Kind kind = PlatformTransactionData.Kind.from(txn.getKind()); - return CreateCustodyTransactionRequest.builder() - .id(txn.getId()) - .memo(txn.getMemo()) - .memoType(txn.getMemoType()) - .protocol("6") - .fromAccount( - ImmutableSet.of(WITHDRAWAL, WITHDRAWAL_EXCHANGE).contains(kind) - ? txn.getFromAccount() - : null) - .toAccount( - ImmutableSet.of(DEPOSIT, DEPOSIT_EXCHANGE).contains(kind) - ? txn.getToAccount() - : txn.getWithdrawAnchorAccount()) - .amount( - ImmutableSet.of(DEPOSIT, DEPOSIT_EXCHANGE).contains(kind) - ? txn.getAmountOut() - : Optional.ofNullable(txn.getAmountExpected()).orElse(txn.getAmountIn())) - .asset( - ImmutableSet.of(DEPOSIT, DEPOSIT_EXCHANGE).contains(kind) - ? txn.getAmountOutAsset() - : txn.getAmountInAsset()) - .kind(txn.getKind()) - .build(); - } - - public static CreateCustodyTransactionRequest toCustodyTransaction(Sep24Transaction txn) { - return CreateCustodyTransactionRequest.builder() - .id(txn.getId()) - .memo(txn.getMemo()) - .memoType(txn.getMemoType()) - .protocol("24") - .fromAccount(WITHDRAWAL.getKind().equals(txn.getKind()) ? txn.getFromAccount() : null) - .toAccount( - DEPOSIT.getKind().equals(txn.getKind()) - ? txn.getToAccount() - : txn.getWithdrawAnchorAccount()) - .amount( - DEPOSIT.getKind().equals(txn.getKind()) - ? txn.getAmountOut() - : Optional.ofNullable(txn.getAmountExpected()).orElse(txn.getAmountIn())) - .asset( - DEPOSIT.getKind().equals(txn.getKind()) - ? txn.getAmountOutAsset() - : txn.getAmountInAsset()) - .kind(txn.getKind()) - .build(); - } - - public static CreateCustodyTransactionRequest toCustodyTransaction(Sep31Transaction txn) { - return CreateCustodyTransactionRequest.builder() - .id(txn.getId()) - .memo(txn.getStellarMemo()) - .memoType(txn.getStellarMemoType()) - .protocol("31") - .toAccount(txn.getToAccount()) - .amount(txn.getAmountIn()) - .asset(txn.getAmountInAsset()) - .kind(RECEIVE.getKind()) - .build(); - } - public static GetTransactionResponse toGetTransactionResponse(Sep31Transaction txn) { Refunds refunds = null; if (txn.getRefunds() != null) { diff --git a/core/src/test/kotlin/org/stellar/anchor/TestHelper.kt b/core/src/test/kotlin/org/stellar/anchor/TestHelper.kt index 9f2f51bcb3..c9e4a694a3 100644 --- a/core/src/test/kotlin/org/stellar/anchor/TestHelper.kt +++ b/core/src/test/kotlin/org/stellar/anchor/TestHelper.kt @@ -4,7 +4,6 @@ import io.mockk.every import javax.crypto.SecretKey import org.stellar.anchor.auth.Sep10Jwt import org.stellar.anchor.auth.WebAuthJwt -import org.stellar.anchor.config.CustodySecretConfig import org.stellar.anchor.config.SecretConfig import org.stellar.anchor.util.KeyUtil import org.stellar.anchor.util.KeyUtil.toSecretKeySpecOrNull @@ -63,9 +62,3 @@ fun SecretConfig.setupMock(block: (() -> Any)? = null) { block?.invoke() } - -fun CustodySecretConfig.setupMock() { - val cfg = this - every { cfg.custodyAuthSecret } returns - "custody_auth_secret_key_________________________".also { KeyUtil.validateJWTSecret(it) } -} diff --git a/core/src/test/kotlin/org/stellar/anchor/auth/AuthHelperTest.kt b/core/src/test/kotlin/org/stellar/anchor/auth/AuthHelperTest.kt index 15abe7a4aa..83b0fc3fc6 100644 --- a/core/src/test/kotlin/org/stellar/anchor/auth/AuthHelperTest.kt +++ b/core/src/test/kotlin/org/stellar/anchor/auth/AuthHelperTest.kt @@ -61,11 +61,6 @@ class AuthHelperTest { currentTimeMilliseconds / 1000L, (currentTimeMilliseconds + JWT_EXPIRATION_MILLISECONDS) / 1000L, ) - val wantCustodyJwt = - CustodyAuthJwt( - currentTimeMilliseconds / 1000L, - (currentTimeMilliseconds + JWT_EXPIRATION_MILLISECONDS) / 1000L, - ) var jwtService = JwtService.builder() @@ -99,22 +94,6 @@ class AuthHelperTest { val wantCallbackAuthHeader = AuthHeader(headerName, "Bearer ${jwtService.encode(wantCallbackJwt)}") assertEquals(wantCallbackAuthHeader, gotCallbackAuthHeader) - - jwtService = - JwtService.builder() - .custodyAuthSecret("secret__________________________________") - .build() - authHelper = - AuthHelper.forJwtToken( - headerName, - jwtService, - JWT_EXPIRATION_MILLISECONDS, - CustodyAuthJwt::class.java - ) - val gotCustodyAuthHeader = authHelper.createAuthHeader() - val wantCustodyAuthHeader = - AuthHeader(headerName, "Bearer ${jwtService.encode(wantCustodyJwt)}") - assertEquals(wantCustodyAuthHeader, gotCustodyAuthHeader) } API_KEY -> { val authHelper = AuthHelper.forApiKey("X-Api-Key", "secret") diff --git a/core/src/test/kotlin/org/stellar/anchor/auth/JwtServiceTest.kt b/core/src/test/kotlin/org/stellar/anchor/auth/JwtServiceTest.kt index e9d6f47a2d..66cb64d895 100644 --- a/core/src/test/kotlin/org/stellar/anchor/auth/JwtServiceTest.kt +++ b/core/src/test/kotlin/org/stellar/anchor/auth/JwtServiceTest.kt @@ -12,7 +12,6 @@ import org.stellar.anchor.TestConstants.Companion.TEST_CLIENT_NAME import org.stellar.anchor.TestConstants.Companion.TEST_HOME_DOMAIN import org.stellar.anchor.auth.JwtService.* import org.stellar.anchor.auth.MoreInfoUrlJwt.Sep24MoreInfoUrlJwt -import org.stellar.anchor.config.CustodySecretConfig import org.stellar.anchor.config.SecretConfig import org.stellar.anchor.setupMock @@ -28,14 +27,11 @@ internal class JwtServiceTest { } lateinit var secretConfig: SecretConfig - lateinit var custodySecretConfig: CustodySecretConfig @BeforeEach fun setup() { secretConfig = mockk() - custodySecretConfig = mockk() secretConfig.setupMock() - custodySecretConfig.setupMock() } @ValueSource(classes = [Sep10Jwt::class, Sep45Jwt::class]) @@ -43,7 +39,7 @@ internal class JwtServiceTest { fun `test apply WebAuthJwt encoding and decoding and make sure the original values are not changed`( clazz: Class ) { - val jwtService = JwtService(secretConfig, custodySecretConfig) + val jwtService = JwtService(secretConfig) val constructor = clazz.getConstructor( String::class.java, @@ -82,7 +78,7 @@ internal class JwtServiceTest { @Test fun `test apply Sep24MoreInfoUrlJwt encoding and decoding and make sure the original values are not changed`() { - val jwtService = JwtService(secretConfig, custodySecretConfig) + val jwtService = JwtService(secretConfig) val token = Sep24MoreInfoUrlJwt(TEST_ACCOUNT, TEST_ISS, TEST_EXP, TEST_CLIENT_DOMAIN, TEST_CLIENT_NAME) val cipher = jwtService.encode(token) @@ -97,7 +93,7 @@ internal class JwtServiceTest { @Test fun `test apply Sep24InteractiveUrlJwt encoding and decoding and make sure the original values are not changed`() { - val jwtService = JwtService(secretConfig, custodySecretConfig) + val jwtService = JwtService(secretConfig) val token = Sep24InteractiveUrlJwt( TEST_ACCOUNT, @@ -120,7 +116,7 @@ internal class JwtServiceTest { @Test fun `make sure decoding bad cipher test throws an error`() { - val jwtService = JwtService(secretConfig, custodySecretConfig) + val jwtService = JwtService(secretConfig) assertThrows { jwtService.decode("This is a bad cipher", Sep10Jwt::class.java) diff --git a/core/src/test/kotlin/org/stellar/anchor/filter/AbstractJwtFilterTest.kt b/core/src/test/kotlin/org/stellar/anchor/filter/AbstractJwtFilterTest.kt index bdd9c656c0..6677586880 100644 --- a/core/src/test/kotlin/org/stellar/anchor/filter/AbstractJwtFilterTest.kt +++ b/core/src/test/kotlin/org/stellar/anchor/filter/AbstractJwtFilterTest.kt @@ -13,7 +13,6 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource import org.stellar.anchor.auth.JwtService -import org.stellar.anchor.config.CustodySecretConfig import org.stellar.anchor.config.SecretConfig import org.stellar.anchor.setupMock @@ -23,9 +22,8 @@ class AbstractJwtFilterTest { @BeforeEach fun setup() { val secretConfig = mockk(relaxed = true) - val custodySecretConfig = mockk(relaxed = true) secretConfig.setupMock() - this.jwtService = JwtService(secretConfig, custodySecretConfig) + this.jwtService = JwtService(secretConfig) } @ParameterizedTest diff --git a/core/src/test/kotlin/org/stellar/anchor/filter/WebAuthJwtFilterTest.kt b/core/src/test/kotlin/org/stellar/anchor/filter/WebAuthJwtFilterTest.kt index 1a57441872..6b7c69f4a6 100644 --- a/core/src/test/kotlin/org/stellar/anchor/filter/WebAuthJwtFilterTest.kt +++ b/core/src/test/kotlin/org/stellar/anchor/filter/WebAuthJwtFilterTest.kt @@ -20,7 +20,6 @@ import org.stellar.anchor.auth.AbstractJwt import org.stellar.anchor.auth.JwtService import org.stellar.anchor.auth.WebAuthJwt import org.stellar.anchor.config.AppConfig -import org.stellar.anchor.config.CustodySecretConfig import org.stellar.anchor.config.SecretConfig import org.stellar.anchor.filter.WebAuthJwtFilter.APPLICATION_JSON_VALUE import org.stellar.anchor.filter.WebAuthJwtFilter.JWT_TOKEN @@ -34,7 +33,6 @@ internal class WebAuthJwtFilterTest { private lateinit var appConfig: AppConfig private lateinit var secretConfig: SecretConfig - private lateinit var custodySecretConfig: CustodySecretConfig private lateinit var jwtService: JwtService private lateinit var webAuthJwtFilter: WebAuthJwtFilter private lateinit var request: HttpServletRequest @@ -45,9 +43,8 @@ internal class WebAuthJwtFilterTest { fun setup() { this.appConfig = mockk(relaxed = true) this.secretConfig = mockk(relaxed = true) - this.custodySecretConfig = mockk(relaxed = true) secretConfig.setupMock() - this.jwtService = JwtService(secretConfig, custodySecretConfig) + this.jwtService = JwtService(secretConfig) this.webAuthJwtFilter = WebAuthJwtFilter(jwtService) this.request = mockk(relaxed = true) this.response = mockk(relaxed = true) diff --git a/core/src/test/kotlin/org/stellar/anchor/sep10/Sep10ServiceTest.kt b/core/src/test/kotlin/org/stellar/anchor/sep10/Sep10ServiceTest.kt index eb0c144eca..d3fb69d12f 100644 --- a/core/src/test/kotlin/org/stellar/anchor/sep10/Sep10ServiceTest.kt +++ b/core/src/test/kotlin/org/stellar/anchor/sep10/Sep10ServiceTest.kt @@ -42,7 +42,6 @@ import org.stellar.anchor.auth.JwtService import org.stellar.anchor.auth.Sep10Jwt import org.stellar.anchor.client.ClientFinder import org.stellar.anchor.config.AppConfig -import org.stellar.anchor.config.CustodySecretConfig import org.stellar.anchor.config.SecretConfig import org.stellar.anchor.config.Sep10Config import org.stellar.anchor.ledger.LedgerClient @@ -92,7 +91,6 @@ internal class Sep10ServiceTest { @MockK(relaxed = true) lateinit var appConfig: AppConfig @MockK(relaxed = true) lateinit var secretConfig: SecretConfig - @MockK(relaxed = true) lateinit var custodySecretConfig: CustodySecretConfig @MockK(relaxed = true) lateinit var sep10Config: Sep10Config @MockK(relaxed = true) lateinit var ledgerClient: LedgerClient @MockK(relaxed = true) lateinit var clientFinder: ClientFinder @@ -115,7 +113,7 @@ internal class Sep10ServiceTest { secretConfig.setupMock() - this.jwtService = spyk(JwtService(secretConfig, custodySecretConfig)) + this.jwtService = spyk(JwtService(secretConfig)) this.sep10Service = Sep10Service(appConfig, secretConfig, sep10Config, ledgerClient, jwtService, clientFinder) } diff --git a/core/src/test/kotlin/org/stellar/anchor/sep24/Sep24ServiceTest.kt b/core/src/test/kotlin/org/stellar/anchor/sep24/Sep24ServiceTest.kt index 65684b42be..6ec955fe22 100644 --- a/core/src/test/kotlin/org/stellar/anchor/sep24/Sep24ServiceTest.kt +++ b/core/src/test/kotlin/org/stellar/anchor/sep24/Sep24ServiceTest.kt @@ -102,7 +102,6 @@ internal class Sep24ServiceTest { @MockK(relaxed = true) lateinit var appConfig: AppConfig @MockK(relaxed = true) lateinit var secretConfig: SecretConfig - @MockK(relaxed = true) lateinit var custodySecretConfig: CustodySecretConfig @MockK(relaxed = true) lateinit var sep24Config: Sep24Config @@ -116,8 +115,6 @@ internal class Sep24ServiceTest { @MockK(relaxed = true) lateinit var moreInfoUrlConstructor: MoreInfoUrlConstructor - @MockK(relaxed = true) lateinit var custodyConfig: CustodyConfig - @MockK(relaxed = true) lateinit var sep38QuoteStore: Sep38QuoteStore @MockK(relaxed = true) lateinit var clientService: ClientService @@ -141,7 +138,7 @@ internal class Sep24ServiceTest { every { txnStore.newInstance() } returns PojoSep24Transaction() requestValidator = spyk(SepRequestValidator(assetService)) - jwtService = spyk(JwtService(secretConfig, custodySecretConfig)) + jwtService = spyk(JwtService(secretConfig)) testInteractiveUrlJwt = createTestInteractiveJwt(null) val strToken = jwtService.encode(testInteractiveUrlJwt) every { interactiveUrlConstructor.construct(any(), any(), any(), any()) } returns @@ -180,7 +177,6 @@ internal class Sep24ServiceTest { eventService, interactiveUrlConstructor, moreInfoUrlConstructor, - custodyConfig, calculator, ) depositQuote = gson.fromJson(DEPOSIT_QUOTE_JSON, PojoSep38Quote::class.java) diff --git a/core/src/test/kotlin/org/stellar/anchor/sep31/Sep31ServiceTest.kt b/core/src/test/kotlin/org/stellar/anchor/sep31/Sep31ServiceTest.kt index 5cac222e04..370565d252 100644 --- a/core/src/test/kotlin/org/stellar/anchor/sep31/Sep31ServiceTest.kt +++ b/core/src/test/kotlin/org/stellar/anchor/sep31/Sep31ServiceTest.kt @@ -25,7 +25,10 @@ import org.stellar.anchor.api.asset.AssetInfo import org.stellar.anchor.api.asset.AssetInfo.Field import org.stellar.anchor.api.asset.Sep31Info import org.stellar.anchor.api.asset.StellarAssetInfo -import org.stellar.anchor.api.callback.* +import org.stellar.anchor.api.callback.CustomerIntegration +import org.stellar.anchor.api.callback.GetCustomerResponse +import org.stellar.anchor.api.callback.GetRateResponse +import org.stellar.anchor.api.callback.RateIntegration import org.stellar.anchor.api.exception.* import org.stellar.anchor.api.sep.sep12.Sep12Status import org.stellar.anchor.api.sep.sep31.* @@ -40,10 +43,8 @@ import org.stellar.anchor.client.ClientConfig.CallbackUrls import org.stellar.anchor.client.ClientService import org.stellar.anchor.client.CustodialClient import org.stellar.anchor.config.* -import org.stellar.anchor.config.CustodyConfig.CustodyType.NONE import org.stellar.anchor.config.Sep31Config.PaymentType.STRICT_RECEIVE import org.stellar.anchor.config.Sep31Config.PaymentType.STRICT_SEND -import org.stellar.anchor.custody.CustodyService import org.stellar.anchor.event.EventService import org.stellar.anchor.event.EventService.EventQueue.TRANSACTION import org.stellar.anchor.event.EventService.Session @@ -244,7 +245,6 @@ class Sep31ServiceTest { @MockK(relaxed = true) lateinit var appConfig: AppConfig @MockK(relaxed = true) lateinit var secretConfig: SecretConfig - @MockK(relaxed = true) lateinit var custodySecretConfig: CustodySecretConfig @MockK(relaxed = true) lateinit var clientService: ClientService @MockK(relaxed = true) lateinit var sep10Config: Sep10Config @MockK(relaxed = true) lateinit var sep31Config: Sep31Config @@ -252,8 +252,6 @@ class Sep31ServiceTest { @MockK(relaxed = true) lateinit var quoteStore: Sep38QuoteStore @MockK(relaxed = true) lateinit var rateIntegration: RateIntegration @MockK(relaxed = true) lateinit var customerIntegration: CustomerIntegration - @MockK(relaxed = true) lateinit var custodyService: CustodyService - @MockK(relaxed = true) lateinit var custodyConfig: CustodyConfig @MockK(relaxed = true) lateinit var eventService: EventService @MockK(relaxed = true) lateinit var eventSession: Session @@ -273,10 +271,9 @@ class Sep31ServiceTest { every { appConfig.languages } returns listOf("en") every { sep31Config.paymentType } returns STRICT_SEND every { txnStore.newTransaction() } returns PojoSep31Transaction() - every { custodyConfig.type } returns NONE every { eventService.createSession(any(), TRANSACTION) } returns eventSession - jwtService = spyk(JwtService(secretConfig, custodySecretConfig)) + jwtService = spyk(JwtService(secretConfig)) sep31Service = Sep31Service( @@ -624,7 +621,6 @@ class Sep31ServiceTest { val mockCustomer = GetCustomerResponse() mockCustomer.status = Sep12Status.ACCEPTED.name every { customerIntegration.getCustomer(any()) } returns mockCustomer - every { custodyConfig.isCustodyIntegrationEnabled } returns true // mock sep31 deposit info generation val txForDepositInfoGenerator = slot() diff --git a/core/src/test/kotlin/org/stellar/anchor/util/CustodyUtilsTest.kt b/core/src/test/kotlin/org/stellar/anchor/util/CustodyUtilsTest.kt deleted file mode 100644 index 0bad150e34..0000000000 --- a/core/src/test/kotlin/org/stellar/anchor/util/CustodyUtilsTest.kt +++ /dev/null @@ -1,33 +0,0 @@ -package org.stellar.anchor.util - -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource -import org.stellar.anchor.config.CustodyConfig.CustodyType.NONE - -class CustodyUtilsTest { - - @ParameterizedTest - @ValueSource(strings = ["id", "text", "hash", "none", "return"]) - fun test_isMemoTypeSupported_noneCustody_supported(memoType: String) { - assertTrue(CustodyUtils.isMemoTypeSupported(NONE, memoType)) - } - - @ParameterizedTest - @ValueSource(strings = ["test"]) - fun test_isMemoTypeSupported_noneCustody_notSupported(memoType: String) { - assertTrue(CustodyUtils.isMemoTypeSupported(NONE, memoType)) - } - - @ParameterizedTest - @ValueSource(strings = ["id", "text", "none", "return"]) - fun test_isMemoTypeSupported_fireblocksCustody_supported(memoType: String) { - assertTrue(CustodyUtils.isMemoTypeSupported(NONE, memoType)) - } - - @ParameterizedTest - @ValueSource(strings = ["hash", "test"]) - fun test_isMemoTypeSupported_fireblocksCustody_notSupported(memoType: String) { - assertTrue(CustodyUtils.isMemoTypeSupported(NONE, memoType)) - } -} diff --git a/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/e2etest/Sep24End2EndTests.kt b/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/e2etest/Sep24End2EndTests.kt index 9ced2885d3..a9b6c5e7a3 100644 --- a/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/e2etest/Sep24End2EndTests.kt +++ b/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/e2etest/Sep24End2EndTests.kt @@ -77,8 +77,7 @@ open class Sep24End2EndTests : IntegrationTestBase(TestConfig()) { config.env["secret.sep24.interactive_url.jwt_secret"]!!, config.env["secret.sep24.more_info_url.jwt_secret"]!!, config.env["secret.callback_api.auth_secret"]!!, - config.env["secret.platform_api.auth_secret"]!!, - config.env["secret.custody_server.auth_secret"]!!, + config.env["secret.platform_api.auth_secret"]!! ) @ParameterizedTest diff --git a/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/CallbackApiTests.kt b/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/CallbackApiTests.kt index 4fbca2c050..d8e1d1881b 100644 --- a/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/CallbackApiTests.kt +++ b/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/CallbackApiTests.kt @@ -55,8 +55,7 @@ class CallbackApiTests : IntegrationTestBase(TestConfig()) { config.env["secret.sep24.interactive_url.jwt_secret"]!!, config.env["secret.sep24.more_info_url.jwt_secret"]!!, config.env["secret.callback_api.auth_secret"]!!, - config.env["secret.platform_api.auth_secret"]!!, - null, + config.env["secret.platform_api.auth_secret"]!! ) private val authHelper = diff --git a/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/Sep10ServiceIntegrationTests.kt b/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/Sep10ServiceIntegrationTests.kt index 910eaeb547..7ba512d302 100644 --- a/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/Sep10ServiceIntegrationTests.kt +++ b/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/Sep10ServiceIntegrationTests.kt @@ -22,7 +22,6 @@ import org.stellar.anchor.api.sep.sep10.ValidationRequest import org.stellar.anchor.auth.JwtService import org.stellar.anchor.client.ClientFinder import org.stellar.anchor.config.AppConfig -import org.stellar.anchor.config.CustodySecretConfig import org.stellar.anchor.config.SecretConfig import org.stellar.anchor.config.Sep10Config import org.stellar.anchor.ledger.Horizon @@ -71,7 +70,6 @@ class Sep10ServiceIntegrationTests : IntegrationTestBase(TestConfig()) { @MockK(relaxed = true) lateinit var appConfig: AppConfig @MockK(relaxed = true) lateinit var secretConfig: SecretConfig - @MockK(relaxed = true) lateinit var custodySecretConfig: CustodySecretConfig @MockK(relaxed = true) lateinit var sep10Config: Sep10Config @MockK(relaxed = true) lateinit var ledgerClient: LedgerClient @MockK(relaxed = true) lateinit var clientFinder: ClientFinder @@ -90,7 +88,7 @@ class Sep10ServiceIntegrationTests : IntegrationTestBase(TestConfig()) { every { appConfig.stellarNetworkPassphrase } returns TESTNET.networkPassphrase secretConfig.setupMock() - this.jwtService = spyk(JwtService(secretConfig, custodySecretConfig)) + this.jwtService = spyk(JwtService(secretConfig)) this.sep10Service = Sep10Service(appConfig, secretConfig, sep10Config, ledgerClient, jwtService, clientFinder) this.httpClient = `create httpClient`() diff --git a/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/Sep24Tests.kt b/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/Sep24Tests.kt index 9e544272cd..30e7eed933 100644 --- a/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/Sep24Tests.kt +++ b/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/Sep24Tests.kt @@ -41,8 +41,7 @@ class Sep24Tests : IntegrationTestBase(TestConfig()) { config.env["secret.sep24.interactive_url.jwt_secret"]!!, config.env["secret.sep24.more_info_url.jwt_secret"]!!, config.env["secret.callback_api.auth_secret"]!!, - config.env["secret.platform_api.auth_secret"]!!, - null, + config.env["secret.platform_api.auth_secret"]!! ) private val platformApiClient = diff --git a/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/Sep45Tests.kt b/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/Sep45Tests.kt index 8e2b5666e7..a9561d0050 100644 --- a/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/Sep45Tests.kt +++ b/essential-tests/src/testFixtures/kotlin/org/stellar/anchor/platform/integrationtest/Sep45Tests.kt @@ -42,8 +42,7 @@ class Sep45Tests : IntegrationTestBase(TestConfig()) { config.env["secret.sep24.interactive_url.jwt_secret"]!!, config.env["secret.sep24.more_info_url.jwt_secret"]!!, config.env["secret.callback_api.auth_secret"]!!, - config.env["secret.platform_api.auth_secret"]!!, - null, + config.env["secret.platform_api.auth_secret"]!! ) private var webAuthDomain = toml.getString("WEB_AUTH_FOR_CONTRACTS_ENDPOINT") diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/auth/AbstractAuthIntegrationTest.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/auth/AbstractAuthIntegrationTest.kt index 95f74eaa25..8f10baa05c 100644 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/auth/AbstractAuthIntegrationTest.kt +++ b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/auth/AbstractAuthIntegrationTest.kt @@ -21,19 +21,9 @@ abstract class AbstractAuthIntegrationTest { const val GET_TRANSACTIONS_MY_ID_ENDPOINT = "GET,/transactions/my_id" const val GET_EXCHANGE_QUOTES_ENDPOINT = "GET,/exchange/quotes" const val GET_EXCHANGE_QUOTES_ID_ENDPOINT = "GET,/exchange/quotes/id" - const val POST_CUSTODY_TRANSACTION_ENDPOINT = "POST,/transactions" private val jwtService = - JwtService( - null, - null, - null, - null, - null, - PLATFORM_TO_ANCHOR_SECRET, - ANCHOR_TO_PLATFORM_SECRET, - PLATFORM_TO_CUSTODY_SECRET, - ) + JwtService(null, null, null, null, null, PLATFORM_TO_ANCHOR_SECRET, ANCHOR_TO_PLATFORM_SECRET) private val jwtWrongKeyService = JwtService( null, @@ -42,8 +32,7 @@ abstract class AbstractAuthIntegrationTest { null, null, (PLATFORM_TO_ANCHOR_SECRET + "bad"), - (ANCHOR_TO_PLATFORM_SECRET + "bad"), - (PLATFORM_TO_CUSTODY_SECRET + "bad"), + (ANCHOR_TO_PLATFORM_SECRET + "bad") ) internal val platformJwtAuthHelper = diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/auth/apikey/custody/AuthApiKeyCustodyTests.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/auth/apikey/custody/AuthApiKeyCustodyTests.kt deleted file mode 100644 index 370d96aafc..0000000000 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/auth/apikey/custody/AuthApiKeyCustodyTests.kt +++ /dev/null @@ -1,55 +0,0 @@ -package org.stellar.anchor.platform.extendedtest.auth.apikey.custody - -import java.util.concurrent.TimeUnit -import kotlin.test.assertEquals -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.RequestBody -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.CsvSource -import org.stellar.anchor.platform.extendedtest.auth.AbstractAuthIntegrationTest -import org.stellar.anchor.util.GsonUtils -import org.stellar.anchor.util.OkHttpUtil - -// use TEST_PROFILE_NAME = "auth-apikey-custody" -internal class AuthApiKeyCustodyTests : AbstractAuthIntegrationTest() { - - private val gson = GsonUtils.getInstance() - private val httpClient: OkHttpClient = - OkHttpClient.Builder() - .connectTimeout(10, TimeUnit.MINUTES) - .readTimeout(10, TimeUnit.MINUTES) - .writeTimeout(10, TimeUnit.MINUTES) - .build() - - @ParameterizedTest - @CsvSource(value = [POST_CUSTODY_TRANSACTION_ENDPOINT]) - fun test_incomingCustodyAuth_emptyApiKey_authFails(method: String, endpoint: String) { - val httpRequest = - Request.Builder() - .url("http://localhost:$CUSTODY_SERVER_SERVER_PORT$endpoint") - .header("Content-Type", "application/json") - .method(method, getCustodyDummyRequestBody()) - .build() - val response = httpClient.newCall(httpRequest).execute() - assertEquals(403, response.code) - } - - @ParameterizedTest - @CsvSource(value = [POST_CUSTODY_TRANSACTION_ENDPOINT]) - fun test_incomingCustodyAuth_emptyApiKey_authPasses(method: String, endpoint: String) { - val httpRequest = - Request.Builder() - .url("http://localhost:$CUSTODY_SERVER_SERVER_PORT$endpoint") - .header("Content-Type", "application/json") - .header("X-Api-Key", PLATFORM_TO_CUSTODY_SECRET) - .method(method, getCustodyDummyRequestBody()) - .build() - val response = httpClient.newCall(httpRequest).execute() - assertEquals(200, response.code) - } - - private fun getCustodyDummyRequestBody(): RequestBody? { - return OkHttpUtil.buildJsonRequestBody(gson.toJson(mapOf("id" to "testId"))) - } -} diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/auth/jwt/custody/AuthJwtCustodyTests.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/auth/jwt/custody/AuthJwtCustodyTests.kt deleted file mode 100644 index 3b219cca4b..0000000000 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/auth/jwt/custody/AuthJwtCustodyTests.kt +++ /dev/null @@ -1,76 +0,0 @@ -package org.stellar.anchor.platform.extendedtest.auth.jwt.custody - -import java.util.concurrent.TimeUnit -import kotlin.test.assertEquals -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.RequestBody -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.CsvSource -import org.stellar.anchor.api.custody.CreateCustodyTransactionRequest -import org.stellar.anchor.api.exception.CustodyException -import org.stellar.anchor.platform.apiclient.CustodyApiClient -import org.stellar.anchor.platform.config.CustodyApiConfig -import org.stellar.anchor.platform.config.PropertyCustodySecretConfig -import org.stellar.anchor.platform.extendedtest.auth.AbstractAuthIntegrationTest -import org.stellar.anchor.platform.gson -import org.stellar.anchor.util.OkHttpUtil - -internal class AuthJwtCustodyTests : AbstractAuthIntegrationTest() { - private val httpClient: OkHttpClient = - OkHttpClient.Builder() - .connectTimeout(10, TimeUnit.MINUTES) - .readTimeout(10, TimeUnit.MINUTES) - .writeTimeout(10, TimeUnit.MINUTES) - .build() - - private val custodyApiConfig: CustodyApiConfig = - CustodyApiConfig(PropertyCustodySecretConfig()).apply { baseUrl = "http://localhost:8086" } - - private val jwtCustodyClient: CustodyApiClient = - CustodyApiClient(httpClient, custodyJwtAuthHelper, custodyApiConfig) - private val jwtWrongKeyCustodyClient: CustodyApiClient = - CustodyApiClient(httpClient, custodyJwtWrongKeyAuthHelper, custodyApiConfig) - private val jwtExpiredTokenCustodyClient: CustodyApiClient = - CustodyApiClient(httpClient, custodyJwtExpiredAuthHelper, custodyApiConfig) - - @Test - fun `test the custody endpoints with JWT auth`() { - // Assert the request does not throw a 403. - // As for the correctness of the request/response, it should be tested in the custody server - // integration tests. - jwtCustodyClient.createTransaction(getCustodyDummyRequest()) - } - - @ParameterizedTest - @CsvSource(value = [POST_CUSTODY_TRANSACTION_ENDPOINT]) - fun `test JWT protection of the custody server`(method: String, endpoint: String) { - // Check if the request without JWT will cause a 403. - val httpRequest = - Request.Builder() - .url("http://localhost:${CUSTODY_SERVER_SERVER_PORT}${endpoint}") - .header("Content-Type", "application/json") - .method(method, getCustodyDummyRequestBody()) - .build() - val response = httpClient.newCall(httpRequest).execute() - assertEquals(403, response.code) - - // Check if the wrong JWT key will cause a 403. - assertThrows { - jwtWrongKeyCustodyClient.createTransaction(getCustodyDummyRequest()) - } - assertThrows { - jwtExpiredTokenCustodyClient.createTransaction(getCustodyDummyRequest()) - } - } - - private fun getCustodyDummyRequest(): CreateCustodyTransactionRequest { - return CreateCustodyTransactionRequest.builder().id("testId").build() - } - - private fun getCustodyDummyRequestBody(): RequestBody? { - return OkHttpUtil.buildJsonRequestBody(gson.toJson(mapOf("id" to "testId"))) - } -} diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/custody/CustodyApiTests.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/custody/CustodyApiTests.kt deleted file mode 100644 index ca16cb16fb..0000000000 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/custody/CustodyApiTests.kt +++ /dev/null @@ -1,718 +0,0 @@ -package org.stellar.anchor.platform.extendedtest.custody - -import java.util.* -import okhttp3.mockwebserver.Dispatcher -import okhttp3.mockwebserver.MockResponse -import okhttp3.mockwebserver.MockWebServer -import okhttp3.mockwebserver.RecordedRequest -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.assertTrue -import org.skyscreamer.jsonassert.Customization -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.skyscreamer.jsonassert.comparator.CustomComparator -import org.stellar.anchor.api.custody.CreateCustodyTransactionRequest -import org.stellar.anchor.api.exception.SepException -import org.stellar.anchor.api.exception.SepNotFoundException -import org.stellar.anchor.api.rpc.method.* -import org.stellar.anchor.api.sep.SepTransactionStatus -import org.stellar.anchor.apiclient.PlatformApiClient -import org.stellar.anchor.auth.AuthHelper -import org.stellar.anchor.client.CustodyApiClient -import org.stellar.anchor.client.Sep24Client -import org.stellar.anchor.platform.TestConfig -import org.stellar.anchor.platform.gson -import org.stellar.anchor.platform.integrationtest.PlatformAPITestBase -import org.stellar.anchor.util.RSAUtil - -class CustodyApiTests : PlatformAPITestBase(TestConfig("custody")) { - companion object { - const val CUSTODY_TX_ID_KEY = "%CUSTODY_TX_ID%" - private val custodyTxnId = UUID.randomUUID().toString() - private val refundCustodyTxnId = UUID.randomUUID().toString() - private val custodyMockServer = MockWebServer() - - @BeforeAll - @JvmStatic - fun construct() { - custodyMockServer.dispatcher = - object : Dispatcher() { - override fun dispatch(request: RecordedRequest): MockResponse { - if ( - "POST" == request.method && - "//v1/vault/accounts/1/XLM_USDC_T_CEKS/addresses" == request.path - ) { - return MockResponse().setResponseCode(200).setBody(CUSTODY_DEPOSIT_ADDRESS_RESPONSE) - } - if ("POST" == request.method && "//v1/transactions" == request.path) { - return MockResponse() - .setResponseCode(200) - .setBody( - CUSTODY_TRANSACTION_PAYMENT_RESPONSE.replace(CUSTODY_TX_ID_KEY, custodyTxnId) - ) - } - return MockResponse().setResponseCode(404) - } - } - custodyMockServer.start(58086) - } - - @AfterAll - @JvmStatic - fun destroy() { - custodyMockServer.shutdown() - } - } - - private val custodyApiClient = - CustodyApiClient( - config.env["custody.server.url"]!!, - token.token, - RSAUtil.generatePrivateKey(config.env["secret.custody.fireblocks.secret_key"]), - ) - private val sep24Client = Sep24Client(toml.getString("TRANSFER_SERVER_SEP0024"), token.token) - private val platformApiClient = - PlatformApiClient(AuthHelper.forNone(), config.env["platform.server.url"]!!) - - @Test - fun `test generate deposit address`() { - val ex: SepException = assertThrows { custodyApiClient.generateDepositAddress("invalidAsset") } - Assertions.assertEquals( - "{\"error\":\"Unable to find Fireblocks asset code by Stellar asset code [invalidAsset]\"}", - ex.message, - ) - - val depositAddressResponse = - custodyApiClient.generateDepositAddress( - "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - ) - JSONAssert.assertEquals( - EXPECTED_DEPOSIT_ADDRESS, - gson.toJson(depositAddressResponse), - JSONCompareMode.STRICT, - ) - - for (count in 1..custodyMockServer.requestCount) { - val recordedRequest = custodyMockServer.takeRequest() - if ( - recordedRequest.method.equals("POST") && - recordedRequest.path.toString() == "//v1/vault/accounts/1/XLM_USDC_T_CEKS/addresses" - ) { - Assertions.assertEquals( - "//v1/vault/accounts/1/XLM_USDC_T_CEKS/addresses", - recordedRequest.path.toString(), - ) - JSONAssert.assertEquals( - CUSTODY_DEPOSIT_ADDRESS_REQUEST, - recordedRequest.body.readUtf8(), - JSONCompareMode.STRICT, - ) - } - } - } - - @Test - fun `test custody transaction payment`() { - val depositRequest = gson.fromJson(DEPOSIT_REQUEST, HashMap::class.java) - - @Suppress("UNCHECKED_CAST") - val depositResponse = sep24Client.deposit(depositRequest as HashMap) - val txId = depositResponse.id - - custodyApiClient.createTransaction( - gson.fromJson( - inject(CUSTODY_TRANSACTION_REQUEST, TX_ID_KEY to txId), - CreateCustodyTransactionRequest::class.java, - ) - ) - - val requestOffchainFundsParams = - gson.fromJson(REQUEST_OFFCHAIN_FUNDS_REQUEST, RequestOffchainFundsRequest::class.java) - requestOffchainFundsParams.transactionId = txId - platformApiClient.sendRpcNotification( - RpcMethod.REQUEST_OFFCHAIN_FUNDS, - requestOffchainFundsParams, - ) - - var txResponse = platformApiClient.getTransaction(txId) - Assertions.assertEquals(SepTransactionStatus.PENDING_USR_TRANSFER_START, txResponse.status) - - val notifyOffchainFundsReceivedParams = - gson.fromJson( - NOTIFY_OFFCHAIN_FUNDS_RECEIVED_REQUEST, - NotifyOffchainFundsReceivedRequest::class.java, - ) - notifyOffchainFundsReceivedParams.transactionId = txId - platformApiClient.sendRpcNotification( - RpcMethod.NOTIFY_OFFCHAIN_FUNDS_RECEIVED, - notifyOffchainFundsReceivedParams, - ) - - txResponse = platformApiClient.getTransaction(txId) - Assertions.assertEquals(SepTransactionStatus.PENDING_ANCHOR, txResponse.status) - - val ex: SepException = assertThrows { custodyApiClient.createTransactionPayment("invalidId") } - Assertions.assertInstanceOf(SepNotFoundException::class.java, ex) - - custodyApiClient.createTransactionPayment(txId) - - var found = false - for (count in 1..custodyMockServer.requestCount) { - val recordedRequest = custodyMockServer.takeRequest() - if ( - recordedRequest.method.equals("POST") && - recordedRequest.path.toString() == "//v1/transactions" - ) { - JSONAssert.assertEquals( - CUSTODY_TRANSACTION_PAYMENT_REQUEST, - recordedRequest.body.readUtf8(), - JSONCompareMode.STRICT, - ) - - val webhookRequest = inject(WEBHOOK_REQUEST, CUSTODY_TX_ID_KEY to custodyTxnId) - custodyApiClient.sendWebhook(webhookRequest) - - txResponse = platformApiClient.getTransaction(txId) - txResponse.startedAt = null - txResponse.updatedAt = null - - JSONAssert.assertEquals( - inject(EXPECTED_TRANSACTION_RESPONSE, TX_ID_KEY to txId), - gson.toJson(txResponse), - CustomComparator( - JSONCompareMode.LENIENT, - Customization("completed_at") { _, _ -> true }, - Customization("stellar_transactions[0].created_at") { _, _ -> true }, - ), - ) - found = true - } - } - assertTrue(found) - } - - @Test - fun `test custody transaction refund`() { - val withdrawRequest = gson.fromJson(SEP_24_WITHDRAW_FLOW_REQUEST, HashMap::class.java) - - @Suppress("UNCHECKED_CAST") - val withdrawResponse = sep24Client.withdraw(withdrawRequest as HashMap) - val txId = withdrawResponse.id - - val mockServerDispatcher: Dispatcher = - object : Dispatcher() { - override fun dispatch(request: RecordedRequest): MockResponse { - if ( - "POST" == request.method && - "//v1/vault/accounts/1/XLM_USDC_T_CEKS/addresses" == request.path - ) { - return MockResponse().setResponseCode(200).setBody(CUSTODY_DEPOSIT_ADDRESS_RESPONSE) - } - if ("POST" == request.method && "//v1/transactions" == request.path) { - return MockResponse() - .setResponseCode(200) - .setBody( - inject(CUSTODY_TRANSACTION_REFUND_RESPONSE, CUSTODY_TX_ID_KEY to refundCustodyTxnId) - ) - } - return MockResponse().setResponseCode(404) - } - } - - custodyMockServer.dispatcher = mockServerDispatcher - - val requestOnchainFundsParams = - gson.fromJson( - inject(REQUEST_ONCHAIN_FUNDS_REQUEST, TX_ID_KEY to txId), - RequestOnchainFundsRequest::class.java, - ) - platformApiClient.sendRpcNotification( - RpcMethod.REQUEST_ONCHAIN_FUNDS, - requestOnchainFundsParams, - ) - - var txResponse = platformApiClient.getTransaction(txId) - Assertions.assertEquals(SepTransactionStatus.PENDING_USR_TRANSFER_START, txResponse.status) - - val notifyOnchainFundsReceivedRequest = - gson.fromJson( - inject(NOTIFY_ONCHAIN_FUNDS_RECEIVED_REQUEST, TX_ID_KEY to txId), - NotifyOnchainFundsReceivedRequest::class.java, - ) - platformApiClient.sendRpcNotification( - RpcMethod.NOTIFY_ONCHAIN_FUNDS_RECEIVED, - notifyOnchainFundsReceivedRequest, - ) - - txResponse = platformApiClient.getTransaction(txId) - Assertions.assertEquals(SepTransactionStatus.PENDING_ANCHOR, txResponse.status) - - val doStellarRefundParams = - gson.fromJson( - inject(DO_STELLAR_REFUND_REQUEST, TX_ID_KEY to txId), - DoStellarRefundRequest::class.java, - ) - platformApiClient.sendRpcNotification(RpcMethod.DO_STELLAR_REFUND, doStellarRefundParams) - txResponse = platformApiClient.getTransaction(txId) - Assertions.assertEquals(SepTransactionStatus.PENDING_STELLAR, txResponse.status) - - custodyApiClient.sendWebhook( - inject(REFUND_WEBHOOK_REQUEST, CUSTODY_TX_ID_KEY to refundCustodyTxnId) - ) - - txResponse = platformApiClient.getTransaction(txId) - txResponse.startedAt = null - txResponse.updatedAt = null - - JSONAssert.assertEquals( - inject(EXPECTED_TXN_REFUND_RESPONSE, TX_ID_KEY to txId), - gson.toJson(txResponse), - CustomComparator( - JSONCompareMode.LENIENT, - Customization("completed_at") { _, _ -> true }, - Customization("stellar_transactions[0].created_at") { _, _ -> true }, - ), - ) - } -} - -private const val DEPOSIT_REQUEST = - """{ - "asset_code": "USDC", - "asset_issuer": "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", - "lang": "en" -}""" - -private const val SEP_24_WITHDRAW_FLOW_REQUEST = - """{ - "amount": "10", - "asset_code": "USDC", - "asset_issuer": "GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP", - "account": "GAIUIZPHLIHQEMNJGSZKCEUWHAZVGUZDBDMO2JXNAJZZZVNSVHQCEWJ4", - "lang": "en", - "refund_memo": "12345", - "refund_memo_type": "id" -}""" - -private const val EXPECTED_DEPOSIT_ADDRESS = - """ - { - "memoType":"id", - "memo": "testTag", - "address": "testAddress" - } -""" - -private const val CUSTODY_DEPOSIT_ADDRESS_REQUEST = """ - { - } -""" - -private const val CUSTODY_DEPOSIT_ADDRESS_RESPONSE = - """ - { - "address":"testAddress", - "legacyAddress": "testLegacyAddress", - "enterpriseAddress": "testEnterpriseAddress", - "tag":"testTag", - "bip44AddressIndex":12345 - } -""" - -private const val CUSTODY_TRANSACTION_REQUEST = - """ - { - "id" : "%TX_ID%", - "memo": "testMemo", - "memoType": "testMemoType", - "protocol": "24", - "fromAccount": "testFromAccount", - "toAccount": "testToAccount", - "amount": "0.45", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", - "kind": "deposit", - "requestAssetCode": "testRequestAssetCode", - "requestAssetIssuer": "testRequestAssetIssuer" - } -""" - -private const val CUSTODY_TRANSACTION_PAYMENT_REQUEST = - """ - { - "assetId": "XLM_USDC_T_CEKS", - "source": { - "type": "VAULT_ACCOUNT", - "id": "1" - }, - "destination": { - "type": "ONE_TIME_ADDRESS", - "oneTimeAddress": { - "address": "testToAccount", - "tag": "testMemo" - } - }, - "amount": "0.45" - } -""" - -private const val CUSTODY_TRANSACTION_PAYMENT_RESPONSE = - """ - { - "id":"%CUSTODY_TX_ID%", - "status": "SUBMITTED" - } -""" - -private const val CUSTODY_TRANSACTION_REFUND_RESPONSE = - """ - { - "id":"%CUSTODY_TX_ID%", - "status": "SUBMITTED" - } -""" - -private const val WEBHOOK_REQUEST = - """ - { - "type": "TRANSACTION_STATUS_UPDATED", - "tenantId": "6ae8e895-7bdb-5021-b865-c65885c61068", - "timestamp": 1687423621679, - "data": { - "id": "%CUSTODY_TX_ID%", - "createdAt": 1687423599336, - "lastUpdated": 1687423620808, - "assetId": "XLM_USDC_T_CEKS", - "source": { - "id": "4", - "type": "VAULT_ACCOUNT", - "name": "NewVault", - "subType": "" - }, - "destination": { - "id": null, - "type": "ONE_TIME_ADDRESS", - "name": "N/A", - "subType": "" - }, - "amount": 1, - "networkFee": 0.00001, - "netAmount": 1, - "sourceAddress": "%TESTPAYMENT_SRC_ACCOUNT%", - "destinationAddress": "%TESTPAYMENT_DEST_ACCOUNT%", - "destinationAddressDescription": "", - "destinationTag": "", - "status": "CONFIRMING", - "txHash": "%TESTPAYMENT_TXN_HASH%", - "subStatus": "CONFIRMED", - "signedBy": [], - "createdBy": "1444ed36-5bc0-4e3b-9b17-5df29fc0590f", - "rejectedBy": "", - "amountUSD": 1, - "addressType": "", - "note": "", - "exchangeTxId": "", - "requestedAmount": 1, - "feeCurrency": "XLM_TEST", - "operation": "TRANSFER", - "customerRefId": null, - "numOfConfirmations": 1, - "amountInfo": { - "amount": "1", - "requestedAmount": "1", - "netAmount": "1", - "amountUSD": "1" - }, - "feeInfo": { - "networkFee": "0.00001" - }, - "destinations": [], - "externalTxId": null, - "blockInfo": { - "blockHeight": "131155", - "blockHash": "46185b809c09e29da09bdc667c947b23fb2716ed8735227c0e77c19fdb620fd4" - }, - "signedMessages": [], - "assetType": "XLM_ASSET" - } -} -""" - -private const val REFUND_WEBHOOK_REQUEST = - """ - { - "type": "TRANSACTION_STATUS_UPDATED", - "tenantId": "6ae8e895-7bdb-5021-b865-c65885c61068", - "timestamp": 1687423621679, - "data": { - "id": "%CUSTODY_TX_ID%", - "createdAt": 1687423599336, - "lastUpdated": 1687423620808, - "assetId": "XLM_USDC_T_CEKS", - "source": { - "id": "4", - "type": "VAULT_ACCOUNT", - "name": "NewVault", - "subType": "" - }, - "destination": { - "id": null, - "type": "ONE_TIME_ADDRESS", - "name": "N/A", - "subType": "" - }, - "amount": 1, - "networkFee": 0.00001, - "netAmount": 1, - "sourceAddress": "%TESTPAYMENT_DEST_ACCOUNT%", - "destinationAddress": "GAIUIZPHLIHQEMNJGSZKCEUWHAZVGUZDBDMO2JXNAJZZZVNSVHQCEWJ4", - "destinationAddressDescription": "", - "destinationTag": "12345", - "status": "CONFIRMING", - "txHash": "%TESTPAYMENT_TXN_HASH%", - "subStatus": "CONFIRMED", - "signedBy": [], - "createdBy": "1444ed36-5bc0-4e3b-9b17-5df29fc0590f", - "rejectedBy": "", - "amountUSD": 1, - "addressType": "", - "note": "", - "exchangeTxId": "", - "requestedAmount": 1, - "feeCurrency": "XLM_TEST", - "operation": "TRANSFER", - "customerRefId": null, - "numOfConfirmations": 1, - "amountInfo": { - "amount": "1", - "requestedAmount": "1", - "netAmount": "1", - "amountUSD": "1" - }, - "feeInfo": { - "networkFee": "0.00001" - }, - "destinations": [], - "externalTxId": null, - "blockInfo": { - "blockHeight": "131155", - "blockHash": "46185b809c09e29da09bdc667c947b23fb2716ed8735227c0e77c19fdb620fd4" - }, - "signedMessages": [], - "assetType": "XLM_ASSET" - } -} -""" - -private const val EXPECTED_TRANSACTION_RESPONSE = - """ - { - "id": "%TX_ID%", - "sep": "24", - "kind": "deposit", - "status": "completed", - "amount_in": { - "amount": "1", - "asset": "iso4217:USD" - }, - "message": "Outgoing payment sent", - "amount_out": { - "amount": "0.9", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "fee_details": { - "total": "0.1", - "asset": "iso4217:USD" - }, - "amount_expected": { - "amount": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "completed_at": "2023-06-22T08:46:39.336Z", - "external_transaction_id": "1", - "stellar_transactions": [ - { - "id": "%TESTPAYMENT_TXN_HASH%", - "payments": [ - { - "amount": { - "amount": "%TESTPAYMENT_AMOUNT%", - "asset": "%TESTPAYMENT_ASSET_CIRCLE_USDC%" - }, - "payment_type": "payment", - "source_account": "%TESTPAYMENT_SRC_ACCOUNT%", - "destination_account": "%TESTPAYMENT_DEST_ACCOUNT%" - } - ] - } - ], - "destination_account": "GDJLBYYKMCXNVVNABOE66NYXQGIA5AC5D223Z2KF6ZEYK4UBCA7FKLTG", - "client_name": "referenceCustodial" -} -""" - -private const val EXPECTED_TXN_REFUND_RESPONSE = - """ - { - "id": "%TX_ID%", - "sep": "24", - "kind": "withdrawal", - "status": "refunded", - "amount_expected": { - "amount": "0.0002", - "asset": "stellar:USDC:GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP" - }, - "amount_in": { - "amount": "0.0002", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": { - "amount": "0.0001", - "asset": "iso4217:USD" - }, - "fee_details": { - "total": "0.0001", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "completed_at": "2023-08-08T14:07:28.799297Z", - "message": "test message", - "refunds": { - "amount_refunded": { - "amount": "0.0002", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_fee": { - "amount": "0", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "payments": [ - { - "id": "%TESTPAYMENT_TXN_HASH%", - "id_type": "stellar", - "amount": { - "amount": "%TESTPAYMENT_AMOUNT%", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "fee": { - "amount": "0", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - } - } - ] - }, - "stellar_transactions": [ - { - "id": "%TESTPAYMENT_TXN_HASH%", - "memo": "testTag", - "memo_type": "id", - "payments": [ - { - "amount": { - "amount": "%TESTPAYMENT_AMOUNT%", - "asset": "%TESTPAYMENT_ASSET_CIRCLE_USDC%" - }, - "payment_type": "payment", - "source_account": "%TESTPAYMENT_SRC_ACCOUNT%", - "destination_account": "%TESTPAYMENT_DEST_ACCOUNT%" - } - ] - } - ], - "source_account": "GAIUIZPHLIHQEMNJGSZKCEUWHAZVGUZDBDMO2JXNAJZZZVNSVHQCEWJ4", - "destination_account": "testAddress", - "memo": "testTag", - "memo_type": "id", - "refund_memo": "12345", - "refund_memo_type": "id", - "client_name": "referenceCustodial" -} -""" - -private const val REQUEST_OFFCHAIN_FUNDS_REQUEST = - """{ - "transaction_id": "testTxId", - "message": "test message", - "amount_in": { - "amount": "1", - "asset": "iso4217:USD" - }, - "amount_out": { - "amount": "0.9", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "fee_details": { - "total": "0.1", - "asset": "iso4217:USD" - }, - "amount_expected": { - "amount": "1" - } - }""" - -private const val NOTIFY_OFFCHAIN_FUNDS_RECEIVED_REQUEST = - """{ - "transaction_id": "testTxId", - "message": "test message", - "amount_in": { - "amount": "1", - "asset": "iso4217:USD" - }, - "amount_out": { - "amount": "0.9", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "fee_details": { - "total": "0.1", - "asset": "iso4217:USD" - }, - "external_transaction_id": "1" - }""" - -private const val REQUEST_ONCHAIN_FUNDS_REQUEST = - """ - { - "transaction_id": "%TX_ID%", - "message": "test message 1", - "amount_in": { - "amount": "0.0002", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": { - "amount": "0.0001", - "asset": "iso4217:USD" - }, - "fee_details": { - "total": "0.0001", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_expected": { - "amount": "0.0002" - } - } -""" - -private const val NOTIFY_ONCHAIN_FUNDS_RECEIVED_REQUEST = - """ - { - "transaction_id": "%TX_ID%", - "message": "test message 1", - "stellar_transaction_id": "%TESTPAYMENT_TXN_HASH%" - } -""" - -private const val DO_STELLAR_REFUND_REQUEST = - """ - { - "transaction_id": "%TX_ID%", - "message": "test message", - "refund": { - "amount": { - "amount": 0.0001, - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_fee": { - "amount": 0, - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - } - } - } -""" diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/custody/PlatformApiCustodyTests.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/custody/PlatformApiCustodyTests.kt deleted file mode 100644 index 17147eadd1..0000000000 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/extendedtest/custody/PlatformApiCustodyTests.kt +++ /dev/null @@ -1,1162 +0,0 @@ -package org.stellar.anchor.platform.extendedtest.custody - -import com.google.gson.reflect.TypeToken -import okhttp3.mockwebserver.Dispatcher -import okhttp3.mockwebserver.MockResponse -import okhttp3.mockwebserver.MockWebServer -import okhttp3.mockwebserver.RecordedRequest -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import org.skyscreamer.jsonassert.Customization -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.skyscreamer.jsonassert.comparator.CustomComparator -import org.stellar.anchor.api.rpc.RpcRequest -import org.stellar.anchor.api.sep.sep12.Sep12PutCustomerRequest -import org.stellar.anchor.api.sep.sep31.Sep31PostTransactionRequest -import org.stellar.anchor.apiclient.PlatformApiClient -import org.stellar.anchor.auth.AuthHelper -import org.stellar.anchor.client.Sep12Client -import org.stellar.anchor.client.Sep24Client -import org.stellar.anchor.client.Sep31Client -import org.stellar.anchor.platform.TestConfig -import org.stellar.anchor.platform.integrationtest.PlatformAPITestBase -import org.stellar.anchor.util.GsonUtils - -class PlatformApiCustodyTests : PlatformAPITestBase(TestConfig("custody")) { - companion object { - private val custodyMockServer = MockWebServer() - - @BeforeAll - @JvmStatic - fun construct() { - custodyMockServer.dispatcher = - object : Dispatcher() { - override fun dispatch(request: RecordedRequest): MockResponse { - if ( - "POST" == request.method && - "//v1/vault/accounts/1/XLM_USDC_T_CEKS/addresses" == request.path - ) { - return MockResponse().setResponseCode(200).setBody(CUSTODY_DEPOSIT_ADDRESS_RESPONSE) - } - if ("POST" == request.method && "//v1/transactions" == request.path) { - return MockResponse() - .setResponseCode(200) - .setBody(CUSTODY_TRANSACTION_PAYMENT_RESPONSE) - } - return MockResponse().setResponseCode(404) - } - } - - custodyMockServer.start(58086) - } - } - - private val gson = GsonUtils.getInstance() - private val platformApiClient = - PlatformApiClient(AuthHelper.forNone(), config.env["platform.server.url"]!!) - private val sep12Client = Sep12Client(toml.getString("KYC_SERVER"), token.token) - private val sep24Client = Sep24Client(toml.getString("TRANSFER_SERVER_SEP0024"), token.token) - private val sep31Client = Sep31Client(toml.getString("DIRECT_PAYMENT_SERVER"), token.token) - - /** - * 1. incomplete -> notify_interactive_flow_complete - * 2. pending_anchor -> request_offchain_funds - * 3. pending_user_transfer_start -> notify_offchain_funds_received - * 4. pending_anchor -> do_stellar_payment - * 5. completed - */ - @Test - fun `SEP-24 deposit complete full`() { - `test deposit flow`( - SEP_24_DEPOSIT_COMPLETE_FULL_FLOW_ACTION_REQUESTS, - SEP_24_DEPOSIT_COMPLETE_FULL_FLOW_ACTION_RESPONSES, - ) - } - - /** - * 1. incomplete -> notify_interactive_flow_complete - * 2. pending_anchor -> request_onchain_funds - * 3. pending_user_transfer_start -> notify_onchain_funds_received - * 4. pending_anchor -> do_stellar_refund - * 5. pending_stellar -> notify_refund_sent - * 6. refunded - */ - @Test - fun `SEP-24 withdraw full refund`() { - `test withdraw flow`( - SEP_24_WITHDRAW_FULL_REFUND_FLOW_ACTION_REQUESTS, - SEP_24_WITHDRAW_FULL_REFUND_FLOW_ACTION_RESPONSES, - ) - } - - /** - * 1. pending_receiver -> request_onchain_funds - * 2. pending_sender -> notify_onchain_funds_received - * 3. pending_receiver -> do_stellar_refund - * 4. pending_stellar -> notify_refund_sent - * 5. pending_sender - */ - @Test - fun `SEP-31 refunded do_stellar_refund`() { - `test receive flow`( - SEP_31_RECEIVE_REFUNDED_DO_STELLAR_REFUND_FLOW_ACTION_REQUESTS, - SEP_31_RECEIVE_REFUNDED_DO_STELLAR_REFUND_FLOW_ACTION_RESPONSES, - ) - } - - @Suppress("UNCHECKED_CAST") - private fun `test deposit flow`(actionRequests: String, actionResponse: String) { - val depositRequest = gson.fromJson(SEP_24_DEPOSIT_FLOW_REQUEST, HashMap::class.java) - val depositResponse = sep24Client.deposit(depositRequest as HashMap) - `test flow`(depositResponse.id, actionRequests, actionResponse) - } - - private fun `test receive flow`(actionRequests: String, actionResponses: String) { - val receiverCustomerRequest = - GsonUtils.getInstance().fromJson(CUSTOMER_1, Sep12PutCustomerRequest::class.java) - val receiverCustomer = sep12Client.putCustomer(receiverCustomerRequest) - val senderCustomerRequest = - GsonUtils.getInstance().fromJson(CUSTOMER_2, Sep12PutCustomerRequest::class.java) - val senderCustomer = sep12Client.putCustomer(senderCustomerRequest) - - val receiveRequestJson = - inject( - SEP_31_RECEIVE_FLOW_REQUEST, - RECEIVER_ID_KEY to receiverCustomer!!.id, - SENDER_ID_KEY to senderCustomer!!.id, - ) - val receiveRequest = gson.fromJson(receiveRequestJson, Sep31PostTransactionRequest::class.java) - val receiveResponse = sep31Client.postTransaction(receiveRequest) - - val updatedActionRequests = - inject( - actionRequests, - RECEIVER_ID_KEY to receiverCustomer.id, - SENDER_ID_KEY to senderCustomer.id, - ) - val updatedActionResponses = - inject( - actionResponses, - RECEIVER_ID_KEY to receiverCustomer.id, - SENDER_ID_KEY to senderCustomer.id, - ) - - `test flow`(receiveResponse.id, updatedActionRequests, updatedActionResponses) - } - - @Suppress("UNCHECKED_CAST") - private fun `test withdraw flow`(actionRequests: String, actionResponse: String) { - val withdrawRequest = gson.fromJson(SEP_24_WITHDRAW_FLOW_REQUEST, HashMap::class.java) - val withdrawResponse = sep24Client.withdraw(withdrawRequest as HashMap) - `test flow`(withdrawResponse.id, actionRequests, actionResponse) - } - - private fun `test flow`(txId: String, actionRequests: String, actionResponses: String) { - val rpcActionRequestsType = object : TypeToken>() {}.type - val rpcActionRequests: List = - gson.fromJson(inject(actionRequests, TX_ID_KEY to txId), rpcActionRequestsType) - - val rpcActionResponses = platformApiClient.sendRpcRequest(rpcActionRequests) - - val expectedResult = inject(actionResponses, TX_ID_KEY to txId) - val actualResult = rpcActionResponses.body?.string()?.trimIndent() - - JSONAssert.assertEquals( - expectedResult, - actualResult, - CustomComparator( - JSONCompareMode.LENIENT, - Customization("[*].result.transfer_received_at") { _, _ -> true }, - Customization("[*].result.started_at") { _, _ -> true }, - Customization("[*].result.updated_at") { _, _ -> true }, - Customization("[*].result.completed_at") { _, _ -> true }, - ), - ) - } -} - -private const val SEP_24_DEPOSIT_COMPLETE_FULL_FLOW_ACTION_REQUESTS = - """ -[ - { - "id": "1", - "method": "notify_interactive_flow_completed", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%", - "message": "test message 1", - "amount_in": { - "amount": "100", - "asset": "iso4217:USD" - }, - "amount_out": { - "amount": "95", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "fee_details": { - "total": "5", - "asset": "iso4217:USD" - }, - "amount_expected": { - "amount": "3" - } - } - }, - { - "id": "2", - "method": "request_offchain_funds", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%", - "message": "test message 2", - "amount_in": { - "amount": "100", - "asset": "iso4217:USD" - }, - "amount_out": { - "amount": "95", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "fee_details": { - "total": "5", - "asset": "iso4217:USD" - }, - "amount_expected": { - "amount": "100" - } - } - }, - { - "id": "3", - "method": "notify_offchain_funds_received", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%", - "message": "test message 3", - "funds_received_at": "2023-07-04T12:34:56Z", - "external_transaction_id": "ext-123456", - "amount_in": { - "amount": 1 - }, - "amount_out": { - "amount": 1 - }, - "fee_details": { - "total": 0, - "asset": "iso4217:USD" - } - } - }, - { - "id": "4", - "method": "do_stellar_payment", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%", - "message": "test message 4" - } - } -] -""" - -private const val SEP_24_DEPOSIT_COMPLETE_FULL_FLOW_ACTION_RESPONSES = - """ -[ - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "24", - "kind": "deposit", - "status": "pending_anchor", - "amount_expected": { - "amount": "3", - "asset": "stellar:USDC:GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP" - }, - "amount_in": { - "amount": "100", - "asset": "iso4217:USD" - }, - "amount_out": { - "amount": "95", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "fee_details": { - "total": "5", - "asset": "iso4217:USD" - }, - "started_at": "2023-08-07T12:52:01.663006Z", - "updated_at": "2023-08-07T12:52:03.100242Z", - "message": "test message 1", - "destination_account": "GDJLBYYKMCXNVVNABOE66NYXQGIA5AC5D223Z2KF6ZEYK4UBCA7FKLTG", - "client_name": "referenceCustodial" - }, - "id": "1" - }, - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "24", - "kind": "deposit", - "status": "pending_user_transfer_start", - "amount_expected": { - "amount": "100", - "asset": "stellar:USDC:GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP" - }, - "amount_in": { - "amount": "100", - "asset": "iso4217:USD" - }, - "amount_out": { - "amount": "95", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "fee_details": { - "total": "5", - "asset": "iso4217:USD" - }, - "started_at": "2023-08-07T12:52:01.663006Z", - "updated_at": "2023-08-07T12:52:04.165625Z", - "message": "test message 2", - "destination_account": "GDJLBYYKMCXNVVNABOE66NYXQGIA5AC5D223Z2KF6ZEYK4UBCA7FKLTG", - "client_name": "referenceCustodial" - }, - "id": "2" - }, - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "24", - "kind": "deposit", - "status": "pending_anchor", - "amount_expected": { - "amount": "100", - "asset": "stellar:USDC:GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP" - }, - "amount_in": { - "amount": "1", - "asset": "iso4217:USD" - }, - "amount_out": { - "amount": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "fee_details": { - "total": "0", - "asset": "iso4217:USD" - }, - "started_at": "2023-08-07T12:52:01.663006Z", - "updated_at": "2023-08-07T12:52:05.241766Z", - "message": "test message 3", - "destination_account": "GDJLBYYKMCXNVVNABOE66NYXQGIA5AC5D223Z2KF6ZEYK4UBCA7FKLTG", - "external_transaction_id": "ext-123456", - "client_name": "referenceCustodial" - }, - "id": "3" - }, - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "24", - "kind": "deposit", - "status": "pending_stellar", - "amount_expected": { - "amount": "100", - "asset": "stellar:USDC:GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP" - }, - "amount_in": { - "amount": "1", - "asset": "iso4217:USD" - }, - "amount_out": { - "amount": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "fee_details": { - "total": "0", - "asset": "iso4217:USD" - }, - "started_at": "2023-08-07T12:52:01.663006Z", - "updated_at": "2023-08-07T12:52:07.016199Z", - "message": "test message 4", - "destination_account": "GDJLBYYKMCXNVVNABOE66NYXQGIA5AC5D223Z2KF6ZEYK4UBCA7FKLTG", - "external_transaction_id": "ext-123456", - "client_name": "referenceCustodial" - }, - "id": "4" - } -] -""" - -private const val SEP_24_WITHDRAW_FULL_REFUND_FLOW_ACTION_REQUESTS = - """ -[ - { - "id": "1", - "method": "notify_interactive_flow_completed", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%", - "message": "test message 1", - "amount_in": { - "amount": "3", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": { - "amount": "2", - "asset": "iso4217:USD" - }, - "fee_details": { - "total": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_expected": { - "amount": "3" - } - } - }, - { - "id": "2", - "method": "request_onchain_funds", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%", - "message": "test message 2", - "amount_in": { - "amount": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": { - "amount": "1", - "asset": "iso4217:USD" - }, - "fee_details": { - "total": "0", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_expected": { - "amount": "3" - } - } - }, - { - "id": "3", - "method": "notify_onchain_funds_received", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%", - "message": "test message 3", - "stellar_transaction_id": "%TESTPAYMENT_TXN_HASH%", - "amount_in": { - "amount": "1" - } - } - }, - { - "id": "4", - "method": "do_stellar_refund", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%", - "message": "test message 4", - "refund": { - "id": "123456", - "amount": { - "amount": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_fee": { - "amount": "0", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - } - } - } - }, - { - "id": "5", - "method": "notify_refund_sent", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%", - "message": "test message 5", - "refund": { - "id": "123456", - "amount": { - "amount": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_fee": { - "amount": "0", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - } - } - } - } -] -""" - -private const val SEP_24_WITHDRAW_FULL_REFUND_FLOW_ACTION_RESPONSES = - """ -[ - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "24", - "kind": "withdrawal", - "status": "pending_anchor", - "amount_expected": { - "amount": "3", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_in": { - "amount": "3", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": { - "amount": "2", - "asset": "iso4217:USD" - }, - "fee_details": { - "total": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "started_at": "2023-08-07T10:35:38.513616Z", - "updated_at": "2023-08-07T10:35:39.973638Z", - "message": "test message 1", - "source_account": "GAIUIZPHLIHQEMNJGSZKCEUWHAZVGUZDBDMO2JXNAJZZZVNSVHQCEWJ4", - "destination_account": "GBN4NNCDGJO4XW4KQU3CBIESUJWFVBUZPOKUZHT7W7WRB7CWOA7BXVQF", - "client_name": "referenceCustodial" - }, - "id": "1" - }, - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "24", - "kind": "withdrawal", - "status": "pending_user_transfer_start", - "amount_expected": { - "amount": "3", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_in": { - "amount": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": { - "amount": "1", - "asset": "iso4217:USD" - }, - "fee_details": { - "total": "0", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "started_at": "2023-08-07T10:35:38.513616Z", - "updated_at": "2023-08-07T10:35:41.086448Z", - "message": "test message 2", - "source_account": "GAIUIZPHLIHQEMNJGSZKCEUWHAZVGUZDBDMO2JXNAJZZZVNSVHQCEWJ4", - "destination_account": "GC6X2ANA2OS3O2ESHUV6X44NH6J46EP2EO2JB7563Y7DYOIXFKHMHJ5O", - "memo": "testMemo", - "memo_type": "id", - "client_name": "referenceCustodial" - }, - "id": "2" - }, - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "24", - "kind": "withdrawal", - "status": "pending_anchor", - "amount_expected": { - "amount": "3", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_in": { - "amount": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": { - "amount": "1", - "asset": "iso4217:USD" - }, - "fee_details": { - "total": "0", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "started_at": "2023-08-07T10:35:38.513616Z", - "updated_at": "2023-08-07T10:35:42.792987Z", - "message": "test message 3", - "stellar_transactions": [ - { - "id": "%TESTPAYMENT_TXN_HASH%", - "memo": "testMemo", - "memo_type": "id", - "payments": [ - { - "amount": { - "amount": "%TESTPAYMENT_AMOUNT%", - "asset": "%TESTPAYMENT_ASSET_CIRCLE_USDC%" - }, - "payment_type": "payment", - "source_account": "%TESTPAYMENT_SRC_ACCOUNT%", - "destination_account": "%TESTPAYMENT_DEST_ACCOUNT%" - } - ] - } - ], - "source_account": "GAIUIZPHLIHQEMNJGSZKCEUWHAZVGUZDBDMO2JXNAJZZZVNSVHQCEWJ4", - "destination_account": "GC6X2ANA2OS3O2ESHUV6X44NH6J46EP2EO2JB7563Y7DYOIXFKHMHJ5O", - "memo": "testMemo", - "memo_type": "id", - "client_name": "referenceCustodial" - }, - "id": "3" - }, - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "24", - "kind": "withdrawal", - "status": "pending_stellar", - "amount_expected": { - "amount": "3", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_in": { - "amount": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": { - "amount": "1", - "asset": "iso4217:USD" - }, - "fee_details": { - "total": "0", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "started_at": "2023-08-07T10:35:38.513616Z", - "updated_at": "2023-08-07T10:35:44.019962Z", - "message": "test message 4", - "stellar_transactions": [ - { - "id": "%TESTPAYMENT_TXN_HASH%", - "memo": "testMemo", - "memo_type": "id", - "payments": [ - { - "amount": { - "amount": "%TESTPAYMENT_AMOUNT%", - "asset": "%TESTPAYMENT_ASSET_CIRCLE_USDC%" - }, - "payment_type": "payment", - "source_account": "%TESTPAYMENT_SRC_ACCOUNT%", - "destination_account": "%TESTPAYMENT_DEST_ACCOUNT%" - } - ] - } - ], - "source_account": "GAIUIZPHLIHQEMNJGSZKCEUWHAZVGUZDBDMO2JXNAJZZZVNSVHQCEWJ4", - "destination_account": "GC6X2ANA2OS3O2ESHUV6X44NH6J46EP2EO2JB7563Y7DYOIXFKHMHJ5O", - "memo": "testMemo", - "memo_type": "id", - "client_name": "referenceCustodial" - }, - "id": "4" - }, - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "24", - "kind": "withdrawal", - "status": "refunded", - "amount_expected": { - "amount": "3", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_in": { - "amount": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": { - "amount": "1", - "asset": "iso4217:USD" - }, - "fee_details": { - "total": "0", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "started_at": "2023-08-07T10:35:38.513616Z", - "updated_at": "2023-08-07T10:35:52.647527Z", - "completed_at": "2023-08-07T10:35:52.647537Z", - "message": "test message 5", - "refunds": { - "amount_refunded": { - "amount": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_fee": { - "amount": "0", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "payments": [ - { - "id": "123456", - "id_type": "stellar", - "amount": { - "amount": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "fee": { - "amount": "0", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - } - } - ] - }, - "stellar_transactions": [ - { - "id": "%TESTPAYMENT_TXN_HASH%", - "memo": "testMemo", - "memo_type": "id", - "payments": [ - { - "amount": { - "amount": "%TESTPAYMENT_AMOUNT%", - "asset": "%TESTPAYMENT_ASSET_CIRCLE_USDC%" - }, - "payment_type": "payment", - "source_account": "%TESTPAYMENT_SRC_ACCOUNT%", - "destination_account": "%TESTPAYMENT_DEST_ACCOUNT%" - } - ] - } - ], - "source_account": "GAIUIZPHLIHQEMNJGSZKCEUWHAZVGUZDBDMO2JXNAJZZZVNSVHQCEWJ4", - "destination_account": "GC6X2ANA2OS3O2ESHUV6X44NH6J46EP2EO2JB7563Y7DYOIXFKHMHJ5O", - "memo": "testMemo", - "memo_type": "id", - "client_name": "referenceCustodial" - }, - "id": "5" - } -] -""" - -private const val SEP_31_RECEIVE_REFUNDED_DO_STELLAR_REFUND_FLOW_ACTION_REQUESTS = - """ - [ - { - "id": "1", - "method": "request_onchain_funds", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%" - } - }, - { - "id": "2", - "method": "notify_onchain_funds_received", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%", - "message": "test message 1", - "stellar_transaction_id": "%TESTPAYMENT_TXN_HASH%" - } - }, - { - "id": "3", - "method": "do_stellar_refund", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%", - "message": "test message 2", - "refund": { - "id": "123456", - "amount": { - "amount": "5", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_fee": { - "amount": "5", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - } - } - } - }, - { - "id": "4", - "method": "notify_refund_sent", - "jsonrpc": "2.0", - "params": { - "transaction_id": "%TX_ID%", - "message": "test message 3", - "refund": { - "id": "123456", - "amount": { - "amount": "5", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_fee": { - "amount": "5", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - } - } - } - } - ] - """ - -private const val SEP_31_RECEIVE_REFUNDED_DO_STELLAR_REFUND_FLOW_ACTION_RESPONSES = - """ -[ - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "31", - "kind": "receive", - "status": "pending_sender", - "amount_expected": { - "amount": "10", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_in": { - "amount": "10", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": {}, - "fee_details": { - "total": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "started_at": "2024-08-20T14:51:13.884362Z", - "updated_at": "2024-08-20T14:51:14.998779Z", - "destination_account": "%CUSTODY_DEST_ACCOUNT%", - "memo": "testMemo", - "memo_type": "id", - "refund_memo": "testMemo", - "refund_memo_type": "id", - "client_name": "referenceCustodial", - "customers": { - "sender": { "id": "%SENDER_ID%" }, - "receiver": { "id": "%RECEIVER_ID%" } - }, - "creator": { - "account": "GDJLBYYKMCXNVVNABOE66NYXQGIA5AC5D223Z2KF6ZEYK4UBCA7FKLTG" - } - }, - "id": "1" - }, - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "31", - "kind": "receive", - "status": "pending_receiver", - "amount_expected": { - "amount": "10", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_in": { - "amount": "10", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": {}, - "fee_details": { - "total": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "started_at": "2024-08-20T14:51:13.884362Z", - "updated_at": "2024-08-20T14:51:16.193778Z", - "transfer_received_at": "2024-10-01T00:34:38Z", - "message": "test message 1", - "stellar_transactions": [ - { - "id": "%TESTPAYMENT_TXN_HASH%", - "memo": "testMemo", - "memo_type": "id", - "payments": [ - { - "amount": { - "amount": "%TESTPAYMENT_AMOUNT%", - "asset": "%TESTPAYMENT_ASSET_CIRCLE_USDC%" - }, - "payment_type": "payment", - "source_account": "%TESTPAYMENT_SRC_ACCOUNT%", - "destination_account": "%TESTPAYMENT_DEST_ACCOUNT%" - } - ] - } - ], - "source_account": "%TESTPAYMENT_SRC_ACCOUNT%", - "destination_account": "%CUSTODY_DEST_ACCOUNT%", - "memo": "testMemo", - "memo_type": "id", - "refund_memo": "testMemo", - "refund_memo_type": "id", - "client_name": "referenceCustodial", - "customers": { - "sender": { - "id": "%SENDER_ID%" - }, - "receiver": { - "id": "%RECEIVER_ID%" - } - }, - "creator": { - "account": "GDJLBYYKMCXNVVNABOE66NYXQGIA5AC5D223Z2KF6ZEYK4UBCA7FKLTG" - } - }, - "id": "2" - }, - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "31", - "kind": "receive", - "status": "pending_stellar", - "amount_expected": { - "amount": "10", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_in": { - "amount": "10", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": {}, - "fee_details": { - "total": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "started_at": "2024-08-20T14:51:13.884362Z", - "updated_at": "2024-08-20T14:51:17.301305Z", - "transfer_received_at": "2024-10-01T00:34:38Z", - "message": "test message 2", - "stellar_transactions": [ - { - "id": "%TESTPAYMENT_TXN_HASH%", - "memo": "testMemo", - "memo_type": "id", - "payments": [ - { - "amount": { - "amount": "%TESTPAYMENT_AMOUNT%", - "asset": "%TESTPAYMENT_ASSET_CIRCLE_USDC%" - }, - "payment_type": "payment", - "source_account": "%TESTPAYMENT_SRC_ACCOUNT%", - "destination_account": "%TESTPAYMENT_DEST_ACCOUNT%" - } - ] - } - ], - "source_account": "%TESTPAYMENT_SRC_ACCOUNT%", - "destination_account": "%CUSTODY_DEST_ACCOUNT%", - "memo": "testMemo", - "memo_type": "id", - "refund_memo": "testMemo", - "refund_memo_type": "id", - "client_name": "referenceCustodial", - "customers": { - "sender": { - "id": "%SENDER_ID%" - }, - "receiver": { - "id": "%RECEIVER_ID%" - } - }, - "creator": { - "account": "GDJLBYYKMCXNVVNABOE66NYXQGIA5AC5D223Z2KF6ZEYK4UBCA7FKLTG" - } - }, - "id": "3" - }, - { - "jsonrpc": "2.0", - "result": { - "id": "%TX_ID%", - "sep": "31", - "kind": "receive", - "status": "refunded", - "amount_expected": { - "amount": "10", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_in": { - "amount": "10", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_out": {}, - "fee_details": { - "total": "1", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "started_at": "2024-08-20T14:51:13.884362Z", - "updated_at": "2024-08-20T14:51:18.325903Z", - "completed_at": "2024-08-20T14:51:18.325901Z", - "transfer_received_at": "2024-10-01T00:34:38Z", - "message": "test message 3", - "refunds": { - "amount_refunded": { - "amount": "10", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "amount_fee": { - "amount": "5", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "payments": [ - { - "id": "123456", - "id_type": "stellar", - "amount": { - "amount": "5", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - }, - "fee": { - "amount": "5", - "asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5" - } - } - ] - }, - "stellar_transactions": [ - { - "id": "%TESTPAYMENT_TXN_HASH%", - "memo": "testMemo", - "memo_type": "id", - "payments": [ - { - "amount": { - "amount": "%TESTPAYMENT_AMOUNT%", - "asset": "%TESTPAYMENT_ASSET_CIRCLE_USDC%" - }, - "payment_type": "payment", - "source_account": "%TESTPAYMENT_SRC_ACCOUNT%", - "destination_account": "%TESTPAYMENT_DEST_ACCOUNT%" - } - ] - } - ], - "source_account": "%TESTPAYMENT_SRC_ACCOUNT%", - "destination_account": "%CUSTODY_DEST_ACCOUNT%", - "memo": "testMemo", - "memo_type": "id", - "refund_memo": "testMemo", - "refund_memo_type": "id", - "client_name": "referenceCustodial", - "customers": { - "sender": { - "id": "%SENDER_ID%" - }, - "receiver": { - "id": "%RECEIVER_ID%" - } - }, - "creator": { - "account": "GDJLBYYKMCXNVVNABOE66NYXQGIA5AC5D223Z2KF6ZEYK4UBCA7FKLTG" - } - }, - "id": "4" - } -] - """ - -private const val SEP_24_DEPOSIT_FLOW_REQUEST = """ -{ - "asset_code": "USDC" -} -""" - -private const val SEP_24_WITHDRAW_FLOW_REQUEST = - """{ - "amount": "10", - "asset_code": "USDC", - "asset_issuer": "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", - "account": "GAIUIZPHLIHQEMNJGSZKCEUWHAZVGUZDBDMO2JXNAJZZZVNSVHQCEWJ4", - "lang": "en" -}""" - -private const val SEP_31_RECEIVE_FLOW_REQUEST = - """ -{ - "amount": "10", - "asset_code": "USDC", - "asset_issuer": "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", - "receiver_id": "%RECEIVER_ID%", - "sender_id": "%SENDER_ID%", - "funding_method": "SEPA", - "fields": { - "transaction": { - "receiver_routing_number": "r0123", - "receiver_account_number": "a0456", - "type": "SWIFT" - } - } -} -""" - -private const val CUSTOMER_1 = - """ -{ - "first_name": "John", - "last_name": "Doe", - "email_address": "johndoe@test.com", - "address": "123 Washington Street", - "city": "San Francisco", - "state_or_province": "CA", - "address_country_code": "US", - "clabe_number": "1234", - "bank_number": "abcd", - "bank_account_number": "1234", - "bank_account_type": "checking" -} -""" - -private const val CUSTOMER_2 = - """ -{ - "first_name": "Jane", - "last_name": "Doe", - "email_address": "janedoe@test.com", - "address": "321 Washington Street", - "city": "San Francisco", - "state_or_province": "CA", - "address_country_code": "US", - "clabe_number": "5678", - "bank_number": "efgh", - "bank_account_number": "5678", - "bank_account_type": "checking" -} -""" - -private const val CUSTODY_TRANSACTION_PAYMENT_RESPONSE = - """ -{ - "id":"df0442b4-6d53-44cd-82d7-3c48edc0b1ac", - "status": "SUBMITTED" -} -""" - -private const val CUSTODY_DEPOSIT_ADDRESS_RESPONSE = - """ -{ - "address":"GC6X2ANA2OS3O2ESHUV6X44NH6J46EP2EO2JB7563Y7DYOIXFKHMHJ5O", - "legacyAddress": "testLegacyAddress", - "enterpriseAddress": "testEnterpriseAddress", - "tag":"testMemo", - "bip44AddressIndex":12345 -} -""" diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/suite/AuthApikeyCustodyTestSuite.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/suite/AuthApikeyCustodyTestSuite.kt deleted file mode 100644 index 7fa8438cb0..0000000000 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/suite/AuthApikeyCustodyTestSuite.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.stellar.anchor.platform.suite - -import org.junit.platform.suite.api.SelectPackages -import org.junit.platform.suite.api.Suite - -@Suite -@SelectPackages("org.stellar.anchor.platform.extendedtest.auth.apikey.custody") -class AuthApikeyCustodyTestSuite diff --git a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/suite/CustodyTestSuite.kt b/extended-tests/src/test/kotlin/org/stellar/anchor/platform/suite/CustodyTestSuite.kt deleted file mode 100644 index 689b1827e2..0000000000 --- a/extended-tests/src/test/kotlin/org/stellar/anchor/platform/suite/CustodyTestSuite.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.stellar.anchor.platform.suite - -import org.junit.platform.suite.api.SelectPackages -import org.junit.platform.suite.api.Suite - -@Suite @SelectPackages("org.stellar.anchor.platform.extendedtest.custody") class CustodyTestSuite diff --git a/lib-util/src/main/kotlin/org/stellar/anchor/client/CustodyApiClient.kt b/lib-util/src/main/kotlin/org/stellar/anchor/client/CustodyApiClient.kt deleted file mode 100644 index 6191c92234..0000000000 --- a/lib-util/src/main/kotlin/org/stellar/anchor/client/CustodyApiClient.kt +++ /dev/null @@ -1,37 +0,0 @@ -package org.stellar.anchor.client - -import com.google.gson.reflect.TypeToken -import java.security.PrivateKey -import org.stellar.anchor.api.custody.CreateCustodyTransactionRequest -import org.stellar.anchor.api.custody.GenerateDepositAddressResponse -import org.stellar.anchor.util.RSAUtil - -class CustodyApiClient( - private val endpoint: String, - private val jwt: String, - private val webhookPrivateKey: PrivateKey -) : SepClient() { - - fun generateDepositAddress(asset: String): GenerateDepositAddressResponse { - val url = "$endpoint/assets/$asset/addresses" - val responseBody = httpPost(url, mapOf(), jwt) - return gson.fromJson(responseBody, GenerateDepositAddressResponse::class.java) - } - - fun createTransaction(custodyTransaction: CreateCustodyTransactionRequest) { - val url = "$endpoint/transactions" - val type = object : TypeToken?>() {}.type - val requestBody: Map = gson.fromJson(gson.toJson(custodyTransaction), type) - httpPost(url, requestBody, jwt) - } - - fun createTransactionPayment(transactionId: String) { - val url = "$endpoint/transactions/$transactionId/payments" - httpPost(url, mapOf(), jwt) - } - - fun sendWebhook(webhookRequest: String) { - val webhookSignature = RSAUtil.sign(webhookRequest, webhookPrivateKey) - httpPost("$endpoint/webhook", webhookRequest, mapOf("fireblocks-signature" to webhookSignature)) - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/CustodyServer.java b/platform/src/main/java/org/stellar/anchor/platform/CustodyServer.java deleted file mode 100644 index 264b52bfd5..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/CustodyServer.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.stellar.anchor.platform; - -import static org.springframework.boot.Banner.Mode.OFF; -import static org.stellar.anchor.util.Log.info; - -import java.util.Map; -import org.slf4j.bridge.SLF4JBridgeHandler; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.retry.annotation.EnableRetry; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.stellar.anchor.platform.configurator.CustodyConfigManager; -import org.stellar.anchor.platform.configurator.SecretManager; - -@SpringBootApplication -@EnableJpaRepositories(basePackages = {"org.stellar.anchor.platform.data"}) -@EntityScan(basePackages = {"org.stellar.anchor.platform.data"}) -@ComponentScan( - basePackages = { - "org.stellar.anchor.platform.controller.custody", - "org.stellar.anchor.platform.component.custody", - "org.stellar.anchor.platform.component.share" - }) -@EnableRetry -@EnableScheduling -@EnableConfigurationProperties -public class CustodyServer extends AbstractPlatformServer implements WebMvcConfigurer { - - private ConfigurableApplicationContext ctx; - - public ConfigurableApplicationContext start(Map environment) { - buildEnvironment(environment); - - SpringApplicationBuilder builder = - new SpringApplicationBuilder(CustodyServer.class).bannerMode(OFF); - SpringApplication springApplication = builder.build(); - info("Adding secret manager as initializers..."); - springApplication.addInitializers(SecretManager.getInstance()); - info("Adding custody config manager as initializers..."); - springApplication.addInitializers(CustodyConfigManager.getInstance()); - - // Bridge Tomcat's JUL logging to SLF4J - SLF4JBridgeHandler.removeHandlersForRootLogger(); - SLF4JBridgeHandler.install(); - - return ctx = springApplication.run(); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/apiclient/CustodyApiClient.java b/platform/src/main/java/org/stellar/anchor/platform/apiclient/CustodyApiClient.java deleted file mode 100644 index 297ed2b81c..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/apiclient/CustodyApiClient.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.stellar.anchor.platform.apiclient; - -import static org.stellar.anchor.util.OkHttpUtil.TYPE_JSON; - -import com.google.gson.Gson; -import java.io.IOException; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpStatus; -import org.stellar.anchor.api.custody.CreateCustodyTransactionRequest; -import org.stellar.anchor.api.custody.CreateTransactionPaymentResponse; -import org.stellar.anchor.api.custody.CreateTransactionRefundRequest; -import org.stellar.anchor.api.custody.CustodyExceptionResponse; -import org.stellar.anchor.api.custody.GenerateDepositAddressResponse; -import org.stellar.anchor.api.exception.CustodyException; -import org.stellar.anchor.api.exception.InvalidConfigException; -import org.stellar.anchor.auth.AuthHelper; -import org.stellar.anchor.platform.config.CustodyApiConfig; -import org.stellar.anchor.util.AuthHeader; -import org.stellar.anchor.util.GsonUtils; - -/** API client, that is responsible for communication with Custody Server */ -public class CustodyApiClient { - - private static final Gson gson = GsonUtils.getInstance(); - - private static final String CREATE_TRANSACTION_URL_FORMAT = "/transactions"; - private static final String GENERATE_DEPOSIT_ADDRESS_URL_FORMAT = "/assets/%s/addresses"; - private static final String CREATE_TRANSACTION_PAYMENT_URL_FORMAT = "/transactions/%s/payments"; - private static final String CREATE_TRANSACTION_REFUND_URL_FORMAT = "/transactions/%s/refunds"; - - private final OkHttpClient httpClient; - private final AuthHelper authHelper; - private final CustodyApiConfig custodyApiConfig; - - public CustodyApiClient( - OkHttpClient httpClient, AuthHelper authHelper, CustodyApiConfig custodyApiConfig) { - this.httpClient = httpClient; - this.authHelper = authHelper; - this.custodyApiConfig = custodyApiConfig; - } - - public void createTransaction(CreateCustodyTransactionRequest transactionRequest) - throws CustodyException, InvalidConfigException { - Request request = - getRequestBuilder() - .url(custodyApiConfig.getBaseUrl() + CREATE_TRANSACTION_URL_FORMAT) - .post(RequestBody.create(gson.toJson(transactionRequest).getBytes(), TYPE_JSON)) - .build(); - doRequest(request); - } - - public GenerateDepositAddressResponse generateDepositAddress(String assetId) - throws CustodyException, InvalidConfigException { - Request request = - getRequestBuilder() - .url( - custodyApiConfig.getBaseUrl() - + String.format(GENERATE_DEPOSIT_ADDRESS_URL_FORMAT, assetId)) - .post(RequestBody.create(StringUtils.EMPTY, null)) - .build(); - String responseBody = doRequest(request); - return gson.fromJson(responseBody, GenerateDepositAddressResponse.class); - } - - public CreateTransactionPaymentResponse createTransactionPayment(String txnId, String requestBody) - throws CustodyException, InvalidConfigException { - final String url = - custodyApiConfig.getBaseUrl() + String.format(CREATE_TRANSACTION_PAYMENT_URL_FORMAT, txnId); - - Request request = - getRequestBuilder() - .url(url) - .post(RequestBody.create(gson.toJson(requestBody).getBytes(), TYPE_JSON)) - .build(); - - return gson.fromJson(doRequest(request), CreateTransactionPaymentResponse.class); - } - - public CreateTransactionPaymentResponse createTransactionRefund( - String txnId, CreateTransactionRefundRequest refundRequest) - throws CustodyException, InvalidConfigException { - final String url = - custodyApiConfig.getBaseUrl() + String.format(CREATE_TRANSACTION_REFUND_URL_FORMAT, txnId); - - Request request = - getRequestBuilder() - .url(url) - .post(RequestBody.create(gson.toJson(refundRequest).getBytes(), TYPE_JSON)) - .build(); - - return gson.fromJson(doRequest(request), CreateTransactionPaymentResponse.class); - } - - private Request.Builder getRequestBuilder() throws InvalidConfigException { - Request.Builder requestBuilder = new Request.Builder(); - AuthHeader authHeader = authHelper.createAuthHeader(); - return authHeader == null - ? requestBuilder - : requestBuilder.header(authHeader.getName(), authHeader.getValue()); - } - - private String doRequest(Request request) throws CustodyException { - try (Response response = httpClient.newCall(request).execute()) { - ResponseBody responseBody = response.body(); - String responseBodyJson = null; - - if (responseBody != null) { - responseBodyJson = responseBody.string(); - } - - if (HttpStatus.valueOf(response.code()).is2xxSuccessful()) { - return responseBodyJson; - } else { - String rawMessage = - gson.fromJson(responseBodyJson, CustodyExceptionResponse.class).getRawErrorMessage(); - if (rawMessage != null) { - throw new CustodyException(rawMessage, response.code()); - } else { - throw new CustodyException(responseBodyJson, response.code()); - } - } - } catch (IOException e) { - throw new CustodyException(e); - } - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/component/custody/ConfigBeans.java b/platform/src/main/java/org/stellar/anchor/platform/component/custody/ConfigBeans.java deleted file mode 100644 index 74d8b8ba68..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/component/custody/ConfigBeans.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.stellar.anchor.platform.component.custody; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.stellar.anchor.platform.configurator.ConfigManager; -import org.stellar.anchor.platform.configurator.CustodyConfigManager; - -@Configuration -public class ConfigBeans { - - @Bean(name = "configManager") - ConfigManager configManager() { - return CustodyConfigManager.getInstance(); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/component/custody/CustodyBeans.java b/platform/src/main/java/org/stellar/anchor/platform/component/custody/CustodyBeans.java deleted file mode 100644 index 53ec57e3c9..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/component/custody/CustodyBeans.java +++ /dev/null @@ -1,107 +0,0 @@ -package org.stellar.anchor.platform.component.custody; - -import jakarta.servlet.Filter; -import java.util.concurrent.TimeUnit; -import okhttp3.OkHttpClient; -import okhttp3.OkHttpClient.Builder; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.stellar.anchor.api.custody.fireblocks.TransactionDetails; -import org.stellar.anchor.apiclient.PlatformApiClient; -import org.stellar.anchor.auth.JwtService; -import org.stellar.anchor.filter.ApiKeyFilter; -import org.stellar.anchor.filter.CustodyAuthJwtFilter; -import org.stellar.anchor.filter.NoneFilter; -import org.stellar.anchor.metrics.MetricsService; -import org.stellar.anchor.platform.config.CustodyApiConfig; -import org.stellar.anchor.platform.config.PropertyCustodyConfig; -import org.stellar.anchor.platform.config.RpcConfig; -import org.stellar.anchor.platform.custody.*; -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo; - -@Configuration -public class CustodyBeans { - - /** - * Register platform-to-custody token filter. - * - * @return Spring Filter Registration Bean - */ - @Bean - public FilterRegistrationBean platformToCustodyTokenFilter( - CustodyApiConfig custodyApiConfig) { - String authSecret = custodyApiConfig.getAuth().getSecretString(); - - Filter platformToCustody; - switch (custodyApiConfig.getAuth().getType()) { - case JWT: - JwtService jwtService = JwtService.builder().custodyAuthSecret(authSecret).build(); - platformToCustody = - new CustodyAuthJwtFilter( - jwtService, custodyApiConfig.getAuth().getJwt().getHttpHeader()); - break; - - case API_KEY: - platformToCustody = - new ApiKeyFilter(authSecret, custodyApiConfig.getAuth().getApiKey().getHttpHeader()); - break; - - default: - platformToCustody = new NoneFilter(); - break; - } - - FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); - registrationBean.setFilter(platformToCustody); - registrationBean.addUrlPatterns("/transactions/*"); - return registrationBean; - } - - @Bean - Sep6CustodyPaymentHandler sep6CustodyPaymentHandler( - JdbcCustodyTransactionRepo custodyTransactionRepo, - PlatformApiClient platformApiClient, - RpcConfig rpcConfig, - MetricsService metricsService) { - return new Sep6CustodyPaymentHandler( - custodyTransactionRepo, platformApiClient, rpcConfig, metricsService); - } - - @Bean - Sep24CustodyPaymentHandler sep24CustodyPaymentHandler( - JdbcCustodyTransactionRepo custodyTransactionRepo, - PlatformApiClient platformApiClient, - RpcConfig rpcConfig, - MetricsService metricsService) { - return new Sep24CustodyPaymentHandler( - custodyTransactionRepo, platformApiClient, rpcConfig, metricsService); - } - - @Bean - Sep31CustodyPaymentHandler sep31CustodyPaymentHandler( - JdbcCustodyTransactionRepo custodyTransactionRepo, - PlatformApiClient platformApiClient, - RpcConfig rpcConfig, - MetricsService metricsService) { - return new Sep31CustodyPaymentHandler( - custodyTransactionRepo, platformApiClient, rpcConfig, metricsService); - } - - @Bean(name = "custodyHttpClient") - OkHttpClient custodyHttpClient(PropertyCustodyConfig custodyConfig) { - return new Builder() - .connectTimeout(custodyConfig.getHttpClient().getConnectTimeout(), TimeUnit.SECONDS) - .readTimeout(custodyConfig.getHttpClient().getReadTimeout(), TimeUnit.SECONDS) - .writeTimeout(custodyConfig.getHttpClient().getWriteTimeout(), TimeUnit.SECONDS) - .callTimeout(custodyConfig.getHttpClient().getCallTimeout(), TimeUnit.SECONDS) - .build(); - } - - @Bean - CustodyTransactionService custodyTransactionService( - JdbcCustodyTransactionRepo custodyTransactionRepo, - CustodyPaymentService custodyPaymentService) { - return new CustodyTransactionService(custodyTransactionRepo, custodyPaymentService); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/component/custody/FireblocksBeans.java b/platform/src/main/java/org/stellar/anchor/platform/component/custody/FireblocksBeans.java deleted file mode 100644 index 613618cb18..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/component/custody/FireblocksBeans.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.stellar.anchor.platform.component.custody; - -import okhttp3.OkHttpClient; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.stellar.anchor.api.custody.fireblocks.TransactionDetails; -import org.stellar.anchor.api.exception.InvalidConfigException; -import org.stellar.anchor.config.CustodySecretConfig; -import org.stellar.anchor.ledger.LedgerClient; -import org.stellar.anchor.platform.config.FireblocksConfig; -import org.stellar.anchor.platform.custody.*; -import org.stellar.anchor.platform.custody.fireblocks.FireblocksApiClient; -import org.stellar.anchor.platform.custody.fireblocks.FireblocksEventService; -import org.stellar.anchor.platform.custody.fireblocks.FireblocksPaymentService; -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo; -import org.stellar.anchor.platform.fireblocks.job.FireblocksTransactionsReconciliationJob; - -@Configuration -@ConditionalOnProperty(value = "custody.type", havingValue = "fireblocks") -public class FireblocksBeans { - - @Bean - @ConfigurationProperties(prefix = "custody.fireblocks") - FireblocksConfig fireblocksConfig(CustodySecretConfig custodySecretConfig) { - return new FireblocksConfig(custodySecretConfig); - } - - @Bean - FireblocksTransactionsReconciliationJob reconciliationJob( - FireblocksConfig fireblocksConfig, - CustodyPaymentService custodyPaymentService, - FireblocksEventService fireblocksEventService, - CustodyTransactionService custodyTransactionService) { - return new FireblocksTransactionsReconciliationJob( - fireblocksConfig, custodyPaymentService, fireblocksEventService, custodyTransactionService); - } - - @Bean - FireblocksApiClient fireblocksApiClient( - @Qualifier("custodyHttpClient") OkHttpClient httpClient, FireblocksConfig fireblocksConfig) - throws InvalidConfigException { - return new FireblocksApiClient(httpClient, fireblocksConfig); - } - - @Bean - FireblocksEventService fireblocksEventService( - JdbcCustodyTransactionRepo custodyTransactionRepo, - Sep6CustodyPaymentHandler sep6CustodyPaymentHandler, - Sep24CustodyPaymentHandler sep24CustodyPaymentHandler, - Sep31CustodyPaymentHandler sep31CustodyPaymentHandler, - LedgerClient horizon, - FireblocksConfig fireblocksConfig) - throws InvalidConfigException { - return new FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - horizon, - fireblocksConfig); - } - - @Bean - CustodyPaymentService custodyPaymentService( - FireblocksApiClient fireblocksApiClient, FireblocksConfig fireblocksConfig) { - return new FireblocksPaymentService(fireblocksApiClient, fireblocksConfig); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/component/platform/PlatformServerBeans.java b/platform/src/main/java/org/stellar/anchor/platform/component/platform/PlatformServerBeans.java index dfbc2977aa..20a9d0774b 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/component/platform/PlatformServerBeans.java +++ b/platform/src/main/java/org/stellar/anchor/platform/component/platform/PlatformServerBeans.java @@ -1,7 +1,6 @@ package org.stellar.anchor.platform.component.platform; import jakarta.servlet.Filter; -import java.util.Optional; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -9,24 +8,16 @@ import org.stellar.anchor.asset.AssetService; import org.stellar.anchor.auth.JwtService; import org.stellar.anchor.auth.NonceStore; -import org.stellar.anchor.config.CustodyConfig; import org.stellar.anchor.config.Sep24Config; import org.stellar.anchor.config.Sep31Config; import org.stellar.anchor.config.Sep6Config; -import org.stellar.anchor.custody.CustodyService; import org.stellar.anchor.event.EventService; import org.stellar.anchor.filter.ApiKeyFilter; import org.stellar.anchor.filter.NoneFilter; import org.stellar.anchor.filter.PlatformAuthJwtFilter; -import org.stellar.anchor.ledger.LedgerClient; -import org.stellar.anchor.platform.apiclient.CustodyApiClient; import org.stellar.anchor.platform.config.PlatformApiConfig; import org.stellar.anchor.platform.config.PlatformServerConfig; -import org.stellar.anchor.platform.config.PropertyCustodyConfig; -import org.stellar.anchor.platform.data.JdbcTransactionPendingTrustRepo; import org.stellar.anchor.platform.job.NonceCleanupJob; -import org.stellar.anchor.platform.job.TrustlineCheckJob; -import org.stellar.anchor.platform.rpc.NotifyTrustSetHandler; import org.stellar.anchor.platform.service.*; import org.stellar.anchor.sep24.Sep24DepositInfoGenerator; import org.stellar.anchor.sep24.Sep24TransactionStore; @@ -49,22 +40,17 @@ public FilterRegistrationBean platformTokenFilter( PlatformServerConfig serverConfig, PlatformApiConfig apiConfig) { Filter anchorToPlatformFilter; String authSecret = serverConfig.getSecretConfig().getPlatformAuthSecret(); - switch (apiConfig.getAuth().getType()) { - case JWT: - JwtService jwtService = JwtService.builder().platformAuthSecret(authSecret).build(); - anchorToPlatformFilter = - new PlatformAuthJwtFilter(jwtService, apiConfig.getAuth().getJwt().getHttpHeader()); - break; - - case API_KEY: - anchorToPlatformFilter = - new ApiKeyFilter(authSecret, apiConfig.getAuth().getApiKey().getHttpHeader()); - break; - - default: - anchorToPlatformFilter = new NoneFilter(); - break; - } + anchorToPlatformFilter = + switch (apiConfig.getAuth().getType()) { + case JWT -> { + JwtService jwtService = JwtService.builder().platformAuthSecret(authSecret).build(); + yield new PlatformAuthJwtFilter( + jwtService, apiConfig.getAuth().getJwt().getHttpHeader()); + } + case API_KEY -> + new ApiKeyFilter(authSecret, apiConfig.getAuth().getApiKey().getHttpHeader()); + default -> new NoneFilter(); + }; FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(anchorToPlatformFilter); @@ -72,69 +58,45 @@ public FilterRegistrationBean platformTokenFilter( } @Bean - Sep31DepositInfoGenerator sep31DepositInfoGenerator( - Sep31Config sep31Config, Optional custodyApiClient) + Sep31DepositInfoGenerator sep31DepositInfoGenerator(Sep31Config sep31Config) throws InvalidConfigException { - switch (sep31Config.getDepositInfoGeneratorType()) { - case SELF: - return new Sep31DepositInfoSelfGenerator(); - case CUSTODY: - return new Sep31DepositInfoCustodyGenerator( - custodyApiClient.orElseThrow( - () -> - new InvalidConfigException("Integration with custody service is not enabled"))); - case NONE: - return new Sep31DepositInfoNoneGenerator(); - default: - throw new RuntimeException( - String.format( - "sep31.deposit_info_generator_type: %s is not supported", - sep31Config.getDepositInfoGeneratorType())); - } + return switch (sep31Config.getDepositInfoGeneratorType()) { + case SELF -> new Sep31DepositInfoSelfGenerator(); + case NONE -> new Sep31DepositInfoNoneGenerator(); + default -> + throw new RuntimeException( + String.format( + "sep31.deposit_info_generator_type: %s is not supported", + sep31Config.getDepositInfoGeneratorType())); + }; } @Bean - Sep24DepositInfoGenerator sep24DepositInfoGenerator( - Sep24Config sep24Config, Optional custodyApiClient) + Sep24DepositInfoGenerator sep24DepositInfoGenerator(Sep24Config sep24Config) throws InvalidConfigException { - switch (sep24Config.getDepositInfoGeneratorType()) { - case SELF: - return new Sep24DepositInfoSelfGenerator(); - case CUSTODY: - return new Sep24DepositInfoCustodyGenerator( - custodyApiClient.orElseThrow( - () -> - new InvalidConfigException("Integration with custody service is not enabled"))); - case NONE: - return new Sep24DepositInfoNoneGenerator(); - default: - throw new RuntimeException( - String.format( - "sep24.deposit_info_generator_type: %s is not supported", - sep24Config.getDepositInfoGeneratorType())); - } + return switch (sep24Config.getDepositInfoGeneratorType()) { + case SELF -> new Sep24DepositInfoSelfGenerator(); + case NONE -> new Sep24DepositInfoNoneGenerator(); + default -> + throw new RuntimeException( + String.format( + "sep24.deposit_info_generator_type: %s is not supported", + sep24Config.getDepositInfoGeneratorType())); + }; } @Bean Sep6DepositInfoGenerator sep6DepositInfoGenerator( - Sep6Config sep6Config, AssetService assetService, Optional custodyApiClient) - throws InvalidConfigException { - switch (sep6Config.getDepositInfoGeneratorType()) { - case SELF: - return new Sep6DepositInfoSelfGenerator(assetService); - case CUSTODY: - return new Sep6DepositInfoCustodyGenerator( - custodyApiClient.orElseThrow( - () -> - new InvalidConfigException("Integration with custody service is not enabled"))); - case NONE: - return new Sep6DepositInfoNoneGenerator(); - default: - throw new RuntimeException( - String.format( - "sep6.deposit_info_generator_type: %s is not supported", - sep6Config.getDepositInfoGeneratorType())); - } + Sep6Config sep6Config, AssetService assetService) throws InvalidConfigException { + return switch (sep6Config.getDepositInfoGeneratorType()) { + case SELF -> new Sep6DepositInfoSelfGenerator(assetService); + case NONE -> new Sep6DepositInfoNoneGenerator(); + default -> + throw new RuntimeException( + String.format( + "sep6.deposit_info_generator_type: %s is not supported", + sep6Config.getDepositInfoGeneratorType())); + }; } @Bean @@ -146,9 +108,7 @@ TransactionService transactionService( AssetService assetService, EventService eventService, Sep6DepositInfoGenerator sep6DepositInfoGenerator, - Sep24DepositInfoGenerator sep24DepositInfoGenerator, - CustodyService custodyService, - CustodyConfig custodyConfig) { + Sep24DepositInfoGenerator sep24DepositInfoGenerator) { return new TransactionService( txn6Store, txn24Store, @@ -157,23 +117,7 @@ TransactionService transactionService( assetService, eventService, sep6DepositInfoGenerator, - sep24DepositInfoGenerator, - custodyService, - custodyConfig); - } - - @Bean - TrustlineCheckJob trustlineCheckJob( - LedgerClient ledgerClient, - JdbcTransactionPendingTrustRepo transactionPendingTrustRepo, - PropertyCustodyConfig custodyConfig, - NotifyTrustSetHandler notifyTrustSetHandler) { - if (custodyConfig.isCustodyIntegrationEnabled()) { - return new TrustlineCheckJob( - ledgerClient, transactionPendingTrustRepo, custodyConfig, notifyTrustSetHandler); - } else { - return null; - } + sep24DepositInfoGenerator); } @Bean diff --git a/platform/src/main/java/org/stellar/anchor/platform/component/platform/RpcActionBeans.java b/platform/src/main/java/org/stellar/anchor/platform/component/platform/RpcActionBeans.java index d9f37541a1..2b7c680dde 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/component/platform/RpcActionBeans.java +++ b/platform/src/main/java/org/stellar/anchor/platform/component/platform/RpcActionBeans.java @@ -6,13 +6,10 @@ import org.springframework.context.annotation.Import; import org.stellar.anchor.api.callback.CustomerIntegration; import org.stellar.anchor.asset.AssetService; -import org.stellar.anchor.config.CustodyConfig; -import org.stellar.anchor.custody.CustodyService; import org.stellar.anchor.event.EventService; import org.stellar.anchor.ledger.LedgerClient; import org.stellar.anchor.metrics.MetricsService; import org.stellar.anchor.platform.component.sep.ApiClientBeans; -import org.stellar.anchor.platform.config.PropertyCustodyConfig; import org.stellar.anchor.platform.config.RpcConfig; import org.stellar.anchor.platform.data.JdbcTransactionPendingTrustRepo; import org.stellar.anchor.platform.observer.stellar.PaymentObservingAccountsManager; @@ -38,56 +35,6 @@ RpcService rpcService(List> rpcMethodHandlers, RpcConfig rpc return new RpcService(rpcMethodHandlers, rpcConfig); } - @Bean - DoStellarPaymentHandler doStellarPaymentHandler( - Sep6TransactionStore txn6Store, - Sep24TransactionStore txn24Store, - Sep31TransactionStore txn31Store, - RequestValidator requestValidator, - CustodyConfig custodyConfig, - LedgerClient ledgerClient, - AssetService assetService, - CustodyService custodyService, - EventService eventService, - MetricsService metricsService, - JdbcTransactionPendingTrustRepo transactionPendingTrustRepo) { - return new DoStellarPaymentHandler( - txn6Store, - txn24Store, - txn31Store, - requestValidator, - custodyConfig, - ledgerClient, - assetService, - custodyService, - eventService, - metricsService, - transactionPendingTrustRepo); - } - - @Bean - DoStellarRefundHandler doStellarRefundHandler( - Sep6TransactionStore txn6Store, - Sep24TransactionStore txn24Store, - Sep31TransactionStore txn31Store, - RequestValidator requestValidator, - CustodyConfig custodyConfig, - AssetService assetService, - CustodyService custodyService, - EventService eventService, - MetricsService metricsService) { - return new DoStellarRefundHandler( - txn6Store, - txn24Store, - txn31Store, - requestValidator, - custodyConfig, - assetService, - custodyService, - eventService, - metricsService); - } - @Bean GetTransactionHandler getTransactionHandler(TransactionService txnService) { return new GetTransactionHandler(txnService); @@ -186,8 +133,6 @@ NotifyOffchainFundsReceivedHandler notifyOffchainFundsReceivedHandler( Sep31TransactionStore txn31Store, RequestValidator requestValidator, AssetService assetService, - CustodyService custodyService, - CustodyConfig custodyConfig, EventService eventService, MetricsService metricsService) { return new NotifyOffchainFundsReceivedHandler( @@ -196,8 +141,6 @@ NotifyOffchainFundsReceivedHandler notifyOffchainFundsReceivedHandler( txn31Store, requestValidator, assetService, - custodyService, - custodyConfig, eventService, metricsService); } @@ -393,9 +336,7 @@ NotifyTrustSetHandler notifyTrustSetHandler( RequestValidator requestValidator, AssetService assetService, EventService eventService, - MetricsService metricsService, - PropertyCustodyConfig custodyConfig, - CustodyService custodyService) { + MetricsService metricsService) { return new NotifyTrustSetHandler( txn6Store, txn24Store, @@ -403,9 +344,7 @@ NotifyTrustSetHandler notifyTrustSetHandler( requestValidator, assetService, eventService, - metricsService, - custodyConfig, - custodyService); + metricsService); } @Bean @@ -455,8 +394,6 @@ RequestOnchainFundsHandler requestOnchainFundsHandler( Sep31TransactionStore txn31Store, RequestValidator requestValidator, AssetService assetService, - CustodyService custodyService, - CustodyConfig custodyConfig, Sep6DepositInfoGenerator sep6DepositInfoGenerator, Sep24DepositInfoGenerator sep24DepositInfoGenerator, Sep31DepositInfoGenerator sep31DepositInfoGenerator, @@ -469,8 +406,6 @@ RequestOnchainFundsHandler requestOnchainFundsHandler( txn31Store, requestValidator, assetService, - custodyService, - custodyConfig, sep6DepositInfoGenerator, sep24DepositInfoGenerator, sep31DepositInfoGenerator, @@ -486,7 +421,6 @@ RequestTrustlineHandler requestTrustHandler( Sep31TransactionStore txn31Store, RequestValidator requestValidator, AssetService assetService, - CustodyConfig custodyConfig, EventService eventService, MetricsService metricsService) { return new RequestTrustlineHandler( @@ -495,7 +429,6 @@ RequestTrustlineHandler requestTrustHandler( txn31Store, requestValidator, assetService, - custodyConfig, eventService, metricsService); } diff --git a/platform/src/main/java/org/stellar/anchor/platform/component/sep/SepBeans.java b/platform/src/main/java/org/stellar/anchor/platform/component/sep/SepBeans.java index 27075afb70..1f41528b37 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/component/sep/SepBeans.java +++ b/platform/src/main/java/org/stellar/anchor/platform/component/sep/SepBeans.java @@ -198,7 +198,6 @@ Sep24Service sep24Service( EventService eventService, InteractiveUrlConstructor interactiveUrlConstructor, @Qualifier("sep24MoreInfoUrlConstructor") MoreInfoUrlConstructor sep24MoreInfoUrlConstructor, - CustodyConfig custodyConfig, Sep38QuoteStore sep38QuoteStore) { ExchangeAmountsCalculator exchangeAmountsCalculator = new ExchangeAmountsCalculator(sep38QuoteStore); @@ -214,7 +213,6 @@ Sep24Service sep24Service( eventService, interactiveUrlConstructor, sep24MoreInfoUrlConstructor, - custodyConfig, exchangeAmountsCalculator); } diff --git a/platform/src/main/java/org/stellar/anchor/platform/component/share/CustodyApiBeans.java b/platform/src/main/java/org/stellar/anchor/platform/component/share/CustodyApiBeans.java deleted file mode 100644 index dd91544192..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/component/share/CustodyApiBeans.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.stellar.anchor.platform.component.share; - -import java.util.concurrent.TimeUnit; -import okhttp3.OkHttpClient; -import okhttp3.OkHttpClient.Builder; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.stellar.anchor.auth.ApiAuthJwt; -import org.stellar.anchor.auth.AuthHelper; -import org.stellar.anchor.auth.JwtService; -import org.stellar.anchor.platform.apiclient.CustodyApiClient; -import org.stellar.anchor.platform.config.CustodyApiConfig; - -@Configuration -@ConditionalOnExpression(value = "'${custody.type}' != 'none'") -public class CustodyApiBeans { - - @Bean(name = "custodyApiHttpClient") - @Primary - OkHttpClient custodyApiHttpClient(CustodyApiConfig custodyApiConfig) { - return new Builder() - .connectTimeout(custodyApiConfig.getHttpClient().getConnectTimeout(), TimeUnit.SECONDS) - .readTimeout(custodyApiConfig.getHttpClient().getReadTimeout(), TimeUnit.SECONDS) - .writeTimeout(custodyApiConfig.getHttpClient().getWriteTimeout(), TimeUnit.SECONDS) - .callTimeout(custodyApiConfig.getHttpClient().getCallTimeout(), TimeUnit.SECONDS) - .build(); - } - - @Bean - CustodyApiClient custodyApiClient( - @Qualifier("custodyApiHttpClient") OkHttpClient httpClient, - CustodyApiConfig custodyApiConfig) { - return new CustodyApiClient(httpClient, buildAuthHelper(custodyApiConfig), custodyApiConfig); - } - - AuthHelper buildAuthHelper(CustodyApiConfig custodyApiConfig) { - String authSecret = custodyApiConfig.getAuth().getSecretString(); - switch (custodyApiConfig.getAuth().getType()) { - case JWT: - return AuthHelper.forJwtToken( - custodyApiConfig.getAuth().getJwt().getHttpHeader(), - JwtService.builder().custodyAuthSecret(authSecret).build(), - Long.parseLong(custodyApiConfig.getAuth().getJwt().getExpirationMilliseconds()), - ApiAuthJwt.CustodyAuthJwt.class); - case API_KEY: - return AuthHelper.forApiKey( - custodyApiConfig.getAuth().getApiKey().getHttpHeader(), authSecret); - default: - return AuthHelper.forNone(); - } - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/component/share/SharedConfigBeans.java b/platform/src/main/java/org/stellar/anchor/platform/component/share/SharedConfigBeans.java index fdfe122046..0b758447b0 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/component/share/SharedConfigBeans.java +++ b/platform/src/main/java/org/stellar/anchor/platform/component/share/SharedConfigBeans.java @@ -3,15 +3,11 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.stellar.anchor.config.CustodySecretConfig; import org.stellar.anchor.config.SecretConfig; import org.stellar.anchor.platform.config.AppLoggingConfig; import org.stellar.anchor.platform.config.CallbackApiConfig; -import org.stellar.anchor.platform.config.CustodyApiConfig; import org.stellar.anchor.platform.config.PlatformApiConfig; import org.stellar.anchor.platform.config.PlatformServerConfig; -import org.stellar.anchor.platform.config.PropertyCustodyConfig; -import org.stellar.anchor.platform.config.PropertyCustodySecretConfig; import org.stellar.anchor.platform.config.PropertyDataConfig; import org.stellar.anchor.platform.config.PropertySecretConfig; @@ -30,23 +26,6 @@ PlatformApiConfig platformApiConfig(PropertySecretConfig secretConfig) { return new PlatformApiConfig(secretConfig); } - @Bean - CustodySecretConfig custodySecretConfig() { - return new PropertyCustodySecretConfig(); - } - - @Bean - @ConfigurationProperties(prefix = "custody-server") - public CustodyApiConfig custodyApiConfig(CustodySecretConfig custodySecretConfig) { - return new CustodyApiConfig(custodySecretConfig); - } - - @Bean - @ConfigurationProperties(prefix = "custody") - PropertyCustodyConfig propertyCustodyConfig() { - return new PropertyCustodyConfig(); - } - @Bean @ConfigurationProperties(prefix = "callback-api") CallbackApiConfig callbackApiConfig(PropertySecretConfig secretConfig) { diff --git a/platform/src/main/java/org/stellar/anchor/platform/component/share/SharedCustodyBeans.java b/platform/src/main/java/org/stellar/anchor/platform/component/share/SharedCustodyBeans.java deleted file mode 100644 index c847f20428..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/component/share/SharedCustodyBeans.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.stellar.anchor.platform.component.share; - -import java.util.Optional; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.stellar.anchor.custody.CustodyService; -import org.stellar.anchor.platform.apiclient.CustodyApiClient; -import org.stellar.anchor.platform.service.CustodyServiceImpl; - -@Configuration -public class SharedCustodyBeans { - - @Bean - CustodyService custodyService(Optional custodyApiClient) { - return new CustodyServiceImpl(custodyApiClient); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/component/share/UtilityBeans.java b/platform/src/main/java/org/stellar/anchor/platform/component/share/UtilityBeans.java index 087f3e9b62..3394f4cc7b 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/component/share/UtilityBeans.java +++ b/platform/src/main/java/org/stellar/anchor/platform/component/share/UtilityBeans.java @@ -71,22 +71,20 @@ MoreInfoUrlConstructor sep24MoreInfoUrlConstructor( @Bean @ConfigurationProperties(prefix = "sep31") - Sep31Config sep31Config(CustodyConfig custodyConfig, AssetService assetService) { - return new PropertySep31Config(custodyConfig, assetService); + Sep31Config sep31Config(AssetService assetService) { + return new PropertySep31Config(assetService); } @Bean @ConfigurationProperties(prefix = "sep24") - PropertySep24Config sep24Config( - SecretConfig secretConfig, CustodyConfig custodyConfig, AssetService assetService) { - return new PropertySep24Config(secretConfig, custodyConfig, assetService); + PropertySep24Config sep24Config(SecretConfig secretConfig, AssetService assetService) { + return new PropertySep24Config(secretConfig, assetService); } @Bean @ConfigurationProperties(prefix = "sep6") - PropertySep6Config sep6Config( - CustodyConfig custodyConfig, AssetService assetService, SecretConfig secretConfig) { - return new PropertySep6Config(custodyConfig, assetService, secretConfig); + PropertySep6Config sep6Config(AssetService assetService, SecretConfig secretConfig) { + return new PropertySep6Config(assetService, secretConfig); } /********************************** @@ -98,9 +96,8 @@ PropertySecretConfig secretConfig() { } @Bean - public JwtService jwtService(SecretConfig secretConfig, CustodySecretConfig custodySecretConfig) - throws NotSupportedException { - return new JwtService(secretConfig, custodySecretConfig); + public JwtService jwtService(SecretConfig secretConfig) throws NotSupportedException { + return new JwtService(secretConfig); } @Bean diff --git a/platform/src/main/java/org/stellar/anchor/platform/config/CustodyApiConfig.java b/platform/src/main/java/org/stellar/anchor/platform/config/CustodyApiConfig.java deleted file mode 100644 index dcc98c4044..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/config/CustodyApiConfig.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.stellar.anchor.platform.config; - -import static org.stellar.anchor.auth.AuthType.API_KEY; -import static org.stellar.anchor.auth.AuthType.JWT; -import static org.stellar.anchor.util.StringHelper.isEmpty; - -import java.util.List; -import lombok.Data; -import org.jetbrains.annotations.NotNull; -import org.springframework.validation.Errors; -import org.springframework.validation.Validator; -import org.stellar.anchor.auth.AuthConfig; -import org.stellar.anchor.auth.AuthType; -import org.stellar.anchor.config.CustodySecretConfig; -import org.stellar.anchor.util.KeyUtil; -import org.stellar.anchor.util.NetUtil; - -@Data -public class CustodyApiConfig implements Validator { - - private String baseUrl; - private HttpClientConfig httpClient; - private AuthConfig auth; - private CustodySecretConfig secretConfig; - - public CustodyApiConfig(CustodySecretConfig secretConfig) { - this.secretConfig = secretConfig; - } - - public void setAuth(AuthConfig auth) { - auth.setSecretString(secretConfig.getCustodyAuthSecret()); - this.auth = auth; - } - - @Override - public boolean supports(@NotNull Class clazz) { - return CustodyApiConfig.class.isAssignableFrom(clazz); - } - - @Override - public void validate(@NotNull Object target, @NotNull Errors errors) { - validateBaseUrl(errors); - validateApiSecret(errors); - httpClient.validate("custody-server", errors); - } - - private void validateBaseUrl(Errors errors) { - if (isEmpty(baseUrl)) { - errors.rejectValue( - "baseUrl", - "custody-server-base-url-empty", - "The custody_server.base_url cannot be empty and must be defined"); - } - if (!NetUtil.isUrlValid(baseUrl)) { - errors.rejectValue( - "baseUrl", - "custody-server-base-url-invalid", - "The custody_server.base_url is not a valid URL"); - } - } - - private void validateApiSecret(Errors errors) { - if (List.of(AuthType.API_KEY, AuthType.JWT).contains(auth.getType())) { - if (isEmpty(secretConfig.getCustodyAuthSecret())) { - errors.reject( - "empty-secret-custody-server-secret", - "Please set environment variable secret.custody_server.auth_secret or SECRET.CUSTODY_SERVER.AUTH_SECRET"); - } - - if (List.of(API_KEY, JWT).contains(auth.getType())) { - if (AuthType.JWT == auth.getType()) { - KeyUtil.rejectWeakJWTSecret( - secretConfig.getCustodyAuthSecret(), errors, "secret.custody_server.auth_secret"); - } - - if (auth.getSecretString() == null) { - errors.rejectValue( - "secret", - "empty-secret", - "Please set environment variable [SECRET.CUSTODY_SERVER.AUTH_SECRET] for auth type:" - + auth.getType()); - } - } - } - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/config/FireblocksConfig.java b/platform/src/main/java/org/stellar/anchor/platform/config/FireblocksConfig.java deleted file mode 100644 index 90493b6b6a..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/config/FireblocksConfig.java +++ /dev/null @@ -1,229 +0,0 @@ -package org.stellar.anchor.platform.config; - -import static org.stellar.anchor.util.RSAUtil.RSA_ALGORITHM; -import static org.stellar.anchor.util.RSAUtil.generatePrivateKey; -import static org.stellar.anchor.util.RSAUtil.generatePublicKey; -import static org.stellar.anchor.util.StringHelper.isEmpty; - -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; -import org.springframework.scheduling.support.CronExpression; -import org.springframework.validation.Errors; -import org.springframework.validation.Validator; -import org.stellar.anchor.api.exception.InvalidConfigException; -import org.stellar.anchor.config.CustodySecretConfig; -import org.stellar.anchor.util.Log; -import org.stellar.anchor.util.NetUtil; -import org.stellar.anchor.util.RSAUtil; - -@Data -public class FireblocksConfig implements Validator { - - private String baseUrl; - private String vaultAccountId; - private CustodySecretConfig secretConfig; - private String publicKey; - private RetryConfig retryConfig; - private Reconciliation reconciliation; - private Map assetMappings; - - public FireblocksConfig(CustodySecretConfig secretConfig) { - this.secretConfig = secretConfig; - } - - public void setAssetMappings(String assetMappings) { - if (StringUtils.isEmpty(assetMappings)) { - this.assetMappings = Map.of(); - } else { - this.assetMappings = - Arrays.stream(assetMappings.split(StringUtils.LF)) - .collect( - Collectors.toMap( - mapping -> mapping.substring(mapping.indexOf(StringUtils.SPACE) + 1), - mapping -> mapping.substring(0, mapping.indexOf(StringUtils.SPACE)))); - } - } - - /** - * Get Fireblocks asset code by Stellar asset code - * - * @return Fireblocks asset code or null if no mapping found - */ - public String getFireblocksAssetCode(String stellarAssetCode) throws InvalidConfigException { - if (assetMappings.containsKey(stellarAssetCode)) { - return assetMappings.get(stellarAssetCode); - } - - String message = - String.format( - "Unable to find Fireblocks asset code by Stellar asset code [%s]", stellarAssetCode); - Log.warnF( - message + " Please add corresponding asset mapping in custody.fireblocks.asset_mapping"); - throw new InvalidConfigException(message); - } - - @Override - public boolean supports(@NotNull Class clazz) { - return FireblocksConfig.class.isAssignableFrom(clazz); - } - - @Override - public void validate(@NotNull Object target, @NotNull Errors errors) { - validateBaseUrl(errors); - validateVaultAccountId(errors); - validateApiKey(errors); - validateSecretKey(errors); - validateReconciliationCronExpression(errors); - validateReconciliationMaxAttempts(errors); - validatePublicKey(errors); - validateRetryMaxAttempts(errors); - validateRetryDelay(errors); - } - - private void validateBaseUrl(Errors errors) { - if (isEmpty(baseUrl)) { - errors.rejectValue( - "baseUrl", - "custody-fireblocks-base-url-empty", - "The custody.fireblocks.base_url cannot be empty and must be defined"); - } - if (!NetUtil.isUrlValid(baseUrl)) { - errors.rejectValue( - "baseUrl", - "custody-fireblocks-base-url-invalid", - "The custody.fireblocks.base_url is not a valid URL"); - } - } - - private void validateVaultAccountId(Errors errors) { - if (isEmpty(vaultAccountId)) { - errors.rejectValue( - "vaultAccountId", - "custody-fireblocks-vault-account-id-empty", - "The custody.fireblocks.vault_account_id cannot be empty and must be defined"); - } - } - - private void validateApiKey(Errors errors) { - if (isEmpty(secretConfig.getFireblocksApiKey())) { - errors.reject( - "secret-custody-fireblocks-api-key-empty", - "Please set environment variable secret.custody.fireblocks.api_key or SECRET_CUSTODY_FIREBLOCKS_API_KEY"); - } - } - - private void validateSecretKey(Errors errors) { - if (isEmpty(secretConfig.getFireblocksSecretKey())) { - errors.reject( - "secret-custody-fireblocks-secret-key-empty", - "Please set environment variable secret.custody.fireblocks.secret_key or SECRET_CUSTODY_FIREBLOCKS_SECRET_KEY"); - } - if (!RSAUtil.isValidPrivateKey(secretConfig.getFireblocksSecretKey(), RSAUtil.RSA_ALGORITHM)) { - errors.reject( - "secret-custody-fireblocks-secret_key-invalid", - "The secret-custody-fireblocks-secret_key is invalid"); - } - } - - private void validateReconciliationCronExpression(Errors errors) { - if (isEmpty(reconciliation.cronExpression)) { - errors.reject( - "custody-fireblocks-reconciliation-cron_expression-empty", - "The custody.fireblocks.reconciliation.cron_expression is empty"); - } - if (!CronExpression.isValidExpression(reconciliation.cronExpression)) { - errors.reject( - "custody-fireblocks-reconciliation-cron_expression-invalid", - "The custody.fireblocks.reconciliation.cron_expression is invalid"); - } - } - - private void validateReconciliationMaxAttempts(Errors errors) { - if (reconciliation.maxAttempts < 0) { - errors.reject( - "custody-fireblocks-reconciliation-max_attempts-invalid", - "custody.fireblocks.reconciliation.max_attempts must be greater than or equal to 0"); - } - } - - public void validatePublicKey(Errors errors) { - if (isEmpty(publicKey)) { - errors.reject( - "custody-fireblocks-public_key-empty", "The custody.fireblocks.public_key is empty"); - } - if (!RSAUtil.isValidPublicKey(publicKey, RSAUtil.RSA_ALGORITHM)) { - errors.reject( - "custody-fireblocks-public_key-invalid", "The custody-fireblocks-public_key is invalid"); - } - } - - public void validateRetryMaxAttempts(Errors errors) { - if (retryConfig.maxAttempts < 0) { - errors.reject( - "custody-fireblocks-retry_config-max_attempts-invalid", - "custody.fireblocks.retry_config.max_attempts must be greater than or equal to 0"); - } - } - - public void validateRetryDelay(Errors errors) { - if (retryConfig.delay < 0) { - errors.reject( - "custody-fireblocks-retry_config-delay-invalid", - "custody.fireblocks.retry_config.delay must be greater than or equal to 0"); - } - } - - /** - * Get Fireblocks public key - * - * @return public key - */ - public PublicKey getFireblocksPublicKey() throws InvalidConfigException { - try { - return generatePublicKey(publicKey, RSA_ALGORITHM); - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - throw new InvalidConfigException(List.of("Failed to generate Fireblocks public key"), e); - } - } - - /** - * Get Fireblocks private key - * - * @return private key - */ - public PrivateKey getFireblocksPrivateKey() throws InvalidConfigException { - try { - return generatePrivateKey(secretConfig.getFireblocksSecretKey(), RSA_ALGORITHM); - } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - throw new InvalidConfigException(List.of("Failed to generate Fireblocks private key"), e); - } - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - public static class RetryConfig { - - private int maxAttempts; - private int delay; - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - public static class Reconciliation { - private int maxAttempts; - private String cronExpression; - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/config/PropertyCustodyConfig.java b/platform/src/main/java/org/stellar/anchor/platform/config/PropertyCustodyConfig.java deleted file mode 100644 index 517e3179c2..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/config/PropertyCustodyConfig.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.stellar.anchor.platform.config; - -import static org.stellar.anchor.util.StringHelper.isEmpty; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.jetbrains.annotations.NotNull; -import org.springframework.scheduling.support.CronExpression; -import org.springframework.validation.Errors; -import org.springframework.validation.Validator; -import org.stellar.anchor.config.CustodyConfig; - -@Data -public class PropertyCustodyConfig implements CustodyConfig, Validator { - - private CustodyType type; - private HttpClientConfig httpClient; - private Trustline trustline; - - @Override - public boolean supports(@NotNull Class clazz) { - return PropertyCustodyConfig.class.isAssignableFrom(clazz); - } - - @Override - public void validate(@NotNull Object target, @NotNull Errors errors) { - validateType(errors); - if (isCustodyIntegrationEnabled()) { - httpClient.validate("custody", errors); - validateCheckCronExpression(errors); - validateCheckDuration(errors); - } - } - - private void validateType(Errors errors) { - if (type == null) { - errors.rejectValue( - "type", "custody-type-empty", "The custody.type cannot be empty and must be defined"); - } - } - - private void validateCheckCronExpression(Errors errors) { - if (isEmpty(trustline.checkCronExpression)) { - errors.reject( - "custody-trustline-check_cron_expression-empty", - "The custody.trustline.check_cron_expression is empty"); - } - if (!CronExpression.isValidExpression(trustline.checkCronExpression)) { - errors.reject( - "custody-trustline-check_cron_expression-invalid", - "The custody.trustline.check_cron_expression is invalid"); - } - } - - private void validateCheckDuration(Errors errors) { - if (trustline.checkDuration <= 0) { - errors.reject( - "custody-trustline-check_duration-invalid", - "custody-trustline-check_duration must be greater than 0"); - } - } - - @Data - @AllArgsConstructor - @NoArgsConstructor - public static class Trustline { - private String checkCronExpression; - private int checkDuration; - private String timeoutMessage; - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/config/PropertyCustodySecretConfig.java b/platform/src/main/java/org/stellar/anchor/platform/config/PropertyCustodySecretConfig.java deleted file mode 100644 index 7f06a574e6..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/config/PropertyCustodySecretConfig.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.stellar.anchor.platform.config; - -import org.stellar.anchor.config.CustodySecretConfig; -import org.stellar.anchor.platform.configurator.SecretManager; - -public class PropertyCustodySecretConfig implements CustodySecretConfig { - - public static final String SECRET_FIREBLOCKS_API_KEY = "secret.custody.fireblocks.api_key"; - public static final String SECRET_FIREBLOCKS_SECRET_KEY = "secret.custody.fireblocks.secret_key"; - public static final String SECRET_CUSTODY_SERVER_AUTH_SECRET = - "secret.custody_server.auth_secret"; - - @Override - public String getFireblocksApiKey() { - return SecretManager.getInstance().get(SECRET_FIREBLOCKS_API_KEY); - } - - @Override - public String getFireblocksSecretKey() { - return SecretManager.getInstance().get(SECRET_FIREBLOCKS_SECRET_KEY); - } - - @Override - public String getCustodyAuthSecret() { - return SecretManager.getInstance().get(SECRET_CUSTODY_SERVER_AUTH_SECRET); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/config/PropertySep24Config.java b/platform/src/main/java/org/stellar/anchor/platform/config/PropertySep24Config.java index c2360cdeba..d345fe9f9d 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/config/PropertySep24Config.java +++ b/platform/src/main/java/org/stellar/anchor/platform/config/PropertySep24Config.java @@ -18,7 +18,6 @@ import org.springframework.validation.Validator; import org.stellar.anchor.api.asset.StellarAssetInfo; import org.stellar.anchor.asset.AssetService; -import org.stellar.anchor.config.CustodyConfig; import org.stellar.anchor.config.SecretConfig; import org.stellar.anchor.config.Sep24Config; import org.stellar.anchor.platform.data.JdbcSep24Transaction; @@ -41,14 +40,11 @@ public class PropertySep24Config implements Sep24Config, Validator { Features features; DepositInfoGeneratorType depositInfoGeneratorType; Long initialUserDeadlineSeconds; - CustodyConfig custodyConfig; KycFieldsForwarding kycFieldsForwarding; AssetService assetService; - public PropertySep24Config( - SecretConfig secretConfig, CustodyConfig custodyConfig, AssetService assetService) { + public PropertySep24Config(SecretConfig secretConfig, AssetService assetService) { this.secretConfig = secretConfig; - this.custodyConfig = custodyConfig; this.assetService = assetService; } @@ -87,7 +83,6 @@ public void validate(@NotNull Object target, @NotNull Errors errors) { if (enabled) { validateInteractiveUrlConfig(errors); validateMoreInfoUrlConfig(errors); - validateFeaturesConfig(errors); validateDepositInfoGeneratorType(errors); } } @@ -184,39 +179,7 @@ void validateMoreInfoUrlConfig(Errors errors) { } } - void validateFeaturesConfig(Errors errors) { - if (custodyConfig.isCustodyIntegrationEnabled()) { - if (features.getAccountCreation()) { - errors.rejectValue( - "features.accountCreation", - "sep24-features-account_creation-not-supported", - "Custody service doesn't support creating accounts for users requesting deposits"); - } - if (features.getClaimableBalances()) { - errors.rejectValue( - "features.claimableBalances", - "sep24-features-claimable_balances-not-supported", - "Custody service doesn't support sending deposit funds as claimable balances"); - } - } - } - void validateDepositInfoGeneratorType(Errors errors) { - if (custodyConfig.isCustodyIntegrationEnabled() && CUSTODY != depositInfoGeneratorType) { - errors.rejectValue( - "depositInfoGeneratorType", - "sep24-deposit-info-generator-type", - String.format( - "[%s] deposit info generator type is not supported when custody integration is enabled", - depositInfoGeneratorType.toString().toLowerCase())); - } else if (!custodyConfig.isCustodyIntegrationEnabled() - && CUSTODY == depositInfoGeneratorType) { - errors.rejectValue( - "depositInfoGeneratorType", - "sep24-deposit-info-generator-type", - "[custody] deposit info generator type is not supported when custody integration is disabled"); - } - if (SELF == depositInfoGeneratorType) { for (StellarAssetInfo asset : assetService.getStellarAssets()) { if (!asset.getCode().equals("native") && isEmpty(asset.getDistributionAccount())) { diff --git a/platform/src/main/java/org/stellar/anchor/platform/config/PropertySep31Config.java b/platform/src/main/java/org/stellar/anchor/platform/config/PropertySep31Config.java index 3cc2127e1e..6bcc429e01 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/config/PropertySep31Config.java +++ b/platform/src/main/java/org/stellar/anchor/platform/config/PropertySep31Config.java @@ -1,6 +1,5 @@ package org.stellar.anchor.platform.config; -import static org.stellar.anchor.config.Sep31Config.DepositInfoGeneratorType.CUSTODY; import static org.stellar.anchor.config.Sep31Config.DepositInfoGeneratorType.SELF; import static org.stellar.anchor.config.Sep31Config.PaymentType.STRICT_SEND; import static org.stellar.anchor.util.StringHelper.isEmpty; @@ -11,7 +10,6 @@ import org.springframework.validation.Validator; import org.stellar.anchor.api.asset.StellarAssetInfo; import org.stellar.anchor.asset.AssetService; -import org.stellar.anchor.config.CustodyConfig; import org.stellar.anchor.config.Sep31Config; @Data @@ -19,11 +17,10 @@ public class PropertySep31Config implements Sep31Config, Validator { boolean enabled; PaymentType paymentType = STRICT_SEND; DepositInfoGeneratorType depositInfoGeneratorType; - CustodyConfig custodyConfig; + AssetService assetService; - public PropertySep31Config(CustodyConfig custodyConfig, AssetService assetService) { - this.custodyConfig = custodyConfig; + public PropertySep31Config(AssetService assetService) { this.assetService = assetService; } @@ -40,21 +37,6 @@ public void validate(@NotNull Object target, @NotNull Errors errors) { } void validateDepositInfoGeneratorType(Errors errors) { - if (custodyConfig.isCustodyIntegrationEnabled() && CUSTODY != depositInfoGeneratorType) { - errors.rejectValue( - "depositInfoGeneratorType", - "sep31-deposit-info-generator-type", - String.format( - "[%s] deposit info generator type is not supported when custody integration is enabled", - depositInfoGeneratorType.toString().toLowerCase())); - } else if (!custodyConfig.isCustodyIntegrationEnabled() - && CUSTODY == depositInfoGeneratorType) { - errors.rejectValue( - "depositInfoGeneratorType", - "sep31-deposit-info-generator-type", - "[custody] deposit info generator type is not supported when custody integration is disabled"); - } - if (SELF == depositInfoGeneratorType) { for (StellarAssetInfo asset : assetService.getStellarAssets()) { if (!asset.getCode().equals("native") && isEmpty(asset.getDistributionAccount())) { diff --git a/platform/src/main/java/org/stellar/anchor/platform/config/PropertySep6Config.java b/platform/src/main/java/org/stellar/anchor/platform/config/PropertySep6Config.java index 4ac8623b79..64e7c0a81b 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/config/PropertySep6Config.java +++ b/platform/src/main/java/org/stellar/anchor/platform/config/PropertySep6Config.java @@ -1,6 +1,5 @@ package org.stellar.anchor.platform.config; -import static org.stellar.anchor.config.Sep6Config.DepositInfoGeneratorType.CUSTODY; import static org.stellar.anchor.config.Sep6Config.DepositInfoGeneratorType.SELF; import static org.stellar.anchor.util.StringHelper.isEmpty; import static org.stellar.anchor.util.StringHelper.snakeToCamelCase; @@ -15,7 +14,6 @@ import org.springframework.validation.Validator; import org.stellar.anchor.api.asset.StellarAssetInfo; import org.stellar.anchor.asset.AssetService; -import org.stellar.anchor.config.CustodyConfig; import org.stellar.anchor.config.SecretConfig; import org.stellar.anchor.config.Sep6Config; import org.stellar.anchor.platform.data.JdbcSep6Transaction; @@ -33,14 +31,11 @@ public class PropertySep6Config implements Sep6Config, Validator { Features features; DepositInfoGeneratorType depositInfoGeneratorType; Long initialUserDeadlineSeconds; - CustodyConfig custodyConfig; AssetService assetService; MoreInfoUrlConfig moreInfoUrl; SecretConfig secretConfig; - public PropertySep6Config( - CustodyConfig custodyConfig, AssetService assetService, SecretConfig secretConfig) { - this.custodyConfig = custodyConfig; + public PropertySep6Config(AssetService assetService, SecretConfig secretConfig) { this.assetService = assetService; this.secretConfig = secretConfig; } @@ -120,21 +115,6 @@ void validateMoreInfoUrlConfig(Errors errors) { } void validateDepositInfoGeneratorType(Errors errors) { - if (custodyConfig.isCustodyIntegrationEnabled() && CUSTODY != depositInfoGeneratorType) { - errors.rejectValue( - "depositInfoGeneratorType", - "sep6-deposit-info-generator-type", - String.format( - "[%s] deposit info generator type is not supported when custody integration is enabled", - depositInfoGeneratorType.toString().toLowerCase())); - } else if (!custodyConfig.isCustodyIntegrationEnabled() - && CUSTODY == depositInfoGeneratorType) { - errors.rejectValue( - "depositInfoGeneratorType", - "sep6-deposit-info-generator-type", - "[custody] deposit info generator type is not supported when custody integration is disabled"); - } - if (SELF == depositInfoGeneratorType) { for (StellarAssetInfo asset : assetService.getStellarAssets()) { if (!asset.getCode().equals("native") && isEmpty(asset.getDistributionAccount())) { diff --git a/platform/src/main/java/org/stellar/anchor/platform/configurator/CustodyConfigManager.java b/platform/src/main/java/org/stellar/anchor/platform/configurator/CustodyConfigManager.java deleted file mode 100644 index 016bd393b6..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/configurator/CustodyConfigManager.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.stellar.anchor.platform.configurator; - -import static org.stellar.anchor.util.Log.info; - -import java.util.List; -import lombok.SneakyThrows; -import org.jetbrains.annotations.NotNull; -import org.springframework.context.ConfigurableApplicationContext; -import org.stellar.anchor.api.exception.InvalidConfigException; - -public class CustodyConfigManager extends ConfigManager { - private static final CustodyConfigManager custodyConfigManager = new CustodyConfigManager(); - - private CustodyConfigManager() {} - - public static CustodyConfigManager getInstance() { - return custodyConfigManager; - } - - @SneakyThrows - @Override - public void initialize(@NotNull ConfigurableApplicationContext applicationContext) { - // Read configuration from system environment variables, configuration file, and default values - info("Read and process configurations"); - configMap = processConfigurations(applicationContext); - - // Make sure no secret is leaked. - sanitize(configMap); - - // Send values to Spring - sendToSpring( - applicationContext, - configMap, - List.of( - new SentryConfigAdapter(), - new LogConfigAdapter(), - new DataConfigAdapter(), - new CustodyServerConfigAdapter())); - } -} - -class CustodyServerConfigAdapter extends SpringConfigAdapter { - @Override - void updateSpringEnv(ConfigMap config) throws InvalidConfigException { - copy(config, "custody_server.context_path", "server.servlet.context-path"); - copy(config, "custody_server.port", "server.port"); - set("spring.mvc.converters.preferred-json-mapper", "gson"); - if (config.getBoolean("metrics.enabled")) { - set("management.endpoints.enabled-by-default", true); - set("management.endpoints.web.exposure.include", "health,info,prometheus"); - } else { - set("management.endpoints.enabled-by-default", false); - } - } - - @Override - void validate(ConfigMap config) throws InvalidConfigException { - // noop - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/configurator/SecretManager.java b/platform/src/main/java/org/stellar/anchor/platform/configurator/SecretManager.java index 105c3466c4..5d94d7f63f 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/configurator/SecretManager.java +++ b/platform/src/main/java/org/stellar/anchor/platform/configurator/SecretManager.java @@ -10,7 +10,6 @@ import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.PropertiesPropertySource; -import org.stellar.anchor.platform.config.PropertyCustodySecretConfig; import org.stellar.anchor.platform.config.PropertySecretConfig; public class SecretManager @@ -25,13 +24,10 @@ public class SecretManager PropertySecretConfig.SECRET_SEP_45_JWT_SECRET, PropertySecretConfig.SECRET_CALLBACK_API_AUTH_SECRET, PropertySecretConfig.SECRET_PLATFORM_API_AUTH_SECRET, - PropertyCustodySecretConfig.SECRET_CUSTODY_SERVER_AUTH_SECRET, PropertySecretConfig.SECRET_DATA_USERNAME, PropertySecretConfig.SECRET_DATA_PASSWORD, PropertySecretConfig.SECRET_EVENTS_QUEUE_KAFKA_USERNAME, PropertySecretConfig.SECRET_EVENTS_QUEUE_KAFKA_PASSWORD, - PropertyCustodySecretConfig.SECRET_FIREBLOCKS_SECRET_KEY, - PropertyCustodySecretConfig.SECRET_FIREBLOCKS_API_KEY, PropertySecretConfig.SECRET_SSL_KEYSTORE_PASSWORD, PropertySecretConfig.SECRET_SSL_KEY_PASSWORD, PropertySecretConfig.SECRET_SSL_TRUSTSTORE_PASSWORD); diff --git a/platform/src/main/java/org/stellar/anchor/platform/controller/AbstractControllerExceptionHandler.java b/platform/src/main/java/org/stellar/anchor/platform/controller/AbstractControllerExceptionHandler.java index 9c7105f866..dc9fd6239b 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/controller/AbstractControllerExceptionHandler.java +++ b/platform/src/main/java/org/stellar/anchor/platform/controller/AbstractControllerExceptionHandler.java @@ -11,12 +11,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.resource.NoResourceFoundException; -import org.stellar.anchor.api.custody.CustodyExceptionResponse; import org.stellar.anchor.api.exception.*; -import org.stellar.anchor.api.exception.custody.CustodyBadRequestException; -import org.stellar.anchor.api.exception.custody.CustodyNotFoundException; -import org.stellar.anchor.api.exception.custody.CustodyServiceUnavailableException; -import org.stellar.anchor.api.exception.custody.CustodyTooManyRequestsException; import org.stellar.anchor.api.sep.CustomerInfoNeededResponse; import org.stellar.anchor.api.sep.SepExceptionResponse; @@ -67,36 +62,6 @@ public SepExceptionResponse handleNotImplementedError(Exception ex) { return new SepExceptionResponse(ex.getMessage()); } - @ResponseStatus(HttpStatus.BAD_REQUEST) - @ExceptionHandler({CustodyBadRequestException.class}) - public CustodyExceptionResponse handleCustodyBadRequest(AnchorException ex) { - debugF("Bad request (custody server): {}", ex.getMessage()); - return new CustodyExceptionResponse(ex.getMessage()); - } - - @ResponseStatus(value = HttpStatus.NOT_FOUND) - @ExceptionHandler({CustodyNotFoundException.class}) - public CustodyExceptionResponse handleCustodyNotFound(AnchorException ex) { - traceF("Resource not found (custody server): {}", ex.getMessage()); - return new CustodyExceptionResponse(ex.getMessage()); - } - - @ResponseStatus(HttpStatus.TOO_MANY_REQUESTS) - @ExceptionHandler({CustodyTooManyRequestsException.class}) - public CustodyExceptionResponse handleCustodyTooManyRequestsError(AnchorException ex) { - errorF("Too many requests (custody server): {}", ex.getMessage()); - captureException(ex); - return new CustodyExceptionResponse(ex.getMessage()); - } - - @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) - @ExceptionHandler({CustodyServiceUnavailableException.class}) - public CustodyExceptionResponse handleCustodyServiceUnavailableError(AnchorException ex) { - errorEx(ex); - captureException(ex); - return new CustodyExceptionResponse(ex.getMessage()); - } - // HTTP code: 502 // Received an invalid response from the upstream server. @ResponseStatus(HttpStatus.BAD_GATEWAY) diff --git a/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyControllerConfig.java b/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyControllerConfig.java deleted file mode 100644 index 0cebe616a2..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyControllerConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.stellar.anchor.platform.controller.custody; - -import org.springframework.beans.propertyeditors.StringTrimmerEditor; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; - -public class CustodyControllerConfig { - @InitBinder - void initBinder(final WebDataBinder binder) { - // Maps empty strings to null when a @RequestParam is being bound. - // This is required due to a bug in Spring. - binder.registerCustomEditor(String.class, new StringTrimmerEditor(true)); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyControllerExceptionHandler.java b/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyControllerExceptionHandler.java deleted file mode 100644 index c2cd345798..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyControllerExceptionHandler.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.stellar.anchor.platform.controller.custody; - -import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.stellar.anchor.platform.controller.AbstractControllerExceptionHandler; - -/** The uncaught exception handler. */ -@RestControllerAdvice -public class CustodyControllerExceptionHandler extends AbstractControllerExceptionHandler {} diff --git a/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyHealthController.java b/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyHealthController.java deleted file mode 100644 index 58f5cb133e..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyHealthController.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.stellar.anchor.platform.controller.custody; - -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.stellar.anchor.platform.controller.HealthController; -import org.stellar.anchor.platform.service.HealthCheckService; - -@RestController -@CrossOrigin(origins = "*") -@RequestMapping( - value = "/health", - produces = {MediaType.APPLICATION_JSON_VALUE}) -public class CustodyHealthController extends HealthController { - public CustodyHealthController(HealthCheckService healthCheckService) { - super(healthCheckService); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyPaymentController.java b/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyPaymentController.java deleted file mode 100644 index 85b7bacc81..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyPaymentController.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.stellar.anchor.platform.controller.custody; - -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; -import org.stellar.anchor.api.custody.GenerateDepositAddressResponse; -import org.stellar.anchor.api.exception.CustodyException; -import org.stellar.anchor.api.exception.InvalidConfigException; -import org.stellar.anchor.platform.custody.CustodyPaymentService; - -@RestController -public class CustodyPaymentController { - - private final CustodyPaymentService custodyPaymentService; - - public CustodyPaymentController(CustodyPaymentService custodyPaymentService) { - this.custodyPaymentService = custodyPaymentService; - } - - @CrossOrigin(origins = "*") - @ResponseStatus(code = HttpStatus.OK) - @RequestMapping( - value = "/assets/{assetId}/addresses", - produces = {MediaType.APPLICATION_JSON_VALUE}, - method = {RequestMethod.POST}) - public GenerateDepositAddressResponse generateDepositAddress( - @PathVariable(name = "assetId") String assetId) - throws CustodyException, InvalidConfigException { - return custodyPaymentService.generateDepositAddress(assetId); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyTransactionController.java b/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyTransactionController.java deleted file mode 100644 index 8f5f10592e..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyTransactionController.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.stellar.anchor.platform.controller.custody; - -import static org.stellar.anchor.platform.data.JdbcCustodyTransaction.PaymentType.PAYMENT; - -import lombok.SneakyThrows; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; -import org.stellar.anchor.api.custody.CreateCustodyTransactionRequest; -import org.stellar.anchor.api.custody.CreateTransactionPaymentResponse; -import org.stellar.anchor.api.custody.CreateTransactionRefundRequest; -import org.stellar.anchor.platform.custody.CustodyTransactionService; - -@RestController -public class CustodyTransactionController { - - private final CustodyTransactionService custodyTransactionService; - - public CustodyTransactionController(CustodyTransactionService custodyTransactionService) { - this.custodyTransactionService = custodyTransactionService; - } - - @SneakyThrows - @CrossOrigin(origins = "*") - @ResponseStatus(code = HttpStatus.OK) - @RequestMapping( - value = "/transactions", - produces = {MediaType.APPLICATION_JSON_VALUE}, - method = {RequestMethod.POST}) - public void createCustodyTransaction(@RequestBody CreateCustodyTransactionRequest request) { - custodyTransactionService.create(request, PAYMENT); - } - - @SneakyThrows - @CrossOrigin(origins = "*") - @ResponseStatus(code = HttpStatus.OK) - @RequestMapping( - value = "/transactions/{id}/payments", - method = {RequestMethod.POST}, - produces = {MediaType.APPLICATION_JSON_VALUE}, - consumes = {MediaType.APPLICATION_JSON_VALUE}) - public CreateTransactionPaymentResponse createTransactionPayment( - @PathVariable(name = "id") String txnId, @RequestBody String requestBody) { - return custodyTransactionService.createPayment(txnId, requestBody); - } - - @SneakyThrows - @CrossOrigin(origins = "*") - @ResponseStatus(code = HttpStatus.OK) - @RequestMapping( - value = "/transactions/{id}/refunds", - method = {RequestMethod.POST}, - produces = {MediaType.APPLICATION_JSON_VALUE}, - consumes = {MediaType.APPLICATION_JSON_VALUE}) - public CreateTransactionPaymentResponse createTransactionRefund( - @PathVariable(name = "id") String txnId, - @RequestBody CreateTransactionRefundRequest refundRequest) { - return custodyTransactionService.createRefund(txnId, refundRequest); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyWebhookController.java b/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyWebhookController.java deleted file mode 100644 index 871d2b98f3..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/controller/custody/CustodyWebhookController.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.stellar.anchor.platform.controller.custody; - -import java.util.Map; -import lombok.AllArgsConstructor; -import lombok.SneakyThrows; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.stellar.anchor.platform.custody.CustodyEventService; - -@RestController -@AllArgsConstructor -@CrossOrigin(origins = "*") -public class CustodyWebhookController { - - private final CustodyEventService custodyEventService; - - @SneakyThrows - @CrossOrigin(origins = "*") - @ResponseStatus(code = HttpStatus.OK) - @RequestMapping( - value = "/webhook", - method = {RequestMethod.POST}, - consumes = {MediaType.APPLICATION_JSON_VALUE}, - produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity handleEvent( - @RequestBody String eventObject, @RequestHeader Map headers) { - custodyEventService.handleEvent(eventObject, headers); - return ResponseEntity.ok().build(); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/controller/platform/PlatformController.java b/platform/src/main/java/org/stellar/anchor/platform/controller/platform/PlatformController.java index bd9f23dc88..9d05ed1147 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/controller/platform/PlatformController.java +++ b/platform/src/main/java/org/stellar/anchor/platform/controller/platform/PlatformController.java @@ -12,11 +12,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -import org.stellar.anchor.api.custody.CreateTransactionPaymentResponse; import org.stellar.anchor.api.exception.AnchorException; import org.stellar.anchor.api.platform.*; import org.stellar.anchor.api.sep.SepTransactionStatus; -import org.stellar.anchor.custody.CustodyService; import org.stellar.anchor.platform.service.TransactionService; import org.stellar.anchor.util.TransactionsParams; @@ -24,11 +22,9 @@ public class PlatformController { private final TransactionService transactionService; - private final CustodyService custodyService; - PlatformController(TransactionService transactionService, CustodyService custodyService) { + PlatformController(TransactionService transactionService) { this.transactionService = transactionService; - this.custodyService = custodyService; } @Deprecated // ANCHOR-641 Use Rpc method GET_TRANSACTION instead @@ -43,19 +39,6 @@ public GetTransactionResponse getTransaction(@PathVariable(name = "id") String t return transactionService.findTransaction(txnId); } - @Deprecated // ANCHOR-641 - @CrossOrigin(origins = "*") - @RequestMapping( - value = "/transactions/{id}/payments", - method = {RequestMethod.POST}, - consumes = {MediaType.APPLICATION_JSON_VALUE}, - produces = {MediaType.APPLICATION_JSON_VALUE}) - public CreateTransactionPaymentResponse createCustodyTransactionPayment( - @PathVariable(name = "id") String txnId, @RequestBody String requestBody) - throws AnchorException { - return custodyService.createTransactionPayment(txnId, requestBody); - } - @Deprecated // ANCHOR-641 Use corresponding Rpc method to update transaction/**/ @CrossOrigin(origins = "*") @ResponseStatus(code = HttpStatus.OK) diff --git a/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyEventService.java b/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyEventService.java deleted file mode 100644 index 2aa2f2a565..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyEventService.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.stellar.anchor.platform.custody; - -import static org.stellar.anchor.api.platform.PlatformTransactionData.Kind.RECEIVE; -import static org.stellar.anchor.util.Log.warnF; - -import java.io.IOException; -import java.time.Instant; -import java.util.Map; -import org.apache.commons.lang3.StringUtils; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.api.exception.BadRequestException; -import org.stellar.anchor.api.platform.PlatformTransactionData.Kind; -import org.stellar.anchor.api.platform.PlatformTransactionData.Sep; -import org.stellar.anchor.platform.data.JdbcCustodyTransaction; -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo; - -/** - * Basic class, that contains common logic to handle custody event. It links event to custody - * transaction and pass event to appropriate handler. Event can be retrieved as webhook or as a - * result of reconciliation job - */ -public abstract class CustodyEventService { - - private final JdbcCustodyTransactionRepo custodyTransactionRepo; - private final Sep6CustodyPaymentHandler sep6CustodyPaymentHandler; - private final Sep24CustodyPaymentHandler sep24CustodyPaymentHandler; - private final Sep31CustodyPaymentHandler sep31CustodyPaymentHandler; - - protected CustodyEventService( - JdbcCustodyTransactionRepo custodyTransactionRepo, - Sep6CustodyPaymentHandler sep6CustodyPaymentHandler, - Sep24CustodyPaymentHandler sep24CustodyPaymentHandler, - Sep31CustodyPaymentHandler sep31CustodyPaymentHandler) { - this.custodyTransactionRepo = custodyTransactionRepo; - this.sep6CustodyPaymentHandler = sep6CustodyPaymentHandler; - this.sep24CustodyPaymentHandler = sep24CustodyPaymentHandler; - this.sep31CustodyPaymentHandler = sep31CustodyPaymentHandler; - } - - public abstract void handleEvent(String event, Map headers) - throws BadRequestException; - - public void handlePayment(CustodyPayment payment) throws AnchorException, IOException { - JdbcCustodyTransaction custodyTransaction = getCustodyTransaction(payment); - - if (custodyTransaction == null) { - warnF( - "Custody transaction was not found. Payment: id[{}], externalTxId[{}]", - payment.getId(), - payment.getExternalTxId()); - return; - } - - switch (Sep.from(custodyTransaction.getProtocol())) { - case SEP_6: - switch (Kind.from(custodyTransaction.getKind())) { - case DEPOSIT: - case DEPOSIT_EXCHANGE: - sep6CustodyPaymentHandler.onSent(custodyTransaction, payment); - return; - case WITHDRAWAL: - case WITHDRAWAL_EXCHANGE: - sep6CustodyPaymentHandler.onReceived(custodyTransaction, payment); - return; - } - case SEP_24: - switch (Kind.from(custodyTransaction.getKind())) { - case DEPOSIT: - sep24CustodyPaymentHandler.onSent(custodyTransaction, payment); - return; - case WITHDRAWAL: - sep24CustodyPaymentHandler.onReceived(custodyTransaction, payment); - return; - } - break; - case SEP_31: - if (RECEIVE == Kind.from(custodyTransaction.getKind())) { - sep31CustodyPaymentHandler.onReceived(custodyTransaction, payment); - return; - } - break; - } - - warnF( - "Handler for custody transaction event with protocol[{}] and kind[{}] is not found. Payment: id[{}], externalTxId[{}]", - custodyTransaction.getProtocol(), - custodyTransaction.getKind(), - payment.getId(), - payment.getExternalTxId()); - } - - private JdbcCustodyTransaction getCustodyTransaction(CustodyPayment custodyPayment) { - JdbcCustodyTransaction custodyTransaction = - custodyTransactionRepo.findByExternalTxId(custodyPayment.getExternalTxId()); - - if (custodyTransaction == null) { - custodyTransaction = - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc( - custodyPayment.getTo(), custodyPayment.getTransactionMemo()); - } - - return custodyTransaction; - } - - public void setExternalTxId(String to, String memo, String externalTxId) { - JdbcCustodyTransaction custodyTransaction = - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(to, memo); - - if (custodyTransaction != null && StringUtils.isEmpty(custodyTransaction.getExternalTxId())) { - custodyTransaction.setExternalTxId(externalTxId); - custodyTransaction.setUpdatedAt(Instant.now()); - custodyTransactionRepo.save(custodyTransaction); - } - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyPayment.java b/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyPayment.java deleted file mode 100644 index 898143a71b..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyPayment.java +++ /dev/null @@ -1,192 +0,0 @@ -package org.stellar.anchor.platform.custody; - -import static org.stellar.anchor.ledger.LedgerTransaction.*; -import static org.stellar.anchor.util.AssetHelper.fromXdrAmount; - -import com.google.gson.annotations.SerializedName; -import java.time.Instant; -import lombok.Builder; -import lombok.Data; -import org.stellar.anchor.api.exception.SepException; -import org.stellar.anchor.ledger.LedgerTransaction; -import org.stellar.anchor.util.AssetHelper; -import org.stellar.anchor.util.MemoHelper; -import org.stellar.sdk.Memo; - -/** - * A class, that contains payment (inbound/outbound) information for custody transaction. It is an - * abstract representation of payment and is not custody-specific - */ -@Data -@Builder -public class CustodyPayment { - - String id; - String externalTxId; - Type type; - - String from; - String to; - - String amount; - String assetType; - String assetCode; - String assetIssuer; - String assetName; - - Instant updatedAt; - CustodyPaymentStatus status; - String message; - - String transactionHash; - String transactionMemo; - String transactionMemoType; - String transactionEnvelope; - - public static CustodyPayment fromPayment( - LedgerTransaction ledgerTxn, - LedgerPaymentOperation paymentOperation, - String externalTxId, - Instant updatedAt, - CustodyPaymentStatus status, - String message, - String transactionHash) - throws SepException { - String id = null; - String from = null; - String to = null; - String assetCode = null; - String assetIssuer = null; - String amount = null; - String assetType = null; - String assetName = null; - String transactionMemo = null; - String transactionMemoType = null; - String transactionEnvelope = null; - - if (paymentOperation != null) { - id = paymentOperation.getId(); - to = paymentOperation.getTo(); - amount = fromXdrAmount(paymentOperation.getAmount()); - assetType = AssetHelper.getAssetType(paymentOperation.getAsset()); - assetName = AssetHelper.getSep11AssetName(paymentOperation.getAsset()); - assetCode = AssetHelper.getAssetCode(assetName); - assetIssuer = AssetHelper.getAssetIssuer(assetName); - - String sourceAccount = ledgerTxn.getSourceAccount(); - from = paymentOperation.getFrom() != null ? paymentOperation.getFrom() : sourceAccount; - Memo memo = Memo.fromXdr(ledgerTxn.getMemo()); - - transactionMemo = MemoHelper.memoAsString(memo); - transactionMemoType = MemoHelper.memoTypeAsString(memo); - transactionEnvelope = ledgerTxn.getEnvelopeXdr(); - } - - return CustodyPayment.builder() - .id(id) - .externalTxId(externalTxId) - .type(Type.PAYMENT) - .from(from) - .to(to) - .amount(amount) - .assetType(assetType) - .assetCode(assetCode) - .assetIssuer(assetIssuer) - .assetName(assetName) - .updatedAt(updatedAt) - .status(status) - .message(message) - .transactionHash(transactionHash) - .transactionMemo(transactionMemo) - .transactionMemoType(transactionMemoType) - .transactionEnvelope(transactionEnvelope) - .build(); - } - - public static CustodyPayment fromPathPayment( - LedgerTransaction ledgerTxn, - LedgerPathPaymentOperation operation, - String externalTxId, - Instant updatedAt, - CustodyPaymentStatus status, - String message, - String transactionHash) - throws SepException { - String id = null; - String from = null; - String to = null; - String assetCode = null; - String assetIssuer = null; - String amount = null; - String assetType = null; - String assetName = null; - String transactionMemo = null; - String transactionMemoType = null; - String transactionEnvelope = null; - - if (operation != null) { - id = operation.getId(); - to = operation.getTo(); - amount = fromXdrAmount(operation.getAmount()); - assetType = AssetHelper.getAssetType(operation.getAsset()); - assetName = AssetHelper.getSep11AssetName(operation.getAsset()); - assetCode = AssetHelper.getAssetCode(assetName); - assetIssuer = AssetHelper.getAssetIssuer(assetName); - - String sourceAccount = - operation.getSourceAccount() != null - ? operation.getSourceAccount() - : ledgerTxn.getSourceAccount(); - from = operation.getFrom() != null ? operation.getFrom() : sourceAccount; - Memo memo = Memo.fromXdr(ledgerTxn.getMemo()); - - transactionMemo = MemoHelper.memoAsString(memo); - transactionMemoType = MemoHelper.memoTypeAsString(memo); - transactionEnvelope = ledgerTxn.getEnvelopeXdr(); - } - - return CustodyPayment.builder() - .id(id) - .externalTxId(externalTxId) - .type(Type.PATH_PAYMENT) - .from(from) - .to(to) - .amount(amount) - .assetType(assetType) - .assetCode(assetCode) - .assetIssuer(assetIssuer) - .assetName(assetName) - .updatedAt(updatedAt) - .status(status) - .message(message) - .transactionHash(transactionHash) - .transactionMemo(transactionMemo) - .transactionMemoType(transactionMemoType) - .transactionEnvelope(transactionEnvelope) - .build(); - } - - public enum CustodyPaymentStatus { - SUCCESS, - ERROR - } - - public enum Type { - @SerializedName("payment") - PAYMENT("payment"), - - @SerializedName("path_payment") - PATH_PAYMENT("path_payment"); - - private final String name; - - Type(String name) { - this.name = name; - } - - @Override - public String toString() { - return name; - } - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyPaymentHandler.java b/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyPaymentHandler.java deleted file mode 100644 index fced2887f3..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyPaymentHandler.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.stellar.anchor.platform.custody; - -import static org.stellar.anchor.util.Log.debugF; -import static org.stellar.anchor.util.Log.warnF; -import static org.stellar.anchor.util.MathHelper.decimal; -import static org.stellar.anchor.util.MathHelper.formatAmount; - -import java.io.IOException; -import java.math.BigDecimal; -import java.util.Set; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.platform.data.CustodyTransactionStatus; -import org.stellar.anchor.platform.data.JdbcCustodyTransaction; -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo; - -/** - * Abstract custody payment handler. Contains common logic for payment validation, custody and SEP - * transaction update - */ -public abstract class CustodyPaymentHandler { - - public static final String STELLAR_ASSET_PREFIX = "stellar:"; - private static final Set SUPPORTED_ASSET_TYPES = - Set.of("credit_alphanum4", "credit_alphanum12", "native"); - - private final JdbcCustodyTransactionRepo custodyTransactionRepo; - - public CustodyPaymentHandler(JdbcCustodyTransactionRepo custodyTransactionRepo) { - this.custodyTransactionRepo = custodyTransactionRepo; - } - - /** - * Handle inbound(withdrawal) payment - * - * @param txn custody transaction - * @param payment inbound custody payment - * @throws AnchorException if error happens during SEP transaction update - * @throws IOException if error happens during SEP transaction update - */ - public abstract void onReceived(JdbcCustodyTransaction txn, CustodyPayment payment) - throws AnchorException, IOException; - - /** - * Handle outbound(deposit) payment - * - * @param txn custody transaction - * @param payment outbound custody payment - * @throws AnchorException if error happens during SEP transaction update - * @throws IOException if error happens during SEP transaction update - */ - public abstract void onSent(JdbcCustodyTransaction txn, CustodyPayment payment) - throws AnchorException, IOException; - - protected void updateTransaction(JdbcCustodyTransaction txn, CustodyPayment payment) { - txn.setUpdatedAt(payment.getUpdatedAt()); - txn.setFromAccount(payment.getFrom()); - txn.setExternalTxId(payment.getExternalTxId()); - txn.setStatus(getCustodyTransactionStatus(payment.getStatus()).toString()); - custodyTransactionRepo.save(txn); - } - - protected void validatePayment(JdbcCustodyTransaction txn, CustodyPayment payment) { - if (CustodyPayment.CustodyPaymentStatus.SUCCESS != payment.getStatus()) { - return; - } - - if (!SUPPORTED_ASSET_TYPES.contains(payment.getAssetType())) { - debugF( - "Unsupported asset type[{}]. Payment: id[{}], externalTxId[{}]", - payment.getAssetType(), - payment.getId(), - payment.getExternalTxId()); - } - - String paymentAssetName = STELLAR_ASSET_PREFIX + payment.getAssetName(); - if (!txn.getAsset().equals(paymentAssetName)) { - warnF( - "Incoming payment asset[{}] does not match the expected asset[{}]. Payment: id[{}], " - + "externalTxId[{}] ", - payment.getAssetName(), - txn.getAsset(), - payment.getId(), - payment.getExternalTxId()); - } - - // Check if the payment contains the expected amount (or greater) - BigDecimal expectedAmount = decimal(txn.getAmount()); - BigDecimal gotAmount = decimal(payment.getAmount()); - - if (gotAmount.compareTo(expectedAmount) < 0) { - warnF( - "The incoming payment amount was insufficient. Expected: [{}], Received: [{}]. Payment: id[{}], externalTxId[{}]", - formatAmount(expectedAmount), - formatAmount(gotAmount), - payment.getId(), - payment.getExternalTxId()); - } - } - - protected CustodyTransactionStatus getCustodyTransactionStatus( - CustodyPayment.CustodyPaymentStatus custodyPaymentStatus) { - switch (custodyPaymentStatus) { - case SUCCESS: - return CustodyTransactionStatus.COMPLETED; - case ERROR: - return CustodyTransactionStatus.FAILED; - default: - throw new RuntimeException( - String.format("Unsupported custody transaction status[%s]", custodyPaymentStatus)); - } - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyPaymentService.java b/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyPaymentService.java deleted file mode 100644 index 84809ddc70..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyPaymentService.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.stellar.anchor.platform.custody; - -import java.time.Instant; -import java.util.List; -import org.stellar.anchor.api.custody.CreateTransactionPaymentResponse; -import org.stellar.anchor.api.custody.GenerateDepositAddressResponse; -import org.stellar.anchor.api.exception.CustodyException; -import org.stellar.anchor.api.exception.InvalidConfigException; -import org.stellar.anchor.platform.data.JdbcCustodyTransaction; - -/** - * Abstract payment service. The specific implementation will be used based on selected custody - * service - */ -public interface CustodyPaymentService { - - /** - * Generates deposit address and memo for outbound payment - * - * @param assetId Stellar asset code - * @return generated address and memo - * @throws CustodyException if an error happens on custody service - * @throws InvalidConfigException if the Stellar asset code doesn't have a mapping to the custody - * asset code - */ - GenerateDepositAddressResponse generateDepositAddress(String assetId) - throws CustodyException, InvalidConfigException; - - /** - * Submits outbound transaction payment - * - * @param custodyTxn custody transaction - * @param requestBody additional data, that will be sent in a request to custody service. Can be - * used, if, for example, custody service supports some fields in a request specific to it - * @return external transaction payment ID - * @throws CustodyException if an error happens on custody service - * @throws InvalidConfigException if the Stellar asset code doesn't have a mapping to the custody - * asset code - */ - CreateTransactionPaymentResponse createTransactionPayment( - JdbcCustodyTransaction custodyTxn, String requestBody) - throws CustodyException, InvalidConfigException; - - /** - * Get custody transaction by id - * - * @param txnId custody transaction ID - * @return custody transaction - * @throws CustodyException if an error happens on custody service - */ - T getTransactionById(String txnId) throws CustodyException; - - /** - * Get custody transactions within time range - * - * @param startTime start from time - * @param endTime to time - * @return list of custody transactions - * @throws CustodyException if an error happens on custody service - */ - List getTransactionsByTimeRange(Instant startTime, Instant endTime) throws CustodyException; -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyTransactionService.java b/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyTransactionService.java deleted file mode 100644 index 3806b0f3a2..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/custody/CustodyTransactionService.java +++ /dev/null @@ -1,182 +0,0 @@ -package org.stellar.anchor.platform.custody; - -import static org.stellar.anchor.api.platform.PlatformTransactionData.Kind.RECEIVE; -import static org.stellar.anchor.api.platform.PlatformTransactionData.Kind.WITHDRAWAL; -import static org.stellar.anchor.util.Log.debugF; - -import java.time.Instant; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpStatus; -import org.stellar.anchor.api.custody.CreateCustodyTransactionRequest; -import org.stellar.anchor.api.custody.CreateTransactionPaymentResponse; -import org.stellar.anchor.api.custody.CreateTransactionRefundRequest; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.api.exception.FireblocksException; -import org.stellar.anchor.api.exception.custody.CustodyBadRequestException; -import org.stellar.anchor.api.exception.custody.CustodyNotFoundException; -import org.stellar.anchor.api.exception.custody.CustodyServiceUnavailableException; -import org.stellar.anchor.api.exception.custody.CustodyTooManyRequestsException; -import org.stellar.anchor.platform.data.CustodyTransactionStatus; -import org.stellar.anchor.platform.data.JdbcCustodyTransaction; -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo; - -public class CustodyTransactionService { - - private final CustodyPaymentService custodyPaymentService; - private final JdbcCustodyTransactionRepo custodyTransactionRepo; - - public CustodyTransactionService( - JdbcCustodyTransactionRepo custodyTransactionRepo, - CustodyPaymentService custodyPaymentService) { - this.custodyTransactionRepo = custodyTransactionRepo; - this.custodyPaymentService = custodyPaymentService; - } - - /** - * Create custody transaction - * - * @param request custody transaction info - * @return {@link JdbcCustodyTransaction} object - */ - public JdbcCustodyTransaction create( - CreateCustodyTransactionRequest request, JdbcCustodyTransaction.PaymentType type) - throws CustodyBadRequestException { - return custodyTransactionRepo.save( - JdbcCustodyTransaction.builder() - .id(UUID.randomUUID().toString()) - .sepTxId(request.getId()) - .status(CustodyTransactionStatus.CREATED.toString()) - .createdAt(Instant.now()) - .memo(request.getMemo()) - .memoType(request.getMemoType()) - .protocol(request.getProtocol()) - .fromAccount(request.getFromAccount()) - .toAccount(request.getToAccount()) - .amount(request.getAmount()) - .amountFee(request.getAmountFee()) - .asset(request.getAsset()) - .kind(request.getKind()) - .reconciliationAttemptCount(0) - .type(type.getType()) - .build()); - } - - /** - * Create custody transaction payment. This method acts like a proxy. It forwards request to - * custody payment service, update custody transaction and handles errors - * - * @param txnId custody/SEP transaction ID - * @param requestBody additional data, that will be sent in a request to custody service. Can be - * used, if, for example, custody service supports some fields in a request specific to it - * @return external transaction payment ID - */ - public CreateTransactionPaymentResponse createPayment(String txnId, String requestBody) - throws AnchorException { - JdbcCustodyTransaction txn = - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc( - txnId, JdbcCustodyTransaction.PaymentType.PAYMENT.getType()); - if (txn == null) { - throw new CustodyNotFoundException(String.format("Transaction (id=%s) is not found", txnId)); - } - - CreateTransactionPaymentResponse response; - try { - response = custodyPaymentService.createTransactionPayment(txn, requestBody); - updateCustodyTransaction(txn, response.getId(), CustodyTransactionStatus.SUBMITTED); - } catch (FireblocksException e) { - updateCustodyTransaction(txn, StringUtils.EMPTY, CustodyTransactionStatus.FAILED); - throw (getResponseException(e)); - } - - return response; - } - - /** - * Create custody transaction refund. This method acts like a proxy. It forwards request to - * custody payment service, updates custody transaction and handles errors - * - * @param txnId custody/SEP transaction ID - * @param refundRequest {@link CreateTransactionRefundRequest} object - * @return external transaction payment ID - */ - public CreateTransactionPaymentResponse createRefund( - String txnId, CreateTransactionRefundRequest refundRequest) throws AnchorException { - JdbcCustodyTransaction txn = - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc( - txnId, JdbcCustodyTransaction.PaymentType.PAYMENT.getType()); - if (txn == null) { - throw new CustodyNotFoundException(String.format("Transaction (id=%s) is not found", txnId)); - } - - JdbcCustodyTransaction refundTxn = createTransactionRefundRecord(txn, refundRequest); - - CreateTransactionPaymentResponse response; - try { - response = custodyPaymentService.createTransactionPayment(refundTxn, null); - updateCustodyTransaction(refundTxn, response.getId(), CustodyTransactionStatus.SUBMITTED); - } catch (FireblocksException e) { - custodyTransactionRepo.deleteById(refundTxn.getId()); - throw (getResponseException(e)); - } - - return response; - } - - private JdbcCustodyTransaction createTransactionRefundRecord( - JdbcCustodyTransaction txn, CreateTransactionRefundRequest refundRequest) - throws CustodyBadRequestException { - return create( - CreateCustodyTransactionRequest.builder() - .id(txn.getSepTxId()) - .memo(refundRequest.getMemo()) - .memoType(refundRequest.getMemoType()) - .protocol(txn.getProtocol()) - .toAccount(txn.getFromAccount()) - .amount(refundRequest.getAmount()) - .amountFee(refundRequest.getAmountFee()) - .asset(txn.getAsset()) - .kind(txn.getKind()) - .build(), - JdbcCustodyTransaction.PaymentType.REFUND); - } - - private void updateCustodyTransaction( - JdbcCustodyTransaction txn, String externalTransactionId, CustodyTransactionStatus status) { - txn.setExternalTxId(externalTransactionId); - txn.setStatus(status.toString()); - updateCustodyTransaction(txn); - } - - public void updateCustodyTransaction(JdbcCustodyTransaction txn) { - txn.setUpdatedAt(Instant.now()); - custodyTransactionRepo.save(txn); - } - - public List getOutboundTransactionsEligibleForReconciliation() { - return custodyTransactionRepo.findAllByStatusAndExternalTxIdNotNull( - CustodyTransactionStatus.SUBMITTED.toString()); - } - - public List getInboundTransactionsEligibleForReconciliation() { - return custodyTransactionRepo.findAllByStatusAndKindIn( - CustodyTransactionStatus.CREATED.toString(), - Set.of(RECEIVE.getKind(), WITHDRAWAL.getKind())); - } - - private AnchorException getResponseException(FireblocksException e) { - switch (HttpStatus.valueOf(e.getStatusCode())) { - case TOO_MANY_REQUESTS: - return new CustodyTooManyRequestsException(e.getRawMessage()); - case BAD_REQUEST: - return new CustodyBadRequestException(e.getRawMessage()); - case SERVICE_UNAVAILABLE: - return new CustodyServiceUnavailableException(e.getRawMessage()); - default: - debugF("Unhandled status code (%s)", e.getStatusCode()); - return e; - } - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/custody/Sep24CustodyPaymentHandler.java b/platform/src/main/java/org/stellar/anchor/platform/custody/Sep24CustodyPaymentHandler.java deleted file mode 100644 index c16b972d64..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/custody/Sep24CustodyPaymentHandler.java +++ /dev/null @@ -1,106 +0,0 @@ -package org.stellar.anchor.platform.custody; - -import static org.stellar.anchor.util.Log.infoF; - -import java.io.IOException; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.apiclient.PlatformApiClient; -import org.stellar.anchor.metrics.MetricsService; -import org.stellar.anchor.platform.config.RpcConfig; -import org.stellar.anchor.platform.data.CustodyTransactionStatus; -import org.stellar.anchor.platform.data.JdbcCustodyTransaction; -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo; -import org.stellar.anchor.platform.service.AnchorMetrics; - -/** Custody payment handler for SEP24 transactions */ -public class Sep24CustodyPaymentHandler extends CustodyPaymentHandler { - - private final PlatformApiClient platformApiClient; - private final RpcConfig rpcConfig; - private final MetricsService metricsService; - - public Sep24CustodyPaymentHandler( - JdbcCustodyTransactionRepo custodyTransactionRepo, - PlatformApiClient platformApiClient, - RpcConfig rpcConfig, - MetricsService metricsService) { - super(custodyTransactionRepo); - this.platformApiClient = platformApiClient; - this.rpcConfig = rpcConfig; - this.metricsService = metricsService; - } - - /** - * @see CustodyPaymentHandler#onReceived(JdbcCustodyTransaction, CustodyPayment) - */ - @Override - public void onReceived(JdbcCustodyTransaction txn, CustodyPayment payment) - throws AnchorException, IOException { - infoF( - "Incoming inbound payment for SEP-24 transaction. Payment: id[{}], externalTxId[{}], type[{}]", - payment.getId(), - payment.getExternalTxId(), - txn.getType()); - - validatePayment(txn, payment); - updateTransaction(txn, payment); - - if (CustodyTransactionStatus.FAILED == CustodyTransactionStatus.from(txn.getStatus())) { - platformApiClient.notifyTransactionError( - txn.getSepTxId(), rpcConfig.getCustomMessages().getCustodyTransactionFailed()); - } else { - switch (JdbcCustodyTransaction.PaymentType.from(txn.getType())) { - case PAYMENT: - platformApiClient.notifyOnchainFundsReceived( - txn.getSepTxId(), - payment.getTransactionHash(), - payment.getAmount(), - rpcConfig.getCustomMessages().getIncomingPaymentReceived()); - - metricsService - .counter(AnchorMetrics.PAYMENT_RECEIVED, "asset", payment.getAssetName()) - .increment(Double.parseDouble(payment.getAmount())); - - break; - case REFUND: - platformApiClient.notifyRefundSent( - txn.getSepTxId(), - payment.getTransactionHash(), - payment.getAmount(), - txn.getAmountFee(), - txn.getAsset()); - break; - } - } - } - - /** - * @see CustodyPaymentHandler#onSent(JdbcCustodyTransaction, CustodyPayment) - */ - @Override - public void onSent(JdbcCustodyTransaction txn, CustodyPayment payment) - throws AnchorException, IOException { - infoF( - "Incoming outbound payment for SEP-24 transaction. Payment: id[{}], externalTxId[{}], type[{}]", - payment.getId(), - payment.getExternalTxId(), - txn.getType()); - - validatePayment(txn, payment); - updateTransaction(txn, payment); - - if (CustodyTransactionStatus.FAILED == CustodyTransactionStatus.from(txn.getStatus())) { - platformApiClient.notifyTransactionError( - txn.getSepTxId(), rpcConfig.getCustomMessages().getCustodyTransactionFailed()); - } else { - platformApiClient.notifyOnchainFundsSent( - txn.getSepTxId(), - payment.getTransactionHash(), - rpcConfig.getCustomMessages().getOutgoingPaymentSent()); - - metricsService - .counter(AnchorMetrics.PAYMENT_SENT, "asset", payment.getAssetName()) - .increment(Double.parseDouble(payment.getAmount())); - } - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/custody/Sep31CustodyPaymentHandler.java b/platform/src/main/java/org/stellar/anchor/platform/custody/Sep31CustodyPaymentHandler.java deleted file mode 100644 index 268814e0e6..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/custody/Sep31CustodyPaymentHandler.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.stellar.anchor.platform.custody; - -import static org.stellar.anchor.util.Log.infoF; -import static org.stellar.anchor.util.Log.warn; - -import java.io.IOException; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.apiclient.PlatformApiClient; -import org.stellar.anchor.metrics.MetricsService; -import org.stellar.anchor.platform.config.RpcConfig; -import org.stellar.anchor.platform.data.CustodyTransactionStatus; -import org.stellar.anchor.platform.data.JdbcCustodyTransaction; -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo; -import org.stellar.anchor.platform.service.AnchorMetrics; - -/** Custody payment handler for SEP31 transactions */ -public class Sep31CustodyPaymentHandler extends CustodyPaymentHandler { - - private final PlatformApiClient platformApiClient; - private final RpcConfig rpcConfig; - private final MetricsService metricsService; - - public Sep31CustodyPaymentHandler( - JdbcCustodyTransactionRepo custodyTransactionRepo, - PlatformApiClient platformApiClient, - RpcConfig rpcConfig, - MetricsService metricsService) { - super(custodyTransactionRepo); - this.platformApiClient = platformApiClient; - this.rpcConfig = rpcConfig; - this.metricsService = metricsService; - } - - @Override - public void onReceived(JdbcCustodyTransaction txn, CustodyPayment payment) - throws AnchorException, IOException { - infoF( - "Incoming inbound payment for SEP-31 transaction. Payment: id[{}], externalTxId[{}], type[{}]", - payment.getId(), - payment.getExternalTxId(), - txn.getType()); - - validatePayment(txn, payment); - updateTransaction(txn, payment); - - if (CustodyTransactionStatus.FAILED == CustodyTransactionStatus.from(txn.getStatus())) { - platformApiClient.notifyTransactionError( - txn.getSepTxId(), rpcConfig.getCustomMessages().getCustodyTransactionFailed()); - } else { - switch (JdbcCustodyTransaction.PaymentType.from(txn.getType())) { - case PAYMENT: - platformApiClient.notifyOnchainFundsReceived( - txn.getSepTxId(), - payment.getTransactionHash(), - payment.getAmount(), - rpcConfig.getCustomMessages().getIncomingPaymentReceived()); - - metricsService - .counter(AnchorMetrics.PAYMENT_RECEIVED, "asset", payment.getAssetName()) - .increment(Double.parseDouble(payment.getAmount())); - - break; - case REFUND: - platformApiClient.notifyRefundSent( - txn.getSepTxId(), - payment.getTransactionHash(), - payment.getAmount(), - txn.getAmountFee(), - txn.getAsset()); - break; - } - } - } - - @Override - public void onSent(JdbcCustodyTransaction tx, CustodyPayment payment) { - warn("Outbound payments are not implemented for SEP31"); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/custody/Sep6CustodyPaymentHandler.java b/platform/src/main/java/org/stellar/anchor/platform/custody/Sep6CustodyPaymentHandler.java deleted file mode 100644 index 644c73e9b3..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/custody/Sep6CustodyPaymentHandler.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.stellar.anchor.platform.custody; - -import static org.stellar.anchor.util.Log.infoF; - -import java.io.IOException; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.apiclient.PlatformApiClient; -import org.stellar.anchor.metrics.MetricsService; -import org.stellar.anchor.platform.config.RpcConfig; -import org.stellar.anchor.platform.data.CustodyTransactionStatus; -import org.stellar.anchor.platform.data.JdbcCustodyTransaction; -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo; -import org.stellar.anchor.platform.service.AnchorMetrics; - -/** Custody payment handler for SEP6 transactions */ -public class Sep6CustodyPaymentHandler extends CustodyPaymentHandler { - private final PlatformApiClient platformApiClient; - private final RpcConfig rpcConfig; - private final MetricsService metricsService; - - public Sep6CustodyPaymentHandler( - JdbcCustodyTransactionRepo custodyTransactionRepo, - PlatformApiClient platformApiClient, - RpcConfig rpcConfig, - MetricsService metricsService) { - super(custodyTransactionRepo); - this.platformApiClient = platformApiClient; - this.rpcConfig = rpcConfig; - this.metricsService = metricsService; - } - - @Override - public void onReceived(JdbcCustodyTransaction txn, CustodyPayment payment) - throws AnchorException, IOException { - infoF( - "Incoming inbound payment for SEP-6 transaction. Payment: id[{}], externalTxId[{}], type[{}]", - payment.getId(), - payment.getExternalTxId(), - txn.getType()); - - validatePayment(txn, payment); - updateTransaction(txn, payment); - - if (CustodyTransactionStatus.FAILED == CustodyTransactionStatus.from(txn.getStatus())) { - platformApiClient.notifyTransactionError( - txn.getSepTxId(), rpcConfig.getCustomMessages().getCustodyTransactionFailed()); - } else { - switch (JdbcCustodyTransaction.PaymentType.from(txn.getType())) { - case PAYMENT: - platformApiClient.notifyOnchainFundsReceived( - txn.getSepTxId(), - payment.getTransactionHash(), - payment.getAmount(), - rpcConfig.getCustomMessages().getIncomingPaymentReceived()); - - metricsService - .counter(AnchorMetrics.PAYMENT_RECEIVED, "asset", payment.getAssetName()) - .increment(Double.parseDouble(payment.getAmount())); - case REFUND: - platformApiClient.notifyRefundSent( - txn.getSepTxId(), - payment.getTransactionHash(), - payment.getAmount(), - txn.getAmountFee(), - txn.getAsset()); - break; - } - } - } - - @Override - public void onSent(JdbcCustodyTransaction txn, CustodyPayment payment) - throws AnchorException, IOException { - infoF( - "Incoming outbound payment for SEP-6 transaction. Payment: id[{}], externalTxId[{}], type[{}]", - payment.getId(), - payment.getExternalTxId(), - txn.getType()); - - validatePayment(txn, payment); - updateTransaction(txn, payment); - - if (CustodyTransactionStatus.FAILED == CustodyTransactionStatus.from(txn.getStatus())) { - platformApiClient.notifyTransactionError( - txn.getSepTxId(), rpcConfig.getCustomMessages().getCustodyTransactionFailed()); - } else { - platformApiClient.notifyOnchainFundsSent( - txn.getSepTxId(), - payment.getTransactionHash(), - rpcConfig.getCustomMessages().getOutgoingPaymentSent()); - - metricsService - .counter(AnchorMetrics.PAYMENT_SENT, "asset", payment.getAssetName()) - .increment(Double.parseDouble(payment.getAmount())); - } - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/custody/fireblocks/FireblocksApiClient.java b/platform/src/main/java/org/stellar/anchor/platform/custody/fireblocks/FireblocksApiClient.java deleted file mode 100644 index 23671a6d5f..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/custody/fireblocks/FireblocksApiClient.java +++ /dev/null @@ -1,145 +0,0 @@ -package org.stellar.anchor.platform.custody.fireblocks; - -import static org.stellar.anchor.util.JwtUtil.jwtsBuilder; -import static org.stellar.anchor.util.OkHttpUtil.TYPE_JSON; - -import io.jsonwebtoken.SignatureAlgorithm; -import jakarta.validation.constraints.NotNull; -import java.io.IOException; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.time.Instant; -import java.util.Date; -import java.util.Map; -import java.util.UUID; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpStatus; -import org.springframework.web.util.UriBuilder; -import org.springframework.web.util.UriComponentsBuilder; -import org.stellar.anchor.api.exception.FireblocksException; -import org.stellar.anchor.api.exception.InvalidConfigException; -import org.stellar.anchor.platform.config.FireblocksConfig; - -/** - * API client, that is responsible for communication with Fireblocks. It generates and adds JWT - * token to the request and validates the response status code - */ -public class FireblocksApiClient { - - private static final String API_KEY_HEADER_NAME = "X-API-Key"; - private static final String AUTHORIZATION_HEADER_NAME = "Authorization"; - private static final String TOKEN_PREFIX = "Bearer "; - private static final int TOKEN_EXPIRATION_SECONDS = 55; - private static final String SHA256_ALGORITHM = "SHA-256"; - - private final OkHttpClient client; - private final String baseUrl; - private final String apiKey; - - private final PrivateKey privateKey; - - public FireblocksApiClient(OkHttpClient httpClient, FireblocksConfig fireblocksConfig) - throws InvalidConfigException { - this.client = httpClient; - this.baseUrl = fireblocksConfig.getBaseUrl(); - this.apiKey = fireblocksConfig.getSecretConfig().getFireblocksApiKey(); - this.privateKey = fireblocksConfig.getFireblocksPrivateKey(); - } - - public String get(String path) throws FireblocksException { - return get(path, Map.of()); - } - - public String get(String path, Map queryParams) throws FireblocksException { - path = buildPath(path, queryParams); - return doRequest( - new Request.Builder() - .url(baseUrl + path) - .addHeader(API_KEY_HEADER_NAME, apiKey) - .addHeader(AUTHORIZATION_HEADER_NAME, TOKEN_PREFIX + signJwt(path, StringUtils.EMPTY)) - .build()); - } - - public String post(String path, String data) throws FireblocksException { - return doRequest( - new Request.Builder() - .url(baseUrl + path) - .addHeader(API_KEY_HEADER_NAME, apiKey) - .addHeader(AUTHORIZATION_HEADER_NAME, TOKEN_PREFIX + signJwt(path, data)) - .post(RequestBody.create(data.getBytes(), TYPE_JSON)) - .build()); - } - - private String doRequest(Request request) throws FireblocksException { - try (Response response = client.newCall(request).execute()) { - ResponseBody responseBody = response.body(); - String responseBodyJson = null; - - if (responseBody != null) { - responseBodyJson = responseBody.string(); - } - - if (HttpStatus.valueOf(response.code()).is2xxSuccessful()) { - return responseBodyJson; - } else { - throw new FireblocksException(responseBodyJson, response.code()); - } - } catch (IOException e) { - throw new FireblocksException(e); - } - } - - private String signJwt(String path, String dataJSONString) { - String bodyHash; - - try { - MessageDigest digest = MessageDigest.getInstance(SHA256_ALGORITHM); - digest.update(dataJSONString.getBytes()); - BigInteger number = new BigInteger(1, digest.digest()); - StringBuilder hexString = new StringBuilder(number.toString(16)); - - while (hexString.length() < 64) { - hexString.insert(0, '0'); - } - - bodyHash = hexString.toString(); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException("Unable to generate body hash for Fireblocks JWT token", e); - } - - Instant now = Instant.now(); - Instant expirationTime = now.plusSeconds(TOKEN_EXPIRATION_SECONDS); - - return jwtsBuilder() - .setSubject(apiKey) - .setIssuedAt(Date.from(now)) - .setExpiration(Date.from(expirationTime)) - .claim("nonce", UUID.randomUUID().toString()) - .claim("uri", path) - .claim("bodyHash", bodyHash) - .signWith(SignatureAlgorithm.RS256, privateKey) - .compact(); - } - - private String buildPath(@NotNull String path, Map params) { - if (params.isEmpty()) { - return path; - } - - UriBuilder builder = UriComponentsBuilder.newInstance(); - builder.path(path); - - for (Map.Entry param : params.entrySet()) { - builder.queryParam(param.getKey(), param.getValue()); - } - - return builder.build().toString(); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/custody/fireblocks/FireblocksEventService.java b/platform/src/main/java/org/stellar/anchor/platform/custody/fireblocks/FireblocksEventService.java deleted file mode 100644 index e83399d609..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/custody/fireblocks/FireblocksEventService.java +++ /dev/null @@ -1,192 +0,0 @@ -package org.stellar.anchor.platform.custody.fireblocks; - -import static org.stellar.anchor.util.Log.*; -import static org.stellar.anchor.util.StringHelper.isEmpty; -import static org.stellar.sdk.xdr.OperationType.*; - -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.security.SignatureException; -import java.time.Instant; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import lombok.SneakyThrows; -import org.stellar.anchor.api.custody.fireblocks.FireblocksEventObject; -import org.stellar.anchor.api.custody.fireblocks.TransactionDetails; -import org.stellar.anchor.api.exception.*; -import org.stellar.anchor.ledger.LedgerClient; -import org.stellar.anchor.ledger.LedgerTransaction; -import org.stellar.anchor.platform.config.FireblocksConfig; -import org.stellar.anchor.platform.custody.*; -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo; -import org.stellar.anchor.util.GsonUtils; -import org.stellar.anchor.util.RSAUtil; -import org.stellar.sdk.xdr.OperationType; - -/** Service, that is responsible for handling Fireblocks events */ -public class FireblocksEventService extends CustodyEventService { - - public static final String FIREBLOCKS_SIGNATURE_HEADER = "fireblocks-signature"; - private static final Set PAYMENT_TRANSACTION_OPERATION_TYPES = - Set.of(PAYMENT, PATH_PAYMENT_STRICT_SEND, PATH_PAYMENT_STRICT_RECEIVE); - - private final LedgerClient ledgerClient; - private final PublicKey publicKey; - - public FireblocksEventService( - JdbcCustodyTransactionRepo custodyTransactionRepo, - Sep6CustodyPaymentHandler sep6CustodyPaymentHandler, - Sep24CustodyPaymentHandler sep24CustodyPaymentHandler, - Sep31CustodyPaymentHandler sep31CustodyPaymentHandler, - LedgerClient ledgerClient, - FireblocksConfig fireblocksConfig) - throws InvalidConfigException { - super( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler); - this.ledgerClient = ledgerClient; - this.publicKey = fireblocksConfig.getFireblocksPublicKey(); - } - - /** - * Process request sent by Fireblocks to webhook endpoint - * - * @param event Request body - * @param headers HTTP headers - * @throws BadRequestException when fireblocks-signature is missing, empty or contains invalid - * signature - */ - @Override - public void handleEvent(String event, Map headers) throws BadRequestException { - String signature = headers.get(FIREBLOCKS_SIGNATURE_HEADER); - if (signature == null) { - throw new BadRequestException("'" + FIREBLOCKS_SIGNATURE_HEADER + "' header missed"); - } - - if (isEmpty(signature)) { - throw new BadRequestException("'" + FIREBLOCKS_SIGNATURE_HEADER + "' is empty"); - } - - debugF("Fireblocks /webhook endpoint called with signature '{}'", signature); - debugF("Fireblocks /webhook endpoint called with data '{}'", event); - - try { - if (RSAUtil.isValidSignature(signature, event, publicKey)) { - FireblocksEventObject fireblocksEventObject = - GsonUtils.getInstance().fromJson(event, FireblocksEventObject.class); - - TransactionDetails transactionDetails = fireblocksEventObject.getData(); - setExternalTxId( - transactionDetails.getDestinationAddress(), - transactionDetails.getDestinationTag(), - transactionDetails.getId()); - - if (!transactionDetails.getStatus().isObservableByWebhook()) { - debugF("Skipping Fireblocks webhook event[{}] due to the status", event); - return; - } - - try { - Optional payment = convert(transactionDetails); - if (payment.isPresent()) { - handlePayment(payment.get()); - } - } catch (AnchorException | IOException e) { - throw new BadRequestException("Unable to handle Fireblocks webhook event", e); - } - } else { - error("Fireblocks webhook event signature is invalid"); - } - } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) { - errorEx("Fireblocks webhook event signature validation failed", e); - } - } - - @SneakyThrows - public Optional convert(TransactionDetails td) throws IOException { - LedgerTransaction.LedgerOperation ledgerOperation = null; - CustodyPayment.CustodyPaymentStatus status = - td.getStatus().isCompleted() - ? CustodyPayment.CustodyPaymentStatus.SUCCESS - : CustodyPayment.CustodyPaymentStatus.ERROR; - String message = null; - - if (CustodyPayment.CustodyPaymentStatus.ERROR == status && td.getSubStatus() != null) { - message = td.getSubStatus().name(); - } - - LedgerTransaction ledgerTxn = null; - - try { - ledgerTxn = ledgerClient.getTransaction(td.getTxHash()); - if (ledgerTxn == null) - throw new Exception(String.format("Transaction(hash=%s) not found", td.getTxHash())); - - Optional op = - ledgerTxn.getOperations().stream() - .filter(it -> PAYMENT_TRANSACTION_OPERATION_TYPES.contains(it.getType())) - .findFirst(); - - if (op.isEmpty()) { - return Optional.empty(); - } - - ledgerOperation = op.get(); - } catch (Exception e) { - warnF( - "Unable to find Stellar transaction for Fireblocks event. Id[{}], error[{}]", - td.getId(), - e.getMessage()); - } - - CustodyPayment payment = null; - - try { - if (ledgerOperation == null) { - payment = - CustodyPayment.fromPayment( - ledgerTxn, - null, - td.getId(), - Instant.ofEpochMilli(td.getLastUpdated()), - status, - message, - td.getTxHash()); - } else if (ledgerOperation.getType() == PAYMENT) { - payment = - CustodyPayment.fromPayment( - ledgerTxn, - ledgerOperation.getPaymentOperation(), - td.getId(), - Instant.ofEpochMilli(td.getLastUpdated()), - status, - message, - td.getTxHash()); - } else if (List.of(PATH_PAYMENT_STRICT_RECEIVE, PATH_PAYMENT_STRICT_SEND) - .contains(ledgerOperation.getType())) { - payment = - CustodyPayment.fromPathPayment( - ledgerTxn, - ledgerOperation.getPathPaymentOperation(), - td.getId(), - Instant.ofEpochMilli(td.getLastUpdated()), - status, - message, - td.getTxHash()); - } else { - throw new LedgerException("Unknown Stellar transaction operation type"); - } - } catch (SepException ex) { - warnF("Fireblocks event with id[{}] contains unsupported memo", td.getId()); - warnEx(ex); - } - - return Optional.ofNullable(payment); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/custody/fireblocks/FireblocksPaymentService.java b/platform/src/main/java/org/stellar/anchor/platform/custody/fireblocks/FireblocksPaymentService.java deleted file mode 100644 index 82fca4364f..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/custody/fireblocks/FireblocksPaymentService.java +++ /dev/null @@ -1,182 +0,0 @@ -package org.stellar.anchor.platform.custody.fireblocks; - -import static org.stellar.anchor.api.custody.fireblocks.CreateTransactionRequest.DestinationTransferPeerPathType.ONE_TIME_ADDRESS; -import static org.stellar.anchor.api.custody.fireblocks.CreateTransactionRequest.TransferPeerPathType.VAULT_ACCOUNT; -import static org.stellar.anchor.util.MemoHelper.memoTypeAsString; - -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import java.lang.reflect.Type; -import java.time.Instant; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import lombok.Getter; -import org.springframework.retry.annotation.Backoff; -import org.springframework.retry.annotation.Retryable; -import org.stellar.anchor.api.custody.CreateTransactionPaymentResponse; -import org.stellar.anchor.api.custody.GenerateDepositAddressResponse; -import org.stellar.anchor.api.custody.fireblocks.CreateAddressRequest; -import org.stellar.anchor.api.custody.fireblocks.CreateAddressResponse; -import org.stellar.anchor.api.custody.fireblocks.CreateTransactionRequest; -import org.stellar.anchor.api.custody.fireblocks.CreateTransactionResponse; -import org.stellar.anchor.api.custody.fireblocks.TransactionDetails; -import org.stellar.anchor.api.exception.FireblocksException; -import org.stellar.anchor.api.exception.InvalidConfigException; -import org.stellar.anchor.platform.config.FireblocksConfig; -import org.stellar.anchor.platform.custody.CustodyPaymentService; -import org.stellar.anchor.platform.data.JdbcCustodyTransaction; -import org.stellar.anchor.util.GsonUtils; -import org.stellar.sdk.xdr.MemoType; - -/** Fireblocks implementation of payment service */ -@Getter -public class FireblocksPaymentService implements CustodyPaymentService { - - private static final Gson gson = GsonUtils.getInstance(); - - private static final String CREATE_NEW_DEPOSIT_ADDRESS_URL_FORMAT = - "/v1/vault/accounts/%s/%s/addresses"; - private static final String TRANSACTIONS_URL = "/v1/transactions"; - private static final String GET_TRANSACTION_BY_ID_URL_FORMAT = "/v1/transactions/%s"; - - private static final String QUERY_PARAM_AFTER = "after"; - private static final String QUERY_PARAM_BEFORE = "before"; - private static final String QUERY_PARAM_LIMIT = "limit"; - private static final String QUERY_PARAM_ORDER_BY = "orderBy"; - private static final String QUERY_PARAM_SORT = "sort"; - private static final String TRANSACTIONS_ORDER_BY = "createdAt"; - private static final String TRANSACTIONS_SORT = "ASC"; - private static final int DEFAULT_TRANSACTION_LIMIT = 500; - - private final FireblocksApiClient fireblocksApiClient; - private final FireblocksConfig fireblocksConfig; - private final Type transactionDetailsListType; - private int transactionLimit = DEFAULT_TRANSACTION_LIMIT; - - public FireblocksPaymentService( - FireblocksApiClient fireblocksApiClient, FireblocksConfig fireblocksConfig) { - this.fireblocksApiClient = fireblocksApiClient; - this.fireblocksConfig = fireblocksConfig; - this.transactionDetailsListType = new TypeToken>() {}.getType(); - } - - public FireblocksPaymentService( - FireblocksApiClient fireblocksApiClient, - FireblocksConfig fireblocksConfig, - int transactionLimit) { - this.fireblocksApiClient = fireblocksApiClient; - this.fireblocksConfig = fireblocksConfig; - this.transactionDetailsListType = new TypeToken>() {}.getType(); - this.transactionLimit = transactionLimit; - } - - /** - * @see CustodyPaymentService#generateDepositAddress(String) - */ - @Override - public GenerateDepositAddressResponse generateDepositAddress(String assetId) - throws FireblocksException, InvalidConfigException { - CreateAddressRequest request = new CreateAddressRequest(); - CreateAddressResponse depositAddress = - gson.fromJson( - fireblocksApiClient.post( - String.format( - CREATE_NEW_DEPOSIT_ADDRESS_URL_FORMAT, - fireblocksConfig.getVaultAccountId(), - fireblocksConfig.getFireblocksAssetCode(assetId)), - gson.toJson(request)), - CreateAddressResponse.class); - return new GenerateDepositAddressResponse( - depositAddress.getAddress(), depositAddress.getTag(), memoTypeAsString(MemoType.MEMO_ID)); - } - - /** - * @see CustodyPaymentService#createTransactionPayment(JdbcCustodyTransaction, String) - */ - @Retryable( - value = FireblocksException.class, - maxAttemptsExpression = "${custody.fireblocks.retry_config.max_attempts}", - backoff = @Backoff(delayExpression = "${custody.fireblocks.retry_config.delay}"), - exceptionExpression = - "#root instanceof T(org.stellar.anchor.api.exception.FireblocksException) AND" - + "(#root.statusCode == 429 OR #root.statusCode == 503)") - public CreateTransactionPaymentResponse createTransactionPayment( - JdbcCustodyTransaction txn, String requestBody) - throws FireblocksException, InvalidConfigException { - CreateTransactionRequest request = getCreateTransactionRequest(txn); - - CreateTransactionResponse response = - gson.fromJson( - fireblocksApiClient.post(TRANSACTIONS_URL, gson.toJson(request)), - CreateTransactionResponse.class); - - return new CreateTransactionPaymentResponse(response.getId()); - } - - private CreateTransactionRequest getCreateTransactionRequest(JdbcCustodyTransaction txn) - throws InvalidConfigException { - return CreateTransactionRequest.builder() - .assetId(fireblocksConfig.getFireblocksAssetCode(txn.getAsset())) - .amount(txn.getAmount()) - .source( - new CreateTransactionRequest.TransferPeerPath( - VAULT_ACCOUNT, fireblocksConfig.getVaultAccountId())) - .destination( - new CreateTransactionRequest.DestinationTransferPeerPath( - ONE_TIME_ADDRESS, - new CreateTransactionRequest.OneTimeAddress(txn.getToAccount(), txn.getMemo()))) - .build(); - } - - @Override - public TransactionDetails getTransactionById(String txnId) throws FireblocksException { - return gson.fromJson( - fireblocksApiClient.get(String.format(GET_TRANSACTION_BY_ID_URL_FORMAT, txnId)), - TransactionDetails.class); - } - - @Override - public List getTransactionsByTimeRange(Instant startTime, Instant endTime) - throws FireblocksException { - if (startTime.isAfter(endTime)) { - throw new IllegalArgumentException("End time can't be before start time"); - } - - Map queryParams = - new HashMap<>( - Map.of( - QUERY_PARAM_AFTER, String.valueOf(startTime.toEpochMilli()), - QUERY_PARAM_BEFORE, String.valueOf(endTime.toEpochMilli()), - QUERY_PARAM_LIMIT, String.valueOf(transactionLimit), - QUERY_PARAM_ORDER_BY, TRANSACTIONS_ORDER_BY, - QUERY_PARAM_SORT, TRANSACTIONS_SORT)); - - List transactions = new ArrayList<>(getTransactions(queryParams)); - - while (transactions.size() % transactionLimit == 0) { - Long maxCreatedAt = - transactions.stream() - .map(TransactionDetails::getCreatedAt) - .reduce(Long.MIN_VALUE, Long::max); - - queryParams.put(QUERY_PARAM_AFTER, String.valueOf(maxCreatedAt)); - - List retrievedTransactions = getTransactions(queryParams); - if (retrievedTransactions == null) { - return transactions; - } else { - transactions.addAll(getTransactions(queryParams)); - } - } - - return transactions; - } - - private List getTransactions(Map queryParams) - throws FireblocksException { - return gson.fromJson( - fireblocksApiClient.get(TRANSACTIONS_URL, queryParams), transactionDetailsListType); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/fireblocks/job/FireblocksTransactionsReconciliationJob.java b/platform/src/main/java/org/stellar/anchor/platform/fireblocks/job/FireblocksTransactionsReconciliationJob.java deleted file mode 100644 index deaff15c05..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/fireblocks/job/FireblocksTransactionsReconciliationJob.java +++ /dev/null @@ -1,163 +0,0 @@ -package org.stellar.anchor.platform.fireblocks.job; - -import static org.stellar.anchor.util.Log.debug; -import static org.stellar.anchor.util.Log.debugF; -import static org.stellar.anchor.util.Log.errorEx; -import static org.stellar.anchor.util.Log.info; - -import java.io.IOException; -import java.time.Instant; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; -import org.apache.commons.lang3.StringUtils; -import org.springframework.scheduling.annotation.Scheduled; -import org.stellar.anchor.api.custody.fireblocks.TransactionDetails; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.api.exception.CustodyException; -import org.stellar.anchor.platform.config.FireblocksConfig; -import org.stellar.anchor.platform.custody.CustodyPayment; -import org.stellar.anchor.platform.custody.CustodyPaymentService; -import org.stellar.anchor.platform.custody.CustodyTransactionService; -import org.stellar.anchor.platform.custody.fireblocks.FireblocksEventService; -import org.stellar.anchor.platform.data.CustodyTransactionStatus; -import org.stellar.anchor.platform.data.JdbcCustodyTransaction; - -public class FireblocksTransactionsReconciliationJob { - - private final FireblocksConfig fireblocksConfig; - private final CustodyPaymentService custodyPaymentService; - private final FireblocksEventService fireblocksEventService; - private final CustodyTransactionService custodyTransactionService; - - public FireblocksTransactionsReconciliationJob( - FireblocksConfig fireblocksConfig, - CustodyPaymentService custodyPaymentService, - FireblocksEventService fireblocksEventService, - CustodyTransactionService custodyTransactionService) { - this.fireblocksConfig = fireblocksConfig; - this.custodyPaymentService = custodyPaymentService; - this.fireblocksEventService = fireblocksEventService; - this.custodyTransactionService = custodyTransactionService; - } - - @Scheduled(cron = "${custody.fireblocks.reconciliation.cron_expression}") - public void reconcileTransactions() { - info("Fireblocks Transaction Reconciliation job started"); - - custodyTransactionService - .getOutboundTransactionsEligibleForReconciliation() - .forEach(this::reconcileOutboundTransactions); - - List inboundTransactions = - custodyTransactionService.getInboundTransactionsEligibleForReconciliation(); - reconcileInboundTransactions(inboundTransactions); - - info("Fireblocks Transaction Reconciliation job finished"); - } - - private void reconcileOutboundTransactions(JdbcCustodyTransaction txn) { - try { - TransactionDetails fireblocksTxn = - custodyPaymentService.getTransactionById(txn.getExternalTxId()); - - int attempt = txn.getReconciliationAttemptCount() + 1; - if (fireblocksTxn.getStatus().isObservable()) { - handleStatusChanged(fireblocksTxn, attempt); - } else { - handleStatusNotChanged(txn, attempt, fireblocksTxn.getExternalTxId()); - } - } catch (AnchorException | IOException e) { - errorEx(String.format("Failed to reconcile status for transaction (id=%s)", txn.getId()), e); - } - } - - private void reconcileInboundTransactions(List transactions) { - if (transactions.isEmpty()) { - debug("No inbound transactions to reconcile"); - return; - } - - Instant startTime = - transactions.stream() - .map(JdbcCustodyTransaction::getCreatedAt) - .min(Instant::compareTo) - .orElse(null); - - try { - List fireblocksTransactions = - custodyPaymentService.getTransactionsByTimeRange(startTime, Instant.now()); - - if (fireblocksTransactions.isEmpty()) { - debug("No Fireblocks transactions within specified time range"); - return; - } - - Map mappings = - fireblocksTransactions.stream() - .filter( - txn -> - !StringUtils.isEmpty(txn.getDestinationAddress()) - && !StringUtils.isEmpty(txn.getDestinationTag())) - .collect( - Collectors.toMap( - txn -> - txn.getDestinationAddress() + StringUtils.SPACE + txn.getDestinationTag(), - txn -> txn, - (txn1, txn2) -> txn1)); - - transactions.forEach( - txn -> { - int attempt = txn.getReconciliationAttemptCount() + 1; - - try { - String key = txn.getToAccount() + StringUtils.SPACE + txn.getMemo(); - TransactionDetails fireblocksTxn = mappings.get(key); - if (fireblocksTxn != null && fireblocksTxn.getStatus().isObservable()) { - handleStatusChanged(fireblocksTxn, attempt); - } else { - final String externalTxId = - fireblocksTxn != null ? fireblocksTxn.getExternalTxId() : null; - handleStatusNotChanged(txn, attempt, externalTxId); - } - } catch (AnchorException | IOException e) { - errorEx( - String.format("Failed to reconcile status for transaction (id=%s)", txn.getId()), - e); - } - }); - } catch (CustodyException e) { - errorEx("Failed to retrieve fireblocks transactions", e); - } - } - - private void handleStatusChanged(TransactionDetails fireblocksTxn, int attempt) - throws IOException, AnchorException { - debugF( - "Reconciliation attempt #[{}]: Fireblocks transaction status changed to [{}]", - attempt, - fireblocksTxn.getStatus().name()); - Optional payment = fireblocksEventService.convert(fireblocksTxn); - if (payment.isPresent()) { - fireblocksEventService.handlePayment(payment.get()); - } - } - - private void handleStatusNotChanged( - JdbcCustodyTransaction txn, int attempt, String externalTxnId) { - debugF("Reconciliation attempt #[{}]: Fireblocks transaction status wasn't changed", attempt); - - txn.setReconciliationAttemptCount(attempt); - if (txn.getReconciliationAttemptCount() - >= fireblocksConfig.getReconciliation().getMaxAttempts()) { - debugF("Change transaction [{}] status to FAILED", txn.getId()); - txn.setStatus(CustodyTransactionStatus.FAILED.toString()); - } - - if (!StringUtils.isEmpty(externalTxnId)) { - txn.setExternalTxId(externalTxnId); - } - custodyTransactionService.updateCustodyTransaction(txn); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/job/TrustlineCheckJob.java b/platform/src/main/java/org/stellar/anchor/platform/job/TrustlineCheckJob.java deleted file mode 100644 index 0b193128f4..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/job/TrustlineCheckJob.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.stellar.anchor.platform.job; - -import static org.stellar.anchor.util.Log.info; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import org.springframework.scheduling.annotation.Scheduled; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.api.rpc.method.NotifyTrustSetRequest; -import org.stellar.anchor.ledger.LedgerClient; -import org.stellar.anchor.platform.config.PropertyCustodyConfig; -import org.stellar.anchor.platform.data.JdbcTransactionPendingTrust; -import org.stellar.anchor.platform.data.JdbcTransactionPendingTrustRepo; -import org.stellar.anchor.platform.rpc.NotifyTrustSetHandler; -import org.stellar.sdk.exception.NetworkException; - -public class TrustlineCheckJob { - - private final LedgerClient horizon; - private final JdbcTransactionPendingTrustRepo transactionPendingTrustRepo; - private final PropertyCustodyConfig custodyConfig; - private final NotifyTrustSetHandler notifyTrustSetHandler; - - public TrustlineCheckJob( - LedgerClient ledgerClient, - JdbcTransactionPendingTrustRepo transactionPendingTrustRepo, - PropertyCustodyConfig custodyConfig, - NotifyTrustSetHandler notifyTrustSetHandler) { - this.horizon = ledgerClient; - this.transactionPendingTrustRepo = transactionPendingTrustRepo; - this.custodyConfig = custodyConfig; - this.notifyTrustSetHandler = notifyTrustSetHandler; - } - - @Scheduled(cron = "${custody.trustline.check_cron_expression}") - public void checkTrust() throws AnchorException { - info("Trustline Check job started"); - - for (JdbcTransactionPendingTrust t : transactionPendingTrustRepo.findAll()) { - if (isCheckTimedOut(t)) { - notifyTrustSetHandler.handle( - NotifyTrustSetRequest.builder() - .transactionId(t.getId()) - .message(custodyConfig.getTrustline().getTimeoutMessage()) - .success(false) - .build()); - - transactionPendingTrustRepo.delete(t); - } else { - boolean trustlineConfigured; - try { - trustlineConfigured = horizon.hasTrustline(t.getAccount(), t.getAsset()); - } catch (NetworkException ex) { - trustlineConfigured = false; - } - - if (trustlineConfigured) { - notifyTrustSetHandler.handle( - NotifyTrustSetRequest.builder().transactionId(t.getId()).success(true).build()); - - transactionPendingTrustRepo.delete(t); - } - } - } - - info("Trustline Check job finished"); - } - - private boolean isCheckTimedOut(JdbcTransactionPendingTrust trust) { - return trust - .getCreatedAt() - .plus(custodyConfig.getTrustline().getCheckDuration(), ChronoUnit.MINUTES) - .isBefore(Instant.now()); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/rpc/DoStellarPaymentHandler.java b/platform/src/main/java/org/stellar/anchor/platform/rpc/DoStellarPaymentHandler.java deleted file mode 100644 index 916bf9c4c5..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/rpc/DoStellarPaymentHandler.java +++ /dev/null @@ -1,196 +0,0 @@ -package org.stellar.anchor.platform.rpc; - -import static java.util.Collections.emptySet; -import static org.stellar.anchor.api.platform.PlatformTransactionData.Kind.DEPOSIT; -import static org.stellar.anchor.api.platform.PlatformTransactionData.Kind.DEPOSIT_EXCHANGE; -import static org.stellar.anchor.api.rpc.method.RpcMethod.DO_STELLAR_PAYMENT; -import static org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_ANCHOR; -import static org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_STELLAR; -import static org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_TRUST; - -import com.google.common.collect.ImmutableSet; -import java.time.Instant; -import java.util.Set; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.api.exception.BadRequestException; -import org.stellar.anchor.api.exception.LedgerException; -import org.stellar.anchor.api.exception.rpc.InvalidParamsException; -import org.stellar.anchor.api.exception.rpc.InvalidRequestException; -import org.stellar.anchor.api.platform.PlatformTransactionData.Kind; -import org.stellar.anchor.api.platform.PlatformTransactionData.Sep; -import org.stellar.anchor.api.rpc.method.DoStellarPaymentRequest; -import org.stellar.anchor.api.rpc.method.RpcMethod; -import org.stellar.anchor.api.sep.SepTransactionStatus; -import org.stellar.anchor.asset.AssetService; -import org.stellar.anchor.config.CustodyConfig; -import org.stellar.anchor.custody.CustodyService; -import org.stellar.anchor.event.EventService; -import org.stellar.anchor.ledger.LedgerClient; -import org.stellar.anchor.metrics.MetricsService; -import org.stellar.anchor.platform.data.*; -import org.stellar.anchor.platform.validator.RequestValidator; -import org.stellar.anchor.sep24.Sep24TransactionStore; -import org.stellar.anchor.sep31.Sep31TransactionStore; -import org.stellar.anchor.sep6.Sep6TransactionStore; -import org.stellar.sdk.exception.NetworkException; - -public class DoStellarPaymentHandler extends RpcTransactionStatusHandler { - - private final CustodyService custodyService; - private final CustodyConfig custodyConfig; - private final JdbcTransactionPendingTrustRepo transactionPendingTrustRepo; - private final LedgerClient ledgerClient; - - public DoStellarPaymentHandler( - Sep6TransactionStore txn6Store, - Sep24TransactionStore txn24Store, - Sep31TransactionStore txn31Store, - RequestValidator requestValidator, - CustodyConfig custodyConfig, - LedgerClient ledgerClient, - AssetService assetService, - CustodyService custodyService, - EventService eventService, - MetricsService metricsService, - JdbcTransactionPendingTrustRepo transactionPendingTrustRepo) { - super( - txn6Store, - txn24Store, - txn31Store, - requestValidator, - assetService, - eventService, - metricsService, - DoStellarPaymentRequest.class); - this.custodyService = custodyService; - this.custodyConfig = custodyConfig; - this.ledgerClient = ledgerClient; - this.transactionPendingTrustRepo = transactionPendingTrustRepo; - } - - @Override - protected void validate(JdbcSepTransaction txn, DoStellarPaymentRequest request) - throws InvalidRequestException, InvalidParamsException, BadRequestException { - super.validate(txn, request); - - if (!custodyConfig.isCustodyIntegrationEnabled()) { - throw new InvalidRequestException( - String.format("RPC method[%s] requires enabled custody integration", getRpcMethod())); - } - } - - @Override - public RpcMethod getRpcMethod() { - return DO_STELLAR_PAYMENT; - } - - @Override - protected SepTransactionStatus getNextStatus( - JdbcSepTransaction txn, DoStellarPaymentRequest request) { - - boolean trustlineConfigured = false; - try { - switch (Sep.from(txn.getProtocol())) { - case SEP_6: - JdbcSep6Transaction txn6 = (JdbcSep6Transaction) txn; - trustlineConfigured = - ledgerClient.hasTrustline(txn6.getToAccount(), txn6.getAmountOutAsset()); - break; - case SEP_24: - JdbcSep24Transaction txn24 = (JdbcSep24Transaction) txn; - trustlineConfigured = - ledgerClient.hasTrustline(txn24.getToAccount(), txn24.getAmountOutAsset()); - break; - default: - break; - } - } catch (LedgerException ex) { - // assume trustline is not configured - } - - if (trustlineConfigured) { - return PENDING_STELLAR; - } else { - return PENDING_TRUST; - } - } - - @Override - protected Set getSupportedStatuses(JdbcSepTransaction txn) { - switch (Sep.from(txn.getProtocol())) { - case SEP_6: - JdbcSep6Transaction txn6 = (JdbcSep6Transaction) txn; - if (ImmutableSet.of(DEPOSIT, DEPOSIT_EXCHANGE).contains(Kind.from(txn6.getKind()))) { - if (areFundsReceived(txn6)) { - return Set.of(PENDING_ANCHOR); - } - } - break; - case SEP_24: - JdbcSep24Transaction txn24 = (JdbcSep24Transaction) txn; - if (DEPOSIT == Kind.from(txn24.getKind())) { - if (areFundsReceived(txn24)) { - return Set.of(PENDING_ANCHOR); - } - } - break; - default: - break; - } - return emptySet(); - } - - @Override - protected void updateTransactionWithRpcRequest( - JdbcSepTransaction txn, DoStellarPaymentRequest request) throws AnchorException { - boolean trustlineConfigured; - switch (Sep.from(txn.getProtocol())) { - case SEP_6: - JdbcSep6Transaction txn6 = (JdbcSep6Transaction) txn; - - try { - trustlineConfigured = - ledgerClient.hasTrustline(txn6.getToAccount(), txn6.getAmountOutAsset()); - } catch (NetworkException ex) { - trustlineConfigured = false; - } - - if (trustlineConfigured) { - custodyService.createTransactionPayment(txn6.getId(), null); - } else { - transactionPendingTrustRepo.save( - JdbcTransactionPendingTrust.builder() - .id(txn6.getId()) - .createdAt(Instant.now()) - .asset(txn6.getAmountOutAsset()) - .account(txn6.getToAccount()) - .build()); - } - break; - case SEP_24: - JdbcSep24Transaction txn24 = (JdbcSep24Transaction) txn; - - try { - trustlineConfigured = - ledgerClient.hasTrustline(txn24.getToAccount(), txn24.getAmountOutAsset()); - } catch (NetworkException ex) { - trustlineConfigured = false; - } - - if (trustlineConfigured) { - custodyService.createTransactionPayment(txn24.getId(), null); - } else { - transactionPendingTrustRepo.save( - JdbcTransactionPendingTrust.builder() - .id(txn24.getId()) - .createdAt(Instant.now()) - .asset(txn24.getAmountOutAsset()) - .account(txn24.getToAccount()) - .build()); - } - break; - default: - break; - } - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/rpc/DoStellarRefundHandler.java b/platform/src/main/java/org/stellar/anchor/platform/rpc/DoStellarRefundHandler.java deleted file mode 100644 index dadb1e5278..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/rpc/DoStellarRefundHandler.java +++ /dev/null @@ -1,219 +0,0 @@ -package org.stellar.anchor.platform.rpc; - -import static java.util.Collections.emptySet; -import static org.stellar.anchor.api.platform.PlatformTransactionData.Kind.*; -import static org.stellar.anchor.api.platform.PlatformTransactionData.Sep.SEP_31; -import static org.stellar.anchor.api.rpc.method.RpcMethod.DO_STELLAR_REFUND; -import static org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_ANCHOR; -import static org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_RECEIVER; -import static org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_STELLAR; -import static org.stellar.anchor.util.AssetHelper.getAssetCode; -import static org.stellar.anchor.util.MathHelper.decimal; -import static org.stellar.anchor.util.MathHelper.sum; - -import com.google.common.collect.ImmutableSet; -import java.math.BigDecimal; -import java.util.Set; -import org.stellar.anchor.api.asset.AssetInfo; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.api.exception.BadRequestException; -import org.stellar.anchor.api.exception.rpc.InvalidParamsException; -import org.stellar.anchor.api.exception.rpc.InvalidRequestException; -import org.stellar.anchor.api.platform.PlatformTransactionData.Kind; -import org.stellar.anchor.api.platform.PlatformTransactionData.Sep; -import org.stellar.anchor.api.rpc.method.AmountAssetRequest; -import org.stellar.anchor.api.rpc.method.DoStellarRefundRequest; -import org.stellar.anchor.api.rpc.method.RpcMethod; -import org.stellar.anchor.api.sep.SepTransactionStatus; -import org.stellar.anchor.api.shared.Refunds; -import org.stellar.anchor.asset.AssetService; -import org.stellar.anchor.config.CustodyConfig; -import org.stellar.anchor.custody.CustodyService; -import org.stellar.anchor.event.EventService; -import org.stellar.anchor.metrics.MetricsService; -import org.stellar.anchor.platform.data.JdbcSep24Transaction; -import org.stellar.anchor.platform.data.JdbcSep31Transaction; -import org.stellar.anchor.platform.data.JdbcSep6Transaction; -import org.stellar.anchor.platform.data.JdbcSepTransaction; -import org.stellar.anchor.platform.utils.AssetValidationUtils; -import org.stellar.anchor.platform.validator.RequestValidator; -import org.stellar.anchor.sep24.Sep24Refunds; -import org.stellar.anchor.sep24.Sep24TransactionStore; -import org.stellar.anchor.sep31.Sep31Refunds; -import org.stellar.anchor.sep31.Sep31TransactionStore; -import org.stellar.anchor.sep6.Sep6TransactionStore; - -public class DoStellarRefundHandler extends RpcTransactionStatusHandler { - - private final CustodyService custodyService; - private final CustodyConfig custodyConfig; - - public DoStellarRefundHandler( - Sep6TransactionStore txn6Store, - Sep24TransactionStore txn24Store, - Sep31TransactionStore txn31Store, - RequestValidator requestValidator, - CustodyConfig custodyConfig, - AssetService assetService, - CustodyService custodyService, - EventService eventService, - MetricsService metricsService) { - super( - txn6Store, - txn24Store, - txn31Store, - requestValidator, - assetService, - eventService, - metricsService, - DoStellarRefundRequest.class); - this.custodyService = custodyService; - this.custodyConfig = custodyConfig; - } - - @Override - protected void validate(JdbcSepTransaction txn, DoStellarRefundRequest request) - throws InvalidParamsException, InvalidRequestException, BadRequestException { - super.validate(txn, request); - - if (!custodyConfig.isCustodyIntegrationEnabled()) { - throw new InvalidParamsException( - String.format("RPC method[%s] requires enabled custody integration", getRpcMethod())); - } - - AssetValidationUtils.validateAssetAmount( - "refund.amount", - AmountAssetRequest.builder() - .amount(request.getRefund().getAmount().getAmount()) - .asset(txn.getAmountInAsset()) - .build(), - assetService); - AssetValidationUtils.validateAssetAmount( - "refund.amountFee", - AmountAssetRequest.builder() - .amount(request.getRefund().getAmountFee().getAmount()) - .asset(txn.getAmountInAsset()) - .build(), - true, - assetService); - - if (!txn.getAmountInAsset().equals(request.getRefund().getAmount().getAsset())) { - throw new InvalidParamsException( - "refund.amount.asset does not match transaction amount_in_asset"); - } - if (!txn.getAmountFeeAsset().equals(request.getRefund().getAmountFee().getAsset())) { - throw new InvalidParamsException( - "refund.amount_fee.asset does not match match transaction amount_fee_asset"); - } - } - - @Override - public RpcMethod getRpcMethod() { - return DO_STELLAR_REFUND; - } - - @Override - protected SepTransactionStatus getNextStatus( - JdbcSepTransaction txn, DoStellarRefundRequest request) - throws InvalidRequestException, InvalidParamsException { - String amount = request.getRefund().getAmount().getAmount(); - String amountFee = request.getRefund().getAmountFee().getAmount(); - AssetInfo assetInfo = assetService.getAsset(getAssetCode(txn.getAmountInAsset())); - - BigDecimal totalRefunded; - switch (Sep.from(txn.getProtocol())) { - case SEP_6: - JdbcSep6Transaction txn6 = (JdbcSep6Transaction) txn; - Refunds refunds = txn6.getRefunds(); - if (refunds == null || refunds.getPayments() == null) { - totalRefunded = sum(assetInfo, amount, amountFee); - } else { - totalRefunded = - sum(assetInfo, refunds.getAmountRefunded().getAmount(), amount, amountFee); - } - break; - case SEP_24: - JdbcSep24Transaction txn24 = (JdbcSep24Transaction) txn; - Sep24Refunds sep24Refunds = txn24.getRefunds(); - if (sep24Refunds == null || sep24Refunds.getRefundPayments() == null) { - totalRefunded = sum(assetInfo, amount, amountFee); - } else { - totalRefunded = sum(assetInfo, sep24Refunds.getAmountRefunded(), amount, amountFee); - } - break; - case SEP_31: - JdbcSep31Transaction txn31 = (JdbcSep31Transaction) txn; - Sep31Refunds sep31Refunds = txn31.getRefunds(); - if (sep31Refunds == null || sep31Refunds.getRefundPayments() == null) { - totalRefunded = sum(assetInfo, amount, amountFee); - } else { - throw new InvalidRequestException( - String.format( - "Multiple refunds aren't supported for kind[%s], protocol[%s] and action[%s]", - RECEIVE, txn.getProtocol(), getRpcMethod())); - } - break; - default: - throw new InvalidRequestException( - String.format( - "RPC method[%s] is not supported for protocol[%s]", - getRpcMethod(), txn.getProtocol())); - } - - BigDecimal amountIn = decimal(txn.getAmountIn(), assetInfo); - if (totalRefunded.compareTo(amountIn) > 0) { - throw new InvalidParamsException("Refund amount exceeds amount_in"); - } else if (SEP_31 == Sep.from(txn.getProtocol()) && totalRefunded.compareTo(amountIn) < 0) { - throw new InvalidParamsException("Refund amount is less than amount_in"); - } - return PENDING_STELLAR; - } - - @Override - protected Set getSupportedStatuses(JdbcSepTransaction txn) { - switch (Sep.from(txn.getProtocol())) { - case SEP_6: - JdbcSep6Transaction txn6 = (JdbcSep6Transaction) txn; - if (ImmutableSet.of(WITHDRAWAL, WITHDRAWAL_EXCHANGE).contains(Kind.from(txn6.getKind()))) { - if (areFundsReceived(txn6)) { - return Set.of(PENDING_ANCHOR); - } - } - break; - case SEP_24: - JdbcSep24Transaction txn24 = (JdbcSep24Transaction) txn; - if (WITHDRAWAL == Kind.from(txn24.getKind())) { - if (areFundsReceived(txn24)) { - return Set.of(PENDING_ANCHOR); - } - } - break; - case SEP_31: - return Set.of(PENDING_RECEIVER); - } - - return emptySet(); - } - - @Override - protected void updateTransactionWithRpcRequest( - JdbcSepTransaction txn, DoStellarRefundRequest request) throws AnchorException { - switch (Sep.from(txn.getProtocol())) { - case SEP_6: - JdbcSep6Transaction txn6 = (JdbcSep6Transaction) txn; - custodyService.createTransactionRefund( - request, txn6.getRefundMemo(), txn6.getRefundMemoType()); - break; - case SEP_24: - JdbcSep24Transaction txn24 = (JdbcSep24Transaction) txn; - custodyService.createTransactionRefund( - request, txn24.getRefundMemo(), txn24.getRefundMemoType()); - break; - case SEP_31: - JdbcSep31Transaction txn31 = (JdbcSep31Transaction) txn; - custodyService.createTransactionRefund( - request, txn31.getStellarMemo(), txn31.getStellarMemoType()); - break; - } - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/rpc/NotifyOffchainFundsReceivedHandler.java b/platform/src/main/java/org/stellar/anchor/platform/rpc/NotifyOffchainFundsReceivedHandler.java index ccbc5084ec..f911f32ea7 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/rpc/NotifyOffchainFundsReceivedHandler.java +++ b/platform/src/main/java/org/stellar/anchor/platform/rpc/NotifyOffchainFundsReceivedHandler.java @@ -20,8 +20,6 @@ import org.stellar.anchor.api.rpc.method.RpcMethod; import org.stellar.anchor.api.sep.SepTransactionStatus; import org.stellar.anchor.asset.AssetService; -import org.stellar.anchor.config.CustodyConfig; -import org.stellar.anchor.custody.CustodyService; import org.stellar.anchor.event.EventService; import org.stellar.anchor.metrics.MetricsService; import org.stellar.anchor.platform.data.JdbcSep24Transaction; @@ -36,17 +34,12 @@ public class NotifyOffchainFundsReceivedHandler extends RpcTransactionStatusHandler { - private final CustodyService custodyService; - private final CustodyConfig custodyConfig; - public NotifyOffchainFundsReceivedHandler( Sep6TransactionStore txn6Store, Sep24TransactionStore txn24Store, Sep31TransactionStore txn31Store, RequestValidator requestValidator, AssetService assetService, - CustodyService custodyService, - CustodyConfig custodyConfig, EventService eventService, MetricsService metricsService) { super( @@ -58,8 +51,6 @@ public NotifyOffchainFundsReceivedHandler( eventService, metricsService, NotifyOffchainFundsReceivedRequest.class); - this.custodyService = custodyService; - this.custodyConfig = custodyConfig; } @Override @@ -171,22 +162,5 @@ protected void updateTransactionWithRpcRequest( txn.setAmountFee(request.getFeeDetails().getTotal()); txn.setFeeDetailsList(request.getFeeDetails().getDetails()); } - - switch (Sep.from(txn.getProtocol())) { - case SEP_6: - JdbcSep6Transaction txn6 = (JdbcSep6Transaction) txn; - if (custodyConfig.isCustodyIntegrationEnabled()) { - custodyService.createTransaction(txn6); - } - break; - case SEP_24: - JdbcSep24Transaction txn24 = (JdbcSep24Transaction) txn; - if (custodyConfig.isCustodyIntegrationEnabled()) { - custodyService.createTransaction(txn24); - } - break; - default: - break; - } } } diff --git a/platform/src/main/java/org/stellar/anchor/platform/rpc/NotifyTrustSetHandler.java b/platform/src/main/java/org/stellar/anchor/platform/rpc/NotifyTrustSetHandler.java index d592bb2527..52f0c0cc98 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/rpc/NotifyTrustSetHandler.java +++ b/platform/src/main/java/org/stellar/anchor/platform/rpc/NotifyTrustSetHandler.java @@ -5,7 +5,6 @@ import static org.stellar.anchor.api.platform.PlatformTransactionData.Kind.DEPOSIT_EXCHANGE; import static org.stellar.anchor.api.rpc.method.RpcMethod.NOTIFY_TRUST_SET; import static org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_ANCHOR; -import static org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_STELLAR; import static org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_TRUST; import com.google.common.collect.ImmutableSet; @@ -20,10 +19,8 @@ import org.stellar.anchor.api.rpc.method.RpcMethod; import org.stellar.anchor.api.sep.SepTransactionStatus; import org.stellar.anchor.asset.AssetService; -import org.stellar.anchor.custody.CustodyService; import org.stellar.anchor.event.EventService; import org.stellar.anchor.metrics.MetricsService; -import org.stellar.anchor.platform.config.PropertyCustodyConfig; import org.stellar.anchor.platform.data.JdbcSep24Transaction; import org.stellar.anchor.platform.data.JdbcSep6Transaction; import org.stellar.anchor.platform.data.JdbcSepTransaction; @@ -33,10 +30,6 @@ import org.stellar.anchor.sep6.Sep6TransactionStore; public class NotifyTrustSetHandler extends RpcTransactionStatusHandler { - - private final PropertyCustodyConfig custodyConfig; - private final CustodyService custodyService; - public NotifyTrustSetHandler( Sep6TransactionStore txn6Store, Sep24TransactionStore txn24Store, @@ -44,9 +37,7 @@ public NotifyTrustSetHandler( RequestValidator requestValidator, AssetService assetService, EventService eventService, - MetricsService metricsService, - PropertyCustodyConfig custodyConfig, - CustodyService custodyService) { + MetricsService metricsService) { super( txn6Store, txn24Store, @@ -56,8 +47,6 @@ public NotifyTrustSetHandler( eventService, metricsService, NotifyTrustSetRequest.class); - this.custodyConfig = custodyConfig; - this.custodyService = custodyService; } @Override @@ -74,11 +63,7 @@ public RpcMethod getRpcMethod() { @Override protected SepTransactionStatus getNextStatus( JdbcSepTransaction txn, NotifyTrustSetRequest request) { - if (!custodyConfig.isCustodyIntegrationEnabled() || !request.isSuccess()) { - return PENDING_ANCHOR; - } else { - return PENDING_STELLAR; - } + return PENDING_ANCHOR; } @Override @@ -104,9 +89,5 @@ protected Set getSupportedStatuses(JdbcSepTransaction txn) @Override protected void updateTransactionWithRpcRequest( - JdbcSepTransaction txn, NotifyTrustSetRequest request) throws AnchorException { - if (custodyConfig.isCustodyIntegrationEnabled() && request.isSuccess()) { - custodyService.createTransactionPayment(txn.getId(), null); - } - } + JdbcSepTransaction txn, NotifyTrustSetRequest request) throws AnchorException {} } diff --git a/platform/src/main/java/org/stellar/anchor/platform/rpc/RequestOnchainFundsHandler.java b/platform/src/main/java/org/stellar/anchor/platform/rpc/RequestOnchainFundsHandler.java index 62c001cbbb..72d16decd1 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/rpc/RequestOnchainFundsHandler.java +++ b/platform/src/main/java/org/stellar/anchor/platform/rpc/RequestOnchainFundsHandler.java @@ -27,8 +27,6 @@ import org.stellar.anchor.api.sep.SepTransactionStatus; import org.stellar.anchor.api.shared.SepDepositInfo; import org.stellar.anchor.asset.AssetService; -import org.stellar.anchor.config.CustodyConfig; -import org.stellar.anchor.custody.CustodyService; import org.stellar.anchor.event.EventService; import org.stellar.anchor.metrics.MetricsService; import org.stellar.anchor.platform.data.JdbcSep24Transaction; @@ -47,15 +45,12 @@ import org.stellar.anchor.sep31.Sep31TransactionStore; import org.stellar.anchor.sep6.Sep6DepositInfoGenerator; import org.stellar.anchor.sep6.Sep6TransactionStore; -import org.stellar.anchor.util.CustodyUtils; import org.stellar.anchor.util.Log; import org.stellar.sdk.Memo; public class RequestOnchainFundsHandler extends RpcTransactionStatusHandler { - private final CustodyService custodyService; - private final CustodyConfig custodyConfig; private final Sep6DepositInfoGenerator sep6DepositInfoGenerator; private final Sep24DepositInfoGenerator sep24DepositInfoGenerator; private final Sep31DepositInfoGenerator sep31DepositInfoGenerator; @@ -67,8 +62,6 @@ public RequestOnchainFundsHandler( Sep31TransactionStore txn31Store, RequestValidator requestValidator, AssetService assetService, - CustodyService custodyService, - CustodyConfig custodyConfig, Sep6DepositInfoGenerator sep6DepositInfoGenerator, Sep24DepositInfoGenerator sep24DepositInfoGenerator, Sep31DepositInfoGenerator sep31DepositInfoGenerator, @@ -84,8 +77,6 @@ public RequestOnchainFundsHandler( eventService, metricsService, RequestOnchainFundsRequest.class); - this.custodyService = custodyService; - this.custodyConfig = custodyConfig; this.sep6DepositInfoGenerator = sep6DepositInfoGenerator; this.sep24DepositInfoGenerator = sep24DepositInfoGenerator; this.sep31DepositInfoGenerator = sep31DepositInfoGenerator; @@ -293,17 +284,6 @@ protected void updateTransactionWithRpcRequest( txn6.setMemo(sep6DepositInfo.getMemo()); txn6.setMemoType(sep6DepositInfo.getMemoType()); } - - if (!CustodyUtils.isMemoTypeSupported(custodyConfig.getType(), txn6.getMemoType())) { - throw new InvalidParamsException( - String.format( - "Memo type[%s] is not supported for custody type[%s]", - txn6.getMemoType(), custodyConfig.getType())); - } - - if (custodyConfig.isCustodyIntegrationEnabled()) { - custodyService.createTransaction(txn6); - } break; case SEP_24: JdbcSep24Transaction txn24 = (JdbcSep24Transaction) txn; @@ -330,16 +310,6 @@ protected void updateTransactionWithRpcRequest( txn24.setMemoType(sep24DepositInfo.getMemoType()); } - if (!CustodyUtils.isMemoTypeSupported(custodyConfig.getType(), txn24.getMemoType())) { - throw new InvalidParamsException( - String.format( - "Memo type[%s] is not supported for custody type[%s]", - txn24.getMemoType(), custodyConfig.getType())); - } - - if (custodyConfig.isCustodyIntegrationEnabled()) { - custodyService.createTransaction(txn24); - } break; case SEP_31: JdbcSep31Transaction txn31 = (JdbcSep31Transaction) txn; @@ -369,18 +339,6 @@ protected void updateTransactionWithRpcRequest( paymentObservingAccountsManager.upsert( txn31.getToAccount(), PaymentObservingAccountsManager.AccountType.TRANSIENT); - if (!CustodyUtils.isMemoTypeSupported( - custodyConfig.getType(), txn31.getStellarMemoType())) { - throw new InvalidParamsException( - String.format( - "Memo type[%s] is not supported for custody type[%s]", - txn31.getStellarMemoType(), custodyConfig.getType())); - } - - if (custodyConfig.isCustodyIntegrationEnabled()) { - custodyService.createTransaction(txn31); - } - break; default: break; diff --git a/platform/src/main/java/org/stellar/anchor/platform/rpc/RequestTrustlineHandler.java b/platform/src/main/java/org/stellar/anchor/platform/rpc/RequestTrustlineHandler.java index 72a6747497..bd139ad1df 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/rpc/RequestTrustlineHandler.java +++ b/platform/src/main/java/org/stellar/anchor/platform/rpc/RequestTrustlineHandler.java @@ -18,7 +18,6 @@ import org.stellar.anchor.api.rpc.method.RpcMethod; import org.stellar.anchor.api.sep.SepTransactionStatus; import org.stellar.anchor.asset.AssetService; -import org.stellar.anchor.config.CustodyConfig; import org.stellar.anchor.event.EventService; import org.stellar.anchor.metrics.MetricsService; import org.stellar.anchor.platform.data.JdbcSep24Transaction; @@ -31,15 +30,12 @@ public class RequestTrustlineHandler extends RpcTransactionStatusHandler { - private final CustodyConfig custodyConfig; - public RequestTrustlineHandler( Sep6TransactionStore txn6Store, Sep24TransactionStore txn24Store, Sep31TransactionStore txn31Store, RequestValidator requestValidator, AssetService assetService, - CustodyConfig custodyConfig, EventService eventService, MetricsService metricsService) { super( @@ -51,18 +47,12 @@ public RequestTrustlineHandler( eventService, metricsService, RequestTrustRequest.class); - this.custodyConfig = custodyConfig; } @Override protected void validate(JdbcSepTransaction txn, RequestTrustRequest request) throws InvalidRequestException, InvalidParamsException, BadRequestException { super.validate(txn, request); - - if (custodyConfig.isCustodyIntegrationEnabled()) { - throw new InvalidRequestException( - String.format("RPC method[%s] requires disabled custody integration", getRpcMethod())); - } } @Override diff --git a/platform/src/main/java/org/stellar/anchor/platform/service/CustodyServiceImpl.java b/platform/src/main/java/org/stellar/anchor/platform/service/CustodyServiceImpl.java deleted file mode 100644 index 7b49c50315..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/service/CustodyServiceImpl.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.stellar.anchor.platform.service; - -import static org.stellar.anchor.util.Log.debugF; -import static org.stellar.anchor.util.TransactionMapper.toCustodyTransaction; - -import java.util.Optional; -import org.springframework.http.HttpStatus; -import org.stellar.anchor.api.custody.CreateCustodyTransactionRequest; -import org.stellar.anchor.api.custody.CreateTransactionPaymentResponse; -import org.stellar.anchor.api.custody.CreateTransactionRefundRequest; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.api.exception.CustodyException; -import org.stellar.anchor.api.exception.InvalidConfigException; -import org.stellar.anchor.api.exception.custody.CustodyBadRequestException; -import org.stellar.anchor.api.exception.custody.CustodyNotFoundException; -import org.stellar.anchor.api.exception.custody.CustodyServiceUnavailableException; -import org.stellar.anchor.api.exception.custody.CustodyTooManyRequestsException; -import org.stellar.anchor.api.rpc.method.DoStellarRefundRequest; -import org.stellar.anchor.custody.CustodyService; -import org.stellar.anchor.platform.apiclient.CustodyApiClient; -import org.stellar.anchor.sep24.Sep24Transaction; -import org.stellar.anchor.sep31.Sep31Transaction; -import org.stellar.anchor.sep6.Sep6Transaction; - -public class CustodyServiceImpl implements CustodyService { - - private final Optional custodyApiClient; - - public CustodyServiceImpl(Optional custodyApiClient) { - this.custodyApiClient = custodyApiClient; - } - - @Override - public void createTransaction(Sep6Transaction txn) throws AnchorException { - create(toCustodyTransaction(txn)); - } - - @Override - public void createTransaction(Sep24Transaction txn) throws AnchorException { - create(toCustodyTransaction(txn)); - } - - @Override - public void createTransaction(Sep31Transaction txn) throws AnchorException { - create(toCustodyTransaction(txn)); - } - - @Override - public CreateTransactionPaymentResponse createTransactionPayment(String txnId, String requestBody) - throws AnchorException { - if (custodyApiClient.isEmpty()) { - // custody.type is set to 'none' - throw new InvalidConfigException("Integration with custody service is not enabled"); - } - - try { - return custodyApiClient.get().createTransactionPayment(txnId, requestBody); - } catch (CustodyException e) { - throw (getResponseException(e)); - } - } - - @Override - public CreateTransactionPaymentResponse createTransactionRefund( - DoStellarRefundRequest request, String memo, String memoType) throws AnchorException { - if (custodyApiClient.isEmpty()) { - // custody.type is set to 'none' - throw new InvalidConfigException("Integration with custody service is not enabled"); - } - - CreateTransactionRefundRequest createTransactionRefundRequest = - CreateTransactionRefundRequest.builder() - .amount(request.getRefund().getAmount().getAmount()) - .amountFee(request.getRefund().getAmountFee().getAmount()) - .memo(memo) - .memoType(memoType) - .build(); - - try { - return custodyApiClient - .get() - .createTransactionRefund(request.getTransactionId(), createTransactionRefundRequest); - } catch (CustodyException e) { - throw (getResponseException(e)); - } - } - - private void create(CreateCustodyTransactionRequest request) - throws CustodyException, InvalidConfigException { - if (custodyApiClient.isEmpty()) { - // custody.type is set to 'none' - throw new InvalidConfigException("Integration with custody service is not enabled"); - } - custodyApiClient.get().createTransaction(request); - } - - public AnchorException getResponseException(CustodyException e) { - switch (HttpStatus.valueOf(e.getStatusCode())) { - case NOT_FOUND: - return new CustodyNotFoundException(e.getRawMessage()); - case TOO_MANY_REQUESTS: - return new CustodyTooManyRequestsException(e.getRawMessage()); - case BAD_REQUEST: - return new CustodyBadRequestException(e.getRawMessage()); - case SERVICE_UNAVAILABLE: - return new CustodyServiceUnavailableException(e.getRawMessage()); - default: - debugF("Unhandled status code (%s)", e.getStatusCode()); - return e; - } - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/service/Sep24DepositInfoCustodyGenerator.java b/platform/src/main/java/org/stellar/anchor/platform/service/Sep24DepositInfoCustodyGenerator.java deleted file mode 100644 index 406310d0f1..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/service/Sep24DepositInfoCustodyGenerator.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.stellar.anchor.platform.service; - -import org.stellar.anchor.api.custody.GenerateDepositAddressResponse; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.api.shared.SepDepositInfo; -import org.stellar.anchor.platform.apiclient.CustodyApiClient; -import org.stellar.anchor.sep24.Sep24DepositInfoGenerator; -import org.stellar.anchor.sep24.Sep24Transaction; - -public class Sep24DepositInfoCustodyGenerator implements Sep24DepositInfoGenerator { - - private final CustodyApiClient custodyApiClient; - - public Sep24DepositInfoCustodyGenerator(CustodyApiClient custodyApiClient) { - this.custodyApiClient = custodyApiClient; - } - - @Override - public SepDepositInfo generate(Sep24Transaction txn) throws AnchorException { - GenerateDepositAddressResponse depositAddress = - custodyApiClient.generateDepositAddress(txn.getAmountInAsset()); - return new SepDepositInfo( - depositAddress.getAddress(), depositAddress.getMemo(), depositAddress.getMemoType()); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/service/Sep31DepositInfoCustodyGenerator.java b/platform/src/main/java/org/stellar/anchor/platform/service/Sep31DepositInfoCustodyGenerator.java deleted file mode 100644 index 0e44e12c4f..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/service/Sep31DepositInfoCustodyGenerator.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.stellar.anchor.platform.service; - -import org.stellar.anchor.api.custody.GenerateDepositAddressResponse; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.api.shared.SepDepositInfo; -import org.stellar.anchor.platform.apiclient.CustodyApiClient; -import org.stellar.anchor.sep31.Sep31DepositInfoGenerator; -import org.stellar.anchor.sep31.Sep31Transaction; - -public class Sep31DepositInfoCustodyGenerator implements Sep31DepositInfoGenerator { - - private final CustodyApiClient custodyApiClient; - - public Sep31DepositInfoCustodyGenerator(CustodyApiClient custodyApiClient) { - this.custodyApiClient = custodyApiClient; - } - - @Override - public SepDepositInfo generate(Sep31Transaction txn) throws AnchorException { - GenerateDepositAddressResponse depositAddress = - custodyApiClient.generateDepositAddress(txn.getAmountInAsset()); - return new SepDepositInfo( - depositAddress.getAddress(), depositAddress.getMemo(), depositAddress.getMemoType()); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/service/Sep6DepositInfoCustodyGenerator.java b/platform/src/main/java/org/stellar/anchor/platform/service/Sep6DepositInfoCustodyGenerator.java deleted file mode 100644 index 37d852b93f..0000000000 --- a/platform/src/main/java/org/stellar/anchor/platform/service/Sep6DepositInfoCustodyGenerator.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.stellar.anchor.platform.service; - -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import org.stellar.anchor.api.custody.GenerateDepositAddressResponse; -import org.stellar.anchor.api.exception.AnchorException; -import org.stellar.anchor.api.shared.SepDepositInfo; -import org.stellar.anchor.platform.apiclient.CustodyApiClient; -import org.stellar.anchor.sep6.Sep6DepositInfoGenerator; -import org.stellar.anchor.sep6.Sep6Transaction; - -@RequiredArgsConstructor -public class Sep6DepositInfoCustodyGenerator implements Sep6DepositInfoGenerator { - @NonNull private final CustodyApiClient custodyApiClient; - - @Override - public SepDepositInfo generate(Sep6Transaction txn) throws AnchorException { - GenerateDepositAddressResponse depositAddress = - custodyApiClient.generateDepositAddress(txn.getAmountInAsset()); - return new SepDepositInfo( - depositAddress.getAddress(), depositAddress.getMemo(), depositAddress.getMemoType()); - } -} diff --git a/platform/src/main/java/org/stellar/anchor/platform/service/TransactionService.java b/platform/src/main/java/org/stellar/anchor/platform/service/TransactionService.java index 0f9c6b0e3c..7b0c99b333 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/service/TransactionService.java +++ b/platform/src/main/java/org/stellar/anchor/platform/service/TransactionService.java @@ -3,7 +3,6 @@ import static org.stellar.anchor.api.event.AnchorEvent.Type.TRANSACTION_STATUS_CHANGED; import static org.stellar.anchor.api.sep.SepTransactionStatus.ERROR; import static org.stellar.anchor.api.sep.SepTransactionStatus.EXPIRED; -import static org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_ANCHOR; import static org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_CUSTOMER_INFO_UPDATE; import static org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_USR_TRANSFER_START; import static org.stellar.anchor.event.EventService.EventQueue.TRANSACTION; @@ -43,8 +42,6 @@ import org.stellar.anchor.api.shared.FeeDetails; import org.stellar.anchor.api.shared.SepDepositInfo; import org.stellar.anchor.asset.AssetService; -import org.stellar.anchor.config.CustodyConfig; -import org.stellar.anchor.custody.CustodyService; import org.stellar.anchor.event.EventService; import org.stellar.anchor.event.EventService.Session; import org.stellar.anchor.platform.data.JdbcSep24Transaction; @@ -84,8 +81,6 @@ public class TransactionService { private final Sep6DepositInfoGenerator sep6DepositInfoGenerator; private final Sep24DepositInfoGenerator sep24DepositInfoGenerator; - private final CustodyService custodyService; - private final CustodyConfig custodyConfig; private final Counter findSep6TransactionCounter = Metrics.counter(PLATFORM_FIND_TRANSACTION, SEP, TV_SEP6); private final Counter findSep24TransactionCounter = @@ -124,9 +119,7 @@ public TransactionService( AssetService assetService, EventService eventService, Sep6DepositInfoGenerator sep6DepositInfoGenerator, - Sep24DepositInfoGenerator sep24DepositInfoGenerator, - CustodyService custodyService, - CustodyConfig custodyConfig) { + Sep24DepositInfoGenerator sep24DepositInfoGenerator) { this.txn6Store = txn6Store; this.txn24Store = txn24Store; this.txn31Store = txn31Store; @@ -136,8 +129,6 @@ public TransactionService( this.assetService = assetService; this.sep6DepositInfoGenerator = sep6DepositInfoGenerator; this.sep24DepositInfoGenerator = sep24DepositInfoGenerator; - this.custodyService = custodyService; - this.custodyConfig = custodyConfig; } /** @@ -263,7 +254,6 @@ private GetTransactionResponse patchTransaction(PatchTransactionRequest patch) throw new BadRequestException( String.format("transaction(id=%s) not found", patch.getTransaction().getId())); - String lastStatus = txn.getStatus(); updateSepTransaction(patch.getTransaction(), txn); switch (txn.getProtocol()) { case "6": @@ -271,11 +261,6 @@ private GetTransactionResponse patchTransaction(PatchTransactionRequest patch) Log.infoF( "Updating SEP-6 transaction: {}", GsonUtils.getInstance().toJson(sep6Transaction)); - boolean shouldCreateDepositTxn = - ImmutableSet.of(Kind.DEPOSIT, Kind.DEPOSIT_EXCHANGE) - .contains(Kind.from(sep6Transaction.getKind())) - // TODO: check if this is correct - && txn.getStatus().equals(PENDING_ANCHOR.toString()); boolean shouldCreateWithdrawTxn = ImmutableSet.of(Kind.WITHDRAWAL, Kind.WITHDRAWAL_EXCHANGE) .contains(Kind.from(sep6Transaction.getKind())) @@ -288,12 +273,6 @@ private GetTransactionResponse patchTransaction(PatchTransactionRequest patch) sep6Transaction.setMemoType(sep6DepositInfo.getMemoType()); } - if (custodyConfig.isCustodyIntegrationEnabled() - && !lastStatus.equals(sep6Transaction.getStatus()) - && (shouldCreateDepositTxn || shouldCreateWithdrawTxn)) { - custodyService.createTransaction(sep6Transaction); - } - if (feeDetails != null) { sep6Transaction.setFeeDetails(feeDetails); } @@ -312,15 +291,6 @@ private GetTransactionResponse patchTransaction(PatchTransactionRequest patch) case "24": JdbcSep24Transaction sep24Txn = (JdbcSep24Transaction) txn; - if (custodyConfig.isCustodyIntegrationEnabled() - && !lastStatus.equals(sep24Txn.getStatus()) - && ((Kind.DEPOSIT.getKind().equals(sep24Txn.getKind()) - && PENDING_ANCHOR.toString().equals(sep24Txn.getStatus())) - || (Kind.WITHDRAWAL.getKind().equals(sep24Txn.getKind()) - && PENDING_USR_TRANSFER_START.toString().equals(sep24Txn.getStatus())))) { - custodyService.createTransaction(sep24Txn); - } - if (feeDetails != null) { sep24Txn.setFeeDetails(feeDetails); } @@ -496,17 +466,12 @@ void validateIfStatusIsSupported(String status) throws BadRequestException { * @throws BadRequestException if the provided asset is not supported */ void validateAsset(String fieldName, Amount amount) throws BadRequestException { - validateAsset(fieldName, amount, false); - } - - void validateAsset(String fieldName, Amount amount, boolean allowZero) - throws BadRequestException { if (amount == null) { return; } // asset amount needs to be non-empty and valid - SepHelper.validateAmount(fieldName + ".", amount.getAmount(), allowZero); + SepHelper.validateAmount(fieldName + ".", amount.getAmount(), false); // asset name cannot be empty if (StringHelper.isEmpty(amount.getAsset())) { diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/callback/PlatformIntegrationHelperTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/callback/PlatformIntegrationHelperTest.kt index 4451a6935d..6533efd72c 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/callback/PlatformIntegrationHelperTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/callback/PlatformIntegrationHelperTest.kt @@ -47,7 +47,6 @@ class PlatformIntegrationHelperTest { JwtService.builder() .callbackAuthSecret("secret__________________________________") .platformAuthSecret("secret__________________________________") - .custodyAuthSecret("secret__________________________________") .build() val authHelper = AuthHelper.forJwtToken( diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/component/SepBeansTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/component/SepBeansTest.kt index 3b6671108c..bf6f9eddd1 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/component/SepBeansTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/component/SepBeansTest.kt @@ -11,7 +11,6 @@ import org.stellar.anchor.platform.component.sep.SepBeans class SepBeansTest { @MockK(relaxed = true) private lateinit var secretConfig: SecretConfig - @MockK(relaxed = true) lateinit var custodySecretConfig: CustodySecretConfig @MockK(relaxed = true) lateinit var sep38Config: Sep38Config private lateinit var jwtService: JwtService private lateinit var sepBeans: SepBeans @@ -19,9 +18,8 @@ class SepBeansTest { @BeforeEach fun setUp() { secretConfig = mockk(relaxed = true) - custodySecretConfig = mockk(relaxed = true) sep38Config = mockk(relaxed = true) - jwtService = JwtService(secretConfig, custodySecretConfig) + jwtService = JwtService(secretConfig) sepBeans = SepBeans() } diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/config/CustodyApiConfigTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/config/CustodyApiConfigTest.kt deleted file mode 100644 index 807be0564d..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/config/CustodyApiConfigTest.kt +++ /dev/null @@ -1,167 +0,0 @@ -package org.stellar.anchor.platform.config - -import io.mockk.every -import io.mockk.mockk -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.NullSource -import org.junit.jupiter.params.provider.ValueSource -import org.springframework.validation.BindException -import org.springframework.validation.Errors -import org.stellar.anchor.auth.AuthConfig -import org.stellar.anchor.auth.AuthType.JWT -import org.stellar.anchor.auth.AuthType.NONE -import org.stellar.anchor.config.CustodySecretConfig - -class CustodyApiConfigTest { - - private lateinit var config: CustodyApiConfig - private lateinit var secretConfig: CustodySecretConfig - private lateinit var errors: Errors - - @BeforeEach - fun setUp() { - secretConfig = mockk() - every { secretConfig.custodyAuthSecret } returns "testCustodyApiSecrettestCustodyApiSecret" - config = CustodyApiConfig(secretConfig) - config.baseUrl = "https://test.com" - val authConfig = AuthConfig() - authConfig.type = JWT - config.auth = authConfig - config.httpClient = HttpClientConfig(10, 30, 30, 60) - errors = BindException(config, "config") - } - - @ParameterizedTest - @NullSource - @ValueSource(strings = [""]) - fun `test empty host_url`(url: String?) { - config.baseUrl = url - config.validate(config, errors) - assertErrorCode(errors, "custody-server-base-url-empty") - } - - @ParameterizedTest - @ValueSource(strings = ["https://custody.com", "https://custody.org:8080"]) - fun `test valid host_url`(url: String) { - config.baseUrl = url - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(strings = ["https ://custody.com", "custody.com", "abc"]) - fun `test invalid host_url`(url: String) { - config.baseUrl = url - config.validate(config, errors) - assertErrorCode(errors, "custody-server-base-url-invalid") - } - - @ParameterizedTest - @NullSource - @ValueSource(strings = [""]) - fun `test empty api_key`(apiKey: String?) { - every { secretConfig.custodyAuthSecret } returns apiKey - config.validate(config, errors) - assertErrorCode(errors, "empty-secret-custody-server-secret") - } - - @ParameterizedTest - @NullSource - @ValueSource(strings = [""]) - fun `test empty api_key with NONE auth type`(apiKey: String?) { - every { secretConfig.custodyAuthSecret } returns apiKey - config.auth.type = NONE - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @Test - fun `test valid api_key`() { - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [0, 1, Int.MAX_VALUE]) - fun `test valid http_client_connect_timeout`(connectTimeout: Int) { - config.httpClient.connectTimeout = connectTimeout - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid http_client_connect_timeout`(connectTimeout: Int) { - config.httpClient.connectTimeout = connectTimeout - config.validate(config, errors) - assertErrorCode(errors, "custody-server-http-client-connect-timeout-invalid") - } - - @ParameterizedTest - @ValueSource(ints = [0, 1, Int.MAX_VALUE]) - fun `test valid http_client_read_timeout`(readTimeout: Int) { - config.httpClient.readTimeout = readTimeout - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid http_client_read_timeout`(readTimeout: Int) { - config.httpClient.readTimeout = readTimeout - config.validate(config, errors) - assertErrorCode(errors, "custody-server-http-client-read-timeout-invalid") - } - - @ParameterizedTest - @ValueSource(ints = [0, 1, Int.MAX_VALUE]) - fun `test valid http_client_write_timeout`(writeTimeout: Int) { - config.httpClient.writeTimeout = writeTimeout - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid http_client_write_timeout`(writeTimeout: Int) { - config.httpClient.writeTimeout = writeTimeout - config.validate(config, errors) - assertErrorCode(errors, "custody-server-http-client-write-timeout-invalid") - } - - @ParameterizedTest - @ValueSource(ints = [0, 1, Int.MAX_VALUE]) - fun `test valid http_client_call_timeout`(callTimeout: Int) { - config.httpClient.callTimeout = callTimeout - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid http_client_call_timeout`(callTimeout: Int) { - config.httpClient.callTimeout = callTimeout - config.validate(config, errors) - assertErrorCode(errors, "custody-server-http-client-call-timeout-invalid") - } - - @Test - fun `validate JWT`() { - every { secretConfig.custodyAuthSecret }.returns("tooshort") - config.setAuth( - AuthConfig( - JWT, - null, - AuthConfig.JwtConfig("30000", "Authorization"), - AuthConfig.ApiKeyConfig("X-Api-Key") - ) - ) - config.validate(config, errors) - Assertions.assertTrue(errors.hasErrors()) - assertErrorCode(errors, "hmac-weak-secret") - } -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/config/CustodyConfigTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/config/CustodyConfigTest.kt deleted file mode 100644 index a55f1874a3..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/config/CustodyConfigTest.kt +++ /dev/null @@ -1,169 +0,0 @@ -package org.stellar.anchor.platform.config - -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.EnumSource -import org.junit.jupiter.params.provider.ValueSource -import org.springframework.validation.BindException -import org.springframework.validation.Errors -import org.stellar.anchor.config.CustodyConfig.CustodyType -import org.stellar.anchor.config.CustodyConfig.CustodyType.FIREBLOCKS -import org.stellar.anchor.config.CustodyConfig.CustodyType.NONE -import org.stellar.anchor.platform.config.PropertyCustodyConfig.Trustline - -class PropertyCustodyConfigTest { - - private lateinit var config: PropertyCustodyConfig - private lateinit var errors: Errors - - @BeforeEach - fun setUp() { - config = PropertyCustodyConfig() - config.type = FIREBLOCKS - config.httpClient = HttpClientConfig(10, 30, 30, 60) - config.trustline = Trustline("* * * * * *", 10, "testMessage") - errors = BindException(config, "config") - } - - @ParameterizedTest - @EnumSource(names = ["NONE", "FIREBLOCKS"]) - fun `test valid type`(type: CustodyType) { - config.type = type - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @Test - fun `test empty type`() { - config.type = null - config.validate(config, errors) - assertErrorCode(errors, "custody-type-empty") - } - - @ParameterizedTest - @ValueSource(ints = [0, 1, Int.MAX_VALUE]) - fun `test valid http_client_connect_timeout`(connectTimeout: Int) { - config.httpClient.connectTimeout = connectTimeout - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid http_client_connect_timeout`(connectTimeout: Int) { - config.httpClient.connectTimeout = connectTimeout - config.validate(config, errors) - assertErrorCode(errors, "custody-http-client-connect-timeout-invalid") - } - - @ParameterizedTest - @ValueSource(ints = [0, 1, Int.MAX_VALUE]) - fun `test valid http_client_read_timeout`(readTimeout: Int) { - config.httpClient.readTimeout = readTimeout - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid http_client_read_timeout`(readTimeout: Int) { - config.httpClient.readTimeout = readTimeout - config.validate(config, errors) - assertErrorCode(errors, "custody-http-client-read-timeout-invalid") - } - - @ParameterizedTest - @ValueSource(ints = [0, 1, Int.MAX_VALUE]) - fun `test valid http_client_write_timeout`(writeTimeout: Int) { - config.httpClient.writeTimeout = writeTimeout - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid http_client_write_timeout`(writeTimeout: Int) { - config.httpClient.writeTimeout = writeTimeout - config.validate(config, errors) - assertErrorCode(errors, "custody-http-client-write-timeout-invalid") - } - - @ParameterizedTest - @ValueSource(ints = [0, 1, Int.MAX_VALUE]) - fun `test valid http_client_call_timeout`(callTimeout: Int) { - config.httpClient.callTimeout = callTimeout - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid http_client_call_timeout`(callTimeout: Int) { - config.httpClient.callTimeout = callTimeout - config.validate(config, errors) - assertErrorCode(errors, "custody-http-client-call-timeout-invalid") - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid http_client none type`(timeout: Int) { - config.type = NONE - config.httpClient.connectTimeout = timeout - config.httpClient.readTimeout = timeout - config.httpClient.writeTimeout = timeout - config.httpClient.callTimeout = timeout - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(strings = ["* * * * * *", "0 0/15 * * * *"]) - fun `test valid trustline_check_cron`(cron: String) { - config.trustline.checkCronExpression = cron - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(strings = [""]) - fun `test empty trustline_check_cron`(cron: String) { - config.trustline.checkCronExpression = cron - config.validate(config, errors) - assertErrorCode(errors, "custody-trustline-check_cron_expression-empty") - } - - @ParameterizedTest - @ValueSource(strings = ["* * * * *", "* * * * * * *", "0/a * * * * *"]) - fun `test invalid trustline_check_cron`(cron: String) { - config.trustline.checkCronExpression = cron - config.validate(config, errors) - assertErrorCode(errors, "custody-trustline-check_cron_expression-invalid") - } - - @ParameterizedTest - @ValueSource(ints = [1, Int.MAX_VALUE]) - fun `test valid trustline_check_duration`(duration: Int) { - config.trustline.checkDuration = duration - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [0, -1, Int.MIN_VALUE]) - fun `test invalid trustline_check_duration`(duration: Int) { - config.trustline.checkDuration = duration - config.validate(config, errors) - assertErrorCode(errors, "custody-trustline-check_duration-invalid") - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid trustline none type`(timeout: Int) { - config.type = NONE - config.trustline.checkDuration = timeout - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/config/FireblocksConfigTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/config/FireblocksConfigTest.kt deleted file mode 100644 index 63ca00209f..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/config/FireblocksConfigTest.kt +++ /dev/null @@ -1,230 +0,0 @@ -package org.stellar.anchor.platform.config - -import io.mockk.every -import io.mockk.mockk -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.NullSource -import org.junit.jupiter.params.provider.ValueSource -import org.springframework.validation.BindException -import org.springframework.validation.Errors -import org.stellar.anchor.api.exception.InvalidConfigException -import org.stellar.anchor.platform.config.FireblocksConfig.Reconciliation -import org.stellar.anchor.platform.config.FireblocksConfig.RetryConfig -import org.stellar.anchor.util.FileUtil - -class FireblocksConfigTest { - - private lateinit var config: FireblocksConfig - private lateinit var secretConfig: PropertyCustodySecretConfig - private lateinit var errors: Errors - - @BeforeEach - fun setUp() { - secretConfig = mockk() - every { secretConfig.fireblocksApiKey } returns "testApiKey" - every { secretConfig.fireblocksSecretKey } returns - FileUtil.getResourceFileAsString("custody/fireblocks/client/secret_key.txt") - config = FireblocksConfig(secretConfig) - config.retryConfig = RetryConfig(3, 1000) - config.baseUrl = "https://test.com" - config.vaultAccountId = "testAccountId" - config.reconciliation = Reconciliation(10, "* * * * * *") - config.publicKey = FileUtil.getResourceFileAsString("custody/fireblocks/client/public_key.txt") - errors = BindException(config, "config") - } - - @ParameterizedTest - @NullSource - @ValueSource(strings = [""]) - fun `test empty host_url`(url: String?) { - config.baseUrl = url - config.validate(config, errors) - assertErrorCode(errors, "custody-fireblocks-base-url-empty") - } - - @ParameterizedTest - @ValueSource(strings = ["https://fireblocks.com", "https://fireblocks.org:8080"]) - fun `test valid host_url`(url: String) { - config.baseUrl = url - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(strings = ["https ://fireblocks.com", "fireblocks.com", "abc"]) - fun `test invalid host_url`(url: String) { - config.baseUrl = url - config.validate(config, errors) - assertErrorCode(errors, "custody-fireblocks-base-url-invalid") - } - - @ParameterizedTest - @NullSource - @ValueSource(strings = [""]) - fun `test empty vault_account_id`(url: String?) { - config.vaultAccountId = url - config.validate(config, errors) - assertErrorCode(errors, "custody-fireblocks-vault-account-id-empty") - } - - @ParameterizedTest - @NullSource - @ValueSource(strings = [""]) - fun `test empty api_key`(apiKey: String?) { - every { secretConfig.fireblocksApiKey } returns apiKey - config.validate(config, errors) - assertErrorCode(errors, "secret-custody-fireblocks-api-key-empty") - } - - @Test - fun `test valid api_key`() { - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @NullSource - @ValueSource(strings = [""]) - fun `test empty secret_key`(secretKey: String?) { - every { secretConfig.fireblocksSecretKey } returns secretKey - config.validate(config, errors) - assertErrorCode(errors, "secret-custody-fireblocks-secret-key-empty") - } - - @ParameterizedTest - @ValueSource( - strings = - ["test_certificate", "-----BEGIN PRIVATE KEY----- test_certificate -----END PRIVATE KEY-----"] - ) - fun `test invalid secret_key`(secretKey: String) { - every { secretConfig.fireblocksSecretKey } returns secretKey - config.validate(config, errors) - assertErrorCode(errors, "secret-custody-fireblocks-secret_key-invalid") - } - - @Test - fun `test valid secret_key`() { - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(strings = ["* * * * * *", "0 0/15 * * * *"]) - fun `test valid transactions_reconciliation_cron`(cron: String) { - config.reconciliation.cronExpression = cron - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(strings = [""]) - fun `test empty transactions_reconciliation_cron`(cron: String) { - config.reconciliation.cronExpression = cron - config.validate(config, errors) - assertErrorCode(errors, "custody-fireblocks-reconciliation-cron_expression-empty") - } - - @ParameterizedTest - @ValueSource(strings = ["* * * * *", "* * * * * * *", "0/a * * * * *"]) - fun `test invalid transactions_reconciliation_cron`(cron: String) { - config.reconciliation.cronExpression = cron - config.validate(config, errors) - assertErrorCode(errors, "custody-fireblocks-reconciliation-cron_expression-invalid") - } - - @ParameterizedTest - @ValueSource(ints = [0, 1, Int.MAX_VALUE]) - fun `test valid reconciliation_max_attempts`(maxAttempts: Int) { - config.reconciliation.maxAttempts = maxAttempts - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid reconciliation_max_attempts`(maxAttempts: Int) { - config.reconciliation.maxAttempts = maxAttempts - config.validate(config, errors) - assertErrorCode(errors, "custody-fireblocks-reconciliation-max_attempts-invalid") - } - - @Test - fun `test valid public_key`() { - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(strings = [""]) - fun `test empty public_key`(publicKey: String) { - config.publicKey = publicKey - config.validate(config, errors) - assertErrorCode(errors, "custody-fireblocks-public_key-empty") - } - - @ParameterizedTest - @ValueSource( - strings = - ["test_certificate", "-----BEGIN PUBLIC KEY----- test_certificate -----END PUBLIC KEY-----"] - ) - fun `test invalid public_key`(publicKey: String) { - config.publicKey = publicKey - config.validate(config, errors) - assertErrorCode(errors, "custody-fireblocks-public_key-invalid") - } - - @ParameterizedTest - @ValueSource(ints = [0, 1, Int.MAX_VALUE]) - fun `test valid retry_config_max_attempts`(maxAttempts: Int) { - config.retryConfig.maxAttempts = maxAttempts - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid retry_config_max_attempts`(maxAttempts: Int) { - config.retryConfig.maxAttempts = maxAttempts - config.validate(config, errors) - assertErrorCode(errors, "custody-fireblocks-retry_config-max_attempts-invalid") - } - - @ParameterizedTest - @ValueSource(ints = [0, 1, Int.MAX_VALUE]) - fun `test valid retry_config_delay`(delay: Int) { - config.retryConfig.delay = delay - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @ParameterizedTest - @ValueSource(ints = [-1, Int.MIN_VALUE]) - fun `test invalid retry_config_delay`(delay: Int) { - config.retryConfig.delay = delay - config.validate(config, errors) - assertErrorCode(errors, "custody-fireblocks-retry_config-delay-invalid") - } - - @ParameterizedTest - @ValueSource(strings = [""]) - fun `test empty asset mappings`(mappings: String) { - config.setAssetMappings(mappings) - assertTrue(config.assetMappings.isEmpty()) - } - - @ParameterizedTest - @ValueSource(strings = ["FIREBLOCKS_ASSET_CODE STELLAR_ASSET_CODE"]) - fun `test getFireblocksAssetCode`(mappings: String) { - config.setAssetMappings(mappings) - val stellarAssetCode = config.getFireblocksAssetCode("STELLAR_ASSET_CODE") - assertEquals("FIREBLOCKS_ASSET_CODE", stellarAssetCode) - - assertThrows { config.getFireblocksAssetCode("INVALID_ASSET_CODE") } - } -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/config/PropertyQueueConfigTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/config/PropertyQueueConfigTest.kt index b95d208191..11b9181e9c 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/config/PropertyQueueConfigTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/config/PropertyQueueConfigTest.kt @@ -44,8 +44,7 @@ class PropertyQueueConfigTest { @ParameterizedTest @ValueSource(strings = [""]) - @NullSource - fun `test empty bootstrap servers`(bootstrapServer: String?) { + fun `test empty bootstrap servers`() { configs.kafka.bootstrapServer = null configs.validate(configs, errors) diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/config/Sep24ConfigTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/config/Sep24ConfigTest.kt index 56462920b8..807b7e9a5b 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/config/Sep24ConfigTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/config/Sep24ConfigTest.kt @@ -12,12 +12,11 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource import org.springframework.validation.BindException import org.springframework.validation.Errors +import org.stellar.anchor.api.asset.StellarAssetInfo import org.stellar.anchor.asset.AssetService import org.stellar.anchor.asset.DefaultAssetService -import org.stellar.anchor.config.CustodyConfig import org.stellar.anchor.config.SecretConfig import org.stellar.anchor.config.Sep24Config.DepositInfoGeneratorType -import org.stellar.anchor.config.Sep24Config.Features import org.stellar.anchor.platform.config.PropertySep24Config.InteractiveUrlConfig import org.stellar.anchor.platform.utils.setupMock @@ -25,18 +24,15 @@ class Sep24ConfigTest { lateinit var config: PropertySep24Config lateinit var errors: Errors lateinit var secretConfig: SecretConfig - lateinit var custodyConfig: CustodyConfig lateinit var assetService: AssetService @BeforeEach fun setUp() { secretConfig = mockk() - custodyConfig = mockk() assetService = DefaultAssetService.fromJsonResource("test_assets.json") secretConfig.setupMock() - every { custodyConfig.isCustodyIntegrationEnabled } returns false - config = PropertySep24Config(secretConfig, custodyConfig, assetService) + config = PropertySep24Config(secretConfig, assetService) config.enabled = true errors = BindException(config, "config") config.interactiveUrl = InteractiveUrlConfig("https://www.stellar.org", 600, listOf("")) @@ -52,28 +48,8 @@ class Sep24ConfigTest { @Test fun `test invalid deposit info generator type`() { - config.depositInfoGeneratorType = DepositInfoGeneratorType.CUSTODY - config.validate(config, errors) - assertEquals("sep24-deposit-info-generator-type", errors.allErrors[0].code) - } - - @Test - fun `test valid sep24 configuration with custody integration`() { - every { custodyConfig.isCustodyIntegrationEnabled } returns true - config.features = Features() - config.features.accountCreation = false - config.features.claimableBalances = false - config.depositInfoGeneratorType = DepositInfoGeneratorType.CUSTODY - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @Test - fun `test invalid deposit info generator type with custody integration`() { - every { custodyConfig.isCustodyIntegrationEnabled } returns true - config.features = Features() - config.features.accountCreation = false - config.features.claimableBalances = false + config.depositInfoGeneratorType = DepositInfoGeneratorType.SELF + (assetService.assets[0] as StellarAssetInfo).distributionAccount = null config.validate(config, errors) assertEquals("sep24-deposit-info-generator-type", errors.allErrors[0].code) } @@ -143,36 +119,4 @@ class Sep24ConfigTest { config.validate(config, errors) assertEquals("sep24-more-info-url-jwt-expiration-not-valid", errors.allErrors[0].code) } - - @Test - fun `test validate accountCreation = true and claimableBalances = true with custody integration`() { - config.features = Features() - config.features.accountCreation = true - config.features.claimableBalances = true - config.depositInfoGeneratorType = DepositInfoGeneratorType.CUSTODY - every { custodyConfig.isCustodyIntegrationEnabled } returns true - config.validate(config, errors) - assertEquals("sep24-features-account_creation-not-supported", errors.allErrors[0].code) - assertEquals("sep24-features-claimable_balances-not-supported", errors.allErrors[1].code) - } - - @Test - fun `test validate accountCreation = false and claimableBalances = false with custody integration`() { - config.features = Features() - config.features.accountCreation = false - config.features.claimableBalances = false - config.depositInfoGeneratorType = DepositInfoGeneratorType.CUSTODY - every { custodyConfig.isCustodyIntegrationEnabled } returns true - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @Test - fun `test validate accountCreation = true and claimableBalances = true with disabled custody integration`() { - config.features = Features() - config.features.accountCreation = true - config.features.claimableBalances = true - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } } diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/config/Sep31ConfigTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/config/Sep31ConfigTest.kt index 43c896d444..0af97af98c 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/config/Sep31ConfigTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/config/Sep31ConfigTest.kt @@ -1,31 +1,26 @@ package org.stellar.anchor.platform.config -import io.mockk.every -import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.springframework.validation.BindException import org.springframework.validation.Errors +import org.stellar.anchor.api.asset.StellarAssetInfo import org.stellar.anchor.asset.AssetService import org.stellar.anchor.asset.DefaultAssetService -import org.stellar.anchor.config.CustodyConfig import org.stellar.anchor.config.Sep31Config.DepositInfoGeneratorType class Sep31ConfigTest { lateinit var config: PropertySep31Config lateinit var errors: Errors - lateinit var custodyConfig: CustodyConfig lateinit var assetService: AssetService @BeforeEach fun setUp() { - custodyConfig = mockk() assetService = DefaultAssetService.fromJsonResource("test_assets.json") - every { custodyConfig.isCustodyIntegrationEnabled } returns false - config = PropertySep31Config(custodyConfig, assetService) + config = PropertySep31Config(assetService) config.enabled = true errors = BindException(config, "config") config.depositInfoGeneratorType = DepositInfoGeneratorType.SELF @@ -39,22 +34,8 @@ class Sep31ConfigTest { @Test fun `test invalid deposit info generator type`() { - config.depositInfoGeneratorType = DepositInfoGeneratorType.CUSTODY - config.validate(config, errors) - assertEquals("sep31-deposit-info-generator-type", errors.allErrors[0].code) - } - - @Test - fun `test valid sep31 configuration with custody integration`() { - every { custodyConfig.isCustodyIntegrationEnabled } returns true - config.depositInfoGeneratorType = DepositInfoGeneratorType.CUSTODY - config.validate(config, errors) - assertFalse(errors.hasErrors()) - } - - @Test - fun `test invalid deposit info generator type with custody integration`() { - every { custodyConfig.isCustodyIntegrationEnabled } returns true + config.depositInfoGeneratorType = DepositInfoGeneratorType.SELF + (assetService.assets[0] as StellarAssetInfo).distributionAccount = null config.validate(config, errors) assertEquals("sep31-deposit-info-generator-type", errors.allErrors[0].code) } diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/config/Sep6ConfigTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/config/Sep6ConfigTest.kt index 02ff0288f0..4c2d283a3f 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/config/Sep6ConfigTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/config/Sep6ConfigTest.kt @@ -6,19 +6,15 @@ import io.mockk.impl.annotations.MockK import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.CsvSource import org.springframework.validation.BindException import org.springframework.validation.Errors import org.stellar.anchor.asset.AssetService import org.stellar.anchor.asset.DefaultAssetService -import org.stellar.anchor.config.CustodyConfig import org.stellar.anchor.config.SecretConfig import org.stellar.anchor.config.Sep6Config import org.stellar.anchor.platform.utils.setupMock class Sep6ConfigTest { - @MockK(relaxed = true) lateinit var custodyConfig: CustodyConfig @MockK(relaxed = true) lateinit var assetService: AssetService @MockK(relaxed = true) lateinit var secretConfig: SecretConfig lateinit var config: PropertySep6Config @@ -28,13 +24,11 @@ class Sep6ConfigTest { fun setUp() { MockKAnnotations.init(this, relaxUnitFun = true) assetService = DefaultAssetService.fromJsonResource("test_assets.json") - every { custodyConfig.isCustodyIntegrationEnabled } returns true secretConfig.setupMock {} config = - PropertySep6Config(custodyConfig, assetService, secretConfig).apply { + PropertySep6Config(assetService, secretConfig).apply { enabled = true features = Sep6Config.Features(false, false) - depositInfoGeneratorType = Sep6Config.DepositInfoGeneratorType.CUSTODY } config.moreInfoUrl = MoreInfoUrlConfig("https://www.stellar.org", 600, listOf("")) errors = BindException(config, "config") @@ -108,22 +102,4 @@ class Sep6ConfigTest { Assertions.assertTrue(errors.hasErrors()) assertErrorCode(errors, "hmac-weak-secret") } - - @CsvSource(value = ["NONE", "SELF"]) - @ParameterizedTest - fun `test validation rejecting custody enabled and non-custodial deposit info generator`( - type: String - ) { - config.depositInfoGeneratorType = Sep6Config.DepositInfoGeneratorType.valueOf(type) - config.validate(config, errors) - Assertions.assertEquals("sep6-deposit-info-generator-type", errors.allErrors[0].code) - } - - @Test - fun `test validation rejecting custody disabled and custodial deposit generator`() { - every { custodyConfig.isCustodyIntegrationEnabled } returns false - config.depositInfoGeneratorType = Sep6Config.DepositInfoGeneratorType.CUSTODY - config.validate(config, errors) - Assertions.assertEquals("sep6-deposit-info-generator-type", errors.allErrors[0].code) - } } diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/CustodyApiClientTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/custody/CustodyApiClientTest.kt deleted file mode 100644 index 5a871a084b..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/CustodyApiClientTest.kt +++ /dev/null @@ -1,242 +0,0 @@ -package org.stellar.anchor.platform.custody - -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.mockk -import io.mockk.slot -import java.io.IOException -import java.nio.charset.Charset -import okhttp3.* -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.ResponseBody.Companion.toResponseBody -import okio.Buffer -import org.apache.http.HttpStatus -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.stellar.anchor.api.custody.CreateCustodyTransactionRequest -import org.stellar.anchor.api.exception.CustodyException -import org.stellar.anchor.auth.AuthHelper -import org.stellar.anchor.platform.apiclient.CustodyApiClient -import org.stellar.anchor.platform.config.CustodyApiConfig -import org.stellar.anchor.util.AuthHeader -import org.stellar.anchor.util.GsonUtils - -class CustodyApiClientTest { - - companion object { - private const val AUTH_HEADER_NAME = "testApiKeyName" - private const val AUTH_HEADER_VALUE = "testApiKeyValue" - private const val BASE_URL = "http://testBaseUrl.com" - private const val ASSET_ID = "TEST_ASSET_ID" - private const val TXN_ID = "1" - private const val REQUEST_BODY = "{}" - } - - private val gson = GsonUtils.getInstance() - - @MockK(relaxed = true) private lateinit var httpClient: OkHttpClient - @MockK(relaxed = true) private lateinit var authHelper: AuthHelper - @MockK(relaxed = true) private lateinit var custodyApiConfig: CustodyApiConfig - - private lateinit var custodyApiClient: CustodyApiClient - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - - val authHeader = AuthHeader(AUTH_HEADER_NAME, AUTH_HEADER_VALUE) - every { authHelper.createAuthHeader() } returns authHeader - every { custodyApiConfig.baseUrl } returns BASE_URL - - custodyApiClient = CustodyApiClient(httpClient, authHelper, custodyApiConfig) - } - - @Test - fun test_createTransaction_success() { - val response = getMockResponse(200, createTransactionResponse) - val requestCapture = slot() - val call = mockk() - val request = - gson.fromJson(createTransactionRequest, CreateCustodyTransactionRequest::class.java) - - every { httpClient.newCall(capture(requestCapture)) } returns call - every { call.execute() } returns response - - custodyApiClient.createTransaction(request) - - Assertions.assertEquals( - "http://testbaseurl.com/transactions", - requestCapture.captured.url.toString() - ) - Assertions.assertEquals("testApiKeyValue", requestCapture.captured.header("testApiKeyName")) - JSONAssert.assertEquals( - createTransactionRequest, - requestBodyToString(requestCapture.captured.body), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_createTransaction_fail_IOException() { - val call = mockk() - val request = - gson.fromJson(createTransactionRequest, CreateCustodyTransactionRequest::class.java) - - every { httpClient.newCall(any()) } returns call - every { call.execute() } throws IOException("Custody IO exception") - - val exception = assertThrows { custodyApiClient.createTransaction(request) } - - Assertions.assertEquals("Exception occurred during request to Custody API", exception.message) - } - - @Test - fun test_createTransaction_fail_errorStatusCode() { - val response = getMockResponse(400, errorResponseBody) - val requestCapture = slot() - val call = mockk() - val request = - gson.fromJson(createTransactionRequest, CreateCustodyTransactionRequest::class.java) - - every { httpClient.newCall(capture(requestCapture)) } returns call - every { call.execute() } returns response - - val exception = assertThrows { custodyApiClient.createTransaction(request) } - - Assertions.assertEquals( - "Custody API returned an error. HTTP status[400], response[Custody error]", - exception.message - ) - } - - @Test - fun test_generateDepositAddress_success() { - val response = getMockResponse(200, createTransactionResponse) - val requestCapture = slot() - val call = mockk() - - every { httpClient.newCall(capture(requestCapture)) } returns call - every { call.execute() } returns response - - val responseAddress = custodyApiClient.generateDepositAddress(ASSET_ID) - - Assertions.assertEquals( - "http://testbaseurl.com/assets/TEST_ASSET_ID/addresses", - requestCapture.captured.url.toString() - ) - Assertions.assertEquals("testApiKeyValue", requestCapture.captured.header("testApiKeyName")) - - JSONAssert.assertEquals( - createTransactionResponse, - gson.toJson(responseAddress), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_createTransactionPayment_success() { - val response = getMockResponse(200, "{\"id\":\"1\"}") - val requestCapture = slot() - val call = mockk() - - every { httpClient.newCall(capture(requestCapture)) } returns call - every { call.execute() } returns response - - custodyApiClient.createTransactionPayment(TXN_ID, REQUEST_BODY) - - Assertions.assertEquals( - String.format("http://testbaseurl.com/transactions/%s/payments", TXN_ID), - requestCapture.captured.url.toString() - ) - Assertions.assertEquals("testApiKeyValue", requestCapture.captured.header("testApiKeyName")) - } - - @Test - fun test_createTransactionPayment_fail_IOException() { - val call = mockk() - every { httpClient.newCall(any()) } returns call - every { call.execute() } throws IOException("Custody IO exception") - - val exception = - assertThrows { - custodyApiClient.createTransactionPayment(TXN_ID, REQUEST_BODY) - } - - Assertions.assertEquals(HttpStatus.SC_SERVICE_UNAVAILABLE, exception.statusCode) - Assertions.assertEquals("Exception occurred during request to Custody API", exception.message) - } - - @Test - fun test_createCustodyTransaction_fail_errorStatusCode() { - val response = getMockResponse(429, "{\"rawMessage\":\"Too many requests\"}") - val requestCapture = slot() - val call = mockk() - - every { httpClient.newCall(capture(requestCapture)) } returns call - every { call.execute() } returns response - - val exception = - assertThrows { - custodyApiClient.createTransactionPayment(TXN_ID, REQUEST_BODY) - } - - Assertions.assertEquals(HttpStatus.SC_TOO_MANY_REQUESTS, exception.statusCode) - Assertions.assertEquals("{\"rawMessage\":\"Too many requests\"}", exception.rawMessage) - } - - private fun requestBodyToString(requestBody: RequestBody?): String { - val buffer = Buffer() - requestBody?.writeTo(buffer) - return buffer.readString(Charset.forName("UTF-8")) - } - - private fun getMockResponse(code: Int, responseJson: String): Response { - return Response.Builder() - .request(Request.Builder().url("http://test.com").build()) - .protocol(Protocol.HTTP_1_1) - .code(code) - .message("OK") - .body(responseJson.toResponseBody("application/json".toMediaTypeOrNull())) - .build() - } - - private val createTransactionRequest = - """ -{ - "id" : "testId", - "memo": "testMemo", - "memoType": "testMemoType", - "protocol": "testProtocol", - "fromAccount": "testFromAccount", - "toAccount": "testToAccount", - "amount": "testAmount", - "asset": "testAmountAsset", - "kind": "testKind" -} -""" - - private val createTransactionResponse = """ -{ -} -""" - - private val errorResponseBody = """ -{ - "rawErrorMessage": "Custody error" -} -""" - - private val generateDepositAddressResponse = - """ -{ - "address": "testAddress", - "memo": "testMemo", - "memoType": "hash" -} -""" -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/CustodyEventServiceTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/custody/CustodyEventServiceTest.kt deleted file mode 100644 index 62b7078c61..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/CustodyEventServiceTest.kt +++ /dev/null @@ -1,313 +0,0 @@ -package org.stellar.anchor.platform.custody - -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.verify -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource -import org.stellar.anchor.platform.data.JdbcCustodyTransaction -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo -import org.stellar.anchor.util.Log.debug - -class CustodyEventServiceTest { - - // test implementation - class CustodyEventServiceTestImpl( - custodyTransactionRepo: JdbcCustodyTransactionRepo, - sep6CustodyPaymentHandler: Sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler: Sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler: Sep31CustodyPaymentHandler - ) : - CustodyEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler - ) { - override fun handleEvent(event: String?, headers: MutableMap?) { - debug("Test implementation") - } - } - - @MockK(relaxed = true) private lateinit var custodyTransactionRepo: JdbcCustodyTransactionRepo - @MockK(relaxed = true) private lateinit var sep6CustodyPaymentHandler: Sep6CustodyPaymentHandler - @MockK(relaxed = true) private lateinit var sep24CustodyPaymentHandler: Sep24CustodyPaymentHandler - @MockK(relaxed = true) private lateinit var sep31CustodyPaymentHandler: Sep31CustodyPaymentHandler - - private lateinit var custodyEventService: CustodyEventService - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - custodyEventService = - CustodyEventServiceTestImpl( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler - ) - } - - @Test - fun test_handleEvent_transactionIsNotFound() { - val payment = - CustodyPayment.builder() - .externalTxId("testExternalTxId") - .to("testTo") - .transactionMemo("testMemo") - .assetType("credit_alphanum4") - .build() - - every { custodyTransactionRepo.findByExternalTxId(any()) } returns null - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - custodyEventService.handlePayment(payment) - - verify(exactly = 0) { sep6CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep6CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onSent(any(), any()) } - } - - @Test - fun test_handleEvent_notSupportedAssetType() { - val payment = - CustodyPayment.builder() - .externalTxId("testExternalTxId") - .to("testTo") - .transactionMemo("testMemo") - .assetType("test_credit_alphanum4") - .build() - - every { custodyTransactionRepo.findByExternalTxId(any()) } returns null - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - custodyEventService.handlePayment(payment) - - verify(exactly = 0) { sep6CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep6CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onSent(any(), any()) } - } - - @Test - fun test_handleEvent_sep6_receive() { - val payment = - CustodyPayment.builder() - .externalTxId("testExternalTxId") - .to("testTo") - .transactionMemo("testMemo") - .assetType("credit_alphanum4") - .build() - val txn = JdbcCustodyTransaction.builder().kind("receive").protocol("6").build() - - every { custodyTransactionRepo.findByExternalTxId("testExternalTxId") } returns txn - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - custodyEventService.handlePayment(payment) - - verify(exactly = 0) { sep6CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep6CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onSent(any(), any()) } - } - - @ValueSource(strings = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_handleEvent_sep6_deposit(kind: String) { - val payment = - CustodyPayment.builder() - .externalTxId("testExternalTxId") - .to("testTo") - .transactionMemo("testMemo") - .assetType("credit_alphanum4") - .build() - val txn = JdbcCustodyTransaction.builder().kind(kind).protocol("6").build() - - every { custodyTransactionRepo.findByExternalTxId("testExternalTxId") } returns txn - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - custodyEventService.handlePayment(payment) - - verify(exactly = 0) { sep6CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 1) { sep6CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onSent(txn, payment) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onSent(any(), any()) } - } - - @ValueSource(strings = ["withdrawal", "withdrawal-exchange"]) - @ParameterizedTest - fun test_handleEvent_sep6_withdrawal(kind: String) { - val payment = - CustodyPayment.builder() - .externalTxId("testExternalTxId") - .to("testTo") - .transactionMemo("testMemo") - .assetType("credit_alphanum4") - .build() - val txn = JdbcCustodyTransaction.builder().kind(kind).protocol("6").build() - - every { custodyTransactionRepo.findByExternalTxId(any()) } returns null - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc("testTo", "testMemo") - } returns txn - - custodyEventService.handlePayment(payment) - - verify(exactly = 1) { sep6CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep6CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onReceived(txn, payment) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onSent(any(), any()) } - } - - @Test - fun test_handleEvent_sep24_receive() { - val payment = - CustodyPayment.builder() - .externalTxId("testExternalTxId") - .to("testTo") - .transactionMemo("testMemo") - .assetType("credit_alphanum4") - .build() - val txn = JdbcCustodyTransaction.builder().kind("receive").protocol("24").build() - - every { custodyTransactionRepo.findByExternalTxId("testExternalTxId") } returns txn - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - custodyEventService.handlePayment(payment) - - verify(exactly = 0) { sep6CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep6CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onSent(any(), any()) } - } - - @Test - fun test_handleEvent_sep24_deposit() { - val payment = - CustodyPayment.builder() - .externalTxId("testExternalTxId") - .to("testTo") - .transactionMemo("testMemo") - .assetType("credit_alphanum4") - .build() - val txn = JdbcCustodyTransaction.builder().kind("deposit").protocol("24").build() - - every { custodyTransactionRepo.findByExternalTxId("testExternalTxId") } returns txn - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - custodyEventService.handlePayment(payment) - - verify(exactly = 0) { sep6CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep6CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 1) { sep24CustodyPaymentHandler.onSent(txn, payment) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onSent(any(), any()) } - } - - @Test - fun test_handleEvent_sep24_withdrawal() { - val payment = - CustodyPayment.builder() - .externalTxId("testExternalTxId") - .to("testTo") - .transactionMemo("testMemo") - .assetType("credit_alphanum4") - .build() - val txn = JdbcCustodyTransaction.builder().kind("withdrawal").protocol("24").build() - - every { custodyTransactionRepo.findByExternalTxId(any()) } returns null - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc("testTo", "testMemo") - } returns txn - - custodyEventService.handlePayment(payment) - - verify(exactly = 0) { sep6CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep6CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 1) { sep24CustodyPaymentHandler.onReceived(txn, payment) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onSent(any(), any()) } - } - - @Test - fun test_handleEvent_sep31_deposit() { - val payment = - CustodyPayment.builder() - .externalTxId("testExternalTxId") - .to("testTo") - .transactionMemo("testMemo") - .assetType("credit_alphanum4") - .build() - val txn = JdbcCustodyTransaction.builder().kind("deposit").protocol("31").build() - - every { custodyTransactionRepo.findByExternalTxId("testExternalTxId") } returns txn - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - custodyEventService.handlePayment(payment) - - verify(exactly = 0) { sep6CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep6CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onSent(any(), any()) } - } - - @Test - fun test_handleEvent_sep31_receive() { - val payment = - CustodyPayment.builder() - .externalTxId("testExternalTxId") - .to("testTo") - .transactionMemo("testMemo") - .assetType("credit_alphanum4") - .build() - val txn = JdbcCustodyTransaction.builder().kind("receive").protocol("31").build() - - every { custodyTransactionRepo.findByExternalTxId(any()) } returns txn - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc("testTo", "testMemo") - } returns null - - custodyEventService.handlePayment(payment) - - verify(exactly = 0) { sep6CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep6CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onReceived(any(), any()) } - verify(exactly = 0) { sep24CustodyPaymentHandler.onSent(any(), any()) } - verify(exactly = 1) { sep31CustodyPaymentHandler.onReceived(txn, payment) } - verify(exactly = 0) { sep31CustodyPaymentHandler.onSent(any(), any()) } - } -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/CustodyPaymentHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/custody/CustodyPaymentHandlerTest.kt deleted file mode 100644 index 219dddb9d0..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/CustodyPaymentHandlerTest.kt +++ /dev/null @@ -1,178 +0,0 @@ -package org.stellar.anchor.platform.custody - -import io.mockk.* -import io.mockk.impl.annotations.MockK -import org.junit.jupiter.api.Assertions.assertNull -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.stellar.anchor.platform.data.JdbcCustodyTransaction -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo -import org.stellar.anchor.util.GsonUtils -import org.stellar.anchor.util.Log.info - -class CustodyPaymentHandlerTest { - - // test implementation - class CustodyPaymentHandlerTestImpl(custodyTransactionRepo: JdbcCustodyTransactionRepo) : - CustodyPaymentHandler(custodyTransactionRepo) { - override fun onReceived(txn: JdbcCustodyTransaction?, payment: CustodyPayment?) { - info("Test implementation") - } - - override fun onSent(txn: JdbcCustodyTransaction?, payment: CustodyPayment?) { - info("Test implementation") - } - } - - private val gson = GsonUtils.getInstance() - - @MockK(relaxed = true) private lateinit var custodyTransactionRepo: JdbcCustodyTransactionRepo - - private lateinit var custodyPaymentHandler: CustodyPaymentHandler - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - custodyPaymentHandler = CustodyPaymentHandlerTestImpl(custodyTransactionRepo) - } - - @Test - fun test_validatePayment_unsupportedType_errorStatus() { - val txn = - gson.fromJson(custodyTransactionInputSep24DepositPayment, JdbcCustodyTransaction::class.java) - val payment = - gson.fromJson(custodyPaymentUnsupportedAssetErrorStatus, CustodyPayment::class.java) - - custodyPaymentHandler.validatePayment(txn, payment) - - assertNull(payment.getMessage()) - } - - @Test - fun test_validatePayment_validAsset() { - val txn = - gson.fromJson(custodyTransactionInputSep24DepositPayment, JdbcCustodyTransaction::class.java) - val payment = gson.fromJson(custodyPaymentValidAsset, CustodyPayment::class.java) - - custodyPaymentHandler.validatePayment(txn, payment) - - assertNull(payment.getMessage()) - } - - @Test - fun test_updateTransaction_withStellarTxId() { - val txn = - gson.fromJson(custodyTransactionInputSep24DepositPayment, JdbcCustodyTransaction::class.java) - val payment = gson.fromJson(custodyPaymentWithId, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - - custodyPaymentHandler.updateTransaction(txn, payment) - - JSONAssert.assertEquals( - custodyTransactionDbSep24DepositPayment, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - private val custodyPaymentUnsupportedAssetErrorStatus = - """ -{ - "id": "12345", - "externalTxId": "testEventId", - "type": "payment", - "from": "testFrom", - "to": "testTo", - "amount": "1.0000000", - "asset_type": "credit_alphanum100", - "asset_name": "testAmountInAsset", - "created_at": "2023-05-10T10:18:25.778Z", - "status": "ERROR", - "transaction_hash": "testTxHash", - "transaction_memo_type": "none", - "transaction_envelope": "testEnvelopeXdr" -} -""" - - private val custodyPaymentValidAsset = - """ -{ - "id": "12345", - "externalTxId": "testEventId", - "type": "payment", - "from": "testFrom", - "to": "testTo", - "amount": "1.0000000", - "assetType": "credit_alphanum4", - "assetName": "testAmountInAsset", - "createdAt": "2023-05-10T10:18:25.778Z", - "status": "SUCCESS", - "transactionHash": "testTxHash", - "transactionMemoType": "none", - "transactionEnvelope": "testEnvelopeXdr" -} -""" - - private val custodyPaymentWithId = - """ -{ - "id": "12345", - "externalTxId": "testEventId", - "type": "payment", - "from": "testFrom", - "to": "testTo", - "amount": "1.0000000", - "assetType": "credit_alphanum4", - "assetName": "testAmountInAsset", - "updatedAt": "2023-05-10T10:18:25.778Z", - "status": "SUCCESS", - "transactionHash": "testTxHash", - "transactionMemoType": "none", - "transactionEnvelope": "testEnvelopeXdr" -} -""" - - private val custodyTransactionDbSep24DepositPayment = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "completed", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "24", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "deposit", - "reconciliation_attempt_count": 0, - "type": "payment" -} -""" - - private val custodyTransactionInputSep24DepositPayment = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "status": "submitted", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "24", - "from_account": "testFromAccount1", - "to_account": "testToAccount", - "kind": "deposit", - "type": "payment" -} -""" -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/CustodyTransactionServiceTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/custody/CustodyTransactionServiceTest.kt deleted file mode 100644 index 2c5ec0d9bf..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/CustodyTransactionServiceTest.kt +++ /dev/null @@ -1,401 +0,0 @@ -package org.stellar.anchor.platform.custody - -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.slot -import io.mockk.verify -import java.time.Instant -import kotlin.test.assertTrue -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.skyscreamer.jsonassert.Customization -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.skyscreamer.jsonassert.comparator.CustomComparator -import org.stellar.anchor.api.custody.CreateCustodyTransactionRequest -import org.stellar.anchor.api.custody.CreateTransactionRefundRequest -import org.stellar.anchor.api.exception.FireblocksException -import org.stellar.anchor.api.exception.custody.CustodyBadRequestException -import org.stellar.anchor.api.exception.custody.CustodyNotFoundException -import org.stellar.anchor.api.exception.custody.CustodyServiceUnavailableException -import org.stellar.anchor.api.exception.custody.CustodyTooManyRequestsException -import org.stellar.anchor.platform.data.JdbcCustodyTransaction -import org.stellar.anchor.platform.data.JdbcCustodyTransaction.PaymentType.PAYMENT -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo -import org.stellar.anchor.util.GsonUtils - -class CustodyTransactionServiceTest { - - companion object { - private const val TRANSACTION_ID = "TRANSACTION_ID" - private const val REFUND_TRANSACTION_ID = "REFUND_TRANSACTION_ID" - private const val REQUEST_BODY = "REQUEST_BODY" - } - - private val gson = GsonUtils.getInstance() - - @MockK(relaxed = true) private lateinit var custodyTransactionRepo: JdbcCustodyTransactionRepo - @MockK(relaxed = true) private lateinit var custodyPaymentService: CustodyPaymentService - - private lateinit var custodyTransactionService: CustodyTransactionService - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - custodyTransactionService = - CustodyTransactionService(custodyTransactionRepo, custodyPaymentService) - } - - @Test - fun test_create_success() { - val request = - gson.fromJson(createCustodyTransactionRequest, CreateCustodyTransactionRequest::class.java) - val entityCapture = slot() - - every { custodyTransactionRepo.save(capture(entityCapture)) } returns null - - custodyTransactionService.create(request, PAYMENT) - - val actualCustodyTransaction = entityCapture.captured - assertTrue(!Instant.now().isBefore(actualCustodyTransaction.createdAt)) - actualCustodyTransaction.createdAt = null - JSONAssert.assertEquals( - createCustodyTransactionEntity, - gson.toJson(entityCapture.captured), - CustomComparator(JSONCompareMode.STRICT, Customization("id") { _, _ -> true }) - ) - } - - @Test - fun test_createPayment_transaction_does_not_exist() { - every { - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc(any(), any()) - } returns null - - val exception = - assertThrows { - custodyTransactionService.createPayment(TRANSACTION_ID, REQUEST_BODY) - } - Assertions.assertEquals("Transaction (id=TRANSACTION_ID) is not found", exception.message) - - verify(exactly = 0) { custodyPaymentService.createTransactionPayment(any(), any()) } - } - - @Test - fun test_createPayment_transaction_exists() { - val transaction = JdbcCustodyTransaction() - every { - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc( - TRANSACTION_ID, - PAYMENT.type - ) - } returns transaction - every { custodyTransactionRepo.save(any()) } returns null - - custodyTransactionService.createPayment(TRANSACTION_ID, REQUEST_BODY) - - verify(exactly = 1) { - custodyPaymentService.createTransactionPayment(transaction, REQUEST_BODY) - } - } - - @Test - fun test_createPayment_bad_request() { - val transaction = JdbcCustodyTransaction() - every { - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc( - TRANSACTION_ID, - PAYMENT.type - ) - } returns transaction - every { custodyTransactionRepo.save(any()) } returns null - every { custodyPaymentService.createTransactionPayment(transaction, REQUEST_BODY) } throws - FireblocksException("Bad request", 400) - - val ex = - assertThrows { - custodyTransactionService.createPayment(TRANSACTION_ID, REQUEST_BODY) - } - Assertions.assertEquals("Bad request", ex.message) - } - - @Test - fun test_createPayment_too_many_requests() { - val transaction = JdbcCustodyTransaction() - every { - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc( - TRANSACTION_ID, - PAYMENT.type - ) - } returns transaction - every { custodyTransactionRepo.save(any()) } returns null - every { custodyPaymentService.createTransactionPayment(transaction, REQUEST_BODY) } throws - FireblocksException("Too many requests", 429) - - val ex = - assertThrows { - custodyTransactionService.createPayment(TRANSACTION_ID, REQUEST_BODY) - } - Assertions.assertEquals("Too many requests", ex.message) - } - - @Test - fun test_createPayment_service_unavailable() { - val transaction = JdbcCustodyTransaction() - every { - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc( - TRANSACTION_ID, - PAYMENT.type - ) - } returns transaction - every { custodyTransactionRepo.save(any()) } returns null - every { custodyPaymentService.createTransactionPayment(transaction, REQUEST_BODY) } throws - FireblocksException("Service unavailable", 503) - - val ex = - assertThrows { - custodyTransactionService.createPayment(TRANSACTION_ID, REQUEST_BODY) - } - Assertions.assertEquals("Service unavailable", ex.message) - } - - @Test - fun test_createPayment_unexpected_status_code() { - val transaction = JdbcCustodyTransaction() - every { - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc( - TRANSACTION_ID, - PAYMENT.type - ) - } returns transaction - every { custodyTransactionRepo.save(any()) } returns null - every { custodyPaymentService.createTransactionPayment(transaction, REQUEST_BODY) } throws - FireblocksException("Forbidden", 403) - - val ex = - assertThrows { - custodyTransactionService.createPayment(TRANSACTION_ID, REQUEST_BODY) - } - Assertions.assertEquals( - "Fireblocks API returned an error. HTTP status[403], response[Forbidden]", - ex.message - ) - } - - @Test - fun test_createRefund_transaction_does_not_exist() { - val request = gson.fromJson(refundRequest, CreateTransactionRefundRequest::class.java) - - every { - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc(any(), any()) - } returns null - - val exception = - assertThrows { - custodyTransactionService.createRefund(TRANSACTION_ID, request) - } - Assertions.assertEquals("Transaction (id=TRANSACTION_ID) is not found", exception.message) - - verify(exactly = 0) { custodyPaymentService.createTransactionPayment(any(), any()) } - verify(exactly = 0) { custodyTransactionRepo.deleteById(any()) } - } - - @Test - fun test_createRefund_transaction_exists() { - val request = gson.fromJson(refundRequest, CreateTransactionRefundRequest::class.java) - val transaction = gson.fromJson(custodyTransactionPayment, JdbcCustodyTransaction::class.java) - val refundTransaction = JdbcCustodyTransaction() - refundTransaction.id = REFUND_TRANSACTION_ID - val custodyTxCapture = slot() - - every { - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc( - TRANSACTION_ID, - PAYMENT.type - ) - } returns transaction - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns refundTransaction - - custodyTransactionService.createRefund(TRANSACTION_ID, request) - - verify(exactly = 1) { custodyPaymentService.createTransactionPayment(refundTransaction, null) } - } - - @Test - fun test_createRefund_bad_request() { - val request = gson.fromJson(refundRequest, CreateTransactionRefundRequest::class.java) - val transaction = JdbcCustodyTransaction() - val refundTransaction = JdbcCustodyTransaction() - refundTransaction.id = REFUND_TRANSACTION_ID - - every { - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc( - TRANSACTION_ID, - PAYMENT.type - ) - } returns transaction - every { custodyTransactionRepo.save(any()) } returns refundTransaction - every { custodyPaymentService.createTransactionPayment(refundTransaction, null) } throws - FireblocksException("Bad request", 400) - - val ex = - assertThrows { - custodyTransactionService.createRefund(TRANSACTION_ID, request) - } - Assertions.assertEquals("Bad request", ex.message) - - verify(exactly = 1) { custodyTransactionRepo.deleteById(REFUND_TRANSACTION_ID) } - } - - @Test - fun test_createRefund_too_many_requests() { - val request = gson.fromJson(refundRequest, CreateTransactionRefundRequest::class.java) - val transaction = JdbcCustodyTransaction() - val refundTransaction = JdbcCustodyTransaction() - refundTransaction.id = REFUND_TRANSACTION_ID - - every { - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc( - TRANSACTION_ID, - PAYMENT.type - ) - } returns transaction - every { custodyTransactionRepo.save(any()) } returns refundTransaction - every { custodyPaymentService.createTransactionPayment(refundTransaction, null) } throws - FireblocksException("Too many requests", 429) - - val ex = - assertThrows { - custodyTransactionService.createRefund(TRANSACTION_ID, request) - } - Assertions.assertEquals("Too many requests", ex.message) - - verify(exactly = 1) { custodyTransactionRepo.deleteById(REFUND_TRANSACTION_ID) } - } - - @Test - fun test_createRefund_service_unavailable() { - val request = gson.fromJson(refundRequest, CreateTransactionRefundRequest::class.java) - val transaction = JdbcCustodyTransaction() - val refundTransaction = JdbcCustodyTransaction() - refundTransaction.id = REFUND_TRANSACTION_ID - - every { - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc( - TRANSACTION_ID, - PAYMENT.type - ) - } returns transaction - every { custodyTransactionRepo.save(any()) } returns refundTransaction - every { custodyPaymentService.createTransactionPayment(refundTransaction, null) } throws - FireblocksException("Service unavailable", 503) - - val ex = - assertThrows { - custodyTransactionService.createRefund(TRANSACTION_ID, request) - } - Assertions.assertEquals("Service unavailable", ex.message) - - verify(exactly = 1) { custodyTransactionRepo.deleteById(REFUND_TRANSACTION_ID) } - } - - @Test - fun test_createRefund_unexpected_status_code() { - val request = gson.fromJson(refundRequest, CreateTransactionRefundRequest::class.java) - val transaction = JdbcCustodyTransaction() - val refundTransaction = JdbcCustodyTransaction() - refundTransaction.id = REFUND_TRANSACTION_ID - - every { - custodyTransactionRepo.findFirstBySepTxIdAndTypeOrderByCreatedAtAsc( - TRANSACTION_ID, - PAYMENT.type - ) - } returns transaction - every { custodyTransactionRepo.save(any()) } returns refundTransaction - every { custodyPaymentService.createTransactionPayment(refundTransaction, null) } throws - FireblocksException("Forbidden", 403) - - val ex = - assertThrows { - custodyTransactionService.createRefund(TRANSACTION_ID, request) - } - Assertions.assertEquals( - "Fireblocks API returned an error. HTTP status[403], response[Forbidden]", - ex.message - ) - - verify(exactly = 1) { custodyTransactionRepo.deleteById(REFUND_TRANSACTION_ID) } - } - - private val createCustodyTransactionEntity = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "status": "created", - "amount": "testAmount", - "asset": "testAmountAsset", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "testProtocol", - "from_account": "testFromAccount", - "to_account": "testToAccount", - "kind": "testKind", - "reconciliation_attempt_count": 0, - "type":"payment" -} -""" - - private val createCustodyTransactionRequest = - """ -{ - "id" : "testId", - "memo": "testMemo", - "memoType": "testMemoType", - "protocol": "testProtocol", - "fromAccount": "testFromAccount", - "toAccount": "testToAccount", - "amount": "testAmount", - "asset": "testAmountAsset", - "kind": "testKind", - "requestAssetCode": "testRequestAssetCode", - "requestAssetIssuer": "testRequestAssetIssuer", - "type":"payment" -} -""" - - private val custodyTransactionPayment = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "completed", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "24", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "withdrawal", - "reconciliation_attempt_count": 0, - "type": "payment" -} -""" - - private val refundRequest = - """ -{ - "memo": "testMemo", - "memoType": "testMemoType", - "amount": "testAmount", - "amountFee": "testAmountFee" -} -""" -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/Sep24CustodyPaymentHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/custody/Sep24CustodyPaymentHandlerTest.kt deleted file mode 100644 index 4831514d2d..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/Sep24CustodyPaymentHandlerTest.kt +++ /dev/null @@ -1,395 +0,0 @@ -package org.stellar.anchor.platform.custody - -import io.micrometer.core.instrument.Counter -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.slot -import io.mockk.verify -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.stellar.anchor.apiclient.PlatformApiClient -import org.stellar.anchor.metrics.MetricsService -import org.stellar.anchor.platform.config.RpcConfig -import org.stellar.anchor.platform.data.JdbcCustodyTransaction -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo -import org.stellar.anchor.platform.service.AnchorMetrics.PAYMENT_RECEIVED -import org.stellar.anchor.platform.service.AnchorMetrics.PAYMENT_SENT -import org.stellar.anchor.util.GsonUtils - -class Sep24CustodyPaymentHandlerTest { - - @MockK(relaxed = true) private lateinit var custodyTransactionRepo: JdbcCustodyTransactionRepo - - @MockK(relaxed = true) private lateinit var platformApiClient: PlatformApiClient - - @MockK(relaxed = true) private lateinit var paymentReceivedCounter: Counter - - @MockK(relaxed = true) private lateinit var paymentSentCounter: Counter - - @MockK(relaxed = true) private lateinit var rpcConfig: RpcConfig - - @MockK(relaxed = true) private lateinit var metricsService: MetricsService - - private lateinit var sep24CustodyPaymentHandler: Sep24CustodyPaymentHandler - - private val gson = GsonUtils.getInstance() - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - sep24CustodyPaymentHandler = - Sep24CustodyPaymentHandler( - custodyTransactionRepo, - platformApiClient, - rpcConfig, - metricsService - ) - } - - @Test - fun test_handleEvent_onReceived_payment_success() { - val txn = - gson.fromJson( - custodyTransactionInputSep24WithdrawalPayment, - JdbcCustodyTransaction::class.java - ) - val payment = gson.fromJson(custodyPaymentWithId, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.incomingPaymentReceived } returns "payment received" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - every { metricsService.counter(PAYMENT_RECEIVED, "asset", "testAmountInAsset") } returns - paymentReceivedCounter - - sep24CustodyPaymentHandler.onReceived(txn, payment) - - verify(exactly = 1) { paymentReceivedCounter.increment(1.0000000) } - verify(exactly = 1) { - platformApiClient.notifyOnchainFundsReceived( - txn.id, - payment.transactionHash, - payment.amount, - "payment received" - ) - } - - JSONAssert.assertEquals( - custodyTransactionDbSep24WithdrawalPayment, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_handleEvent_onReceived_refund_success() { - val txn = - gson.fromJson( - custodyTransactionInputSep24WithdrawalRefund, - JdbcCustodyTransaction::class.java - ) - val payment = gson.fromJson(custodyPaymentWithId, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.incomingPaymentReceived } returns "payment received" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - - sep24CustodyPaymentHandler.onReceived(txn, payment) - - verify(exactly = 0) { metricsService.counter(PAYMENT_RECEIVED, any(), any()) } - verify(exactly = 1) { - platformApiClient.notifyRefundSent( - txn.id, - payment.transactionHash, - payment.amount, - txn.amountFee, - txn.asset - ) - } - - JSONAssert.assertEquals( - custodyTransactionDbSep24WithdrawalRefund, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_handleEvent_onReceived_payment_error() { - val txn = - gson.fromJson( - custodyTransactionInputSep24WithdrawalPayment, - JdbcCustodyTransaction::class.java - ) - val payment = gson.fromJson(custodyPaymentWithIdError, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.custodyTransactionFailed } returns "payment failed" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - - sep24CustodyPaymentHandler.onReceived(txn, payment) - - verify(exactly = 0) { metricsService.counter(PAYMENT_SENT, any(), any()) } - verify(exactly = 1) { platformApiClient.notifyTransactionError(txn.id, "payment failed") } - - JSONAssert.assertEquals( - custodyTransactionDbSep24WithdrawalPaymentError, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_handleEvent_onSent_success() { - val txn = - gson.fromJson(custodyTransactionInputSep24DepositPayment, JdbcCustodyTransaction::class.java) - val payment = gson.fromJson(custodyPaymentWithId, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.outgoingPaymentSent } returns "payment sent" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - every { metricsService.counter(PAYMENT_SENT, "asset", "testAmountInAsset") } returns - paymentSentCounter - - sep24CustodyPaymentHandler.onSent(txn, payment) - - verify(exactly = 1) { paymentSentCounter.increment(1.0000000) } - verify(exactly = 1) { - platformApiClient.notifyOnchainFundsSent(txn.id, payment.transactionHash, "payment sent") - } - - JSONAssert.assertEquals( - custodyTransactionDbSep24DepositPayment, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_handleEvent_onSent_payment_error() { - val txn = - gson.fromJson(custodyTransactionInputSep24DepositPayment, JdbcCustodyTransaction::class.java) - val payment = gson.fromJson(custodyPaymentWithIdError, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.custodyTransactionFailed } returns "payment failed" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - - sep24CustodyPaymentHandler.onSent(txn, payment) - - verify(exactly = 0) { metricsService.counter(PAYMENT_SENT, any(), any()) } - verify(exactly = 1) { platformApiClient.notifyTransactionError(txn.id, "payment failed") } - - JSONAssert.assertEquals( - custodyTransactionDbSep24DepositPaymentError, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - private val custodyPaymentWithId = - """ -{ - "id": "12345", - "externalTxId": "testEventId", - "type": "payment", - "from": "testFrom", - "to": "testTo", - "amount": "1.0000000", - "assetType": "credit_alphanum4", - "assetName": "testAmountInAsset", - "updatedAt": "2023-05-10T10:18:25.778Z", - "status": "SUCCESS", - "transactionHash": "testTxHash", - "transactionMemoType": "none", - "transactionEnvelope": "testEnvelopeXdr" -} -""" - - private val custodyPaymentWithIdError = - """ - { - "id": "12345", - "externalTxId": "testEventId", - "type": "payment", - "from": "testFrom", - "to": "testTo", - "amount": "1.0000000", - "assetType": "credit_alphanum4", - "assetName": "testAmountInAsset", - "updatedAt": "2023-05-10T10:18:25.778Z", - "status": "ERROR", - "transactionHash": "testTxHash", - "transactionMemoType": "none", - "transactionEnvelope": "testEnvelopeXdr" -} -""" - - private val custodyTransactionDbSep24DepositPayment = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "completed", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "24", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "deposit", - "reconciliation_attempt_count": 0, - "type": "payment" -} -""" - - private val custodyTransactionDbSep24DepositPaymentError = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "failed", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "24", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "deposit", - "reconciliation_attempt_count": 0, - "type": "payment" -} -""" - - private val custodyTransactionDbSep24WithdrawalPayment = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "completed", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "24", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "withdrawal", - "reconciliation_attempt_count": 0, - "type": "payment" -} -""" - - private val custodyTransactionDbSep24WithdrawalPaymentError = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "failed", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "24", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "withdrawal", - "reconciliation_attempt_count": 0, - "type": "payment" -} -""" - - private val custodyTransactionDbSep24WithdrawalRefund = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "completed", - "amount": "1", - "amount_fee": "0.1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "24", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "withdrawal", - "reconciliation_attempt_count": 0, - "type": "refund" -} -""" - - private val custodyTransactionInputSep24DepositPayment = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "status": "submitted", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "24", - "from_account": "testFromAccount1", - "to_account": "testToAccount", - "kind": "deposit", - "type": "payment" -} -""" - - private val custodyTransactionInputSep24WithdrawalPayment = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "status": "submitted", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "24", - "from_account": "testFromAccount1", - "to_account": "testToAccount", - "kind": "withdrawal", - "type": "payment" -} -""" - - private val custodyTransactionInputSep24WithdrawalRefund = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "status": "submitted", - "amount": "1", - "amount_fee": "0.1", - "asset": "stellar:testAmountInAsset", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "24", - "from_account": "testFromAccount1", - "to_account": "testToAccount", - "kind": "withdrawal", - "type": "refund" -} -""" -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/Sep31CustodyPaymentHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/custody/Sep31CustodyPaymentHandlerTest.kt deleted file mode 100644 index 6b0962f05c..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/Sep31CustodyPaymentHandlerTest.kt +++ /dev/null @@ -1,286 +0,0 @@ -package org.stellar.anchor.platform.custody - -import io.micrometer.core.instrument.Counter -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.slot -import io.mockk.verify -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.stellar.anchor.apiclient.PlatformApiClient -import org.stellar.anchor.metrics.MetricsService -import org.stellar.anchor.platform.config.RpcConfig -import org.stellar.anchor.platform.data.JdbcCustodyTransaction -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo -import org.stellar.anchor.platform.service.AnchorMetrics.PAYMENT_RECEIVED -import org.stellar.anchor.platform.service.AnchorMetrics.PAYMENT_SENT -import org.stellar.anchor.util.GsonUtils - -class Sep31CustodyPaymentHandlerTest { - - @MockK(relaxed = true) private lateinit var custodyTransactionRepo: JdbcCustodyTransactionRepo - - @MockK(relaxed = true) private lateinit var platformApiClient: PlatformApiClient - - @MockK(relaxed = true) private lateinit var paymentReceivedCounter: Counter - - @MockK(relaxed = true) private lateinit var rpcConfig: RpcConfig - - @MockK(relaxed = true) private lateinit var metricsService: MetricsService - - private lateinit var sep31CustodyPaymentHandler: Sep31CustodyPaymentHandler - - private val gson = GsonUtils.getInstance() - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - sep31CustodyPaymentHandler = - Sep31CustodyPaymentHandler( - custodyTransactionRepo, - platformApiClient, - rpcConfig, - metricsService - ) - } - - @Test - fun test_handleEvent_onReceived_payment() { - val txn = - gson.fromJson(custodyTransactionInputSep31ReceivePayment, JdbcCustodyTransaction::class.java) - val payment = gson.fromJson(custodyPaymentWithId, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.incomingPaymentReceived } returns "payment received" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - every { metricsService.counter(PAYMENT_RECEIVED, "asset", "testAmountInAsset") } returns - paymentReceivedCounter - - sep31CustodyPaymentHandler.onReceived(txn, payment) - - verify(exactly = 1) { paymentReceivedCounter.increment(1.0000000) } - verify(exactly = 1) { - platformApiClient.notifyOnchainFundsReceived( - txn.id, - payment.transactionHash, - payment.amount, - "payment received" - ) - } - - JSONAssert.assertEquals( - custodyTransactionDbSep31ReceivePayment, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_handleEvent_onReceived_refund() { - val txn = - gson.fromJson(custodyTransactionInputSep31ReceiveRefund, JdbcCustodyTransaction::class.java) - val payment = gson.fromJson(custodyPaymentWithId, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.incomingPaymentReceived } returns "payment received" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - - sep31CustodyPaymentHandler.onReceived(txn, payment) - - verify(exactly = 0) { metricsService.counter(PAYMENT_SENT, any(), any()) } - verify(exactly = 1) { - platformApiClient.notifyRefundSent( - txn.id, - payment.transactionHash, - payment.amount, - txn.amountFee, - txn.asset - ) - } - - JSONAssert.assertEquals( - custodyTransactionDbSep31ReceiveRefund, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_handleEvent_onReceived_payment_error() { - val txn = - gson.fromJson(custodyTransactionInputSep31ReceivePayment, JdbcCustodyTransaction::class.java) - val payment = gson.fromJson(custodyPaymentWithIdError, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.custodyTransactionFailed } returns "payment failed" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - - sep31CustodyPaymentHandler.onReceived(txn, payment) - - verify(exactly = 0) { metricsService.counter(PAYMENT_RECEIVED, any(), any()) } - verify(exactly = 1) { platformApiClient.notifyTransactionError(txn.id, "payment failed") } - - JSONAssert.assertEquals( - custodyTransactionDbSep31ReceivePaymentError, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_handleEvent_onSent_payment() { - val txn = - gson.fromJson(custodyTransactionInputSep31ReceivePayment, JdbcCustodyTransaction::class.java) - val payment = gson.fromJson(custodyPaymentWithId, CustodyPayment::class.java) - - sep31CustodyPaymentHandler.onSent(txn, payment) - - verify(exactly = 0) { metricsService.counter(PAYMENT_SENT, any(), any()) } - verify(exactly = 0) { custodyTransactionRepo.save(any()) } - } - - private val custodyPaymentWithId = - """ -{ - "id": "12345", - "externalTxId": "testEventId", - "type": "payment", - "from": "testFrom", - "to": "testTo", - "amount": "1.0000000", - "assetType": "credit_alphanum4", - "assetName": "testAmountInAsset", - "updatedAt": "2023-05-10T10:18:25.778Z", - "status": "SUCCESS", - "transactionHash": "testTxHash", - "transactionMemoType": "none", - "transactionEnvelope": "testEnvelopeXdr" -} -""" - - private val custodyPaymentWithIdError = - """ - { - "id": "12345", - "externalTxId": "testEventId", - "type": "payment", - "from": "testFrom", - "to": "testTo", - "amount": "1.0000000", - "assetType": "credit_alphanum4", - "assetName": "testAmountInAsset", - "updatedAt": "2023-05-10T10:18:25.778Z", - "status": "ERROR", - "transactionHash": "testTxHash", - "transactionMemoType": "none", - "transactionEnvelope": "testEnvelopeXdr" -} -""" - - private val custodyTransactionDbSep31ReceivePayment = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "completed", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "31", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "receive", - "reconciliation_attempt_count": 0, - "type": "payment" -} -""" - - private val custodyTransactionDbSep31ReceivePaymentError = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "failed", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "31", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "receive", - "reconciliation_attempt_count": 0, - "type": "payment" -} -""" - - private val custodyTransactionDbSep31ReceiveRefund = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "completed", - "amount": "1", - "amount_fee": "0.1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "31", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "receive", - "reconciliation_attempt_count": 0, - "type": "refund" -} -""" - - private val custodyTransactionInputSep31ReceivePayment = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "status": "submitted", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "31", - "from_account": "testFromAccount1", - "to_account": "testToAccount", - "kind": "receive", - "type": "payment" -} -""" - - private val custodyTransactionInputSep31ReceiveRefund = - """ -{ - "id": "testId", - "sep_tx_id": "testId", - "status": "submitted", - "amount": "1", - "amount_fee": "0.1", - "asset": "stellar:testAmountInAsset", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "31", - "from_account": "testFromAccount1", - "to_account": "testToAccount", - "kind": "receive", - "type": "refund" -} -""" -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/Sep6CustodyPaymentHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/custody/Sep6CustodyPaymentHandlerTest.kt deleted file mode 100644 index df5cc6f0ec..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/Sep6CustodyPaymentHandlerTest.kt +++ /dev/null @@ -1,393 +0,0 @@ -package org.stellar.anchor.platform.custody - -import io.micrometer.core.instrument.Counter -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.slot -import io.mockk.verify -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.stellar.anchor.apiclient.PlatformApiClient -import org.stellar.anchor.metrics.MetricsService -import org.stellar.anchor.platform.config.RpcConfig -import org.stellar.anchor.platform.data.JdbcCustodyTransaction -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo -import org.stellar.anchor.platform.service.AnchorMetrics -import org.stellar.anchor.util.GsonUtils - -class Sep6CustodyPaymentHandlerTest { - - @MockK(relaxed = true) private lateinit var custodyTransactionRepo: JdbcCustodyTransactionRepo - - @MockK(relaxed = true) private lateinit var platformApiClient: PlatformApiClient - - @MockK(relaxed = true) private lateinit var paymentReceivedCounter: Counter - - @MockK(relaxed = true) private lateinit var paymentSentCounter: Counter - - @MockK(relaxed = true) private lateinit var rpcConfig: RpcConfig - - @MockK(relaxed = true) private lateinit var metricsService: MetricsService - - private lateinit var sep6CustodyPaymentHandler: Sep6CustodyPaymentHandler - - private val gson = GsonUtils.getInstance() - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - sep6CustodyPaymentHandler = - Sep6CustodyPaymentHandler( - custodyTransactionRepo, - platformApiClient, - rpcConfig, - metricsService - ) - } - - @Test - fun test_handleEvent_onReceived_payment_success() { - val txn = - gson.fromJson( - custodyTransactionInputSep6WithdrawalPayment, - JdbcCustodyTransaction::class.java - ) - val payment = gson.fromJson(custodyPaymentWithId, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.incomingPaymentReceived } returns "payment received" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - every { - metricsService.counter(AnchorMetrics.PAYMENT_RECEIVED, "asset", "testAmountInAsset") - } returns paymentReceivedCounter - - sep6CustodyPaymentHandler.onReceived(txn, payment) - - verify(exactly = 1) { paymentReceivedCounter.increment(1.0000000) } - verify(exactly = 1) { - platformApiClient.notifyOnchainFundsReceived( - txn.id, - payment.transactionHash, - payment.amount, - "payment received" - ) - } - - JSONAssert.assertEquals( - custodyTransactionDbSep6WithdrawalPayment, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_handleEvent_onReceived_refund_success() { - val txn = - gson.fromJson(custodyTransactionInputSep6WithdrawalRefund, JdbcCustodyTransaction::class.java) - val payment = gson.fromJson(custodyPaymentWithId, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.incomingPaymentReceived } returns "payment received" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - - sep6CustodyPaymentHandler.onReceived(txn, payment) - - verify(exactly = 0) { metricsService.counter(AnchorMetrics.PAYMENT_RECEIVED, any(), any()) } - verify(exactly = 1) { - platformApiClient.notifyRefundSent( - txn.id, - payment.transactionHash, - payment.amount, - txn.amountFee, - txn.asset - ) - } - - JSONAssert.assertEquals( - custodyTransactionDbSep6WithdrawalRefund, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_handleEvent_onReceived_payment_error() { - val txn = - gson.fromJson( - custodyTransactionInputSep6WithdrawalPayment, - JdbcCustodyTransaction::class.java - ) - val payment = gson.fromJson(custodyPaymentWithIdError, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.custodyTransactionFailed } returns "payment failed" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - - sep6CustodyPaymentHandler.onReceived(txn, payment) - - verify(exactly = 0) { metricsService.counter(AnchorMetrics.PAYMENT_SENT, any(), any()) } - verify(exactly = 1) { platformApiClient.notifyTransactionError(txn.id, "payment failed") } - - JSONAssert.assertEquals( - custodyTransactionDbSep6WithdrawalPaymentError, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_handleEvent_onSent_success() { - val txn = - gson.fromJson(custodyTransactionInputSep6DepositPayment, JdbcCustodyTransaction::class.java) - val payment = gson.fromJson(custodyPaymentWithId, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.outgoingPaymentSent } returns "payment sent" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - every { - metricsService.counter(AnchorMetrics.PAYMENT_SENT, "asset", "testAmountInAsset") - } returns paymentSentCounter - - sep6CustodyPaymentHandler.onSent(txn, payment) - - verify(exactly = 1) { paymentSentCounter.increment(1.0000000) } - verify(exactly = 1) { - platformApiClient.notifyOnchainFundsSent(txn.id, payment.transactionHash, "payment sent") - } - - JSONAssert.assertEquals( - custodyTransactionDbSep6DepositPayment, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_handleEvent_onSent_payment_error() { - val txn = - gson.fromJson(custodyTransactionInputSep6DepositPayment, JdbcCustodyTransaction::class.java) - val payment = gson.fromJson(custodyPaymentWithIdError, CustodyPayment::class.java) - - val custodyTxCapture = slot() - - every { rpcConfig.customMessages.custodyTransactionFailed } returns "payment failed" - every { custodyTransactionRepo.save(capture(custodyTxCapture)) } returns txn - - sep6CustodyPaymentHandler.onSent(txn, payment) - - verify(exactly = 0) { metricsService.counter(AnchorMetrics.PAYMENT_SENT, any(), any()) } - verify(exactly = 1) { platformApiClient.notifyTransactionError(txn.id, "payment failed") } - - JSONAssert.assertEquals( - custodyTransactionDbSep6DepositPaymentError, - gson.toJson(custodyTxCapture.captured), - JSONCompareMode.STRICT - ) - } - - private val custodyPaymentWithId = - """ - { - "id": "12345", - "externalTxId": "testEventId", - "type": "payment", - "from": "testFrom", - "to": "testTo", - "amount": "1.0000000", - "assetType": "credit_alphanum4", - "assetName": "testAmountInAsset", - "updatedAt": "2023-05-10T10:18:25.778Z", - "status": "SUCCESS", - "transactionHash": "testTxHash", - "transactionMemoType": "none", - "transactionEnvelope": "testEnvelopeXdr" - } - """ - - private val custodyPaymentWithIdError = - """ - { - "id": "12345", - "externalTxId": "testEventId", - "type": "payment", - "from": "testFrom", - "to": "testTo", - "amount": "1.0000000", - "assetType": "credit_alphanum4", - "assetName": "testAmountInAsset", - "updatedAt": "2023-05-10T10:18:25.778Z", - "status": "ERROR", - "transactionHash": "testTxHash", - "transactionMemoType": "none", - "transactionEnvelope": "testEnvelopeXdr" - } - """ - - private val custodyTransactionDbSep6DepositPayment = - """ - { - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "completed", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "6", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "deposit", - "reconciliation_attempt_count": 0, - "type": "payment" - } - """ - - private val custodyTransactionDbSep6DepositPaymentError = - """ - { - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "failed", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "6", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "deposit", - "reconciliation_attempt_count": 0, - "type": "payment" - } - """ - - private val custodyTransactionDbSep6WithdrawalPayment = - """ - { - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "completed", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "6", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "withdrawal", - "reconciliation_attempt_count": 0, - "type": "payment" - } - """ - - private val custodyTransactionDbSep6WithdrawalPaymentError = - """ - { - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "failed", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "6", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "withdrawal", - "reconciliation_attempt_count": 0, - "type": "payment" - } - """ - - private val custodyTransactionDbSep6WithdrawalRefund = - """ - { - "id": "testId", - "sep_tx_id": "testId", - "external_tx_id": "testEventId", - "status": "completed", - "amount": "1", - "amount_fee": "0.1", - "asset": "stellar:testAmountInAsset", - "updated_at": "2023-05-10T10:18:25.778Z", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "6", - "from_account": "testFrom", - "to_account": "testToAccount", - "kind": "withdrawal", - "reconciliation_attempt_count": 0, - "type": "refund" - } - """ - - private val custodyTransactionInputSep6DepositPayment = - """ - { - "id": "testId", - "sep_tx_id": "testId", - "status": "submitted", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "6", - "from_account": "testFromAccount1", - "to_account": "testToAccount", - "kind": "deposit", - "type": "payment" - } - """ - - private val custodyTransactionInputSep6WithdrawalPayment = - """ - { - "id": "testId", - "sep_tx_id": "testId", - "status": "submitted", - "amount": "1", - "asset": "stellar:testAmountInAsset", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "6", - "from_account": "testFromAccount1", - "to_account": "testToAccount", - "kind": "withdrawal", - "type": "payment" - } - """ - - private val custodyTransactionInputSep6WithdrawalRefund = - """ - { - "id": "testId", - "sep_tx_id": "testId", - "status": "submitted", - "amount": "1", - "amount_fee": "0.1", - "asset": "stellar:testAmountInAsset", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "6", - "from_account": "testFromAccount1", - "to_account": "testToAccount", - "kind": "withdrawal", - "type": "refund" - } - """ -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/fireblocks/FireblocksApiClientTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/custody/fireblocks/FireblocksApiClientTest.kt deleted file mode 100644 index 8bf91046f9..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/fireblocks/FireblocksApiClientTest.kt +++ /dev/null @@ -1,294 +0,0 @@ -package org.stellar.anchor.platform.custody.fireblocks - -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.mockk -import io.mockk.slot -import java.io.IOException -import java.nio.charset.Charset -import java.security.KeyFactory -import java.security.PublicKey -import java.security.spec.X509EncodedKeySpec -import java.time.Instant -import java.util.* -import okhttp3.* -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.ResponseBody.Companion.toResponseBody -import okio.Buffer -import org.apache.commons.lang3.StringUtils -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.stellar.anchor.api.exception.FireblocksException -import org.stellar.anchor.api.exception.InvalidConfigException -import org.stellar.anchor.config.CustodySecretConfig -import org.stellar.anchor.platform.config.FireblocksConfig -import org.stellar.anchor.util.FileUtil.getResourceFileAsString -import org.stellar.anchor.util.JwtUtil.jwtsParser - -class FireblocksApiClientTest { - - companion object { - private const val API_KEY = "testApiKey" - private const val BASE_URL = "https://testBaseUrl.com" - } - - @MockK(relaxed = true) private lateinit var client: OkHttpClient - - @MockK(relaxed = true) private lateinit var secretConfig: CustodySecretConfig - - private lateinit var fireblocksApiClient: FireblocksApiClient - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - - every { secretConfig.fireblocksApiKey } returns API_KEY - every { secretConfig.fireblocksSecretKey } returns - getResourceFileAsString("custody/fireblocks/client/secret_key.txt") - - val fireblocksConfig = FireblocksConfig(secretConfig) - fireblocksConfig.baseUrl = BASE_URL - - fireblocksApiClient = FireblocksApiClient(client, fireblocksConfig) - } - - @Test - fun test_init_invalidSecretKey() { - every { secretConfig.fireblocksSecretKey } returns "dGVzdA==" - val fireblocksConfig = FireblocksConfig(secretConfig) - - val exception = - assertThrows { FireblocksApiClient(client, fireblocksConfig) } - - Assertions.assertEquals("Failed to generate Fireblocks private key", exception.message) - } - - @Test - fun test_getRequest_success() { - val response = getSuccessMockResponse() - val requestCapture = slot() - val call = mockk() - - every { client.newCall(capture(requestCapture)) } returns call - every { call.execute() } returns response - - val result = fireblocksApiClient.get("/getPath") - - Assertions.assertEquals( - "https://testbaseurl.com/getPath", - requestCapture.captured.url.toString() - ) - Assertions.assertEquals("testApiKey", requestCapture.captured.header("X-API-Key")) - Assertions.assertTrue(requestCapture.captured.header("Authorization")!!.startsWith("Bearer ")) - JSONAssert.assertEquals(successResponseBody, result, JSONCompareMode.STRICT) - - validateToken( - requestCapture.captured.header("Authorization")!!, - "/getPath", - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - ) - } - - @Test - fun test_getRequest_fail_IOException() { - val call = mockk() - - every { client.newCall(any()) } returns call - every { call.execute() } throws IOException("Fireblocks IO exception") - - val exception = assertThrows { fireblocksApiClient.get("/getPath") } - - Assertions.assertEquals( - "Exception occurred during request to Fireblocks API", - exception.message - ) - } - - @Test - fun test_getRequest_fail_errorStatusCode() { - val response = getErrorMockResponse() - val requestCapture = slot() - val call = mockk() - - every { client.newCall(capture(requestCapture)) } returns call - every { call.execute() } returns response - - val exception = assertThrows { fireblocksApiClient.get("/getPath") } - - Assertions.assertEquals( - """ - Fireblocks API returned an error. HTTP status[400], response[{ - "error_code": "12345", - "message": "Fireblocks error" - }] - """ - .trimIndent(), - exception.message?.trimIndent() - ) - } - - @Test - fun test_postRequest_success() { - val response = getSuccessMockResponse() - val requestCapture = slot() - val call = mockk() - - every { client.newCall(capture(requestCapture)) } returns call - every { call.execute() } returns response - - val result = fireblocksApiClient.post("/postPath", requestBody.trimIndent()) - - Assertions.assertEquals( - "https://testbaseurl.com/postPath", - requestCapture.captured.url.toString() - ) - Assertions.assertEquals("testApiKey", requestCapture.captured.header("X-API-Key")) - Assertions.assertTrue(requestCapture.captured.header("Authorization")!!.startsWith("Bearer ")) - Assertions.assertEquals( - "application/json; charset=utf-8", - requestCapture.captured.body!!.contentType().toString() - ) - JSONAssert.assertEquals(requestBody, requestBodyToString(requestCapture.captured.body), true) - JSONAssert.assertEquals(successResponseBody, result, JSONCompareMode.STRICT) - - validateToken( - requestCapture.captured.header("Authorization")!!, - "/postPath", - "479309383f8f54af2e09fac9ec40e1b736dd46b6e5a33d49211faaecddabdea4" - ) - } - - @Test - fun `test getRequest with empty query parameters`() { - val response = getSuccessMockResponse() - val requestCapture = slot() - val call = mockk() - - every { client.newCall(capture(requestCapture)) } returns call - every { call.execute() } returns response - - fireblocksApiClient.get("/getPath", mapOf()) - - Assertions.assertEquals( - "https://testbaseurl.com/getPath", - requestCapture.captured.url.toString() - ) - } - - @Test - fun `test getRequest with query parameters`() { - val response = getSuccessMockResponse() - val requestCapture = slot() - val call = mockk() - - every { client.newCall(capture(requestCapture)) } returns call - every { call.execute() } returns response - - fireblocksApiClient.get( - "/getPath", - mapOf("testKey1" to "testValue1", "testKey2" to "testValue2") - ) - - Assertions.assertEquals( - "https://testbaseurl.com/getPath?testKey1=testValue1&testKey2=testValue2", - requestCapture.captured.url.toString() - ) - } - - private fun requestBodyToString(requestBody: RequestBody?): String { - val buffer = Buffer() - requestBody?.writeTo(buffer) - return buffer.readString(Charset.forName("UTF-8")) - } - - private fun getSuccessMockResponse(): Response { - return Response.Builder() - .request(Request.Builder().url("https://test.com").build()) - .protocol(Protocol.HTTP_1_1) - .code(200) - .message("OK") - .body(successResponseBody.toResponseBody("application/json".toMediaTypeOrNull())) - .build() - } - - private fun getErrorMockResponse(): Response { - return Response.Builder() - .request(Request.Builder().url("https://test.com").build()) - .protocol(Protocol.HTTP_1_1) - .code(400) - .message("ERROR") - .body(errorResponseBody.toResponseBody("application/json".toMediaTypeOrNull())) - .build() - } - - private fun validateToken(token: String, path: String, bodyHash: String) { - Assertions.assertNotNull(token) - Assertions.assertTrue(token.startsWith("Bearer ")) - - val claims = - jwtsParser() - .verifyWith(getPublicKey()) - .build() - .parseSignedClaims(StringUtils.substringAfter(token, "Bearer ")) - .body - - Assertions.assertEquals(API_KEY, claims.subject) - Assertions.assertTrue(claims.issuedAt.toInstant().isBefore(Instant.now())) - Assertions.assertTrue(claims.expiration.toInstant().isAfter(Instant.now())) - Assertions.assertTrue(isUuid(claims["nonce"].toString())) - Assertions.assertEquals(path, claims["uri"]) - Assertions.assertEquals(bodyHash, claims["bodyHash"]) - } - - private fun getPublicKey(): PublicKey? { - try { - val publicKeyBytes = - Base64.getDecoder() - .decode( - getResourceFileAsString("custody/fireblocks/client/public_key.txt") - .replace("-----BEGIN PUBLIC KEY-----", StringUtils.EMPTY) - .replace("-----END PUBLIC KEY-----", StringUtils.EMPTY) - .replace(StringUtils.LF, StringUtils.EMPTY) - .replace(StringUtils.CR, StringUtils.EMPTY) - ) - val publicKeySpec = X509EncodedKeySpec(publicKeyBytes) - val keyFactory = KeyFactory.getInstance("RSA") - return keyFactory.generatePublic(publicKeySpec) - } catch (e: Exception) { - e.printStackTrace() - } - return null - } - - private fun isUuid(text: String): Boolean { - return try { - UUID.fromString(text) - true - } catch (e: IllegalArgumentException) { - e.printStackTrace() - false - } - } - - private val errorResponseBody = """{ - "error_code": "12345", - "message": "Fireblocks error" -}""" - - private val requestBody = """{ - "key3": "value3", - "key4": "value4" -}""" - - private val successResponseBody = """ -{ - "key3": "value3", - "key4": "value4" -} -""" -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/fireblocks/FireblocksEventServiceTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/custody/fireblocks/FireblocksEventServiceTest.kt deleted file mode 100644 index 778e1de76c..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/fireblocks/FireblocksEventServiceTest.kt +++ /dev/null @@ -1,757 +0,0 @@ -package org.stellar.anchor.platform.custody.fireblocks - -import io.mockk.* -import java.math.BigInteger -import java.security.Signature -import java.util.* -import kotlin.test.assertEquals -import org.apache.commons.lang3.StringUtils -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.EnumSource -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.stellar.anchor.api.custody.fireblocks.FireblocksEventObject -import org.stellar.anchor.api.custody.fireblocks.TransactionDetails -import org.stellar.anchor.api.custody.fireblocks.TransactionStatus -import org.stellar.anchor.api.exception.BadRequestException -import org.stellar.anchor.api.exception.InvalidConfigException -import org.stellar.anchor.ledger.LedgerClient -import org.stellar.anchor.ledger.LedgerTransaction -import org.stellar.anchor.ledger.LedgerTransaction.* -import org.stellar.anchor.platform.config.FireblocksConfig -import org.stellar.anchor.platform.config.PropertyCustodySecretConfig -import org.stellar.anchor.platform.custody.CustodyPayment -import org.stellar.anchor.platform.custody.Sep24CustodyPaymentHandler -import org.stellar.anchor.platform.custody.Sep31CustodyPaymentHandler -import org.stellar.anchor.platform.custody.Sep6CustodyPaymentHandler -import org.stellar.anchor.platform.custody.fireblocks.FireblocksEventService.FIREBLOCKS_SIGNATURE_HEADER -import org.stellar.anchor.platform.data.JdbcCustodyTransaction -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo -import org.stellar.anchor.util.FileUtil.getResourceFileAsString -import org.stellar.anchor.util.GsonUtils -import org.stellar.anchor.util.RSAUtil -import org.stellar.anchor.util.RSAUtil.RSA_ALGORITHM -import org.stellar.anchor.util.RSAUtil.SHA512_WITH_RSA_ALGORITHM -import org.stellar.sdk.Server -import org.stellar.sdk.requests.PaymentsRequestBuilder -import org.stellar.sdk.responses.Page -import org.stellar.sdk.responses.operations.OperationResponse -import org.stellar.sdk.xdr.* -import org.stellar.sdk.xdr.MemoType.MEMO_ID - -class FireblocksEventServiceTest { - - private val gson = GsonUtils.getInstance() - - private lateinit var secretConfig: PropertyCustodySecretConfig - private lateinit var custodyTransactionRepo: JdbcCustodyTransactionRepo - private lateinit var sep6CustodyPaymentHandler: Sep6CustodyPaymentHandler - private lateinit var sep24CustodyPaymentHandler: Sep24CustodyPaymentHandler - private lateinit var sep31CustodyPaymentHandler: Sep31CustodyPaymentHandler - private lateinit var ledgerClient: LedgerClient - private lateinit var server: Server - private lateinit var paymentsRequestBuilder: PaymentsRequestBuilder - private lateinit var page: Page - - @BeforeEach - fun setUp() { - secretConfig = mockk() - custodyTransactionRepo = mockk() - sep6CustodyPaymentHandler = mockk() - sep24CustodyPaymentHandler = mockk() - sep31CustodyPaymentHandler = mockk() - ledgerClient = mockk() - server = mockk() - paymentsRequestBuilder = mockk() - page = mockk() - } - - @Test - fun `test handleFireblocksEvent() for valid SUBMITTED event object and signature test`() { - val config = - getFireblocksConfig(getResourceFileAsString("custody/fireblocks/webhook/public_key.txt")) - val eventsService = - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - - val signature: String = - getResourceFileAsString("custody/fireblocks/webhook/submitted_event_valid_signature.txt") - val httpHeaders: Map = mutableMapOf(FIREBLOCKS_SIGNATURE_HEADER to signature) - val eventObject: String = - getResourceFileAsString("custody/fireblocks/webhook/submitted_event_request.json") - - eventsService.handleEvent(eventObject, httpHeaders) - } - - @Test - fun `test handleFireblocksEvent() for valid CONFIRMING event object with no Stellar transaction test`() { - val config = - getFireblocksConfig(getResourceFileAsString("custody/fireblocks/webhook/public_key.txt")) - val eventsService = - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - val custodyTxn = gson.fromJson(custodyTransaction, JdbcCustodyTransaction::class.java) - - val eventObject: String = confirmingEventRequest.trimIndent() - val signature: String = generateSignature(eventObject) - val httpHeaders: Map = mutableMapOf(FIREBLOCKS_SIGNATURE_HEADER to signature) - - val paymentCapture = slot() - - every { custodyTransactionRepo.findByExternalTxId(any()) } returns custodyTxn - every { sep24CustodyPaymentHandler.onSent(eq(custodyTxn), capture(paymentCapture)) } just runs - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - eventsService.handleEvent(eventObject, httpHeaders) - - JSONAssert.assertEquals( - completedEventNotStellarTxnPayment, - gson.toJson(paymentCapture.captured), - JSONCompareMode.STRICT, - ) - } - - @Test - fun `test handleFireblocksEvent() for valid FAILED event object and signature with no Stellar transaction test`() { - val config = - getFireblocksConfig(getResourceFileAsString("custody/fireblocks/webhook/public_key.txt")) - val eventsService = - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - val custodyTxn = gson.fromJson(custodyTransaction, JdbcCustodyTransaction::class.java) - - val httpHeaders: Map = - mutableMapOf(FIREBLOCKS_SIGNATURE_HEADER to failedEventSignature) - val eventObject: String = failedEventRequest.trimIndent() - - val paymentCapture = slot() - - every { custodyTransactionRepo.findByExternalTxId(any()) } returns custodyTxn - every { sep24CustodyPaymentHandler.onSent(eq(custodyTxn), capture(paymentCapture)) } just runs - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - eventsService.handleEvent(eventObject, httpHeaders) - - JSONAssert.assertEquals( - failedEventNoStellarTxnPayment, - gson.toJson(paymentCapture.captured), - JSONCompareMode.STRICT, - ) - } - - @Test - fun `test handleFireblocksEvent() for valid COMPLETED event object and signature test with Stellar transaction unknown payment test`() { - val config = - getFireblocksConfig(getResourceFileAsString("custody/fireblocks/webhook/public_key.txt")) - val eventsService = - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - val custodyTxn = gson.fromJson(custodyTransaction, JdbcCustodyTransaction::class.java) - - val eventObject: String = confirmingEventRequest.trimIndent() - val signature: String = generateSignature(eventObject) - val httpHeaders: Map = mutableMapOf(FIREBLOCKS_SIGNATURE_HEADER to signature) - - val paymentCapture = slot() - - every { ledgerClient.getTransaction("testTxHash") } returns - getMockLedgerTransaction(::getTrustlineOperations) - - every { custodyTransactionRepo.findByExternalTxId(any()) } returns custodyTxn - every { sep24CustodyPaymentHandler.onSent(eq(custodyTxn), capture(paymentCapture)) } just runs - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - eventsService.handleEvent(eventObject, httpHeaders) - - verify(exactly = 0) { custodyTransactionRepo.findByExternalTxId(any()) } - } - - @Test - fun `test handleFireblocksEvent() for valid CONFIRMING event object and signature with Stellar transaction payment test`() { - val config = - getFireblocksConfig(getResourceFileAsString("custody/fireblocks/webhook/public_key.txt")) - val eventsService = - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - val custodyTxn = gson.fromJson(custodyTransaction, JdbcCustodyTransaction::class.java) - val eventObject: String = confirmingEventRequest.trimIndent() - val signature: String = generateSignature(eventObject) - val httpHeaders: Map = mutableMapOf(FIREBLOCKS_SIGNATURE_HEADER to signature) - - val paymentCapture = slot() - - every { ledgerClient.getTransaction("testTxHash") } returns - getMockLedgerTransaction(::getPaymentOperations) - - every { custodyTransactionRepo.findByExternalTxId(any()) } returns custodyTxn - every { sep24CustodyPaymentHandler.onSent(eq(custodyTxn), capture(paymentCapture)) } just runs - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - eventsService.handleEvent(eventObject, httpHeaders) - JSONAssert.assertEquals( - stellarTxnPayment, - gson.toJson(paymentCapture.captured), - JSONCompareMode.STRICT, - ) - } - - @Test - fun `test handleFireblocksEvent() for valid CONFIRMING event object and signature with Stellar transaction path payment test`() { - val config = - getFireblocksConfig(getResourceFileAsString("custody/fireblocks/webhook/public_key.txt")) - val eventsService = - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - val custodyTxn = gson.fromJson(custodyTransaction, JdbcCustodyTransaction::class.java) - val eventObject: String = confirmingEventRequest.trimIndent() - val signature: String = generateSignature(eventObject) - val httpHeaders: Map = mutableMapOf(FIREBLOCKS_SIGNATURE_HEADER to signature) - - val paymentCapture = slot() - - every { ledgerClient.getTransaction("testTxHash") } returns - getMockLedgerTransaction(::getPathPaymentOperations) - - every { custodyTransactionRepo.findByExternalTxId(any()) } returns custodyTxn - every { sep24CustodyPaymentHandler.onSent(eq(custodyTxn), capture(paymentCapture)) } just runs - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - eventsService.handleEvent(eventObject, httpHeaders) - - JSONAssert.assertEquals( - stellarTxnPathPayment, - gson.toJson(paymentCapture.captured), - JSONCompareMode.STRICT, - ) - } - - @ParameterizedTest - @EnumSource( - value = TransactionStatus::class, - mode = EnumSource.Mode.INCLUDE, - names = - [ - "QUEUED", - "PENDING_AUTHORIZATION", - "PENDING_SIGNATURE", - "BROADCASTING", - "PENDING_3RD_PARTY_MANUAL_APPROVAL", - "PENDING_3RD_PARTY", - "COMPLETED", - "PARTIALLY_COMPLETED", - "PENDING_AML_SCREENING", - "REJECTED", - ], - ) - fun `test handleEvent() for ignored event object statuses`(status: TransactionStatus) { - val config = - getFireblocksConfig(getResourceFileAsString("custody/fireblocks/webhook/public_key.txt")) - val eventsService = - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - - val eventObject = FireblocksEventObject() - eventObject.data = TransactionDetails.builder().status(status).build() - - val eventObjectTxt = gson.toJson(eventObject, FireblocksEventObject::class.java) - - val signature: String = generateSignature(eventObjectTxt) - val httpHeaders: Map = mutableMapOf(FIREBLOCKS_SIGNATURE_HEADER to signature) - - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns null - - eventsService.handleEvent(eventObjectTxt, httpHeaders) - - verify(exactly = 0) { custodyTransactionRepo.findByExternalTxId(any()) } - } - - @Test - fun `test handleFireblocksEvent() for invalid public key`() { - val config = getFireblocksConfig(StringUtils.EMPTY) - val ex = - assertThrows { - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - } - assertEquals("Failed to generate Fireblocks public key", ex.message) - } - - @Test - fun `test handleFireblocksEvent() for missed fireblocks-signature header`() { - val config = - getFireblocksConfig(getResourceFileAsString("custody/fireblocks/webhook/public_key.txt")) - val eventsService = - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - - val eventObject = StringUtils.EMPTY - val emptyHeaders: Map = mapOf() - - val ex = - assertThrows { eventsService.handleEvent(eventObject, emptyHeaders) } - assertEquals("'fireblocks-signature' header missed", ex.message) - } - - @Test - fun `test handleFireblocksEvent() for empty signature`() { - val config = - getFireblocksConfig(getResourceFileAsString("custody/fireblocks/webhook/public_key.txt")) - val eventsService = - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - - val eventObject = StringUtils.EMPTY - val httpHeaders = mutableMapOf(FIREBLOCKS_SIGNATURE_HEADER to StringUtils.EMPTY) - - val ex = - assertThrows { eventsService.handleEvent(eventObject, httpHeaders) } - assertEquals("'fireblocks-signature' is empty", ex.message) - } - - @Test - fun `test handleFireblocksEvent() for invalid signature`() { - val config = - getFireblocksConfig(getResourceFileAsString("custody/fireblocks/webhook/public_key.txt")) - val eventsService = - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - - val invalidSignature: String = - getResourceFileAsString("custody/fireblocks/webhook/submitted_event_invalid_signature.txt") - val eventObject: String = - getResourceFileAsString("custody/fireblocks/webhook/submitted_event_request.json") - val httpHeaders = mutableMapOf(FIREBLOCKS_SIGNATURE_HEADER to invalidSignature) - - eventsService.handleEvent(eventObject, httpHeaders) - } - - @Test - fun `test handleFireblocksEvent() for invalid signature encoding`() { - val config = - getFireblocksConfig(getResourceFileAsString("custody/fireblocks/webhook/public_key.txt")) - val eventsService = - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - - val invalidSignature = "test" - val eventObject: String = - getResourceFileAsString("custody/fireblocks/webhook/submitted_event_request.json") - val httpHeaders = mutableMapOf(FIREBLOCKS_SIGNATURE_HEADER to invalidSignature) - - eventsService.handleEvent(eventObject, httpHeaders) - } - - @Test - fun `test handleFireblocksEvent() set missing external transaction id`() { - val config = - getFireblocksConfig(getResourceFileAsString("custody/fireblocks/webhook/public_key.txt")) - val eventsService = - FireblocksEventService( - custodyTransactionRepo, - sep6CustodyPaymentHandler, - sep24CustodyPaymentHandler, - sep31CustodyPaymentHandler, - ledgerClient, - config, - ) - val custodyTxn = gson.fromJson(custodyTransaction, JdbcCustodyTransaction::class.java) - - val eventObject: String = confirmingEventRequest.trimIndent() - val signature: String = generateSignature(eventObject) - val httpHeaders: Map = mutableMapOf(FIREBLOCKS_SIGNATURE_HEADER to signature) - - val transactionToUpdate = slot() - val externalTransactionId = "testEventId" - - every { custodyTransactionRepo.findByExternalTxId(any()) } returns custodyTxn - every { sep24CustodyPaymentHandler.onSent(eq(custodyTxn), any()) } just runs - every { - custodyTransactionRepo.findFirstByToAccountAndMemoOrderByCreatedAtDesc(any(), any()) - } returns custodyTxn - every { custodyTransactionRepo.save(capture(transactionToUpdate)) } returns custodyTxn - - eventsService.handleEvent(eventObject, httpHeaders) - assertEquals(externalTransactionId, transactionToUpdate.captured.externalTxId) - } - - private fun getTrustlineOperations(): List { - return listOf(LedgerOperation.builder().type(OperationType.SET_TRUST_LINE_FLAGS).build()) - } - - private fun getPaymentOperations(): List { - return listOf( - LedgerOperation.builder() - .type(OperationType.PAYMENT) - .paymentOperation( - LedgerPaymentOperation.builder() - .id("12345") - .from("testFrom") - .to("testTo") - .amount(BigInteger.valueOf(150000000)) - .asset(Asset.builder().discriminant(AssetType.ASSET_TYPE_NATIVE).build()) - .sourceAccount("testSourceAccount") - .build() - ) - .build() - ) - } - - private fun getPathPaymentOperations(): List { - return listOf( - LedgerOperation.builder() - .type(OperationType.PATH_PAYMENT_STRICT_RECEIVE) - .pathPaymentOperation( - LedgerPathPaymentOperation.builder() - .id("12345") - .from("testFrom") - .to("testTo") - .amount(BigInteger.valueOf(150000000)) - .asset(Asset.builder().discriminant(AssetType.ASSET_TYPE_NATIVE).build()) - .sourceAccount("testSourceAccount") - .build() - ) - .build() - ) - } - - private fun getMockLedgerTransaction( - getOperations: () -> List - ): LedgerTransaction { - val memoId = Uint64() - memoId.uint64 = XdrUnsignedHyperInteger(12345L) - - return builder() - .hash("testTxHash") - .sourceAccount("testSourceAccount") - .memo(Memo.builder().discriminant(MEMO_ID).id(memoId).build()) - .envelopeXdr("testEnvelopeXdr") - .operations(getOperations()) - .build() - } - - private fun getFireblocksConfig(publicKey: String): FireblocksConfig { - val config = FireblocksConfig(secretConfig) - config.publicKey = publicKey - return config - } - - private fun generateSignature(requestBody: String): String { - val privateKey = - RSAUtil.generatePrivateKey( - getResourceFileAsString("custody/fireblocks/webhook/private_key.txt"), - RSA_ALGORITHM, - ) - - val data = requestBody.toByteArray() - - val sig: Signature = Signature.getInstance(SHA512_WITH_RSA_ALGORITHM) - sig.initSign(privateKey) - sig.update(data) - val signatureBytes: ByteArray = sig.sign() - - return String(Base64.getEncoder().encode(signatureBytes)) - } - - private val completedEventNotStellarTxnPayment = - """ -{ - "externalTxId": "testEventId", - "type": "payment", - "updatedAt": "2023-05-10T10:18:26.130Z", - "status": "SUCCESS", - "transactionHash": "testTxHash" -} -""" - - private val confirmingEventRequest = - """ -{ - "type": "TRANSACTION_STATUS_UPDATED", - "tenantId": "testTenantId", - "timestamp": 1683713916523, - "data": { - "id": "testEventId", - "createdAt": 1683713905778, - "lastUpdated": 1683713906130, - "assetId": "XLM_TEST", - "source": { - "id": "", - "type": "UNKNOWN", - "name": "External", - "subType": "" - }, - "destination": { - "id": "1", - "type": "VAULT_ACCOUNT", - "name": "TestAnchor", - "subType": "" - }, - "amount": 15, - "networkFee": 0.00001, - "netAmount": 15, - "sourceAddress": "testSourceAddress", - "destinationAddress": "testDestinationAddress", - "destinationAddressDescription": "", - "destinationTag": "", - "status": "CONFIRMING", - "txHash": "testTxHash", - "subStatus": "CONFIRMED", - "signedBy": [], - "createdBy": "", - "rejectedBy": "", - "amountUSD": 1.33, - "addressType": "", - "note": "", - "exchangeTxId": "", - "requestedAmount": 15, - "feeCurrency": "XLM_TEST", - "operation": "TRANSFER", - "customerRefId": null, - "numOfConfirmations": 1, - "amountInfo": { - "amount": "15", - "requestedAmount": "15", - "netAmount": "15", - "amountUSD": "1.33" - }, - "feeInfo": { - "networkFee": "0.00001" - }, - "destinations": [], - "externalTxId": null, - "blockInfo": { - "blockHeight": "921353", - "blockHash": "testBlockHash" - }, - "signedMessages": [], - "index": 0, - "assetType": "BASE_ASSET" - } -} -""" - - private val custodyTransaction = - """ -{ - "id": "testId", - "status": "created", - "amount_": "testAmount", - "amount_asset": "testAmountAsset", - "memo": "testMemo", - "memo_type": "testMemoType", - "protocol": "24", - "from_account": "testFromAccount", - "to_account": "testToAccount", - "kind": "deposit" -} -""" - - private val failedEventNoStellarTxnPayment = - """ -{ - "externalTxId": "testEventId", - "type": "payment", - "updatedAt": "2023-05-10T10:18:26.130Z", - "status": "ERROR", - "transactionHash": "testTxHash", - "message": "THIRD_PARTY_FAILED" -} -""" - - private val failedEventRequest = - """{ - "type": "TRANSACTION_STATUS_UPDATED", - "tenantId": "testTenantId", - "timestamp": 1683713916523, - "data": { - "id": "testEventId", - "createdAt": 1683713905778, - "lastUpdated": 1683713906130, - "assetId": "XLM_TEST", - "source": { - "id": "", - "type": "UNKNOWN", - "name": "External", - "subType": "" - }, - "destination": { - "id": "1", - "type": "VAULT_ACCOUNT", - "name": "TestAnchor", - "subType": "" - }, - "amount": 15, - "networkFee": 0.00001, - "netAmount": 15, - "sourceAddress": "testSourceAddress", - "destinationAddress": "testDestinationAddress", - "destinationAddressDescription": "", - "destinationTag": "", - "status": "FAILED", - "txHash": "testTxHash", - "subStatus": "3RD_PARTY_FAILED", - "signedBy": [], - "createdBy": "", - "rejectedBy": "", - "amountUSD": 1.33, - "addressType": "", - "note": "", - "exchangeTxId": "", - "requestedAmount": 15, - "feeCurrency": "XLM_TEST", - "operation": "TRANSFER", - "customerRefId": null, - "numOfConfirmations": 1, - "amountInfo": { - "amount": "15", - "requestedAmount": "15", - "netAmount": "15", - "amountUSD": "1.33" - }, - "feeInfo": { - "networkFee": "0.00001" - }, - "destinations": [], - "externalTxId": null, - "blockInfo": { - "blockHeight": "921353", - "blockHash": "testBlockHash" - }, - "signedMessages": [], - "index": 0, - "assetType": "BASE_ASSET" - } -}""" - - private val failedEventSignature = - "WgGX+S1rfljuehHaFmHMhkCs/OxURLSOwDvrCl3IhpqilJclx/hLwDxu7fB49WD+5Reh8DSk+DREbCgjJE4OyPQWyLeiqGfk1W1PuKmn23ZnUq98CYhPn3rlZoggC9op4JR5F5dC8xVf2QrP7lRS5V32pKaoFGQPAqY/mQxRFbA=" - - private val stellarTxnPathPayment = - """ -{ - "id": "12345", - "externalTxId": "testEventId", - "type": "path_payment", - "from": "testFrom", - "to": "testTo", - "amount": "15.0000000", - "assetType": "native", - "assetCode": "native", - "assetName": "native", - "updatedAt": "2023-05-10T10:18:26.130Z", - "status": "SUCCESS", - "transactionHash": "testTxHash", - "transactionMemo": "12345", - "transactionMemoType": "id", - "transactionEnvelope": "testEnvelopeXdr" -} -""" - - private val stellarTxnPayment = - """ -{ - "id": "12345", - "externalTxId": "testEventId", - "type": "payment", - "from": "testFrom", - "to": "testTo", - "amount": "15.0000000", - "assetType": "native", - "assetCode": "native", - "assetName": "native", - "updatedAt": "2023-05-10T10:18:26.130Z", - "status": "SUCCESS", - "transactionHash": "testTxHash", - "transactionMemo": "12345", - "transactionMemoType": "id", - "transactionEnvelope": "testEnvelopeXdr" -} -""" -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/fireblocks/FireblocksPaymentServiceTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/custody/fireblocks/FireblocksPaymentServiceTest.kt deleted file mode 100644 index f8c1231dc7..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/custody/fireblocks/FireblocksPaymentServiceTest.kt +++ /dev/null @@ -1,553 +0,0 @@ -package org.stellar.anchor.platform.custody.fireblocks - -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.slot -import java.time.Instant -import kotlin.test.assertEquals -import org.apache.commons.lang3.StringUtils -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.hasEntry -import org.junit.jupiter.api.* -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.stellar.anchor.api.exception.FireblocksException -import org.stellar.anchor.platform.config.FireblocksConfig -import org.stellar.anchor.platform.data.JdbcCustodyTransaction -import org.stellar.anchor.util.GsonUtils - -class FireblocksPaymentServiceTest { - - companion object { - private const val VAULT_ACCOUNT_ID = "testVaultAccountId" - private const val ASSET_ID = "TEST_ASSET_ID" - private const val TO_ACCOUNT_ID = "toVaultAccountId" - private const val AMOUNT = "10" - private const val EXTERNAL_TXN_ID = "TRANSACTION_ID" - } - - private val gson = GsonUtils.getInstance() - - @MockK(relaxed = true) private lateinit var fireblocksClient: FireblocksApiClient - @MockK(relaxed = true) private lateinit var fireblocksConfig: FireblocksConfig - - private lateinit var fireblocksPaymentService: FireblocksPaymentService - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - - every { fireblocksConfig.vaultAccountId } returns VAULT_ACCOUNT_ID - - fireblocksPaymentService = FireblocksPaymentService(fireblocksClient, fireblocksConfig) - } - - @Test - fun test_generateDepositAddress_success() { - val requestCapture = slot() - val urlCapture = slot() - - every { fireblocksClient.post(capture(urlCapture), capture(requestCapture)) } returns - createNewDepositAddressResponse - every { fireblocksConfig.getFireblocksAssetCode(ASSET_ID) } returns ASSET_ID - - val response = fireblocksPaymentService.generateDepositAddress(ASSET_ID) - - Assertions.assertEquals( - "/v1/vault/accounts/testVaultAccountId/TEST_ASSET_ID/addresses", - urlCapture.captured - ) - JSONAssert.assertEquals( - createNewDepositAddressRequest, - requestCapture.captured, - JSONCompareMode.STRICT - ) - JSONAssert.assertEquals( - generatedDepositAddressResponse, - gson.toJson(response), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_createTransactionPayment_success() { - val expectedResponse = "{\"id\":\"1\"}" - val requestCapture = slot() - val urlCapture = slot() - - every { fireblocksClient.post(capture(urlCapture), capture(requestCapture)) } returns - createNewTransactionPaymentResponse - every { fireblocksConfig.getFireblocksAssetCode(ASSET_ID) } returns ASSET_ID - - val transaction = - JdbcCustodyTransaction.builder() - .fromAccount(VAULT_ACCOUNT_ID) - .toAccount(TO_ACCOUNT_ID) - .asset(ASSET_ID) - .amount(AMOUNT) - .build() - val response = fireblocksPaymentService.createTransactionPayment(transaction, StringUtils.EMPTY) - - Assertions.assertEquals("/v1/transactions", urlCapture.captured) - - JSONAssert.assertEquals( - createNewTransactionPaymentRequest, - requestCapture.captured, - JSONCompareMode.STRICT - ) - JSONAssert.assertEquals(expectedResponse, gson.toJson(response), JSONCompareMode.STRICT) - } - - @Test - fun test_createTransactionPayment_failure() { - val expectedResponse = "{\"id\":\"\"}" - - every { fireblocksClient.post(any(), any()) } returns errorResponse - - val transaction = JdbcCustodyTransaction() - val response = fireblocksPaymentService.createTransactionPayment(transaction, StringUtils.EMPTY) - - JSONAssert.assertEquals(expectedResponse, gson.toJson(response), JSONCompareMode.STRICT) - } - - @Test - fun test_createTransactionPayment_IOException() { - val expectedMessage = - "Fireblocks API returned an error. HTTP status[503], response[Fireblocks service is unavailable]" - - every { fireblocksClient.post(any(), any()) } throws - FireblocksException("Fireblocks service is unavailable", 503) - - val transaction = JdbcCustodyTransaction() - val exception = - assertThrows { - fireblocksPaymentService.createTransactionPayment(transaction, StringUtils.EMPTY) - } - - assertEquals(expectedMessage, exception.message) - } - - @Test - fun test_getTransactionById_success() { - val urlCapture = slot() - - every { fireblocksClient.get(capture(urlCapture)) } returns getTransactionResponse - - val response = fireblocksPaymentService.getTransactionById(EXTERNAL_TXN_ID) - - Assertions.assertEquals( - String.format("/v1/transactions/%s", EXTERNAL_TXN_ID), - urlCapture.captured - ) - - JSONAssert.assertEquals(getTransactionResponse, gson.toJson(response), JSONCompareMode.STRICT) - } - - @Test - fun test_getTransactionsByTimeRange_success() { - val urlCapture = slot() - val queryParamsCapture = slot>() - - every { fireblocksClient.get(capture(urlCapture), capture(queryParamsCapture)) } returns - twoTransactionsResponse - - val endTime = Instant.now() - val startTime = endTime.minusSeconds(5) - val response = fireblocksPaymentService.getTransactionsByTimeRange(startTime, endTime) - - Assertions.assertEquals("/v1/transactions", urlCapture.captured) - Assertions.assertEquals(5, queryParamsCapture.captured.size) - - assertThat(queryParamsCapture.captured, hasEntry("after", startTime.toEpochMilli().toString())) - assertThat(queryParamsCapture.captured, hasEntry("before", endTime.toEpochMilli().toString())) - assertThat(queryParamsCapture.captured, hasEntry("limit", "500")) - assertThat(queryParamsCapture.captured, hasEntry("orderBy", "createdAt")) - assertThat(queryParamsCapture.captured, hasEntry("sort", "ASC")) - - JSONAssert.assertEquals(twoTransactionsResponse, gson.toJson(response), JSONCompareMode.STRICT) - } - - @Test - fun `getTransactionsByTimeRange select number of items equal to limit`() { - fireblocksPaymentService = FireblocksPaymentService(fireblocksClient, fireblocksConfig, 2) - val startTime = Instant.now().minusSeconds(5) - val endTime = Instant.now() - - val queryParams = - mapOf( - "after" to startTime.toEpochMilli().toString(), - "before" to endTime.toEpochMilli().toString(), - "limit" to fireblocksPaymentService.transactionLimit.toString(), - "orderBy" to "createdAt", - "sort" to "ASC" - ) - every { fireblocksClient.get(any(), queryParams) } returns twoTransactionsResponse - - val maxCreatedTime = 1684856015569L - val secondQueryParams = - mapOf( - "after" to maxCreatedTime.toString(), - "before" to endTime.toEpochMilli().toString(), - "limit" to fireblocksPaymentService.transactionLimit.toString(), - "orderBy" to "createdAt", - "sort" to "ASC" - ) - every { fireblocksClient.get(any(), secondQueryParams) } returns StringUtils.EMPTY - - val response = fireblocksPaymentService.getTransactionsByTimeRange(startTime, endTime) - - JSONAssert.assertEquals(twoTransactionsResponse, gson.toJson(response), JSONCompareMode.STRICT) - } - - @Test - fun `getTransactionsByTimeRange select more than limit`() { - fireblocksPaymentService = FireblocksPaymentService(fireblocksClient, fireblocksConfig, 2) - val startTime = Instant.now().minusSeconds(5) - val endTime = Instant.now() - - val queryParams = - mapOf( - "after" to startTime.toEpochMilli().toString(), - "before" to endTime.toEpochMilli().toString(), - "limit" to fireblocksPaymentService.transactionLimit.toString(), - "orderBy" to "createdAt", - "sort" to "ASC" - ) - every { fireblocksClient.get(any(), queryParams) } returns twoTransactionsResponse - - val maxCreatedTime = 1684856015569L - val secondQueryParams = - mapOf( - "after" to maxCreatedTime.toString(), - "before" to endTime.toEpochMilli().toString(), - "limit" to fireblocksPaymentService.transactionLimit.toString(), - "orderBy" to "createdAt", - "sort" to "ASC" - ) - every { fireblocksClient.get(any(), secondQueryParams) } returns oneTransactionResponse - - val response = fireblocksPaymentService.getTransactionsByTimeRange(startTime, endTime) - assertEquals(3, response.size) - } - - @Test - fun `getTransactionsByTimeRange time range validation`() { - val startTime = Instant.now().minusSeconds(5) - val endTime = Instant.now() - - val ex = - assertThrows { - fireblocksPaymentService.getTransactionsByTimeRange(endTime, startTime) - } - assertEquals("End time can't be before start time", ex.message) - } - - private val createNewDepositAddressRequest = """ -{ -} -""" - - private val createNewDepositAddressResponse = - """ -{ - "address": "testAddress", - "legacyAddress": "testLegacyAddress", - "enterpriseAddress": "testEnterpriseAddress", - "tag": "testTag", - "bip44AddressIndex": "12345" -} -""" - - private val generatedDepositAddressResponse = - """ -{ - "address": "testAddress", - "memo": "testTag", - "memoType": "id" -} -""" - - private val createNewTransactionPaymentRequest = - """ -{ - "assetId": "TEST_ASSET_ID", - "source": { - "type": "VAULT_ACCOUNT", - "id": "testVaultAccountId" - }, - "destination": { - "type": "ONE_TIME_ADDRESS", - "oneTimeAddress": { - "address": "toVaultAccountId" - } - }, - "amount": "10" -} -""" - - private val createNewTransactionPaymentResponse = """ -{ - "id": "1", - "status": "SUBMITTED" -} -""" - - private val errorResponse = - """ -{ - "id": "", - "status": "FAILED", - "systemMessages": [ - { - "type": "WARN", - "message": "message1" - }, - { - "type": "BLOCK", - "message": "message2" - } - ] -} -""" - - private val getTransactionResponse = - """ -{ - "id": "2db60d66-163f-4caf-9cf4-538ce895f32f", - "createdAt": 1683906114688, - "lastUpdated": 1683906114958, - "assetId": "XLM_USDC_T_CEKS", - "source": { - "id": "", - "type": "UNKNOWN", - "name": "External", - "subType": "" - }, - "destination": { - "id": "1", - "type": "VAULT_ACCOUNT", - "name": "TestAnchor", - "subType": "" - }, - "amount": 0.2, - "fee": 0.00001, - "networkFee": 0.00001, - "netAmount": 0.2, - "sourceAddress": "GD3ZMOHJXZCFGZZSTFD7ZXFVSYEJUBFV4RQXJOQSLNSSQ46WNSESU2PS", - "destinationAddress": "GB4LDT5G6GC26QUDS3YQ3XCIRSIYTIIWA3PLKFP4B7PRZC5DRZKSZHSK", - "destinationAddressDescription": "", - "destinationTag": "1222977634", - "status": "COMPLETED", - "txHash": "fc0023db2ddcedf1d9ee0c7b8da68eef043b8e6928d1ec74347c05038a2e584c", - "subStatus": "CONFIRMED", - "signedBy": [], - "createdBy": "", - "rejectedBy": "", - "amountUSD": 0.2, - "addressType": "", - "note": "", - "exchangeTxId": "", - "requestedAmount": 0.2, - "feeCurrency": "XLM_TEST", - "operation": "TRANSFER", - "numOfConfirmations": 1, - "amountInfo": { - "amount": "0.2", - "requestedAmount": "0.2", - "netAmount": "0.2", - "amountUSD": "0.20" - }, - "feeInfo": { - "networkFee": "0.00001" - }, - "destinations": [], - "blockInfo": { - "blockHeight": "957916", - "blockHash": "09337b0c320b753bab7e884e660892990f73d5eed3e4ec07dbf85880d34eccb7" - }, - "signedMessages": [], - "index": 0 -} -""" - - private val oneTransactionResponse = - """ -[ - { - "id": "c12775b1-ed62-4998-9061-cfa7c6e38bbc", - "createdAt": 1684856015023, - "lastUpdated": 1684856424529, - "assetId": "XLM_USDC_T_CEKS", - "source": { - "id": "1", - "type": "VAULT_ACCOUNT", - "name": "TestAnchor", - "subType": "" - }, - "destination": { - "type": "ONE_TIME_ADDRESS", - "name": "N/A", - "subType": "" - }, - "amount": 0.28, - "fee": 0.00001, - "networkFee": 0.00001, - "netAmount": 0.28, - "sourceAddress": "GB4LDT5G6GC26QUDS3YQ3XCIRSIYTIIWA3PLKFP4B7PRZC5DRZKSZHSK", - "destinationAddress": "GBX3C62RHF6C7EVG4QXJ4PORIRSQLPBBOIDSKYRLK4H2JBTPTJZM4V6E", - "destinationAddressDescription": "", - "destinationTag": "", - "status": "COMPLETED", - "txHash": "916a7f9dd6e09b86256771b5df7be5f8f4ac09e1ddc6eb4423f0b500151e9367", - "subStatus": "CONFIRMED", - "signedBy": [ - "1444ed36-5bc0-4e3b-9b17-5df29fc0590f" - ], - "createdBy": "1444ed36-5bc0-4e3b-9b17-5df29fc0590f", - "rejectedBy": "", - "amountUSD": 0.28112, - "addressType": "", - "note": "", - "exchangeTxId": "", - "requestedAmount": 0.28, - "feeCurrency": "XLM_TEST", - "operation": "TRANSFER", - "numOfConfirmations": 1, - "amountInfo": { - "amount": "0.28", - "requestedAmount": "0.28", - "netAmount": "0.28", - "amountUSD": "0.28112" - }, - "feeInfo": { - "networkFee": "0.00001" - }, - "destinations": [], - "blockInfo": { - "blockHeight": "1138780", - "blockHash": "5252168d088561d5b1ac6dedfa4d078384f2aff4d123add1cc58225770c2815d" - }, - "signedMessages": [] - } -] -""" - - private val twoTransactionsResponse = - """ -[ - { - "id": "83f2f9f1-e30f-4bd5-8db1-b51f0323870e", - "createdAt": 1684856015569, - "lastUpdated": 1684856489313, - "assetId": "XLM_USDC_T_CEKS", - "source": { - "id": "1", - "type": "VAULT_ACCOUNT", - "name": "TestAnchor", - "subType": "" - }, - "destination": { - "type": "ONE_TIME_ADDRESS", - "name": "N/A", - "subType": "" - }, - "amount": 0.6, - "fee": 0.0005, - "networkFee": 0.0005, - "netAmount": 0.6, - "sourceAddress": "GB4LDT5G6GC26QUDS3YQ3XCIRSIYTIIWA3PLKFP4B7PRZC5DRZKSZHSK", - "destinationAddress": "GBX3C62RHF6C7EVG4QXJ4PORIRSQLPBBOIDSKYRLK4H2JBTPTJZM4V6E", - "destinationAddressDescription": "", - "destinationTag": "", - "status": "COMPLETED", - "txHash": "d0bae55aba646131ce7fa4991a1a5fb467e8afc9f84836fa7e0994ae864f2cea", - "subStatus": "CONFIRMED", - "signedBy": [ - "1444ed36-5bc0-4e3b-9b17-5df29fc0590f" - ], - "createdBy": "1444ed36-5bc0-4e3b-9b17-5df29fc0590f", - "rejectedBy": "", - "amountUSD": 0.6024, - "addressType": "", - "note": "", - "exchangeTxId": "", - "requestedAmount": 0.6, - "feeCurrency": "XLM_TEST", - "operation": "TRANSFER", - "numOfConfirmations": 1, - "amountInfo": { - "amount": "0.6", - "requestedAmount": "0.6", - "netAmount": "0.6", - "amountUSD": "0.6024" - }, - "feeInfo": { - "networkFee": "0.0005" - }, - "destinations": [], - "blockInfo": { - "blockHeight": "1138792", - "blockHash": "d73713270ad506554f1aadfd29d054a2d35ef16399a99230c5d1d4201c3169bd" - }, - "signedMessages": [] - }, - { - "id": "c12775b1-ed62-4998-9061-cfa7c6e38bbc", - "createdAt": 1684856015023, - "lastUpdated": 1684856424529, - "assetId": "XLM_USDC_T_CEKS", - "source": { - "id": "1", - "type": "VAULT_ACCOUNT", - "name": "TestAnchor", - "subType": "" - }, - "destination": { - "type": "ONE_TIME_ADDRESS", - "name": "N/A", - "subType": "" - }, - "amount": 0.28, - "fee": 0.00001, - "networkFee": 0.00001, - "netAmount": 0.28, - "sourceAddress": "GB4LDT5G6GC26QUDS3YQ3XCIRSIYTIIWA3PLKFP4B7PRZC5DRZKSZHSK", - "destinationAddress": "GBX3C62RHF6C7EVG4QXJ4PORIRSQLPBBOIDSKYRLK4H2JBTPTJZM4V6E", - "destinationAddressDescription": "", - "destinationTag": "", - "status": "COMPLETED", - "txHash": "916a7f9dd6e09b86256771b5df7be5f8f4ac09e1ddc6eb4423f0b500151e9367", - "subStatus": "CONFIRMED", - "signedBy": [ - "1444ed36-5bc0-4e3b-9b17-5df29fc0590f" - ], - "createdBy": "1444ed36-5bc0-4e3b-9b17-5df29fc0590f", - "rejectedBy": "", - "amountUSD": 0.28112, - "addressType": "", - "note": "", - "exchangeTxId": "", - "requestedAmount": 0.28, - "feeCurrency": "XLM_TEST", - "operation": "TRANSFER", - "numOfConfirmations": 1, - "amountInfo": { - "amount": "0.28", - "requestedAmount": "0.28", - "netAmount": "0.28", - "amountUSD": "0.28112" - }, - "feeInfo": { - "networkFee": "0.00001" - }, - "destinations": [], - "blockInfo": { - "blockHeight": "1138780", - "blockHash": "5252168d088561d5b1ac6dedfa4d078384f2aff4d123add1cc58225770c2815d" - }, - "signedMessages": [] - } -] -""" -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/event/KafkaSessionTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/event/KafkaSessionTest.kt index 28cc1f7e11..8d176db76f 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/event/KafkaSessionTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/event/KafkaSessionTest.kt @@ -25,7 +25,6 @@ import org.stellar.anchor.platform.config.PropertySecretConfig.SECRET_EVENTS_QUE import org.stellar.anchor.platform.config.PropertySecretConfig.SECRET_EVENTS_QUEUE_KAFKA_USERNAME import org.stellar.anchor.platform.configurator.SecretManager import org.stellar.anchor.platform.utils.ResourceHelper -import org.stellar.anchor.platform.utils.TrustAllSslEngineFactory @ExtendWith(LockAndMockTest::class) class KafkaSessionTest { @@ -102,7 +101,7 @@ class KafkaSessionTest { assertEquals("truststore.jks", properties.getProperty(SSL_TRUSTSTORE_LOCATION_CONFIG)) } else { assertEquals("", properties.getProperty(SSL_ENDPOINT_IDENTIFICATION_ALGORITHM_CONFIG)) - val cls = properties.get(SSL_ENGINE_FACTORY_CLASS_CONFIG) as Class + val cls = properties[SSL_ENGINE_FACTORY_CLASS_CONFIG] as Class<*> assertEquals("org.stellar.anchor.platform.utils.TrustAllSslEngineFactory", cls.name) // make sure we don't look for keystore and truststore diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/job/FireblocksTransactionsReconciliationJobTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/job/FireblocksTransactionsReconciliationJobTest.kt deleted file mode 100644 index 36c48fe0da..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/job/FireblocksTransactionsReconciliationJobTest.kt +++ /dev/null @@ -1,409 +0,0 @@ -package org.stellar.anchor.platform.job - -import io.mockk.MockKAnnotations -import io.mockk.Runs -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.just -import io.mockk.slot -import io.mockk.verify -import java.time.Instant -import java.util.* -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertDoesNotThrow -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.EnumSource -import org.stellar.anchor.api.custody.fireblocks.TransactionDetails -import org.stellar.anchor.api.custody.fireblocks.TransactionStatus -import org.stellar.anchor.api.exception.FireblocksException -import org.stellar.anchor.api.platform.PlatformTransactionData -import org.stellar.anchor.platform.config.FireblocksConfig -import org.stellar.anchor.platform.custody.CustodyPayment -import org.stellar.anchor.platform.custody.CustodyPaymentService -import org.stellar.anchor.platform.custody.CustodyTransactionService -import org.stellar.anchor.platform.custody.fireblocks.FireblocksEventService -import org.stellar.anchor.platform.data.CustodyTransactionStatus -import org.stellar.anchor.platform.data.JdbcCustodyTransaction -import org.stellar.anchor.platform.data.JdbcCustodyTransactionRepo -import org.stellar.anchor.platform.fireblocks.job.FireblocksTransactionsReconciliationJob - -class FireblocksTransactionsReconciliationJobTest { - - companion object { - private const val EXTERNAL_TXN_ID = "TRANSACTION_ID" - private const val MEMO = "1234567890" - private const val DESTINATION_ADDRESS = - "GBX3C62RHF6C7EVG4QXJ4PORIRSQLPBBOIDSKYRLK4H2JBTPTJZM4V6E" - private val START_TIME = Instant.now().minusSeconds(5) - } - - @MockK(relaxed = true) private lateinit var fireblocksConfig: FireblocksConfig - @MockK(relaxed = true) - private lateinit var custodyPaymentService: CustodyPaymentService - @MockK(relaxed = true) private lateinit var fireblocksEventService: FireblocksEventService - @MockK(relaxed = true) private lateinit var custodyTransactionRepo: JdbcCustodyTransactionRepo - private lateinit var custodyTransactionService: CustodyTransactionService - - private lateinit var reconciliationJob: FireblocksTransactionsReconciliationJob - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - custodyTransactionService = - CustodyTransactionService(custodyTransactionRepo, custodyPaymentService) - - reconciliationJob = - FireblocksTransactionsReconciliationJob( - fireblocksConfig, - custodyPaymentService, - fireblocksEventService, - custodyTransactionService - ) - } - - @ParameterizedTest - @EnumSource( - value = TransactionStatus::class, - mode = EnumSource.Mode.INCLUDE, - names = ["CONFIRMING", "COMPLETED", "CANCELLED", "BLOCKED", "FAILED"] - ) - fun `reconcile outbound transactions - successful reconciliation`(status: TransactionStatus) { - val custodyTxn = - JdbcCustodyTransaction.builder() - .externalTxId(EXTERNAL_TXN_ID) - .status(CustodyTransactionStatus.SUBMITTED.toString()) - .build() - val fireblocksTxn = TransactionDetails.builder().status(status).build() - val custodyPayment = CustodyPayment.builder().build() - - every { - custodyTransactionRepo.findAllByStatusAndExternalTxIdNotNull( - CustodyTransactionStatus.SUBMITTED.toString() - ) - } returns listOf(custodyTxn) - every { custodyPaymentService.getTransactionById(EXTERNAL_TXN_ID) } returns fireblocksTxn - every { fireblocksEventService.convert(fireblocksTxn) } returns Optional.of(custodyPayment) - - reconciliationJob.reconcileTransactions() - - verify(exactly = 1) { custodyPaymentService.getTransactionById(EXTERNAL_TXN_ID) } - verify(exactly = 1) { fireblocksEventService.handlePayment(custodyPayment) } - } - - @Test - fun `reconcile outbound transactions - no transactions to reconcile`() { - every { - custodyTransactionRepo.findAllByStatusAndExternalTxIdNotNull( - CustodyTransactionStatus.SUBMITTED.toString() - ) - } returns emptyList() - - reconciliationJob.reconcileTransactions() - - verify(exactly = 0) { custodyPaymentService.getTransactionById(any()) } - } - - @ParameterizedTest - @EnumSource( - value = TransactionStatus::class, - mode = EnumSource.Mode.INCLUDE, - names = - [ - "QUEUED", - "PENDING_AUTHORIZATION", - "PENDING_SIGNATURE", - "BROADCASTING", - "PENDING_3RD_PARTY_MANUAL_APPROVAL", - "PENDING_3RD_PARTY", - "PARTIALLY_COMPLETED", - "PENDING_AML_SCREENING", - "REJECTED" - ] - ) - fun `reconcile outbound transactions - attempt increase`(status: TransactionStatus) { - val attemptCount = 0 - val custodyTxn = - JdbcCustodyTransaction.builder() - .externalTxId(EXTERNAL_TXN_ID) - .status(CustodyTransactionStatus.SUBMITTED.toString()) - .reconciliationAttemptCount(attemptCount) - .build() - val fireblocksTxn = TransactionDetails.builder().status(status).build() - - val requestCapture = slot() - every { custodyTransactionService.updateCustodyTransaction(capture(requestCapture)) } just Runs - every { custodyTransactionService.outboundTransactionsEligibleForReconciliation } returns - listOf(custodyTxn) - every { custodyPaymentService.getTransactionById(EXTERNAL_TXN_ID) } returns fireblocksTxn - every { custodyTransactionRepo.save(any()) } returns null - every { fireblocksConfig.reconciliation.maxAttempts } returns 10 - - reconciliationJob.reconcileTransactions() - - verify(exactly = 1) { custodyPaymentService.getTransactionById(EXTERNAL_TXN_ID) } - verify(exactly = 1) { custodyTransactionService.updateCustodyTransaction(any()) } - - assertEquals(CustodyTransactionStatus.SUBMITTED.toString(), custodyTxn.status) - assertEquals(1, custodyTxn.reconciliationAttemptCount) - } - - @Test - fun `reconcile outbound transactions - change_status_to_failed`() { - val attemptCount = 9 - val custodyTxn = - JdbcCustodyTransaction.builder() - .externalTxId(EXTERNAL_TXN_ID) - .status(CustodyTransactionStatus.SUBMITTED.toString()) - .reconciliationAttemptCount(attemptCount) - .build() - val fireblocksTxn = TransactionDetails.builder().status(TransactionStatus.BROADCASTING).build() - - val requestCapture = slot() - every { custodyTransactionService.updateCustodyTransaction(capture(requestCapture)) } just Runs - every { custodyTransactionService.outboundTransactionsEligibleForReconciliation } returns - listOf(custodyTxn) - every { custodyPaymentService.getTransactionById(EXTERNAL_TXN_ID) } returns fireblocksTxn - every { fireblocksConfig.reconciliation.maxAttempts } returns 10 - every { custodyTransactionRepo.save(any()) } returns null - - reconciliationJob.reconcileTransactions() - - verify(exactly = 1) { custodyPaymentService.getTransactionById(EXTERNAL_TXN_ID) } - verify(exactly = 1) { custodyTransactionService.updateCustodyTransaction(any()) } - - assertEquals(CustodyTransactionStatus.FAILED.toString(), custodyTxn.status) - assertEquals(10, custodyTxn.reconciliationAttemptCount) - } - - @Test - fun `reconcile outbound transactions - handle exception`() { - val attemptCount = 9 - val custodyTxn = - JdbcCustodyTransaction.builder() - .externalTxId(EXTERNAL_TXN_ID) - .status(CustodyTransactionStatus.SUBMITTED.toString()) - .reconciliationAttemptCount(attemptCount) - .build() - - every { custodyTransactionService.outboundTransactionsEligibleForReconciliation } returns - listOf(custodyTxn) - every { custodyPaymentService.getTransactionById(EXTERNAL_TXN_ID) } throws - FireblocksException("Too many requests", 429) - - assertDoesNotThrow { reconciliationJob.reconcileTransactions() } - } - - @ParameterizedTest - @EnumSource( - value = TransactionStatus::class, - mode = EnumSource.Mode.INCLUDE, - names = ["CONFIRMING", "COMPLETED", "CANCELLED", "BLOCKED", "FAILED"] - ) - fun `reconcile inbound transactions - successful reconciliation`(status: TransactionStatus) { - val custodyTxn = - JdbcCustodyTransaction.builder() - .status(CustodyTransactionStatus.CREATED.toString()) - .memo(MEMO) - .toAccount(DESTINATION_ADDRESS) - .createdAt(START_TIME) - .build() - val fireblocksTxn1 = - TransactionDetails.builder() - .id("1") - .status(status) - .destinationAddress(DESTINATION_ADDRESS) - .destinationTag(MEMO) - .build() - val fireblocksTxn2 = - TransactionDetails.builder() - .id("2") - .status(status) - .destinationAddress(DESTINATION_ADDRESS) - .destinationTag(MEMO) - .build() - val custodyPayment = CustodyPayment.builder().build() - - every { - custodyTransactionRepo.findAllByStatusAndKindIn( - CustodyTransactionStatus.CREATED.toString(), - mutableSetOf( - PlatformTransactionData.Kind.RECEIVE.getKind(), - PlatformTransactionData.Kind.WITHDRAWAL.getKind() - ) - ) - } returns listOf(custodyTxn) - every { custodyPaymentService.getTransactionsByTimeRange(START_TIME, any()) } returns - listOf(fireblocksTxn1, fireblocksTxn2) - every { fireblocksEventService.convert(fireblocksTxn1) } returns Optional.of(custodyPayment) - - reconciliationJob.reconcileTransactions() - - verify(exactly = 1) { custodyPaymentService.getTransactionsByTimeRange(any(), any()) } - verify(exactly = 1) { fireblocksEventService.handlePayment(custodyPayment) } - } - - @Test - fun `reconcile inbound transactions - no transactions to reconcile`() { - every { - custodyTransactionRepo.findAllByStatusAndKindIn( - CustodyTransactionStatus.CREATED.toString(), - mutableSetOf( - PlatformTransactionData.Kind.RECEIVE.getKind(), - PlatformTransactionData.Kind.WITHDRAWAL.getKind() - ) - ) - } returns emptyList() - - reconciliationJob.reconcileTransactions() - - verify(exactly = 0) { custodyPaymentService.getTransactionsByTimeRange(any(), any()) } - } - - @Test - fun `reconcile inbound transactions - no Fireblocks transactions within range`() { - val custodyTxn = - JdbcCustodyTransaction.builder() - .status(CustodyTransactionStatus.CREATED.toString()) - .memo(MEMO) - .toAccount(DESTINATION_ADDRESS) - .createdAt(START_TIME) - .build() - - every { - custodyTransactionRepo.findAllByStatusAndKindIn( - CustodyTransactionStatus.CREATED.toString(), - mutableSetOf( - PlatformTransactionData.Kind.RECEIVE.getKind(), - PlatformTransactionData.Kind.WITHDRAWAL.getKind() - ) - ) - } returns listOf(custodyTxn) - - every { custodyPaymentService.getTransactionsByTimeRange(any(), any()) } returns listOf() - - reconciliationJob.reconcileTransactions() - - verify(exactly = 0) { fireblocksEventService.handlePayment(any()) } - } - - @ParameterizedTest - @EnumSource( - value = TransactionStatus::class, - mode = EnumSource.Mode.INCLUDE, - names = - [ - "QUEUED", - "PENDING_AUTHORIZATION", - "PENDING_SIGNATURE", - "BROADCASTING", - "PENDING_3RD_PARTY_MANUAL_APPROVAL", - "PENDING_3RD_PARTY", - "PARTIALLY_COMPLETED", - "PENDING_AML_SCREENING", - "REJECTED" - ] - ) - fun `reconcile inbound transactions - attempt increase`(status: TransactionStatus) { - val attemptCount = 0 - val custodyTxn = - JdbcCustodyTransaction.builder() - .status(CustodyTransactionStatus.CREATED.toString()) - .memo(MEMO) - .toAccount(DESTINATION_ADDRESS) - .createdAt(START_TIME) - .reconciliationAttemptCount(attemptCount) - .build() - val fireblocksTxn = - TransactionDetails.builder() - .status(status) - .destinationAddress(DESTINATION_ADDRESS) - .destinationTag(MEMO) - .build() - val custodyPayment = CustodyPayment.builder().build() - - every { - custodyTransactionRepo.findAllByStatusAndKindIn( - CustodyTransactionStatus.CREATED.toString(), - mutableSetOf( - PlatformTransactionData.Kind.RECEIVE.getKind(), - PlatformTransactionData.Kind.WITHDRAWAL.getKind() - ) - ) - } returns listOf(custodyTxn) - every { custodyPaymentService.getTransactionsByTimeRange(START_TIME, any()) } returns - listOf(fireblocksTxn) - every { fireblocksEventService.convert(fireblocksTxn) } returns Optional.of(custodyPayment) - every { custodyTransactionRepo.save(any()) } returns null - every { fireblocksConfig.reconciliation.maxAttempts } returns 10 - - reconciliationJob.reconcileTransactions() - - verify(exactly = 1) { custodyPaymentService.getTransactionsByTimeRange(START_TIME, any()) } - verify(exactly = 1) { custodyTransactionService.updateCustodyTransaction(any()) } - - assertEquals(CustodyTransactionStatus.CREATED.toString(), custodyTxn.status) - assertEquals(1, custodyTxn.reconciliationAttemptCount) - } - - @Test - fun `reconcile inbound transactions - change_status_to_failed`() { - val attemptCount = 9 - val custodyTxn = - JdbcCustodyTransaction.builder() - .status(CustodyTransactionStatus.CREATED.toString()) - .memo(MEMO) - .toAccount(DESTINATION_ADDRESS) - .createdAt(START_TIME) - .reconciliationAttemptCount(attemptCount) - .build() - val fireblocksTxn = - TransactionDetails.builder() - .status(TransactionStatus.PENDING_SIGNATURE) - .destinationAddress(DESTINATION_ADDRESS) - .destinationTag(MEMO) - .build() - val custodyPayment = CustodyPayment.builder().build() - - every { - custodyTransactionRepo.findAllByStatusAndKindIn( - CustodyTransactionStatus.CREATED.toString(), - mutableSetOf( - PlatformTransactionData.Kind.RECEIVE.getKind(), - PlatformTransactionData.Kind.WITHDRAWAL.getKind() - ) - ) - } returns listOf(custodyTxn) - every { custodyPaymentService.getTransactionsByTimeRange(START_TIME, any()) } returns - listOf(fireblocksTxn) - every { fireblocksEventService.convert(fireblocksTxn) } returns Optional.of(custodyPayment) - every { fireblocksConfig.reconciliation.maxAttempts } returns 10 - every { custodyTransactionRepo.save(any()) } returns null - - reconciliationJob.reconcileTransactions() - - verify(exactly = 1) { custodyPaymentService.getTransactionsByTimeRange(START_TIME, any()) } - verify(exactly = 1) { custodyTransactionService.updateCustodyTransaction(any()) } - - assertEquals(CustodyTransactionStatus.FAILED.toString(), custodyTxn.status) - assertEquals(10, custodyTxn.reconciliationAttemptCount) - } - - @Test - fun `reconcile inbound transactions - handle exception`() { - val custodyTxn = - JdbcCustodyTransaction.builder() - .externalTxId(EXTERNAL_TXN_ID) - .status(CustodyTransactionStatus.SUBMITTED.toString()) - .createdAt(START_TIME) - .build() - - every { custodyTransactionService.inboundTransactionsEligibleForReconciliation } returns - listOf(custodyTxn) - every { custodyPaymentService.getTransactionsByTimeRange(START_TIME, any()) } throws - FireblocksException("Too many requests", 429) - - assertDoesNotThrow { reconciliationJob.reconcileTransactions() } - } -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/job/NonceCleanupJobTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/job/NonceCleanupJobTest.kt deleted file mode 100644 index 32eec328bf..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/job/NonceCleanupJobTest.kt +++ /dev/null @@ -1,26 +0,0 @@ -package org.stellar.anchor.platform.job - -import io.mockk.MockKAnnotations -import io.mockk.impl.annotations.MockK -import io.mockk.verify -import kotlin.test.Test -import org.junit.jupiter.api.BeforeEach -import org.stellar.anchor.auth.NonceStore - -class NonceCleanupJobTest { - - @MockK(relaxed = true) private lateinit var nonceStore: NonceStore - private lateinit var nonceCleanupJob: NonceCleanupJob - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - nonceCleanupJob = NonceCleanupJob(nonceStore) - } - - @Test - fun test_cleanup() { - nonceCleanupJob.cleanup() - verify(exactly = 1) { nonceStore.deleteExpiredNonces() } - } -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/job/TrustlineCheckJobTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/job/TrustlineCheckJobTest.kt deleted file mode 100644 index 09eeffaef3..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/job/TrustlineCheckJobTest.kt +++ /dev/null @@ -1,131 +0,0 @@ -package org.stellar.anchor.platform.job - -import io.mockk.* -import io.mockk.impl.annotations.MockK -import java.time.Instant -import java.time.temporal.ChronoUnit -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.stellar.anchor.api.rpc.method.NotifyTrustSetRequest -import org.stellar.anchor.ledger.Horizon -import org.stellar.anchor.platform.config.PropertyCustodyConfig -import org.stellar.anchor.platform.config.PropertyCustodyConfig.Trustline -import org.stellar.anchor.platform.data.JdbcTransactionPendingTrust -import org.stellar.anchor.platform.data.JdbcTransactionPendingTrustRepo -import org.stellar.anchor.platform.rpc.NotifyTrustSetHandler -import org.stellar.anchor.util.GsonUtils - -class TrustlineCheckJobTest { - - companion object { - private val gson = GsonUtils.getInstance() - private const val TX_ID = "testId" - private const val ACCOUNT = "testToAccount" - private const val ASSET = "testAmountOutAsset" - private const val TX_MESSAGE = "testMessage" - } - - @MockK(relaxed = true) private lateinit var horizon: Horizon - - @MockK(relaxed = true) - private lateinit var transactionPendingTrustRepo: JdbcTransactionPendingTrustRepo - @MockK(relaxed = true) private lateinit var custodyConfig: PropertyCustodyConfig - @MockK(relaxed = true) private lateinit var notifyTrustSetHandler: NotifyTrustSetHandler - - private lateinit var trustlineCheckJob: TrustlineCheckJob - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - trustlineCheckJob = - TrustlineCheckJob(horizon, transactionPendingTrustRepo, custodyConfig, notifyTrustSetHandler) - } - - @Test - fun test_checkNotTimedOut_trustNotConfigured() { - val createdAt = Instant.now() - val txnPendingTrust = JdbcTransactionPendingTrust() - txnPendingTrust.id = TX_ID - txnPendingTrust.createdAt = createdAt - txnPendingTrust.asset = ASSET - txnPendingTrust.account = ACCOUNT - val trustline = Trustline() - trustline.checkDuration = 10 - - every { transactionPendingTrustRepo.findAll() } returns listOf(txnPendingTrust) - every { custodyConfig.trustline } returns trustline - every { horizon.hasTrustline(ACCOUNT, ASSET) } returns false - - trustlineCheckJob.checkTrust() - - verify(exactly = 0) { notifyTrustSetHandler.handle(any()) } - verify(exactly = 0) { transactionPendingTrustRepo.delete(any()) } - } - - @Test - fun test_checkNotTimedOut_trustConfigured() { - val createdAt = Instant.now() - val txnPendingTrust = JdbcTransactionPendingTrust() - txnPendingTrust.id = TX_ID - txnPendingTrust.createdAt = createdAt - txnPendingTrust.asset = ASSET - txnPendingTrust.account = ACCOUNT - val trustline = Trustline() - trustline.checkDuration = 10 - val notifyTrustSetRequestCapture = slot() - - every { transactionPendingTrustRepo.findAll() } returns listOf(txnPendingTrust) - every { custodyConfig.trustline } returns trustline - every { horizon.hasTrustline(ACCOUNT, ASSET) } returns true - every { notifyTrustSetHandler.handle(capture(notifyTrustSetRequestCapture)) } returns null - - trustlineCheckJob.checkTrust() - - verify(exactly = 1) { transactionPendingTrustRepo.delete(txnPendingTrust) } - - val expectedNotifyTrustSetRequest = NotifyTrustSetRequest() - expectedNotifyTrustSetRequest.transactionId = TX_ID - expectedNotifyTrustSetRequest.isSuccess = true - - JSONAssert.assertEquals( - gson.toJson(expectedNotifyTrustSetRequest), - gson.toJson(notifyTrustSetRequestCapture.captured), - JSONCompareMode.STRICT - ) - } - - @Test - fun test_checkTimedOut() { - val createdAt = Instant.now().minus(11, ChronoUnit.HOURS) - val txnPendingTrust = JdbcTransactionPendingTrust() - txnPendingTrust.id = TX_ID - txnPendingTrust.createdAt = createdAt - txnPendingTrust.asset = ASSET - txnPendingTrust.account = ACCOUNT - val trustline = Trustline() - trustline.checkDuration = 10 - trustline.timeoutMessage = TX_MESSAGE - val notifyTrustSetRequestCapture = slot() - - every { transactionPendingTrustRepo.findAll() } returns listOf(txnPendingTrust) - every { custodyConfig.trustline } returns trustline - every { notifyTrustSetHandler.handle(capture(notifyTrustSetRequestCapture)) } returns null - - trustlineCheckJob.checkTrust() - - verify(exactly = 1) { transactionPendingTrustRepo.delete(txnPendingTrust) } - - val expectedNotifyTrustSetRequest = NotifyTrustSetRequest() - expectedNotifyTrustSetRequest.transactionId = TX_ID - expectedNotifyTrustSetRequest.message = TX_MESSAGE - expectedNotifyTrustSetRequest.isSuccess = false - - JSONAssert.assertEquals( - gson.toJson(expectedNotifyTrustSetRequest), - gson.toJson(notifyTrustSetRequestCapture.captured), - JSONCompareMode.STRICT - ) - } -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/DoStellarPaymentHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/DoStellarPaymentHandlerTest.kt deleted file mode 100644 index 6f23066fa8..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/DoStellarPaymentHandlerTest.kt +++ /dev/null @@ -1,674 +0,0 @@ -package org.stellar.anchor.platform.rpc - -import io.micrometer.core.instrument.Counter -import io.mockk.* -import io.mockk.impl.annotations.MockK -import java.time.Instant -import kotlin.test.assertEquals -import kotlin.test.assertTrue -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.CsvSource -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.stellar.anchor.api.event.AnchorEvent -import org.stellar.anchor.api.event.AnchorEvent.Type.TRANSACTION_STATUS_CHANGED -import org.stellar.anchor.api.exception.rpc.InvalidParamsException -import org.stellar.anchor.api.exception.rpc.InvalidRequestException -import org.stellar.anchor.api.platform.GetTransactionResponse -import org.stellar.anchor.api.platform.PlatformTransactionData -import org.stellar.anchor.api.platform.PlatformTransactionData.Kind.DEPOSIT -import org.stellar.anchor.api.platform.PlatformTransactionData.Sep.* -import org.stellar.anchor.api.rpc.method.DoStellarPaymentRequest -import org.stellar.anchor.api.sep.SepTransactionStatus.* -import org.stellar.anchor.api.shared.Amount -import org.stellar.anchor.api.shared.Customers -import org.stellar.anchor.api.shared.StellarId -import org.stellar.anchor.asset.AssetService -import org.stellar.anchor.config.CustodyConfig -import org.stellar.anchor.custody.CustodyService -import org.stellar.anchor.event.EventService -import org.stellar.anchor.event.EventService.EventQueue.TRANSACTION -import org.stellar.anchor.event.EventService.Session -import org.stellar.anchor.ledger.Horizon -import org.stellar.anchor.metrics.MetricsService -import org.stellar.anchor.platform.data.JdbcSep24Transaction -import org.stellar.anchor.platform.data.JdbcSep6Transaction -import org.stellar.anchor.platform.data.JdbcTransactionPendingTrust -import org.stellar.anchor.platform.data.JdbcTransactionPendingTrustRepo -import org.stellar.anchor.platform.service.AnchorMetrics.PLATFORM_RPC_TRANSACTION -import org.stellar.anchor.platform.validator.RequestValidator -import org.stellar.anchor.sep24.Sep24TransactionStore -import org.stellar.anchor.sep31.Sep31TransactionStore -import org.stellar.anchor.sep6.Sep6TransactionStore -import org.stellar.anchor.util.GsonUtils - -class DoStellarPaymentHandlerTest { - - companion object { - private val gson = GsonUtils.getInstance() - private const val TX_ID = "testId" - private const val TO_ACCOUNT = "testToAccount" - private const val AMOUNT_OUT_ASSET = "testAmountOutAsset" - private const val VALIDATION_ERROR_MESSAGE = "Invalid request" - } - - @MockK(relaxed = true) private lateinit var txn6Store: Sep6TransactionStore - - @MockK(relaxed = true) private lateinit var txn24Store: Sep24TransactionStore - - @MockK(relaxed = true) private lateinit var txn31Store: Sep31TransactionStore - - @MockK(relaxed = true) private lateinit var horizon: Horizon - - @MockK(relaxed = true) private lateinit var requestValidator: RequestValidator - - @MockK(relaxed = true) private lateinit var assetService: AssetService - - @MockK(relaxed = true) private lateinit var custodyConfig: CustodyConfig - - @MockK(relaxed = true) private lateinit var custodyService: CustodyService - - @MockK(relaxed = true) private lateinit var eventService: EventService - - @MockK(relaxed = true) private lateinit var metricsService: MetricsService - - @MockK(relaxed = true) private lateinit var eventSession: Session - - @MockK(relaxed = true) private lateinit var sepTransactionCounter: Counter - - @MockK(relaxed = true) - private lateinit var transactionPendingTrustRepo: JdbcTransactionPendingTrustRepo - - private lateinit var handler: DoStellarPaymentHandler - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - every { eventService.createSession(any(), TRANSACTION) } returns eventSession - this.handler = - DoStellarPaymentHandler( - txn6Store, - txn24Store, - txn31Store, - requestValidator, - custodyConfig, - horizon, - assetService, - custodyService, - eventService, - metricsService, - transactionPendingTrustRepo - ) - } - - @Test - fun test_handle_unsupportedProtocol() { - val request = DoStellarPaymentRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_ANCHOR.toString() - txn24.kind = DEPOSIT.kind - txn24.transferReceivedAt = Instant.now() - val spyTxn24 = spyk(txn24) - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns spyTxn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { spyTxn24.protocol } returns SEP_38.sep.toString() - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - val ex = assertThrows { handler.handle(request) } - assertEquals( - "RPC method[do_stellar_payment] is not supported. Status[pending_anchor], kind[null], protocol[38], funds received[true]", - ex.message - ) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_invalidRequest() { - val request = DoStellarPaymentRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_ANCHOR.toString() - txn24.kind = DEPOSIT.kind - txn24.transferReceivedAt = Instant.now() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { requestValidator.validate(request) } throws - InvalidParamsException(VALIDATION_ERROR_MESSAGE) - - val ex = assertThrows { handler.handle(request) } - assertEquals(VALIDATION_ERROR_MESSAGE, ex.message?.trimIndent()) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_sep24_unsupportedStatus() { - val request = DoStellarPaymentRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_TRUST.toString() - txn24.kind = DEPOSIT.kind - txn24.transferReceivedAt = Instant.now() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - val ex = assertThrows { handler.handle(request) } - assertEquals( - "RPC method[do_stellar_payment] is not supported. Status[pending_trust], kind[deposit], protocol[24], funds received[true]", - ex.message - ) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_sep24_custodyIntegrationDisabled() { - val request = DoStellarPaymentRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_ANCHOR.toString() - txn24.kind = DEPOSIT.kind - txn24.transferReceivedAt = Instant.now() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false - - val ex = assertThrows { handler.handle(request) } - assertEquals("RPC method[do_stellar_payment] requires enabled custody integration", ex.message) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_sep24_transferNotReceived() { - val request = DoStellarPaymentRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_ANCHOR.toString() - txn24.kind = DEPOSIT.kind - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - val ex = assertThrows { handler.handle(request) } - assertEquals( - "RPC method[do_stellar_payment] is not supported. Status[pending_anchor], kind[deposit], protocol[24], funds received[false]", - ex.message - ) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_sep24_ok_trustlineConfigured() { - val transferReceivedAt = Instant.now() - val request = DoStellarPaymentRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.id = TX_ID - txn24.status = PENDING_ANCHOR.toString() - txn24.kind = DEPOSIT.kind - txn24.transferReceivedAt = transferReceivedAt - txn24.toAccount = TO_ACCOUNT - txn24.amountOutAsset = AMOUNT_OUT_ASSET - txn24.userActionRequiredBy = Instant.now() - val sep24TxnCapture = slot() - val anchorEventCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { horizon.hasTrustline(TO_ACCOUNT, AMOUNT_OUT_ASSET) } returns true - every { eventSession.publish(capture(anchorEventCapture)) } just Runs - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { transactionPendingTrustRepo.save(any()) } - verify(exactly = 1) { custodyService.createTransactionPayment(TX_ID, null) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep24Txn = JdbcSep24Transaction() - expectedSep24Txn.id = TX_ID - expectedSep24Txn.kind = DEPOSIT.kind - expectedSep24Txn.status = PENDING_STELLAR.toString() - expectedSep24Txn.updatedAt = sep24TxnCapture.captured.updatedAt - expectedSep24Txn.transferReceivedAt = transferReceivedAt - expectedSep24Txn.toAccount = TO_ACCOUNT - expectedSep24Txn.amountOutAsset = AMOUNT_OUT_ASSET - - JSONAssert.assertEquals( - gson.toJson(expectedSep24Txn), - gson.toJson(sep24TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.id = TX_ID - expectedResponse.sep = SEP_24 - expectedResponse.kind = DEPOSIT - expectedResponse.status = PENDING_STELLAR - expectedResponse.amountExpected = Amount(null, "") - expectedResponse.updatedAt = sep24TxnCapture.captured.updatedAt - expectedResponse.destinationAccount = TO_ACCOUNT - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - val expectedEvent = - AnchorEvent.builder() - .id(anchorEventCapture.captured.id) - .sep(SEP_24.sep.toString()) - .type(TRANSACTION_STATUS_CHANGED) - .transaction(expectedResponse) - .build() - - JSONAssert.assertEquals( - gson.toJson(expectedEvent), - gson.toJson(anchorEventCapture.captured), - JSONCompareMode.STRICT - ) - - assertTrue(sep24TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep24TxnCapture.captured.updatedAt <= endDate) - } - - @Test - fun test_handle_sep24_ok_trustlineNotConfigured() { - val transferReceivedAt = Instant.now() - val request = DoStellarPaymentRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.id = TX_ID - txn24.status = PENDING_ANCHOR.toString() - txn24.kind = DEPOSIT.kind - txn24.transferReceivedAt = transferReceivedAt - txn24.toAccount = TO_ACCOUNT - txn24.amountOutAsset = AMOUNT_OUT_ASSET - val sep24TxnCapture = slot() - val txnPendingTrustCapture = slot() - val anchorEventCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { horizon.hasTrustline(TO_ACCOUNT, AMOUNT_OUT_ASSET) } returns false - every { transactionPendingTrustRepo.save(capture(txnPendingTrustCapture)) } returns - JdbcTransactionPendingTrust() - every { eventSession.publish(capture(anchorEventCapture)) } just Runs - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { custodyService.createTransactionPayment(any(), any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep24Txn = JdbcSep24Transaction() - expectedSep24Txn.id = TX_ID - expectedSep24Txn.kind = DEPOSIT.kind - expectedSep24Txn.status = PENDING_TRUST.toString() - expectedSep24Txn.updatedAt = sep24TxnCapture.captured.updatedAt - expectedSep24Txn.transferReceivedAt = transferReceivedAt - expectedSep24Txn.toAccount = TO_ACCOUNT - expectedSep24Txn.amountOutAsset = AMOUNT_OUT_ASSET - - JSONAssert.assertEquals( - gson.toJson(expectedSep24Txn), - gson.toJson(sep24TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.id = TX_ID - expectedResponse.sep = SEP_24 - expectedResponse.kind = DEPOSIT - expectedResponse.status = PENDING_TRUST - expectedResponse.amountExpected = Amount(null, "") - expectedResponse.updatedAt = sep24TxnCapture.captured.updatedAt - expectedResponse.destinationAccount = TO_ACCOUNT - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - val expectedEvent = - AnchorEvent.builder() - .id(anchorEventCapture.captured.id) - .sep(SEP_24.sep.toString()) - .type(TRANSACTION_STATUS_CHANGED) - .transaction(expectedResponse) - .build() - - JSONAssert.assertEquals( - gson.toJson(expectedEvent), - gson.toJson(anchorEventCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedTxnPendingTrust = JdbcTransactionPendingTrust() - expectedTxnPendingTrust.id = TX_ID - expectedTxnPendingTrust.asset = AMOUNT_OUT_ASSET - expectedTxnPendingTrust.account = TO_ACCOUNT - expectedTxnPendingTrust.createdAt = txnPendingTrustCapture.captured.createdAt - - JSONAssert.assertEquals( - gson.toJson(expectedTxnPendingTrust), - gson.toJson(txnPendingTrustCapture.captured), - JSONCompareMode.STRICT - ) - - assertTrue(sep24TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep24TxnCapture.captured.updatedAt <= endDate) - assertTrue(txnPendingTrustCapture.captured.createdAt >= startDate) - assertTrue(txnPendingTrustCapture.captured.createdAt <= endDate) - } - - @CsvSource(value = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_handle_sep6_unsupportedStatus(kind: String) { - val request = DoStellarPaymentRequest.builder().transactionId(TX_ID).build() - val txn6 = JdbcSep6Transaction() - txn6.status = PENDING_TRUST.toString() - txn6.kind = kind - txn6.transferReceivedAt = Instant.now() - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - val ex = assertThrows { handler.handle(request) } - assertEquals( - "RPC method[do_stellar_payment] is not supported. Status[pending_trust], kind[$kind], protocol[6], funds received[true]", - ex.message - ) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @CsvSource(value = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_handle_sep6_custodyIntegrationDisabled(kind: String) { - val request = DoStellarPaymentRequest.builder().transactionId(TX_ID).build() - val txn6 = JdbcSep6Transaction() - txn6.status = PENDING_ANCHOR.toString() - txn6.kind = kind - txn6.transferReceivedAt = Instant.now() - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false - - val ex = assertThrows { handler.handle(request) } - assertEquals("RPC method[do_stellar_payment] requires enabled custody integration", ex.message) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @CsvSource(value = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_handle_sep6_transferNotReceived(kind: String) { - val request = DoStellarPaymentRequest.builder().transactionId(TX_ID).build() - val txn6 = JdbcSep6Transaction() - txn6.status = PENDING_ANCHOR.toString() - txn6.kind = kind - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(TX_ID) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - val ex = assertThrows { handler.handle(request) } - assertEquals( - "RPC method[do_stellar_payment] is not supported. Status[pending_anchor], kind[$kind], protocol[6], funds received[false]", - ex.message - ) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @CsvSource(value = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_handle_sep6_ok_trustlineConfigured(kind: String) { - val transferReceivedAt = Instant.now() - val request = DoStellarPaymentRequest.builder().transactionId(TX_ID).build() - val txn6 = JdbcSep6Transaction() - txn6.id = TX_ID - txn6.status = PENDING_ANCHOR.toString() - txn6.kind = kind - txn6.transferReceivedAt = transferReceivedAt - txn6.toAccount = TO_ACCOUNT - txn6.amountOutAsset = AMOUNT_OUT_ASSET - val sep6TxnCapture = slot() - val anchorEventCapture = slot() - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { horizon.hasTrustline(TO_ACCOUNT, AMOUNT_OUT_ASSET) } returns true - every { eventSession.publish(capture(anchorEventCapture)) } just Runs - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { transactionPendingTrustRepo.save(any()) } - verify(exactly = 1) { custodyService.createTransactionPayment(TX_ID, null) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep6Txn = JdbcSep6Transaction() - expectedSep6Txn.id = TX_ID - expectedSep6Txn.kind = kind - expectedSep6Txn.status = PENDING_STELLAR.toString() - expectedSep6Txn.updatedAt = sep6TxnCapture.captured.updatedAt - expectedSep6Txn.transferReceivedAt = transferReceivedAt - expectedSep6Txn.toAccount = TO_ACCOUNT - expectedSep6Txn.amountOutAsset = AMOUNT_OUT_ASSET - - JSONAssert.assertEquals( - gson.toJson(expectedSep6Txn), - gson.toJson(sep6TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.id = TX_ID - expectedResponse.sep = SEP_6 - expectedResponse.kind = PlatformTransactionData.Kind.from(kind) - expectedResponse.status = PENDING_STELLAR - expectedResponse.amountExpected = Amount(null, "") - expectedResponse.updatedAt = sep6TxnCapture.captured.updatedAt - expectedResponse.transferReceivedAt = transferReceivedAt - expectedResponse.destinationAccount = TO_ACCOUNT - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - val expectedEvent = - AnchorEvent.builder() - .id(anchorEventCapture.captured.id) - .sep(SEP_6.sep.toString()) - .type(TRANSACTION_STATUS_CHANGED) - .transaction(expectedResponse) - .build() - - JSONAssert.assertEquals( - gson.toJson(expectedEvent), - gson.toJson(anchorEventCapture.captured), - JSONCompareMode.STRICT - ) - - assertTrue(sep6TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep6TxnCapture.captured.updatedAt <= endDate) - } - - @CsvSource(value = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_handle_sep6_ok_trustlineNotConfigured(kind: String) { - val transferReceivedAt = Instant.now() - val request = DoStellarPaymentRequest.builder().transactionId(TX_ID).build() - val txn6 = JdbcSep6Transaction() - txn6.id = TX_ID - txn6.status = PENDING_ANCHOR.toString() - txn6.kind = kind - txn6.transferReceivedAt = transferReceivedAt - txn6.toAccount = TO_ACCOUNT - txn6.amountOutAsset = AMOUNT_OUT_ASSET - val sep6TxnCapture = slot() - val txnPendingTrustCapture = slot() - val anchorEventCapture = slot() - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { horizon.hasTrustline(TO_ACCOUNT, AMOUNT_OUT_ASSET) } returns false - every { transactionPendingTrustRepo.save(capture(txnPendingTrustCapture)) } returns - JdbcTransactionPendingTrust() - every { eventSession.publish(capture(anchorEventCapture)) } just Runs - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { custodyService.createTransactionPayment(any(), any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep6Txn = JdbcSep6Transaction() - expectedSep6Txn.id = TX_ID - expectedSep6Txn.kind = kind - expectedSep6Txn.status = PENDING_TRUST.toString() - expectedSep6Txn.updatedAt = sep6TxnCapture.captured.updatedAt - expectedSep6Txn.transferReceivedAt = transferReceivedAt - expectedSep6Txn.toAccount = TO_ACCOUNT - expectedSep6Txn.amountOutAsset = AMOUNT_OUT_ASSET - - JSONAssert.assertEquals( - gson.toJson(expectedSep6Txn), - gson.toJson(sep6TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.id = TX_ID - expectedResponse.sep = SEP_6 - expectedResponse.kind = PlatformTransactionData.Kind.from(kind) - expectedResponse.status = PENDING_TRUST - expectedResponse.amountExpected = Amount(null, "") - expectedResponse.updatedAt = sep6TxnCapture.captured.updatedAt - expectedResponse.transferReceivedAt = transferReceivedAt - expectedResponse.destinationAccount = TO_ACCOUNT - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - val expectedEvent = - AnchorEvent.builder() - .id(anchorEventCapture.captured.id) - .sep(SEP_6.sep.toString()) - .type(TRANSACTION_STATUS_CHANGED) - .transaction(expectedResponse) - .build() - - JSONAssert.assertEquals( - gson.toJson(expectedEvent), - gson.toJson(anchorEventCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedTxnPendingTrust = JdbcTransactionPendingTrust() - expectedTxnPendingTrust.id = TX_ID - expectedTxnPendingTrust.asset = AMOUNT_OUT_ASSET - expectedTxnPendingTrust.account = TO_ACCOUNT - expectedTxnPendingTrust.createdAt = txnPendingTrustCapture.captured.createdAt - - JSONAssert.assertEquals( - gson.toJson(expectedTxnPendingTrust), - gson.toJson(txnPendingTrustCapture.captured), - JSONCompareMode.STRICT - ) - - assertTrue(sep6TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep6TxnCapture.captured.updatedAt <= endDate) - assertTrue(txnPendingTrustCapture.captured.createdAt >= startDate) - assertTrue(txnPendingTrustCapture.captured.createdAt <= endDate) - } -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/DoStellarRefundHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/DoStellarRefundHandlerTest.kt deleted file mode 100644 index 3e3fbb3712..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/DoStellarRefundHandlerTest.kt +++ /dev/null @@ -1,947 +0,0 @@ -package org.stellar.anchor.platform.rpc - -import io.micrometer.core.instrument.Counter -import io.mockk.* -import io.mockk.impl.annotations.MockK -import java.time.Instant -import kotlin.test.assertEquals -import kotlin.test.assertTrue -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.CsvSource -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.stellar.anchor.api.event.AnchorEvent -import org.stellar.anchor.api.event.AnchorEvent.Type.TRANSACTION_STATUS_CHANGED -import org.stellar.anchor.api.exception.BadRequestException -import org.stellar.anchor.api.exception.rpc.InvalidParamsException -import org.stellar.anchor.api.exception.rpc.InvalidRequestException -import org.stellar.anchor.api.platform.GetTransactionResponse -import org.stellar.anchor.api.platform.PlatformTransactionData -import org.stellar.anchor.api.platform.PlatformTransactionData.Kind.DEPOSIT -import org.stellar.anchor.api.platform.PlatformTransactionData.Kind.RECEIVE -import org.stellar.anchor.api.platform.PlatformTransactionData.Kind.WITHDRAWAL -import org.stellar.anchor.api.platform.PlatformTransactionData.Sep.SEP_24 -import org.stellar.anchor.api.platform.PlatformTransactionData.Sep.SEP_31 -import org.stellar.anchor.api.platform.PlatformTransactionData.Sep.SEP_38 -import org.stellar.anchor.api.platform.PlatformTransactionData.Sep.SEP_6 -import org.stellar.anchor.api.rpc.method.AmountAssetRequest -import org.stellar.anchor.api.rpc.method.DoStellarRefundRequest -import org.stellar.anchor.api.sep.SepTransactionStatus -import org.stellar.anchor.api.sep.SepTransactionStatus.INCOMPLETE -import org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_ANCHOR -import org.stellar.anchor.api.sep.SepTransactionStatus.PENDING_RECEIVER -import org.stellar.anchor.api.shared.Amount -import org.stellar.anchor.api.shared.Customers -import org.stellar.anchor.api.shared.RefundPayment -import org.stellar.anchor.api.shared.Refunds -import org.stellar.anchor.api.shared.StellarId -import org.stellar.anchor.asset.AssetService -import org.stellar.anchor.asset.DefaultAssetService -import org.stellar.anchor.config.CustodyConfig -import org.stellar.anchor.custody.CustodyService -import org.stellar.anchor.event.EventService -import org.stellar.anchor.event.EventService.EventQueue.TRANSACTION -import org.stellar.anchor.event.EventService.Session -import org.stellar.anchor.metrics.MetricsService -import org.stellar.anchor.platform.data.JdbcSep24RefundPayment -import org.stellar.anchor.platform.data.JdbcSep24Refunds -import org.stellar.anchor.platform.data.JdbcSep24Transaction -import org.stellar.anchor.platform.data.JdbcSep31RefundPayment -import org.stellar.anchor.platform.data.JdbcSep31Refunds -import org.stellar.anchor.platform.data.JdbcSep31Transaction -import org.stellar.anchor.platform.data.JdbcSep6Transaction -import org.stellar.anchor.platform.service.AnchorMetrics.PLATFORM_RPC_TRANSACTION -import org.stellar.anchor.platform.utils.toRate -import org.stellar.anchor.platform.validator.RequestValidator -import org.stellar.anchor.sep24.Sep24TransactionStore -import org.stellar.anchor.sep31.Sep31TransactionStore -import org.stellar.anchor.sep6.Sep6TransactionStore -import org.stellar.anchor.util.GsonUtils - -class DoStellarRefundHandlerTest { - - companion object { - private val gson = GsonUtils.getInstance() - private const val TX_ID = "testId" - private const val FIAT_USD = "iso4217:USD" - private const val STELLAR_USDC = - "stellar:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN" - private const val FIAT_USD_CODE = "USD" - private const val MEMO = "testMemo" - private const val MEMO_TYPE = "text" - private const val VALIDATION_ERROR_MESSAGE = "Invalid request" - } - - @MockK(relaxed = true) private lateinit var txn6Store: Sep6TransactionStore - - @MockK(relaxed = true) private lateinit var txn24Store: Sep24TransactionStore - - @MockK(relaxed = true) private lateinit var txn31Store: Sep31TransactionStore - - @MockK(relaxed = true) private lateinit var requestValidator: RequestValidator - - @MockK(relaxed = true) private lateinit var assetService: AssetService - - @MockK(relaxed = true) private lateinit var custodyConfig: CustodyConfig - - @MockK(relaxed = true) private lateinit var custodyService: CustodyService - - @MockK(relaxed = true) private lateinit var eventService: EventService - - @MockK(relaxed = true) private lateinit var metricsService: MetricsService - - @MockK(relaxed = true) private lateinit var eventSession: Session - - @MockK(relaxed = true) private lateinit var sepTransactionCounter: Counter - - private lateinit var handler: DoStellarRefundHandler - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - every { eventService.createSession(any(), TRANSACTION) } returns eventSession - this.assetService = DefaultAssetService.fromJsonResource("test_assets.json") - this.handler = - DoStellarRefundHandler( - txn6Store, - txn24Store, - txn31Store, - requestValidator, - custodyConfig, - assetService, - custodyService, - eventService, - metricsService - ) - } - - @Test - fun test_handle_unsupportedProtocol() { - val request = DoStellarRefundRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_ANCHOR.toString() - val spyTxn24 = spyk(txn24) - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns spyTxn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { spyTxn24.protocol } returns SEP_38.sep.toString() - - val ex = assertThrows { handler.handle(request) } - assertEquals( - "RPC method[do_stellar_refund] is not supported. Status[pending_anchor], kind[null], protocol[38], funds received[false]", - ex.message - ) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_invalidRequest() { - val request = DoStellarRefundRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_ANCHOR.toString() - txn24.transferReceivedAt = Instant.now() - txn24.kind = WITHDRAWAL.kind - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { requestValidator.validate(request) } throws - InvalidParamsException(VALIDATION_ERROR_MESSAGE) - - val ex = assertThrows { handler.handle(request) } - assertEquals(VALIDATION_ERROR_MESSAGE, ex.message?.trimIndent()) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_invalidAmounts() { - val request = - DoStellarRefundRequest.builder() - .transactionId(TX_ID) - .refund( - DoStellarRefundRequest.Refund.builder() - .amount(AmountAssetRequest("1", STELLAR_USDC)) - .amountFee(AmountAssetRequest("0.1", FIAT_USD)) - .build() - ) - .build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_ANCHOR.toString() - txn24.requestAssetCode = FIAT_USD_CODE - txn24.amountInAsset = STELLAR_USDC - txn24.amountOutAsset = STELLAR_USDC - txn24.amountFeeAsset = FIAT_USD - txn24.transferReceivedAt = Instant.now() - txn24.kind = WITHDRAWAL.kind - val sep24TxnCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - request.refund.amount.amount = "-1" - var ex = assertThrows { handler.handle(request) } - assertEquals("refund.amount.amount should be positive", ex.message) - request.refund.amount.amount = "1" - - request.refund.amountFee.amount = "-0.1" - ex = assertThrows { handler.handle(request) } - assertEquals("refund.amountFee.amount should be non-negative", ex.message) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_invalidAssets() { - val request = - DoStellarRefundRequest.builder() - .transactionId(TX_ID) - .refund( - DoStellarRefundRequest.Refund.builder() - .amount(AmountAssetRequest("1", STELLAR_USDC)) - .amountFee(AmountAssetRequest("0.1", FIAT_USD)) - .build() - ) - .build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_ANCHOR.toString() - txn24.requestAssetCode = FIAT_USD_CODE - txn24.amountInAsset = STELLAR_USDC - txn24.amountOutAsset = STELLAR_USDC - txn24.amountFeeAsset = FIAT_USD - txn24.transferReceivedAt = Instant.now() - txn24.kind = WITHDRAWAL.kind - val sep24TxnCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - request.refund.amount.asset = FIAT_USD - var ex = assertThrows { handler.handle(request) } - assertEquals("refund.amount.asset does not match transaction amount_in_asset", ex.message) - request.refund.amount.asset = STELLAR_USDC - - request.refund.amountFee.asset = STELLAR_USDC - ex = assertThrows { handler.handle(request) } - assertEquals( - "refund.amount_fee.asset does not match match transaction amount_fee_asset", - ex.message - ) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_sep24_unsupportedKind() { - val request = DoStellarRefundRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.status = INCOMPLETE.toString() - txn24.kind = DEPOSIT.kind - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - - val ex = assertThrows { handler.handle(request) } - assertEquals( - "RPC method[do_stellar_refund] is not supported. Status[incomplete], kind[deposit], protocol[24], funds received[false]", - ex.message - ) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_sep24_unsupportedStatus() { - val request = DoStellarRefundRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.status = INCOMPLETE.toString() - txn24.kind = WITHDRAWAL.kind - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - - val ex = assertThrows { handler.handle(request) } - assertEquals( - "RPC method[do_stellar_refund] is not supported. Status[incomplete], kind[withdrawal], protocol[24], funds received[false]", - ex.message - ) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_sep24_disabledCustodyIntegration() { - val request = DoStellarRefundRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_ANCHOR.toString() - txn24.requestAssetCode = FIAT_USD_CODE - txn24.amountOutAsset = STELLAR_USDC - txn24.amountFeeAsset = FIAT_USD - txn24.transferReceivedAt = Instant.now() - txn24.kind = WITHDRAWAL.kind - val sep24TxnCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - - val ex = assertThrows { handler.handle(request) } - assertEquals("RPC method[do_stellar_refund] requires enabled custody integration", ex.message) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_sep24_sent_more_then_amount_in() { - val transferReceivedAt = Instant.now() - val request = - DoStellarRefundRequest.builder() - .transactionId(TX_ID) - .refund( - DoStellarRefundRequest.Refund.builder() - .amount(AmountAssetRequest("1", STELLAR_USDC)) - .amountFee(AmountAssetRequest("0.1", STELLAR_USDC)) - .build() - ) - .build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_ANCHOR.toString() - txn24.kind = WITHDRAWAL.kind - txn24.transferReceivedAt = transferReceivedAt - txn24.requestAssetCode = FIAT_USD_CODE - txn24.amountInAsset = STELLAR_USDC - txn24.amountIn = "1.1" - txn24.amountOutAsset = FIAT_USD - txn24.amountOut = "1" - txn24.amountFeeAsset = STELLAR_USDC - txn24.amountFee = "0.1" - txn24.refundMemo = MEMO - txn24.refundMemoType = MEMO_TYPE - - val payment = JdbcSep24RefundPayment() - payment.id = "1" - payment.idType = RefundPayment.IdType.STELLAR.toString() - payment.amount = "0.1" - payment.fee = "0" - val refunds = JdbcSep24Refunds() - refunds.amountRefunded = "1" - refunds.amountFee = "0.1" - refunds.payments = listOf(payment) - txn24.refunds = refunds - - val sep24TxnCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - val ex = assertThrows { handler.handle(request) } - assertEquals("Refund amount exceeds amount_in", ex.message) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_sep24_ok() { - val transferReceivedAt = Instant.now() - val request = - DoStellarRefundRequest.builder() - .transactionId(TX_ID) - .refund( - DoStellarRefundRequest.Refund.builder() - .amount(AmountAssetRequest("1", STELLAR_USDC)) - .amountFee(AmountAssetRequest("0.1", FIAT_USD)) - .build() - ) - .build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_ANCHOR.toString() - txn24.kind = WITHDRAWAL.kind - txn24.transferReceivedAt = transferReceivedAt - txn24.requestAssetCode = FIAT_USD_CODE - txn24.amountInAsset = STELLAR_USDC - txn24.amountIn = "1.1" - txn24.amountOutAsset = STELLAR_USDC - txn24.amountOut = "1" - txn24.amountFeeAsset = FIAT_USD - txn24.amountFee = "0.1" - txn24.refundMemo = MEMO - txn24.refundMemoType = MEMO_TYPE - txn24.userActionRequiredBy = Instant.now() - val sep24TxnCapture = slot() - val anchorEventCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { eventSession.publish(capture(anchorEventCapture)) } just Runs - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep24Txn = JdbcSep24Transaction() - expectedSep24Txn.kind = WITHDRAWAL.kind - expectedSep24Txn.status = SepTransactionStatus.PENDING_STELLAR.toString() - expectedSep24Txn.updatedAt = sep24TxnCapture.captured.updatedAt - expectedSep24Txn.requestAssetCode = FIAT_USD_CODE - expectedSep24Txn.amountInAsset = STELLAR_USDC - expectedSep24Txn.amountIn = "1.1" - expectedSep24Txn.amountOutAsset = STELLAR_USDC - expectedSep24Txn.amountOut = "1" - expectedSep24Txn.amountFeeAsset = FIAT_USD - expectedSep24Txn.amountFee = "0.1" - expectedSep24Txn.refundMemo = MEMO - expectedSep24Txn.refundMemoType = MEMO_TYPE - expectedSep24Txn.transferReceivedAt = transferReceivedAt - - JSONAssert.assertEquals( - gson.toJson(expectedSep24Txn), - gson.toJson(sep24TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.sep = SEP_24 - expectedResponse.kind = WITHDRAWAL - expectedResponse.status = SepTransactionStatus.PENDING_STELLAR - expectedResponse.amountExpected = Amount(null, FIAT_USD) - expectedResponse.amountIn = Amount("1.1", STELLAR_USDC) - expectedResponse.amountOut = Amount("1", STELLAR_USDC) - expectedResponse.feeDetails = Amount("0.1", FIAT_USD).toRate() - expectedResponse.updatedAt = sep24TxnCapture.captured.updatedAt - expectedResponse.refundMemo = MEMO - expectedResponse.refundMemoType = MEMO_TYPE - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - val expectedEvent = - AnchorEvent.builder() - .id(anchorEventCapture.captured.id) - .sep(SEP_24.sep.toString()) - .type(TRANSACTION_STATUS_CHANGED) - .transaction(expectedResponse) - .build() - - JSONAssert.assertEquals( - gson.toJson(expectedEvent), - gson.toJson(anchorEventCapture.captured), - JSONCompareMode.STRICT - ) - - assertTrue(sep24TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep24TxnCapture.captured.updatedAt <= endDate) - } - - @Test - fun test_handle_sep31_ok() { - val transferReceivedAt = Instant.now() - val request = - DoStellarRefundRequest.builder() - .transactionId(TX_ID) - .refund( - DoStellarRefundRequest.Refund.builder() - .amount(AmountAssetRequest("1", STELLAR_USDC)) - .amountFee(AmountAssetRequest("0.1", STELLAR_USDC)) - .build() - ) - .build() - val txn31 = JdbcSep31Transaction() - txn31.status = PENDING_RECEIVER.toString() - txn31.transferReceivedAt = transferReceivedAt - txn31.amountInAsset = STELLAR_USDC - txn31.amountIn = "1.1" - txn31.amountOutAsset = FIAT_USD - txn31.amountOut = "1" - txn31.amountFeeAsset = STELLAR_USDC - txn31.amountFee = "0.1" - val sep31TxnCapture = slot() - val anchorEventCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(TX_ID) } returns txn31 - every { txn31Store.save(capture(sep31TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { eventSession.publish(capture(anchorEventCapture)) } just Runs - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn24Store.save(any()) } - - val expectedSep31Txn = JdbcSep31Transaction() - expectedSep31Txn.status = SepTransactionStatus.PENDING_STELLAR.toString() - expectedSep31Txn.updatedAt = sep31TxnCapture.captured.updatedAt - expectedSep31Txn.amountInAsset = STELLAR_USDC - expectedSep31Txn.amountIn = "1.1" - expectedSep31Txn.amountOutAsset = FIAT_USD - expectedSep31Txn.amountOut = "1" - expectedSep31Txn.amountFeeAsset = STELLAR_USDC - expectedSep31Txn.amountFee = "0.1" - expectedSep31Txn.transferReceivedAt = transferReceivedAt - - JSONAssert.assertEquals( - gson.toJson(expectedSep31Txn), - gson.toJson(sep31TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.sep = SEP_31 - expectedResponse.kind = RECEIVE - expectedResponse.status = SepTransactionStatus.PENDING_STELLAR - expectedResponse.amountExpected = Amount(null, STELLAR_USDC) - expectedResponse.amountIn = Amount("1.1", STELLAR_USDC) - expectedResponse.amountOut = Amount("1", FIAT_USD) - expectedResponse.feeDetails = Amount("0.1", STELLAR_USDC).toRate() - expectedResponse.updatedAt = sep31TxnCapture.captured.updatedAt - expectedResponse.transferReceivedAt = transferReceivedAt - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - val expectedEvent = - AnchorEvent.builder() - .id(anchorEventCapture.captured.id) - .sep(SEP_31.sep.toString()) - .type(AnchorEvent.Type.TRANSACTION_STATUS_CHANGED) - .transaction(expectedResponse) - .build() - - JSONAssert.assertEquals( - gson.toJson(expectedEvent), - gson.toJson(anchorEventCapture.captured), - JSONCompareMode.STRICT - ) - - assertTrue(sep31TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep31TxnCapture.captured.updatedAt <= endDate) - } - - @Test - fun test_handle_sep31_sent_more_then_amount_in() { - val transferReceivedAt = Instant.now() - val request = - DoStellarRefundRequest.builder() - .transactionId(TX_ID) - .refund( - DoStellarRefundRequest.Refund.builder() - .amount(AmountAssetRequest("1", STELLAR_USDC)) - .amountFee(AmountAssetRequest("0.1", STELLAR_USDC)) - .build() - ) - .build() - val txn31 = JdbcSep31Transaction() - txn31.status = PENDING_RECEIVER.toString() - txn31.transferReceivedAt = transferReceivedAt - txn31.amountInAsset = STELLAR_USDC - txn31.amountIn = "1" - txn31.amountOutAsset = FIAT_USD - txn31.amountOut = "1" - txn31.amountFeeAsset = STELLAR_USDC - txn31.amountFee = "0.1" - val sep31TxnCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(TX_ID) } returns txn31 - every { txn31Store.save(capture(sep31TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - val ex = assertThrows { handler.handle(request) } - assertEquals("Refund amount exceeds amount_in", ex.message) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_sep31_sent_less_then_amount_in() { - val transferReceivedAt = Instant.now() - val request = - DoStellarRefundRequest.builder() - .transactionId(TX_ID) - .refund( - DoStellarRefundRequest.Refund.builder() - .amount(AmountAssetRequest("0.8", STELLAR_USDC)) - .amountFee(AmountAssetRequest("0.1", STELLAR_USDC)) - .build() - ) - .build() - val txn31 = JdbcSep31Transaction() - txn31.status = PENDING_RECEIVER.toString() - txn31.transferReceivedAt = transferReceivedAt - txn31.amountInAsset = STELLAR_USDC - txn31.amountIn = "1" - txn31.amountOutAsset = FIAT_USD - txn31.amountOut = "1" - txn31.amountFeeAsset = STELLAR_USDC - txn31.amountFee = "0.1" - val sep31TxnCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns null - every { txn31Store.findByTransactionId(any()) } returns txn31 - every { txn31Store.save(capture(sep31TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - val ex = assertThrows { handler.handle(request) } - assertEquals("Refund amount is less than amount_in", ex.message) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @Test - fun test_handle_sep31_sent_multiple_refund() { - val transferReceivedAt = Instant.now() - val request = - DoStellarRefundRequest.builder() - .transactionId(TX_ID) - .refund( - DoStellarRefundRequest.Refund.builder() - .amount(AmountAssetRequest("0.8", STELLAR_USDC)) - .amountFee(AmountAssetRequest("0.1", STELLAR_USDC)) - .build() - ) - .build() - val txn31 = JdbcSep31Transaction() - txn31.status = PENDING_RECEIVER.toString() - txn31.transferReceivedAt = transferReceivedAt - txn31.amountInAsset = STELLAR_USDC - txn31.amountIn = "1" - txn31.amountOutAsset = FIAT_USD - txn31.amountOut = "1" - txn31.amountFeeAsset = STELLAR_USDC - txn31.amountFee = "0.1" - val payment = JdbcSep31RefundPayment() - payment.id = "1" - payment.amount = "0.1" - payment.fee = "0.1" - val refunds = JdbcSep31Refunds() - refunds.amountRefunded = "0.1" - refunds.amountFee = "0.1" - refunds.payments = listOf(payment) - txn31.refunds = refunds - val sep31TxnCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(TX_ID) } returns txn31 - every { txn31Store.save(capture(sep31TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - val ex = assertThrows { handler.handle(request) } - assertEquals( - "Multiple refunds aren't supported for kind[RECEIVE], protocol[31] and action[do_stellar_refund]", - ex.message - ) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @CsvSource(value = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_handle_sep6_unsupportedKind(kind: String) { - val request = DoStellarRefundRequest.builder().transactionId(TX_ID).build() - val txn6 = JdbcSep6Transaction() - txn6.status = INCOMPLETE.toString() - txn6.kind = kind - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - - val ex = assertThrows { handler.handle(request) } - assertEquals( - "RPC method[do_stellar_refund] is not supported. Status[incomplete], kind[$kind], protocol[6], funds received[false]", - ex.message - ) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @CsvSource(value = ["withdrawal", "withdrawal-exchange"]) - @ParameterizedTest - fun test_handle_sep6_unsupportedStatus(kind: String) { - val request = DoStellarRefundRequest.builder().transactionId(TX_ID).build() - val txn6 = JdbcSep6Transaction() - txn6.status = INCOMPLETE.toString() - txn6.kind = kind - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - - val ex = assertThrows { handler.handle(request) } - assertEquals( - "RPC method[do_stellar_refund] is not supported. Status[incomplete], kind[$kind], protocol[6], funds received[false]", - ex.message - ) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @CsvSource(value = ["withdrawal", "withdrawal-exchange"]) - @ParameterizedTest - fun test_handle_sep6_disabledCustodyIntegration(kind: String) { - val request = DoStellarRefundRequest.builder().transactionId(TX_ID).build() - val txn6 = JdbcSep6Transaction() - txn6.status = PENDING_ANCHOR.toString() - txn6.requestAssetCode = FIAT_USD_CODE - txn6.amountOutAsset = STELLAR_USDC - txn6.amountFeeAsset = FIAT_USD - txn6.transferReceivedAt = Instant.now() - txn6.kind = kind - val sep6TxnCapture = slot() - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false - - val ex = assertThrows { handler.handle(request) } - assertEquals("RPC method[do_stellar_refund] requires enabled custody integration", ex.message) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @CsvSource(value = ["withdrawal", "withdrawal-exchange"]) - @ParameterizedTest - fun test_handle_sep6_sent_more_then_amount_in(kind: String) { - val transferReceivedAt = Instant.now() - val request = - DoStellarRefundRequest.builder() - .transactionId(TX_ID) - .refund( - DoStellarRefundRequest.Refund.builder() - .amount(AmountAssetRequest("1", STELLAR_USDC)) - .amountFee(AmountAssetRequest("0.1", STELLAR_USDC)) - .build() - ) - .build() - val txn6 = JdbcSep6Transaction() - txn6.status = PENDING_ANCHOR.toString() - txn6.kind = kind - txn6.transferReceivedAt = transferReceivedAt - txn6.requestAssetCode = FIAT_USD_CODE - txn6.amountInAsset = STELLAR_USDC - txn6.amountIn = "1.1" - txn6.amountOutAsset = FIAT_USD - txn6.amountOut = "1" - txn6.amountFeeAsset = STELLAR_USDC - txn6.amountFee = "0.1" - txn6.refundMemo = MEMO - txn6.refundMemoType = MEMO_TYPE - - val payment = RefundPayment() - payment.id = "1" - payment.amount = Amount("0.1", STELLAR_USDC) - payment.fee = Amount("0", STELLAR_USDC) - val refunds = Refunds() - refunds.amountRefunded = Amount("1", STELLAR_USDC) - refunds.amountFee = Amount("0.1", STELLAR_USDC) - refunds.payments = arrayOf(payment) - txn6.refunds = refunds - - val sep24TxnCapture = slot() - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - val ex = assertThrows { handler.handle(request) } - assertEquals("Refund amount exceeds amount_in", ex.message) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - - @CsvSource(value = ["withdrawal", "withdrawal-exchange"]) - @ParameterizedTest - fun test_handle_sep6_ok(kind: String) { - val transferReceivedAt = Instant.now() - val request = - DoStellarRefundRequest.builder() - .transactionId(TX_ID) - .refund( - DoStellarRefundRequest.Refund.builder() - .amount(AmountAssetRequest("1", STELLAR_USDC)) - .amountFee(AmountAssetRequest("0.1", FIAT_USD)) - .build() - ) - .build() - val txn6 = JdbcSep6Transaction() - txn6.status = PENDING_ANCHOR.toString() - txn6.kind = kind - txn6.transferReceivedAt = transferReceivedAt - txn6.requestAssetCode = FIAT_USD_CODE - txn6.amountInAsset = STELLAR_USDC - txn6.amountIn = "1.1" - txn6.amountOutAsset = STELLAR_USDC - txn6.amountOut = "1" - txn6.amountFeeAsset = FIAT_USD - txn6.amountFee = "0.1" - txn6.refundMemo = MEMO - txn6.refundMemoType = MEMO_TYPE - val sep6TxnCapture = slot() - val anchorEventCapture = slot() - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { eventSession.publish(capture(anchorEventCapture)) } just Runs - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep6Txn = JdbcSep6Transaction() - expectedSep6Txn.kind = kind - expectedSep6Txn.status = SepTransactionStatus.PENDING_STELLAR.toString() - expectedSep6Txn.updatedAt = sep6TxnCapture.captured.updatedAt - expectedSep6Txn.requestAssetCode = FIAT_USD_CODE - expectedSep6Txn.amountInAsset = STELLAR_USDC - expectedSep6Txn.amountIn = "1.1" - expectedSep6Txn.amountOutAsset = STELLAR_USDC - expectedSep6Txn.amountOut = "1" - expectedSep6Txn.amountFeeAsset = FIAT_USD - expectedSep6Txn.amountFee = "0.1" - expectedSep6Txn.refundMemo = MEMO - expectedSep6Txn.refundMemoType = MEMO_TYPE - expectedSep6Txn.transferReceivedAt = transferReceivedAt - - JSONAssert.assertEquals( - gson.toJson(expectedSep6Txn), - gson.toJson(sep6TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.sep = SEP_6 - expectedResponse.kind = PlatformTransactionData.Kind.from(kind) - expectedResponse.status = SepTransactionStatus.PENDING_STELLAR - expectedResponse.amountExpected = Amount(null, FIAT_USD) - expectedResponse.amountIn = Amount("1.1", STELLAR_USDC) - expectedResponse.amountOut = Amount("1", STELLAR_USDC) - expectedResponse.feeDetails = Amount("0.1", FIAT_USD).toRate() - expectedResponse.updatedAt = sep6TxnCapture.captured.updatedAt - expectedResponse.transferReceivedAt = transferReceivedAt - expectedResponse.refundMemo = MEMO - expectedResponse.refundMemoType = MEMO_TYPE - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - val expectedEvent = - AnchorEvent.builder() - .id(anchorEventCapture.captured.id) - .sep(SEP_6.sep.toString()) - .type(TRANSACTION_STATUS_CHANGED) - .transaction(expectedResponse) - .build() - - JSONAssert.assertEquals( - gson.toJson(expectedEvent), - gson.toJson(anchorEventCapture.captured), - JSONCompareMode.STRICT - ) - - assertTrue(sep6TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep6TxnCapture.captured.updatedAt <= endDate) - } -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/GetTransactionHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/GetTransactionHandlerTest.kt index 9ec474c8c2..26edd1ccd7 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/GetTransactionHandlerTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/GetTransactionHandlerTest.kt @@ -20,8 +20,6 @@ import org.stellar.anchor.api.shared.Amount import org.stellar.anchor.api.shared.Customers import org.stellar.anchor.api.shared.StellarId import org.stellar.anchor.asset.AssetService -import org.stellar.anchor.config.CustodyConfig -import org.stellar.anchor.custody.CustodyService import org.stellar.anchor.event.EventService import org.stellar.anchor.platform.data.JdbcSep31Transaction import org.stellar.anchor.platform.service.TransactionService @@ -51,8 +49,6 @@ class GetTransactionHandlerTest { @MockK(relaxed = true) private lateinit var eventSession: EventService.Session @MockK(relaxed = true) private lateinit var sep6DepositInfoGenerator: Sep6DepositInfoGenerator @MockK(relaxed = true) private lateinit var sep24DepositInfoGenerator: Sep24DepositInfoGenerator - @MockK(relaxed = true) private lateinit var custodyService: CustodyService - @MockK(relaxed = true) private lateinit var custodyConfig: CustodyConfig private lateinit var transactionService: TransactionService private lateinit var handler: GetTransactionHandler @@ -70,9 +66,7 @@ class GetTransactionHandlerTest { assetService, eventService, sep6DepositInfoGenerator, - sep24DepositInfoGenerator, - custodyService, - custodyConfig + sep24DepositInfoGenerator ) handler = GetTransactionHandler(transactionService) } diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/GetTransactionsHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/GetTransactionsHandlerTest.kt index aca870c804..8cdad22678 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/GetTransactionsHandlerTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/GetTransactionsHandlerTest.kt @@ -13,8 +13,6 @@ import org.stellar.anchor.api.platform.TransactionsSeps import org.stellar.anchor.api.rpc.method.GetTransactionsRpcRequest import org.stellar.anchor.api.sep.SepTransactionStatus import org.stellar.anchor.asset.AssetService -import org.stellar.anchor.config.CustodyConfig -import org.stellar.anchor.custody.CustodyService import org.stellar.anchor.event.EventService import org.stellar.anchor.platform.data.JdbcSep31Transaction import org.stellar.anchor.platform.service.TransactionService @@ -41,8 +39,6 @@ class GetTransactionsHandlerTest { @MockK(relaxed = true) private lateinit var eventSession: EventService.Session @MockK(relaxed = true) private lateinit var sep6DepositInfoGenerator: Sep6DepositInfoGenerator @MockK(relaxed = true) private lateinit var sep24DepositInfoGenerator: Sep24DepositInfoGenerator - @MockK(relaxed = true) private lateinit var custodyService: CustodyService - @MockK(relaxed = true) private lateinit var custodyConfig: CustodyConfig private lateinit var transactionService: TransactionService private lateinit var handler: GetTransactionsHandler @@ -61,9 +57,7 @@ class GetTransactionsHandlerTest { assetService, eventService, sep6DepositInfoGenerator, - sep24DepositInfoGenerator, - custodyService, - custodyConfig + sep24DepositInfoGenerator ) this.handler = GetTransactionsHandler(transactionService) } diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/NotifyOffchainFundsReceivedHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/NotifyOffchainFundsReceivedHandlerTest.kt index cc4a910918..baaa3b3f0e 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/NotifyOffchainFundsReceivedHandlerTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/NotifyOffchainFundsReceivedHandlerTest.kt @@ -32,8 +32,6 @@ import org.stellar.anchor.api.shared.FeeDetails import org.stellar.anchor.api.shared.StellarId import org.stellar.anchor.asset.AssetService import org.stellar.anchor.asset.DefaultAssetService -import org.stellar.anchor.config.CustodyConfig -import org.stellar.anchor.custody.CustodyService import org.stellar.anchor.event.EventService import org.stellar.anchor.event.EventService.EventQueue.TRANSACTION import org.stellar.anchor.event.EventService.Session @@ -43,7 +41,6 @@ import org.stellar.anchor.platform.data.JdbcSep6Transaction import org.stellar.anchor.platform.service.AnchorMetrics.PLATFORM_RPC_TRANSACTION import org.stellar.anchor.platform.utils.toRate import org.stellar.anchor.platform.validator.RequestValidator -import org.stellar.anchor.sep24.Sep24Transaction import org.stellar.anchor.sep24.Sep24TransactionStore import org.stellar.anchor.sep31.Sep31TransactionStore import org.stellar.anchor.sep6.Sep6TransactionStore @@ -72,10 +69,6 @@ class NotifyOffchainFundsReceivedHandlerTest { @MockK(relaxed = true) private lateinit var assetService: AssetService - @MockK(relaxed = true) private lateinit var custodyService: CustodyService - - @MockK(relaxed = true) private lateinit var custodyConfig: CustodyConfig - @MockK(relaxed = true) private lateinit var eventService: EventService @MockK(relaxed = true) private lateinit var metricsService: MetricsService @@ -98,8 +91,6 @@ class NotifyOffchainFundsReceivedHandlerTest { txn31Store, requestValidator, assetService, - custodyService, - custodyConfig, eventService, metricsService ) @@ -323,7 +314,6 @@ class NotifyOffchainFundsReceivedHandlerTest { every { txn24Store.findByTransactionId(TX_ID) } returns txn24 every { txn31Store.findByTransactionId(any()) } returns null every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false every { eventSession.publish(capture(anchorEventCapture)) } just Runs every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns sepTransactionCounter @@ -334,7 +324,6 @@ class NotifyOffchainFundsReceivedHandlerTest { verify(exactly = 0) { txn6Store.save(any()) } verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sepTransactionCounter.increment() } val expectedSep24Txn = JdbcSep24Transaction() @@ -414,7 +403,6 @@ class NotifyOffchainFundsReceivedHandlerTest { every { txn24Store.findByTransactionId(TX_ID) } returns txn24 every { txn31Store.findByTransactionId(any()) } returns null every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false every { eventSession.publish(capture(anchorEventCapture)) } just Runs every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns sepTransactionCounter @@ -425,7 +413,6 @@ class NotifyOffchainFundsReceivedHandlerTest { verify(exactly = 0) { txn6Store.save(any()) } verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sepTransactionCounter.increment() } val expectedSep24Txn = JdbcSep24Transaction() @@ -477,96 +464,6 @@ class NotifyOffchainFundsReceivedHandlerTest { assertTrue(sep24TxnCapture.captured.updatedAt <= endDate) } - @Test - fun test_handle_sep24_ok_withExternalTxIdAndWithoutFundsReceivedAt_custodyIntegrationEnabled() { - val request = - NotifyOffchainFundsReceivedRequest.builder() - .transactionId(TX_ID) - .externalTransactionId(EXTERNAL_TX_ID) - .build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_USR_TRANSFER_START.toString() - txn24.kind = DEPOSIT.kind - txn24.requestAssetCode = FIAT_USD_CODE - val sep24TxnCapture = slot() - val sep24CustodyTxnCapture = slot() - val anchorEventCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { custodyService.createTransaction(capture(sep24CustodyTxnCapture)) } just Runs - every { eventSession.publish(capture(anchorEventCapture)) } just Runs - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep24Txn = JdbcSep24Transaction() - expectedSep24Txn.kind = DEPOSIT.kind - expectedSep24Txn.status = PENDING_ANCHOR.toString() - expectedSep24Txn.status = PENDING_ANCHOR.toString() - expectedSep24Txn.updatedAt = sep24TxnCapture.captured.updatedAt - expectedSep24Txn.externalTransactionId = EXTERNAL_TX_ID - expectedSep24Txn.transferReceivedAt = sep24TxnCapture.captured.transferReceivedAt - expectedSep24Txn.requestAssetCode = FIAT_USD_CODE - - JSONAssert.assertEquals( - gson.toJson(expectedSep24Txn), - gson.toJson(sep24TxnCapture.captured), - JSONCompareMode.STRICT - ) - - JSONAssert.assertEquals( - gson.toJson(expectedSep24Txn), - gson.toJson(sep24CustodyTxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.sep = SEP_24 - expectedResponse.kind = DEPOSIT - expectedResponse.status = PENDING_ANCHOR - expectedResponse.externalTransactionId = EXTERNAL_TX_ID - expectedResponse.updatedAt = sep24TxnCapture.captured.updatedAt - expectedResponse.amountExpected = Amount(null, FIAT_USD) - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - val expectedEvent = - AnchorEvent.builder() - .id(anchorEventCapture.captured.id) - .sep(SEP_24.sep.toString()) - .type(TRANSACTION_STATUS_CHANGED) - .transaction(expectedResponse) - .build() - - JSONAssert.assertEquals( - gson.toJson(expectedEvent), - gson.toJson(anchorEventCapture.captured), - JSONCompareMode.STRICT - ) - - assertTrue(sep24TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep24TxnCapture.captured.updatedAt <= endDate) - assertTrue(sep24TxnCapture.captured.transferReceivedAt >= startDate) - assertTrue(sep24TxnCapture.captured.transferReceivedAt <= endDate) - } - @Test fun test_handle_sep24_ok_withoutAmountAndExternalTxIdAndFundsReceivedAt() { val request = NotifyOffchainFundsReceivedRequest.builder().transactionId(TX_ID).build() @@ -716,7 +613,6 @@ class NotifyOffchainFundsReceivedHandlerTest { every { txn24Store.findByTransactionId(any()) } returns null every { txn31Store.findByTransactionId(any()) } returns null every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false every { eventSession.publish(capture(anchorEventCapture)) } just Runs every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns sepTransactionCounter @@ -807,7 +703,6 @@ class NotifyOffchainFundsReceivedHandlerTest { every { txn24Store.findByTransactionId(any()) } returns null every { txn31Store.findByTransactionId(any()) } returns null every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false every { eventSession.publish(capture(anchorEventCapture)) } just Runs every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns sepTransactionCounter @@ -870,100 +765,6 @@ class NotifyOffchainFundsReceivedHandlerTest { assertTrue(sep6TxnCapture.captured.updatedAt <= endDate) } - @CsvSource(value = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_handle_sep6_ok_withExternalTxIdAndWithoutFundsReceivedAt_custodyIntegrationEnabled( - kind: String - ) { - val request = - NotifyOffchainFundsReceivedRequest.builder() - .transactionId(TX_ID) - .externalTransactionId(EXTERNAL_TX_ID) - .build() - val txn6 = JdbcSep6Transaction() - txn6.status = PENDING_USR_TRANSFER_START.toString() - txn6.kind = kind - txn6.requestAssetCode = FIAT_USD_CODE - val sep6TxnCapture = slot() - val sep6CustodyTxnCapture = slot() - val anchorEventCapture = slot() - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { custodyService.createTransaction(capture(sep6CustodyTxnCapture)) } just Runs - every { eventSession.publish(capture(anchorEventCapture)) } just Runs - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep6Txn = JdbcSep6Transaction() - expectedSep6Txn.kind = kind - expectedSep6Txn.status = PENDING_ANCHOR.toString() - expectedSep6Txn.status = PENDING_ANCHOR.toString() - expectedSep6Txn.updatedAt = sep6TxnCapture.captured.updatedAt - expectedSep6Txn.externalTransactionId = EXTERNAL_TX_ID - expectedSep6Txn.transferReceivedAt = sep6TxnCapture.captured.transferReceivedAt - expectedSep6Txn.requestAssetCode = FIAT_USD_CODE - - JSONAssert.assertEquals( - gson.toJson(expectedSep6Txn), - gson.toJson(sep6TxnCapture.captured), - JSONCompareMode.STRICT - ) - - JSONAssert.assertEquals( - gson.toJson(expectedSep6Txn), - gson.toJson(sep6CustodyTxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.sep = SEP_6 - expectedResponse.kind = Kind.from(kind) - expectedResponse.status = PENDING_ANCHOR - expectedResponse.externalTransactionId = EXTERNAL_TX_ID - expectedResponse.updatedAt = sep6TxnCapture.captured.updatedAt - expectedResponse.transferReceivedAt = sep6TxnCapture.captured.transferReceivedAt - expectedResponse.amountExpected = Amount(null, FIAT_USD) - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - val expectedEvent = - AnchorEvent.builder() - .id(anchorEventCapture.captured.id) - .sep(SEP_6.sep.toString()) - .type(TRANSACTION_STATUS_CHANGED) - .transaction(expectedResponse) - .build() - - JSONAssert.assertEquals( - gson.toJson(expectedEvent), - gson.toJson(anchorEventCapture.captured), - JSONCompareMode.STRICT - ) - - assertTrue(sep6TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep6TxnCapture.captured.updatedAt <= endDate) - assertTrue(sep6TxnCapture.captured.transferReceivedAt >= startDate) - assertTrue(sep6TxnCapture.captured.transferReceivedAt <= endDate) - } - @CsvSource(value = ["deposit", "deposit-exchange"]) @ParameterizedTest fun test_handle_sep6_ok_withoutAmountAndExternalTxIdAndFundsReceivedAt(kind: String) { diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/NotifyRefundPendingHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/NotifyRefundPendingHandlerTest.kt index 7d8475ade7..df2b431f4e 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/NotifyRefundPendingHandlerTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/NotifyRefundPendingHandlerTest.kt @@ -1009,7 +1009,7 @@ class NotifyRefundPendingHandlerTest { @CsvSource(value = ["deposit", "deposit-exchange"]) @ParameterizedTest - fun test_handle_sep6_more_then_amount_in(kind: String) { + fun test_handle_sep6_more_then_amount_in() { val transferReceivedAt = Instant.now() val request = NotifyRefundPendingRequest.builder() diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/NotifyTrustSetHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/NotifyTrustSetHandlerTest.kt index 2fd201a384..8cec7053b8 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/NotifyTrustSetHandlerTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/NotifyTrustSetHandlerTest.kt @@ -28,12 +28,10 @@ import org.stellar.anchor.api.shared.Amount import org.stellar.anchor.api.shared.Customers import org.stellar.anchor.api.shared.StellarId import org.stellar.anchor.asset.AssetService -import org.stellar.anchor.custody.CustodyService import org.stellar.anchor.event.EventService import org.stellar.anchor.event.EventService.EventQueue.TRANSACTION import org.stellar.anchor.event.EventService.Session import org.stellar.anchor.metrics.MetricsService -import org.stellar.anchor.platform.config.PropertyCustodyConfig import org.stellar.anchor.platform.data.JdbcSep24Transaction import org.stellar.anchor.platform.data.JdbcSep6Transaction import org.stellar.anchor.platform.service.AnchorMetrics.PLATFORM_RPC_TRANSACTION @@ -61,10 +59,6 @@ class NotifyTrustSetHandlerTest { @MockK(relaxed = true) private lateinit var assetService: AssetService - @MockK(relaxed = true) private lateinit var custodyConfig: PropertyCustodyConfig - - @MockK(relaxed = true) private lateinit var custodyService: CustodyService - @MockK(relaxed = true) private lateinit var eventService: EventService @MockK(relaxed = true) private lateinit var metricsService: MetricsService @@ -88,8 +82,6 @@ class NotifyTrustSetHandlerTest { assetService, eventService, metricsService, - custodyConfig, - custodyService ) } @@ -186,177 +178,9 @@ class NotifyTrustSetHandlerTest { verify(exactly = 0) { sepTransactionCounter.increment() } } - @Test - fun test_handle_sep24_ok_custodyIntegrationDisabled() { - val request = NotifyTrustSetRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_TRUST.toString() - txn24.kind = DEPOSIT.kind - txn24.userActionRequiredBy = Instant.now() - val sep24TxnCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep24Txn = JdbcSep24Transaction() - expectedSep24Txn.kind = DEPOSIT.kind - expectedSep24Txn.status = PENDING_ANCHOR.toString() - expectedSep24Txn.updatedAt = sep24TxnCapture.captured.updatedAt - - JSONAssert.assertEquals( - gson.toJson(expectedSep24Txn), - gson.toJson(sep24TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.sep = SEP_24 - expectedResponse.kind = DEPOSIT - expectedResponse.status = PENDING_ANCHOR - expectedResponse.amountExpected = Amount(null, "") - expectedResponse.updatedAt = sep24TxnCapture.captured.updatedAt - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - assertTrue(sep24TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep24TxnCapture.captured.updatedAt <= endDate) - } - - @Test - fun test_handle_sep24_ok_custodyIntegrationEnabled_success() { - val request = NotifyTrustSetRequest.builder().transactionId(TX_ID).success(true).build() - val txn24 = JdbcSep24Transaction() - txn24.id = TX_ID - txn24.status = PENDING_TRUST.toString() - txn24.kind = DEPOSIT.kind - val sep24TxnCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 1) { custodyService.createTransactionPayment(TX_ID, null) } - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep24Txn = JdbcSep24Transaction() - expectedSep24Txn.id = TX_ID - expectedSep24Txn.kind = DEPOSIT.kind - expectedSep24Txn.status = PENDING_STELLAR.toString() - expectedSep24Txn.updatedAt = sep24TxnCapture.captured.updatedAt - - JSONAssert.assertEquals( - gson.toJson(expectedSep24Txn), - gson.toJson(sep24TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.id = TX_ID - expectedResponse.sep = SEP_24 - expectedResponse.kind = DEPOSIT - expectedResponse.status = PENDING_STELLAR - expectedResponse.amountExpected = Amount(null, "") - expectedResponse.updatedAt = sep24TxnCapture.captured.updatedAt - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - assertTrue(expectedSep24Txn.updatedAt >= startDate) - assertTrue(expectedSep24Txn.updatedAt <= endDate) - } - - @Test - fun test_handle_sep24_ok_custodyIntegrationEnabled_fail() { - val request = NotifyTrustSetRequest.builder().transactionId(TX_ID).success(false).build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_TRUST.toString() - txn24.kind = DEPOSIT.kind - val sep24TxnCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { custodyService.createTransactionPayment(any(), any()) } - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep24Txn = JdbcSep24Transaction() - expectedSep24Txn.kind = DEPOSIT.kind - expectedSep24Txn.status = PENDING_ANCHOR.toString() - expectedSep24Txn.updatedAt = sep24TxnCapture.captured.updatedAt - - JSONAssert.assertEquals( - gson.toJson(expectedSep24Txn), - gson.toJson(sep24TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.sep = SEP_24 - expectedResponse.kind = DEPOSIT - expectedResponse.status = PENDING_ANCHOR - expectedResponse.amountExpected = Amount(null, "") - expectedResponse.updatedAt = sep24TxnCapture.captured.updatedAt - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - assertTrue(expectedSep24Txn.updatedAt >= startDate) - assertTrue(expectedSep24Txn.updatedAt <= endDate) - } - @Test fun test_handle_sep24_ok() { - val request = NotifyTrustSetRequest.builder().transactionId(TX_ID).build() + val request = NotifyTrustSetRequest.builder().transactionId(TX_ID).success(true).build() val txn24 = JdbcSep24Transaction() txn24.status = PENDING_TRUST.toString() txn24.kind = DEPOSIT.kind @@ -367,7 +191,6 @@ class NotifyTrustSetHandlerTest { every { txn24Store.findByTransactionId(TX_ID) } returns txn24 every { txn31Store.findByTransactionId(any()) } returns null every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false every { eventSession.publish(capture(anchorEventCapture)) } just Runs every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns sepTransactionCounter @@ -472,180 +295,10 @@ class NotifyTrustSetHandlerTest { verify(exactly = 0) { sepTransactionCounter.increment() } } - @CsvSource(value = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_handle_sep6_ok_custodyIntegrationDisabled(kind: String) { - val request = NotifyTrustSetRequest.builder().transactionId(TX_ID).build() - val txn6 = JdbcSep6Transaction() - txn6.status = PENDING_TRUST.toString() - txn6.kind = kind - val sep6TxnCapture = slot() - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep6Txn = JdbcSep6Transaction() - expectedSep6Txn.kind = kind - expectedSep6Txn.status = PENDING_ANCHOR.toString() - expectedSep6Txn.updatedAt = sep6TxnCapture.captured.updatedAt - - JSONAssert.assertEquals( - gson.toJson(expectedSep6Txn), - gson.toJson(sep6TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.sep = SEP_6 - expectedResponse.kind = PlatformTransactionData.Kind.from(kind) - expectedResponse.status = PENDING_ANCHOR - expectedResponse.amountExpected = Amount(null, "") - expectedResponse.updatedAt = sep6TxnCapture.captured.updatedAt - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - assertTrue(sep6TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep6TxnCapture.captured.updatedAt <= endDate) - } - - @CsvSource(value = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_handle_sep6_ok_custodyIntegrationEnabled_success(kind: String) { - val request = NotifyTrustSetRequest.builder().transactionId(TX_ID).success(true).build() - val txn6 = JdbcSep6Transaction() - txn6.id = TX_ID - txn6.status = PENDING_TRUST.toString() - txn6.kind = kind - val sep6TxnCapture = slot() - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 1) { custodyService.createTransactionPayment(TX_ID, null) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep6Txn = JdbcSep6Transaction() - expectedSep6Txn.id = TX_ID - expectedSep6Txn.kind = kind - expectedSep6Txn.status = PENDING_STELLAR.toString() - expectedSep6Txn.updatedAt = sep6TxnCapture.captured.updatedAt - - JSONAssert.assertEquals( - gson.toJson(expectedSep6Txn), - gson.toJson(sep6TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.id = TX_ID - expectedResponse.sep = SEP_6 - expectedResponse.kind = PlatformTransactionData.Kind.from(kind) - expectedResponse.status = PENDING_STELLAR - expectedResponse.amountExpected = Amount(null, "") - expectedResponse.updatedAt = sep6TxnCapture.captured.updatedAt - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - assertTrue(expectedSep6Txn.updatedAt >= startDate) - assertTrue(expectedSep6Txn.updatedAt <= endDate) - } - - @CsvSource(value = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_handle_sep6_ok_custodyIntegrationEnabled_fail(kind: String) { - val request = NotifyTrustSetRequest.builder().transactionId(TX_ID).success(false).build() - val txn6 = JdbcSep6Transaction() - txn6.status = PENDING_TRUST.toString() - txn6.kind = kind - val sep6TxnCapture = slot() - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { custodyService.createTransactionPayment(any(), any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep6Txn = JdbcSep6Transaction() - expectedSep6Txn.kind = kind - expectedSep6Txn.status = PENDING_ANCHOR.toString() - expectedSep6Txn.updatedAt = sep6TxnCapture.captured.updatedAt - - JSONAssert.assertEquals( - gson.toJson(expectedSep6Txn), - gson.toJson(sep6TxnCapture.captured), - JSONCompareMode.STRICT - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.sep = SEP_6 - expectedResponse.kind = PlatformTransactionData.Kind.from(kind) - expectedResponse.status = PENDING_ANCHOR - expectedResponse.amountExpected = Amount(null, "") - expectedResponse.updatedAt = sep6TxnCapture.captured.updatedAt - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT - ) - - assertTrue(expectedSep6Txn.updatedAt >= startDate) - assertTrue(expectedSep6Txn.updatedAt <= endDate) - } - @CsvSource(value = ["deposit", "deposit-exchange"]) @ParameterizedTest fun test_handle_sep6_ok(kind: String) { - val request = NotifyTrustSetRequest.builder().transactionId(TX_ID).build() + val request = NotifyTrustSetRequest.builder().transactionId(TX_ID).success(true).build() val txn6 = JdbcSep6Transaction() txn6.status = PENDING_TRUST.toString() txn6.kind = kind @@ -656,7 +309,6 @@ class NotifyTrustSetHandlerTest { every { txn24Store.findByTransactionId(any()) } returns null every { txn31Store.findByTransactionId(any()) } returns null every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false every { eventSession.publish(capture(anchorEventCapture)) } just Runs every { metricsService.counter(PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns sepTransactionCounter diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/RequestOnchainFundsHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/RequestOnchainFundsHandlerTest.kt index 1a59c3eeaa..6593a18092 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/RequestOnchainFundsHandlerTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/RequestOnchainFundsHandlerTest.kt @@ -31,10 +31,6 @@ import org.stellar.anchor.api.sep.SepTransactionStatus.* import org.stellar.anchor.api.shared.* import org.stellar.anchor.asset.AssetService import org.stellar.anchor.asset.DefaultAssetService -import org.stellar.anchor.config.CustodyConfig -import org.stellar.anchor.config.CustodyConfig.CustodyType.FIREBLOCKS -import org.stellar.anchor.config.CustodyConfig.CustodyType.NONE -import org.stellar.anchor.custody.CustodyService import org.stellar.anchor.event.EventService import org.stellar.anchor.event.EventService.EventQueue.TRANSACTION import org.stellar.anchor.event.EventService.Session @@ -85,10 +81,6 @@ class RequestOnchainFundsHandlerTest { @MockK(relaxed = true) private lateinit var assetService: AssetService - @MockK(relaxed = true) private lateinit var custodyConfig: CustodyConfig - - @MockK(relaxed = true) private lateinit var custodyService: CustodyService - @MockK(relaxed = true) private lateinit var sep6DepositInfoGenerator: Sep6DepositInfoNoneGenerator @MockK(relaxed = true) @@ -122,8 +114,6 @@ class RequestOnchainFundsHandlerTest { txn31Store, requestValidator, assetService, - custodyService, - custodyConfig, sep6DepositInfoGenerator, sep24DepositInfoGenerator, sep31DepositInfoGenerator, @@ -314,37 +304,6 @@ class RequestOnchainFundsHandlerTest { verify(exactly = 0) { sepTransactionCounter.increment() } } - @Test - fun test_handle_notSupportedMemoType() { - val request = - RequestOnchainFundsRequest.builder() - .amountIn(AmountAssetRequest("1", STELLAR_USDC)) - .amountOut(AmountAssetRequest("1", FIAT_USD)) - .feeDetails(FeeDetails("1", STELLAR_USDC)) - .transactionId(TX_ID) - .memo(HASH_MEMO) - .memoType(HASH_MEMO_TYPE) - .destinationAccount(DESTINATION_ACCOUNT) - .build() - val txn24 = JdbcSep24Transaction() - txn24.status = INCOMPLETE.toString() - txn24.kind = WITHDRAWAL.kind - val sep24TxnCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.type } returns FIREBLOCKS - - val ex = assertThrows { handler.handle(request) } - assertEquals("Memo type[hash] is not supported for custody type[fireblocks]", ex.message) - - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - @Test fun test_handle_ok_missingMemo() { val request = @@ -640,8 +599,6 @@ class RequestOnchainFundsHandlerTest { every { txn24Store.findByTransactionId(TX_ID) } returns txn24 every { txn31Store.findByTransactionId(any()) } returns null every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false - every { custodyConfig.type } returns NONE every { eventSession.publish(capture(anchorEventCapture)) } just Runs every { metricsService.counter(AnchorMetrics.PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns sepTransactionCounter @@ -652,7 +609,6 @@ class RequestOnchainFundsHandlerTest { verify(exactly = 0) { txn6Store.save(any()) } verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sepTransactionCounter.increment() } val expectedSep24Txn = JdbcSep24Transaction() @@ -728,8 +684,6 @@ class RequestOnchainFundsHandlerTest { txn31Store, requestValidator, assetService, - custodyService, - custodyConfig, sep6DepositInfoGenerator, sep24DepositInfoGenerator, sep31DepositInfoGenerator, @@ -759,8 +713,6 @@ class RequestOnchainFundsHandlerTest { every { txn24Store.findByTransactionId(TX_ID) } returns txn24 every { txn31Store.findByTransactionId(any()) } returns null every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false - every { custodyConfig.type } returns NONE every { sep24DepositInfoGenerator.generate(ofType(Sep24Transaction::class)) } returns depositInfo every { eventSession.publish(capture(anchorEventCapture)) } just Runs @@ -773,7 +725,6 @@ class RequestOnchainFundsHandlerTest { verify(exactly = 0) { txn6Store.save(any()) } verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sepTransactionCounter.increment() } val expectedSep24Txn = JdbcSep24Transaction() @@ -839,116 +790,6 @@ class RequestOnchainFundsHandlerTest { assertTrue(sep24TxnCapture.captured.updatedAt <= endDate) } - @Test - fun test_handle_sep24_ok_withExpectedAmount_custodyIntegrationEnabled() { - val request = - RequestOnchainFundsRequest.builder() - .transactionId(TX_ID) - .amountIn(AmountAssetRequest("1", STELLAR_USDC)) - .amountOut(AmountAssetRequest("0.9", FIAT_USD)) - .feeDetails(FeeDetails("0.1", STELLAR_USDC)) - .amountExpected(AmountRequest("1")) - .memo(TEXT_MEMO) - .memoType(TEXT_MEMO_TYPE) - .destinationAccount(DESTINATION_ACCOUNT) - .build() - val txn24 = JdbcSep24Transaction() - txn24.status = INCOMPLETE.toString() - txn24.kind = WITHDRAWAL.kind - txn24.requestAssetCode = STELLAR_USDC_CODE - txn24.requestAssetIssuer = STELLAR_USDC_ISSUER - val sep24TxnCapture = slot() - val sep24CustodyTxnCapture = slot() - val anchorEventCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { custodyConfig.type } returns NONE - every { custodyService.createTransaction(capture(sep24CustodyTxnCapture)) } just Runs - every { eventSession.publish(capture(anchorEventCapture)) } just Runs - every { metricsService.counter(AnchorMetrics.PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep24Txn = JdbcSep24Transaction() - expectedSep24Txn.kind = WITHDRAWAL.kind - expectedSep24Txn.status = PENDING_USR_TRANSFER_START.toString() - expectedSep24Txn.updatedAt = sep24TxnCapture.captured.updatedAt - expectedSep24Txn.requestAssetCode = STELLAR_USDC_CODE - expectedSep24Txn.requestAssetIssuer = STELLAR_USDC_ISSUER - expectedSep24Txn.amountIn = "1" - expectedSep24Txn.amountInAsset = STELLAR_USDC - expectedSep24Txn.amountOut = "0.9" - expectedSep24Txn.amountOutAsset = FIAT_USD - expectedSep24Txn.amountFee = "0.1" - expectedSep24Txn.amountFeeAsset = STELLAR_USDC - expectedSep24Txn.amountExpected = "1" - expectedSep24Txn.memo = TEXT_MEMO - expectedSep24Txn.memoType = TEXT_MEMO_TYPE - expectedSep24Txn.toAccount = DESTINATION_ACCOUNT - expectedSep24Txn.withdrawAnchorAccount = DESTINATION_ACCOUNT - - JSONAssert.assertEquals( - gson.toJson(expectedSep24Txn), - gson.toJson(sep24TxnCapture.captured), - JSONCompareMode.STRICT, - ) - - JSONAssert.assertEquals( - gson.toJson(expectedSep24Txn), - gson.toJson(sep24CustodyTxnCapture.captured), - JSONCompareMode.STRICT, - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.sep = SEP_24 - expectedResponse.kind = WITHDRAWAL - expectedResponse.status = PENDING_USR_TRANSFER_START - expectedResponse.amountIn = Amount("1", STELLAR_USDC) - expectedResponse.amountOut = Amount("0.9", FIAT_USD) - expectedResponse.feeDetails = Amount("0.1", STELLAR_USDC).toRate() - expectedResponse.amountExpected = Amount("1", STELLAR_USDC) - expectedResponse.updatedAt = sep24TxnCapture.captured.updatedAt - expectedResponse.memo = TEXT_MEMO - expectedResponse.memoType = TEXT_MEMO_TYPE - expectedResponse.destinationAccount = DESTINATION_ACCOUNT - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT, - ) - - val expectedEvent = - AnchorEvent.builder() - .id(anchorEventCapture.captured.id) - .sep(SEP_24.sep.toString()) - .type(TRANSACTION_STATUS_CHANGED) - .transaction(expectedResponse) - .build() - - JSONAssert.assertEquals( - gson.toJson(expectedEvent), - gson.toJson(anchorEventCapture.captured), - JSONCompareMode.STRICT, - ) - - assertTrue(sep24TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep24TxnCapture.captured.updatedAt <= endDate) - } - @Test fun test_handle_sep24_ok_withUserActionRequiredBy() { val actionRequiredBy = Instant.now().plusSeconds(100) @@ -975,8 +816,6 @@ class RequestOnchainFundsHandlerTest { every { txn24Store.findByTransactionId(TX_ID) } returns txn24 every { txn31Store.findByTransactionId(any()) } returns null every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false - every { custodyConfig.type } returns NONE every { eventSession.publish(capture(anchorEventCapture)) } just Runs every { metricsService.counter(AnchorMetrics.PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns sepTransactionCounter @@ -987,7 +826,6 @@ class RequestOnchainFundsHandlerTest { verify(exactly = 0) { txn6Store.save(any()) } verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sepTransactionCounter.increment() } val expectedSep24Txn = JdbcSep24Transaction() @@ -1083,8 +921,6 @@ class RequestOnchainFundsHandlerTest { every { txn24Store.findByTransactionId(TX_ID) } returns txn24 every { txn31Store.findByTransactionId(any()) } returns null every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false - every { custodyConfig.type } returns NONE every { eventSession.publish(capture(anchorEventCapture)) } just Runs every { metricsService.counter(AnchorMetrics.PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns sepTransactionCounter @@ -1095,7 +931,6 @@ class RequestOnchainFundsHandlerTest { verify(exactly = 0) { txn6Store.save(any()) } verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sepTransactionCounter.increment() } val expectedSep24Txn = JdbcSep24Transaction() @@ -1171,8 +1006,6 @@ class RequestOnchainFundsHandlerTest { txn31Store, requestValidator, assetService, - custodyService, - custodyConfig, sep6DepositInfoGenerator, sep24DepositInfoGenerator, sep31DepositInfoGenerator, @@ -1202,7 +1035,9 @@ class RequestOnchainFundsHandlerTest { val ex = assertThrows { handler.handle(request) } assertEquals( - """Anchor is not configured to accept memo, memo_type and destination_account. Please set configuration deposit_info_generator_type to 'none' if you want to enable this feature""", + "Anchor is not configured to accept memo, memo_type and destination_account. " + + "Please set configuration deposit_info_generator_type to 'none' " + + "if you want to enable this feature", ex.message, ) @@ -1344,117 +1179,6 @@ class RequestOnchainFundsHandlerTest { verify(exactly = 0) { sepTransactionCounter.increment() } } - @CsvSource(value = ["withdrawal", "withdrawal-exchange"]) - @ParameterizedTest - fun test_handle_ok_sep6_withExpectedAmount(kind: String) { - val request = - RequestOnchainFundsRequest.builder() - .transactionId(TX_ID) - .amountIn(AmountAssetRequest("1", STELLAR_USDC)) - .amountOut(AmountAssetRequest("0.9", FIAT_USD)) - .feeDetails(Amount("0.1", STELLAR_USDC).toRate()) - .amountExpected(AmountRequest("1")) - .memo(HASH_MEMO) - .memoType(HASH_MEMO_TYPE) - .destinationAccount(DESTINATION_ACCOUNT) - .build() - val txn6 = JdbcSep6Transaction() - txn6.status = INCOMPLETE.toString() - txn6.kind = kind - txn6.requestAssetCode = STELLAR_USDC_CODE - txn6.requestAssetIssuer = STELLAR_USDC_ISSUER - val sep6TxnCapture = slot() - val sep6CustodyTxnCapture = slot() - val anchorEventCapture = slot() - - every { txn6Store.findByTransactionId(TX_ID) } returns txn6 - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(any()) } returns null - every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { custodyConfig.type } returns NONE - every { custodyService.createTransaction(capture(sep6CustodyTxnCapture)) } just Runs - every { eventSession.publish(capture(anchorEventCapture)) } just Runs - every { metricsService.counter(AnchorMetrics.PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep31Transaction::class)) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep6Txn = JdbcSep6Transaction() - expectedSep6Txn.kind = kind - expectedSep6Txn.status = PENDING_USR_TRANSFER_START.toString() - expectedSep6Txn.updatedAt = sep6TxnCapture.captured.updatedAt - expectedSep6Txn.requestAssetCode = STELLAR_USDC_CODE - expectedSep6Txn.requestAssetIssuer = STELLAR_USDC_ISSUER - expectedSep6Txn.amountIn = "1" - expectedSep6Txn.amountInAsset = STELLAR_USDC - expectedSep6Txn.amountOut = "0.9" - expectedSep6Txn.amountOutAsset = FIAT_USD - expectedSep6Txn.amountFee = "0.1" - expectedSep6Txn.amountFeeAsset = STELLAR_USDC - expectedSep6Txn.amountExpected = "1" - expectedSep6Txn.memo = HASH_MEMO - expectedSep6Txn.memoType = HASH_MEMO_TYPE - expectedSep6Txn.withdrawAnchorAccount = DESTINATION_ACCOUNT - - JSONAssert.assertEquals( - gson.toJson(expectedSep6Txn), - gson.toJson(sep6TxnCapture.captured), - JSONCompareMode.STRICT, - ) - - JSONAssert.assertEquals( - gson.toJson(expectedSep6Txn), - gson.toJson(sep6CustodyTxnCapture.captured), - JSONCompareMode.STRICT, - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.sep = SEP_6 - expectedResponse.kind = PlatformTransactionData.Kind.from(kind) - expectedResponse.status = PENDING_USR_TRANSFER_START - expectedResponse.amountIn = Amount("1", STELLAR_USDC) - expectedResponse.amountOut = Amount("0.9", FIAT_USD) - expectedResponse.feeDetails = Amount("0.1", STELLAR_USDC).toRate() - expectedResponse.amountExpected = Amount("1", STELLAR_USDC) - expectedResponse.updatedAt = sep6TxnCapture.captured.updatedAt - expectedResponse.memo = HASH_MEMO - expectedResponse.memoType = HASH_MEMO_TYPE - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - expectedResponse.creator = StellarId(null, null, null) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT, - ) - - val expectedEvent = - AnchorEvent.builder() - .id(anchorEventCapture.captured.id) - .sep(SEP_6.sep.toString()) - .type(TRANSACTION_STATUS_CHANGED) - .transaction(expectedResponse) - .build() - - JSONAssert.assertEquals( - gson.toJson(expectedEvent), - gson.toJson(anchorEventCapture.captured), - JSONCompareMode.STRICT, - ) - - assertTrue(sep6TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep6TxnCapture.captured.updatedAt <= endDate) - } - @CsvSource(value = ["withdrawal", "withdrawal-exchange"]) @ParameterizedTest fun test_handle_sep6_ok_autogeneratedMemo(kind: String) { @@ -1466,8 +1190,6 @@ class RequestOnchainFundsHandlerTest { txn31Store, requestValidator, assetService, - custodyService, - custodyConfig, sep6DepositInfoGenerator, sep24DepositInfoGenerator, sep31DepositInfoGenerator, @@ -1497,8 +1219,6 @@ class RequestOnchainFundsHandlerTest { every { txn24Store.findByTransactionId(any()) } returns null every { txn31Store.findByTransactionId(any()) } returns null every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false - every { custodyConfig.type } returns NONE every { sep6DepositInfoGenerator.generate(ofType(Sep6Transaction::class)) } returns depositInfo every { eventSession.publish(capture(anchorEventCapture)) } just Runs every { metricsService.counter(AnchorMetrics.PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns @@ -1510,7 +1230,6 @@ class RequestOnchainFundsHandlerTest { verify(exactly = 0) { txn24Store.save(any()) } verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep6Transaction::class)) } verify(exactly = 1) { sepTransactionCounter.increment() } val expectedSep6Txn = JdbcSep6Transaction() @@ -1711,7 +1430,6 @@ class RequestOnchainFundsHandlerTest { verify(exactly = 0) { txn24Store.save(any()) } verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sepTransactionCounter.increment() } val expectedSep6Txn = JdbcSep6Transaction() @@ -1786,8 +1504,6 @@ class RequestOnchainFundsHandlerTest { txn31Store, requestValidator, assetService, - custodyService, - custodyConfig, sep6DepositInfoGenerator, sep24DepositInfoGenerator, sep31DepositInfoGenerator, @@ -1817,10 +1533,9 @@ class RequestOnchainFundsHandlerTest { val ex = assertThrows { handler.handle(request) } assertEquals( - """ - Anchor is not configured to accept memo, memo_type and destination_account. Please set configuration deposit_info_generator_type to 'none' if you want to enable this feature - """ - .trimIndent(), + "Anchor is not configured to accept memo, memo_type and destination_account. " + + "Please set configuration deposit_info_generator_type to 'none' " + + "if you want to enable this feature", ex.message, ) @@ -1875,112 +1590,6 @@ class RequestOnchainFundsHandlerTest { verify(exactly = 0) { sepTransactionCounter.increment() } } - @Test - fun test_handle_ok_sep31_withExpectedAmount() { - val request = - RequestOnchainFundsRequest.builder() - .transactionId(TX_ID) - .amountIn(AmountAssetRequest("1", STELLAR_USDC)) - .amountOut(AmountAssetRequest("0.9", FIAT_USD)) - .feeDetails(Amount("0.1", STELLAR_USDC).toRate()) - .amountExpected(AmountRequest("1")) - .memo(HASH_MEMO) - .memoType(HASH_MEMO_TYPE) - .destinationAccount(DESTINATION_ACCOUNT) - .build() - val txn31 = JdbcSep31Transaction() - txn31.status = PENDING_RECEIVER.toString() - val sep31TxnCapture = slot() - val sep31CustodyTxnCapture = slot() - val anchorEventCapture = slot() - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(any()) } returns null - every { txn31Store.findByTransactionId(TX_ID) } returns txn31 - every { txn31Store.save(capture(sep31TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - every { custodyConfig.type } returns NONE - every { custodyService.createTransaction(capture(sep31CustodyTxnCapture)) } just Runs - every { eventSession.publish(capture(anchorEventCapture)) } just Runs - every { metricsService.counter(AnchorMetrics.PLATFORM_RPC_TRANSACTION, "SEP", "sep31") } returns - sepTransactionCounter - - val startDate = Instant.now() - val response = handler.handle(request) - val endDate = Instant.now() - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep6Transaction::class)) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } - verify(exactly = 1) { sepTransactionCounter.increment() } - - val expectedSep31Txn = JdbcSep31Transaction() - expectedSep31Txn.status = PENDING_SENDER.toString() - expectedSep31Txn.updatedAt = sep31TxnCapture.captured.updatedAt - expectedSep31Txn.amountIn = "1" - expectedSep31Txn.amountInAsset = STELLAR_USDC - expectedSep31Txn.amountOut = "0.9" - expectedSep31Txn.amountOutAsset = FIAT_USD - expectedSep31Txn.amountFee = "0.1" - expectedSep31Txn.amountFeeAsset = STELLAR_USDC - expectedSep31Txn.amountExpected = "1" - expectedSep31Txn.stellarMemo = HASH_MEMO - expectedSep31Txn.stellarMemoType = HASH_MEMO_TYPE - expectedSep31Txn.toAccount = DESTINATION_ACCOUNT - - JSONAssert.assertEquals( - gson.toJson(expectedSep31Txn), - gson.toJson(sep31TxnCapture.captured), - JSONCompareMode.STRICT, - ) - - JSONAssert.assertEquals( - gson.toJson(expectedSep31Txn), - gson.toJson(sep31CustodyTxnCapture.captured), - JSONCompareMode.STRICT, - ) - - val expectedResponse = GetTransactionResponse() - expectedResponse.sep = SEP_31 - expectedResponse.kind = PlatformTransactionData.Kind.RECEIVE - expectedResponse.status = PENDING_SENDER - expectedResponse.amountIn = Amount("1", STELLAR_USDC) - expectedResponse.amountOut = Amount("0.9", FIAT_USD) - expectedResponse.feeDetails = Amount("0.1", STELLAR_USDC).toRate() - expectedResponse.amountExpected = Amount("1", STELLAR_USDC) - expectedResponse.updatedAt = sep31TxnCapture.captured.updatedAt - expectedResponse.destinationAccount = DESTINATION_ACCOUNT - expectedResponse.memo = HASH_MEMO - expectedResponse.memoType = HASH_MEMO_TYPE - expectedResponse.refundMemo = HASH_MEMO - expectedResponse.refundMemoType = HASH_MEMO_TYPE - expectedResponse.customers = Customers(StellarId(null, null, null), StellarId(null, null, null)) - - JSONAssert.assertEquals( - gson.toJson(expectedResponse), - gson.toJson(response), - JSONCompareMode.STRICT, - ) - - val expectedEvent = - AnchorEvent.builder() - .id(anchorEventCapture.captured.id) - .sep(SEP_31.sep.toString()) - .type(TRANSACTION_STATUS_CHANGED) - .transaction(expectedResponse) - .build() - - JSONAssert.assertEquals( - gson.toJson(expectedEvent), - gson.toJson(anchorEventCapture.captured), - JSONCompareMode.STRICT, - ) - - assertTrue(sep31TxnCapture.captured.updatedAt >= startDate) - assertTrue(sep31TxnCapture.captured.updatedAt <= endDate) - } - @Test fun test_handle_sep31_ok_autogeneratedMemo() { val sep31DepositInfoGenerator: Sep31DepositInfoSelfGenerator = mockk() @@ -1991,8 +1600,6 @@ class RequestOnchainFundsHandlerTest { txn31Store, requestValidator, assetService, - custodyService, - custodyConfig, sep6DepositInfoGenerator, sep24DepositInfoGenerator, sep31DepositInfoGenerator, @@ -2019,8 +1626,6 @@ class RequestOnchainFundsHandlerTest { every { txn24Store.findByTransactionId(any()) } returns null every { txn31Store.findByTransactionId(TX_ID) } returns txn31 every { txn31Store.save(capture(sep31TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false - every { custodyConfig.type } returns NONE every { sep31DepositInfoGenerator.generate(ofType(Sep31Transaction::class)) } returns depositInfo every { eventSession.publish(capture(anchorEventCapture)) } just Runs @@ -2033,8 +1638,6 @@ class RequestOnchainFundsHandlerTest { verify(exactly = 0) { txn6Store.save(any()) } verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep6Transaction::class)) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sepTransactionCounter.increment() } val expectedSep31Txn = JdbcSep31Transaction() @@ -2225,8 +1828,6 @@ class RequestOnchainFundsHandlerTest { verify(exactly = 0) { txn6Store.save(any()) } verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep6Transaction::class)) } - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sepTransactionCounter.increment() } val expectedSep31Txn = JdbcSep31Transaction() @@ -2299,8 +1900,6 @@ class RequestOnchainFundsHandlerTest { txn31Store, requestValidator, assetService, - custodyService, - custodyConfig, sep6DepositInfoGenerator, sep24DepositInfoGenerator, sep31DepositInfoGenerator, @@ -2329,10 +1928,9 @@ class RequestOnchainFundsHandlerTest { val ex = assertThrows { handler.handle(request) } assertEquals( - """ - Anchor is not configured to accept memo, memo_type and destination_account. Please set configuration deposit_info_generator_type to 'none' if you want to enable this feature - """ - .trimIndent(), + "Anchor is not configured to accept memo, memo_type and destination_account. " + + "Please set configuration deposit_info_generator_type to 'none' " + + "if you want to enable this feature", ex.message, ) diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/RequestTrustlineHandlerTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/RequestTrustlineHandlerTest.kt index db607ef718..0b24ac7d03 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/RequestTrustlineHandlerTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/rpc/RequestTrustlineHandlerTest.kt @@ -28,7 +28,6 @@ import org.stellar.anchor.api.shared.Amount import org.stellar.anchor.api.shared.Customers import org.stellar.anchor.api.shared.StellarId import org.stellar.anchor.asset.AssetService -import org.stellar.anchor.config.CustodyConfig import org.stellar.anchor.event.EventService import org.stellar.anchor.event.EventService.EventQueue.TRANSACTION import org.stellar.anchor.event.EventService.Session @@ -60,8 +59,6 @@ class RequestTrustlineHandlerTest { @MockK(relaxed = true) private lateinit var assetService: AssetService - @MockK(relaxed = true) private lateinit var custodyConfig: CustodyConfig - @MockK(relaxed = true) private lateinit var eventService: EventService @MockK(relaxed = true) private lateinit var metricsService: MetricsService @@ -83,7 +80,6 @@ class RequestTrustlineHandlerTest { txn31Store, requestValidator, assetService, - custodyConfig, eventService, metricsService ) @@ -115,28 +111,6 @@ class RequestTrustlineHandlerTest { verify(exactly = 0) { sepTransactionCounter.increment() } } - @Test - fun test_handle_custodyIntegrationEnabled() { - val request = RequestTrustRequest.builder().transactionId(TX_ID).build() - val txn24 = JdbcSep24Transaction() - txn24.status = PENDING_ANCHOR.toString() - txn24.kind = DEPOSIT.kind - txn24.transferReceivedAt = Instant.now() - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - every { txn6Store.findByTransactionId(any()) } returns null - every { txn24Store.findByTransactionId(TX_ID) } returns txn24 - every { txn31Store.findByTransactionId(any()) } returns null - - val ex = assertThrows { handler.handle(request) } - assertEquals("RPC method[request_trust] requires disabled custody integration", ex.message) - - verify(exactly = 0) { txn6Store.save(any()) } - verify(exactly = 0) { txn24Store.save(any()) } - verify(exactly = 0) { txn31Store.save(any()) } - verify(exactly = 0) { sepTransactionCounter.increment() } - } - @Test fun test_handle_invalidRequest() { val request = RequestTrustRequest.builder().transactionId(TX_ID).build() @@ -166,7 +140,6 @@ class RequestTrustlineHandlerTest { val txn24 = JdbcSep24Transaction() txn24.status = PENDING_ANCHOR.toString() txn24.kind = DEPOSIT.kind - every { custodyConfig.isCustodyIntegrationEnabled } returns false every { txn6Store.findByTransactionId(any()) } returns null every { txn24Store.findByTransactionId(TX_ID) } returns txn24 @@ -228,7 +201,6 @@ class RequestTrustlineHandlerTest { every { txn24Store.findByTransactionId(TX_ID) } returns txn24 every { txn31Store.findByTransactionId(any()) } returns null every { txn24Store.save(capture(sep24TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false every { eventSession.publish(capture(anchorEventCapture)) } just Runs every { metricsService.counter(AnchorMetrics.PLATFORM_RPC_TRANSACTION, "SEP", "sep24") } returns sepTransactionCounter @@ -295,7 +267,6 @@ class RequestTrustlineHandlerTest { val txn6 = JdbcSep6Transaction() txn6.status = PENDING_ANCHOR.toString() txn6.kind = kind - every { custodyConfig.isCustodyIntegrationEnabled } returns false every { txn6Store.findByTransactionId(TX_ID) } returns txn6 every { txn24Store.findByTransactionId(any()) } returns null @@ -379,7 +350,6 @@ class RequestTrustlineHandlerTest { every { txn24Store.findByTransactionId(any()) } returns null every { txn31Store.findByTransactionId(any()) } returns null every { txn6Store.save(capture(sep6TxnCapture)) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns false every { eventSession.publish(capture(anchorEventCapture)) } just Runs every { metricsService.counter(AnchorMetrics.PLATFORM_RPC_TRANSACTION, "SEP", "sep6") } returns sepTransactionCounter diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/service/CustodyServiceTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/service/CustodyServiceTest.kt deleted file mode 100644 index 2ff9ef145c..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/service/CustodyServiceTest.kt +++ /dev/null @@ -1,489 +0,0 @@ -package org.stellar.anchor.platform.service - -import io.mockk.* -import io.mockk.impl.annotations.MockK -import java.util.* -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.CsvSource -import org.skyscreamer.jsonassert.JSONAssert -import org.skyscreamer.jsonassert.JSONCompareMode -import org.stellar.anchor.api.custody.CreateCustodyTransactionRequest -import org.stellar.anchor.api.custody.CreateTransactionPaymentResponse -import org.stellar.anchor.api.exception.CustodyException -import org.stellar.anchor.api.exception.InvalidConfigException -import org.stellar.anchor.api.exception.custody.CustodyBadRequestException -import org.stellar.anchor.api.exception.custody.CustodyNotFoundException -import org.stellar.anchor.api.exception.custody.CustodyServiceUnavailableException -import org.stellar.anchor.api.exception.custody.CustodyTooManyRequestsException -import org.stellar.anchor.custody.CustodyService -import org.stellar.anchor.platform.apiclient.CustodyApiClient -import org.stellar.anchor.platform.data.JdbcSep24Transaction -import org.stellar.anchor.platform.data.JdbcSep31Transaction -import org.stellar.anchor.platform.data.JdbcSep6Transaction -import org.stellar.anchor.util.GsonUtils - -class CustodyServiceTest { - - companion object { - private val gson = GsonUtils.getInstance() - private val TXN_ID = "1" - private val REQUEST_BODY = "{}" - } - - @MockK(relaxed = true) private lateinit var custodyApiClient: CustodyApiClient - private lateinit var custodyService: CustodyService - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - custodyService = CustodyServiceImpl(Optional.of(custodyApiClient)) - } - - @CsvSource(value = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_createTransaction_sep6Deposit(kind: String) { - val txn = - gson.fromJson(sep6DepositEntity, JdbcSep6Transaction::class.java).apply { this.kind = kind } - val requestCapture = slot() - - every { custodyApiClient.createTransaction(capture(requestCapture)) } just Runs - - custodyService.createTransaction(txn) - - JSONAssert.assertEquals( - sep6DepositRequest.replace("testKind", kind), - gson.toJson(requestCapture.captured), - JSONCompareMode.STRICT, - ) - } - - @CsvSource(value = ["withdrawal", "withdrawal-exchange"]) - @ParameterizedTest - fun test_createTransaction_sep6Withdrawal(kind: String) { - val txn = - gson.fromJson(sep6WithdrawalEntity, JdbcSep6Transaction::class.java).apply { - this.kind = kind - } - val requestCapture = slot() - - every { custodyApiClient.createTransaction(capture(requestCapture)) } just Runs - - custodyService.createTransaction(txn) - - JSONAssert.assertEquals( - sep6WithdrawalRequest.replace("testKind", kind), - gson.toJson(requestCapture.captured), - JSONCompareMode.STRICT, - ) - } - - @Test - fun test_createTransaction_sep24Deposit() { - val txn = gson.fromJson(sep24DepositEntity, JdbcSep24Transaction::class.java) - - val requestCapture = slot() - - every { custodyApiClient.createTransaction(capture(requestCapture)) } just Runs - - custodyService.createTransaction(txn) - - JSONAssert.assertEquals( - sep24DepositRequest, - gson.toJson(requestCapture.captured), - JSONCompareMode.STRICT, - ) - } - - @Test - fun test_createTransaction_sep24Withdrawal() { - val txn = gson.fromJson(sep24WithdrawalEntity, JdbcSep24Transaction::class.java) - val requestCapture = slot() - - every { custodyApiClient.createTransaction(capture(requestCapture)) } just Runs - - custodyService.createTransaction(txn) - - JSONAssert.assertEquals( - sep24WithdrawalRequest, - gson.toJson(requestCapture.captured), - JSONCompareMode.STRICT, - ) - } - - @Test - fun test_createTransaction_sep31() { - val txn = gson.fromJson(sep31Entity, JdbcSep31Transaction::class.java) - val requestCapture = slot() - - every { custodyApiClient.createTransaction(capture(requestCapture)) } just Runs - - custodyService.createTransaction(txn) - - JSONAssert.assertEquals( - sep31Request, - gson.toJson(requestCapture.captured), - JSONCompareMode.STRICT, - ) - } - - @Test - fun test_createTransactionPayment_custody_integration_not_enabled() { - custodyService = CustodyServiceImpl(Optional.empty()) - - val ex = - assertThrows { - custodyService.createTransactionPayment(TXN_ID, REQUEST_BODY) - } - Assertions.assertEquals("Integration with custody service is not enabled", ex.message) - verify(exactly = 0) { custodyApiClient.createTransactionPayment(TXN_ID, REQUEST_BODY) } - } - - @Test - fun test_createTransactionPayment_custody_integration_enabled() { - every { custodyApiClient.createTransactionPayment(TXN_ID, REQUEST_BODY) } returns - CreateTransactionPaymentResponse(TXN_ID) - - custodyService.createTransactionPayment(TXN_ID, REQUEST_BODY) - - verify(exactly = 1) { custodyApiClient.createTransactionPayment(TXN_ID, REQUEST_BODY) } - } - - @Test - fun test_createTransactionPayment_custody_integration_disabled() { - custodyService = CustodyServiceImpl(Optional.empty()) - - val exception = - assertThrows { - custodyService.createTransactionPayment(TXN_ID, REQUEST_BODY) - } - Assertions.assertEquals("Integration with custody service is not enabled", exception.message) - - verify(exactly = 0) { custodyApiClient.createTransactionPayment(any(), any()) } - } - - @Test - fun test_createTransactionPayment_custody_server_unavailable() { - every { custodyApiClient.createTransactionPayment(TXN_ID, REQUEST_BODY) } throws - CustodyException("Custody service is unavailable", 503) - - val exception = - assertThrows { - custodyService.createTransactionPayment(TXN_ID, REQUEST_BODY) - } - Assertions.assertEquals("Custody service is unavailable", exception.message) - } - - @Test - fun test_createTransactionPayment_bad_request() { - every { custodyApiClient.createTransactionPayment(TXN_ID, REQUEST_BODY) } throws - CustodyException("Bad request", 400) - - val exception = - assertThrows { - custodyService.createTransactionPayment(TXN_ID, REQUEST_BODY) - } - Assertions.assertEquals("Bad request", exception.message) - } - - @Test - fun test_createTransactionPayment_too_many_requests() { - every { custodyApiClient.createTransactionPayment(TXN_ID, REQUEST_BODY) } throws - CustodyException("Too many requests", 429) - - val exception = - assertThrows { - custodyService.createTransactionPayment(TXN_ID, REQUEST_BODY) - } - Assertions.assertEquals("Too many requests", exception.message) - } - - @Test - fun test_createTransactionPayment_transaction_not_found() { - every { custodyApiClient.createTransactionPayment(TXN_ID, REQUEST_BODY) } throws - CustodyException("Transaction (id=1) is not found", 404) - - val exception = - assertThrows { - custodyService.createTransactionPayment(TXN_ID, REQUEST_BODY) - } - Assertions.assertEquals("Transaction (id=1) is not found", exception.message) - } - - @Test - fun test_createTransactionPayment_unexpected_status_code() { - every { custodyApiClient.createTransactionPayment(TXN_ID, REQUEST_BODY) } throws - CustodyException("Forbidden", 403) - - val exception = - assertThrows { - custodyService.createTransactionPayment(TXN_ID, REQUEST_BODY) - } - Assertions.assertEquals("Forbidden", exception.rawMessage) - } - - private val sep6DepositEntity = - """ - { - "id" : "testId", - "stellar_transaction_id": "testStellarTransactionId", - "external_transaction_id": "testExternalTransactionId", - "status": "pending_anchor", - "kind": "deposit", - "started_at": "2022-04-18T14:00:00.000Z", - "completed_at": "2022-04-18T14:00:00.000Z", - "updated_at": "2022-04-18T14:00:00.000Z", - "transfer_received_at": "2022-04-18T14:00:00.000Z", - "type": "SWIFT", - "requestAssetCode": "testRequestAssetCode", - "requestAssetIssuer": "testRequestAssetIssuer", - "amount_in": "testAmountIn", - "amount_in_asset": "testAmountInAsset", - "amount_out": "testAmountOut", - "amount_out_asset": "testAmountOutAsset", - "amount_fee": "testAmountFee", - "amount_fee_asset": "testAmountFeeAsset", - "amount_expected": "testAmountExpected", - "web_auth_account": "testWebAuthAccount", - "web_auth_account_memo": "testWebAuthAccountMemo", - "from_account": "testFromAccount", - "to_account": "testToAccount", - "memo": "testMemo", - "memo_type": "testMemoType", - "quote_id": "testQuoteId", - "message": "testMessage", - "refundMemo": "testRefundMemo", - "refundMemoType": "testRefundMemoType" - } - """ - .trimIndent() - - private val sep6DepositRequest = - """ - { - "id": "testId", - "memo": "testMemo", - "memoType": "testMemoType", - "protocol": "6", - "toAccount": "testToAccount", - "amount": "testAmountOut", - "asset": "testAmountOutAsset", - "kind": "testKind" - } - """ - .trimIndent() - - private val sep6WithdrawalEntity = - """ - { - "id": "testId", - "stellar_transaction_id": "testStellarTransactionId", - "external_transaction_id": "testExternalTransactionId", - "status": "pending_anchor", - "kind": "withdrawal", - "started_at": "2022-04-18T14:00:00.000Z", - "completed_at": "2022-04-18T14:00:00.000Z", - "updated_at": "2022-04-18T14:00:00.000Z", - "transfer_received_at": "2022-04-18T14:00:00.000Z", - "type": "bank_account", - "requestAssetCode": "testRequestAssetCode", - "requestAssetIssuer": "testRequestAssetIssuer", - "amount_in": "testAmountIn", - "amount_in_asset": "testAmountInAsset", - "amount_out": "testAmountOut", - "amount_out_asset": "testAmountOutAsset", - "amount_fee": "testAmountFee", - "amount_fee_asset": "testAmountFeeAsset", - "amount_expected": "testAmountExpected", - "web_auth_account": "testWebAuthAccount", - "web_auth_account_memo": "testWebAuthAccountMemo", - "withdraw_anchor_account": "testWithdrawAnchorAccount", - "from_account": "testFromAccount", - "to_account": "testToAccount", - "memo": "testMemo", - "memo_type": "testMemoType", - "quote_id": "testQuoteId", - "message": "testMessage", - "refundMemo": "testRefundMemo", - "refundMemoType": "testRefundMemoType" - } - """ - .trimIndent() - - private val sep6WithdrawalRequest = - """ - { - "id": "testId", - "memo": "testMemo", - "memoType": "testMemoType", - "protocol": "6", - "fromAccount": "testFromAccount", - "toAccount": "testWithdrawAnchorAccount", - "amount": "testAmountExpected", - "asset": "testAmountInAsset", - "kind": "testKind" - } - """ - .trimIndent() - - private val sep24DepositEntity = - """ -{ - "id" : "testId", - "status": "pending_anchor", - "updated_at": "2022-04-18T14:00:00.000Z", - "amount_in": "testAmountIn", - "amount_in_asset": "testAmountInAsset", - "amount_out": "testAmountOut", - "amount_out_asset": "testAmountOutAsset", - "amount_fee": "testAmountFee", - "amount_fee_asset": "testAmountFeeAsset", - "started_at": "2022-04-18T14:00:00.000Z", - "completed_at": "2022-04-18T14:00:00.000Z", - "transfer_received_at": "2022-04-18T14:00:00.000Z", - "stellar_transaction_id": "testStellarTransactionId", - "external_transaction_id": "testExternalTransactionId", - "kind": "deposit", - "status_eta": "1", - "kyc_verified": "true", - "more_info_url": "/testMoreInfoUrl", - "transaction_id": "testTxId", - "message": "testMessage", - "refunded": "true", - "withdraw_anchor_account": "testWithdrawAnchorAccount", - "memo": "testMemo", - "memo_type": "testMemoType", - "from_account": "testFromAccount", - "to_account": "testToAccount", - "request_asset_code": "testRequestAssetCode", - "request_asset_issuer": "testRequestAssetIssuer", - "web_auth_account": "testWebAuthAccount", - "web_auth_account_memo": "testWebAuthAccountMemo", - "client_domain": "testClientDomain", - "claimable_balance_supported": "true", - "amount_expected": "testAmountExpected", - "refund_memo": "testRefundMemo", - "refund_memo_type": "testRefundMemoType" -} -""" - - private val sep24DepositRequest = - """ - { - "id": "testId", - "memo": "testMemo", - "memoType": "testMemoType", - "protocol": "24", - "toAccount": "testToAccount", - "amount": "testAmountOut", - "asset": "testAmountOutAsset", - "kind": "deposit" -} -""" - - private val sep24WithdrawalEntity = - """ -{ - "id" : "testId", - "status": "pending_user_transfer_start", - "updated_at": "2022-04-18T14:00:00.000Z", - "amount_in": "testAmountIn", - "amount_in_asset": "testAmountInAsset", - "amount_out": "testAmountOut", - "amount_out_asset": "testAmountOutAsset", - "amount_fee": "testAmountFee", - "amount_fee_asset": "testAmountFeeAsset", - "started_at": "2022-04-18T14:00:00.000Z", - "completed_at": "2022-04-18T14:00:00.000Z", - "transfer_received_at": "2022-04-18T14:00:00.000Z", - "stellar_transaction_id": "testStellarTransactionId", - "external_transaction_id": "testExternalTransactionId", - "kind": "withdrawal", - "status_eta": "1", - "kyc_verified": "true", - "more_info_url": "/testMoreInfoUrl", - "transaction_id": "testTxId", - "message": "testMessage", - "refunded": "true", - "withdraw_anchor_account": "testWithdrawAnchorAccount", - "memo": "testMemo", - "memo_type": "testMemoType", - "from_account": "testFromAccount", - "to_account": "testToAccount", - "request_asset_code": "testRequestAssetCode", - "request_asset_issuer": "testRequestAssetIssuer", - "web_auth_account": "testWebAuthAccount", - "web_auth_account_memo": "testWebAuthAccountMemo", - "client_domain": "testClientDomain", - "claimable_balance_supported": "true", - "amount_expected": "testAmountExpected", - "refund_memo": "testRefundMemo", - "refund_memo_type": "testRefundMemoType" -} -""" - - private val sep24WithdrawalRequest = - """ -{ - "id": "testId", - "memo": "testMemo", - "memoType": "testMemoType", - "protocol": "24", - "fromAccount": "testFromAccount", - "toAccount": "testWithdrawAnchorAccount", - "amount": "testAmountExpected", - "asset": "testAmountInAsset", - "kind": "withdrawal" -} -""" - - private val sep31Entity = - """ - { - "id" : "testId", - "status": "pending_sender", - "updated_at": "2022-04-18T14:00:00.000Z", - "amount_in": "testAmountIn", - "amount_in_asset": "testAmountInAsset", - "amount_out": "testAmountOut", - "amount_out_asset": "testAmountOutAsset", - "amount_fee": "testAmountFee", - "amount_fee_asset": "testAmountFeeAsset", - "started_at": "2022-04-18T14:00:00.000Z", - "completed_at": "2022-04-18T14:00:00.000Z", - "transfer_received_at": "2022-04-18T14:00:00.000Z", - "stellar_transaction_id": "testStellarTransactionId", - "external_transaction_id": "testExternalTransactionId", - "kind": "receive", - "status_eta": "1", - "from_account": "testStellarAccountId", - "to_account": "testStellarAccountId", - "stellar_memo": "testStellarMemo", - "stellar_memo_type": "testStellarMemoType", - "quote_id": "testQuoteId", - "client_domain": "testClientDomain", - "sender_id": "testSenderId", - "receiver_id": "testReceiverId", - "required_info_message": "testRequiredInfoMessage", - "refunded": "true", - "amount_expected": "testAmountExpected" -} -""" - - private val sep31Request = - """ - { - "id": "testId", - "memo": "testStellarMemo", - "memoType": "testStellarMemoType", - "protocol": "31", - "toAccount": "testStellarAccountId", - "amount": "testAmountIn", - "asset": "testAmountInAsset", - "kind": "receive" -} -""" -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/service/DepositInfoGeneratorTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/service/DepositInfoGeneratorTest.kt index 767bf799d3..7553efff09 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/service/DepositInfoGeneratorTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/service/DepositInfoGeneratorTest.kt @@ -1,16 +1,8 @@ package org.stellar.anchor.platform.service -import io.mockk.every -import io.mockk.mockk import kotlin.test.assertEquals -import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.stellar.anchor.api.custody.GenerateDepositAddressResponse -import org.stellar.anchor.api.exception.CustodyException -import org.stellar.anchor.api.shared.SepDepositInfo -import org.stellar.anchor.platform.apiclient.CustodyApiClient import org.stellar.anchor.platform.data.JdbcSep24Transaction import org.stellar.anchor.platform.data.JdbcSep31Transaction @@ -38,39 +30,6 @@ class DepositInfoGeneratorTest { assertTrue(actualInfo.memoType == "id") } - @Test - fun test_sep24_custodyGenerator_success() { - val txn = JdbcSep24Transaction() - txn.amountInAsset = ASSET_ID - val custodyApiClient: CustodyApiClient = mockk() - val generator = Sep24DepositInfoCustodyGenerator(custodyApiClient) - - val depositAddress = GenerateDepositAddressResponse(ADDRESS, MEMO, MEMO_TYPE) - - every { custodyApiClient.generateDepositAddress(ASSET_ID) } returns depositAddress - - val actualInfo = generator.generate(txn) - - val expectedInfo = SepDepositInfo(ADDRESS, MEMO, MEMO_TYPE) - - assertEquals(expectedInfo, actualInfo) - } - - @Test - fun test_sep24_custodyGenerator_error() { - val txn = JdbcSep24Transaction() - txn.amountInAsset = ASSET_ID - val custodyApiClient: CustodyApiClient = mockk() - val generator = Sep24DepositInfoCustodyGenerator(custodyApiClient) - - every { custodyApiClient.generateDepositAddress(ASSET_ID) } throws - CustodyException("Custody exception") - - val exception = assertThrows { generator.generate(txn) } - - Assertions.assertEquals("Custody exception", exception.message) - } - @Test fun test_sep31_selfGenerator_success() { val txn = JdbcSep31Transaction() @@ -84,36 +43,4 @@ class DepositInfoGeneratorTest { assertTrue(actualInfo.memo.toLongOrNull() != null) assertTrue(actualInfo.memoType == "id") } - - @Test - fun test_sep31_selfCustody_success() { - val txn = JdbcSep31Transaction() - txn.amountInAsset = ASSET_ID - val custodyApiClient: CustodyApiClient = mockk() - val generator = Sep31DepositInfoCustodyGenerator(custodyApiClient) - val depositAddress = GenerateDepositAddressResponse(ADDRESS, MEMO, MEMO_TYPE) - - every { custodyApiClient.generateDepositAddress(ASSET_ID) } returns depositAddress - - val actualInfo = generator.generate(txn) - - val expectedInfo = SepDepositInfo(ADDRESS, MEMO, MEMO_TYPE) - - assertEquals(expectedInfo, actualInfo) - } - - @Test - fun test_sep31_custodyGenerator_error() { - val txn = JdbcSep31Transaction() - txn.amountInAsset = ASSET_ID - val custodyApiClient: CustodyApiClient = mockk() - val generator = Sep31DepositInfoCustodyGenerator(custodyApiClient) - - every { custodyApiClient.generateDepositAddress(ASSET_ID) } throws - CustodyException("Custody exception") - - val exception = assertThrows { generator.generate(txn) } - - Assertions.assertEquals("Custody exception", exception.message) - } } diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep24DepositInfoCustodyGeneratorTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep24DepositInfoCustodyGeneratorTest.kt deleted file mode 100644 index 108e1e7e59..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep24DepositInfoCustodyGeneratorTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -package org.stellar.anchor.platform.service - -import io.mockk.every -import io.mockk.mockk -import kotlin.test.assertEquals -import org.junit.jupiter.api.Test -import org.stellar.anchor.api.custody.GenerateDepositAddressResponse -import org.stellar.anchor.api.shared.SepDepositInfo -import org.stellar.anchor.platform.apiclient.CustodyApiClient -import org.stellar.anchor.platform.data.JdbcSep24Transaction - -class Sep24DepositInfoCustodyGeneratorTest { - - companion object { - private const val ADDRESS = "testAccount" - private const val MEMO = "MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc=" - private const val MEMO_TYPE = "hash" - private const val ASSET_ID = "USDC" - } - - @Test - fun test_sep24_custodyGenerator_success() { - val txn = JdbcSep24Transaction() - txn.amountInAsset = ASSET_ID - val custodyApiClient: CustodyApiClient = mockk() - val generator = Sep24DepositInfoCustodyGenerator(custodyApiClient) - - val depositAddress = GenerateDepositAddressResponse(ADDRESS, MEMO, MEMO_TYPE) - - every { custodyApiClient.generateDepositAddress(ASSET_ID) } returns depositAddress - - val actualInfo = generator.generate(txn) - - val expectedInfo = SepDepositInfo(ADDRESS, MEMO, MEMO_TYPE) - - assertEquals(expectedInfo, actualInfo) - } -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep24MoreInfoUrlConstructorTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep24MoreInfoUrlConstructorTest.kt index 25d611212c..40464261ff 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep24MoreInfoUrlConstructorTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep24MoreInfoUrlConstructorTest.kt @@ -16,7 +16,6 @@ import org.stellar.anchor.asset.AssetService import org.stellar.anchor.auth.JwtService import org.stellar.anchor.auth.MoreInfoUrlJwt.Sep24MoreInfoUrlJwt import org.stellar.anchor.client.DefaultClientService -import org.stellar.anchor.config.CustodySecretConfig import org.stellar.anchor.config.SecretConfig import org.stellar.anchor.platform.config.MoreInfoUrlConfig import org.stellar.anchor.platform.data.JdbcSep24Transaction @@ -32,7 +31,6 @@ class Sep24MoreInfoUrlConstructorTest { @MockK(relaxed = true) private lateinit var assetService: AssetService @MockK(relaxed = true) private lateinit var secretConfig: SecretConfig - @MockK(relaxed = true) private lateinit var custodySecretConfig: CustodySecretConfig private lateinit var jwtService: JwtService private lateinit var clientService: DefaultClientService @@ -42,7 +40,7 @@ class Sep24MoreInfoUrlConstructorTest { MockKAnnotations.init(this, relaxUnitFun = true) secretConfig.setupMock() clientService = DefaultClientService.fromYamlResourceFile("test_clients.yaml") - jwtService = JwtService(secretConfig, custodySecretConfig) + jwtService = JwtService(secretConfig) } @Test @@ -110,7 +108,7 @@ class Sep24MoreInfoUrlConstructorTest { assertEquals("txn_123", jwt.jti as String) Assertions.assertTrue(Instant.ofEpochSecond(jwt.exp).isAfter(Instant.now())) - val data = jwt.claims["data"] as Map + val data = jwt.claims["data"] as Map<*, *> assertEquals(LANG, data["lang"] as String) } } diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep31DepositInfoCustodyGeneratorTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep31DepositInfoCustodyGeneratorTest.kt deleted file mode 100644 index 39033d38a0..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep31DepositInfoCustodyGeneratorTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -package org.stellar.anchor.platform.service - -import io.mockk.every -import io.mockk.mockk -import kotlin.test.assertEquals -import org.junit.jupiter.api.Test -import org.stellar.anchor.api.custody.GenerateDepositAddressResponse -import org.stellar.anchor.api.shared.SepDepositInfo -import org.stellar.anchor.platform.apiclient.CustodyApiClient -import org.stellar.anchor.platform.data.JdbcSep31Transaction - -class Sep31DepositInfoCustodyGeneratorTest { - - companion object { - private const val ADDRESS = "testAccount" - private const val MEMO = "MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc=" - private const val MEMO_TYPE = "hash" - private const val ASSET_ID = "USDC" - } - - @Test - fun test_sep31_custodyGenerator_success() { - val txn = JdbcSep31Transaction() - txn.amountInAsset = ASSET_ID - val custodyApiClient: CustodyApiClient = mockk() - val generator = Sep31DepositInfoCustodyGenerator(custodyApiClient) - val depositAddress = GenerateDepositAddressResponse(ADDRESS, MEMO, MEMO_TYPE) - - every { custodyApiClient.generateDepositAddress(ASSET_ID) } returns depositAddress - - val actualInfo = generator.generate(txn) - - val expectedInfo = SepDepositInfo(ADDRESS, MEMO, MEMO_TYPE) - - assertEquals(expectedInfo, actualInfo) - } -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep31DepositInfoSelfGeneratorTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep31DepositInfoSelfGeneratorTest.kt index 7d8c0a9b6e..070e080b15 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep31DepositInfoSelfGeneratorTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep31DepositInfoSelfGeneratorTest.kt @@ -22,16 +22,16 @@ class Sep31DepositInfoSelfGeneratorTest { Arguments.of( "testId1", "GBJDTHT4562X2H37JMOE6IUTZZSDU6RYGYUNFYCHVFG3J4MYJIMU33HK", - "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHRlc3RJZDE=" + "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHRlc3RJZDE=", ), - Arguments.of("testId2", null, "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHRlc3RJZDI=") + Arguments.of("testId2", null, "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHRlc3RJZDI="), ) } } @ParameterizedTest @MethodSource("assets") - fun test_sep31_selfGenerator_success(txnId: String, address: String?, memo: String) { + fun test_sep31_selfGenerator_success(txnId: String, address: String?) { val txn = JdbcSep31Transaction() txn.id = txnId txn.toAccount = address diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep6DepositInfoCustodyGeneratorTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep6DepositInfoCustodyGeneratorTest.kt deleted file mode 100644 index d241f1ab41..0000000000 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep6DepositInfoCustodyGeneratorTest.kt +++ /dev/null @@ -1,44 +0,0 @@ -package org.stellar.anchor.platform.service - -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import kotlin.test.assertEquals -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.stellar.anchor.api.custody.GenerateDepositAddressResponse -import org.stellar.anchor.api.shared.SepDepositInfo -import org.stellar.anchor.platform.apiclient.CustodyApiClient -import org.stellar.anchor.platform.data.JdbcSep6Transaction - -class Sep6DepositInfoCustodyGeneratorTest { - companion object { - private const val ADDRESS = "testAccount" - private const val MEMO = "MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc=" - private const val MEMO_TYPE = "hash" - private const val ASSET_ID = "USDC" - } - - @MockK(relaxed = true) lateinit var custodyApiClient: CustodyApiClient - - private lateinit var generator: Sep6DepositInfoCustodyGenerator - - @BeforeEach - fun setup() { - MockKAnnotations.init(this, relaxUnitFun = true) - generator = Sep6DepositInfoCustodyGenerator(custodyApiClient) - } - - @Test - fun test_sep6_custodyGenerator_success() { - val txn = JdbcSep6Transaction() - txn.amountInAsset = ASSET_ID - - every { custodyApiClient.generateDepositAddress(ASSET_ID) } returns - GenerateDepositAddressResponse(ADDRESS, MEMO, MEMO_TYPE) - - val result = generator.generate(txn) - val expected = SepDepositInfo(ADDRESS, MEMO, MEMO_TYPE) - assertEquals(expected, result) - } -} diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep6DepositInfoSelfGeneratorTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep6DepositInfoSelfGeneratorTest.kt index 42330eee88..f9e9053aee 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep6DepositInfoSelfGeneratorTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/service/Sep6DepositInfoSelfGeneratorTest.kt @@ -23,9 +23,9 @@ class Sep6DepositInfoSelfGeneratorTest { "USDC", "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN", "GBJDTHT4562X2H37JMOE6IUTZZSDU6RYGYUNFYCHVFG3J4MYJIMU33HK", - "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHRlc3RJZDE=" + "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHRlc3RJZDE=", ), - Arguments.of("testId2", "USD", null, null, "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHRlc3RJZDI=") + Arguments.of("testId2", "USD", null, null, "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMHRlc3RJZDI="), ) } } @@ -47,7 +47,6 @@ class Sep6DepositInfoSelfGeneratorTest { assetCode: String, assetIssuer: String?, distributionAccount: String?, - memo: String ) { val txn = JdbcSep6Transaction() txn.id = txnId diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/service/SimpleInteractiveUrlConstructorTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/service/SimpleInteractiveUrlConstructorTest.kt index 3e05bbe3db..6da9ddf800 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/service/SimpleInteractiveUrlConstructorTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/service/SimpleInteractiveUrlConstructorTest.kt @@ -22,7 +22,6 @@ import org.stellar.anchor.auth.Sep24InteractiveUrlJwt import org.stellar.anchor.auth.WebAuthJwt import org.stellar.anchor.client.ClientService import org.stellar.anchor.client.DefaultClientService -import org.stellar.anchor.config.CustodySecretConfig import org.stellar.anchor.config.SecretConfig import org.stellar.anchor.platform.callback.PlatformIntegrationHelperTest.Companion.TEST_HOME_DOMAIN import org.stellar.anchor.platform.config.PropertySep24Config @@ -43,7 +42,6 @@ class SimpleInteractiveUrlConstructorTest { @MockK(relaxed = true) private lateinit var assetService: AssetService @MockK(relaxed = true) private lateinit var secretConfig: SecretConfig - @MockK(relaxed = true) private lateinit var custodySecretConfig: CustodySecretConfig @MockK(relaxed = true) private lateinit var customerIntegration: CustomerIntegration @MockK(relaxed = true) private lateinit var testAsset: AssetInfo @MockK(relaxed = true) private lateinit var webAuthJwt: WebAuthJwt @@ -63,7 +61,7 @@ class SimpleInteractiveUrlConstructorTest { "stellar:USDC:GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP" every { webAuthJwt.homeDomain } returns TEST_HOME_DOMAIN - jwtService = JwtService(secretConfig, custodySecretConfig) + jwtService = JwtService(secretConfig) sep24Config = gson.fromJson(SEP24_CONFIG_JSON_1, PropertySep24Config::class.java) request = gson.fromJson(REQUEST_JSON_1, HashMap::class.java) as HashMap txn = gson.fromJson(TXN_JSON_1, JdbcSep24Transaction::class.java) diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/service/TransactionServiceTest.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/service/TransactionServiceTest.kt index 819a0c1ab8..8cfa496a11 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/service/TransactionServiceTest.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/service/TransactionServiceTest.kt @@ -22,22 +22,21 @@ import org.stellar.anchor.api.platform.PatchTransactionRequest import org.stellar.anchor.api.platform.PatchTransactionsRequest import org.stellar.anchor.api.platform.PlatformTransactionData import org.stellar.anchor.api.sep.SepTransactionStatus -import org.stellar.anchor.api.shared.* +import org.stellar.anchor.api.shared.Amount +import org.stellar.anchor.api.shared.FeeDetails +import org.stellar.anchor.api.shared.RefundPayment +import org.stellar.anchor.api.shared.Refunds import org.stellar.anchor.asset.AssetService import org.stellar.anchor.asset.DefaultAssetService -import org.stellar.anchor.config.CustodyConfig -import org.stellar.anchor.custody.CustodyService import org.stellar.anchor.event.EventService import org.stellar.anchor.event.EventService.EventQueue.TRANSACTION import org.stellar.anchor.event.EventService.Session import org.stellar.anchor.platform.data.* import org.stellar.anchor.sep24.Sep24DepositInfoGenerator -import org.stellar.anchor.sep24.Sep24Transaction import org.stellar.anchor.sep24.Sep24TransactionStore import org.stellar.anchor.sep31.Sep31TransactionStore import org.stellar.anchor.sep38.Sep38QuoteStore import org.stellar.anchor.sep6.Sep6DepositInfoGenerator -import org.stellar.anchor.sep6.Sep6Transaction import org.stellar.anchor.sep6.Sep6TransactionStore import org.stellar.anchor.util.GsonUtils @@ -62,8 +61,6 @@ class TransactionServiceTest { @MockK(relaxed = true) private lateinit var eventSession: Session @MockK(relaxed = true) private lateinit var sep6DepositInfoGenerator: Sep6DepositInfoGenerator @MockK(relaxed = true) private lateinit var sep24DepositInfoGenerator: Sep24DepositInfoGenerator - @MockK(relaxed = true) private lateinit var custodyService: CustodyService - @MockK(relaxed = true) private lateinit var custodyConfig: CustodyConfig private lateinit var transactionService: TransactionService @@ -80,9 +77,7 @@ class TransactionServiceTest { assetService, eventService, sep6DepositInfoGenerator, - sep24DepositInfoGenerator, - custodyService, - custodyConfig + sep24DepositInfoGenerator ) } @@ -234,9 +229,7 @@ class TransactionServiceTest { assetService, eventService, sep6DepositInfoGenerator, - sep24DepositInfoGenerator, - custodyService, - custodyConfig + sep24DepositInfoGenerator ) val mockAsset = Amount("10", fiatUSD) assertDoesNotThrow { transactionService.validateAsset("amount_in", mockAsset) } @@ -297,7 +290,6 @@ class TransactionServiceTest { transactionService.patchTransactions(request) - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sep24TransactionStore.save(any()) } verify(exactly = 1) { eventSession.publish(any()) } } @@ -322,66 +314,10 @@ class TransactionServiceTest { transactionService.patchTransactions(request) - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sep24TransactionStore.save(any()) } verify(exactly = 1) { eventSession.publish(any()) } } - @Test - fun test_patchTransaction_sep24DepositPendingAnchor() { - val txId = "testTxId" - val tx = JdbcSep24Transaction() - tx.status = SepTransactionStatus.INCOMPLETE.toString() - tx.kind = "deposit" - val data = PlatformTransactionData() - data.id = txId - data.memo = "12345" - data.memoType = "id" - data.status = SepTransactionStatus.PENDING_ANCHOR - val request = - PatchTransactionsRequest.builder().records(listOf(PatchTransactionRequest(data))).build() - - every { sep31TransactionStore.findByTransactionId(any()) } returns null - every { sep6TransactionStore.findByTransactionId(any()) } returns null - every { sep24TransactionStore.findByTransactionId(any()) } returns tx - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - transactionService.patchTransactions(request) - - verify(exactly = 1) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } - verify(exactly = 1) { sep24TransactionStore.save(any()) } - verify(exactly = 1) { eventSession.publish(any()) } - } - - @Test - fun test_patchTransaction_sep24WithdrawalPendingUserTransferStart() { - val txId = "testTxId" - val tx = JdbcSep24Transaction() - tx.status = SepTransactionStatus.INCOMPLETE.toString() - tx.kind = "withdrawal" - tx.withdrawAnchorAccount = null - val data = PlatformTransactionData() - data.id = txId - data.memo = "12345" - data.memoType = "id" - data.status = SepTransactionStatus.PENDING_USR_TRANSFER_START - data.withdrawAnchorAccount = TEST_DEST_ACCOUNT - val request = - PatchTransactionsRequest.builder().records(listOf(PatchTransactionRequest(data))).build() - - every { sep31TransactionStore.findByTransactionId(any()) } returns null - every { sep6TransactionStore.findByTransactionId(any()) } returns null - every { sep24TransactionStore.findByTransactionId(any()) } returns tx - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - transactionService.patchTransactions(request) - - verify(exactly = 1) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } - verify(exactly = 1) { sep24TransactionStore.save(any()) } - verify(exactly = 1) { eventSession.publish(any()) } - assertEquals(TEST_DEST_ACCOUNT, tx.withdrawAnchorAccount) - } - @Test fun test_patchTransaction_sep24WithdrawalPendingUserTransferStart_statusNotChanged() { val txId = "testTxId" @@ -402,7 +338,6 @@ class TransactionServiceTest { transactionService.patchTransactions(request) - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sep24TransactionStore.save(any()) } verify(exactly = 1) { eventSession.publish(any()) } } @@ -428,7 +363,6 @@ class TransactionServiceTest { transactionService.patchTransactions(request) - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sep6TransactionStore.save(any()) } verify(exactly = 1) { eventSession.publish(any()) } } @@ -454,61 +388,6 @@ class TransactionServiceTest { transactionService.patchTransactions(request) - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } - verify(exactly = 1) { sep6TransactionStore.save(any()) } - verify(exactly = 1) { eventSession.publish(any()) } - } - - @CsvSource(value = ["deposit", "deposit-exchange"]) - @ParameterizedTest - fun test_patchTransaction_sep6DepositPendingAnchor(kind: String) { - val txId = "testTxId" - val tx = JdbcSep6Transaction() - tx.status = SepTransactionStatus.INCOMPLETE.toString() - tx.kind = kind - val data = PlatformTransactionData() - data.id = txId - data.memo = "12345" - data.memoType = "id" - data.status = SepTransactionStatus.PENDING_ANCHOR - val request = - PatchTransactionsRequest.builder().records(listOf(PatchTransactionRequest(data))).build() - - every { sep31TransactionStore.findByTransactionId(any()) } returns null - every { sep6TransactionStore.findByTransactionId(any()) } returns tx - every { sep24TransactionStore.findByTransactionId(any()) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - transactionService.patchTransactions(request) - - verify(exactly = 1) { custodyService.createTransaction(ofType(Sep6Transaction::class)) } - verify(exactly = 1) { sep6TransactionStore.save(any()) } - verify(exactly = 1) { eventSession.publish(any()) } - } - - @CsvSource(value = ["withdrawal", "withdrawal-exchange"]) - @ParameterizedTest - fun test_patchTransaction_sep6WithdrawalPendingUserTransferStart(kind: String) { - val txId = "testTxId" - val tx = JdbcSep6Transaction() - tx.status = SepTransactionStatus.INCOMPLETE.toString() - tx.kind = kind - val data = PlatformTransactionData() - data.id = txId - data.memo = "12345" - data.memoType = "id" - data.status = SepTransactionStatus.PENDING_USR_TRANSFER_START - val request = - PatchTransactionsRequest.builder().records(listOf(PatchTransactionRequest(data))).build() - - every { sep31TransactionStore.findByTransactionId(any()) } returns null - every { sep6TransactionStore.findByTransactionId(any()) } returns tx - every { sep24TransactionStore.findByTransactionId(any()) } returns null - every { custodyConfig.isCustodyIntegrationEnabled } returns true - - transactionService.patchTransactions(request) - - verify(exactly = 1) { custodyService.createTransaction(ofType(Sep6Transaction::class)) } verify(exactly = 1) { sep6TransactionStore.save(any()) } verify(exactly = 1) { eventSession.publish(any()) } } @@ -534,7 +413,6 @@ class TransactionServiceTest { transactionService.patchTransactions(request) - verify(exactly = 0) { custodyService.createTransaction(ofType(Sep24Transaction::class)) } verify(exactly = 1) { sep6TransactionStore.save(any()) } verify(exactly = 1) { eventSession.publish(any()) } } @@ -646,9 +524,7 @@ class TransactionServiceTest { assetService, eventService, sep6DepositInfoGenerator, - sep24DepositInfoGenerator, - custodyService, - custodyConfig + sep24DepositInfoGenerator ) assertDoesNotThrow { diff --git a/platform/src/test/kotlin/org/stellar/anchor/platform/utils/TestUtil.kt b/platform/src/test/kotlin/org/stellar/anchor/platform/utils/TestUtil.kt index a6b494364f..17dd6a8c18 100644 --- a/platform/src/test/kotlin/org/stellar/anchor/platform/utils/TestUtil.kt +++ b/platform/src/test/kotlin/org/stellar/anchor/platform/utils/TestUtil.kt @@ -4,7 +4,6 @@ import io.mockk.every import javax.crypto.SecretKey import org.stellar.anchor.api.shared.Amount import org.stellar.anchor.api.shared.FeeDetails -import org.stellar.anchor.config.CustodySecretConfig import org.stellar.anchor.config.SecretConfig import org.stellar.anchor.util.KeyUtil @@ -38,9 +37,3 @@ fun SecretConfig.setupMock(block: (() -> Any)? = null) { block?.invoke() } - -fun CustodySecretConfig.setupMock() { - val cfg = this - every { cfg.custodyAuthSecret } returns - "custody_auth_secret_key_________________________".also { KeyUtil.validateJWTSecret(it) } -} diff --git a/platform/src/test/resources/custody/fireblocks/client/public_key.txt b/platform/src/test/resources/custody/fireblocks/client/public_key.txt deleted file mode 100644 index 5ee948e2f5..0000000000 --- a/platform/src/test/resources/custody/fireblocks/client/public_key.txt +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmNC/WJx1agI9OpNRzWmf -Zk07h7hyD0B1ChPR9y4XquvJQdlkJwSE55R43iwnreDS4ZfJ/ixbB8mBpFnzwPhy -V3xwz68si1vqU0KSHyqzqkFHqWM8GTspSF7E+TDvADI++iSAyA+RkThedonS1xzn -DVf9r3S5u9s96KbMy4W6qhrUeJqSpfhLnXhz5GkhMQ/ZF7FLn34lUVHtMxscNaJx -PXuk6BqLAAiMpzd2MWQUZtBGq9zCge47mCsvjBXLc6rsMhZV9DtDmWyEfUQ+VQJO -ZA5VUQBYYDsqlVVfT/7P8V0LanJC7tf/I+/NwmOr75REVK9cxjBGCWEyJ6EAuW1j -PyI9Z55bj3u9ONvOIl3za7labRX+sTBVyDi5wuQIzXHHwZFyKT5GB85e0slQCsgc -RPD9fRI8HSjKgvQLSA79MelLThvSfdcNJWwBrIjwbnAr4V+gaDRTCYRhgmBF4sLj -Z4OmKuyYABpAh+Qi6Kuv3Wnxhzqb5aH9MgthOs6dJx3gmJBGlYpKzEw81wzak1j2 -Gdi8D7nSz2e4bfcSm0n2wPLmY8K1fAIWKiJD8GC1+0plnBhb4gv9VYIAP+AOYhF+ -GMWaoqUgsxB4l5bleBtZrvSIcur6QHtiLC2L+e56/t2eKUD7EYPXGW6vBRo30Wmk -+psyGyUjpCPR9CvxEggjiVcCAwEAAQ== ------END PUBLIC KEY----- diff --git a/platform/src/test/resources/custody/fireblocks/client/secret_key.txt b/platform/src/test/resources/custody/fireblocks/client/secret_key.txt deleted file mode 100644 index 2558568d33..0000000000 --- a/platform/src/test/resources/custody/fireblocks/client/secret_key.txt +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQCY0L9YnHVqAj06 -k1HNaZ9mTTuHuHIPQHUKE9H3Lheq68lB2WQnBITnlHjeLCet4NLhl8n+LFsHyYGk -WfPA+HJXfHDPryyLW+pTQpIfKrOqQUepYzwZOylIXsT5MO8AMj76JIDID5GROF52 -idLXHOcNV/2vdLm72z3opszLhbqqGtR4mpKl+EudeHPkaSExD9kXsUuffiVRUe0z -Gxw1onE9e6ToGosACIynN3YxZBRm0Ear3MKB7juYKy+MFctzquwyFlX0O0OZbIR9 -RD5VAk5kDlVRAFhgOyqVVV9P/s/xXQtqckLu1/8j783CY6vvlERUr1zGMEYJYTIn -oQC5bWM/Ij1nnluPe704284iXfNruVptFf6xMFXIOLnC5AjNccfBkXIpPkYHzl7S -yVAKyBxE8P19EjwdKMqC9AtIDv0x6UtOG9J91w0lbAGsiPBucCvhX6BoNFMJhGGC -YEXiwuNng6Yq7JgAGkCH5CLoq6/dafGHOpvlof0yC2E6zp0nHeCYkEaVikrMTDzX -DNqTWPYZ2LwPudLPZ7ht9xKbSfbA8uZjwrV8AhYqIkPwYLX7SmWcGFviC/1VggA/ -4A5iEX4YxZqipSCzEHiXluV4G1mu9Ihy6vpAe2IsLYv57nr+3Z4pQPsRg9cZbq8F -GjfRaaT6mzIbJSOkI9H0K/ESCCOJVwIDAQABAoICACVf1JSa+3p4Xro/Qp//B62x -EPCyMy84wAc9brpKCx7R+RCCVyTVzZ2H6gHIgdHcbScghRYFiiP+7Baiq2OUP/7W -oA6jVL0mt0oyu2MlAfkPazPp4c538jJRf1JE3gdJwq5SFC8Z46vFQhEyxx62YmoY -jaKGZVClH+CRPwA9zjgBRNrUTy6JahDy30FLVwNVUW6ifZDd/MmFRIKdzEnv10tG -vL/JkhppfOD+xwjwZa75nHc3EFlPx0KBuWjFAdPhx0++t7n6bp+M82Bhuu72l+6P -tN0RPQwYHGOWlC+P7hKlg3++v1EEAdIrucKjtu7q78VYeFO+hbqQi4A2t+Mh9kYf -TdQPUfrCAQHUc4VqEuYqy36Q/x3DPItlMBFCD0MyScIlF+5U515zvEbWcebfkILb -wWGenvPQ9X53xC04FZICAmYC3TT0mRY5PbLl9cB4mYZFiEsNTx4nLL1ziMapv6zF -a0ErCxEx5lVOvtlzUIYimqfaPFtkOWcEVx9lcck4l6k6SArMGO6V6sBf++tQQCid -zkvsaYLMoB3TNJgn6GOHPvZJ6pGGm5Mba9i4kq/iiZlObWPQfc19OoP1Rp+alGdy -siGgX0Zjgyjrjq9IsBjUMCz3I/2ePJi/l3keNSKNLU/OlvTrBJo7LMHWwdXMks94 -0+8MVzQNxxRPhir1egD5AoIBAQDM79kdMeFvmjpBXNUofpGFHLZ6FtrgDbszA60S -FmxUsux1iZGUXeAVenSrQK+bbJ2NuSsgkAxDx3YqNB1o93lnm8MjLrKxs2thqrwp -El9Ky5Me4GsRGcPzLbhv1h3hImqZEWiqXlKcQltQI73EFLP7sDEaHgIXhTAKh5W3 -yPY3OOvWs+U3pblJ2+xdgrzezh9MvsWW++v/sAS7DYPe2HU+ouv/x22UDOjnZRBI -mI7y3TqlPqrL6fWv4y0vuJ5ElN84u9SIITlK/0EAN9q1eSXxtJ+zKGd8rKVmLBoQ -27UgwULGWQOAJzo/eazpGWBpUu1GGBfNR/lo1TYENzuPFavJAoIBAQC+5EQxzK8s -dXlqzUSXNJAiBd76LL4F8ef9Ej3wSNAz8gP62q6ygydD781W+pBohHXEk+Rxa1/N -rjXSu/3BndcdYsiDb+WZEE7wG4nd1Sf32W+bzX/Y3Y5D2IZ+tSFfaaGY9UP5kSRg -fjLit8/CrBvD1YmCVo6OEtssL86/+QC3fizt8H0JNDdjDIrYCK+fqGZrVDgVvF06 -KfXJdQ3kMKHQB73czlybld1qc1pFFZT0OM4OSFfXloJPke0F8v+qos5Io3tM/G3N -2av2BtOTU3dZlbedOUqOmfGgHvJftNp9rHNr60SpVeqDBO88DoA7SSWv1FrpK9Ru -tHxwRuw5S9wfAoIBAQC8WeBY1cRU9xF9i12ZlyIVjjsj4KePbu9TRpilXBh6i+uJ -Z0N2cgUGmIJr9squ9Vekrr+1cM7k6Ihw5270Tya0k9L4He48JiOWIv5GXU1Zh9qS -cT3V/LE8ahPuLYNOrHv+bKKemowgc1hLQl4hvclhlCC82UGg7lcmBlETFc89F8Kh -lLshtt2hxU/q7cK6oATiWx9s0eKoH9VpOVWu1ZCEXea7MUZD4M2aBie+IQCYs703 -rG37de7JleisyGglrhGuqer+plJq3EiYc70+Nrcp+5Efbw4V5vMpAXV/cv54uUau -15bBDMybFDBYGGukwYOzfWc602Cl+fdoTn8d1ZQJAoIBAQC0Fd7r6EC+PZktE7eI -ulLbdhR0ia8/JocIbaQ3QkoBJ2lvx8yVpjtt0RYz3zBLGeOMb0VNZtG71YT7aNSt -Sv38wAuQTAkbjJIb3K8EsyoAnYW4Q3dAHUwwX14KvVBdleaYyZG2KKJb0dKppmT/ -L6UF7DbM2mGkUM+p8PQokX2Gnic+1ejvvO4fqF54DkG46oKs8If0oX6F973jr2VK -iEKvYg5Q9/HP6eM3oNlhYUnj2I80VDVisp/+9xjXyvY9gVgAmRIif+YLFDYxZSFG -1LA8uIy/a6QVwc98kpVvisuxUfaVsh9VbTNPJWGYk8mPpKNELJ8TaIEBa44V0iLa -ruFbAoIBAQCIS14M+ZnXr/uSmSYF5/bP6rauBkC1lSqsnOSZibcKRmIRkjQr+Xik -DShDiXJV01PEYvWqajOhlynq+T0ScRdKRAOTRZDWZhJKH0mMVcZfrWhlat2rANwp -lw+ARcShnHeleeeB9qLoZ8BwNLH/6mD2UfpiJBjEve7boK2VwvD9WpQUu0Khe9fC -yFTwoZl2CXH6yT3rob44ZbDBU0mX2Z/33DUVhQPXaeBP3QTNmBJXv7ZTj9Vhk/8o -cgFkxo4yd4Tc44RHIFxkhZJ5PLsT86xHLWVTCbY4sh7MCUHf524ZMFJhoxVKPSFc -E2x5AjWw4usjI1jk0Xtl6/Y39UMq5JD7 ------END PRIVATE KEY----- \ No newline at end of file diff --git a/platform/src/test/resources/custody/fireblocks/webhook/private_key.txt b/platform/src/test/resources/custody/fireblocks/webhook/private_key.txt deleted file mode 100644 index a7a660daa1..0000000000 --- a/platform/src/test/resources/custody/fireblocks/webhook/private_key.txt +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJPq9AU6f8GrkScc -loRb+UJmbxilZ7iLkvp0FeR/yymQuJDMNXPXwt5MYR9VJoY0uMDHThaEwbQAc2M4 -L9wl0EtsESebODUTqnsWZ8/a6GsmvIM3kz02ZOUHLst0krSkECPUHLLUKuw/HWz3 -LWxWLeqVRIcsCwKJNaBPZJzFw/8PAgMBAAECgYBhWXSYLFQApmW1k/8LxWxa4wei -9Nk6f8GPy+7Mn76Z8IFH6t4TC6FYpHQXJvdfxDsDxSgDcgP574IBfu0gulJHEIso -Dy1xR8TPobfhwoS1NJn70a8KQA6Whu+K5pjJxGUC7bpJH4ZK0O+szB1mNC0WgUyw -Ic0lXCFFnCrgbNFWQQJBAM7+zgHAyD6rqQem09JcabMkwxu0mYih1mvQ3uv/fay8 -BdgZAqMuacMAPQ/A1TLeuTbMQ0LojnK/QlotbhHVnSkCQQC27684POaRrVIsVo5u -KvQsKSNlYPpvGrGapHKiVgIQYfZSEY9SrazqKbA5yux0OR8ZFFSiJCThRElpzhnW -FYl3AkEArNsLnVsn3W3sUX93FAwoGHlylQhTzk2XiaF7BwjsIftBxhvcn/h6SWVB -mI4ne7uSX7hj0tPxYNFmz3dwm2QPQQJBAKS8mPSy2wtqoiotVBvfcHzoGujrePpe -dnuFBXosq7UnEpN7Hq7cmW9RVVHl7CMJYXjLNx/AHroBLX8rS1bflCcCQBclpUG1 -PybVy1jHXTdI0w6zB6AwjaeFN5x4+b7hRe29yLNF532uIatxif19LHb5jUC7Eefp -LWBxx/bB4JCIyug= ------END PRIVATE KEY----- \ No newline at end of file diff --git a/platform/src/test/resources/custody/fireblocks/webhook/public_key.txt b/platform/src/test/resources/custody/fireblocks/webhook/public_key.txt deleted file mode 100644 index 3ff730fd89..0000000000 --- a/platform/src/test/resources/custody/fireblocks/webhook/public_key.txt +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCT6vQFOn/Bq5EnHJaEW/lCZm8Y -pWe4i5L6dBXkf8spkLiQzDVz18LeTGEfVSaGNLjAx04WhMG0AHNjOC/cJdBLbBEn -mzg1E6p7FmfP2uhrJryDN5M9NmTlBy7LdJK0pBAj1Byy1CrsPx1s9y1sVi3qlUSH -LAsCiTWgT2ScxcP/DwIDAQAB ------END PUBLIC KEY----- \ No newline at end of file diff --git a/platform/src/test/resources/custody/fireblocks/webhook/submitted_event_invalid_signature.txt b/platform/src/test/resources/custody/fireblocks/webhook/submitted_event_invalid_signature.txt deleted file mode 100644 index 6a85ba3e4d..0000000000 --- a/platform/src/test/resources/custody/fireblocks/webhook/submitted_event_invalid_signature.txt +++ /dev/null @@ -1 +0,0 @@ -Yww6co109EfZ6BBam0zr1ewhv2gB3sFrfzcmbEFTttGp6GYVNEOslcMIMbjrFsFtkiEIO5ogvPI7Boz7yQUiXqh92Spj1aG5NoGDdjiW2ozTJxKq7ECK9IsS5vTjIxnBXUIXokCAN2BuiyA8d7LciJ6HwzS+DIvFNyvv7uKU6O0= \ No newline at end of file diff --git a/platform/src/test/resources/custody/fireblocks/webhook/submitted_event_request.json b/platform/src/test/resources/custody/fireblocks/webhook/submitted_event_request.json deleted file mode 100644 index d858c95ecc..0000000000 --- a/platform/src/test/resources/custody/fireblocks/webhook/submitted_event_request.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "type": "TRANSACTION_CREATED", - "tenantId": ".........-.....-....-....-...........", - "timestamp": 1679651214621, - "data": { - "id": "........-....-....-....-............", - "createdAt": 1679651104380, - "lastUpdated": 1679651104380, - "assetId": "WETH_TEST3", - "source": { - "id": "0", - "type": "VAULT_ACCOUNT", - "name": "Main", - "subType": "" - }, - "destination": { - "id": "12", - "type": "VAULT_ACCOUNT", - "name": "MintBurn", - "subType": "" - }, - "amount": 0.001, - "sourceAddress": "", - "destinationAddress": "", - "destinationAddressDescription": "", - "destinationTag": "", - "status": "SUBMITTED", - "txHash": "", - "subStatus": "", - "signedBy": [], - "createdBy": ".........-.....-....-....-...........", - "rejectedBy": "", - "amountUSD": null, - "addressType": "", - "note": "", - "exchangeTxId": "", - "requestedAmount": 0.001, - "feeCurrency": "ETH_TEST3", - "operation": "TRANSFER", - "customerRefId": null, - "amountInfo": { - "amount": "0.001", - "requestedAmount": "0.001" - }, - "feeInfo": {}, - "destinations": [], - "externalTxId": null, - "blockInfo": {}, - "signedMessages": [], - "assetType": "ERC20" - } -} \ No newline at end of file diff --git a/platform/src/test/resources/custody/fireblocks/webhook/submitted_event_valid_signature.txt b/platform/src/test/resources/custody/fireblocks/webhook/submitted_event_valid_signature.txt deleted file mode 100644 index 6bfebe14bf..0000000000 --- a/platform/src/test/resources/custody/fireblocks/webhook/submitted_event_valid_signature.txt +++ /dev/null @@ -1 +0,0 @@ -AWrAEl3SHoPsj5iQPP5jJNubpzbqJYVLkeTi6lxmc2raiz3u+LN3kGAVcmPz9PTBX7636fh8kfGQbogZEipT2LRDPWL7By3h/Qg2NtuOS4mM7lidf2r4CoJGUxfpH+KFDvE8UrgzV847iLmck7spH6WpcBXOYFY1I7eapwX35T8= \ No newline at end of file diff --git a/service-runner/src/main/java/org/stellar/anchor/platform/ServiceRunner.java b/service-runner/src/main/java/org/stellar/anchor/platform/ServiceRunner.java index 3e04c2adec..8af7b63685 100644 --- a/service-runner/src/main/java/org/stellar/anchor/platform/ServiceRunner.java +++ b/service-runner/src/main/java/org/stellar/anchor/platform/ServiceRunner.java @@ -30,11 +30,6 @@ private static void startServers(String[] args) { anyServerStarted = true; } - if (cmd.hasOption("custody-server") || cmd.hasOption("all")) { - startCustodyServer(null); - anyServerStarted = true; - } - if (cmd.hasOption("platform-server") || cmd.hasOption("all")) { startPlatformServer(null); anyServerStarted = true; @@ -86,7 +81,6 @@ private static Options getOptions() { options.addOption("h", "help", false, "Print this message."); options.addOption("a", "all", false, "Start all servers."); options.addOption("s", "sep-server", false, "Start SEP endpoint server."); - options.addOption("c", "custody-server", false, "Start Custody server."); options.addOption("p", "platform-server", false, "Start Platform API endpoint server."); options.addOption( "o", "stellar-observer", false, "Start Observer that streams from the Stellar blockchain."); @@ -107,11 +101,6 @@ public static ConfigurableApplicationContext startPlatformServer(Map env) { - info("Starting custody server..."); - return new CustodyServer().start(env); - } - public static ConfigurableApplicationContext startStellarObserver(Map env) { info("Starting observer..."); return new StellarObservingServer().start(env); diff --git a/service-runner/src/main/kotlin/org/stellar/anchor/platform/TestProfileRunner.kt b/service-runner/src/main/kotlin/org/stellar/anchor/platform/TestProfileRunner.kt index 3fd08432be..5ad6e18935 100644 --- a/service-runner/src/main/kotlin/org/stellar/anchor/platform/TestProfileRunner.kt +++ b/service-runner/src/main/kotlin/org/stellar/anchor/platform/TestProfileRunner.kt @@ -117,9 +117,6 @@ class TestProfileExecutor(val config: TestConfig) { if ((shouldStartAllServers || shouldStartObserver) && !custodyEnabled) { jobs += scope.launch { runningServers.add(ServiceRunner.startStellarObserver(envMap)) } } - if ((shouldStartAllServers || shouldStartCustodyServer) && custodyEnabled) { - jobs += scope.launch { runningServers.add(ServiceRunner.startCustodyServer(envMap)) } - } if (shouldStartAllServers || shouldStartEventProcessingServer) { jobs += scope.launch { runningServers.add(ServiceRunner.startEventProcessingServer(envMap)) } diff --git a/service-runner/src/main/resources/profiles/auth-apikey-custody/config.env b/service-runner/src/main/resources/profiles/auth-apikey-custody/config.env deleted file mode 100644 index 1ddd80e35f..0000000000 --- a/service-runner/src/main/resources/profiles/auth-apikey-custody/config.env +++ /dev/null @@ -1,21 +0,0 @@ -# Use Horizon for custody server tests -stellar_network.rpc_url= -stellar_network.horizon_url: https://horizon-testnet.stellar.org - -secret.custody.fireblocks.api_key=testApiKey -secret.custody.fireblocks.secret_key=MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCGijeeahIvJQ+AQ7VVnp6iwj1jPDM/4cb35R6iV3O8PVmtiN2HlTP980EPM1eW7QOVjNeTq4EY4kW1XN3kcvaIXkxnkqxNZsxqbejgU1IvyQ8OSpxstNTkKZQboFexmQL0vZfhB4LYuYmFR8i/EqBMXPAZ88xas908UMRk9WyUeX7BN8RA3444oRWUuA8g0iZKqYUSEv1jMYieD5fcEl6rWTfeUVU1Ob7kog+pJU3NiaRG0Fnt4qloB3mn1VqJx4H7OEPjbKKO734Yrb7PrhsNm9S76q5ICEa3JfKqtqqpINQUh1O5tkcb+lS6ZfzfR17RS+ozxmFkZICzJG56d2pfqY89rlnWVJkpvuTlePghmH/po36XXTM3NYaGhDdKYdF/abFvIO8/5BGAI7vB/80Te2KRxbBravrd9A2GFdnarD3+nEiCSGTkIHBD3ZRF9yex+q4kt5n7F8bqR70l98Gd5ueWivr5CTW0lfLrHptr32JoiE7/9t37FCAvcFTmeVqz9uny7DZiZRFLGveIxCD5THIJHUGEVpU8EK96gcXv9YDOlrXckK2St7JfbkNDbxRrqYzl8dyZrY+BjiQxoPeaEuiSecv0mbSrzze76nbmozWi48wzO7rmeUUtb0cgacoywzaVxbv5VuM4AeTdAIMfJ+GsJrrN7is8Nd+7A5d26wIDAQABAoICAFb7l5f41gba/BmeLOfNJJzv3gaBjlTX4O62mEa3KaFjPM6ANVGKOlIOalqshA2U8QNISrwzXsS6zfzCrMcdOJzT8qvn3TQxqSmKI8ycsf8pC23e+SEjDJzy8MmnseqllY6r4TzgwUzjL7EWxwgZv79/OtBcmhtuPDPZuA/ZyLh5kGTVbDHRFz8vjidXlJhQpecRkHIuCtqN1Nj0Fed3jPNVPFSk2uUNj4h76ooeDx46xhXucKXnLEWQx6ulueJoNXY5W1L3EmY9GpZUBpB47Q6wWCqiQMlbaHTSuQB8Fvw/uD4JfC0OVw0UnKDifUnR99BzN1kAAkmI3Nh1TmQpPoDkwJ71KqkLBO8VjfawdKOB7GBNa9/f5tBFNfLwuqypMC4A8WAJvEbKhrdBNUs7hLMiSaIIvCRAHaNjiDbIf6iW/zVWm2qk87+gKQX7Cj0NMha81AWcUrT+i9sUJujrfMyA809IduzY8xXJACM446BicfUm3NA7rJacaj/bBlYtGbyuUKmuAfF9bReWOaHz+QO6tnP/YkfddRvNq2EivUKxd9+fMT9BxA6xF2vx3I2sjZso952QLUVgleyzsC/bryA4qs+jTZVwb3XiiriSNlgqBgR/pLmjInLCDR7LH9VvWcFAsLJ4aCbxalLHYnb6ZaXRY27uYuIog244cqXHxu5RAoIBAQDev9CFI9bH1pV6gss6N9sWGU1HNYODwepustMJkb0KsFalKd+WY7ZJ3yzMO21bdSAn6Q8IxHYUKR/f+63Mb9g0aXsN3YNrBVBSffg3lVvf1LrH6Ehe+E3I9vGe0cbFA4bxSh41VOoRuSUeCyINT5qZfZOmIMJ1kHBTidN7cqgaI6iJnySP5nCXHQhSVPMm4EqSiSwFsrBNKeoNGlpyZD5j+Zxxlk4SdeQv4THyYvBMbpqoBtzz8ywPr/C8KyosSZB1I1R9nacQV1e0x40gqMgD1XNa4OB0P0D9Sf7KBf5fdRqVpVxCEMGK4deujqODc1nnzHChrpQQsRW+CDD4WhjDAoIBAQCan41p6/EZgWrAIF2SuybzBZ6K9r/O8AoFGWMYDsyRv6qf9LBlbY+EvSUUZaEDJyiRLPVR3WKtYb9T+hkyQMFWvXM21JmQhkKMRgLCSdtxaFFnZ7IQKdjA3yaYm0AOV4wSCxFxYrhPGaBd8agPiGjjIaU58ex39pd9EHOe9ITiSa9kDVeahxXuwgpw/xiwXoydj2EYgsd+1ki1+sr9Z5gLEBqkSYnxhMRmtPxwudvl/2zUDDvgscFMJKQBZvCCcXyNT+5uXWOCv9PBf412+9YOuGAsho+5cnzTV162kQ62GHRhWK2Hl58/la9RlUyqjyNJJnfQB0lapiYVCkJkDQa5AoIBAQC49gfc9kR7jfhzUTYVspnOgNYFgi8hch0LLJxGfui9fm2EGgAZ7dRJBPM650HIRrqbyU99lT0DQp7AK0lGz7x00P2oJr7gV/o5dXZuGSy/8PHj20J08bQNYtiBa2mk4Gfl5gitekQe0WE70DzHVslmGLtIoD04x+Ytx+1+vVdO1Ts2g5olj1EAedAWJYn+RxJFGXgfhyAUAvDhTne6MvkHpRY9z3QmqhP/mfwSbAtjPBhZ29EPFGYK5Kp+ZU1QlwlH6z1WTVmTHSOr7mOGsf/cBxsRZFecz4VUjWPvNwjhFOktR7ES/rznaN7iiHjIRzlpW/5OkxtJvPHz6PNY1d9nAoIBAQCTm+WATMGpu6aRK1SpEwknzyF/P/f0MasfGxFCkj4wlWzprsoGygTMj1SqmCqRu/w6O1UmhdYB9uKT5JnRI4huqgUnPuYq1kEJcHyJxcA8D35G3PNcAtbtqRpLbUpFZsZNI0vPlpKk0K4LgPsTeQHIcwIswwsfqsULm30FLiD9daJ+PU54MqV5fLxDCwQiuuA6OfpTT+Xq694V/Al0yESZqxID7EBH1Js6AWVMp+GJO8JE3Dj9VDRX4FLBE+heXsCslrXjnju1QjOA8ae3Asti2hjV6M+kFeucRjggyXHl3Iqds+5VFyXrqZXoqEK8QFFLL/IJIiug4iRQya5/nP/hAoIBAFSELSTQ/KElxQvy6vHr9fjfMiRGcTmsNj2v+zHN9OLmG9c2xBOJcM4a5lCt4FrqgeMMsrOVWSfvwVz2edwCqNPcO3wz9/l9EdKrIL9MPQK9SUxMWl1hjW2Yni2I9wkMsTivszqEVLVe9EQMs1WP2Tb2ijc/Otx6jUpxP73fNjI/p9FBiM3pV1Pt2YtmjQLecG5SYkgUbIGW3iEmvkdIfK5z6Vdm7LCJPADOP3OV1xd+wpJMdnePww1Mtp3Kf8ZPrTHL8t2U2kV+sW0OjrRabxF79izjhYs2KQdKoyptX/pXXPIg12Hnt5YNwVJfThpqSWVj83AVOX9ZlGqwtiNOLk4= - -# seps -sep6.deposit_info_generator_type=custody -sep24.deposit_info_generator_type=custody -sep31.deposit_info_generator_type=custody - -# custody -custody.type=fireblocks -custody.fireblocks.vault_account_id=1 -custody.fireblocks.public_key=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAhoo3nmoSLyUPgEO1VZ6eosI9YzwzP+HG9+UeoldzvD1ZrYjdh5Uz/fNBDzNXlu0DlYzXk6uBGOJFtVzd5HL2iF5MZ5KsTWbMam3o4FNSL8kPDkqcbLTU5CmUG6BXsZkC9L2X4QeC2LmJhUfIvxKgTFzwGfPMWrPdPFDEZPVslHl+wTfEQN+OOKEVlLgPINImSqmFEhL9YzGIng+X3BJeq1k33lFVNTm+5KIPqSVNzYmkRtBZ7eKpaAd5p9VaiceB+zhD42yiju9+GK2+z64bDZvUu+quSAhGtyXyqraqqSDUFIdTubZHG/pUumX830de0UvqM8ZhZGSAsyRuendqX6mPPa5Z1lSZKb7k5Xj4IZh/6aN+l10zNzWGhoQ3SmHRf2mxbyDvP+QRgCO7wf/NE3tikcWwa2r63fQNhhXZ2qw9/pxIgkhk5CBwQ92URfcnsfquJLeZ+xfG6ke9JffBnebnlor6+Qk1tJXy6x6ba99iaIhO//bd+xQgL3BU5nlas/bp8uw2YmURSxr3iMQg+UxyCR1BhFaVPBCveoHF7/WAzpa13JCtkreyX25DQ28Ua6mM5fHcma2PgY4kMaD3mhLoknnL9Jm0q883u+p25qM1ouPMMzu65nlFLW9HIGnKMsM2lcW7+VbjOAHk3QCDHyfhrCa6ze4rPDXfuwOXdusCAwEAAQ== -custody.fireblocks.asset_mappings=XLM_USDC_T_CEKS stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5\nXLM_TEST stellar:native -custody.fireblocks.reconciliation.cron_expression=0/10 * * * * * - -# Customize from custody config -custody_server.auth.type=api_key diff --git a/service-runner/src/main/resources/profiles/auth-apikey-custody/test.env b/service-runner/src/main/resources/profiles/auth-apikey-custody/test.env deleted file mode 100644 index 1238a8db90..0000000000 --- a/service-runner/src/main/resources/profiles/auth-apikey-custody/test.env +++ /dev/null @@ -1,3 +0,0 @@ -run.all.servers=false -run.custody.server=true -app.custodyEnabled=true diff --git a/service-runner/src/main/resources/profiles/auth-jwt-custody/config.env b/service-runner/src/main/resources/profiles/auth-jwt-custody/config.env deleted file mode 100644 index b41ed3617d..0000000000 --- a/service-runner/src/main/resources/profiles/auth-jwt-custody/config.env +++ /dev/null @@ -1,23 +0,0 @@ -# Use Horizon for custody server tests -stellar_network.rpc_url= -stellar_network.horizon_url: https://horizon-testnet.stellar.org - -secret.custody.fireblocks.api_key=testApiKey -secret.custody.fireblocks.secret_key=MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCGijeeahIvJQ+AQ7VVnp6iwj1jPDM/4cb35R6iV3O8PVmtiN2HlTP980EPM1eW7QOVjNeTq4EY4kW1XN3kcvaIXkxnkqxNZsxqbejgU1IvyQ8OSpxstNTkKZQboFexmQL0vZfhB4LYuYmFR8i/EqBMXPAZ88xas908UMRk9WyUeX7BN8RA3444oRWUuA8g0iZKqYUSEv1jMYieD5fcEl6rWTfeUVU1Ob7kog+pJU3NiaRG0Fnt4qloB3mn1VqJx4H7OEPjbKKO734Yrb7PrhsNm9S76q5ICEa3JfKqtqqpINQUh1O5tkcb+lS6ZfzfR17RS+ozxmFkZICzJG56d2pfqY89rlnWVJkpvuTlePghmH/po36XXTM3NYaGhDdKYdF/abFvIO8/5BGAI7vB/80Te2KRxbBravrd9A2GFdnarD3+nEiCSGTkIHBD3ZRF9yex+q4kt5n7F8bqR70l98Gd5ueWivr5CTW0lfLrHptr32JoiE7/9t37FCAvcFTmeVqz9uny7DZiZRFLGveIxCD5THIJHUGEVpU8EK96gcXv9YDOlrXckK2St7JfbkNDbxRrqYzl8dyZrY+BjiQxoPeaEuiSecv0mbSrzze76nbmozWi48wzO7rmeUUtb0cgacoywzaVxbv5VuM4AeTdAIMfJ+GsJrrN7is8Nd+7A5d26wIDAQABAoICAFb7l5f41gba/BmeLOfNJJzv3gaBjlTX4O62mEa3KaFjPM6ANVGKOlIOalqshA2U8QNISrwzXsS6zfzCrMcdOJzT8qvn3TQxqSmKI8ycsf8pC23e+SEjDJzy8MmnseqllY6r4TzgwUzjL7EWxwgZv79/OtBcmhtuPDPZuA/ZyLh5kGTVbDHRFz8vjidXlJhQpecRkHIuCtqN1Nj0Fed3jPNVPFSk2uUNj4h76ooeDx46xhXucKXnLEWQx6ulueJoNXY5W1L3EmY9GpZUBpB47Q6wWCqiQMlbaHTSuQB8Fvw/uD4JfC0OVw0UnKDifUnR99BzN1kAAkmI3Nh1TmQpPoDkwJ71KqkLBO8VjfawdKOB7GBNa9/f5tBFNfLwuqypMC4A8WAJvEbKhrdBNUs7hLMiSaIIvCRAHaNjiDbIf6iW/zVWm2qk87+gKQX7Cj0NMha81AWcUrT+i9sUJujrfMyA809IduzY8xXJACM446BicfUm3NA7rJacaj/bBlYtGbyuUKmuAfF9bReWOaHz+QO6tnP/YkfddRvNq2EivUKxd9+fMT9BxA6xF2vx3I2sjZso952QLUVgleyzsC/bryA4qs+jTZVwb3XiiriSNlgqBgR/pLmjInLCDR7LH9VvWcFAsLJ4aCbxalLHYnb6ZaXRY27uYuIog244cqXHxu5RAoIBAQDev9CFI9bH1pV6gss6N9sWGU1HNYODwepustMJkb0KsFalKd+WY7ZJ3yzMO21bdSAn6Q8IxHYUKR/f+63Mb9g0aXsN3YNrBVBSffg3lVvf1LrH6Ehe+E3I9vGe0cbFA4bxSh41VOoRuSUeCyINT5qZfZOmIMJ1kHBTidN7cqgaI6iJnySP5nCXHQhSVPMm4EqSiSwFsrBNKeoNGlpyZD5j+Zxxlk4SdeQv4THyYvBMbpqoBtzz8ywPr/C8KyosSZB1I1R9nacQV1e0x40gqMgD1XNa4OB0P0D9Sf7KBf5fdRqVpVxCEMGK4deujqODc1nnzHChrpQQsRW+CDD4WhjDAoIBAQCan41p6/EZgWrAIF2SuybzBZ6K9r/O8AoFGWMYDsyRv6qf9LBlbY+EvSUUZaEDJyiRLPVR3WKtYb9T+hkyQMFWvXM21JmQhkKMRgLCSdtxaFFnZ7IQKdjA3yaYm0AOV4wSCxFxYrhPGaBd8agPiGjjIaU58ex39pd9EHOe9ITiSa9kDVeahxXuwgpw/xiwXoydj2EYgsd+1ki1+sr9Z5gLEBqkSYnxhMRmtPxwudvl/2zUDDvgscFMJKQBZvCCcXyNT+5uXWOCv9PBf412+9YOuGAsho+5cnzTV162kQ62GHRhWK2Hl58/la9RlUyqjyNJJnfQB0lapiYVCkJkDQa5AoIBAQC49gfc9kR7jfhzUTYVspnOgNYFgi8hch0LLJxGfui9fm2EGgAZ7dRJBPM650HIRrqbyU99lT0DQp7AK0lGz7x00P2oJr7gV/o5dXZuGSy/8PHj20J08bQNYtiBa2mk4Gfl5gitekQe0WE70DzHVslmGLtIoD04x+Ytx+1+vVdO1Ts2g5olj1EAedAWJYn+RxJFGXgfhyAUAvDhTne6MvkHpRY9z3QmqhP/mfwSbAtjPBhZ29EPFGYK5Kp+ZU1QlwlH6z1WTVmTHSOr7mOGsf/cBxsRZFecz4VUjWPvNwjhFOktR7ES/rznaN7iiHjIRzlpW/5OkxtJvPHz6PNY1d9nAoIBAQCTm+WATMGpu6aRK1SpEwknzyF/P/f0MasfGxFCkj4wlWzprsoGygTMj1SqmCqRu/w6O1UmhdYB9uKT5JnRI4huqgUnPuYq1kEJcHyJxcA8D35G3PNcAtbtqRpLbUpFZsZNI0vPlpKk0K4LgPsTeQHIcwIswwsfqsULm30FLiD9daJ+PU54MqV5fLxDCwQiuuA6OfpTT+Xq694V/Al0yESZqxID7EBH1Js6AWVMp+GJO8JE3Dj9VDRX4FLBE+heXsCslrXjnju1QjOA8ae3Asti2hjV6M+kFeucRjggyXHl3Iqds+5VFyXrqZXoqEK8QFFLL/IJIiug4iRQya5/nP/hAoIBAFSELSTQ/KElxQvy6vHr9fjfMiRGcTmsNj2v+zHN9OLmG9c2xBOJcM4a5lCt4FrqgeMMsrOVWSfvwVz2edwCqNPcO3wz9/l9EdKrIL9MPQK9SUxMWl1hjW2Yni2I9wkMsTivszqEVLVe9EQMs1WP2Tb2ijc/Otx6jUpxP73fNjI/p9FBiM3pV1Pt2YtmjQLecG5SYkgUbIGW3iEmvkdIfK5z6Vdm7LCJPADOP3OV1xd+wpJMdnePww1Mtp3Kf8ZPrTHL8t2U2kV+sW0OjrRabxF79izjhYs2KQdKoyptX/pXXPIg12Hnt5YNwVJfThpqSWVj83AVOX9ZlGqwtiNOLk4= - -# seps -sep6.deposit_info_generator_type=custody -sep24.deposit_info_generator_type=custody -sep31.deposit_info_generator_type=custody - -# custody -custody.type=fireblocks -custody.fireblocks.vault_account_id=1 -custody.fireblocks.public_key=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAhoo3nmoSLyUPgEO1VZ6eosI9YzwzP+HG9+UeoldzvD1ZrYjdh5Uz/fNBDzNXlu0DlYzXk6uBGOJFtVzd5HL2iF5MZ5KsTWbMam3o4FNSL8kPDkqcbLTU5CmUG6BXsZkC9L2X4QeC2LmJhUfIvxKgTFzwGfPMWrPdPFDEZPVslHl+wTfEQN+OOKEVlLgPINImSqmFEhL9YzGIng+X3BJeq1k33lFVNTm+5KIPqSVNzYmkRtBZ7eKpaAd5p9VaiceB+zhD42yiju9+GK2+z64bDZvUu+quSAhGtyXyqraqqSDUFIdTubZHG/pUumX830de0UvqM8ZhZGSAsyRuendqX6mPPa5Z1lSZKb7k5Xj4IZh/6aN+l10zNzWGhoQ3SmHRf2mxbyDvP+QRgCO7wf/NE3tikcWwa2r63fQNhhXZ2qw9/pxIgkhk5CBwQ92URfcnsfquJLeZ+xfG6ke9JffBnebnlor6+Qk1tJXy6x6ba99iaIhO//bd+xQgL3BU5nlas/bp8uw2YmURSxr3iMQg+UxyCR1BhFaVPBCveoHF7/WAzpa13JCtkreyX25DQ28Ua6mM5fHcma2PgY4kMaD3mhLoknnL9Jm0q883u+p25qM1ouPMMzu65nlFLW9HIGnKMsM2lcW7+VbjOAHk3QCDHyfhrCa6ze4rPDXfuwOXdusCAwEAAQ== -custody.fireblocks.asset_mappings=XLM_USDC_T_CEKS stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5\nXLM_TEST stellar:native -custody.fireblocks.reconciliation.cron_expression=0/10 * * * * * - -# Customize from custody config -run.all.servers=false -run.custody.server=true -custody_server.auth.type=jwt diff --git a/service-runner/src/main/resources/profiles/auth-jwt-custody/test.env b/service-runner/src/main/resources/profiles/auth-jwt-custody/test.env deleted file mode 100644 index 1238a8db90..0000000000 --- a/service-runner/src/main/resources/profiles/auth-jwt-custody/test.env +++ /dev/null @@ -1,3 +0,0 @@ -run.all.servers=false -run.custody.server=true -app.custodyEnabled=true diff --git a/service-runner/src/main/resources/profiles/custody/config.env b/service-runner/src/main/resources/profiles/custody/config.env deleted file mode 100644 index 8a3bd82369..0000000000 --- a/service-runner/src/main/resources/profiles/custody/config.env +++ /dev/null @@ -1,20 +0,0 @@ -# Use Horizon for custody server tests -stellar_network.rpc_url= -stellar_network.horizon_url: https://horizon-testnet.stellar.org - -secret.custody.fireblocks.api_key=testApiKey -secret.custody.fireblocks.secret_key=MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCGijeeahIvJQ+AQ7VVnp6iwj1jPDM/4cb35R6iV3O8PVmtiN2HlTP980EPM1eW7QOVjNeTq4EY4kW1XN3kcvaIXkxnkqxNZsxqbejgU1IvyQ8OSpxstNTkKZQboFexmQL0vZfhB4LYuYmFR8i/EqBMXPAZ88xas908UMRk9WyUeX7BN8RA3444oRWUuA8g0iZKqYUSEv1jMYieD5fcEl6rWTfeUVU1Ob7kog+pJU3NiaRG0Fnt4qloB3mn1VqJx4H7OEPjbKKO734Yrb7PrhsNm9S76q5ICEa3JfKqtqqpINQUh1O5tkcb+lS6ZfzfR17RS+ozxmFkZICzJG56d2pfqY89rlnWVJkpvuTlePghmH/po36XXTM3NYaGhDdKYdF/abFvIO8/5BGAI7vB/80Te2KRxbBravrd9A2GFdnarD3+nEiCSGTkIHBD3ZRF9yex+q4kt5n7F8bqR70l98Gd5ueWivr5CTW0lfLrHptr32JoiE7/9t37FCAvcFTmeVqz9uny7DZiZRFLGveIxCD5THIJHUGEVpU8EK96gcXv9YDOlrXckK2St7JfbkNDbxRrqYzl8dyZrY+BjiQxoPeaEuiSecv0mbSrzze76nbmozWi48wzO7rmeUUtb0cgacoywzaVxbv5VuM4AeTdAIMfJ+GsJrrN7is8Nd+7A5d26wIDAQABAoICAFb7l5f41gba/BmeLOfNJJzv3gaBjlTX4O62mEa3KaFjPM6ANVGKOlIOalqshA2U8QNISrwzXsS6zfzCrMcdOJzT8qvn3TQxqSmKI8ycsf8pC23e+SEjDJzy8MmnseqllY6r4TzgwUzjL7EWxwgZv79/OtBcmhtuPDPZuA/ZyLh5kGTVbDHRFz8vjidXlJhQpecRkHIuCtqN1Nj0Fed3jPNVPFSk2uUNj4h76ooeDx46xhXucKXnLEWQx6ulueJoNXY5W1L3EmY9GpZUBpB47Q6wWCqiQMlbaHTSuQB8Fvw/uD4JfC0OVw0UnKDifUnR99BzN1kAAkmI3Nh1TmQpPoDkwJ71KqkLBO8VjfawdKOB7GBNa9/f5tBFNfLwuqypMC4A8WAJvEbKhrdBNUs7hLMiSaIIvCRAHaNjiDbIf6iW/zVWm2qk87+gKQX7Cj0NMha81AWcUrT+i9sUJujrfMyA809IduzY8xXJACM446BicfUm3NA7rJacaj/bBlYtGbyuUKmuAfF9bReWOaHz+QO6tnP/YkfddRvNq2EivUKxd9+fMT9BxA6xF2vx3I2sjZso952QLUVgleyzsC/bryA4qs+jTZVwb3XiiriSNlgqBgR/pLmjInLCDR7LH9VvWcFAsLJ4aCbxalLHYnb6ZaXRY27uYuIog244cqXHxu5RAoIBAQDev9CFI9bH1pV6gss6N9sWGU1HNYODwepustMJkb0KsFalKd+WY7ZJ3yzMO21bdSAn6Q8IxHYUKR/f+63Mb9g0aXsN3YNrBVBSffg3lVvf1LrH6Ehe+E3I9vGe0cbFA4bxSh41VOoRuSUeCyINT5qZfZOmIMJ1kHBTidN7cqgaI6iJnySP5nCXHQhSVPMm4EqSiSwFsrBNKeoNGlpyZD5j+Zxxlk4SdeQv4THyYvBMbpqoBtzz8ywPr/C8KyosSZB1I1R9nacQV1e0x40gqMgD1XNa4OB0P0D9Sf7KBf5fdRqVpVxCEMGK4deujqODc1nnzHChrpQQsRW+CDD4WhjDAoIBAQCan41p6/EZgWrAIF2SuybzBZ6K9r/O8AoFGWMYDsyRv6qf9LBlbY+EvSUUZaEDJyiRLPVR3WKtYb9T+hkyQMFWvXM21JmQhkKMRgLCSdtxaFFnZ7IQKdjA3yaYm0AOV4wSCxFxYrhPGaBd8agPiGjjIaU58ex39pd9EHOe9ITiSa9kDVeahxXuwgpw/xiwXoydj2EYgsd+1ki1+sr9Z5gLEBqkSYnxhMRmtPxwudvl/2zUDDvgscFMJKQBZvCCcXyNT+5uXWOCv9PBf412+9YOuGAsho+5cnzTV162kQ62GHRhWK2Hl58/la9RlUyqjyNJJnfQB0lapiYVCkJkDQa5AoIBAQC49gfc9kR7jfhzUTYVspnOgNYFgi8hch0LLJxGfui9fm2EGgAZ7dRJBPM650HIRrqbyU99lT0DQp7AK0lGz7x00P2oJr7gV/o5dXZuGSy/8PHj20J08bQNYtiBa2mk4Gfl5gitekQe0WE70DzHVslmGLtIoD04x+Ytx+1+vVdO1Ts2g5olj1EAedAWJYn+RxJFGXgfhyAUAvDhTne6MvkHpRY9z3QmqhP/mfwSbAtjPBhZ29EPFGYK5Kp+ZU1QlwlH6z1WTVmTHSOr7mOGsf/cBxsRZFecz4VUjWPvNwjhFOktR7ES/rznaN7iiHjIRzlpW/5OkxtJvPHz6PNY1d9nAoIBAQCTm+WATMGpu6aRK1SpEwknzyF/P/f0MasfGxFCkj4wlWzprsoGygTMj1SqmCqRu/w6O1UmhdYB9uKT5JnRI4huqgUnPuYq1kEJcHyJxcA8D35G3PNcAtbtqRpLbUpFZsZNI0vPlpKk0K4LgPsTeQHIcwIswwsfqsULm30FLiD9daJ+PU54MqV5fLxDCwQiuuA6OfpTT+Xq694V/Al0yESZqxID7EBH1Js6AWVMp+GJO8JE3Dj9VDRX4FLBE+heXsCslrXjnju1QjOA8ae3Asti2hjV6M+kFeucRjggyXHl3Iqds+5VFyXrqZXoqEK8QFFLL/IJIiug4iRQya5/nP/hAoIBAFSELSTQ/KElxQvy6vHr9fjfMiRGcTmsNj2v+zHN9OLmG9c2xBOJcM4a5lCt4FrqgeMMsrOVWSfvwVz2edwCqNPcO3wz9/l9EdKrIL9MPQK9SUxMWl1hjW2Yni2I9wkMsTivszqEVLVe9EQMs1WP2Tb2ijc/Otx6jUpxP73fNjI/p9FBiM3pV1Pt2YtmjQLecG5SYkgUbIGW3iEmvkdIfK5z6Vdm7LCJPADOP3OV1xd+wpJMdnePww1Mtp3Kf8ZPrTHL8t2U2kV+sW0OjrRabxF79izjhYs2KQdKoyptX/pXXPIg12Hnt5YNwVJfThpqSWVj83AVOX9ZlGqwtiNOLk4= - -# seps -sep6.deposit_info_generator_type=custody -sep24.deposit_info_generator_type=custody -sep31.deposit_info_generator_type=custody -sep45.enabled=false - -# custody -custody.type=fireblocks -custody.fireblocks.base_url=http://localhost:58086/ -custody.fireblocks.vault_account_id=1 -custody.fireblocks.public_key=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAhoo3nmoSLyUPgEO1VZ6eosI9YzwzP+HG9+UeoldzvD1ZrYjdh5Uz/fNBDzNXlu0DlYzXk6uBGOJFtVzd5HL2iF5MZ5KsTWbMam3o4FNSL8kPDkqcbLTU5CmUG6BXsZkC9L2X4QeC2LmJhUfIvxKgTFzwGfPMWrPdPFDEZPVslHl+wTfEQN+OOKEVlLgPINImSqmFEhL9YzGIng+X3BJeq1k33lFVNTm+5KIPqSVNzYmkRtBZ7eKpaAd5p9VaiceB+zhD42yiju9+GK2+z64bDZvUu+quSAhGtyXyqraqqSDUFIdTubZHG/pUumX830de0UvqM8ZhZGSAsyRuendqX6mPPa5Z1lSZKb7k5Xj4IZh/6aN+l10zNzWGhoQ3SmHRf2mxbyDvP+QRgCO7wf/NE3tikcWwa2r63fQNhhXZ2qw9/pxIgkhk5CBwQ92URfcnsfquJLeZ+xfG6ke9JffBnebnlor6+Qk1tJXy6x6ba99iaIhO//bd+xQgL3BU5nlas/bp8uw2YmURSxr3iMQg+UxyCR1BhFaVPBCveoHF7/WAzpa13JCtkreyX25DQ28Ua6mM5fHcma2PgY4kMaD3mhLoknnL9Jm0q883u+p25qM1ouPMMzu65nlFLW9HIGnKMsM2lcW7+VbjOAHk3QCDHyfhrCa6ze4rPDXfuwOXdusCAwEAAQ== -custody.fireblocks.asset_mappings=XLM_USDC_T_CEKS stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5\nXLM_TEST stellar:native -custody.fireblocks.reconciliation.cron_expression=0/10 * * * * * diff --git a/service-runner/src/main/resources/profiles/custody/test.env b/service-runner/src/main/resources/profiles/custody/test.env deleted file mode 100644 index 1ef48514ea..0000000000 --- a/service-runner/src/main/resources/profiles/custody/test.env +++ /dev/null @@ -1,2 +0,0 @@ -run.custody.server=true -app.custodyEnabled=true