Mweb fixes (#1972)
* quick fix [still investigating] * cleaner quick fix * cleaner quick fix * log fix * save to downloads would require an additional permission * change address handling updates [skip ci] * should change address not updating * change address handling updates * remove unnecessary code + don't reset change address index to 0
Matthew Fosse committed
Jan 24, 2025 at 10:09 UTC
199ada3fa9dbc6c5e6da4f7c7f2c8cfef9692674
7 files changed
+55
-60
cw_bitcoin/lib/bitcoin_wallet_addresses.dart
+5
-1
@@ -2,6 +2,7 @@ import 'package:bitcoin_base/bitcoin_base.dart';
2
import 'package:blockchain_utils/bip/bip/bip32/bip32.dart';
3
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
4
import 'package:cw_bitcoin/utils.dart';
5
+import 'package:cw_core/unspent_coin_type.dart';
6
import 'package:cw_core/wallet_info.dart';
7
import 'package:mobx/mobx.dart';
8
@@ -26,7 +27,10 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
27
28
@override
29
String getAddress(
29
- {required int index, required Bip32Slip10Secp256k1 hd, BitcoinAddressType? addressType}) {
30
+ {required int index,
31
+ required Bip32Slip10Secp256k1 hd,
32
+ BitcoinAddressType? addressType,
33
+ UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any}) {
34
if (addressType == P2pkhAddressType.p2pkh)
35
return generateP2PKHAddress(hd: hd, index: index, network: network);
36
cw_bitcoin/lib/electrum_wallet.dart
+1
@@ -842,6 +842,7 @@ abstract class ElectrumWalletBase
842
final changeAddress = await walletAddresses.getChangeAddress(
843
inputs: utxoDetails.availableInputs,
844
outputs: updatedOutputs,
845
+ coinTypeToSpendFrom: coinTypeToSpendFrom,
846
);
847
final address = RegexUtils.addressTypeFromStr(changeAddress.address, network);
848
updatedOutputs.add(BitcoinOutput(
cw_bitcoin/lib/electrum_wallet_addresses.dart
+11
-8
@@ -4,6 +4,7 @@ import 'package:bitcoin_base/bitcoin_base.dart';
4
import 'package:blockchain_utils/blockchain_utils.dart';
5
import 'package:cw_bitcoin/bitcoin_address_record.dart';
6
import 'package:cw_bitcoin/electrum_wallet.dart';
7
+import 'package:cw_core/unspent_coin_type.dart';
8
import 'package:cw_core/utils/print_verbose.dart';
9
import 'package:cw_bitcoin/bitcoin_unspent.dart';
10
import 'package:cw_core/wallet_addresses.dart';
@@ -47,7 +48,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
48
List<BitcoinAddressRecord>? initialMwebAddresses,
49
Bip32Slip10Secp256k1? masterHd,
50
BitcoinAddressType? initialAddressPageType,
50
-
51
}) : _addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()),
52
addressesByReceiveType =
53
ObservableList<BaseBitcoinAddressRecord>.of((<BitcoinAddressRecord>[]).toSet()),
@@ -187,13 +187,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
187
return;
188
}
189
try {
190
- final addressRecord = _addresses.firstWhere(
191
- (addressRecord) => addressRecord.address == addr,
192
- );
190
+ final addressRecord = _addresses.firstWhere(
191
+ (addressRecord) => addressRecord.address == addr,
192
+ );
193
194
- previousAddressRecord = addressRecord;
195
- receiveAddresses.remove(addressRecord);
196
- receiveAddresses.insert(0, addressRecord);
194
+ previousAddressRecord = addressRecord;
195
+ receiveAddresses.remove(addressRecord);
196
+ receiveAddresses.insert(0, addressRecord);
197
} catch (e) {
198
printV("ElectrumWalletAddressBase: set address ($addr): $e");
199
}
@@ -274,7 +274,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
274
}
275
276
@action
277
- Future<BitcoinAddressRecord> getChangeAddress({List<BitcoinUnspent>? inputs, List<BitcoinOutput>? outputs, bool isPegIn = false}) async {
277
+ Future<BitcoinAddressRecord> getChangeAddress(
278
+ {List<BitcoinUnspent>? inputs,
279
+ List<BitcoinOutput>? outputs,
280
+ UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any}) async {
281
updateChangeAddresses();
282
283
if (changeAddresses.isEmpty) {
cw_bitcoin/lib/litecoin_wallet.dart
+21
-12
@@ -9,6 +9,7 @@ import 'package:crypto/crypto.dart';
9
import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
10
import 'package:cw_core/cake_hive.dart';
11
import 'package:cw_core/mweb_utxo.dart';
12
+import 'package:cw_core/unspent_coin_type.dart';
13
import 'package:cw_core/utils/print_verbose.dart';
14
import 'package:cw_core/node.dart';
15
import 'package:cw_mweb/mwebd.pbgrpc.dart';
@@ -394,7 +395,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
395
// if the confirmations haven't changed, skip updating:
396
if (tx.confirmations == confirmations) continue;
397
397
-
398
// if an outgoing tx is now confirmed, delete the utxo from the box (delete the unspent coin):
399
if (confirmations >= 2 &&
400
tx.direction == TransactionDirection.outgoing &&
@@ -966,10 +966,19 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
966
List<ECPrivateInfo>? inputPrivKeyInfos,
967
List<Outpoint>? vinOutpoints,
968
}) async {
969
- final spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb);
970
- final paysToMweb = outputs
969
+ bool spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb);
970
+ bool paysToMweb = outputs
971
.any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddresType.mweb);
972
- if (!spendsMweb && !paysToMweb) {
972
+
973
+ bool isRegular = !spendsMweb && !paysToMweb;
974
+ bool isMweb = spendsMweb || paysToMweb;
975
+
976
+ if (isMweb && !mwebEnabled) {
977
+ throw Exception("MWEB is not enabled! can't calculate fee without starting the mweb server!");
978
+ // TODO: likely the change address is mweb and just not updated
979
+ }
980
+
981
+ if (isRegular) {
982
return await super.calcFee(
983
utxos: utxos,
984
outputs: outputs,
@@ -981,10 +990,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
990
);
991
}
992
984
- if (!mwebEnabled) {
985
- throw Exception("MWEB is not enabled! can't calculate fee without starting the mweb server!");
986
- }
987
-
993
if (outputs.length == 1 && outputs[0].toOutput.amount == BigInt.zero) {
994
outputs = [
995
BitcoinScriptOutput(
@@ -1055,7 +1060,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
1060
1061
if (!mwebEnabled) {
1062
tx.changeAddressOverride =
1058
- (await (walletAddresses as LitecoinWalletAddresses).getChangeAddress(isPegIn: false))
1063
+ (await (walletAddresses as LitecoinWalletAddresses).getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb))
1064
.address;
1065
return tx;
1066
}
@@ -1103,13 +1108,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
1108
}
1109
}
1110
1111
+ // could probably be simplified but left for clarity:
1112
bool isPegIn = !hasMwebInput && hasMwebOutput;
1113
bool isPegOut = hasMwebInput && hasRegularOutput;
1114
bool isRegular = !hasMwebInput && !hasMwebOutput;
1115
+ bool shouldNotUseMwebChange = isPegIn || isRegular || !hasMwebInput;
1116
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
1110
- .getChangeAddress(isPegIn: isPegIn || isRegular))
1117
+ .getChangeAddress(coinTypeToSpendFrom: shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any))
1118
.address;
1112
- if (!hasMwebInput && !hasMwebOutput) {
1119
+ if (isRegular) {
1120
tx.isMweb = false;
1121
return tx;
1122
}
@@ -1212,7 +1219,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
1219
}
1220
1221
Future<void> setMwebEnabled(bool enabled) async {
1215
- if (mwebEnabled == enabled) {
1222
+ if (mwebEnabled == enabled &&
1223
+ alwaysScan == enabled &&
1224
+ (walletAddresses as LitecoinWalletAddresses).mwebEnabled == enabled) {
1225
return;
1226
}
1227
cw_bitcoin/lib/litecoin_wallet_addresses.dart
+9
-8
@@ -9,6 +9,7 @@ import 'package:cw_bitcoin/bitcoin_unspent.dart';
9
import 'package:cw_bitcoin/electrum_wallet.dart';
10
import 'package:cw_bitcoin/utils.dart';
11
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
12
+import 'package:cw_core/unspent_coin_type.dart';
13
import 'package:cw_core/utils/print_verbose.dart';
14
import 'package:cw_core/wallet_info.dart';
15
import 'package:cw_mweb/cw_mweb.dart';
@@ -148,10 +149,12 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
149
@action
150
@override
151
Future<BitcoinAddressRecord> getChangeAddress(
151
- {List<BitcoinUnspent>? inputs, List<BitcoinOutput>? outputs, bool isPegIn = false}) async {
152
+ {List<BitcoinUnspent>? inputs,
153
+ List<BitcoinOutput>? outputs,
154
+ UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any}) async {
155
// use regular change address on peg in, otherwise use mweb for change address:
156
154
- if (!mwebEnabled || isPegIn) {
157
+ if (!mwebEnabled || coinTypeToSpendFrom == UnspentCoinType.nonMweb) {
158
return super.getChangeAddress();
159
}
160
@@ -178,19 +181,17 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
181
});
182
183
bool isPegIn = !comesFromMweb && outputsToMweb;
184
+ bool isNonMweb = !comesFromMweb && !outputsToMweb;
185
182
- if (isPegIn && mwebEnabled) {
183
- return super.getChangeAddress();
184
- }
185
-
186
- // use regular change address if it's not an mweb tx:
187
- if (!comesFromMweb && !outputsToMweb) {
186
+ // use regular change address if it's not an mweb tx or if it's a peg in:
187
+ if (isPegIn || isNonMweb) {
188
return super.getChangeAddress();
189
}
190
}
191
192
if (mwebEnabled) {
193
await ensureMwebAddressUpToIndexExists(1);
194
+ updateChangeAddresses();
195
return BitcoinAddressRecord(
196
mwebAddrs[0],
197
index: 0,
lib/src/screens/settings/mweb_logs_page.dart
+6
-12
@@ -77,15 +77,12 @@ class MwebLogsPage extends BasePage {
77
return AlertWithTwoActions(
78
alertTitle: S.of(context).export_backup,
79
alertContent: S.of(context).select_destination,
80
- rightButtonText: S.of(context).save_to_downloads,
81
- leftButtonText: S.of(context).share,
82
- actionRightButton: () async {
83
- const downloadDirPath = "/storage/emulated/0/Download";
84
- final filePath = downloadDirPath + "/debug.log";
85
- await mwebSettingsViewModelBase.saveLogsLocally(filePath);
80
+ rightButtonText: S.of(context).save,
81
+ leftButtonText: S.of(context).cancel,
82
+ actionLeftButton: () async {
83
Navigator.of(dialogContext).pop();
84
},
88
- actionLeftButton: () async {
85
+ actionRightButton: () async {
86
Navigator.of(dialogContext).pop();
87
try {
88
await share(context);
@@ -101,11 +98,8 @@ class MwebLogsPage extends BasePage {
98
}
99
100
Future<void> share(BuildContext context) async {
104
- final filePath = (await getAppDir()).path + "/debug.log";
105
- bool success = await mwebSettingsViewModelBase.saveLogsLocally(filePath);
106
- if (!success) return;
107
- await ShareUtil.shareFile(filePath: filePath, fileName: "debug.log", context: context);
108
- await mwebSettingsViewModelBase.removeLogsLocally(filePath);
101
+ final inAppPath = "${(await getApplicationSupportDirectory()).path}/logs/debug.log";
102
+ await ShareUtil.shareFile(filePath: inAppPath, fileName: "debug.log", context: context);
103
}
104
105
Future<void> _saveFile() async {
lib/view_model/settings/mweb_settings_view_model.dart
+2
-19
@@ -3,6 +3,8 @@ import 'dart:io';
3
import 'package:cake_wallet/bitcoin/bitcoin.dart';
4
import 'package:cake_wallet/store/settings_store.dart';
5
import 'package:cake_wallet/utils/exception_handler.dart';
6
+import 'package:cw_core/root_dir.dart';
7
+import 'package:cw_core/utils/print_verbose.dart';
8
import 'package:cw_core/wallet_base.dart';
9
import 'package:flutter/widgets.dart';
10
import 'package:mobx/mobx.dart';
@@ -47,25 +49,6 @@ abstract class MwebSettingsViewModelBase with Store {
49
_settingsStore.mwebAlwaysScan = value;
50
}
51
50
- Future<bool> saveLogsLocally(String filePath) async {
51
- try {
52
- final appSupportPath = (await getApplicationSupportDirectory()).path;
53
- final logsFile = File("$appSupportPath/logs/debug.log");
54
- if (!logsFile.existsSync()) {
55
- throw Exception('Logs file does not exist');
56
- }
57
- await logsFile.copy(filePath);
58
- return true;
59
- } catch (e, s) {
60
- ExceptionHandler.onError(FlutterErrorDetails(
61
- exception: e,
62
- stack: s,
63
- library: "Export Logs",
64
- ));
65
- return false;
66
- }
67
- }
68
-
52
Future<String> getAbbreviatedLogs() async {
53
final appSupportPath = (await getApplicationSupportDirectory()).path;
54
final logsFile = File("$appSupportPath/logs/debug.log");