Updated on 2026-08-14
This commit is contained in:
parent
bacb21c115
commit
639e1fe793
5 changed files with 72 additions and 19 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()) + " <br><small><small> + " + convertToAmount(coinData.getReserveInInternalUnits()).toDescriptionString(getDecimals()) + " reserve</small></small>";
|
||||
return " " + balance.toDescriptionString(getDecimals()) + " <br><small><small>+ " + convertToAmount(coinData.getReserveInInternalUnits()).toDescriptionString(getDecimals()) + " reserve</small></small>";
|
||||
} 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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue