Skip to content

Commit

Permalink
Merge branch 'main' into CW-774-Enforce-Seed-Verification
Browse files Browse the repository at this point in the history
  • Loading branch information
OmarHatem28 authored Dec 14, 2024
2 parents c8a2b3f + d55e635 commit 42ebb6b
Show file tree
Hide file tree
Showing 63 changed files with 1,101 additions and 304 deletions.
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'
}
}
24 changes: 12 additions & 12 deletions cw_bitcoin/lib/electrum.dart
Original file line number Diff line number Diff line change
Expand Up @@ -235,21 +235,21 @@ class ElectrumClient {
return [];
});

Future<List<Map<String, dynamic>>> getListUnspent(String scriptHash) =>
call(method: 'blockchain.scripthash.listunspent', params: [scriptHash])
.then((dynamic result) {
if (result is List) {
return result.map((dynamic val) {
if (val is Map<String, dynamic>) {
return val;
}
Future<List<Map<String, dynamic>>> getListUnspent(String scriptHash) async {
final result = await call(method: 'blockchain.scripthash.listunspent', params: [scriptHash]);

return <String, dynamic>{};
}).toList();
if (result is List) {
return result.map((dynamic val) {
if (val is Map<String, dynamic>) {
return val;
}

return [];
});
return <String, dynamic>{};
}).toList();
}

return [];
}

Future<List<Map<String, dynamic>>> getMempool(String scriptHash) =>
call(method: 'blockchain.scripthash.get_mempool', params: [scriptHash])
Expand Down
68 changes: 34 additions & 34 deletions cw_bitcoin/lib/electrum_wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1382,8 +1382,9 @@ abstract class ElectrumWalletBase
}));

unspentCoins = updatedUnspentCoins;

final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) => element.walletId == id);

final currentWalletUnspentCoins =
unspentCoinsInfo.values.where((element) => element.walletId == id);

if (currentWalletUnspentCoins.length != updatedUnspentCoins.length) {
unspentCoins.forEach((coin) => addCoinInfo(coin));
Expand Down Expand Up @@ -1450,10 +1451,9 @@ abstract class ElectrumWalletBase

@action
Future<void> addCoinInfo(BitcoinUnspent coin) async {

// Check if the coin is already in the unspentCoinsInfo for the wallet
final existingCoinInfo = unspentCoinsInfo.values.firstWhereOrNull(
(element) => element.walletId == walletInfo.id && element == coin);
final existingCoinInfo = unspentCoinsInfo.values
.firstWhereOrNull((element) => element.walletId == walletInfo.id && element == coin);

if (existingCoinInfo == null) {
final newInfo = UnspentCoinsInfo(
Expand All @@ -1475,19 +1475,18 @@ abstract class ElectrumWalletBase

Future<void> _refreshUnspentCoinsInfo() async {
try {
final List<dynamic> keys = <dynamic>[];
final List<dynamic> keys = [];
final currentWalletUnspentCoins =
unspentCoinsInfo.values.where((element) => element.walletId.contains(id));
unspentCoinsInfo.values.where((record) => record.walletId == id);

if (currentWalletUnspentCoins.isNotEmpty) {
currentWalletUnspentCoins.forEach((element) {
final existUnspentCoins = unspentCoins
.where((coin) => element.hash.contains(coin.hash) && element.vout == coin.vout);
for (final element in currentWalletUnspentCoins) {
if (RegexUtils.addressTypeFromStr(element.address, network) is MwebAddress) continue;

if (existUnspentCoins.isEmpty) {
keys.add(element.key);
}
});
final existUnspentCoins = unspentCoins.where((coin) => element == coin);

if (existUnspentCoins.isEmpty) {
keys.add(element.key);
}
}

if (keys.isNotEmpty) {
Expand All @@ -1499,7 +1498,8 @@ abstract class ElectrumWalletBase
}

Future<void> cleanUpDuplicateUnspentCoins() async {
final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) => element.walletId == id);
final currentWalletUnspentCoins =
unspentCoinsInfo.values.where((element) => element.walletId == id);
final Map<String, UnspentCoinsInfo> uniqueUnspentCoins = {};
final List<dynamic> duplicateKeys = [];

Expand Down Expand Up @@ -1535,7 +1535,8 @@ abstract class ElectrumWalletBase
final ownAddresses = walletAddresses.allAddresses.map((addr) => addr.address).toSet();

final receiverAmount = outputs
.where((output) => !ownAddresses.contains(addressFromOutputScript(output.scriptPubKey, network)))
.where((output) =>
!ownAddresses.contains(addressFromOutputScript(output.scriptPubKey, network)))
.fold<int>(0, (sum, output) => sum + output.amount.toInt());

if (receiverAmount == 0) {
Expand Down Expand Up @@ -1584,7 +1585,7 @@ abstract class ElectrumWalletBase
allInputsAmount += outTransaction.amount.toInt();

final addressRecord =
walletAddresses.allAddresses.firstWhere((element) => element.address == address);
walletAddresses.allAddresses.firstWhere((element) => element.address == address);
final btcAddress = RegexUtils.addressTypeFromStr(addressRecord.address, network);
final privkey = generateECPrivate(
hd: addressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
Expand All @@ -1602,7 +1603,7 @@ abstract class ElectrumWalletBase
scriptType: _getScriptType(btcAddress),
),
ownerDetails:
UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: btcAddress),
UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: btcAddress),
),
);
}
Expand Down Expand Up @@ -1630,7 +1631,7 @@ abstract class ElectrumWalletBase

// Calculate the total amount and fees
int totalOutAmount =
outputs.fold<int>(0, (previousValue, output) => previousValue + output.value.toInt());
outputs.fold<int>(0, (previousValue, output) => previousValue + output.value.toInt());
int currentFee = allInputsAmount - totalOutAmount;
int remainingFee = newFee - currentFee;

Expand Down Expand Up @@ -1686,7 +1687,7 @@ abstract class ElectrumWalletBase
vout: utxo.vout,
scriptType: _getScriptType(address)),
ownerDetails:
UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: address),
UtxoAddressDetails(publicKey: privkey.getPublic().toHex(), address: address),
));

allInputsAmount += utxo.value;
Expand Down Expand Up @@ -1743,11 +1744,11 @@ abstract class ElectrumWalletBase
final changeAddresses = walletAddresses.allAddresses.where((element) => element.isHidden);
final List<BitcoinOutput> changeOutputs = outputs
.where((output) => changeAddresses
.any((element) => element.address == output.address.toAddress(network)))
.any((element) => element.address == output.address.toAddress(network)))
.toList();

int totalChangeAmount =
changeOutputs.fold<int>(0, (sum, output) => sum + output.value.toInt());
changeOutputs.fold<int>(0, (sum, output) => sum + output.value.toInt());

// The final amount that the receiver will receive
int sendingAmount = allInputsAmount - newFee - totalChangeAmount;
Expand All @@ -1764,7 +1765,7 @@ abstract class ElectrumWalletBase

final transaction = txb.buildTransaction((txDigest, utxo, publicKey, sighash) {
final key =
privateKeys.firstWhereOrNull((element) => element.getPublic().toHex() == publicKey);
privateKeys.firstWhereOrNull((element) => element.getPublic().toHex() == publicKey);
if (key == null) {
throw Exception("Cannot find private key");
}
Expand All @@ -1774,7 +1775,6 @@ abstract class ElectrumWalletBase
} else {
return key.signInput(txDigest, sigHash: sighash);
}

});

return PendingBitcoinTransaction(
Expand All @@ -1787,16 +1787,16 @@ abstract class ElectrumWalletBase
hasChange: changeOutputs.isNotEmpty,
feeRate: newFee.toString(),
)..addListener((transaction) async {
transactionHistory.transactions.values.forEach((tx) {
if (tx.id == hash) {
tx.isReplaced = true;
tx.isPending = false;
transactionHistory.addOne(tx);
}
transactionHistory.transactions.values.forEach((tx) {
if (tx.id == hash) {
tx.isReplaced = true;
tx.isPending = false;
transactionHistory.addOne(tx);
}
});
transactionHistory.addOne(transaction);
await updateBalance();
});
transactionHistory.addOne(transaction);
await updateBalance();
});
} catch (e) {
throw e;
}
Expand Down
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))}");
}
}
Loading

0 comments on commit 42ebb6b

Please sign in to comment.