Updated on 2026-08-14

This commit is contained in:
Tangem 2018-12-06 12:11:10 +03:00
parent 71fe425c1b
commit 6ebb46c2a5
10 changed files with 711 additions and 492 deletions

View file

@ -1,159 +0,0 @@
package com.tangem.data.nfc;
import android.app.Activity;
import android.content.Intent;
import android.nfc.tech.IsoDep;
import android.util.Log;
import com.tangem.domain.wallet.BTCUtils;
import com.tangem.domain.wallet.CoinEngine;
import com.tangem.domain.wallet.CoinEngineFactory;
import com.tangem.domain.wallet.TangemContext;
import com.tangem.presentation.activity.SendTransactionActivity;
import com.tangem.presentation.activity.SignPaymentActivity;
import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.reader.NfcManager;
import com.tangem.tangemcard.data.Blockchain;
import java.io.IOException;
public class SignPaymentTask extends Thread{
public static final String TAG = SignPaymentTask.class.getSimpleName();
private CoinEngine.Amount txAmount;
private CoinEngine.Amount txFee;
private Boolean txIncFee = true;
public void SetTransactionValue(CoinEngine.Amount amount, CoinEngine.Amount fee, Boolean incfee) {
txAmount = amount;
txFee = fee;
txIncFee = incfee;
}
private String txOutAddress;
private Activity mContext;
private TangemContext mCtx;
private NfcManager mNfcManager;
private IsoDep mIsoDep;
private CardProtocol.Notifications mNotifications;
private boolean isCancelled = false;
public SignPaymentTask(Activity context, TangemContext ctx, NfcManager nfcManager, IsoDep isoDep, CardProtocol.Notifications notifications, CoinEngine.Amount amount, CoinEngine.Amount fee, Boolean IncFee, String outAddress) {
mCtx=ctx;
mContext = context;
mNfcManager = nfcManager;
mIsoDep = isoDep;
mNotifications = notifications;
txOutAddress = outAddress;
SetTransactionValue(amount, fee, IncFee);
}
@Override
public void run() {
if (mIsoDep == null) {
return;
}
CardProtocol protocol = new CardProtocol(mContext, mIsoDep, mCtx.getCard(), mNotifications);
mNotifications.onReadStart(protocol);
try {
// for Samsung's bugs -
// Workaround for the Samsung Galaxy S5 (since the
// first connection always hangs on transceive).
int timeout = mIsoDep.getTimeout();
mIsoDep.connect();
mIsoDep.close();
mIsoDep.connect();
mIsoDep.setTimeout(timeout);
try {
mNotifications.onReadProgress(protocol, 5);
Log.i(TAG, "[-- Start sign payment --]");
if (isCancelled) return;
protocol.run_Read();
protocol.run_VerifyCard();
Log.i(TAG, "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName());
mNotifications.onReadProgress(protocol, 30);
if (isCancelled) return;
//
// if (mCard.getBlockchain() == Blockchain.Ethereum) {
// SignETH_TX(protocol);
// } else {
// SignBTC_TX(protocol);
// }
CoinEngine engine = CoinEngineFactory.INSTANCE.create(mCtx);
if (engine != null) {
if (mCtx.getCard().getPauseBeforePIN2() > 0) {
mNotifications.onReadWait(mCtx.getCard().getPauseBeforePIN2());
}
byte[] tx = null;
try {
tx = engine.sign(txFee, txAmount, txIncFee, txOutAddress, protocol);
}
catch (IOException e) {
e.printStackTrace();
protocol.setError(e);
} finally {
mNotifications.onReadWait(0);
}
if (tx != null) {
// TODO - move to engine!!!
String txStr = BTCUtils.toHex(tx);
if (mCtx.getBlockchain() == Blockchain.Ethereum || mCtx.getBlockchain() == Blockchain.EthereumTestNet || mCtx.getBlockchain() == Blockchain.Token) {
txStr = String.format("0x%s", txStr);
}
Intent intent = new Intent(mContext, SendTransactionActivity.class);
mCtx.saveToIntent(intent);
intent.putExtra(SendTransactionActivity.EXTRA_TX, txStr);
mContext.startActivityForResult(intent, SignPaymentActivity.REQUEST_CODE_SEND_PAYMENT);
}
}
mNotifications.onReadProgress(protocol, 100);
if (isCancelled) return;
} finally {
mNfcManager.ignoreTag(mIsoDep.getTag());
mNotifications.onReadWait(0);
}
} catch (CardProtocol.TangemException_InvalidPIN e) {
e.printStackTrace();
protocol.setError(e);
} catch (CardProtocol.TangemException_WrongAmount e) {
e.printStackTrace();
protocol.setError(e);
} catch (Exception e) {
e.printStackTrace();
protocol.setError(e);
} finally {
Log.i(TAG, "[-- Finish sign payment --]");
mNotifications.onReadFinish(protocol);
}
}
public void cancel(Boolean AllowInterrupt) {
try {
if (isAlive()) {
isCancelled = true;
join(500);
}
if (isAlive() && AllowInterrupt) {
interrupt();
mNotifications.onReadCancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View file

@ -185,7 +185,7 @@ public abstract class CoinEngine {
public abstract boolean isBalanceNotZero(); public abstract boolean isBalanceNotZero();
public abstract byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception; // public abstract byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception;
// TODO - change isExtractPossible to isExtractPossible and if not - return string message // TODO - change isExtractPossible to isExtractPossible and if not - return string message
public abstract boolean isExtractPossible(); public abstract boolean isExtractPossible();
@ -248,8 +248,22 @@ public abstract class CoinEngine {
} }
public SignTask.PaymentToSign constructPayment(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress) public abstract SignTask.PaymentToSign constructPayment(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress) throws Exception;
public interface OnNeedSendPayment
{ {
return null; void onPaymentPrepared(byte[] txForSend);
}
private OnNeedSendPayment onNeedSendPayment;
public void setOnNeedSendPayment(OnNeedSendPayment onNeedSendPayment) {
this.onNeedSendPayment = onNeedSendPayment;
}
protected void notifyOnNeedSendPayment(byte[] txForSend) throws Exception {
if(onNeedSendPayment==null)
throw new Exception("Payment signed but no callback defined to send!");
onNeedSendPayment.onPaymentPrepared(txForSend);
} }
} }

View file

@ -13,8 +13,6 @@ public class UnspentOutputInfo {
public final long confirmations; public final long confirmations;
public String txHashForBuild; public String txHashForBuild;
public byte[] scriptForBuild; public byte[] scriptForBuild;
public byte[] bodyDoubleHash;
public byte[] bodyHash;
public UnspentOutputInfo(byte[] txHash, Transaction.Script script, long value, int outputIndex, long confirmations, String hashForBuild, byte[] sign) { public UnspentOutputInfo(byte[] txHash, Transaction.Script script, long value, int outputIndex, long confirmations, String hashForBuild, byte[] sign) {
this.txHash = txHash; this.txHash = txHash;

View file

@ -17,6 +17,7 @@ import com.tangem.domain.wallet.TangemContext;
import com.tangem.domain.wallet.Transaction; import com.tangem.domain.wallet.Transaction;
import com.tangem.domain.wallet.UnspentOutputInfo; import com.tangem.domain.wallet.UnspentOutputInfo;
import com.tangem.domain.wallet.BTCUtils; import com.tangem.domain.wallet.BTCUtils;
import com.tangem.tangemcard.tasks.SignTask;
import com.tangem.util.CryptoUtil; import com.tangem.util.CryptoUtil;
import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.util.DecimalDigitsInputFilter;
import com.tangem.util.DerEncodingUtil; import com.tangem.util.DerEncodingUtil;
@ -433,15 +434,13 @@ public class BtcCashEngine extends CoinEngine {
// return mCard.getAmountDescription(Double.parseDouble(amount)); // return mCard.getAmountDescription(Double.parseDouble(amount));
// } // }
@Override
public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String destAddress, CardProtocol protocol) throws Exception {
@Override
public SignTask.PaymentToSign constructPayment(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress) throws Exception {
checkBlockchainDataExists(); checkBlockchainDataExists();
CoinEngine engine = CoinEngineFactory.INSTANCE.create(ctx); String srcLegacyAddress = convertToLegacyAddress(ctx.getCoinData().getWallet());
String destLegacyAddress = convertToLegacyAddress(targetAddress);
String srcLegacyAddress = ((BtcCashEngine)engine).convertToLegacyAddress(ctx.getCoinData().getWallet());
String destLegacyAddress = ((BtcCashEngine)engine).convertToLegacyAddress(destAddress);
byte[] pbKey = ctx.getCard().getWalletPublicKeyRar(); //ALWAYS USING COMPRESS KEY byte[] pbKey = ctx.getCard().getWalletPublicKeyRar(); //ALWAYS USING COMPRESS KEY
// Build script for our address // Build script for our address
@ -470,50 +469,156 @@ public class BtcCashEngine extends CoinEngine {
throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount)); throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount));
} }
byte[][] dataForSign = new byte[unspentOutputs.size()][]; final long amountFinal = amount;
final long changeFinal = change;
byte[][] txForSign= new byte[unspentOutputs.size()][];
byte[][] bodyHash= new byte[unspentOutputs.size()][];
byte[][] bodyDoubleHash= new byte[unspentOutputs.size()][];
for (int i = 0; i < unspentOutputs.size(); ++i) { for (int i = 0; i < unspentOutputs.size(); ++i) {
byte[] newTX = BTCUtils.buildTXForSign(srcLegacyAddress, destLegacyAddress, srcLegacyAddress, unspentOutputs, i, amount, change); txForSign[i] = BTCUtils.buildTXForSign(srcLegacyAddress, destLegacyAddress, srcLegacyAddress, unspentOutputs, i, amount, change);
bodyHash[i] = Util.calculateSHA256(txForSign[i]);
bodyDoubleHash[i] = Util.calculateSHA256(bodyHash[i]);
}
byte[] hashData = Util.calculateSHA256(newTX); return new SignTask.PaymentToSign() {
byte[] doubleHashData = Util.calculateSHA256(hashData);
unspentOutputs.get(i).bodyDoubleHash = doubleHashData; @Override
unspentOutputs.get(i).bodyHash = hashData; public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) {
return signingMethod==TangemCard.SigningMethod.Sign_Hash || signingMethod==TangemCard.SigningMethod.Sign_Raw;
if (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw || ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer) {
dataForSign[i] = newTX;
} else {
dataForSign[i] = doubleHashData;
} }
} @Override
public byte[][] getHashesToSign() throws Exception {
byte[] signFromCard; byte[][] dataForSign=new byte[unspentOutputs.size()][];
if (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw || ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer) { if (txForSign.length > 10) throw new Exception("To much hashes in one transaction!");
ByteArrayOutputStream bs = new ByteArrayOutputStream(); for (int i = 0; i < unspentOutputs.size(); ++i) {
if (dataForSign.length > 10) throw new Exception("To much hashes in one transaction!"); dataForSign[i] = bodyDoubleHash[i];
for (int i = 0; i < dataForSign.length; i++) { }
if (i != 0 && dataForSign[0].length != dataForSign[i].length) return dataForSign;
throw new Exception("Hashes length must be identical!");
bs.write(dataForSign[i]);
} }
signFromCard = protocol.run_SignRaw(PINStorage.getPIN2(), "sha-256x2", bs.toByteArray(), null, null, null).getTLV(TLV.Tag.TAG_Signature).Value;
} else {
//ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer
//ctx.getCard().getIssuer()
signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), dataForSign, null, null, null).getTLV(TLV.Tag.TAG_Signature).Value;
// TODO slice signFromCard to hashes.length parts
}
for (int i = 0; i < unspentOutputs.size(); ++i) { @Override
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, i * 64, 32 + i * 64)); public byte[] getRawDataToSign() throws Exception {
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); ByteArrayOutputStream bs = new ByteArrayOutputStream();
s = CryptoUtil.toCanonicalised(s); for (int i = 0; i < txForSign.length; i++) {
if (i != 0 && txForSign[0].length != txForSign[i].length)
throw new Exception("Hashes length must be identical!");
bs.write(txForSign[i]);
}
unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDerBitcoinCash(r, s, pbKey); return bs.toByteArray();
} }
return BTCUtils.buildTXForSend(destLegacyAddress, srcLegacyAddress, unspentOutputs, amount, change); @Override
public String getHashAlgToSign() {
return "sha-256x2";
}
@Override
public byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer) throws Exception {
throw new Exception("Transaction validation by issuer not supported in this version!");
}
@Override
public void onSignCompleted(byte[] signFromCard) throws Exception {
for (int i = 0; i < unspentOutputs.size(); ++i) {
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, i * 64, 32 + i * 64));
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64));
s = CryptoUtil.toCanonicalised(s);
unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDerBitcoinCash(r, s, pbKey);
}
byte[] txForSend=BTCUtils.buildTXForSend(destLegacyAddress, srcLegacyAddress, unspentOutputs, amountFinal, changeFinal);
notifyOnNeedSendPayment(txForSend);
}
};
} }
// @Override
// public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String destAddress, CardProtocol protocol) throws Exception {
//
// checkBlockchainDataExists();
//
// CoinEngine engine = CoinEngineFactory.INSTANCE.create(ctx);
//
// String srcLegacyAddress = ((BtcCashEngine)engine).convertToLegacyAddress(ctx.getCoinData().getWallet());
// String destLegacyAddress = ((BtcCashEngine)engine).convertToLegacyAddress(destAddress);
// byte[] pbKey = ctx.getCard().getWalletPublicKeyRar(); //ALWAYS USING COMPRESS KEY
//
// // Build script for our address
// List<BtcData.UnspentTransaction> rawTxList = coinData.getUnspentTransactions();
// byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(srcLegacyAddress).bytes;
//
// // Collect unspent
// ArrayList<UnspentOutputInfo> unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend);
//
// long fullAmount = 0;
// for (int i = 0; i < unspentOutputs.size(); ++i) {
// fullAmount += unspentOutputs.get(i).value;
// }
//
//
// long fees = convertToInternalAmount(feeValue).longValueExact();
// long amount = convertToInternalAmount(amountValue).longValueExact();
// long change = fullAmount - amount;
// if (IncFee) {
// amount = amount - fees;
// } else {
// change = change - fees;
// }
//
// if (amount + fees > fullAmount) {
// throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount));
// }
//
// byte[][] dataForSign = new byte[unspentOutputs.size()][];
//
// for (int i = 0; i < unspentOutputs.size(); ++i) {
// byte[] newTX = BTCUtils.buildTXForSign(srcLegacyAddress, destLegacyAddress, srcLegacyAddress, unspentOutputs, i, amount, change);
//
// byte[] hashData = Util.calculateSHA256(newTX);
// byte[] doubleHashData = Util.calculateSHA256(hashData);
//
// unspentOutputs.get(i).bodyDoubleHash = doubleHashData;
// unspentOutputs.get(i).bodyHash = hashData;
//
// if (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw || ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer) {
// dataForSign[i] = newTX;
// } else {
// dataForSign[i] = doubleHashData;
// }
//
// }
//
// byte[] signFromCard;
// if (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw || ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer) {
// ByteArrayOutputStream bs = new ByteArrayOutputStream();
// if (dataForSign.length > 10) throw new Exception("To much hashes in one transaction!");
// for (int i = 0; i < dataForSign.length; i++) {
// if (i != 0 && dataForSign[0].length != dataForSign[i].length)
// throw new Exception("Hashes length must be identical!");
// bs.write(dataForSign[i]);
// }
// signFromCard = protocol.run_SignRaw(PINStorage.getPIN2(), "sha-256x2", bs.toByteArray(), null, null, null).getTLV(TLV.Tag.TAG_Signature).Value;
// } else {
// //ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer
// //ctx.getCard().getIssuer()
// signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), dataForSign, null, null, null).getTLV(TLV.Tag.TAG_Signature).Value;
// // TODO slice signFromCard to hashes.length parts
// }
//
// for (int i = 0; i < unspentOutputs.size(); ++i) {
// BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, i * 64, 32 + i * 64));
// BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64));
// s = CryptoUtil.toCanonicalised(s);
//
// unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDerBitcoinCash(r, s, pbKey);
// }
//
// return BTCUtils.buildTXForSend(destLegacyAddress, srcLegacyAddress, unspentOutputs, amount, change);
// }
} }

View file

@ -16,6 +16,7 @@ import com.tangem.domain.wallet.TangemContext;
import com.tangem.domain.wallet.Transaction; import com.tangem.domain.wallet.Transaction;
import com.tangem.domain.wallet.UnspentOutputInfo; import com.tangem.domain.wallet.UnspentOutputInfo;
import com.tangem.domain.wallet.BTCUtils; import com.tangem.domain.wallet.BTCUtils;
import com.tangem.tangemcard.tasks.SignTask;
import com.tangem.util.CryptoUtil; import com.tangem.util.CryptoUtil;
import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.util.DecimalDigitsInputFilter;
import com.tangem.util.DerEncodingUtil; import com.tangem.util.DerEncodingUtil;
@ -404,8 +405,8 @@ public class BtcEngine extends CoinEngine {
} }
@Override @Override
public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { public SignTask.PaymentToSign constructPayment(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress) throws Exception {
final ArrayList<UnspentOutputInfo> unspentOutputs;
checkBlockchainDataExists(); checkBlockchainDataExists();
String myAddress = ctx.getCoinData().getWallet(); String myAddress = ctx.getCoinData().getWallet();
@ -416,14 +417,13 @@ public class BtcEngine extends CoinEngine {
byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes;
// Collect unspent // Collect unspent
ArrayList<UnspentOutputInfo> unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend);
long fullAmount = 0; long fullAmount = 0;
for (int i = 0; i < unspentOutputs.size(); ++i) { for (int i = 0; i < unspentOutputs.size(); ++i) {
fullAmount += unspentOutputs.get(i).value; fullAmount += unspentOutputs.get(i).value;
} }
long fees = convertToInternalAmount(feeValue).longValueExact(); long fees = convertToInternalAmount(feeValue).longValueExact();
long amount = convertToInternalAmount(amountValue).longValueExact(); long amount = convertToInternalAmount(amountValue).longValueExact();
long change = fullAmount - amount; long change = fullAmount - amount;
@ -433,53 +433,155 @@ public class BtcEngine extends CoinEngine {
change = change - fees; change = change - fees;
} }
final long amountFinal=amount;
final long changeFinal=change;
if (amount + fees > fullAmount) { if (amount + fees > fullAmount) {
throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount)); throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount));
} }
byte[][] dataForSign = new byte[unspentOutputs.size()][]; byte[][] txForSign = new byte[unspentOutputs.size()][];
byte[][] bodyDoubleHash = new byte[unspentOutputs.size()][];
byte[][] bodyHash= new byte[unspentOutputs.size()][];
for (int i = 0; i < unspentOutputs.size(); ++i) { for (int i = 0; i < unspentOutputs.size(); ++i) {
byte[] newTX = BTCUtils.buildTXForSign(myAddress, targetAddress, myAddress, unspentOutputs, i, amount, change); txForSign[i] = BTCUtils.buildTXForSign(myAddress, targetAddress, myAddress, unspentOutputs, i, amount, change);
bodyHash[i] = Util.calculateSHA256(txForSign[i]);
bodyDoubleHash [i] = Util.calculateSHA256(bodyHash[i]);
}
byte[] hashData = Util.calculateSHA256(newTX); return new SignTask.PaymentToSign() {
byte[] doubleHashData = Util.calculateSHA256(hashData);
unspentOutputs.get(i).bodyDoubleHash = doubleHashData; @Override
unspentOutputs.get(i).bodyHash = hashData; public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) {
return signingMethod==TangemCard.SigningMethod.Sign_Hash || signingMethod==TangemCard.SigningMethod.Sign_Raw;
if (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw || ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer) {
dataForSign[i] = newTX;
} else {
dataForSign[i] = doubleHashData;
} }
}
byte[] signFromCard; @Override
if (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw || ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer) { public byte[][] getHashesToSign() throws Exception {
ByteArrayOutputStream bs = new ByteArrayOutputStream(); byte[][] dataForSign=new byte[unspentOutputs.size()][];
if (dataForSign.length > 10) throw new Exception("To much hashes in one transaction!"); if (txForSign.length > 10) throw new Exception("To much hashes in one transaction!");
for (int i = 0; i < dataForSign.length; i++) { for (int i = 0; i < unspentOutputs.size(); ++i) {
if (i != 0 && dataForSign[0].length != dataForSign[i].length) dataForSign[i] = bodyDoubleHash[i];
throw new Exception("Hashes length must be identical!"); }
bs.write(dataForSign[i]); return dataForSign;
} }
signFromCard = protocol.run_SignRaw(PINStorage.getPIN2(), "sha-256x2",bs.toByteArray(),null,null,null).getTLV(TLV.Tag.TAG_Signature).Value;
} else {
//ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer, null, ctx.getCard().getIssuer()
signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), dataForSign, null, null, null).getTLV(TLV.Tag.TAG_Signature).Value;
// TODO slice signFromCard to hashes.length parts
}
for (int i = 0; i < unspentOutputs.size(); ++i) { @Override
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, i * 64, 32 + i * 64)); public byte[] getRawDataToSign() throws Exception {
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64)); ByteArrayOutputStream bs = new ByteArrayOutputStream();
s = CryptoUtil.toCanonicalised(s); for (int i = 0; i < txForSign.length; i++) {
if (i != 0 && txForSign[0].length != txForSign[i].length)
throw new Exception("Hashes length must be identical!");
bs.write(txForSign[i]);
}
unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDer(r, s, pbKey); return bs.toByteArray();
} }
return BTCUtils.buildTXForSend(targetAddress, myAddress, unspentOutputs, amount, change); @Override
public String getHashAlgToSign() {
return "sha-256x2";
}
@Override
public byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer) throws Exception {
throw new Exception("Issuer validation not supported!");
}
@Override
public void onSignCompleted(byte[] signature) throws Exception {
for (int i = 0; i < unspentOutputs.size(); ++i) {
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signature, i * 64, 32 + i * 64));
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, 32 + i * 64, 64 + i * 64));
s = CryptoUtil.toCanonicalised(s);
unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDer(r, s, pbKey);
}
byte[] txForSend=BTCUtils.buildTXForSend(targetAddress, myAddress, unspentOutputs, amountFinal, changeFinal);
notifyOnNeedSendPayment(txForSend);
}
};
} }
// @Override
// public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception {
//
// checkBlockchainDataExists();
//
// String myAddress = ctx.getCoinData().getWallet();
// byte[] pbKey = ctx.getCard().getWalletPublicKey();
//
// // Build script for our address
// List<BtcData.UnspentTransaction> rawTxList = coinData.getUnspentTransactions();
// byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes;
//
// // Collect unspent
// ArrayList<UnspentOutputInfo> unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend);
//
// long fullAmount = 0;
// for (int i = 0; i < unspentOutputs.size(); ++i) {
// fullAmount += unspentOutputs.get(i).value;
// }
//
//
// long fees = convertToInternalAmount(feeValue).longValueExact();
// long amount = convertToInternalAmount(amountValue).longValueExact();
// long change = fullAmount - amount;
// if (IncFee) {
// amount = amount - fees;
// } else {
// change = change - fees;
// }
//
// if (amount + fees > fullAmount) {
// throw new CardProtocol.TangemException_WrongAmount(String.format("Balance (%d) < change (%d) + amount (%d)", fullAmount, change, amount));
// }
//
// byte[][] dataForSign = new byte[unspentOutputs.size()][];
//
// for (int i = 0; i < unspentOutputs.size(); ++i) {
// byte[] newTX = BTCUtils.buildTXForSign(myAddress, targetAddress, myAddress, unspentOutputs, i, amount, change);
//
// byte[] hashData = Util.calculateSHA256(newTX);
// byte[] doubleHashData = Util.calculateSHA256(hashData);
//
// unspentOutputs.get(i).bodyDoubleHash = doubleHashData;
// unspentOutputs.get(i).bodyHash = hashData;
//
// if (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw || ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer) {
// dataForSign[i] = newTX;
// } else {
// dataForSign[i] = doubleHashData;
// }
// }
//
// byte[] signFromCard;
// if (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw || ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Raw_Validated_By_Issuer) {
// ByteArrayOutputStream bs = new ByteArrayOutputStream();
// if (dataForSign.length > 10) throw new Exception("To much hashes in one transaction!");
// for (int i = 0; i < dataForSign.length; i++) {
// if (i != 0 && dataForSign[0].length != dataForSign[i].length)
// throw new Exception("Hashes length must be identical!");
// bs.write(dataForSign[i]);
// }
// signFromCard = protocol.run_SignRaw(PINStorage.getPIN2(), "sha-256x2",bs.toByteArray(),null,null,null).getTLV(TLV.Tag.TAG_Signature).Value;
// } else {
// //ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer, null, ctx.getCard().getIssuer()
// signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), dataForSign, null, null, null).getTLV(TLV.Tag.TAG_Signature).Value;
// // TODO slice signFromCard to hashes.length parts
// }
//
// for (int i = 0; i < unspentOutputs.size(); ++i) {
// BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, i * 64, 32 + i * 64));
// BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64));
// s = CryptoUtil.toCanonicalised(s);
//
// unspentOutputs.get(i).scriptForBuild = DerEncodingUtil.packSignDer(r, s, pbKey);
// }
//
// return BTCUtils.buildTXForSend(targetAddress, myAddress, unspentOutputs, amount, change);
// }
} }

View file

@ -18,6 +18,7 @@ import com.tangem.domain.wallet.Keccak256;
import com.tangem.tangemcard.data.TangemCard; import com.tangem.tangemcard.data.TangemCard;
import com.tangem.domain.wallet.TangemContext; import com.tangem.domain.wallet.TangemContext;
import com.tangem.domain.wallet.BTCUtils; import com.tangem.domain.wallet.BTCUtils;
import com.tangem.tangemcard.tasks.SignTask;
import com.tangem.util.CryptoUtil; import com.tangem.util.CryptoUtil;
import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.util.DecimalDigitsInputFilter;
import com.tangem.wallet.R; import com.tangem.wallet.R;
@ -379,12 +380,9 @@ public class EthEngine extends CoinEngine {
} }
@Override @Override
public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { public SignTask.PaymentToSign constructPayment(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress) throws Exception {
BigInteger nonceValue = coinData.getConfirmedTXCount(); BigInteger nonceValue = coinData.getConfirmedTXCount();
byte[] pbKey = ctx.getCard().getWalletPublicKey(); byte[] pbKey = ctx.getCard().getWalletPublicKey();
// boolean flag = (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer);
Issuer issuer = ctx.getCard().getIssuer();
BigInteger weiFee=convertToInternalAmount(feeValue).toBigIntegerExact(); BigInteger weiFee=convertToInternalAmount(feeValue).toBigIntegerExact();
BigInteger weiAmount=convertToInternalAmount(amountValue).toBigIntegerExact(); BigInteger weiAmount=convertToInternalAmount(amountValue).toBigIntegerExact();
@ -404,41 +402,124 @@ public class EthEngine extends CoinEngine {
to = to.substring(2); to = to.substring(2);
} }
EthTransaction tx = EthTransaction.create(to, weiAmount, nonce, gasPrice, gasLimit, chainId); final EthTransaction tx = EthTransaction.create(to, weiAmount, nonce, gasPrice, gasLimit, chainId);
byte[][] hashesForSign = new byte[1][]; return new SignTask.PaymentToSign() {
byte[] for_hash = tx.getRawHash(); @Override
hashesForSign[0] = for_hash; public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) {
return signingMethod==TangemCard.SigningMethod.Sign_Hash;
}
byte[] signFromCard = null; @Override
try { public byte[][] getHashesToSign() throws Exception {
signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), hashesForSign, null, null, null).getTLV(TLV.Tag.TAG_Signature).Value; byte[][] hashesForSign = new byte[1][];
// TODO slice signFromCard to hashes.length parts hashesForSign[0] = tx.getRawHash();
} catch (Exception ex) { return hashesForSign;
Log.e("ETH", ex.getMessage()); }
return null;
}
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)); @Override
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)); public byte[] getRawDataToSign() throws Exception {
s = CryptoUtil.toCanonicalised(s); throw new Exception("Signing of raw transaction not supported for ETH");
}
boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey); @Override
public String getHashAlgToSign() throws Exception {
throw new Exception("Signing of raw transaction not supported for ETH");
}
if (!f) { @Override
Log.e("ETH-CHECK", "sign Failed."); public byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer) throws Exception {
} throw new Exception("Transaction validation by issuer not supported in this version");
}
tx.signature = new ECDSASignatureETH(r, s); @Override
int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey); public void onSignCompleted(byte[] signFromCard) throws Exception {
if (v != 27 && v != 28) { byte[] for_hash=tx.getRawHash();
Log.e("ETH", "invalid v"); BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32));
return null; BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64));
} s = CryptoUtil.toCanonicalised(s);
tx.signature.v = (byte) v;
Log.e("ETH_v", String.valueOf(v));
byte[] realTX = tx.getEncoded(); boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey);
return realTX;
if (!f) {
Log.e("ETH-CHECK", "sign Failed.");
}
tx.signature = new ECDSASignatureETH(r, s);
int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey);
if (v != 27 && v != 28) {
Log.e("ETH", "invalid v");
throw new Exception("Error in EthEngine - invalid v");
}
tx.signature.v = (byte) v;
Log.e("ETH_v", String.valueOf(v));
notifyOnNeedSendPayment(tx.getEncoded());
}
};
} }
// @Override
// public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception {
//
// BigInteger nonceValue = coinData.getConfirmedTXCount();
// byte[] pbKey = ctx.getCard().getWalletPublicKey();
//// boolean flag = (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer);
// Issuer issuer = ctx.getCard().getIssuer();
//
// BigInteger weiFee=convertToInternalAmount(feeValue).toBigIntegerExact();
// BigInteger weiAmount=convertToInternalAmount(amountValue).toBigIntegerExact();
//
// if (IncFee) {
// weiAmount = weiAmount.subtract(weiFee);
// }
//
// BigInteger nonce = nonceValue;
// BigInteger gasPrice = weiFee.divide(BigInteger.valueOf(21000));
// BigInteger gasLimit = BigInteger.valueOf(21000);
// Integer chainId = ctx.getBlockchain() == Blockchain.Ethereum ? EthTransaction.ChainEnum.Mainnet.getValue() : EthTransaction.ChainEnum.Rinkeby.getValue();
//
// String to = targetAddress;
//
// if (to.startsWith("0x") || to.startsWith("0X")) {
// to = to.substring(2);
// }
//
// EthTransaction tx = EthTransaction.create(to, weiAmount, nonce, gasPrice, gasLimit, chainId);
//
// byte[][] hashesForSign = new byte[1][];
// byte[] for_hash = tx.getRawHash();
// hashesForSign[0] = for_hash;
//
// byte[] signFromCard = null;
// try {
// signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), hashesForSign, null, null, null).getTLV(TLV.Tag.TAG_Signature).Value;
// // TODO slice signFromCard to hashes.length parts
// } catch (Exception ex) {
// Log.e("ETH", ex.getMessage());
// return null;
// }
//
// BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32));
// BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64));
// s = CryptoUtil.toCanonicalised(s);
//
// boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey);
//
// if (!f) {
// Log.e("ETH-CHECK", "sign Failed.");
// }
//
// tx.signature = new ECDSASignatureETH(r, s);
// int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey);
// if (v != 27 && v != 28) {
// Log.e("ETH", "invalid v");
// return null;
// }
// tx.signature.v = (byte) v;
// Log.e("ETH_v", String.valueOf(v));
//
// byte[] realTX = tx.getEncoded();
// return realTX;
// }
} }

View file

@ -5,6 +5,7 @@ import android.text.InputFilter;
import android.util.Log; import android.util.Log;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.tangem.tangemcard.data.Blockchain;
import com.tangem.tangemcard.data.local.PINStorage; import com.tangem.tangemcard.data.local.PINStorage;
import com.tangem.tangemcard.reader.CardProtocol; import com.tangem.tangemcard.reader.CardProtocol;
import com.tangem.tangemcard.reader.TLV; import com.tangem.tangemcard.reader.TLV;
@ -18,6 +19,7 @@ import com.tangem.domain.wallet.Keccak256;
import com.tangem.tangemcard.data.TangemCard; import com.tangem.tangemcard.data.TangemCard;
import com.tangem.domain.wallet.TangemContext; import com.tangem.domain.wallet.TangemContext;
import com.tangem.domain.wallet.BTCUtils; import com.tangem.domain.wallet.BTCUtils;
import com.tangem.tangemcard.tasks.SignTask;
import com.tangem.util.CryptoUtil; import com.tangem.util.CryptoUtil;
import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.util.DecimalDigitsInputFilter;
import com.tangem.wallet.R; import com.tangem.wallet.R;
@ -345,7 +347,7 @@ public class TokenEngine extends CoinEngine {
if( fee.compareTo(balance)>0 ) if( fee.compareTo(balance)>0 )
return false; return false;
} else if (amount.getCurrency().equals("ETH") && coinData.getBalanceInInternalUnits().isZero()) { } else if (amount.getCurrency().equals("ETH") && coinData.getBalanceInInternalUnits().isZero()) {
// standart ETH transaction // standard ETH transaction
try { try {
BigDecimal cardBalance = getBalance(); BigDecimal cardBalance = getBalance();
@ -425,31 +427,36 @@ public class TokenEngine extends CoinEngine {
} }
@Override @Override
public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { public SignTask.PaymentToSign constructPayment(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress) throws Exception {
if (amountValue.getCurrency().equals("ETH")) { if (amountValue.getCurrency().equals("ETH")) {
return signETH(feeValue, amountValue, IncFee, targetAddress, protocol); return constructPaymentETH(feeValue, amountValue, IncFee, targetAddress);
} else { } else {
return signToken(feeValue, amountValue, IncFee, targetAddress, protocol); return constructPaymentToken(feeValue, amountValue, IncFee, targetAddress);
} }
} }
public byte[] signETH(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { // @Override
// public byte[] sign(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception {
// if (amountValue.getCurrency().equals("ETH")) {
// return signETH(feeValue, amountValue, IncFee, targetAddress, protocol);
// } else {
// return signToken(feeValue, amountValue, IncFee, targetAddress, protocol);
// }
// }
private SignTask.PaymentToSign constructPaymentETH(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress) throws Exception {
BigInteger nonceValue = coinData.getConfirmedTXCount(); BigInteger nonceValue = coinData.getConfirmedTXCount();
byte[] pbKey = ctx.getCard().getWalletPublicKey(); byte[] pbKey = ctx.getCard().getWalletPublicKey();
// boolean flag = (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer);
Issuer issuer = ctx.getCard().getIssuer();
BigInteger weiFee = convertToInternalAmount(feeValue).toBigIntegerExact(); BigInteger weiFee=convertToInternalAmount(feeValue).toBigIntegerExact();
BigInteger weiAmount = convertToInternalAmount(amountValue).toBigIntegerExact(); BigInteger weiAmount=convertToInternalAmount(amountValue).toBigIntegerExact();
if (IncFee) { if (IncFee) {
weiAmount = weiAmount.subtract(weiFee); weiAmount = weiAmount.subtract(weiFee);
} }
BigInteger nonce = nonceValue;
BigInteger gasPrice = weiFee.divide(BigInteger.valueOf(21000)); BigInteger gasPrice = weiFee.divide(BigInteger.valueOf(21000));
BigInteger gasLimit = BigInteger.valueOf(21000); BigInteger gasLimit = BigInteger.valueOf(21000);
// Integer chainId = ctx.getBlockchain() == Blockchain.Ethereum ? EthTransaction.ChainEnum.Mainnet.getValue() : EthTransaction.ChainEnum.Rinkeby.getValue(); // Integer chainId = ctx.getBlockchain() == Blockchain.Ethereum ? EthTransaction.ChainEnum.Mainnet.getValue() : EthTransaction.ChainEnum.Rinkeby.getValue();
Integer chainId = EthTransaction.ChainEnum.Mainnet.getValue(); // Token support on main net only!!! Integer chainId = EthTransaction.ChainEnum.Mainnet.getValue(); // Token support on main net only!!!
@ -459,52 +466,71 @@ public class TokenEngine extends CoinEngine {
to = to.substring(2); to = to.substring(2);
} }
EthTransaction tx = EthTransaction.create(to, weiAmount, nonce, gasPrice, gasLimit, chainId); final EthTransaction tx = EthTransaction.create(to, weiAmount, nonceValue, gasPrice, gasLimit, chainId);
byte[][] hashesForSign = new byte[1][]; return new SignTask.PaymentToSign() {
byte[] for_hash = tx.getRawHash(); @Override
hashesForSign[0] = for_hash; public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) {
return signingMethod==TangemCard.SigningMethod.Sign_Hash;
}
byte[] signFromCard = null; @Override
try { public byte[][] getHashesToSign() {
signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), hashesForSign, null, null, null).getTLV(TLV.Tag.TAG_Signature).Value; byte[][] hashesForSign = new byte[1][];
// TODO slice signFromCard to hashes.length parts hashesForSign[0] = tx.getRawHash();
} catch (Exception ex) { return hashesForSign;
Log.e("ETH", ex.getMessage()); }
return null;
}
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)); @Override
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)); public byte[] getRawDataToSign() throws Exception {
s = CryptoUtil.toCanonicalised(s); throw new Exception("Signing of raw transaction not supported for ETH");
}
boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey); @Override
public String getHashAlgToSign() throws Exception {
throw new Exception("Signing of raw transaction not supported for ETH");
}
if (!f) { @Override
Log.e("ETH-CHECK", "sign Failed."); public byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer) throws Exception {
} throw new Exception("Transaction validation by issuer not supported in this version");
}
tx.signature = new ECDSASignatureETH(r, s); @Override
int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey); public void onSignCompleted(byte[] signFromCard) throws Exception {
if (v != 27 && v != 28) { byte[] for_hash=tx.getRawHash();
Log.e("ETH", "invalid v"); BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32));
return null; BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64));
} s = CryptoUtil.toCanonicalised(s);
tx.signature.v = (byte) v;
Log.e("ETH_v", String.valueOf(v));
byte[] realTX = tx.getEncoded(); boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey);
return realTX;
if (!f) {
Log.e("ETH-CHECK", "sign Failed.");
}
tx.signature = new ECDSASignatureETH(r, s);
int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey);
if (v != 27 && v != 28) {
Log.e("ETH", "invalid v");
throw new Exception("Error in EthEngine - invalid v");
}
tx.signature.v = (byte) v;
Log.e("ETH_v", String.valueOf(v));
notifyOnNeedSendPayment(tx.getEncoded());
}
};
} }
public byte[] signToken(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception { private SignTask.PaymentToSign constructPaymentToken(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress) throws Exception {
BigInteger nonceValue = coinData.getConfirmedTXCount(); BigInteger nonceValue = coinData.getConfirmedTXCount();
byte[] pbKey = ctx.getCard().getWalletPublicKey(); byte[] pbKey = ctx.getCard().getWalletPublicKey();
// boolean flag = (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer); // boolean flag = (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer);
Issuer issuer = ctx.getCard().getIssuer(); // Issuer issuer = ctx.getCard().getIssuer();
BigInteger gigaK = BigInteger.valueOf(1000000000L); // BigInteger gigaK = BigInteger.valueOf(1000000000L);
BigInteger weiFee = convertToInternalAmount(feeValue).toBigIntegerExact(); BigInteger weiFee = convertToInternalAmount(feeValue).toBigIntegerExact();
@ -514,7 +540,6 @@ public class TokenEngine extends CoinEngine {
//amount = amount.subtract(fee); //amount = amount.subtract(fee);
BigInteger nonce = nonceValue;
BigInteger gasPrice = weiFee.divide(BigInteger.valueOf(60000)); BigInteger gasPrice = weiFee.divide(BigInteger.valueOf(60000));
BigInteger gasLimit = BigInteger.valueOf(60000); BigInteger gasLimit = BigInteger.valueOf(60000);
Integer chainId = EthTransaction.ChainEnum.Mainnet.getValue(); Integer chainId = EthTransaction.ChainEnum.Mainnet.getValue();
@ -545,41 +570,212 @@ public class TokenEngine extends CoinEngine {
byte[] data = BTCUtils.fromHex(cmd); byte[] data = BTCUtils.fromHex(cmd);
EthTransaction tx = EthTransaction.create(contractAddress, amountZero, nonce, gasPrice, gasLimit, chainId, data); EthTransaction tx = EthTransaction.create(contractAddress, amountZero, nonceValue, gasPrice, gasLimit, chainId, data);
byte[][] hashesForSign = new byte[1][]; return new SignTask.PaymentToSign() {
byte[] for_hash = tx.getRawHash(); @Override
hashesForSign[0] = for_hash; public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) {
return signingMethod==TangemCard.SigningMethod.Sign_Hash;
}
byte[] signFromCard = null; @Override
try { public byte[][] getHashesToSign() {
signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), hashesForSign, null, null, null).getTLV(TLV.Tag.TAG_Signature).Value; byte[][] hashesForSign = new byte[1][];
// TODO slice signFromCard to hashes.length parts hashesForSign[0] = tx.getRawHash();
} catch (Exception ex) { return hashesForSign;
Log.e("ETH", ex.getMessage()); }
return null;
}
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)); @Override
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)); public byte[] getRawDataToSign() throws Exception {
s = CryptoUtil.toCanonicalised(s); throw new Exception("Signing of raw transaction not supported for ETH");
}
boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey); @Override
public String getHashAlgToSign() throws Exception {
throw new Exception("Signing of raw transaction not supported for ETH");
}
if (!f) { @Override
Log.e("ETH-CHECK", "sign Failed."); public byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer) throws Exception {
} throw new Exception("Transaction validation by issuer not supported in this version");
}
tx.signature = new ECDSASignatureETH(r, s); @Override
int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey); public void onSignCompleted(byte[] signFromCard) throws Exception {
if (v != 27 && v != 28) { byte[] for_hash = tx.getRawHash();
Log.e("ETH", "invalid v"); BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32));
return null; BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64));
} s = CryptoUtil.toCanonicalised(s);
tx.signature.v = (byte) v;
Log.e("ETH_v", String.valueOf(v)); boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey);
if (!f) {
Log.e("ETH-CHECK", "sign Failed.");
}
tx.signature = new ECDSASignatureETH(r, s);
int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey);
if (v != 27 && v != 28) {
Log.e("ETH", "invalid v");
throw new Exception("Error in EthEngine - invalid v");
}
tx.signature.v = (byte) v;
Log.e("ETH_v", String.valueOf(v));
notifyOnNeedSendPayment(tx.getEncoded());
}
};
byte[] realTX = tx.getEncoded();
return realTX;
} }
// public byte[] signETH(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception {
// BigInteger nonceValue = coinData.getConfirmedTXCount();
// byte[] pbKey = ctx.getCard().getWalletPublicKey();
//// boolean flag = (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer);
// Issuer issuer = ctx.getCard().getIssuer();
//
// BigInteger weiFee = convertToInternalAmount(feeValue).toBigIntegerExact();
// BigInteger weiAmount = convertToInternalAmount(amountValue).toBigIntegerExact();
//
// if (IncFee) {
// weiAmount = weiAmount.subtract(weiFee);
// }
//
// BigInteger nonce = nonceValue;
// BigInteger gasPrice = weiFee.divide(BigInteger.valueOf(21000));
// BigInteger gasLimit = BigInteger.valueOf(21000);
// Integer chainId = ctx.getBlockchain() == Blockchain.Ethereum ? EthTransaction.ChainEnum.Mainnet.getValue() : EthTransaction.ChainEnum.Rinkeby.getValue();
// Integer chainId = EthTransaction.ChainEnum.Mainnet.getValue(); // Token support on main net only!!!
//
// String to = targetAddress;
//
// if (to.startsWith("0x") || to.startsWith("0X")) {
// to = to.substring(2);
// }
//
// EthTransaction tx = EthTransaction.create(to, weiAmount, nonce, gasPrice, gasLimit, chainId);
//
// byte[][] hashesForSign = new byte[1][];
// byte[] for_hash = tx.getRawHash();
// hashesForSign[0] = for_hash;
//
// byte[] signFromCard = null;
// try {
// signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), hashesForSign, null, null, null).getTLV(TLV.Tag.TAG_Signature).Value;
// // TODO slice signFromCard to hashes.length parts
// } catch (Exception ex) {
// Log.e("ETH", ex.getMessage());
// return null;
// }
//
// BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32));
// BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64));
// s = CryptoUtil.toCanonicalised(s);
//
// boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey);
//
// if (!f) {
// Log.e("ETH-CHECK", "sign Failed.");
// }
//
// tx.signature = new ECDSASignatureETH(r, s);
// int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey);
// if (v != 27 && v != 28) {
// Log.e("ETH", "invalid v");
// return null;
// }
// tx.signature.v = (byte) v;
// Log.e("ETH_v", String.valueOf(v));
//
// byte[] realTX = tx.getEncoded();
// return realTX;
// }
// public byte[] signToken(Amount feeValue, Amount amountValue, boolean IncFee, String targetAddress, CardProtocol protocol) throws Exception {
// BigInteger nonceValue = coinData.getConfirmedTXCount();
// byte[] pbKey = ctx.getCard().getWalletPublicKey();
//// boolean flag = (ctx.getCard().getSigningMethod() == TangemCard.SigningMethod.Sign_Hash_Validated_By_Issuer);
// Issuer issuer = ctx.getCard().getIssuer();
//
//
// BigInteger gigaK = BigInteger.valueOf(1000000000L);
//
// BigInteger weiFee = convertToInternalAmount(feeValue).toBigIntegerExact();
//
// InternalAmount amountDec=convertToInternalAmount(amountValue);
// BigInteger amount = amountDec.toBigInteger(); //new BigInteger(amountValue, 10);
//
//
// //amount = amount.subtract(fee);
//
// BigInteger nonce = nonceValue;
// BigInteger gasPrice = weiFee.divide(BigInteger.valueOf(60000));
// BigInteger gasLimit = BigInteger.valueOf(60000);
// Integer chainId = EthTransaction.ChainEnum.Mainnet.getValue();
// BigInteger amountZero = BigInteger.ZERO;
//
// String to = targetAddress;
//
// if (to.startsWith("0x") || to.startsWith("0X")) {
// to = to.substring(2);
// }
//
// String contractAddress = getContractAddress(ctx.getCard());
//
// if (contractAddress.startsWith("0x") || contractAddress.startsWith("0X")) {
// contractAddress = contractAddress.substring(2);
// }
//
// String amountLeadZero = amount.toString(16);
// if (amountLeadZero.startsWith("0x") || amountLeadZero.startsWith("0X")) {
// amountLeadZero = amountLeadZero.substring(2);
// }
//
// while (amountLeadZero.length() < 64) {
// amountLeadZero = "0" + amountLeadZero;
// }
//
// String cmd = "a9059cbb000000000000000000000000" + to + amountLeadZero; //TODO only for BAT
//
//
// byte[] data = BTCUtils.fromHex(cmd);
// EthTransaction tx = EthTransaction.create(contractAddress, amountZero, nonce, gasPrice, gasLimit, chainId, data);
//
// byte[][] hashesForSign = new byte[1][];
// byte[] for_hash = tx.getRawHash();
// hashesForSign[0] = for_hash;
//
// byte[] signFromCard = null;
// try {
// signFromCard = protocol.run_SignHashes(PINStorage.getPIN2(), hashesForSign, null, null, null).getTLV(TLV.Tag.TAG_Signature).Value;
// // TODO slice signFromCard to hashes.length parts
// } catch (Exception ex) {
// Log.e("ETH", ex.getMessage());
// return null;
// }
//
// BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32));
// BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64));
// s = CryptoUtil.toCanonicalised(s);
//
// boolean f = ECKey.verify(for_hash, new ECKey.ECDSASignature(r, s), pbKey);
//
// if (!f) {
// Log.e("ETH-CHECK", "sign Failed.");
// }
//
// tx.signature = new ECDSASignatureETH(r, s);
// int v = tx.BruteRecoveryID2(tx.signature, for_hash, pbKey);
// if (v != 27 && v != 28) {
// Log.e("ETH", "invalid v");
// return null;
// }
// tx.signature.v = (byte) v;
// Log.e("ETH_v", String.valueOf(v));
//
// byte[] realTX = tx.getEncoded();
// return realTX;
// }
} }

View file

@ -109,13 +109,6 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
// requestElectrum(ctx.card, ElectrumRequest.checkBalance(ctx.card!!.wallet)) // requestElectrum(ctx.card, ElectrumRequest.checkBalance(ctx.card!!.wallet))
calcSize = 256
try {
calcSize = buildSize(etWallet!!.text.toString(), "0.00", etAmount.text.toString())
} catch (ex: Exception) {
Log.e("Build Fee error", ex.message)
}
ctx.coinData!!.resetFailedBalanceRequestCounter() ctx.coinData!!.resetFailedBalanceRequestCounter()
progressBar.visibility = View.VISIBLE progressBar.visibility = View.VISIBLE
@ -444,8 +437,8 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
// Log.e("TX_HASH_1", BTCUtils.toHex(hashData)) // Log.e("TX_HASH_1", BTCUtils.toHex(hashData))
// Log.e("TX_HASH_2", BTCUtils.toHex(doubleHashData)) // Log.e("TX_HASH_2", BTCUtils.toHex(doubleHashData))
unspentOutputs[i].bodyDoubleHash = doubleHashData // unspentOutputs[i].bodyDoubleHash = doubleHashData
unspentOutputs[i].bodyHash = hashData // unspentOutputs[i].bodyHash = hashData
hashesForSign[i] = doubleHashData hashesForSign[i] = doubleHashData
} }
@ -478,6 +471,16 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
} }
private fun requestEstimateFee() { private fun requestEstimateFee() {
if( calcSize==0 )
{
calcSize = 256
try {
calcSize = buildSize(etWallet!!.text.toString(), "0.00", etAmount.text.toString())
} catch (ex: Exception) {
Log.e("Build Fee error", ex.message)
}
}
serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_PRIORITY) serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_PRIORITY)
serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_NORMAL) serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_NORMAL)
serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL) serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL)

View file

@ -13,19 +13,21 @@ import android.view.KeyEvent
import android.view.View import android.view.View
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.Toast import android.widget.Toast
import com.tangem.data.nfc.SignPaymentTask import com.tangem.domain.wallet.BTCUtils
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.domain.wallet.CoinEngine import com.tangem.domain.wallet.CoinEngine
import com.tangem.domain.wallet.CoinEngineFactory import com.tangem.domain.wallet.CoinEngineFactory
import com.tangem.domain.wallet.TangemContext import com.tangem.domain.wallet.TangemContext
import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog import com.tangem.presentation.dialog.NoExtendedLengthSupportDialog
import com.tangem.presentation.dialog.WaitSecurityDelayDialog import com.tangem.presentation.dialog.WaitSecurityDelayDialog
import com.tangem.tangemcard.data.Blockchain
import com.tangem.tangemcard.reader.CardProtocol
import com.tangem.tangemcard.reader.NfcManager
import com.tangem.tangemcard.tasks.SignTask import com.tangem.tangemcard.tasks.SignTask
import com.tangem.tangemcard.util.Util import com.tangem.tangemcard.util.Util
import com.tangem.wallet.R import com.tangem.wallet.R
import kotlinx.android.synthetic.main.activity_sign_payment.* import kotlinx.android.synthetic.main.activity_sign_payment.*
class SignPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications { class SignPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications {
companion object { companion object {
@ -136,129 +138,6 @@ class SignPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, Card
// signPaymentTask = SignPaymentTask(this, ctx, nfcManager, isoDep, this, amount, fee, isIncludeFee, outAddressStr) // signPaymentTask = SignPaymentTask(this, ctx, nfcManager, isoDep, this, amount, fee, isIncludeFee, outAddressStr)
val coinEngine= CoinEngineFactory.create(ctx) ?: throw CardProtocol.TangemException("Can't create CoinEngine!") val coinEngine= CoinEngineFactory.create(ctx) ?: throw CardProtocol.TangemException("Can't create CoinEngine!")
val paymentToSign = coinEngine.constructPayment(amount, fee, isIncludeFee, outAddressStr) coinEngine.setOnNeedSendPayment { tx->
if (tx != null) {
// object: SignTask.PaymentToSign { // [REDACTED_TODO_COMMENT]
// override fun call(context: String?) { println("Call: $context") }
// override fun run(context: String?) { println("Run: $context") }
// }
signPaymentTask = SignTask(this, ctx.card, nfcManager, isoDep, this, paymentToSign)
signPaymentTask!!.start()
} else {
// Log.d(TAG, "Mismatch card UID (" + sUID + " instead of " + card!!.uid + ")")
nfcManager!!.ignoreTag(isoDep.tag)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onReadStart(cardProtocol: CardProtocol) {
progressBar!!.post {
progressBar!!.visibility = View.VISIBLE
progressBar!!.progress = 5
}
}
override fun onReadProgress(protocol: CardProtocol, progress: Int) {
progressBar!!.post { progressBar!!.progress = progress }
}
override fun onReadFinish(cardProtocol: CardProtocol?) {
signPaymentTask = null
if (cardProtocol != null) {
if (cardProtocol.error == null) {
progressBar!!.post {
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.GREEN)
}
} else {
lastReadSuccess = false
if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) {
progressBar!!.post {
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.RED)
}
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
val intent = Intent()
intent.putExtra("message", getString(R.string.cannot_sign_transaction__make_sure_you_enter_correct_pin_2))
intent.putExtra("UID", cardProtocol.card.uid)
intent.putExtra("Card", cardProtocol.card.asBundle)
setResult(RESULT_INVALID_PIN, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
} else {
if (cardProtocol.error is CardProtocol.TangemException_WrongAmount) {
try {
val intent = Intent()
intent.putExtra("message", getString(R.string.cannot_sign_transaction_wrong_amount))
intent.putExtra("UID", cardProtocol.card.uid)
intent.putExtra("Card", cardProtocol.card.asBundle)
setResult(Activity.RESULT_CANCELED, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
}
progressBar!!.post {
if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allReadyShowed) {
NoExtendedLengthSupportDialog.message = getText(R.string.the_nfc_adapter_length_apdu).toString() + "\n" + getText(R.string.the_nfc_adapter_length_apdu_advice).toString()
NoExtendedLengthSupportDialog().show(supportFragmentManager, NoExtendedLengthSupportDialog.TAG)
}
} else {
Toast.makeText(baseContext, R.string.try_to_scan_again, Toast.LENGTH_LONG).show()
}
progressBar!!.progress = 100
progressBar!!.progressTintList = ColorStateList.valueOf(Color.RED)
}
}
}
}
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadCancel() {
signPaymentTask = null
progressBar!!.postDelayed({
try {
progressBar!!.progress = 0
progressBar!!.progressTintList = ColorStateList.valueOf(Color.DKGRAY)
progressBar!!.visibility = View.INVISIBLE
} catch (e: Exception) {
e.printStackTrace()
}
}, 500)
}
override fun onReadWait(msec: Int) {
WaitSecurityDelayDialog.OnReadWait(this, msec)
}
override fun onReadBeforeRequest(timeout: Int) {
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout)
}
override fun onReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(this)
}
}

View file

@ -22,15 +22,15 @@ public class SignTask extends CustomReadCardTask {
public interface PaymentToSign { public interface PaymentToSign {
boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod); boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod);
byte[][] getHashesToSign(); byte[][] getHashesToSign() throws Exception;
byte[] getRawDataToSign(); byte[] getRawDataToSign() throws Exception;
String getHashAlgToSign(); String getHashAlgToSign() throws Exception;
byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer); byte[] getIssuerTransactionSignature(byte[] dataToSignByIssuer) throws Exception;
void OnSignCompleted(byte[] signature); void onSignCompleted(byte[] signature) throws Exception;
} }
private PaymentToSign paymentToSign; private PaymentToSign paymentToSign;
@ -86,7 +86,7 @@ public class SignTask extends CustomReadCardTask {
throw new CardProtocol.TangemException("Signing method isn't supported!"); throw new CardProtocol.TangemException("Signing method isn't supported!");
} }
paymentToSign.OnSignCompleted(signResult.getTLV(TLV.Tag.TAG_Signature).Value); paymentToSign.onSignCompleted(signResult.getTLV(TLV.Tag.TAG_Signature).Value);
mNotifications.onReadProgress(protocol, 100); mNotifications.onReadProgress(protocol, 100);
} }