diff --git a/app/src/main/java/com/tangem/data/nfc/SignPaymentTask.java b/app/src/main/java/com/tangem/data/nfc/SignPaymentTask.java deleted file mode 100644 index 40dbb93549..0000000000 --- a/app/src/main/java/com/tangem/data/nfc/SignPaymentTask.java +++ /dev/null @@ -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(); - } - } - -} \ No newline at end of file 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 e3d6576bcd..f93c438f78 100644 --- a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java @@ -185,7 +185,7 @@ public abstract class CoinEngine { 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 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); + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/domain/wallet/UnspentOutputInfo.java b/app/src/main/java/com/tangem/domain/wallet/UnspentOutputInfo.java index ddd4b2a113..88191c50b6 100644 --- a/app/src/main/java/com/tangem/domain/wallet/UnspentOutputInfo.java +++ b/app/src/main/java/com/tangem/domain/wallet/UnspentOutputInfo.java @@ -13,8 +13,6 @@ public class UnspentOutputInfo { public final long confirmations; public String txHashForBuild; 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) { this.txHash = txHash; diff --git a/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java b/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java index 1c36a95f6e..f615742b96 100644 --- a/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/bch/BtcCashEngine.java @@ -17,6 +17,7 @@ import com.tangem.domain.wallet.TangemContext; import com.tangem.domain.wallet.Transaction; import com.tangem.domain.wallet.UnspentOutputInfo; import com.tangem.domain.wallet.BTCUtils; +import com.tangem.tangemcard.tasks.SignTask; import com.tangem.util.CryptoUtil; import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.util.DerEncodingUtil; @@ -433,15 +434,13 @@ public class BtcCashEngine extends CoinEngine { // 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(); - CoinEngine engine = CoinEngineFactory.INSTANCE.create(ctx); - - String srcLegacyAddress = ((BtcCashEngine)engine).convertToLegacyAddress(ctx.getCoinData().getWallet()); - String destLegacyAddress = ((BtcCashEngine)engine).convertToLegacyAddress(destAddress); + String srcLegacyAddress = convertToLegacyAddress(ctx.getCoinData().getWallet()); + String destLegacyAddress = convertToLegacyAddress(targetAddress); byte[] pbKey = ctx.getCard().getWalletPublicKeyRar(); //ALWAYS USING COMPRESS KEY // 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)); } - 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) { - 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); - byte[] doubleHashData = Util.calculateSHA256(hashData); + return new SignTask.PaymentToSign() { - 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; + @Override + public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) { + return signingMethod==TangemCard.SigningMethod.Sign_Hash || signingMethod==TangemCard.SigningMethod.Sign_Raw; } - } - - 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]); + @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]; + } + 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 - //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); + @Override + public byte[] getRawDataToSign() throws Exception { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + 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 rawTxList = coinData.getUnspentTransactions(); +// byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(srcLegacyAddress).bytes; +// +// // Collect unspent +// ArrayList 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); +// } } diff --git a/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java b/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java index eab7e750a7..8d3a950a1f 100644 --- a/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/btc/BtcEngine.java @@ -16,6 +16,7 @@ import com.tangem.domain.wallet.TangemContext; import com.tangem.domain.wallet.Transaction; import com.tangem.domain.wallet.UnspentOutputInfo; import com.tangem.domain.wallet.BTCUtils; +import com.tangem.tangemcard.tasks.SignTask; import com.tangem.util.CryptoUtil; import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.util.DerEncodingUtil; @@ -404,8 +405,8 @@ public class BtcEngine extends CoinEngine { } @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 unspentOutputs; checkBlockchainDataExists(); String myAddress = ctx.getCoinData().getWallet(); @@ -416,14 +417,13 @@ public class BtcEngine extends CoinEngine { byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; // Collect unspent - ArrayList unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend); + 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; @@ -433,53 +433,155 @@ public class BtcEngine extends CoinEngine { change = change - fees; } + final long amountFinal=amount; + final long changeFinal=change; + 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()][]; + 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) { - 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); - byte[] doubleHashData = Util.calculateSHA256(hashData); + return new SignTask.PaymentToSign() { - 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; + @Override + public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) { + return signingMethod==TangemCard.SigningMethod.Sign_Hash || signingMethod==TangemCard.SigningMethod.Sign_Raw; } - } - 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]); + @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]; + } + 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) { - 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); + @Override + public byte[] getRawDataToSign() throws Exception { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + 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 rawTxList = coinData.getUnspentTransactions(); +// byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes; +// +// // Collect unspent +// ArrayList 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); +// } + } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java b/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java index f7e3836c6d..7614c9a2c7 100644 --- a/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/eth/EthEngine.java @@ -18,6 +18,7 @@ import com.tangem.domain.wallet.Keccak256; import com.tangem.tangemcard.data.TangemCard; import com.tangem.domain.wallet.TangemContext; import com.tangem.domain.wallet.BTCUtils; +import com.tangem.tangemcard.tasks.SignTask; import com.tangem.util.CryptoUtil; import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.wallet.R; @@ -379,12 +380,9 @@ public class EthEngine extends CoinEngine { } @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(); 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(); @@ -404,41 +402,124 @@ public class EthEngine extends CoinEngine { 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][]; - byte[] for_hash = tx.getRawHash(); - hashesForSign[0] = for_hash; + return new SignTask.PaymentToSign() { + @Override + public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) { + return signingMethod==TangemCard.SigningMethod.Sign_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; - } + @Override + public byte[][] getHashesToSign() throws Exception { + byte[][] hashesForSign = new byte[1][]; + hashesForSign[0] = tx.getRawHash(); + return hashesForSign; + } - BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)); - s = CryptoUtil.toCanonicalised(s); + @Override + public byte[] getRawDataToSign() throws Exception { + 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) { - Log.e("ETH-CHECK", "sign Failed."); - } + @Override + 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); - 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)); + @Override + public void onSignCompleted(byte[] signFromCard) throws Exception { + byte[] for_hash=tx.getRawHash(); + BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)); + s = CryptoUtil.toCanonicalised(s); - byte[] realTX = tx.getEncoded(); - return realTX; + 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()); + } + }; } + +// @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; +// } } diff --git a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java index 5b6c5172f1..bfcd0a4d34 100644 --- a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java @@ -5,6 +5,7 @@ import android.text.InputFilter; import android.util.Log; import com.google.common.base.Strings; +import com.tangem.tangemcard.data.Blockchain; import com.tangem.tangemcard.data.local.PINStorage; import com.tangem.tangemcard.reader.CardProtocol; 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.domain.wallet.TangemContext; import com.tangem.domain.wallet.BTCUtils; +import com.tangem.tangemcard.tasks.SignTask; import com.tangem.util.CryptoUtil; import com.tangem.util.DecimalDigitsInputFilter; import com.tangem.wallet.R; @@ -345,7 +347,7 @@ public class TokenEngine extends CoinEngine { if( fee.compareTo(balance)>0 ) return false; } else if (amount.getCurrency().equals("ETH") && coinData.getBalanceInInternalUnits().isZero()) { - // standart ETH transaction + // standard ETH transaction try { BigDecimal cardBalance = getBalance(); @@ -425,31 +427,36 @@ public class TokenEngine extends CoinEngine { } @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")) { - return signETH(feeValue, amountValue, IncFee, targetAddress, protocol); + return constructPaymentETH(feeValue, amountValue, IncFee, targetAddress); } 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(); 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(); + 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!!! @@ -459,52 +466,71 @@ public class TokenEngine extends CoinEngine { 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][]; - byte[] for_hash = tx.getRawHash(); - hashesForSign[0] = for_hash; + return new SignTask.PaymentToSign() { + @Override + public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) { + return signingMethod==TangemCard.SigningMethod.Sign_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; - } + @Override + public byte[][] getHashesToSign() { + byte[][] hashesForSign = new byte[1][]; + hashesForSign[0] = tx.getRawHash(); + return hashesForSign; + } - BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)); - s = CryptoUtil.toCanonicalised(s); + @Override + public byte[] getRawDataToSign() throws Exception { + 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) { - Log.e("ETH-CHECK", "sign Failed."); - } + @Override + 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); - 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)); + @Override + public void onSignCompleted(byte[] signFromCard) throws Exception { + byte[] for_hash=tx.getRawHash(); + BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)); + s = CryptoUtil.toCanonicalised(s); - byte[] realTX = tx.getEncoded(); - return realTX; + 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()); + } + }; } - 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(); byte[] pbKey = ctx.getCard().getWalletPublicKey(); // 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(); @@ -514,7 +540,6 @@ public class TokenEngine extends CoinEngine { //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(); @@ -545,41 +570,212 @@ public class TokenEngine extends CoinEngine { 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][]; - byte[] for_hash = tx.getRawHash(); - hashesForSign[0] = for_hash; + return new SignTask.PaymentToSign() { + @Override + public boolean isSigningMethodSupported(TangemCard.SigningMethod signingMethod) { + return signingMethod==TangemCard.SigningMethod.Sign_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; - } + @Override + public byte[][] getHashesToSign() { + byte[][] hashesForSign = new byte[1][]; + hashesForSign[0] = tx.getRawHash(); + return hashesForSign; + } - BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)); - s = CryptoUtil.toCanonicalised(s); + @Override + public byte[] getRawDataToSign() throws Exception { + 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) { - Log.e("ETH-CHECK", "sign Failed."); - } + @Override + 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); - 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)); + @Override + public void onSignCompleted(byte[] signFromCard) throws Exception { + byte[] for_hash = tx.getRawHash(); + 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"); + 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; +// } } diff --git a/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.kt b/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.kt index 61e1b95dab..b2ffd02cda 100644 --- a/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.kt +++ b/app/src/main/java/com/tangem/presentation/activity/ConfirmPaymentActivity.kt @@ -109,13 +109,6 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { // 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() 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_2", BTCUtils.toHex(doubleHashData)) - unspentOutputs[i].bodyDoubleHash = doubleHashData - unspentOutputs[i].bodyHash = hashData +// unspentOutputs[i].bodyDoubleHash = doubleHashData +// unspentOutputs[i].bodyHash = hashData hashesForSign[i] = doubleHashData } @@ -478,6 +471,16 @@ class ConfirmPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { } 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_NORMAL) serverApiCommon.estimateFee(ServerApiCommon.ESTIMATE_FEE_MINIMAL) diff --git a/app/src/main/java/com/tangem/presentation/activity/SignPaymentActivity.kt b/app/src/main/java/com/tangem/presentation/activity/SignPaymentActivity.kt index 1436f8d288..4d778acd70 100644 --- a/app/src/main/java/com/tangem/presentation/activity/SignPaymentActivity.kt +++ b/app/src/main/java/com/tangem/presentation/activity/SignPaymentActivity.kt @@ -13,19 +13,21 @@ import android.view.KeyEvent import android.view.View import android.widget.ProgressBar import android.widget.Toast -import com.tangem.data.nfc.SignPaymentTask -import com.tangem.tangemcard.reader.CardProtocol -import com.tangem.tangemcard.reader.NfcManager +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.dialog.NoExtendedLengthSupportDialog 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.util.Util import com.tangem.wallet.R import kotlinx.android.synthetic.main.activity_sign_payment.* + class SignPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, CardProtocol.Notifications { companion object { @@ -136,129 +138,6 @@ class SignPaymentActivity : AppCompatActivity(), NfcAdapter.ReaderCallback, Card // signPaymentTask = SignPaymentTask(this, ctx, nfcManager, isoDep, this, amount, fee, isIncludeFee, outAddressStr) val coinEngine= CoinEngineFactory.create(ctx) ?: throw CardProtocol.TangemException("Can't create CoinEngine!") - val paymentToSign = coinEngine.constructPayment(amount, fee, isIncludeFee, outAddressStr) - -// object: SignTask.PaymentToSign { -// 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) - } - -} \ No newline at end of file + coinEngine.setOnNeedSendPayment { tx-> + if (tx != null) { + // [REDACTED_TODO_COMMENT] \ No newline at end of file diff --git a/tangemcard/src/main/java/com/tangem/tangemcard/tasks/SignTask.java b/tangemcard/src/main/java/com/tangem/tangemcard/tasks/SignTask.java index 75a3bdd830..66e7721ee0 100644 --- a/tangemcard/src/main/java/com/tangem/tangemcard/tasks/SignTask.java +++ b/tangemcard/src/main/java/com/tangem/tangemcard/tasks/SignTask.java @@ -22,15 +22,15 @@ public class SignTask extends CustomReadCardTask { public interface PaymentToSign { 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; @@ -86,7 +86,7 @@ public class SignTask extends CustomReadCardTask { 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); }