Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ledger monero fix #1834

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,4 @@ configurations {
implementation.exclude module:'proto-google-common-protos'
implementation.exclude module:'protolite-well-known-types'
implementation.exclude module:'protobuf-javalite'
}
}
2 changes: 1 addition & 1 deletion cw_core/lib/hardware/device_connection_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ enum DeviceConnectionType {
static List<DeviceConnectionType> supportedConnectionTypes(WalletType walletType,
[bool isIOS = false]) {
switch (walletType) {
// case WalletType.monero:
case WalletType.monero:
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.ethereum:
Expand Down
13 changes: 10 additions & 3 deletions cw_monero/lib/api/transaction_history.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,16 @@ String? commitTransactionFromPointerAddress({required int address, required bool
commitTransaction(transactionPointer: monero.PendingTransaction.fromAddress(address), useUR: useUR);

String? commitTransaction({required monero.PendingTransaction transactionPointer, required bool useUR}) {
final transactionPointerAddress = transactionPointer.address;
final txCommit = useUR
? monero.PendingTransaction_commitUR(transactionPointer, 120)
: monero.PendingTransaction_commit(transactionPointer, filename: '', overwrite: false);
? monero.PendingTransaction_commitUR(transactionPointer, 120)
: Isolate.run(() {
monero.PendingTransaction_commit(
Pointer.fromAddress(transactionPointerAddress),
filename: '',
overwrite: false,
);
});

String? error = (() {
final status = monero.PendingTransaction_status(transactionPointer.cast());
Expand All @@ -221,7 +228,7 @@ String? commitTransaction({required monero.PendingTransaction transactionPointer
})();

}
if (error != null) {
if (error != null && error != "no tx keys found for this txid") {
throw CreationTransactionException(message: error);
}
if (useUR) {
Expand Down
70 changes: 67 additions & 3 deletions cw_monero/lib/ledger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import 'dart:async';
import 'dart:ffi';
import 'dart:typed_data';

import 'package:collection/collection.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:ffi/ffi.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus_dart.dart';
import 'package:monero/monero.dart' as monero;
// import 'package:polyseed/polyseed.dart';

LedgerConnection? gLedger;

Expand All @@ -28,9 +29,16 @@ void enableLedgerExchange(monero.wallet ptr, LedgerConnection connection) {
ptr, emptyPointer.cast<UnsignedChar>(), 0);
malloc.free(emptyPointer);

// printV("> ${ledgerRequest.toHexString()}");
_logLedgerCommand(ledgerRequest, false);
final response = await exchange(connection, ledgerRequest);
// printV("< ${response.toHexString()}");
_logLedgerCommand(response, true);

if (ListEquality().equals(response, [0x55, 0x15])) {
await connection.disconnect();
// // TODO: Show POPUP pls unlock your device
// await Future.delayed(Duration(seconds: 15));
// response = await exchange(connection, ledgerRequest);
}

final Pointer<Uint8> result = malloc<Uint8>(response.length);
for (var i = 0; i < response.length; i++) {
Expand Down Expand Up @@ -82,3 +90,59 @@ class ExchangeOperation extends LedgerRawOperation<Uint8List> {
@override
Future<List<Uint8List>> write(ByteDataWriter writer) async => [inputData];
}

const _ledgerMoneroCommands = {
0x00: "INS_NONE",
0x02: "INS_RESET",
0x20: "INS_GET_KEY",
0x21: "INS_DISPLAY_ADDRESS",
0x22: "INS_PUT_KEY",
0x24: "INS_GET_CHACHA8_PREKEY",
0x26: "INS_VERIFY_KEY",
0x28: "INS_MANAGE_SEEDWORDS",
0x30: "INS_SECRET_KEY_TO_PUBLIC_KEY",
0x32: "INS_GEN_KEY_DERIVATION",
0x34: "INS_DERIVATION_TO_SCALAR",
0x36: "INS_DERIVE_PUBLIC_KEY",
0x38: "INS_DERIVE_SECRET_KEY",
0x3A: "INS_GEN_KEY_IMAGE",
0x3B: "INS_DERIVE_VIEW_TAG",
0x3C: "INS_SECRET_KEY_ADD",
0x3E: "INS_SECRET_KEY_SUB",
0x40: "INS_GENERATE_KEYPAIR",
0x42: "INS_SECRET_SCAL_MUL_KEY",
0x44: "INS_SECRET_SCAL_MUL_BASE",
0x46: "INS_DERIVE_SUBADDRESS_PUBLIC_KEY",
0x48: "INS_GET_SUBADDRESS",
0x4A: "INS_GET_SUBADDRESS_SPEND_PUBLIC_KEY",
0x4C: "INS_GET_SUBADDRESS_SECRET_KEY",
0x70: "INS_OPEN_TX",
0x72: "INS_SET_SIGNATURE_MODE",
0x74: "INS_GET_ADDITIONAL_KEY",
0x76: "INS_STEALTH",
0x77: "INS_GEN_COMMITMENT_MASK",
0x78: "INS_BLIND",
0x7A: "INS_UNBLIND",
0x7B: "INS_GEN_TXOUT_KEYS",
0x7D: "INS_PREFIX_HASH",
0x7C: "INS_VALIDATE",
0x7E: "INS_MLSAG",
0x7F: "INS_CLSAG",
0x80: "INS_CLOSE_TX",
0xA0: "INS_GET_TX_PROOF",
0xC0: "INS_GET_RESPONSE"
};

void _logLedgerCommand(Uint8List command, [bool isResponse = true]) {
String toHexString(Uint8List data) =>
data.map((e) => e.toRadixString(16).padLeft(2, '0')).join();



if (isResponse) {
printV("< ${toHexString(command)}");
} else {
printV(
"> ${_ledgerMoneroCommands[command[1]]} ${toHexString(command.sublist(2))}");
}
}
56 changes: 26 additions & 30 deletions cw_monero/lib/monero_wallet_service.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:ffi';
import 'dart:io';

import 'package:cw_core/get_height_by_date.dart';
import 'package:cw_core/monero_wallet_utils.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/unspent_coins_info.dart';
Expand All @@ -9,16 +11,16 @@ import 'package:cw_core/wallet_credentials.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cw_core/get_height_by_date.dart';
import 'package:cw_monero/api/account_list.dart';
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
import 'package:cw_monero/api/wallet_manager.dart';
import 'package:cw_monero/ledger.dart';
import 'package:cw_monero/monero_wallet.dart';
import 'package:collection/collection.dart';
import 'package:hive/hive.dart';
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
import 'package:polyseed/polyseed.dart';
import 'package:monero/monero.dart' as monero;
import 'package:polyseed/polyseed.dart';

class MoneroNewWalletCredentials extends WalletCredentials {
MoneroNewWalletCredentials(
Expand Down Expand Up @@ -133,14 +135,12 @@ class MoneroWalletService extends WalletService<
try {
final path = await pathForWallet(name: name, type: getType());

if (walletFilesExist(path)) {
await repairOldAndroidWallet(name);
}
if (walletFilesExist(path)) await repairOldAndroidWallet(name);

await monero_wallet_manager
.openWalletAsync({'path': path, 'password': password});
final walletInfo = walletInfoSource.values.firstWhere(
(info) => info.id == WalletBase.idFor(name, getType()));
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
final wallet = MoneroWallet(
walletInfo: walletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
Expand Down Expand Up @@ -204,7 +204,7 @@ class MoneroWalletService extends WalletService<
@override
Future<void> rename(String currentName, String password, String newName) async {
final currentWalletInfo = walletInfoSource.values.firstWhere(
(info) => info.id == WalletBase.idFor(currentName, getType()));
(info) => info.id == WalletBase.idFor(currentName, getType()));
final currentWallet = MoneroWallet(
walletInfo: currentWalletInfo,
unspentCoinsInfo: unspentCoinsInfoSource,
Expand Down Expand Up @@ -255,14 +255,14 @@ class MoneroWalletService extends WalletService<
final password = credentials.password;
final height = credentials.height;

if (wptr == null ) monero_wallet_manager.createWalletPointer();
if (wptr == null) monero_wallet_manager.createWalletPointer();

enableLedgerExchange(wptr!, credentials.ledgerConnection);
await monero_wallet_manager.restoreWalletFromHardwareWallet(
path: path,
password: password!,
restoreHeight: height!,
deviceName: 'Ledger');
path: path,
password: password!,
restoreHeight: height!,
deviceName: 'Ledger');

final wallet = MoneroWallet(
walletInfo: credentials.walletInfo!,
Expand All @@ -279,7 +279,8 @@ class MoneroWalletService extends WalletService<
}

@override
Future<MoneroWallet> restoreFromSeed(MoneroRestoreWalletFromSeedCredentials credentials,
Future<MoneroWallet> restoreFromSeed(
MoneroRestoreWalletFromSeedCredentials credentials,
{bool? isTestnet}) async {
// Restore from Polyseed
if (Polyseed.isValidSeed(credentials.mnemonic)) {
Expand Down Expand Up @@ -313,7 +314,8 @@ class MoneroWalletService extends WalletService<
final path = await pathForWallet(name: credentials.name, type: getType());
final polyseedCoin = PolyseedCoin.POLYSEED_MONERO;
final lang = PolyseedLang.getByPhrase(credentials.mnemonic);
final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin);
final polyseed =
Polyseed.decode(credentials.mnemonic, lang, polyseedCoin);

return _restoreFromPolyseed(
path, credentials.password!, polyseed, credentials.walletInfo!, lang);
Expand Down Expand Up @@ -355,24 +357,18 @@ class MoneroWalletService extends WalletService<

Future<void> repairOldAndroidWallet(String name) async {
try {
if (!Platform.isAndroid) {
return;
}
if (!Platform.isAndroid) return;

final oldAndroidWalletDirPath = await outdatedAndroidPathForWalletDir(name: name);
final dir = Directory(oldAndroidWalletDirPath);

if (!dir.existsSync()) {
return;
}
if (!dir.existsSync()) return;

final newWalletDirPath = await pathForWalletDir(name: name, type: getType());

dir.listSync().forEach((f) {
final file = File(f.path);
final name = f.path
.split('/')
.last;
final name = f.path.split('/').last;
final newPath = newWalletDirPath + '/$name';
final newFile = File(newPath);

Expand All @@ -391,9 +387,7 @@ class MoneroWalletService extends WalletService<
try {
final path = await pathForWallet(name: name, type: getType());

if (walletFilesExist(path)) {
await repairOldAndroidWallet(name);
}
if (walletFilesExist(path)) await repairOldAndroidWallet(name);

await monero_wallet_manager.openWalletAsync({'path': path, 'password': password});
final walletInfo = walletInfoSource.values
Expand All @@ -412,8 +406,10 @@ class MoneroWalletService extends WalletService<

@override
bool requireHardwareWalletConnection(String name) {
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
return walletInfo.isHardwareWallet;
return walletInfoSource.values
.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(name, getType()))
?.isHardwareWallet ??
false;
}
}
25 changes: 14 additions & 11 deletions lib/reactions/on_authentication_state_change.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,31 @@ void startAuthenticationStateChange(
} catch (error, stack) {
loginError = error;
await ExceptionHandler.resetLastPopupDate();
await ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack));
await ExceptionHandler.onError(
FlutterErrorDetails(exception: error, stack: stack));
}
return;
}

if (state == AuthenticationState.allowed) {
if (requireHardwareWalletConnection()) {
if ([AuthenticationState.allowed, AuthenticationState.allowedCreate]
.contains(state)) {
if (state == AuthenticationState.allowed &&
requireHardwareWalletConnection()) {
await navigatorKey.currentState!.pushNamedAndRemoveUntil(
Routes.connectDevices,
(route) => false,
arguments: ConnectDevicePageParams(
walletType: WalletType.monero,
onConnectDevice: (context, ledgerVM) async {
monero!.setGlobalLedgerConnection(ledgerVM.connection);
showPopUp<void>(
context: context,
builder: (BuildContext context) => AlertWithOneAction(
alertTitle: S.of(context).proceed_on_device,
alertContent: S.of(context).proceed_on_device_description,
buttonText: S.of(context).cancel,
buttonAction: () => Navigator.of(context).pop()),
);
showPopUp<void>(
context: context,
builder: (BuildContext context) => AlertWithOneAction(
alertTitle: S.of(context).proceed_on_device,
alertContent: S.of(context).proceed_on_device_description,
buttonText: S.of(context).cancel,
buttonAction: () => Navigator.of(context).pop()),
);
await loadCurrentWallet();
getIt.get<BottomSheetService>().resetCurrentSheet();
await navigatorKey.currentState!
Expand Down
20 changes: 17 additions & 3 deletions lib/src/screens/connect_device/connect_device_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,45 @@ class ConnectDevicePageParams {
final WalletType walletType;
final OnConnectDevice onConnectDevice;
final bool allowChangeWallet;
final bool allowBack;

ConnectDevicePageParams({
required this.walletType,
required this.onConnectDevice,
this.allowChangeWallet = false,
this.allowBack = true,
});
}

class ConnectDevicePage extends BasePage {
final WalletType walletType;
final OnConnectDevice onConnectDevice;
final bool allowChangeWallet;
final bool allowBack;
final LedgerViewModel ledgerVM;

ConnectDevicePage(ConnectDevicePageParams params, this.ledgerVM)
: walletType = params.walletType,
onConnectDevice = params.onConnectDevice,
allowChangeWallet = params.allowChangeWallet;
allowChangeWallet = params.allowChangeWallet,
allowBack = params.allowBack;

@override
String get title => S.current.restore_title_from_hardware_wallet;

@override
Widget body(BuildContext context) => ConnectDevicePageBody(
walletType, onConnectDevice, allowChangeWallet, ledgerVM);
Widget? leading(BuildContext context) =>
allowBack ? super.leading(context) : null;

@override
Widget body(BuildContext context) => PopScope(
canPop: allowBack,
child: ConnectDevicePageBody(
walletType,
onConnectDevice,
allowChangeWallet,
ledgerVM,
));
}

class ConnectDevicePageBody extends StatefulWidget {
Expand Down
5 changes: 2 additions & 3 deletions lib/src/screens/restore/restore_options_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
}

if (isMoneroOnly) {
// return DeviceConnectionType.supportedConnectionTypes(WalletType.monero, Platform.isIOS)
// .isNotEmpty;
return false;
return DeviceConnectionType.supportedConnectionTypes(WalletType.monero, Platform.isIOS)
.isNotEmpty;
}

return true;
Expand Down
5 changes: 4 additions & 1 deletion lib/src/screens/send/send_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,10 @@ class SendPage extends BasePage {
alertTitle: S.of(context).proceed_on_device,
alertContent: S.of(context).proceed_on_device_description,
buttonText: S.of(context).cancel,
buttonAction: () => Navigator.of(context).pop());
buttonAction: () {
sendViewModel.state = InitialExecutionState();
Navigator.of(context).pop();
});
});
});
}
Expand Down
Loading