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

Merged
merged 20 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3b83b85
Fix sending for monero ledger
konstantinullrich Nov 26, 2024
78f1d65
Ignore no tx keys found error
konstantinullrich Nov 27, 2024
3bbb73c
Merge branch 'main' into ledger-monero-fix
konstantinullrich Nov 27, 2024
57e65a0
re-add Monero to Ledger enabled wallets
OmarHatem28 Nov 28, 2024
a135820
Merge branch 'main' into ledger-monero-fix
konstantinullrich Nov 28, 2024
81d8d36
Fix No Element Exception on requireHardwareWalletConnection check
konstantinullrich Dec 2, 2024
6e97790
Merge branch 'refs/heads/main' into ledger-monero-fix
konstantinullrich Dec 2, 2024
8cc4300
Fix Showing connection screen again
konstantinullrich Dec 3, 2024
845a4c7
Merge branch 'main' into ledger-monero-fix
konstantinullrich Dec 3, 2024
1cb91e3
Maybe fix Race condition
konstantinullrich Dec 3, 2024
1978501
fix namespace
MrCyjaneK Dec 6, 2024
35c2408
Merge branch 'main' into ledger-monero-fix
MrCyjaneK Dec 6, 2024
3e7f540
Maybe fix Race condition and add missing pop
konstantinullrich Dec 9, 2024
c9535f1
Merge remote-tracking branch 'origin/ledger-monero-fix' into ledger-m…
konstantinullrich Dec 9, 2024
8aac560
Minor fixes
konstantinullrich Dec 12, 2024
9192c78
Merge branch 'refs/heads/main' into ledger-monero-fix
konstantinullrich Dec 12, 2024
3d96e37
Minor fixes
konstantinullrich Dec 12, 2024
883ecec
Fix minor localization
konstantinullrich Dec 13, 2024
723185b
Fix minor localization
konstantinullrich Dec 13, 2024
e0e54f5
Add Text prompt if device is not showing after 10 seconds.
konstantinullrich Dec 13, 2024
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
Loading