Skip to content

Commit

Permalink
Add memo to sep-10 and sep-24
Browse files Browse the repository at this point in the history
  • Loading branch information
lijamie98 committed Jun 2, 2022
1 parent 92ac66b commit a1c23ab
Show file tree
Hide file tree
Showing 17 changed files with 129 additions and 54 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ subprojects {
version = "0.1.0"

repositories {
mavenLocal()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ public class PropertySep10Config implements Sep10Config {
private Integer jwtTimeout = 86400;
private List<String> clientAttributionDenyList;
private List<String> clientAttributionAllowList;
private List<String> omnibusAccountList;
}
2 changes: 1 addition & 1 deletion core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
id("org.jetbrains.kotlin.jvm") version "1.6.10"
}

version = "1.0.5"
version = "1.0.8"

dependencies {
compileOnly(libs.servlet.api)
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/org/stellar/anchor/config/Sep10Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ public interface Sep10Config {
List<String> getClientAttributionDenyList();

List<String> getClientAttributionAllowList();

List<String> getOmnibusAccountList();
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public interface Sep24Transaction {

String getStellarAccount();

// The account from the JWT.
void setStellarAccount(String stellarAccount);

String getReceivingAnchorAccount();
Expand Down Expand Up @@ -106,6 +107,7 @@ public interface Sep24Transaction {

void setAmountFeeAsset(String amountFeeAsset);

// From JWT account memo
String getAccountMemo();

void setAccountMemo(String accountMemo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ public Sep24TransactionBuilder stellarAccount(String stellarAccount) {
return this;
}

public Sep24TransactionBuilder stellarAccountMemo(String accountMemo) {
txn.setAccountMemo(accountMemo);
return this;
}

public Sep24TransactionBuilder receivingAnchorAccount(String receivingAnchorAccount) {
txn.setReceivingAnchorAccount(receivingAnchorAccount);
return this;
Expand Down
14 changes: 10 additions & 4 deletions core/src/main/java/org/stellar/anchor/sep10/JwtToken.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public JwtToken() {}
long iat;
long exp;
String jti;
String account;

@SerializedName(value = "client_domain")
String clientDomain;
Expand All @@ -23,10 +24,6 @@ public JwtToken() {}
@SerializedName(value = "muxed_account")
String muxedAccount;

public String getAccount() {
return this.sub;
}

public String getTransactionId() {
return this.jti;
}
Expand All @@ -52,6 +49,15 @@ public static JwtToken of(
token.exp = exp;
token.jti = jti;
token.clientDomain = clientDomain;

String[] subs = sub.split(":");
if (subs.length == 2) {
token.account = subs[0];
token.accountMemo = subs[1];
} else {
token.account = sub;
token.accountMemo = null;
}
return token;
}
}
37 changes: 24 additions & 13 deletions core/src/main/java/org/stellar/anchor/sep10/Sep10Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,16 @@ public ChallengeResponse createChallenge(ChallengeRequest challengeRequest) thro
String.format("home_domain [%s] is not supported.", challengeRequest.getHomeDomain()));
}

if (sep10Config.isClientAttributionRequired()) {
boolean omnibusWallet =
sep10Config.getOmnibusAccountList().contains(challengeRequest.getAccount().trim());
// if (omnibusWallet) {
// if (challengeRequest.getClientDomain() != null) {
// throw new SepValidationException(
// "client_domain must not be specified if the account is an omni-wallet account");
// }
// }

if (!omnibusWallet && sep10Config.isClientAttributionRequired()) {
if (challengeRequest.getClientDomain() == null) {
infoF("ALERT: client domain required and not provided");
throw new SepValidationException("client_domain is required");
Expand Down Expand Up @@ -90,13 +99,15 @@ public ChallengeResponse createChallenge(ChallengeRequest challengeRequest) thro
}

// Validate memo. It should be 64-bit positive integer if not null.
Memo memo = null;
try {
if (challengeRequest.getMemo() != null) {
int memoInt = Integer.parseInt(challengeRequest.getMemo());
if (memoInt <= 0) {
throw new SepValidationException(
String.format("Invalid memo value: %s", challengeRequest.getMemo()));
}
memo = new MemoId(memoInt);
}
} catch (NumberFormatException e) {
throw new SepValidationException(
Expand Down Expand Up @@ -125,7 +136,8 @@ public ChallengeResponse createChallenge(ChallengeRequest challengeRequest) thro
(challengeRequest.getClientDomain() == null)
? ""
: challengeRequest.getClientDomain(),
(clientSigningKey == null) ? "" : clientSigningKey);
(clientSigningKey == null) ? "" : clientSigningKey,
memo);
// Convert the challenge to response
return ChallengeResponse.of(
txn.toEnvelopeXdrBase64(), appConfig.getStellarNetworkPassphrase());
Expand All @@ -144,14 +156,8 @@ public ValidationResponse validateChallenge(ValidationRequest validationRequest)
throw new SepValidationException("{transaction} is required.");
}

String clientDomain = validateChallenge(validationRequest.getTransaction());
return ValidationResponse.of(
generateSep10Jwt(validationRequest.getTransaction(), clientDomain));
}

public String validateChallenge(String challengeXdr)
throws IOException, InvalidSep10ChallengeException, URISyntaxException {
Log.info("Parse challenge string.");
Log.debug("Parse challenge string.");
String challengeXdr = validationRequest.getTransaction();
Sep10Challenge.ChallengeTransaction challenge =
Sep10Challenge.readChallengeTransaction(
challengeXdr,
Expand Down Expand Up @@ -210,7 +216,8 @@ public String validateChallenge(String challengeXdr)
getDomainFromURI(appConfig.getHostUrl()),
signers);

return clientDomain;
return ValidationResponse.of(
generateSep10Jwt(validationRequest.getTransaction(), clientDomain));
}

// Find the signers of the client account.
Expand All @@ -237,7 +244,8 @@ public String validateChallenge(String challengeXdr)
threshold,
signers);

return clientDomain;
return ValidationResponse.of(
generateSep10Jwt(validationRequest.getTransaction(), clientDomain));
}

String getClientAccountId(String clientDomain) throws SepException {
Expand Down Expand Up @@ -274,10 +282,13 @@ String generateSep10Jwt(String challengeXdr, String clientDomain)
sep10Config.getHomeDomain(),
getDomainFromURI(appConfig.getHostUrl()));
long issuedAt = challenge.getTransaction().getTimeBounds().getMinTime();
Memo memo = challenge.getTransaction().getMemo();
JwtToken jwtToken =
JwtToken.of(
appConfig.getHostUrl() + "/auth",
challenge.getClientAccountId(),
(memo == null)
? challenge.getClientAccountId()
: challenge.getClientAccountId() + ":" + memo,
issuedAt,
issuedAt + sep10Config.getJwtTimeout(),
challenge.getTransaction().hashHex(),
Expand Down
56 changes: 35 additions & 21 deletions core/src/main/java/org/stellar/anchor/sep24/Sep24Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import static org.stellar.anchor.model.Sep24Transaction.Kind.WITHDRAWAL;
import static org.stellar.anchor.util.Log.errorEx;
import static org.stellar.anchor.util.Log.shorter;
import static org.stellar.anchor.util.SepUtil.memoType;
import static org.stellar.anchor.util.SepUtil.memoTypeString;
import static org.stellar.sdk.xdr.MemoType.MEMO_ID;
import static org.stellar.sdk.xdr.MemoType.*;

import java.io.IOException;
import java.math.BigDecimal;
Expand Down Expand Up @@ -87,10 +88,6 @@ public InteractiveTransactionResponse withdraw(
throw new SepValidationException("'account' is required");
}

if (!sourceAccount.equals(token.getAccount())) {
throw new SepValidationException("'account' does not match the one in the token");
}

// Verify that the asset code exists in our database, with withdraw enabled.
AssetResponse asset = assetService.getAsset(assetCode, assetIssuer);
if (asset == null || !asset.getWithdraw().getEnabled() || !asset.getSep24Enabled()) {
Expand All @@ -114,23 +111,31 @@ public InteractiveTransactionResponse withdraw(
throw new SepValidationException(String.format("invalid account: %s", sourceAccount), ex);
}

Memo memo = makeMemo(withdrawRequest.get("memo"), withdrawRequest.get("memo_type"));

String txnId = UUID.randomUUID().toString();
Sep24Transaction txn =
Sep24TransactionBuilder builder =
new Sep24TransactionBuilder(txnStore)
.transactionId(txnId)
.status(Sep24Transaction.Status.INCOMPLETE.toString())
.kind(Sep24Transaction.Kind.WITHDRAWAL.toString())
.kind(WITHDRAWAL.toString())
.amountIn(strAmount)
.amountOut(strAmount)
.assetCode(assetCode)
.assetIssuer(withdrawRequest.get("asset_issuer"))
.startedAt(Instant.now().getEpochSecond())
.stellarAccount(token.getAccount())
.stellarAccountMemo(token.getAccountMemo())
.fromAccount(sourceAccount)
.protocol(Sep24Transaction.Protocol.SEP24.toString())
.memoType(memoTypeString(MEMO_ID))
.domainClient(token.getClientDomain())
.build();
.domainClient(token.getClientDomain());

if (memo != null) {
builder.memo(memo.toString());
builder.memoType(memoTypeString(memoType(memo)));
}

Sep24Transaction txn = builder.build();

txnStore.save(txn);

Expand Down Expand Up @@ -189,12 +194,6 @@ public InteractiveTransactionResponse deposit(
throw new SepValidationException("'account' is required");
}

if (!destinationAccount.equals(token.getAccount())) {
throw new SepValidationException("'account' does not match the one in the token");
}

makeMemo(depositRequest.get("memo"), depositRequest.get("memo_type"));

// Verify that the asset code exists in our database, with withdraw enabled.
AssetResponse asset = assetService.getAsset(assetCode, assetIssuer);
if (asset == null || !asset.getDeposit().getEnabled() || !asset.getSep24Enabled()) {
Expand All @@ -217,23 +216,32 @@ public InteractiveTransactionResponse deposit(
String.format("invalid account: %s", destinationAccount), ex);
}

Memo memo = makeMemo(depositRequest.get("memo"), depositRequest.get("memo_type"));

String txnId = UUID.randomUUID().toString();
Sep24Transaction txn =
Sep24TransactionBuilder builder =
new Sep24TransactionBuilder(txnStore)
.transactionId(txnId)
.status(Sep24Transaction.Status.INCOMPLETE.toString())
.kind(Sep24Transaction.Kind.DEPOSIT.toString())
.kind(DEPOSIT.toString())
.amountIn(strAmount)
.amountOut(strAmount)
.assetCode(assetCode)
.assetIssuer(depositRequest.get("asset_issuer"))
.startedAt(Instant.now().getEpochSecond())
.stellarAccount(token.getAccount())
.stellarAccountMemo(token.getAccountMemo())
.toAccount(destinationAccount)
.protocol(Sep24Transaction.Protocol.SEP24.toString())
.domainClient(token.getClientDomain())
.claimableBalanceSupported(claimableSupported)
.build();
.claimableBalanceSupported(claimableSupported);

if (memo != null) {
builder.memo(memo.toString());
builder.memoType(memoTypeString(memoType(memo)));
}

Sep24Transaction txn = builder.build();

txnStore.save(txn);
Log.infoF(
Expand Down Expand Up @@ -266,7 +274,8 @@ public GetTransactionsResponse findTransactions(JwtToken token, GetTransactionsR
if (assetService.getAsset(txReq.getAssetCode(), null) == null) {
throw new SepValidationException("asset code is not supported");
}
List<Sep24Transaction> txns = txnStore.findTransactions(token.getAccount(), txReq);
List<Sep24Transaction> txns =
txnStore.findTransactions(token.getAccount(), token.getAccountMemo(), txReq);
GetTransactionsResponse result = new GetTransactionsResponse();
List<TransactionResponse> list = new ArrayList<>();
for (Sep24Transaction txn : txns) {
Expand Down Expand Up @@ -310,6 +319,11 @@ public GetTransactionResponse findTransaction(JwtToken token, GetTransactionRequ
throw new SepNotFoundException("transaction is not found");
}

// If the token has a memo, make sure the transaction belongs to the account with the same memo.
if (token.getAccountMemo() != null && !token.getAccountMemo().equals(txn.getAccountMemo())) {
throw new SepNotFoundException("transaction is not found");
}

return GetTransactionResponse.of(fromTxn(txn));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ public interface Sep24TransactionStore {
* @param request The query request.
* @return The list of transaction documents. If not found, return empty list.
*/
List<Sep24Transaction> findTransactions(String accountId, GetTransactionsRequest request)
throws SepException;
List<Sep24Transaction> findTransactions(
String accountId, String accountMemo, GetTransactionsRequest request) throws SepException;

/**
* Save a transaction.
Expand Down
18 changes: 18 additions & 0 deletions core/src/main/java/org/stellar/anchor/util/SepUtil.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package org.stellar.anchor.util;

import static org.stellar.sdk.xdr.MemoType.*;
import static org.stellar.sdk.xdr.MemoType.MEMO_NONE;

import org.stellar.anchor.exception.SepException;
import org.stellar.sdk.*;
import org.stellar.sdk.xdr.MemoType;

public class SepUtil {
Expand All @@ -25,4 +30,17 @@ public static String memoTypeString(MemoType memoType) {

return result;
}

public static MemoType memoType(Memo memo) throws SepException {
if (memo instanceof MemoId) {
return MEMO_ID;
} else if (memo instanceof MemoHash) {
return MEMO_HASH;
} else if (memo instanceof MemoText) {
return MEMO_TEXT;
} else if (memo instanceof MemoNone) {
return MEMO_NONE;
}
throw new SepException("Unsupported memo type: " + memo.getClass());
}
}
Loading

0 comments on commit a1c23ab

Please sign in to comment.