From 639e1fe7939a9cf1dcddcdeeae2f4a9bdd5f9ff0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 29 Mar 2019 15:17:25 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../data/network/model/RippleResponse.kt | 6 +- .../com/tangem/domain/wallet/CoinEngine.java | 2 +- .../com/tangem/domain/wallet/xrp/XrpData.java | 14 ++++ .../tangem/domain/wallet/xrp/XrpEngine.java | 67 ++++++++++++++----- .../com/tangem/ui/fragment/LoadedWallet.kt | 2 +- 5 files changed, 72 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/tangem/data/network/model/RippleResponse.kt b/app/src/main/java/com/tangem/data/network/model/RippleResponse.kt index d8d54ba683..4a865aca67 100644 --- a/app/src/main/java/com/tangem/data/network/model/RippleResponse.kt +++ b/app/src/main/java/com/tangem/data/network/model/RippleResponse.kt @@ -36,7 +36,11 @@ data class RippleResult( //for RIPPLE_SERVER_STATE @SerializedName("state") - var state: RippleState? = null + var state: RippleState? = null, + + //for "Account not found error" + @SerializedName("error_code") + var error_code: Int? = null ) data class RippleAccountData( diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java index f539bb9443..5936584616 100644 --- a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java @@ -216,7 +216,7 @@ public abstract class CoinEngine { public abstract boolean validateAddress(String address); - public abstract String calculateAddress(byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException, CborException, IOException; + public abstract String calculateAddress(byte[] pkUncompressed) throws NoSuchProviderException, NoSuchAlgorithmException, CborException, IOException, Exception; public abstract Amount convertToAmount(InternalAmount internalAmount) throws Exception; diff --git a/app/src/main/java/com/tangem/domain/wallet/xrp/XrpData.java b/app/src/main/java/com/tangem/domain/wallet/xrp/XrpData.java index 180f454a37..a44db475f8 100644 --- a/app/src/main/java/com/tangem/domain/wallet/xrp/XrpData.java +++ b/app/src/main/java/com/tangem/domain/wallet/xrp/XrpData.java @@ -17,6 +17,8 @@ public class XrpData extends CoinData { private Long reserve = 20000000L; + private Boolean accountNotFound = false; + @Override public void loadFromBundle(Bundle B) { super.loadFromBundle(B); @@ -29,6 +31,8 @@ public class XrpData extends CoinData { else sequence = null; if (B.containsKey("Reserve")) reserve = B.getLong("Reserve"); else reserve = 20000000L; + if (B.containsKey("AccoundNotFound")) accountNotFound = B.getBoolean("AccoundNotFound"); + else reserve = 20000000L; } @Override @@ -39,6 +43,7 @@ public class XrpData extends CoinData { if (balanceUnconfirmed != null) B.putLong("BalanceUnconfirmed", balanceUnconfirmed); if (sequence != null) B.putLong("Sequence", sequence); if (reserve != null) B.putLong("Reserve", reserve); + if (accountNotFound != null) B.putBoolean("AccoundNotFound", accountNotFound); } catch (Exception e) { Log.e("Can't save to bundle ", e.getMessage()); } @@ -51,6 +56,7 @@ public class XrpData extends CoinData { balanceUnconfirmed = null; sequence = null; reserve = 20000000L; + accountNotFound = false; } // balanceUnconfirmed is just the latest balance, it equals balanceConfirmed if no unconfirmed transaction present @@ -89,6 +95,14 @@ public class XrpData extends CoinData { return new CoinEngine.InternalAmount(BigDecimal.valueOf(reserve), "Drops"); } + public boolean isAccountNotFound() { + return accountNotFound; + } + + public void setAccountNotFound(boolean accountFound) { + this.accountNotFound = accountFound; + } + public boolean hasBalanceInfo() { return balanceConfirmed != null || balanceUnconfirmed != null; } diff --git a/app/src/main/java/com/tangem/domain/wallet/xrp/XrpEngine.java b/app/src/main/java/com/tangem/domain/wallet/xrp/XrpEngine.java index 48aa9a47ce..fda44e7931 100644 --- a/app/src/main/java/com/tangem/domain/wallet/xrp/XrpEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/xrp/XrpEngine.java @@ -8,7 +8,9 @@ import com.ripple.core.coretypes.AccountID; import com.ripple.core.coretypes.uint.UInt32; import com.ripple.core.types.known.tx.signed.SignedTransaction; import com.ripple.core.types.known.tx.txns.Payment; +import com.ripple.crypto.ecdsa.ECDSASignature; import com.ripple.encodings.addresses.Addresses; +import com.ripple.utils.HashUtils; import com.tangem.card_common.data.TangemCard; import com.tangem.card_common.reader.CardProtocol; import com.tangem.card_common.tasks.SignTask; @@ -24,7 +26,13 @@ import com.tangem.util.CryptoUtil; import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.wallet.R; +import org.spongycastle.asn1.ASN1EncodableVector; +import org.spongycastle.asn1.ASN1Integer; +import org.spongycastle.asn1.DERSequence; + import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Arrays; public class XrpEngine extends CoinEngine { @@ -66,7 +74,7 @@ public class XrpEngine extends CoinEngine { public String getBalanceHTML() { Amount balance = getBalance(); if (balance != null) { - return " " + balance.toDescriptionString(getDecimals()) + "
+ " + convertToAmount(coinData.getReserveInInternalUnits()).toDescriptionString(getDecimals()) + " reserve"; + return " " + balance.toDescriptionString(getDecimals()) + "
+ " + convertToAmount(coinData.getReserveInInternalUnits()).toDescriptionString(getDecimals()) + " reserve"; } else { return ""; } @@ -201,6 +209,13 @@ public class XrpEngine extends CoinEngine { @Override public boolean validateBalance(BalanceValidator balanceValidator) { try { + if (coinData.isAccountNotFound()) { + balanceValidator.setScore(0); + balanceValidator.setFirstLine("Account not found"); + balanceValidator.setSecondLine("Load 20+ XRP to create account"); + return false; + } + if (((ctx.getCard().getOfflineBalance() == null) && !ctx.getCoinData().isBalanceReceived()) || (!ctx.getCoinData().isBalanceReceived() && (ctx.getCard().getRemainingSignatures() != ctx.getCard().getMaxSignatures()))) { balanceValidator.setScore(0); balanceValidator.setFirstLine("Unknown balance"); @@ -263,7 +278,7 @@ public class XrpEngine extends CoinEngine { return balance.toEquivalentString(coinData.getRate()); } - public String calculateAddress(byte[] pkCompressed) { + public String calculateAddress(byte[] pkCompressed) throws Exception { byte[] canonisedPubKey = canonisePubKey(pkCompressed); byte[] accountId = CryptoUtil.sha256ripemd160(canonisedPubKey); String address = Addresses.encodeAccountID(accountId); @@ -301,9 +316,6 @@ public class XrpEngine extends CoinEngine { @Override public byte[] convertToByteArray(InternalAmount internalAmount) { byte[] bytes = Util.longToByteArray(internalAmount.longValueExact()); -// byte[] reversed = new byte[bytes.length]; TODO: check if needed -// for (int i = 0; i < bytes.length; i++) reversed[i] = bytes[bytes.length - i - 1]; -// return reversed; return bytes; } @@ -328,15 +340,16 @@ public class XrpEngine extends CoinEngine { } } - public byte[] canonisePubKey(byte[] pkCompressed) { + public byte[] canonisePubKey(byte[] pkCompressed) throws Exception { byte[] canonicalPubKey = new byte[33]; if (pkCompressed.length == 32) { canonicalPubKey[0] = (byte) 0xED; System.arraycopy(pkCompressed, 0, canonicalPubKey, 1, 32); - } else { + } else if (pkCompressed.length == 33) canonicalPubKey = pkCompressed; - } + else + throw new Exception("Invalid pubkey length"); return canonicalPubKey; } @@ -373,9 +386,14 @@ public class XrpEngine extends CoinEngine { } @Override - public byte[][] getHashesToSign() { + public byte[][] getHashesToSign() throws Exception { byte[][] dataForSign = new byte[1][]; - dataForSign[0] = signedTx.signingData; + if (ctx.getCard().getWalletPublicKeyRar().length == 33) + dataForSign[0] = HashUtils.halfSha512(signedTx.signingData); + else if (ctx.getCard().getWalletPublicKeyRar().length == 32) + dataForSign[0] = signedTx.signingData; + else + throw new Exception("Invalid pubkey length"); return dataForSign; } @@ -396,7 +414,21 @@ public class XrpEngine extends CoinEngine { @Override public byte[] onSignCompleted(byte[] signFromCard) throws Exception { - signedTx.addSign(signFromCard); + if (ctx.getCard().getWalletPublicKeyRar().length == 33) { + int size = signFromCard.length / 2; + BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, size)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, size, size * 2)); + s = CryptoUtil.toCanonicalised(s); + ECDSASignature sig = new ECDSASignature(r, s); + byte[] sigDer = sig.encodeToDER(); + if (!ECDSASignature.isStrictlyCanonical(sigDer)) { + throw new IllegalStateException("Signature is not strictly canonical"); + } + signedTx.addSign(sigDer); + } else if (ctx.getCard().getWalletPublicKeyRar().length == 32) + signedTx.addSign(signFromCard); + else + throw new Exception("Invalid pubkey length"); byte[] txForSend = BTCUtils.fromHex(signedTx.tx_blob); notifyOnNeedSendTransaction(txForSend); return txForSend; @@ -414,12 +446,15 @@ public class XrpEngine extends CoinEngine { switch (method) { case ServerApiRipple.RIPPLE_ACCOUNT_INFO: { try { - String walletAddress = rippleResponse.getResult().getAccount_data().getAccount(); - if (!walletAddress.equals(coinData.getWallet())) { - throw new Exception("Invalid wallet address in answer!"); + if (rippleResponse.getResult().getAccount_data() != null) { + String walletAddress = rippleResponse.getResult().getAccount_data().getAccount(); + if (!walletAddress.equals(coinData.getWallet())) { + throw new Exception("Invalid wallet address in answer!"); + } + coinData.setBalanceConfirmed(Long.parseLong(rippleResponse.getResult().getAccount_data().getBalance())); } - - coinData.setBalanceConfirmed(Long.parseLong(rippleResponse.getResult().getAccount_data().getBalance())); + else if (rippleResponse.getResult().getError_code().equals(19)) // "Account not found" + coinData.setAccountNotFound(true); } catch (Exception e) { e.printStackTrace(); Log.e(TAG, "FAIL RIPPLE_ACCOUNT_INFO Exception"); diff --git a/app/src/main/java/com/tangem/ui/fragment/LoadedWallet.kt b/app/src/main/java/com/tangem/ui/fragment/LoadedWallet.kt index 346f7900be..cafb001a58 100644 --- a/app/src/main/java/com/tangem/ui/fragment/LoadedWallet.kt +++ b/app/src/main/java/com/tangem/ui/fragment/LoadedWallet.kt @@ -594,7 +594,7 @@ class LoadedWallet : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback tvBalance.text = html } - else -> tvBalance.text = getString(R.string.no_data_string) + else -> tvBalance.text = "" } tvWallet.text = ctx.coinData!!.wallet