subaddress fix (#1620)

* subaddress fix * fix subaddress generation * rewrite usedAddresses for xmr and wow * [skip ci] remove print statements

cyan committed Aug 27, 2024 at 01:40 UTC 4c795ea5c26f2b5ba88317cf6d02fd284bc420fb
8 files changed +99 -13
cw_monero/lib/api/transaction_history.dart
+22 -5
@@ -45,6 +45,8 @@ List<Transaction> getAllTransactions() {
45 confirmations: 0,
46 blockheight: 0,
47 accountIndex: i,
48 + addressIndex: 0,
49 + addressIndexList: [0],
50 paymentId: "",
51 amount: fullBalance - availBalance,
52 isSpend: false,
@@ -245,19 +247,30 @@ Future<PendingTransactionDescription> createTransactionMultDest(
247
248 class Transaction {
249 final String displayLabel;
248 - String subaddressLabel = monero.Wallet_getSubaddressLabel(wptr!, accountIndex: 0, addressIndex: 0);
249 - late final String address = monero.Wallet_address(
250 + String get subaddressLabel => monero.Wallet_getSubaddressLabel(
251 wptr!,
251 - accountIndex: 0,
252 - addressIndex: 0,
252 + accountIndex: accountIndex,
253 + addressIndex: addressIndex,
254 );
255 + String get address => monero.Wallet_address(
256 + wptr!,
257 + accountIndex: accountIndex,
258 + addressIndex: addressIndex,
259 + );
260 + List<String> get addressList => List.generate(addressIndexList.length, (index) =>
261 + monero.Wallet_address(
262 + wptr!,
263 + accountIndex: accountIndex,
264 + addressIndex: addressIndexList[index],
265 + ));
266 final String description;
267 final int fee;
268 final int confirmations;
269 late final bool isPending = confirmations < 10;
270 final int blockheight;
259 - final int addressIndex = 0;
271 + final int addressIndex;
272 final int accountIndex;
273 + final List<int> addressIndexList;
274 final String paymentId;
275 final int amount;
276 final bool isSpend;
@@ -303,6 +316,8 @@ class Transaction {
316 amount = monero.TransactionInfo_amount(txInfo),
317 paymentId = monero.TransactionInfo_paymentId(txInfo),
318 accountIndex = monero.TransactionInfo_subaddrAccount(txInfo),
319 + addressIndex = int.tryParse(monero.TransactionInfo_subaddrIndex(txInfo).split(", ")[0]) ?? 0,
320 + addressIndexList = monero.TransactionInfo_subaddrIndex(txInfo).split(", ").map((e) => int.tryParse(e) ?? 0).toList(),
321 blockheight = monero.TransactionInfo_blockHeight(txInfo),
322 confirmations = monero.TransactionInfo_confirmations(txInfo),
323 fee = monero.TransactionInfo_fee(txInfo),
@@ -316,6 +331,8 @@ class Transaction {
331 required this.confirmations,
332 required this.blockheight,
333 required this.accountIndex,
334 + required this.addressIndexList,
335 + required this.addressIndex,
336 required this.paymentId,
337 required this.amount,
338 required this.isSpend,
cw_monero/lib/monero_subaddress_list.dart
+6 -3
@@ -1,6 +1,7 @@
1 import 'package:cw_core/subaddress.dart';
2 import 'package:cw_monero/api/coins_info.dart';
3 import 'package:cw_monero/api/subaddress_list.dart' as subaddress_list;
4 +import 'package:cw_monero/api/wallet.dart';
5 import 'package:flutter/services.dart';
6 import 'package:mobx/mobx.dart';
7
@@ -103,6 +104,9 @@ abstract class MoneroSubaddressListBase with Store {
104 required List<String> usedAddresses,
105 }) async {
106 _usedAddresses.addAll(usedAddresses);
107 + final _all = _usedAddresses.toSet().toList();
108 + _usedAddresses.clear();
109 + _usedAddresses.addAll(_all);
110 if (_isUpdating) {
111 return;
112 }
@@ -124,7 +128,7 @@ abstract class MoneroSubaddressListBase with Store {
128 Future<List<Subaddress>> _getAllUnusedAddresses(
129 {required int accountIndex, required String label}) async {
130 final allAddresses = subaddress_list.getAllSubaddresses();
127 - if (allAddresses.isEmpty || _usedAddresses.contains(allAddresses.last)) {
131 + if (allAddresses.isEmpty || _usedAddresses.contains(allAddresses.first.address)) {
132 final isAddressUnused = await _newSubaddress(accountIndex: accountIndex, label: label);
133 if (!isAddressUnused) {
134 return await _getAllUnusedAddresses(accountIndex: accountIndex, label: label);
@@ -143,8 +147,7 @@ abstract class MoneroSubaddressListBase with Store {
147 label.toLowerCase() == 'Primary account'.toLowerCase()
148 ? 'Primary address'
149 : label);
146 - })
147 - .toList();
150 + }).toList().reversed.toList();
151 }
152
153 Future<bool> _newSubaddress({required int accountIndex, required String label}) async {
cw_monero/lib/monero_wallet.dart
+5
@@ -88,6 +88,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
88 reaction((_) => isEnabledAutoGenerateSubaddress, (bool enabled) {
89 _updateSubAddress(enabled, account: walletAddresses.account);
90 });
91 + reaction((_) => transactionHistory, (__) {
92 + _updateSubAddress(isEnabledAutoGenerateSubaddress, account: walletAddresses.account);
93 + });
94 }
95
96 static const int _autoSaveInterval = 30;
@@ -130,6 +133,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
133
134 monero_wallet.SyncListener? _listener;
135 ReactionDisposer? _onAccountChangeReaction;
136 + ReactionDisposer? _onTxHistoryChangeReaction;
137 bool _isTransactionUpdating;
138 bool _hasSyncAfterStartup;
139 Timer? _autoSaveTimer;
@@ -169,6 +173,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
173 void close() async {
174 _listener?.stop();
175 _onAccountChangeReaction?.reaction.dispose();
176 + _onTxHistoryChangeReaction?.reaction.dispose();
177 _autoSaveTimer?.cancel();
178 }
179
cw_monero/lib/monero_wallet_addresses.dart
+20
@@ -3,6 +3,7 @@ import 'package:cw_core/address_info.dart';
3 import 'package:cw_core/subaddress.dart';
4 import 'package:cw_core/wallet_addresses.dart';
5 import 'package:cw_core/wallet_info.dart';
6 +import 'package:cw_monero/api/transaction_history.dart';
7 import 'package:cw_monero/api/wallet.dart';
8 import 'package:cw_monero/monero_account_list.dart';
9 import 'package:cw_monero/monero_subaddress_list.dart';
@@ -37,6 +38,25 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store {
38
39 MoneroAccountList accountList;
40
41 + @override
42 + Set<String> get usedAddresses {
43 + final txs = getAllTransactions();
44 + final adds = _originalUsedAddresses.toList();
45 + for (var i = 0; i < txs.length; i++) {
46 + for (var j = 0; j < txs[i].addressList.length; j++) {
47 + adds.add(txs[i].addressList[j]);
48 + }
49 + }
50 + return adds.toSet();
51 + }
52 +
53 + Set<String> _originalUsedAddresses = Set();
54 +
55 + @override
56 + set usedAddresses(Set<String> _usedAddresses) {
57 + _originalUsedAddresses = _usedAddresses;
58 + }
59 +
60 @override
61 Future<void> init() async {
62 accountList.update();
cw_wownero/lib/api/transaction_history.dart
+18 -5
@@ -45,6 +45,8 @@ List<Transaction> getAllTransactions() {
45 confirmations: 0,
46 blockheight: 0,
47 accountIndex: i,
48 + addressIndex: 0,
49 + addressIndexList: [0],
50 paymentId: "",
51 amount: fullBalance - availBalance,
52 isSpend: false,
@@ -243,19 +245,26 @@ Future<PendingTransactionDescription> createTransactionMultDest(
245
246 class Transaction {
247 final String displayLabel;
246 - String subaddressLabel = wownero.Wallet_getSubaddressLabel(wptr!, accountIndex: 0, addressIndex: 0);
247 - late final String address = wownero.Wallet_address(
248 + String get subaddressLabel => wownero.Wallet_getSubaddressLabel(wptr!, accountIndex: 0, addressIndex: 0);
249 + String get address => wownero.Wallet_address(
250 wptr!,
249 - accountIndex: 0,
250 - addressIndex: 0,
251 + accountIndex: accountIndex,
252 + addressIndex: addressIndex,
253 );
254 + List<String> get addressList => List.generate(addressIndexList.length, (index) =>
255 + wownero.Wallet_address(
256 + wptr!,
257 + accountIndex: accountIndex,
258 + addressIndex: addressIndexList[index],
259 + ));
260 final String description;
261 final int fee;
262 final int confirmations;
263 late final bool isPending = confirmations < 3;
264 final int blockheight;
257 - final int addressIndex = 0;
265 + final int addressIndex;
266 final int accountIndex;
267 + final List<int> addressIndexList;
268 final String paymentId;
269 final int amount;
270 final bool isSpend;
@@ -301,6 +310,8 @@ class Transaction {
310 amount = wownero.TransactionInfo_amount(txInfo),
311 paymentId = wownero.TransactionInfo_paymentId(txInfo),
312 accountIndex = wownero.TransactionInfo_subaddrAccount(txInfo),
313 + addressIndex = int.tryParse(wownero.TransactionInfo_subaddrIndex(txInfo).split(", ")[0]) ?? 0,
314 + addressIndexList = wownero.TransactionInfo_subaddrIndex(txInfo).split(", ").map((e) => int.tryParse(e) ?? 0).toList(),
315 blockheight = wownero.TransactionInfo_blockHeight(txInfo),
316 confirmations = wownero.TransactionInfo_confirmations(txInfo),
317 fee = wownero.TransactionInfo_fee(txInfo),
@@ -314,6 +325,8 @@ class Transaction {
325 required this.confirmations,
326 required this.blockheight,
327 required this.accountIndex,
328 + required this.addressIndex,
329 + required this.addressIndexList,
330 required this.paymentId,
331 required this.amount,
332 required this.isSpend,
cw_wownero/lib/wownero_subaddress_list.dart
+4
@@ -1,6 +1,7 @@
1 import 'package:cw_core/subaddress.dart';
2 import 'package:cw_wownero/api/coins_info.dart';
3 import 'package:cw_wownero/api/subaddress_list.dart' as subaddress_list;
4 +import 'package:cw_wownero/api/wallet.dart';
5 import 'package:flutter/services.dart';
6 import 'package:mobx/mobx.dart';
7
@@ -103,6 +104,9 @@ abstract class WowneroSubaddressListBase with Store {
104 required List<String> usedAddresses,
105 }) async {
106 _usedAddresses.addAll(usedAddresses);
107 + final _all = _usedAddresses.toSet().toList();
108 + _usedAddresses.clear();
109 + _usedAddresses.addAll(_all);
110 if (_isUpdating) {
111 return;
112 }
cw_wownero/lib/wownero_wallet.dart
+6
@@ -82,6 +82,10 @@ abstract class WowneroWalletBase
82 reaction((_) => isEnabledAutoGenerateSubaddress, (bool enabled) {
83 _updateSubAddress(enabled, account: walletAddresses.account);
84 });
85 +
86 + _onTxHistoryChangeReaction = reaction((_) => transactionHistory, (__) {
87 + _updateSubAddress(isEnabledAutoGenerateSubaddress, account: walletAddresses.account);
88 + });
89 }
90
91 static const int _autoSaveInterval = 30;
@@ -123,6 +127,7 @@ abstract class WowneroWalletBase
127
128 wownero_wallet.SyncListener? _listener;
129 ReactionDisposer? _onAccountChangeReaction;
130 + ReactionDisposer? _onTxHistoryChangeReaction;
131 bool _isTransactionUpdating;
132 bool _hasSyncAfterStartup;
133 Timer? _autoSaveTimer;
@@ -158,6 +163,7 @@ abstract class WowneroWalletBase
163 void close() async {
164 _listener?.stop();
165 _onAccountChangeReaction?.reaction.dispose();
166 + _onTxHistoryChangeReaction?.reaction.dispose();
167 _autoSaveTimer?.cancel();
168 }
169
cw_wownero/lib/wownero_wallet_addresses.dart
+18
@@ -3,6 +3,7 @@ import 'package:cw_core/address_info.dart';
3 import 'package:cw_core/subaddress.dart';
4 import 'package:cw_core/wallet_addresses.dart';
5 import 'package:cw_core/wallet_info.dart';
6 +import 'package:cw_wownero/api/transaction_history.dart';
7 import 'package:cw_wownero/api/wallet.dart';
8 import 'package:cw_wownero/wownero_account_list.dart';
9 import 'package:cw_wownero/wownero_subaddress_list.dart';
@@ -36,7 +37,24 @@ abstract class WowneroWalletAddressesBase extends WalletAddresses with Store {
37 WowneroSubaddressList subaddressList;
38
39 WowneroAccountList accountList;
40 + @override
41 + Set<String> get usedAddresses {
42 + final txs = getAllTransactions();
43 + final adds = _originalUsedAddresses.toList();
44 + for (var i = 0; i < txs.length; i++) {
45 + for (var j = 0; j < txs[i].addressList.length; j++) {
46 + adds.add(txs[i].addressList[j]);
47 + }
48 + }
49 + return adds.toSet();
50 + }
51
52 + Set<String> _originalUsedAddresses = Set();
53 +
54 + @override
55 + set usedAddresses(Set<String> _usedAddresses) {
56 + _originalUsedAddresses = _usedAddresses;
57 + }
58 @override
59 Future<void> init() async {
60 accountList.update();