new Ledger interface

This commit is contained in:
m2049r 2018-09-27 07:53:01 +02:00 committed by m2049r
parent f01a8eac5d
commit 3e90f2e22e
10 changed files with 141 additions and 90 deletions

View File

@ -37,7 +37,8 @@ android {
abi { abi {
enable true enable true
reset() reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' //include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
include 'arm64-v8a'
universalApk true universalApk true
} }
} }

View File

@ -0,0 +1,88 @@
// Copyright (c) 2017-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#if defined(HAVE_MONERUJO)
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief LedgerFind - find Ledger Device and return it's name
* @param buffer - buffer for name of found device
* @param len - length of buffer
* @return 0 - success
* -1 - no device connected / found
* -2 - JVM not found
*/
int LedgerFind(char *buffer, size_t len);
/**
* @brief LedgerExchange - exchange data with Ledger Device
* @param command - buffer for data to send
* @param cmd_len - length of send to send
* @param response - buffer for received data
* @param max_resp_len - size of receive buffer
*
* @return length of received data in response or -1 if error
*/
int LedgerExchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len);
#ifdef __cplusplus
}
#endif
#include "device_io.hpp"
#pragma once
namespace hw {
namespace io {
class device_io_monerujo: device_io {
public:
device_io_monerujo() {};
~device_io_monerujo() {};
void init() {};
void release() {};
void connect(void *params) {};
void disconnect() {};
bool connected() const {return true;}; // monerujo is always connected before it gets here
// returns number of bytes read or -1 on error
int exchange(unsigned char *command, unsigned int cmd_len, unsigned char *response, unsigned int max_resp_len) {
return LedgerExchange(command, cmd_len, response, max_resp_len);
}
};
};
};
#endif //#if defined(HAVE_MONERUJO)

View File

@ -1386,24 +1386,22 @@ Java_com_m2049r_xmrwallet_model_WalletManager_setLogLevel(JNIEnv *env, jobject i
// Ledger Stuff // Ledger Stuff
// //
#include "monerujo_ledger.h" #include "device_io_monerujo.hpp"
/** /**
* @brief LedgerExchange - exchange data with Ledger Device * @brief LedgerExchange - exchange data with Ledger Device
* @param pbSendBuffer - buffer for data to send * @param command - buffer for data to send
* @param cbSendLength - length of send buffer * @param cmd_len - length of send to send
* @param pbRecvBuffer - buffer for received data * @param response - buffer for received data
* @param pcbRecvLength - pointer to size of receive buffer * @param max_resp_len - size of receive buffer
* gets set with length of received data on successful return *
* @return SCARD_S_SUCCESS - success * @return length of received data in response or -1 if error
* SCARD_E_NO_READERS_AVAILABLE - no device connected / found
* SCARD_E_INSUFFICIENT_BUFFER - pbRecvBuffer is too small for the response
*/ */
LONG LedgerExchange( int LedgerExchange(
LPCBYTE pbSendBuffer, unsigned char *command,
DWORD cbSendLength, unsigned int cmd_len,
LPBYTE pbRecvBuffer, unsigned char *response,
LPDWORD pcbRecvLength) { unsigned int max_resp_len) {
LOGD("LedgerExchange"); LOGD("LedgerExchange");
JNIEnv *jenv; JNIEnv *jenv;
int envStat = attachJVM(&jenv); int envStat = attachJVM(&jenv);
@ -1411,30 +1409,29 @@ LONG LedgerExchange(
jmethodID exchangeMethod = jenv->GetStaticMethodID(class_Ledger, "Exchange", "([B)[B"); jmethodID exchangeMethod = jenv->GetStaticMethodID(class_Ledger, "Exchange", "([B)[B");
jsize sendLen = static_cast<jsize>(cbSendLength); jsize sendLen = static_cast<jsize>(cmd_len);
jbyteArray dataSend = jenv->NewByteArray(sendLen); jbyteArray dataSend = jenv->NewByteArray(sendLen);
jenv->SetByteArrayRegion(dataSend, 0, sendLen, (jbyte *) pbSendBuffer); jenv->SetByteArrayRegion(dataSend, 0, sendLen, (jbyte *) command);
jbyteArray dataRecv = (jbyteArray) jenv->CallStaticObjectMethod(class_Ledger, exchangeMethod, jbyteArray dataRecv = (jbyteArray) jenv->CallStaticObjectMethod(class_Ledger, exchangeMethod,
dataSend); dataSend);
jenv->DeleteLocalRef(dataSend); jenv->DeleteLocalRef(dataSend);
if (dataRecv == nullptr) { if (dataRecv == nullptr) {
detachJVM(jenv, envStat); detachJVM(jenv, envStat);
LOGD("LedgerExchange SCARD_E_NO_READERS_AVAILABLE"); LOGD("LedgerExchange SCARD_E_NO_READERS_AVAILABLE");
return SCARD_E_NO_READERS_AVAILABLE; return -1;
} }
jsize len = jenv->GetArrayLength(dataRecv); jsize len = jenv->GetArrayLength(dataRecv);
LOGD("LedgerExchange SCARD_S_SUCCESS %ld/%d", cbSendLength, len); LOGD("LedgerExchange SCARD_S_SUCCESS %ld/%d", cmd_len, len);
if (len <= *pcbRecvLength) { if (len <= max_resp_len) {
*pcbRecvLength = static_cast<DWORD>(len); jenv->GetByteArrayRegion(dataRecv, 0, len, (jbyte *) response);
jenv->GetByteArrayRegion(dataRecv, 0, len, (jbyte *) pbRecvBuffer);
jenv->DeleteLocalRef(dataRecv); jenv->DeleteLocalRef(dataRecv);
detachJVM(jenv, envStat); detachJVM(jenv, envStat);
return SCARD_S_SUCCESS; return static_cast<int>(len);;
} else { } else {
jenv->DeleteLocalRef(dataRecv); jenv->DeleteLocalRef(dataRecv);
detachJVM(jenv, envStat); detachJVM(jenv, envStat);
LOGE("LedgerExchange SCARD_E_INSUFFICIENT_BUFFER"); LOGE("LedgerExchange SCARD_E_INSUFFICIENT_BUFFER");
return SCARD_E_INSUFFICIENT_BUFFER; return -1;
} }
} }

View File

@ -1,46 +0,0 @@
/**
* Copyright (c) 2018 m2049r
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef XMRWALLET_LEDGER_H
#define XMRWALLET_LEDGER_H
#ifdef __cplusplus
extern "C"
{
#endif
#define SCARD_S_SUCCESS ((LONG)0x00000000) /**< No error was encountered. */
#define SCARD_E_INSUFFICIENT_BUFFER ((LONG)0x80100008) /**< The data buffer to receive returned data is too small for the returned data. */
#define SCARD_E_NO_READERS_AVAILABLE ((LONG)0x8010002E) /**< Cannot find a smart card reader. */
typedef long LONG;
typedef unsigned long DWORD;
typedef DWORD *LPDWORD;
typedef unsigned char BYTE;
typedef BYTE *LPBYTE;
typedef const BYTE *LPCBYTE;
typedef char CHAR;
typedef CHAR *LPSTR;
int LedgerFind(char *buffer, size_t len);
LONG LedgerExchange(LPCBYTE pbSendBuffer, DWORD cbSendLength, LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength);
#ifdef __cplusplus
}
#endif
#endif //XMRWALLET_LEDGER_H

View File

@ -1177,6 +1177,8 @@ public class LoginActivity extends BaseActivity
case Device_Ledger: case Device_Ledger:
if (!hasLedger()) { if (!hasLedger()) {
toast(R.string.open_wallet_ledger_missing); toast(R.string.open_wallet_ledger_missing);
} else {
startWallet(walletName, password, fingerprintUsed);
} }
break; break;
default: default:

View File

@ -529,16 +529,23 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
} }
@Override @Override
public void onWalletStarted(final boolean success) { public void onWalletStarted(final Wallet.ConnectionStatus connStatus) {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
public void run() { public void run() {
dismissProgressDialog(); dismissProgressDialog();
if (!success) { switch (connStatus) {
case ConnectionStatus_Disconnected:
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_failed), Toast.LENGTH_LONG).show(); Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_failed), Toast.LENGTH_LONG).show();
break;
case ConnectionStatus_WrongVersion:
Toast.makeText(WalletActivity.this, getString(R.string.status_wallet_connect_wrongversion), Toast.LENGTH_LONG).show();
break;
case ConnectionStatus_Connected:
break;
} }
} }
}); });
if (!success) { if (connStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) {
finish(); finish();
} else { } else {
haveWallet = true; haveWallet = true;

View File

@ -34,11 +34,9 @@ import java.io.IOException;
import timber.log.Timber; import timber.log.Timber;
public class Ledger { public class Ledger {
// lookahead parameters as suggest on // 5:20 is same as wallet2.cpp::restore()
// https://monero.stackexchange.com/a/9902/8977 (Step 8) static public final int LOOKAHEAD_ACCOUNTS = 5;
// by dEBRUYNE static public final int LOOKAHEAD_SUBADDRESSES = 20;
static public final int LOOKAHEAD_ACCOUNTS = 3;
static public final int LOOKAHEAD_SUBADDRESSES = 100;
static public final String SUBADDRESS_LOOKAHEAD = LOOKAHEAD_ACCOUNTS + ":" + LOOKAHEAD_SUBADDRESSES; static public final String SUBADDRESS_LOOKAHEAD = LOOKAHEAD_ACCOUNTS + ":" + LOOKAHEAD_SUBADDRESSES;
public static final int SW_OK = 0x9000; public static final int SW_OK = 0x9000;

View File

@ -226,7 +226,7 @@ public class WalletService extends Service {
void onSetNotes(boolean success); void onSetNotes(boolean success);
void onWalletStarted(boolean success); void onWalletStarted(Wallet.ConnectionStatus walletStatus);
void onWalletOpen(Wallet.Device device); void onWalletOpen(Wallet.Device device);
} }
@ -293,9 +293,9 @@ public class WalletService extends Service {
if (walletId != null) { if (walletId != null) {
showProgress(getString(R.string.status_wallet_loading)); showProgress(getString(R.string.status_wallet_loading));
showProgress(10); showProgress(10);
boolean success = start(walletId, walletPw); Wallet.ConnectionStatus connStatus = start(walletId, walletPw);
if (observer != null) observer.onWalletStarted(success); if (observer != null) observer.onWalletStarted(connStatus);
if (!success) { if (connStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) {
errorState = true; errorState = true;
stop(); stop();
} }
@ -490,7 +490,7 @@ public class WalletService extends Service {
return true; // true is important so that onUnbind is also called next time return true; // true is important so that onUnbind is also called next time
} }
private boolean start(String walletName, String walletPassword) { private Wallet.ConnectionStatus start(String walletName, String walletPassword) {
Timber.d("start()"); Timber.d("start()");
startNotfication(); startNotfication();
showProgress(getString(R.string.status_wallet_loading)); showProgress(getString(R.string.status_wallet_loading));
@ -498,9 +498,11 @@ public class WalletService extends Service {
if (listener == null) { if (listener == null) {
Timber.d("start() loadWallet"); Timber.d("start() loadWallet");
Wallet aWallet = loadWallet(walletName, walletPassword); Wallet aWallet = loadWallet(walletName, walletPassword);
if ((aWallet == null) || (aWallet.getConnectionStatus() != Wallet.ConnectionStatus.ConnectionStatus_Connected)) { Wallet.ConnectionStatus connStatus = Wallet.ConnectionStatus.ConnectionStatus_Disconnected;
if (aWallet != null) connStatus = aWallet.getConnectionStatus();
if (connStatus != Wallet.ConnectionStatus.ConnectionStatus_Connected) {
if (aWallet != null) aWallet.close(); if (aWallet != null) aWallet.close();
return false; return connStatus;
} }
listener = new MyWalletListener(); listener = new MyWalletListener();
listener.start(); listener.start();
@ -511,7 +513,7 @@ public class WalletService extends Service {
// if we try to refresh the history here we get occasional segfaults! // if we try to refresh the history here we get occasional segfaults!
// doesnt matter since we update as soon as we get a new block anyway // doesnt matter since we update as soon as we get a new block anyway
Timber.d("start() done"); Timber.d("start() done");
return true; return Wallet.ConnectionStatus.ConnectionStatus_Connected;
} }
public void stop() { public void stop() {
@ -551,6 +553,7 @@ public class WalletService extends Service {
if (walletMgr.walletExists(path)) { if (walletMgr.walletExists(path)) {
Timber.d("open wallet %s", path); Timber.d("open wallet %s", path);
Wallet.Device device = WalletManager.getInstance().queryWalletDevice(path + ".keys", walletPassword); Wallet.Device device = WalletManager.getInstance().queryWalletDevice(path + ".keys", walletPassword);
Timber.d("device is %s", device.toString());
if (observer != null) observer.onWalletOpen(device); if (observer != null) observer.onWalletOpen(device);
wallet = walletMgr.openWallet(path, walletPassword); wallet = walletMgr.openWallet(path, walletPassword);
showProgress(60); showProgress(60);

View File

@ -119,6 +119,7 @@
<string name="status_wallet_unload_failed">Wallet save failed!</string> <string name="status_wallet_unload_failed">Wallet save failed!</string>
<string name="status_wallet_connecting">Connecting &#8230;</string> <string name="status_wallet_connecting">Connecting &#8230;</string>
<string name="status_wallet_connect_failed">Node connection failed!\nCheck username/password</string> <string name="status_wallet_connect_failed">Node connection failed!\nCheck username/password</string>
<string name="status_wallet_connect_wrongversion">Node version incompatible!</string>
<string name="status_wallet_connect_timeout">Node connection timed out!\nTry again or another.</string> <string name="status_wallet_connect_timeout">Node connection timed out!\nTry again or another.</string>
<string name="status_wallet_node_invalid">Node invalid!\nTry another.</string> <string name="status_wallet_node_invalid">Node invalid!\nTry another.</string>
<string name="status_wallet_connect_ioex">Cannot reach node!\nTry again or another.</string> <string name="status_wallet_connect_ioex">Cannot reach node!\nTry again or another.</string>

View File

@ -7,7 +7,7 @@ set -e
orig_path=$PATH orig_path=$PATH
packages=(boost openssl monero) packages=(boost openssl monero)
archs=(arm arm64 x86 x86_64) archs=(arm64 x86_64)
for arch in ${archs[@]}; do for arch in ${archs[@]}; do
case ${arch} in case ${arch} in
@ -32,11 +32,11 @@ for arch in ${archs[@]}; do
OUTPUT_DIR=`pwd`/$package/lib/$xarch OUTPUT_DIR=`pwd`/$package/lib/$xarch
mkdir -p $OUTPUT_DIR mkdir -p $OUTPUT_DIR
rm -f $OUTPUT_DIR/*.a rm -f $OUTPUT_DIR/*.a
cp -a /opt/android/build/$package/$arch/lib/*.a $OUTPUT_DIR cp -a /media/m2049r/DATA/android/external-libs/build/$package/$arch/lib/*.a $OUTPUT_DIR
if [ $package = "monero" -a -d "/opt/android/build/$package/include" ]; then if [ $package = "monero" -a -d "/opt/android/build/$package/include" ]; then
rm -rf $OUTPUT_DIR/../../include rm -rf $OUTPUT_DIR/../../include
cp -a /opt/android/build/$package/include $OUTPUT_DIR/../.. cp -a /media/m2049r/DATA/android/external-libs/build/$package/include $OUTPUT_DIR/../..
fi fi
done done