Updated on 2026-08-14

This commit is contained in:
Tangem 2020-04-21 11:11:12 +03:00
commit a4cc732999
5 changed files with 96 additions and 25 deletions

View file

@ -39,7 +39,6 @@ import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import io.reactivex.CompletableObserver;
import io.reactivex.SingleObserver;
@ -425,8 +424,6 @@ public class BtcCashEngine extends CoinEngine {
@Override
public byte[][] getHashesToSign() throws Exception {
byte[][] dataForSign = new byte[unspentOutputs.size()][];
if (txForSign.length > 10)
throw new Exception("To much hashes in one transaction!");
for (int i = 0; i < unspentOutputs.size(); ++i) {
dataForSign[i] = bodyDoubleHash[i];
}

View file

@ -517,8 +517,6 @@ public class BtcEngine extends CoinEngine {
@Override
public byte[][] getHashesToSign() throws Exception {
byte[][] dataForSign = new byte[unspentOutputs.size()][];
if (txForSign.length > 10)
throw new Exception("To much hashes in one transaction!");
for (int i = 0; i < unspentOutputs.size(); ++i) {
dataForSign[i] = bodyDoubleHash[i];
}

View file

@ -6,12 +6,9 @@ import android.util.Log;
import com.tangem.App;
import com.tangem.data.network.ServerApiBitcore;
import com.tangem.data.network.ServerApiInsight;
import com.tangem.data.network.model.BitcoreBalanceAndUnspents;
import com.tangem.data.network.model.BitcoreSendResponse;
import com.tangem.data.network.model.BitcoreUtxo;
import com.tangem.data.network.model.InsightResponse;
import com.tangem.data.network.model.InsightUtxo;
import com.tangem.tangem_card.data.TangemCard;
import com.tangem.tangem_card.reader.CardProtocol;
import com.tangem.tangem_card.tasks.SignTask;
@ -39,7 +36,6 @@ import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import io.reactivex.SingleObserver;
import io.reactivex.observers.DisposableSingleObserver;
@ -449,8 +445,6 @@ public class DucatusEngine extends BtcEngine {
@Override
public byte[][] getHashesToSign() throws Exception {
byte[][] dataForSign = new byte[unspentOutputs.size()][];
if (txForSign.length > 10)
throw new Exception("To much hashes in one transaction!");
for (int i = 0; i < unspentOutputs.size(); ++i) {
dataForSign[i] = bodyDoubleHash[i];
}

View file

@ -23,6 +23,7 @@ import com.tangem.wallet.BTCUtils;
import com.tangem.wallet.BalanceValidator;
import com.tangem.wallet.Base58;
import com.tangem.wallet.CoinData;
import com.tangem.wallet.CoinEngine;
import com.tangem.wallet.R;
import com.tangem.wallet.TangemContext;
import com.tangem.wallet.Transaction;
@ -348,7 +349,7 @@ public class LtcEngine extends BtcEngine {
@Override
public Amount convertToAmount(InternalAmount internalAmount) {
BigDecimal d = internalAmount.divide(new BigDecimal("100000000"));
BigDecimal d = internalAmount.divide(new BigDecimal("100000000")).setScale(getDecimals(), RoundingMode.DOWN);
return new Amount(d, getBalanceCurrency());
}
@ -450,8 +451,6 @@ public class LtcEngine extends BtcEngine {
@Override
public byte[][] getHashesToSign() throws Exception {
byte[][] dataForSign = new byte[unspentOutputs.size()][];
if (txForSign.length > 10)
throw new Exception("To much hashes in one transaction!");
for (int i = 0; i < unspentOutputs.size(); ++i) {
dataForSign[i] = bodyDoubleHash[i];
}
@ -623,12 +622,74 @@ public class LtcEngine extends BtcEngine {
}
}
private final static BigDecimal relayFee = new BigDecimal(0.00001).setScale(8, RoundingMode.DOWN);
private final static Amount relayFee = new Amount(new BigDecimal(0.00001).setScale(8, RoundingMode.DOWN), "LTC");
private void forceRelayFeeMinimum(Amount fee) {
if (fee.compareTo(relayFee) < 0) {
fee = relayFee;
}
}
@Override
public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) throws Exception {
coinData.minFee = coinData.normalFee = coinData.maxFee = new Amount(relayFee, "LTC");
blockchainRequestsCallbacks.onComplete(true);
final int calcSize = calculateEstimatedTransactionSize(targetAddress, amount.toValueString());
Log.e(TAG, String.format("Estimated tx size %d", calcSize));
coinData.minFee = null;
coinData.maxFee = null;
coinData.normalFee = null;
final ServerApiBlockcypher serverApiBlockcypher = new ServerApiBlockcypher();
ServerApiBlockcypher.ResponseListener blockcypherListener = new ServerApiBlockcypher.ResponseListener() {
@Override
public void onSuccess(String method, BlockcypherResponse blockcypherResponse) {
Log.e(TAG, "Wrong response type for requestFee");
ctx.setError("Wrong response type for requestFee");
blockchainRequestsCallbacks.onComplete(false);
}
public void onSuccess(String method, BlockcypherFee blockcypherFee) {
Log.i(TAG, "onSuccess: " + method);
try {
BigDecimal minByteFee = new BigDecimal(blockcypherFee.getLow_fee_per_kb()).divide(BigDecimal.valueOf(1024));
BigDecimal normalByteFee = new BigDecimal(blockcypherFee.getMedium_fee_per_kb()).divide(BigDecimal.valueOf(1024));
BigDecimal maxByteFee = new BigDecimal(blockcypherFee.getHigh_fee_per_kb()).divide(BigDecimal.valueOf(1024));
CoinEngine.InternalAmount minIntAmount = new CoinEngine.InternalAmount(minByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi");
CoinEngine.InternalAmount normalIntAmount = new CoinEngine.InternalAmount(normalByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi");
CoinEngine.InternalAmount maxIntAmount = new CoinEngine.InternalAmount(maxByteFee.multiply(BigDecimal.valueOf(calcSize)), "satoshi");
coinData.minFee = convertToAmount(minIntAmount);
coinData.normalFee = convertToAmount(normalIntAmount);
coinData.maxFee = convertToAmount(maxIntAmount);
forceRelayFeeMinimum(coinData.minFee);
forceRelayFeeMinimum(coinData.normalFee);
forceRelayFeeMinimum(coinData.maxFee);
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "FAIL BLOCKCYPHER_FEE Exception");
}
if (serverApiBlockcypher.isRequestsSequenceCompleted()) {
blockchainRequestsCallbacks.onComplete(!ctx.hasError());
} else {
blockchainRequestsCallbacks.onProgress();
}
}
@Override
public void onFail(String method, String message) {
Log.i(TAG, "onFail: " + method + " " + message);
ctx.setError(message);
blockchainRequestsCallbacks.onComplete(false);
}
};
serverApiBlockcypher.setResponseListener(blockcypherListener);
serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_FEE, "", "");
}
@Override
@ -677,9 +738,4 @@ public class LtcEngine extends BtcEngine {
serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_SEND, "", txStr);
}
@Override
public boolean allowSelectFeeLevel() {
return false;
}
}

View file

@ -14,6 +14,8 @@ import java.io.ByteArrayOutputStream;
public class SignTask extends CustomReadCardTask {
public static final String TAG = SignTask.class.getSimpleName();
private static final int MAX_HASHES_TO_SIGN = 10;
/**
* Transaction Engine request/notifications during sign process
*/
@ -58,13 +60,31 @@ public class SignTask extends CustomReadCardTask {
TLVList signResult;
switch (mCard.getSigningMethod()) {
case Sign_Hash:
signResult = protocol.run_SignHashes(pinsProvider.getPIN2(), transactionToSign.getHashesToSign(), null, null, null);
break;
byte[][] remainingHashes = transactionToSign.getHashesToSign();
ByteArrayOutputStream signatures = new ByteArrayOutputStream();
while (remainingHashes.length > 0) {
byte[][] hashesToSign;
if (remainingHashes.length <= MAX_HASHES_TO_SIGN) {
hashesToSign = remainingHashes.clone();
remainingHashes = new byte[0][];
} else {
hashesToSign = new byte[MAX_HASHES_TO_SIGN][];
System.arraycopy(remainingHashes, 0, hashesToSign, 0, MAX_HASHES_TO_SIGN);
remainingHashes = removeFirstTenEntries(remainingHashes);
}
signResult = protocol.run_SignHashes(pinsProvider.getPIN2(), hashesToSign, null, null, null);
signatures.write(signResult.getTLV(TLV.Tag.TAG_Signature).Value);
}
mNotifications.onReadProgress(protocol, 100);
transactionToSign.onSignCompleted(signatures.toByteArray());
return;
case Sign_Hash_Validated_By_Issuer:
case Sign_Hash_Validated_By_Issuer_And_WriteIssuerData:
ByteArrayOutputStream bs = new ByteArrayOutputStream();
byte[][] hashes = transactionToSign.getHashesToSign();
if (hashes.length > 10) throw new CardProtocol.TangemException("To much hashes in one transaction!");
if (hashes.length > MAX_HASHES_TO_SIGN) throw new CardProtocol.TangemException("To much hashes in one transaction!");
for (int i = 0; i < hashes.length; i++) {
if (i != 0 && hashes[0].length != hashes[i].length)
throw new CardProtocol.TangemException("Hashes length must be identical!");
@ -89,4 +109,10 @@ public class SignTask extends CustomReadCardTask {
mNotifications.onReadProgress(protocol, 100);
}
private byte[][] removeFirstTenEntries(byte[][] array) {
byte[][] temp = new byte[array.length - MAX_HASHES_TO_SIGN][];
System.arraycopy(array, MAX_HASHES_TO_SIGN, temp, 0, temp.length);
return temp;
}
}