Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ANCHOR-508] SEP-6: Custody integration #1179

Merged
merged 4 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/sub_gradle_test_and_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:

# Prepare Stellar Validation Tests
- name: Pull Stellar Validation Tests Docker Image
run: docker pull stellar/anchor-tests:v0.6.7 &
run: docker pull stellar/anchor-tests:v0.6.9 &

# Set up JDK 11
- name: Set up JDK 11
Expand Down Expand Up @@ -109,7 +109,7 @@ jobs:

- name: Run Stellar validation tool
run: |
docker run --network host -v ${GITHUB_WORKSPACE}/platform/src/test/resources://config stellar/anchor-tests:v0.6.7 --home-domain http://host.docker.internal:8080 --seps 1 6 10 12 24 31 38 --sep-config //config/stellar-anchor-tests-sep-config.json --verbose
docker run --network host -v ${GITHUB_WORKSPACE}/platform/src/test/resources://config stellar/anchor-tests:v0.6.9 --home-domain http://host.docker.internal:8080 --seps 1 6 10 12 24 31 38 --sep-config //config/stellar-anchor-tests-sep-config.json --verbose

- name: Upload Artifacts
if: always()
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/org/stellar/anchor/config/Sep6Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public interface Sep6Config {

Features getFeatures();

DepositInfoGeneratorType getDepositInfoGeneratorType();

@Getter
@Setter
@AllArgsConstructor
Expand All @@ -22,4 +24,10 @@ class Features {
@SerializedName("claimable_balances")
boolean claimableBalances;
}

enum DepositInfoGeneratorType {
SELF,
CUSTODY,
NONE
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,18 @@
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
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.stellar.anchor.sep6;

import org.stellar.anchor.api.exception.AnchorException;
import org.stellar.anchor.api.shared.SepDepositInfo;

public interface Sep6DepositInfoGenerator {

/**
* Gets the deposit info based on the input parameter.
*
* @param txn the original SEP-6 transaction the deposit info will be used for.
* @return a SepDepositInfo instance containing the destination address, memo and memoType.
* @throws AnchorException if the deposit info cannot be generated
*/
SepDepositInfo generate(Sep6Transaction txn) throws AnchorException;
}
22 changes: 2 additions & 20 deletions core/src/main/java/org/stellar/anchor/sep6/Sep6Service.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.stellar.anchor.sep6;

import static org.stellar.anchor.util.MemoHelper.*;
import static org.stellar.sdk.xdr.MemoType.MEMO_HASH;

import com.google.common.collect.ImmutableMap;
import java.time.Instant;
Expand Down Expand Up @@ -190,7 +189,6 @@ public StartDepositResponse depositExchange(Sep10Jwt token, StartDepositExchange
.amountFee(amounts.getAmountFee())
.amountFeeAsset(amounts.getAmountFeeAsset())
.amountExpected(request.getAmount())
.amountExpected(request.getAmount())
.startedAt(Instant.now())
.sep10Account(token.getAccount())
.sep10AccountMemo(token.getAccountMemo())
Expand Down Expand Up @@ -270,10 +268,7 @@ public StartWithdrawResponse withdraw(Sep10Jwt token, StartWithdrawRequest reque
.startedAt(Instant.now())
.sep10Account(token.getAccount())
.sep10AccountMemo(token.getAccountMemo())
.memo(generateMemo(id))
.memoType(memoTypeAsString(MEMO_HASH))
.fromAccount(sourceAccount)
.withdrawAnchorAccount(asset.getDistributionAccount())
.refundMemo(request.getRefundMemo())
.refundMemoType(request.getRefundMemoType());

Expand All @@ -288,12 +283,7 @@ public StartWithdrawResponse withdraw(Sep10Jwt token, StartWithdrawRequest reque
.transaction(TransactionHelper.toGetTransactionResponse(txn, assetService))
.build());

return StartWithdrawResponse.builder()
.accountId(asset.getDistributionAccount())
.id(txn.getId())
.memo(txn.getMemo())
.memoType(memoTypeAsString(MEMO_HASH))
.build();
return StartWithdrawResponse.builder().id(txn.getId()).build();
}

public StartWithdrawResponse withdrawExchange(
Expand Down Expand Up @@ -364,10 +354,7 @@ public StartWithdrawResponse withdrawExchange(
.startedAt(Instant.now())
.sep10Account(token.getAccount())
.sep10AccountMemo(token.getAccountMemo())
.memo(generateMemo(id))
.memoType(memoTypeAsString(MEMO_HASH))
.fromAccount(sourceAccount)
.withdrawAnchorAccount(sellAsset.getDistributionAccount())
.refundMemo(request.getRefundMemo())
.refundMemoType(request.getRefundMemoType())
.quoteId(request.getQuoteId());
Expand All @@ -383,12 +370,7 @@ public StartWithdrawResponse withdrawExchange(
.transaction(TransactionHelper.toGetTransactionResponse(txn, assetService))
.build());

return StartWithdrawResponse.builder()
.accountId(sellAsset.getDistributionAccount())
.id(txn.getId())
.memo(txn.getMemo())
.memoType(memoTypeAsString(MEMO_HASH))
.build();
return StartWithdrawResponse.builder().id(txn.getId()).build();
}

public GetTransactionsResponse findTransactions(Sep10Jwt token, GetTransactionsRequest request)
Expand Down
7 changes: 0 additions & 7 deletions core/src/main/java/org/stellar/anchor/util/MemoHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.util.Base64;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.stellar.anchor.api.exception.SepException;
import org.stellar.anchor.api.exception.SepValidationException;
import org.stellar.sdk.*;
Expand Down Expand Up @@ -126,10 +125,4 @@ public static String memoAsString(Memo memo) throws SepException {
throw new SepException("Unsupported value: " + memoTypeStr);
}
}

public static String generateMemo(String transactionId) {
String memo = StringUtils.truncate(transactionId, 32);
memo = StringUtils.leftPad(memo, 32, "0");
return new String(Base64.getEncoder().encode(memo.getBytes()));
}
}
32 changes: 29 additions & 3 deletions core/src/main/java/org/stellar/anchor/util/TransactionHelper.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package org.stellar.anchor.util;

import static org.stellar.anchor.api.platform.PlatformTransactionData.Kind.DEPOSIT;
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.api.platform.PlatformTransactionData.Kind.*;

import com.google.common.collect.ImmutableSet;
import java.util.Optional;
import javax.annotation.Nullable;
import org.stellar.anchor.api.custody.CreateCustodyTransactionRequest;
Expand All @@ -22,6 +21,33 @@

public class TransactionHelper {

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())
Expand Down
16 changes: 0 additions & 16 deletions core/src/test/kotlin/org/stellar/anchor/sep6/Sep6ServiceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -657,8 +657,6 @@ class Sep6ServiceTest {
JSONCompareMode.LENIENT
)
assert(slotTxn.captured.id.isNotEmpty())
assert(slotTxn.captured.memo.isNotEmpty())
assertEquals(slotTxn.captured.memoType, "hash")
assertNotNull(slotTxn.captured.startedAt)

JSONAssert.assertEquals(
Expand All @@ -668,8 +666,6 @@ class Sep6ServiceTest {
)
assert(slotEvent.captured.id.isNotEmpty())
assert(slotEvent.captured.transaction.id.isNotEmpty())
assert(slotEvent.captured.transaction.memo.isNotEmpty())
assertEquals(slotEvent.captured.transaction.memoType, "hash")
assertNotNull(slotEvent.captured.transaction.startedAt)

// Verify response
Expand Down Expand Up @@ -738,8 +734,6 @@ class Sep6ServiceTest {
JSONCompareMode.LENIENT
)
assert(slotTxn.captured.id.isNotEmpty())
assert(slotTxn.captured.memo.isNotEmpty())
assertEquals(slotTxn.captured.memoType, "hash")
assertNotNull(slotTxn.captured.startedAt)

JSONAssert.assertEquals(
Expand All @@ -749,8 +743,6 @@ class Sep6ServiceTest {
)
assert(slotEvent.captured.id.isNotEmpty())
assert(slotEvent.captured.transaction.id.isNotEmpty())
assert(slotEvent.captured.transaction.memo.isNotEmpty())
assertEquals(slotEvent.captured.transaction.memoType, "hash")
assertNotNull(slotEvent.captured.transaction.startedAt)

// Verify response
Expand Down Expand Up @@ -945,8 +937,6 @@ class Sep6ServiceTest {
JSONCompareMode.LENIENT
)
assert(slotTxn.captured.id.isNotEmpty())
assert(slotTxn.captured.memo.isNotEmpty())
assertEquals(slotTxn.captured.memoType, "hash")
assertNotNull(slotTxn.captured.startedAt)

JSONAssert.assertEquals(
Expand All @@ -956,8 +946,6 @@ class Sep6ServiceTest {
)
assert(slotEvent.captured.id.isNotEmpty())
assert(slotEvent.captured.transaction.id.isNotEmpty())
assert(slotEvent.captured.transaction.memo.isNotEmpty())
assertEquals(slotEvent.captured.transaction.memoType, "hash")
assertNotNull(slotEvent.captured.transaction.startedAt)

// Verify response
Expand Down Expand Up @@ -1018,8 +1006,6 @@ class Sep6ServiceTest {
JSONCompareMode.LENIENT
)
assert(slotTxn.captured.id.isNotEmpty())
assert(slotTxn.captured.memo.isNotEmpty())
assertEquals(slotTxn.captured.memoType, "hash")
assertNotNull(slotTxn.captured.startedAt)

JSONAssert.assertEquals(
Expand All @@ -1029,8 +1015,6 @@ class Sep6ServiceTest {
)
assert(slotEvent.captured.id.isNotEmpty())
assert(slotEvent.captured.transaction.id.isNotEmpty())
assert(slotEvent.captured.transaction.memo.isNotEmpty())
assertEquals(slotEvent.captured.transaction.memoType, "hash")
assertNotNull(slotEvent.captured.transaction.startedAt)

// Verify response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,10 +388,8 @@ class Sep6ServiceTestData {
val withdrawResJson =
"""
{
"account_id": "GA7FYRB5VREZKOBIIKHG5AVTPFGWUBPOBF7LTYG4GTMFVIOOD2DWAL7I",
"memo_type": "hash"
}
"""
"""
.trimIndent()

val withdrawTxnJson =
Expand All @@ -411,9 +409,7 @@ class Sep6ServiceTestData {
"amountExpected": "100",
"sep10Account": "GBLGJA4TUN5XOGTV6WO2BWYUI2OZR5GYQ5PDPCRMQ5XEPJOYWB2X4CJO",
"sep10AccountMemo": "123",
"withdrawAnchorAccount": "GA7FYRB5VREZKOBIIKHG5AVTPFGWUBPOBF7LTYG4GTMFVIOOD2DWAL7I",
"fromAccount": "GBLGJA4TUN5XOGTV6WO2BWYUI2OZR5GYQ5PDPCRMQ5XEPJOYWB2X4CJO",
"memoType": "hash",
"refundMemo": "some text",
"refundMemoType": "text"
}
Expand Down Expand Up @@ -446,7 +442,6 @@ class Sep6ServiceTestData {
"asset": "stellar:USDC:GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP"
},
"source_account": "GBLGJA4TUN5XOGTV6WO2BWYUI2OZR5GYQ5PDPCRMQ5XEPJOYWB2X4CJO",
"memo_type": "hash",
"refund_memo": "some text",
"refund_memo_type": "text",
"customers": {
Expand Down Expand Up @@ -477,9 +472,7 @@ class Sep6ServiceTestData {
"amountFeeAsset": "stellar:USDC:GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP",
"sep10Account": "GBLGJA4TUN5XOGTV6WO2BWYUI2OZR5GYQ5PDPCRMQ5XEPJOYWB2X4CJO",
"sep10AccountMemo": "123",
"withdrawAnchorAccount": "GA7FYRB5VREZKOBIIKHG5AVTPFGWUBPOBF7LTYG4GTMFVIOOD2DWAL7I",
"fromAccount": "GBLGJA4TUN5XOGTV6WO2BWYUI2OZR5GYQ5PDPCRMQ5XEPJOYWB2X4CJO",
"memoType": "hash",
"refundMemo": "some text",
"refundMemoType": "text"
}
Expand All @@ -503,7 +496,6 @@ class Sep6ServiceTestData {
"asset": "stellar:USDC:GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP"
},
"source_account": "GBLGJA4TUN5XOGTV6WO2BWYUI2OZR5GYQ5PDPCRMQ5XEPJOYWB2X4CJO",
"memo_type": "hash",
"refund_memo": "some text",
"refund_memo_type": "text",
"customers": {
Expand Down Expand Up @@ -538,9 +530,7 @@ class Sep6ServiceTestData {
"amountExpected": "100",
"sep10Account": "GBLGJA4TUN5XOGTV6WO2BWYUI2OZR5GYQ5PDPCRMQ5XEPJOYWB2X4CJO",
"sep10AccountMemo": "123",
"withdrawAnchorAccount": "GA7FYRB5VREZKOBIIKHG5AVTPFGWUBPOBF7LTYG4GTMFVIOOD2DWAL7I",
"fromAccount": "GBLGJA4TUN5XOGTV6WO2BWYUI2OZR5GYQ5PDPCRMQ5XEPJOYWB2X4CJO",
"memoType": "hash",
"quoteId": "test-quote-id",
"refundMemo": "some text",
"refundMemoType": "text"
Expand Down Expand Up @@ -569,7 +559,6 @@ class Sep6ServiceTestData {
"amount_fee": { "amount": "2", "asset": "iso4217:USD" },
"quote_id": "test-quote-id",
"source_account": "GBLGJA4TUN5XOGTV6WO2BWYUI2OZR5GYQ5PDPCRMQ5XEPJOYWB2X4CJO",
"memo_type": "hash",
"refund_memo": "some text",
"refund_memo_type": "text",
"customers": {
Expand Down Expand Up @@ -604,9 +593,7 @@ class Sep6ServiceTestData {
"amountExpected": "100",
"sep10Account": "GBLGJA4TUN5XOGTV6WO2BWYUI2OZR5GYQ5PDPCRMQ5XEPJOYWB2X4CJO",
"sep10AccountMemo": "123",
"withdrawAnchorAccount": "GA7FYRB5VREZKOBIIKHG5AVTPFGWUBPOBF7LTYG4GTMFVIOOD2DWAL7I",
"fromAccount": "GBLGJA4TUN5XOGTV6WO2BWYUI2OZR5GYQ5PDPCRMQ5XEPJOYWB2X4CJO",
"memoType": "hash",
"refundMemo": "some text",
"refundMemoType": "text"
}
Expand Down Expand Up @@ -637,7 +624,6 @@ class Sep6ServiceTestData {
"asset": "stellar:USDC:GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP"
},
"source_account": "GBLGJA4TUN5XOGTV6WO2BWYUI2OZR5GYQ5PDPCRMQ5XEPJOYWB2X4CJO",
"memo_type": "hash",
"refund_memo": "some text",
"refund_memo_type": "text",
"customers": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,12 @@ class AnchorPlatformCustodyEnd2EndTest :
fun runSep24Test() {
singleton.sep24CustodyE2eTests.testAll()
}

@Test
@Order(11)
fun runSep6Test() {
// The SEP-6 reference server implementation only implements RPC, so technically this test
// should be in the RPC test suite.
singleton.sep6E2eTests.testAll()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class AnchorPlatformEnd2EndTest : AbstractIntegrationTest(TestConfig(testProfile
@Test
@Order(2)
fun runSep6Test() {
// The SEP-6 reference server implementation only implements RPC, so technically this test
// should be in the RPC test suite.
singleton.sep6E2eTests.testAll()
}
}
Loading
Loading