From 6ff4c5d49aa50359f41998b24269ec40e9c70dcb Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 09:06:10 -0600 Subject: [PATCH 01/37] clean up --- lib/services/coins/firo/firo_wallet.dart | 64 ------------------------ 1 file changed, 64 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index e62b7d069..3527c9802 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -3133,17 +3133,6 @@ class FiroWallet extends CoinServiceAPI txnsData.add(Tuple2(transaction, transactionAddress)); await db.addNewTransactionData(txnsData, walletId); - - // final models.TransactionData newTxData = - // models.TransactionData.fromMap(transactions); - // await DB.instance.put( - // boxName: walletId, - // key: 'latest_lelantus_tx_model', - // value: newTxData); - // final ldata = DB.instance.get( - // boxName: walletId, - // key: 'latest_lelantus_tx_model') as models.TransactionData; - // _lelantusTransactionData = Future(() => ldata); } else { // This is a mint Logging.instance.log("this is a mint", level: LogLevel.Info); @@ -3204,20 +3193,6 @@ class FiroWallet extends CoinServiceAPI Logging.instance.log("fetched fees: $feeObject", level: LogLevel.Info); return feeObject; - - // final result = await electrumXClient.getFeeRate(); - // - // final locale = await Devicelocale.currentLocale; - // final String fee = - // Format.satoshiAmountToPrettyString(result["rate"] as int, locale!); - // - // final fees = { - // "fast": fee, - // "average": fee, - // "slow": fee, - // }; - // final FeeObject feeObject = FeeObject.fromJson(fees); - // return feeObject; } catch (e) { Logging.instance .log("Exception rethrown from _getFees(): $e", level: LogLevel.Error); @@ -4072,45 +4047,6 @@ class FiroWallet extends CoinServiceAPI ); } - // /// Takes in a list of isar_models.UTXOs and adds a name (dependent on object index within list) - // /// and checks for the txid associated with the utxo being blocked and marks it accordingly. - // /// Now also checks for output labeling. - // Future _sortOutputs(List utxos) async { - // final blockedHashArray = - // DB.instance.get(boxName: walletId, key: 'blocked_tx_hashes') - // as List?; - // final List lst = []; - // if (blockedHashArray != null) { - // for (var hash in blockedHashArray) { - // lst.add(hash as String); - // } - // } - // final labels = - // DB.instance.get(boxName: walletId, key: 'labels') as Map? ?? - // {}; - // - // _outputsList = []; - // - // for (var i = 0; i < utxos.length; i++) { - // if (labels[utxos[i].txid] != null) { - // utxos[i].txName = labels[utxos[i].txid] as String? ?? ""; - // } else { - // utxos[i].txName = 'Output #$i'; - // } - // - // if (utxos[i].status.confirmed == false) { - // _outputsList.add(utxos[i]); - // } else { - // if (lst.contains(utxos[i].txid)) { - // utxos[i].blocked = true; - // _outputsList.add(utxos[i]); - // } else if (!lst.contains(utxos[i].txid)) { - // _outputsList.add(utxos[i]); - // } - // } - // } - // } - @override Future fullRescan( int maxUnusedAddressGap, From baa34ca9f2cc4c9165073680930be8551a0fd905 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 09:06:48 -0600 Subject: [PATCH 02/37] isar lelantus coin schema --- lib/db/isar/main_db.dart | 2 + .../models/firo_specific/lelantus_coin.dart | 75 + .../models/firo_specific/lelantus_coin.g.dart | 1570 +++++++++++++++++ 3 files changed, 1647 insertions(+) create mode 100644 lib/models/isar/models/firo_specific/lelantus_coin.dart create mode 100644 lib/models/isar/models/firo_specific/lelantus_coin.g.dart diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index bb6ea5e8d..209f13c5d 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -14,6 +14,7 @@ import 'package:isar/isar.dart'; import 'package:stackwallet/exceptions/main_db/main_db_exception.dart'; import 'package:stackwallet/models/isar/models/block_explorer.dart'; import 'package:stackwallet/models/isar/models/contact_entry.dart'; +import 'package:stackwallet/models/isar/models/firo_specific/lelantus_coin.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/stack_theme.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; @@ -54,6 +55,7 @@ class MainDB { TransactionBlockExplorerSchema, StackThemeSchema, ContactEntrySchema, + LelantusCoinSchema, ], directory: (await StackFileSystem.applicationIsarDirectory()).path, // inspector: kDebugMode, diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.dart b/lib/models/isar/models/firo_specific/lelantus_coin.dart new file mode 100644 index 000000000..3dd48b2dc --- /dev/null +++ b/lib/models/isar/models/firo_specific/lelantus_coin.dart @@ -0,0 +1,75 @@ +import 'package:isar/isar.dart'; + +part 'lelantus_coin.g.dart'; + +@collection +class LelantusCoin { + Id id = Isar.autoIncrement; + + @Index() + final String walletId; + + @Index( + unique: true, + composite: [ + CompositeIndex("walletId"), + CompositeIndex("txid"), + ], + replace: false, + ) + final String publicCoin; + + final String txid; + + final String value; // can't use BigInt in isar :shrug: + + final int index; + + final int anonymitySetId; + + final bool isUsed; + + LelantusCoin({ + required this.walletId, + required this.publicCoin, + required this.txid, + required this.value, + required this.index, + required this.anonymitySetId, + required this.isUsed, + }); + + LelantusCoin copyWith({ + String? walletId, + String? publicCoin, + String? txid, + String? value, + int? index, + int? anonymitySetId, + bool? isUsed, + }) { + return LelantusCoin( + walletId: walletId ?? this.walletId, + publicCoin: publicCoin ?? this.publicCoin, + txid: txid ?? this.txid, + value: value ?? this.value, + index: index ?? this.index, + anonymitySetId: anonymitySetId ?? this.anonymitySetId, + isUsed: isUsed ?? this.isUsed, + ); + } + + @override + String toString() { + return 'LelantusCoin{' + 'id: $id, ' + 'walletId: $walletId, ' + 'publicCoin: $publicCoin, ' + 'txid: $txid, ' + 'value: $value, ' + 'index: $index, ' + 'anonymitySetId: $anonymitySetId, ' + 'isUsed: $isUsed' + '}'; + } +} diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart new file mode 100644 index 000000000..8fafde571 --- /dev/null +++ b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart @@ -0,0 +1,1570 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'lelantus_coin.dart'; + +// ************************************************************************** +// IsarCollectionGenerator +// ************************************************************************** + +// coverage:ignore-file +// ignore_for_file: duplicate_ignore, non_constant_identifier_names, constant_identifier_names, invalid_use_of_protected_member, unnecessary_cast, prefer_const_constructors, lines_longer_than_80_chars, require_trailing_commas, inference_failure_on_function_invocation, unnecessary_parenthesis, unnecessary_raw_strings, unnecessary_null_checks, join_return_with_assignment, prefer_final_locals, avoid_js_rounded_ints, avoid_positional_boolean_parameters + +extension GetLelantusCoinCollection on Isar { + IsarCollection get lelantusCoins => this.collection(); +} + +const LelantusCoinSchema = CollectionSchema( + name: r'LelantusCoin', + id: -6795633185033299066, + properties: { + r'anonymitySetId': PropertySchema( + id: 0, + name: r'anonymitySetId', + type: IsarType.long, + ), + r'index': PropertySchema( + id: 1, + name: r'index', + type: IsarType.long, + ), + r'isUsed': PropertySchema( + id: 2, + name: r'isUsed', + type: IsarType.bool, + ), + r'publicCoin': PropertySchema( + id: 3, + name: r'publicCoin', + type: IsarType.string, + ), + r'txid': PropertySchema( + id: 4, + name: r'txid', + type: IsarType.string, + ), + r'value': PropertySchema( + id: 5, + name: r'value', + type: IsarType.string, + ), + r'walletId': PropertySchema( + id: 6, + name: r'walletId', + type: IsarType.string, + ) + }, + estimateSize: _lelantusCoinEstimateSize, + serialize: _lelantusCoinSerialize, + deserialize: _lelantusCoinDeserialize, + deserializeProp: _lelantusCoinDeserializeProp, + idName: r'id', + indexes: { + r'walletId': IndexSchema( + id: -1783113319798776304, + name: r'walletId', + unique: false, + replace: false, + properties: [ + IndexPropertySchema( + name: r'walletId', + type: IndexType.hash, + caseSensitive: true, + ) + ], + ), + r'publicCoin_walletId_txid': IndexSchema( + id: 5610740154835640070, + name: r'publicCoin_walletId_txid', + unique: true, + replace: false, + properties: [ + IndexPropertySchema( + name: r'publicCoin', + type: IndexType.hash, + caseSensitive: true, + ), + IndexPropertySchema( + name: r'walletId', + type: IndexType.hash, + caseSensitive: true, + ), + IndexPropertySchema( + name: r'txid', + type: IndexType.hash, + caseSensitive: true, + ) + ], + ) + }, + links: {}, + embeddedSchemas: {}, + getId: _lelantusCoinGetId, + getLinks: _lelantusCoinGetLinks, + attach: _lelantusCoinAttach, + version: '3.0.5', +); + +int _lelantusCoinEstimateSize( + LelantusCoin object, + List offsets, + Map> allOffsets, +) { + var bytesCount = offsets.last; + bytesCount += 3 + object.publicCoin.length * 3; + bytesCount += 3 + object.txid.length * 3; + bytesCount += 3 + object.value.length * 3; + bytesCount += 3 + object.walletId.length * 3; + return bytesCount; +} + +void _lelantusCoinSerialize( + LelantusCoin object, + IsarWriter writer, + List offsets, + Map> allOffsets, +) { + writer.writeLong(offsets[0], object.anonymitySetId); + writer.writeLong(offsets[1], object.index); + writer.writeBool(offsets[2], object.isUsed); + writer.writeString(offsets[3], object.publicCoin); + writer.writeString(offsets[4], object.txid); + writer.writeString(offsets[5], object.value); + writer.writeString(offsets[6], object.walletId); +} + +LelantusCoin _lelantusCoinDeserialize( + Id id, + IsarReader reader, + List offsets, + Map> allOffsets, +) { + final object = LelantusCoin( + anonymitySetId: reader.readLong(offsets[0]), + index: reader.readLong(offsets[1]), + isUsed: reader.readBool(offsets[2]), + publicCoin: reader.readString(offsets[3]), + txid: reader.readString(offsets[4]), + value: reader.readString(offsets[5]), + walletId: reader.readString(offsets[6]), + ); + object.id = id; + return object; +} + +P _lelantusCoinDeserializeProp

( + IsarReader reader, + int propertyId, + int offset, + Map> allOffsets, +) { + switch (propertyId) { + case 0: + return (reader.readLong(offset)) as P; + case 1: + return (reader.readLong(offset)) as P; + case 2: + return (reader.readBool(offset)) as P; + case 3: + return (reader.readString(offset)) as P; + case 4: + return (reader.readString(offset)) as P; + case 5: + return (reader.readString(offset)) as P; + case 6: + return (reader.readString(offset)) as P; + default: + throw IsarError('Unknown property with id $propertyId'); + } +} + +Id _lelantusCoinGetId(LelantusCoin object) { + return object.id; +} + +List> _lelantusCoinGetLinks(LelantusCoin object) { + return []; +} + +void _lelantusCoinAttach( + IsarCollection col, Id id, LelantusCoin object) { + object.id = id; +} + +extension LelantusCoinByIndex on IsarCollection { + Future getByPublicCoinWalletIdTxid( + String publicCoin, String walletId, String txid) { + return getByIndex( + r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); + } + + LelantusCoin? getByPublicCoinWalletIdTxidSync( + String publicCoin, String walletId, String txid) { + return getByIndexSync( + r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); + } + + Future deleteByPublicCoinWalletIdTxid( + String publicCoin, String walletId, String txid) { + return deleteByIndex( + r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); + } + + bool deleteByPublicCoinWalletIdTxidSync( + String publicCoin, String walletId, String txid) { + return deleteByIndexSync( + r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); + } + + Future> getAllByPublicCoinWalletIdTxid( + List publicCoinValues, + List walletIdValues, + List txidValues) { + final len = publicCoinValues.length; + assert(walletIdValues.length == len && txidValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); + } + + return getAllByIndex(r'publicCoin_walletId_txid', values); + } + + List getAllByPublicCoinWalletIdTxidSync( + List publicCoinValues, + List walletIdValues, + List txidValues) { + final len = publicCoinValues.length; + assert(walletIdValues.length == len && txidValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); + } + + return getAllByIndexSync(r'publicCoin_walletId_txid', values); + } + + Future deleteAllByPublicCoinWalletIdTxid(List publicCoinValues, + List walletIdValues, List txidValues) { + final len = publicCoinValues.length; + assert(walletIdValues.length == len && txidValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); + } + + return deleteAllByIndex(r'publicCoin_walletId_txid', values); + } + + int deleteAllByPublicCoinWalletIdTxidSync(List publicCoinValues, + List walletIdValues, List txidValues) { + final len = publicCoinValues.length; + assert(walletIdValues.length == len && txidValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); + } + + return deleteAllByIndexSync(r'publicCoin_walletId_txid', values); + } + + Future putByPublicCoinWalletIdTxid(LelantusCoin object) { + return putByIndex(r'publicCoin_walletId_txid', object); + } + + Id putByPublicCoinWalletIdTxidSync(LelantusCoin object, + {bool saveLinks = true}) { + return putByIndexSync(r'publicCoin_walletId_txid', object, + saveLinks: saveLinks); + } + + Future> putAllByPublicCoinWalletIdTxid(List objects) { + return putAllByIndex(r'publicCoin_walletId_txid', objects); + } + + List putAllByPublicCoinWalletIdTxidSync(List objects, + {bool saveLinks = true}) { + return putAllByIndexSync(r'publicCoin_walletId_txid', objects, + saveLinks: saveLinks); + } +} + +extension LelantusCoinQueryWhereSort + on QueryBuilder { + QueryBuilder anyId() { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(const IdWhereClause.any()); + }); + } +} + +extension LelantusCoinQueryWhere + on QueryBuilder { + QueryBuilder idEqualTo(Id id) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: id, + upper: id, + )); + }); + } + + QueryBuilder idNotEqualTo( + Id id) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ) + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ); + } else { + return query + .addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: false), + ) + .addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: false), + ); + } + }); + } + + QueryBuilder idGreaterThan( + Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.greaterThan(lower: id, includeLower: include), + ); + }); + } + + QueryBuilder idLessThan(Id id, + {bool include = false}) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause( + IdWhereClause.lessThan(upper: id, includeUpper: include), + ); + }); + } + + QueryBuilder idBetween( + Id lowerId, + Id upperId, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IdWhereClause.between( + lower: lowerId, + includeLower: includeLower, + upper: upperId, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder walletIdEqualTo( + String walletId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'walletId', + value: [walletId], + )); + }); + } + + QueryBuilder + walletIdNotEqualTo(String walletId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [], + upper: [walletId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [walletId], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [walletId], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'walletId', + lower: [], + upper: [walletId], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + publicCoinEqualToAnyWalletIdTxid(String publicCoin) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'publicCoin_walletId_txid', + value: [publicCoin], + )); + }); + } + + QueryBuilder + publicCoinNotEqualToAnyWalletIdTxid(String publicCoin) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [], + upper: [publicCoin], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [], + upper: [publicCoin], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + publicCoinWalletIdEqualToAnyTxid(String publicCoin, String walletId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'publicCoin_walletId_txid', + value: [publicCoin, walletId], + )); + }); + } + + QueryBuilder + publicCoinEqualToWalletIdNotEqualToAnyTxid( + String publicCoin, String walletId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin], + upper: [publicCoin, walletId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin, walletId], + includeLower: false, + upper: [publicCoin], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin, walletId], + includeLower: false, + upper: [publicCoin], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin], + upper: [publicCoin, walletId], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + publicCoinWalletIdTxidEqualTo( + String publicCoin, String walletId, String txid) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'publicCoin_walletId_txid', + value: [publicCoin, walletId, txid], + )); + }); + } + + QueryBuilder + publicCoinWalletIdEqualToTxidNotEqualTo( + String publicCoin, String walletId, String txid) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin, walletId], + upper: [publicCoin, walletId, txid], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin, walletId, txid], + includeLower: false, + upper: [publicCoin, walletId], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin, walletId, txid], + includeLower: false, + upper: [publicCoin, walletId], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'publicCoin_walletId_txid', + lower: [publicCoin, walletId], + upper: [publicCoin, walletId, txid], + includeUpper: false, + )); + } + }); + } +} + +extension LelantusCoinQueryFilter + on QueryBuilder { + QueryBuilder + anonymitySetIdEqualTo(int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'anonymitySetId', + value: value, + )); + }); + } + + QueryBuilder + anonymitySetIdGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'anonymitySetId', + value: value, + )); + }); + } + + QueryBuilder + anonymitySetIdLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'anonymitySetId', + value: value, + )); + }); + } + + QueryBuilder + anonymitySetIdBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'anonymitySetId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder idEqualTo( + Id value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idGreaterThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idLessThan( + Id value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'id', + value: value, + )); + }); + } + + QueryBuilder idBetween( + Id lower, + Id upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'id', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder indexEqualTo( + int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'index', + value: value, + )); + }); + } + + QueryBuilder + indexGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'index', + value: value, + )); + }); + } + + QueryBuilder indexLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'index', + value: value, + )); + }); + } + + QueryBuilder indexBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'index', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder isUsedEqualTo( + bool value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'isUsed', + value: value, + )); + }); + } + + QueryBuilder + publicCoinEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'publicCoin', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'publicCoin', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'publicCoin', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'publicCoin', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'publicCoin', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'publicCoin', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'publicCoin', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'publicCoin', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + publicCoinIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'publicCoin', + value: '', + )); + }); + } + + QueryBuilder + publicCoinIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'publicCoin', + value: '', + )); + }); + } + + QueryBuilder txidEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'txid', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'txid', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder txidMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'txid', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + txidIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'txid', + value: '', + )); + }); + } + + QueryBuilder + txidIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'txid', + value: '', + )); + }); + } + + QueryBuilder valueEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'value', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueContains( + String value, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'value', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder valueMatches( + String pattern, + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'value', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + valueIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'value', + value: '', + )); + }); + } + + QueryBuilder + valueIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'value', + value: '', + )); + }); + } + + QueryBuilder + walletIdEqualTo( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdGreaterThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdLessThan( + String value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdBetween( + String lower, + String upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'walletId', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'walletId', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'walletId', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + walletIdIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'walletId', + value: '', + )); + }); + } + + QueryBuilder + walletIdIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'walletId', + value: '', + )); + }); + } +} + +extension LelantusCoinQueryObject + on QueryBuilder {} + +extension LelantusCoinQueryLinks + on QueryBuilder {} + +extension LelantusCoinQuerySortBy + on QueryBuilder { + QueryBuilder + sortByAnonymitySetId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.asc); + }); + } + + QueryBuilder + sortByAnonymitySetIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.desc); + }); + } + + QueryBuilder sortByIndex() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'index', Sort.asc); + }); + } + + QueryBuilder sortByIndexDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'index', Sort.desc); + }); + } + + QueryBuilder sortByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.asc); + }); + } + + QueryBuilder sortByIsUsedDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.desc); + }); + } + + QueryBuilder sortByPublicCoin() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'publicCoin', Sort.asc); + }); + } + + QueryBuilder + sortByPublicCoinDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'publicCoin', Sort.desc); + }); + } + + QueryBuilder sortByTxid() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.asc); + }); + } + + QueryBuilder sortByTxidDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.desc); + }); + } + + QueryBuilder sortByValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.asc); + }); + } + + QueryBuilder sortByValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.desc); + }); + } + + QueryBuilder sortByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder sortByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension LelantusCoinQuerySortThenBy + on QueryBuilder { + QueryBuilder + thenByAnonymitySetId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.asc); + }); + } + + QueryBuilder + thenByAnonymitySetIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'anonymitySetId', Sort.desc); + }); + } + + QueryBuilder thenById() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.asc); + }); + } + + QueryBuilder thenByIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'id', Sort.desc); + }); + } + + QueryBuilder thenByIndex() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'index', Sort.asc); + }); + } + + QueryBuilder thenByIndexDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'index', Sort.desc); + }); + } + + QueryBuilder thenByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.asc); + }); + } + + QueryBuilder thenByIsUsedDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'isUsed', Sort.desc); + }); + } + + QueryBuilder thenByPublicCoin() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'publicCoin', Sort.asc); + }); + } + + QueryBuilder + thenByPublicCoinDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'publicCoin', Sort.desc); + }); + } + + QueryBuilder thenByTxid() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.asc); + }); + } + + QueryBuilder thenByTxidDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'txid', Sort.desc); + }); + } + + QueryBuilder thenByValue() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.asc); + }); + } + + QueryBuilder thenByValueDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'value', Sort.desc); + }); + } + + QueryBuilder thenByWalletId() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.asc); + }); + } + + QueryBuilder thenByWalletIdDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'walletId', Sort.desc); + }); + } +} + +extension LelantusCoinQueryWhereDistinct + on QueryBuilder { + QueryBuilder + distinctByAnonymitySetId() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'anonymitySetId'); + }); + } + + QueryBuilder distinctByIndex() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'index'); + }); + } + + QueryBuilder distinctByIsUsed() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'isUsed'); + }); + } + + QueryBuilder distinctByPublicCoin( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'publicCoin', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByTxid( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'txid', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByValue( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'value', caseSensitive: caseSensitive); + }); + } + + QueryBuilder distinctByWalletId( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'walletId', caseSensitive: caseSensitive); + }); + } +} + +extension LelantusCoinQueryProperty + on QueryBuilder { + QueryBuilder idProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'id'); + }); + } + + QueryBuilder anonymitySetIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'anonymitySetId'); + }); + } + + QueryBuilder indexProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'index'); + }); + } + + QueryBuilder isUsedProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'isUsed'); + }); + } + + QueryBuilder publicCoinProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'publicCoin'); + }); + } + + QueryBuilder txidProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'txid'); + }); + } + + QueryBuilder valueProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'value'); + }); + } + + QueryBuilder walletIdProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'walletId'); + }); + } +} From a5ba67aa1d3a44de01d1ef931931f951a9db4429 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 09:42:29 -0600 Subject: [PATCH 03/37] WIP migrate lcoins hive => isar --- lib/models/isar/models/isar_models.dart | 1 + lib/models/lelantus_coin.dart | 2 + lib/utilities/constants.dart | 2 +- lib/utilities/db_version_migration.dart | 65 ++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/lib/models/isar/models/isar_models.dart b/lib/models/isar/models/isar_models.dart index ce7652a46..9de91fc84 100644 --- a/lib/models/isar/models/isar_models.dart +++ b/lib/models/isar/models/isar_models.dart @@ -15,5 +15,6 @@ export 'blockchain_data/output.dart'; export 'blockchain_data/transaction.dart'; export 'blockchain_data/utxo.dart'; export 'ethereum/eth_contract.dart'; +export 'firo_specific/lelantus_coin.dart'; export 'log.dart'; export 'transaction_note.dart'; diff --git a/lib/models/lelantus_coin.dart b/lib/models/lelantus_coin.dart index 0e32d33bf..56557c1cd 100644 --- a/lib/models/lelantus_coin.dart +++ b/lib/models/lelantus_coin.dart @@ -12,6 +12,7 @@ import 'package:hive/hive.dart'; part 'type_adaptors/lelantus_coin.g.dart'; +@Deprecated("Use Isar object instead") // @HiveType(typeId: 9) class LelantusCoin { // @HiveField(0) @@ -27,6 +28,7 @@ class LelantusCoin { // @HiveField(5) bool isUsed; + @Deprecated("Use Isar object instead") LelantusCoin( this.index, this.value, diff --git a/lib/utilities/constants.dart b/lib/utilities/constants.dart index 2dd7be287..ad9d68997 100644 --- a/lib/utilities/constants.dart +++ b/lib/utilities/constants.dart @@ -58,7 +58,7 @@ abstract class Constants { // Enable Logger.print statements static const bool disableLogger = false; - static const int currentDataVersion = 10; + static const int currentDataVersion = 11; static const int rescanV1 = 1; diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index 293b5d48c..3f69161f4 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -180,7 +180,6 @@ class DbVersionMigrator with WalletDB { // clear possible broken firo cache await DB.instance.clearSharedTransactionCache(coin: Coin.firo); - // update version await DB.instance.put( boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 4); @@ -343,12 +342,76 @@ class DbVersionMigrator with WalletDB { // try to continue migrating return await migrate(10, secureStore: secureStore); + case 10: + // migrate + await _v10(secureStore); + + // update version + await DB.instance.put( + boxName: DB.boxNameDBInfo, key: "hive_data_version", value: 11); + + // try to continue migrating + return await migrate(11, secureStore: secureStore); + default: // finally return return; } } + Future _v10(SecureStorageInterface secureStore) async { + await Hive.openBox(DB.boxNameAllWalletsData); + await Hive.openBox(DB.boxNamePrefs); + final walletsService = WalletsService(secureStorageInterface: secureStore); + final prefs = Prefs.instance; + final walletInfoList = await walletsService.walletNames; + await prefs.init(); + await MainDB.instance.initMainDB(); + + for (final walletId in walletInfoList.keys) { + final info = walletInfoList[walletId]!; + assert(info.walletId == walletId); + + if (info.coin == Coin.firo && + MainDB.instance.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .countSync() == + 0) { + final walletBox = await Hive.openBox(walletId); + + final hiveLCoins = DB.instance.get( + boxName: walletId, + key: "_lelantus_coins", + ) as List? ?? + []; + + final List coins = []; + for (final e in hiveLCoins) { + final lcoin = e as LelantusCoin; + + final coin = isar_models.LelantusCoin( + walletId: walletId, + publicCoin: lcoin.publicCoin, + txid: lcoin.txId, + value: lcoin.value.toString(), + index: lcoin.index, + anonymitySetId: lcoin.anonymitySetId, + isUsed: lcoin.isUsed, + ); + + coins.add(coin); + } + + if (coins.isNotEmpty) { + await MainDB.instance.isar.writeTxn(() async { + await MainDB.instance.isar.lelantusCoins.putAll(coins); + }); + } + } + } + } + Future _v4(SecureStorageInterface secureStore) async { await Hive.openBox(DB.boxNameAllWalletsData); await Hive.openBox(DB.boxNamePrefs); From df0b004b82478600ddcc073c222701b820022591 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 12:30:01 -0600 Subject: [PATCH 04/37] finish up basic migrate lcoins hive => isar --- lib/services/coins/firo/firo_wallet.dart | 565 ++++++++++++----------- lib/services/mixins/firo_hive.dart | 26 +- lib/utilities/db_version_migration.dart | 3 +- 3 files changed, 301 insertions(+), 293 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 3527c9802..004e5399c 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -26,7 +26,6 @@ import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart'; import 'package:stackwallet/electrumx_rpc/electrumx.dart'; import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models; -import 'package:stackwallet/models/lelantus_coin.dart'; import 'package:stackwallet/models/lelantus_fee_data.dart'; import 'package:stackwallet/models/paymint/fee_object_model.dart'; import 'package:stackwallet/models/signing_data.dart'; @@ -161,6 +160,7 @@ Future executeNative(Map arguments) async { final mnemonicPassphrase = arguments['mnemonicPassphrase'] as String; final coin = arguments['coin'] as Coin; final network = arguments['network'] as NetworkType; + final walletId = arguments['walletId'] as String; final restoreData = await isolateRestore( mnemonic, @@ -170,6 +170,7 @@ Future executeNative(Map arguments) async { setDataMap, usedSerialNumbers, network, + walletId, ); sendPort.send(restoreData); return; @@ -206,13 +207,14 @@ Future> isolateRestore( Map _setDataMap, List _usedSerialNumbers, NetworkType network, + String walletId, ) async { List jindexes = []; - List> lelantusCoins = []; + List lelantusCoins = []; final List spendTxIds = []; - var lastFoundIndex = 0; - var currentIndex = 0; + int lastFoundIndex = 0; + int currentIndex = 0; try { Set usedSerialNumbersSet = _usedSerialNumbers.toSet(); @@ -239,7 +241,7 @@ Future> isolateRestore( isTestnet: coin == Coin.firoTestNet, ); - for (var setId = 1; setId <= _latestSetId; setId++) { + for (int setId = 1; setId <= _latestSetId; setId++) { final setData = _setDataMap[setId] as Map; final foundCoin = (setData["coins"] as List).firstWhere( (e) => e[1] == mintTag, @@ -264,32 +266,23 @@ Future> isolateRestore( isTestnet: coin == Coin.firoTestNet, ); final bool isUsed = usedSerialNumbersSet.contains(serialNumber); - final duplicateCoin = lelantusCoins.firstWhere( - (element) { - final coin = element.values.first; - return coin.txId == txId && - coin.index == currentIndex && - coin.anonymitySetId != setId; - }, - orElse: () => {}, + + lelantusCoins.removeWhere((e) => + e.txid == txId && + e.index == currentIndex && + e.anonymitySetId != setId); + + lelantusCoins.add( + isar_models.LelantusCoin( + walletId: walletId, + index: currentIndex, + value: amount.toString(), + publicCoin: publicCoin, + txid: txId, + anonymitySetId: setId, + isUsed: isUsed, + ), ); - if (duplicateCoin.isNotEmpty) { - Logging.instance.log( - "Firo isolateRestore removing duplicate coin: $duplicateCoin", - level: LogLevel.Info, - ); - lelantusCoins.remove(duplicateCoin); - } - lelantusCoins.add({ - txId: LelantusCoin( - currentIndex, - amount, - publicCoin, - txId, - setId, - isUsed, - ) - }); Logging.instance.log( "amount $amount used $isUsed", level: LogLevel.Info, @@ -322,32 +315,22 @@ Future> isolateRestore( isTestnet: coin == Coin.firoTestNet, ); bool isUsed = usedSerialNumbersSet.contains(serialNumber); - final duplicateCoin = lelantusCoins.firstWhere( - (element) { - final coin = element.values.first; - return coin.txId == txId && - coin.index == currentIndex && - coin.anonymitySetId != setId; - }, - orElse: () => {}, + lelantusCoins.removeWhere((e) => + e.txid == txId && + e.index == currentIndex && + e.anonymitySetId != setId); + + lelantusCoins.add( + isar_models.LelantusCoin( + walletId: walletId, + index: currentIndex, + value: amount.toString(), + publicCoin: publicCoin, + txid: txId, + anonymitySetId: setId, + isUsed: isUsed, + ), ); - if (duplicateCoin.isNotEmpty) { - Logging.instance.log( - "Firo isolateRestore removing duplicate coin: $duplicateCoin", - level: LogLevel.Info, - ); - lelantusCoins.remove(duplicateCoin); - } - lelantusCoins.add({ - txId: LelantusCoin( - currentIndex, - amount, - publicCoin, - txId, - setId, - isUsed, - ) - }); jindexes.add(currentIndex); spendTxIds.add(txId); @@ -396,73 +379,74 @@ Future> staticProcessRestore( Map result, int currentHeight, ) async { - List? _l = result['_lelantus_coins'] as List?; - final List> lelantusCoins = []; - for (var el in _l ?? []) { - lelantusCoins.add({el.keys.first: el.values.first as LelantusCoin}); - } + List lelantusCoins = + result['_lelantus_coins'] as List; // Edit the receive transactions with the mint fees. - Map editedTransactions = - {}; - for (var item in lelantusCoins) { - item.forEach((key, value) { - String txid = value.txId; - isar_models.Transaction? tx; + List editedTransactions = []; + + for (final coin in lelantusCoins) { + String txid = coin.txid; + isar_models.Transaction? tx; + try { + tx = txns.firstWhere((e) => e.txid == txid); + } catch (_) { + tx = null; + } + + if (tx == null || tx.subType == isar_models.TransactionSubType.join) { + // This is a jmint. + continue; + } + + List inputTxns = []; + for (final input in tx.inputs) { + isar_models.Transaction? inputTx; try { - tx = txns.firstWhere((e) => e.txid == txid); + inputTx = txns.firstWhere((e) => e.txid == input.txid); } catch (_) { - tx = null; + inputTx = null; } + if (inputTx != null) { + inputTxns.add(inputTx); + } + } + if (inputTxns.isEmpty) { + //some error. + Logging.instance.log( + "cryptic \"//some error\" occurred in staticProcessRestore on lelantus coin: $coin", + level: LogLevel.Error, + ); + continue; + } - if (tx == null || tx.subType == isar_models.TransactionSubType.join) { - // This is a jmint. - return; - } - List inputs = []; - for (var element in tx.inputs) { - isar_models.Transaction? input; - try { - input = txns.firstWhere((e) => e.txid == element.txid); - } catch (_) { - input = null; - } - if (input != null) { - inputs.add(input); - } - } - if (inputs.isEmpty) { - //some error. - return; - } - - int mintFee = tx.fee; - int sharedFee = mintFee ~/ inputs.length; - for (var element in inputs) { - editedTransactions[element.txid] = isar_models.Transaction( - walletId: element.walletId, - txid: element.txid, - timestamp: element.timestamp, - type: element.type, - subType: isar_models.TransactionSubType.mint, - amount: element.amount, - amountString: Amount( - rawValue: BigInt.from(element.amount), - fractionDigits: Coin.firo.decimals, - ).toJsonString(), - fee: sharedFee, - height: element.height, - isCancelled: false, - isLelantus: true, - slateId: null, - otherData: txid, - nonce: null, - inputs: element.inputs, - outputs: element.outputs, - numberOfMessages: null, - )..address.value = element.address.value; - } - }); + int mintFee = tx.fee; + int sharedFee = mintFee ~/ inputTxns.length; + for (final inputTx in inputTxns) { + final edited = isar_models.Transaction( + walletId: inputTx.walletId, + txid: inputTx.txid, + timestamp: inputTx.timestamp, + type: inputTx.type, + subType: isar_models.TransactionSubType.mint, + amount: inputTx.amount, + amountString: Amount( + rawValue: BigInt.from(inputTx.amount), + fractionDigits: Coin.firo.decimals, + ).toJsonString(), + fee: sharedFee, + height: inputTx.height, + isCancelled: false, + isLelantus: true, + slateId: null, + otherData: txid, + nonce: null, + inputs: inputTx.inputs, + outputs: inputTx.outputs, + numberOfMessages: null, + )..address.value = inputTx.address.value; + editedTransactions.add(edited); + } } // Logging.instance.log(editedTransactions, addToDebugMessagesDB: false); @@ -472,12 +456,13 @@ Future> staticProcessRestore( } // Logging.instance.log(transactionMap, addToDebugMessagesDB: false); - editedTransactions.forEach((key, value) { - transactionMap.update(key, (_value) => value); - }); + // update with edited transactions + for (final tx in editedTransactions) { + transactionMap[tx.txid] = tx; + } transactionMap.removeWhere((key, value) => - lelantusCoins.any((element) => element.containsKey(key)) || + lelantusCoins.any((element) => element.txid == key) || ((value.height == -1 || value.height == null) && !value.isConfirmed(currentHeight, MINIMUM_CONFIRMATIONS))); @@ -2248,9 +2233,9 @@ class FiroWallet extends CoinServiceAPI _feeObject = Future(() => feeObj); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.60, walletId)); - final lelantusCoins = getLelantusCoinMap(); - Logging.instance.log("_lelantus_coins at refresh: $lelantusCoins", - level: LogLevel.Warning, printFullLength: true); + // final lelantusCoins = getLelantusCoinMap(); + // Logging.instance.log("_lelantus_coins at refresh: $lelantusCoins", + // level: LogLevel.Warning, printFullLength: true); GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.70, walletId)); await _refreshLelantusData(); @@ -2327,7 +2312,8 @@ class FiroWallet extends CoinServiceAPI level: LogLevel.Error); } - final List lelantusCoins = await _getUnspentCoins(); + final List lelantusCoins = + await _getUnspentCoins(); final root = await Bip32Utils.getBip32Root( _mnemonic!, @@ -2349,7 +2335,7 @@ class FiroWallet extends CoinServiceAPI } final String privateKey = Format.uint8listToString(keyPair.privateKey!); return DartLelantusEntry(coin.isUsed ? 1 : 0, 0, coin.anonymitySetId, - coin.value, coin.index, privateKey); + int.parse(coin.value), coin.index, privateKey); }).toList(); final lelantusEntries = await Future.wait(waitLelantusEntries); @@ -2361,61 +2347,55 @@ class FiroWallet extends CoinServiceAPI return lelantusEntries; } - List> getLelantusCoinMap() { - final _l = firoGetLelantusCoins(); - final List> lelantusCoins = []; - for (var el in _l ?? []) { - lelantusCoins.add({el.keys.first: el.values.first as LelantusCoin}); - } - return lelantusCoins; - } + Future> _getUnspentCoins() async { + final jindexes = firoGetJIndex() ?? []; - Future> _getUnspentCoins() async { - final List> lelantusCoins = getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } - final jindexes = firoGetJIndex(); + final lelantusCoinsList = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .not() + .valueEqualTo("0") + .findAll(); - List coins = []; - - List lelantusCoinsList = - lelantusCoins.fold([], (previousValue, element) { - previousValue.add(element.values.first); - return previousValue; - }); + List coins = []; final currentChainHeight = await chainHeight; for (int i = 0; i < lelantusCoinsList.length; i++) { // Logging.instance.log("lelantusCoinsList[$i]: ${lelantusCoinsList[i]}"); - final txid = lelantusCoinsList[i].txId; - final txn = await cachedElectrumXClient.getTransaction( - txHash: txid, - verbose: true, - coin: coin, - ); - final confirmations = txn["confirmations"]; - bool isUnconfirmed = confirmations is int && confirmations < 1; + final coin = lelantusCoinsList[i]; - final tx = await db.getTransaction(walletId, txid); + final tx = await db.getTransaction(walletId, coin.txid); - if (!jindexes!.contains(lelantusCoinsList[i].index) && - tx != null && - tx.isLelantus == true && - !(tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS))) { - isUnconfirmed = true; + // TODO check if sane default + bool isUnconfirmed = false; + + if (tx != null) { + bool isConfirmed = tx.isConfirmed( + currentChainHeight, + MINIMUM_CONFIRMATIONS, + ); + if (!jindexes.contains(coin.index) && + tx.isLelantus == true && + !isConfirmed) { + isUnconfirmed = true; + } else if (!isConfirmed) { + continue; + } + } else { + final txn = await cachedElectrumXClient.getTransaction( + txHash: coin.txid, + verbose: true, + coin: this.coin, + ); + final confirmations = txn["confirmations"]; + isUnconfirmed = confirmations is int && confirmations < 1; } - - if (tx != null && - !tx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - continue; - } - if (!lelantusCoinsList[i].isUsed && - lelantusCoinsList[i].anonymitySetId != ANONYMITY_SET_EMPTY_ID && + if (!coin.isUsed && + coin.anonymitySetId != ANONYMITY_SET_EMPTY_ID && !isUnconfirmed) { - coins.add(lelantusCoinsList[i]); + coins.add(coin); } } return coins; @@ -2427,62 +2407,65 @@ class FiroWallet extends CoinServiceAPI Future _refreshBalance() async { try { final utxosUpdateFuture = _refreshUTXOs(); - final List> lelantusCoins = - getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .not() + .valueEqualTo(0.toString()) + .findAll(); final currentChainHeight = await chainHeight; final jindexes = firoGetJIndex(); int intLelantusBalance = 0; int unconfirmedLelantusBalance = 0; - for (final element in lelantusCoins) { - element.forEach((key, lelantusCoin) { - isar_models.Transaction? txn = db.isar.transactions - .where() - .txidWalletIdEqualTo( - lelantusCoin.txId, - walletId, - ) - .findFirstSync(); + for (final lelantusCoin in lelantusCoins) { + isar_models.Transaction? txn = db.isar.transactions + .where() + .txidWalletIdEqualTo( + lelantusCoin.txid, + walletId, + ) + .findFirstSync(); - if (txn == null) { - // TODO: ?????????????????????????????????????? - } else { - bool isLelantus = txn.isLelantus == true; - if (!jindexes!.contains(lelantusCoin.index) && isLelantus) { - if (!lelantusCoin.isUsed && - txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - // mint tx, add value to balance - intLelantusBalance += lelantusCoin.value; - } /* else { + if (txn == null) { + // TODO: ?????????????????????????????????????? + Logging.instance.log( + "Transaction not found in DB for lelantus coin: $lelantusCoin", + level: LogLevel.Fatal, + ); + } else { + bool isLelantus = txn.isLelantus == true; + if (!jindexes!.contains(lelantusCoin.index) && isLelantus) { + if (!lelantusCoin.isUsed && + txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { + // mint tx, add value to balance + intLelantusBalance += int.parse(lelantusCoin.value); + } /* else { // This coin is not confirmed and may be replaced }*/ - } else if (jindexes.contains(lelantusCoin.index) && - isLelantus && - !lelantusCoin.isUsed && - !txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - unconfirmedLelantusBalance += lelantusCoin.value; - } else if (jindexes.contains(lelantusCoin.index) && - !lelantusCoin.isUsed) { - intLelantusBalance += lelantusCoin.value; - } else if (!lelantusCoin.isUsed && - (txn.isLelantus == true - ? true - : txn.isConfirmed( - currentChainHeight, MINIMUM_CONFIRMATIONS) != - false)) { - intLelantusBalance += lelantusCoin.value; - } else if (!isLelantus && - txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) == - false) { - unconfirmedLelantusBalance += lelantusCoin.value; - } + } else if (jindexes.contains(lelantusCoin.index) && + isLelantus && + !lelantusCoin.isUsed && + !txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { + unconfirmedLelantusBalance += int.parse(lelantusCoin.value); + } else if (jindexes.contains(lelantusCoin.index) && + !lelantusCoin.isUsed) { + intLelantusBalance += int.parse(lelantusCoin.value); + } else if (!lelantusCoin.isUsed && + (txn.isLelantus == true + ? true + : txn.isConfirmed( + currentChainHeight, MINIMUM_CONFIRMATIONS) != + false)) { + intLelantusBalance += int.parse(lelantusCoin.value); + } else if (!isLelantus && + txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) == + false) { + unconfirmedLelantusBalance += int.parse(lelantusCoin.value); } - }); + } } _balancePrivate = Balance( @@ -2551,17 +2534,19 @@ class FiroWallet extends CoinServiceAPI } } - final List> lelantusCoins = getLelantusCoinMap(); - if (lelantusCoins.isNotEmpty) { - lelantusCoins.removeWhere((element) => - element.values.any((elementCoin) => elementCoin.value == 0)); - } + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .not() + .valueEqualTo(0.toString()) + .findAll(); + final data = await _txnData; for (final value in data) { if (value.inputs.isNotEmpty) { for (var element in value.inputs) { - if (lelantusCoins - .any((element) => element.keys.contains(value.txid)) && + if (lelantusCoins.any((e) => e.txid == value.txid) && spendableOutputs.firstWhere( (output) => output?.txid == element.txid, orElse: () => null) != @@ -2844,7 +2829,14 @@ class FiroWallet extends CoinServiceAPI } Future _refreshLelantusData() async { - final List> lelantusCoins = getLelantusCoinMap(); + final lelantusCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .not() + .valueEqualTo(0.toString()) + .findAll(); + final jindexes = firoGetJIndex(); // Get all joinsplit transaction ids @@ -2855,19 +2847,16 @@ class FiroWallet extends CoinServiceAPI .isLelantusEqualTo(true) .findAll(); List joinsplits = []; + for (final tx in listLelantusTxData) { if (tx.subType == isar_models.TransactionSubType.join) { joinsplits.add(tx.txid); } } - for (final coin - in lelantusCoins.fold([], (previousValue, element) { - (previousValue as List).add(element.values.first); - return previousValue; - })) { + for (final coin in lelantusCoins) { if (jindexes != null) { - if (jindexes.contains(coin.index) && !joinsplits.contains(coin.txId)) { - joinsplits.add(coin.txId); + if (jindexes.contains(coin.index) && !joinsplits.contains(coin.txid)) { + joinsplits.add(coin.txid); } } } @@ -3030,51 +3019,66 @@ class FiroWallet extends CoinServiceAPI Logging.instance.log( "_submitLelantusToNetwork txid: ${transactionInfo['txid']}", level: LogLevel.Info); + if (txid == transactionInfo['txid']) { final index = firoGetMintIndex(); - final List> lelantusCoins = - getLelantusCoinMap(); - List> coins; - if (lelantusCoins.isEmpty) { - coins = []; - } else { - coins = [...lelantusCoins]; - } if (transactionInfo['spendCoinIndexes'] != null) { // This is a joinsplit + final spentCoinIndexes = + transactionInfo['spendCoinIndexes'] as List; + final List updatedCoins = []; + // Update all of the coins that have been spent. - for (final lCoinMap in coins) { - final lCoin = lCoinMap.values.first; - if ((transactionInfo['spendCoinIndexes'] as List) - .contains(lCoin.index)) { - lCoinMap[lCoinMap.keys.first] = LelantusCoin( - lCoin.index, - lCoin.value, - lCoin.publicCoin, - lCoin.txId, - lCoin.anonymitySetId, - true); + + for (final index in spentCoinIndexes) { + final possibleCoins = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .indexEqualTo(index) + .findAll(); + + if (possibleCoins.isNotEmpty) { + if (possibleCoins.length > 1) { + print( + "======================= possibleCoins.length > 1 !!! ================================="); + } else { + final spentCoin = possibleCoins.first; + updatedCoins.add(spentCoin.copyWith(isUsed: true)); + } } } // if a jmint was made add it to the unspent coin index - LelantusCoin jmint = LelantusCoin( - index, - transactionInfo['jmintValue'] as int? ?? 0, - transactionInfo['publicCoin'] as String, - transactionInfo['txid'] as String, - latestSetId, - false); - if (jmint.value > 0) { - coins.add({jmint.txId: jmint}); + final jmint = isar_models.LelantusCoin( + walletId: walletId, + index: index, + value: (transactionInfo['jmintValue'] as int? ?? 0).toString(), + publicCoin: transactionInfo['publicCoin'] as String, + txid: transactionInfo['txid'] as String, + anonymitySetId: latestSetId, + isUsed: false, + ); + if (int.parse(jmint.value) > 0) { + updatedCoins.add(jmint); final jindexes = firoGetJIndex()!; jindexes.add(index); await firoUpdateJIndex(jindexes); await firoUpdateMintIndex(index + 1); } - await firoUpdateLelantusCoins(coins); + + await db.isar.writeTxn(() async { + for (final c in updatedCoins) { + await db.isar.lelantusCoins.deleteByPublicCoinWalletIdTxid( + c.publicCoin, + c.walletId, + c.txid, + ); + } + await db.isar.lelantusCoins.putAll(updatedCoins); + }); final amount = Amount.fromDecimal( Decimal.parse(transactionInfo["amount"].toString()), @@ -3087,14 +3091,8 @@ class FiroWallet extends CoinServiceAPI txid: transactionInfo['txid'] as String, timestamp: transactionInfo['timestamp'] as int? ?? (DateTime.now().millisecondsSinceEpoch ~/ 1000), - type: transactionInfo['txType'] == "Received" - ? isar_models.TransactionType.incoming - : isar_models.TransactionType.outgoing, - subType: transactionInfo["subType"] == "mint" - ? isar_models.TransactionSubType.mint - : transactionInfo["subType"] == "join" - ? isar_models.TransactionSubType.join - : isar_models.TransactionSubType.none, + type: isar_models.TransactionType.outgoing, + subType: isar_models.TransactionSubType.join, amount: amount.raw.toInt(), amountString: amount.toJsonString(), fee: Amount.fromDecimal( @@ -3137,25 +3135,30 @@ class FiroWallet extends CoinServiceAPI // This is a mint Logging.instance.log("this is a mint", level: LogLevel.Info); + final List updatedCoins = []; + // TODO: transactionInfo['mintsMap'] for (final mintMap in transactionInfo['mintsMap'] as List>) { - final index = mintMap['index'] as int?; - LelantusCoin mint = LelantusCoin( - index!, - mintMap['value'] as int, - mintMap['publicCoin'] as String, - transactionInfo['txid'] as String, - latestSetId, - false, + final index = mintMap['index'] as int; + final mint = isar_models.LelantusCoin( + walletId: walletId, + index: index, + value: (mintMap['value'] as int).toString(), + publicCoin: mintMap['publicCoin'] as String, + txid: transactionInfo['txid'] as String, + anonymitySetId: latestSetId, + isUsed: false, ); - if (mint.value > 0) { - coins.add({mint.txId: mint}); + if (int.parse(mint.value) > 0) { + updatedCoins.add(mint); await firoUpdateMintIndex(index + 1); } } // Logging.instance.log(coins); - await firoUpdateLelantusCoins(coins); + await db.isar.writeTxn(() async { + await db.isar.lelantusCoins.putAll(updatedCoins); + }); } return true; } else { @@ -4556,6 +4559,7 @@ class FiroWallet extends CoinServiceAPI "setDataMap": setDataMap, "usedSerialNumbers": usedSerialNumbers, "network": _network, + "walletId": walletId, }); await Future.wait([dataFuture]); @@ -4576,7 +4580,10 @@ class FiroWallet extends CoinServiceAPI await Future.wait([ firoUpdateMintIndex(message['mintIndex'] as int), - firoUpdateLelantusCoins(message['_lelantus_coins'] as List), + db.isar.writeTxn(() async { + await db.isar.lelantusCoins.putAll( + message['_lelantus_coins'] as List); + }), firoUpdateJIndex(message['jindex'] as List), ]); diff --git a/lib/services/mixins/firo_hive.dart b/lib/services/mixins/firo_hive.dart index be9845453..631aebe6d 100644 --- a/lib/services/mixins/firo_hive.dart +++ b/lib/services/mixins/firo_hive.dart @@ -30,19 +30,19 @@ mixin FiroHive { ); } - // _lelantus_coins - List? firoGetLelantusCoins() { - return DB.instance.get(boxName: _walletId, key: "_lelantus_coins") - as List?; - } - - Future firoUpdateLelantusCoins(List lelantusCoins) async { - await DB.instance.put( - boxName: _walletId, - key: "_lelantus_coins", - value: lelantusCoins, - ); - } + // // _lelantus_coins + // List? firoGetLelantusCoins() { + // return DB.instance.get(boxName: _walletId, key: "_lelantus_coins") + // as List?; + // } + // + // Future firoUpdateLelantusCoins(List lelantusCoins) async { + // await DB.instance.put( + // boxName: _walletId, + // key: "_lelantus_coins", + // value: lelantusCoins, + // ); + // } // mintIndex int firoGetMintIndex() { diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index 3f69161f4..dac1dbe82 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -388,7 +388,8 @@ class DbVersionMigrator with WalletDB { final List coins = []; for (final e in hiveLCoins) { - final lcoin = e as LelantusCoin; + final map = e as Map; + final lcoin = map.values.first as LelantusCoin; final coin = isar_models.LelantusCoin( walletId: walletId, From c97de6017bec6a8dcf5d51ab6467464e3b2b223f Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 15:42:45 -0600 Subject: [PATCH 05/37] update lelantus coin schema --- lib/db/isar/main_db.dart | 1 - .../models/firo_specific/lelantus_coin.dart | 29 +- .../models/firo_specific/lelantus_coin.g.dart | 663 +++++++++++++++--- lib/services/coins/firo/firo_wallet.dart | 24 +- lib/utilities/db_version_migration.dart | 11 +- test/services/coins/manager_test.mocks.dart | 55 -- .../transaction_card_test.mocks.dart | 54 -- 7 files changed, 634 insertions(+), 203 deletions(-) diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index 209f13c5d..fa565c0cd 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -14,7 +14,6 @@ import 'package:isar/isar.dart'; import 'package:stackwallet/exceptions/main_db/main_db_exception.dart'; import 'package:stackwallet/models/isar/models/block_explorer.dart'; import 'package:stackwallet/models/isar/models/contact_entry.dart'; -import 'package:stackwallet/models/isar/models/firo_specific/lelantus_coin.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/stack_theme.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.dart b/lib/models/isar/models/firo_specific/lelantus_coin.dart index 3dd48b2dc..9ad5bc5a3 100644 --- a/lib/models/isar/models/firo_specific/lelantus_coin.dart +++ b/lib/models/isar/models/firo_specific/lelantus_coin.dart @@ -23,20 +23,33 @@ class LelantusCoin { final String value; // can't use BigInt in isar :shrug: - final int index; + @Index( + unique: true, + replace: false, + composite: [ + CompositeIndex("walletId"), + ], + ) + final int mintIndex; final int anonymitySetId; final bool isUsed; + final bool isJMint; + + final String? otherData; + LelantusCoin({ required this.walletId, required this.publicCoin, required this.txid, required this.value, - required this.index, + required this.mintIndex, required this.anonymitySetId, required this.isUsed, + required this.isJMint, + required this.otherData, }); LelantusCoin copyWith({ @@ -44,18 +57,22 @@ class LelantusCoin { String? publicCoin, String? txid, String? value, - int? index, + int? mintIndex, int? anonymitySetId, bool? isUsed, + bool? isJMint, + String? otherData, }) { return LelantusCoin( walletId: walletId ?? this.walletId, publicCoin: publicCoin ?? this.publicCoin, txid: txid ?? this.txid, value: value ?? this.value, - index: index ?? this.index, + mintIndex: mintIndex ?? this.mintIndex, anonymitySetId: anonymitySetId ?? this.anonymitySetId, isUsed: isUsed ?? this.isUsed, + isJMint: isJMint ?? this.isJMint, + otherData: otherData ?? this.otherData, ); } @@ -67,8 +84,10 @@ class LelantusCoin { 'publicCoin: $publicCoin, ' 'txid: $txid, ' 'value: $value, ' - 'index: $index, ' + 'mintIndex: $mintIndex, ' 'anonymitySetId: $anonymitySetId, ' + 'otherData: $otherData, ' + 'isJMint: $isJMint, ' 'isUsed: $isUsed' '}'; } diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart index 8fafde571..d2e27638d 100644 --- a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart +++ b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart @@ -22,33 +22,43 @@ const LelantusCoinSchema = CollectionSchema( name: r'anonymitySetId', type: IsarType.long, ), - r'index': PropertySchema( + r'isJMint': PropertySchema( id: 1, - name: r'index', - type: IsarType.long, + name: r'isJMint', + type: IsarType.bool, ), r'isUsed': PropertySchema( id: 2, name: r'isUsed', type: IsarType.bool, ), - r'publicCoin': PropertySchema( + r'mintIndex': PropertySchema( id: 3, + name: r'mintIndex', + type: IsarType.long, + ), + r'otherData': PropertySchema( + id: 4, + name: r'otherData', + type: IsarType.string, + ), + r'publicCoin': PropertySchema( + id: 5, name: r'publicCoin', type: IsarType.string, ), r'txid': PropertySchema( - id: 4, + id: 6, name: r'txid', type: IsarType.string, ), r'value': PropertySchema( - id: 5, + id: 7, name: r'value', type: IsarType.string, ), r'walletId': PropertySchema( - id: 6, + id: 8, name: r'walletId', type: IsarType.string, ) @@ -94,6 +104,24 @@ const LelantusCoinSchema = CollectionSchema( caseSensitive: true, ) ], + ), + r'mintIndex_walletId': IndexSchema( + id: -9147309777276196770, + name: r'mintIndex_walletId', + unique: true, + replace: false, + properties: [ + IndexPropertySchema( + name: r'mintIndex', + type: IndexType.value, + caseSensitive: false, + ), + IndexPropertySchema( + name: r'walletId', + type: IndexType.hash, + caseSensitive: true, + ) + ], ) }, links: {}, @@ -110,6 +138,12 @@ int _lelantusCoinEstimateSize( Map> allOffsets, ) { var bytesCount = offsets.last; + { + final value = object.otherData; + if (value != null) { + bytesCount += 3 + value.length * 3; + } + } bytesCount += 3 + object.publicCoin.length * 3; bytesCount += 3 + object.txid.length * 3; bytesCount += 3 + object.value.length * 3; @@ -124,12 +158,14 @@ void _lelantusCoinSerialize( Map> allOffsets, ) { writer.writeLong(offsets[0], object.anonymitySetId); - writer.writeLong(offsets[1], object.index); + writer.writeBool(offsets[1], object.isJMint); writer.writeBool(offsets[2], object.isUsed); - writer.writeString(offsets[3], object.publicCoin); - writer.writeString(offsets[4], object.txid); - writer.writeString(offsets[5], object.value); - writer.writeString(offsets[6], object.walletId); + writer.writeLong(offsets[3], object.mintIndex); + writer.writeString(offsets[4], object.otherData); + writer.writeString(offsets[5], object.publicCoin); + writer.writeString(offsets[6], object.txid); + writer.writeString(offsets[7], object.value); + writer.writeString(offsets[8], object.walletId); } LelantusCoin _lelantusCoinDeserialize( @@ -140,12 +176,14 @@ LelantusCoin _lelantusCoinDeserialize( ) { final object = LelantusCoin( anonymitySetId: reader.readLong(offsets[0]), - index: reader.readLong(offsets[1]), + isJMint: reader.readBool(offsets[1]), isUsed: reader.readBool(offsets[2]), - publicCoin: reader.readString(offsets[3]), - txid: reader.readString(offsets[4]), - value: reader.readString(offsets[5]), - walletId: reader.readString(offsets[6]), + mintIndex: reader.readLong(offsets[3]), + otherData: reader.readStringOrNull(offsets[4]), + publicCoin: reader.readString(offsets[5]), + txid: reader.readString(offsets[6]), + value: reader.readString(offsets[7]), + walletId: reader.readString(offsets[8]), ); object.id = id; return object; @@ -161,17 +199,21 @@ P _lelantusCoinDeserializeProp

( case 0: return (reader.readLong(offset)) as P; case 1: - return (reader.readLong(offset)) as P; + return (reader.readBool(offset)) as P; case 2: return (reader.readBool(offset)) as P; case 3: - return (reader.readString(offset)) as P; + return (reader.readLong(offset)) as P; case 4: - return (reader.readString(offset)) as P; + return (reader.readStringOrNull(offset)) as P; case 5: return (reader.readString(offset)) as P; case 6: return (reader.readString(offset)) as P; + case 7: + return (reader.readString(offset)) as P; + case 8: + return (reader.readString(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); } @@ -290,6 +332,92 @@ extension LelantusCoinByIndex on IsarCollection { return putAllByIndexSync(r'publicCoin_walletId_txid', objects, saveLinks: saveLinks); } + + Future getByMintIndexWalletId(int mintIndex, String walletId) { + return getByIndex(r'mintIndex_walletId', [mintIndex, walletId]); + } + + LelantusCoin? getByMintIndexWalletIdSync(int mintIndex, String walletId) { + return getByIndexSync(r'mintIndex_walletId', [mintIndex, walletId]); + } + + Future deleteByMintIndexWalletId(int mintIndex, String walletId) { + return deleteByIndex(r'mintIndex_walletId', [mintIndex, walletId]); + } + + bool deleteByMintIndexWalletIdSync(int mintIndex, String walletId) { + return deleteByIndexSync(r'mintIndex_walletId', [mintIndex, walletId]); + } + + Future> getAllByMintIndexWalletId( + List mintIndexValues, List walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return getAllByIndex(r'mintIndex_walletId', values); + } + + List getAllByMintIndexWalletIdSync( + List mintIndexValues, List walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return getAllByIndexSync(r'mintIndex_walletId', values); + } + + Future deleteAllByMintIndexWalletId( + List mintIndexValues, List walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return deleteAllByIndex(r'mintIndex_walletId', values); + } + + int deleteAllByMintIndexWalletIdSync( + List mintIndexValues, List walletIdValues) { + final len = mintIndexValues.length; + assert(walletIdValues.length == len, + 'All index values must have the same length'); + final values = >[]; + for (var i = 0; i < len; i++) { + values.add([mintIndexValues[i], walletIdValues[i]]); + } + + return deleteAllByIndexSync(r'mintIndex_walletId', values); + } + + Future putByMintIndexWalletId(LelantusCoin object) { + return putByIndex(r'mintIndex_walletId', object); + } + + Id putByMintIndexWalletIdSync(LelantusCoin object, {bool saveLinks = true}) { + return putByIndexSync(r'mintIndex_walletId', object, saveLinks: saveLinks); + } + + Future> putAllByMintIndexWalletId(List objects) { + return putAllByIndex(r'mintIndex_walletId', objects); + } + + List putAllByMintIndexWalletIdSync(List objects, + {bool saveLinks = true}) { + return putAllByIndexSync(r'mintIndex_walletId', objects, + saveLinks: saveLinks); + } } extension LelantusCoinQueryWhereSort @@ -552,6 +680,144 @@ extension LelantusCoinQueryWhere } }); } + + QueryBuilder + mintIndexEqualToAnyWalletId(int mintIndex) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'mintIndex_walletId', + value: [mintIndex], + )); + }); + } + + QueryBuilder + mintIndexNotEqualToAnyWalletId(int mintIndex) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [], + upper: [mintIndex], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + includeLower: false, + upper: [], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + includeLower: false, + upper: [], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [], + upper: [mintIndex], + includeUpper: false, + )); + } + }); + } + + QueryBuilder + mintIndexGreaterThanAnyWalletId( + int mintIndex, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + includeLower: include, + upper: [], + )); + }); + } + + QueryBuilder + mintIndexLessThanAnyWalletId( + int mintIndex, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [], + upper: [mintIndex], + includeUpper: include, + )); + }); + } + + QueryBuilder + mintIndexBetweenAnyWalletId( + int lowerMintIndex, + int upperMintIndex, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [lowerMintIndex], + includeLower: includeLower, + upper: [upperMintIndex], + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + mintIndexWalletIdEqualTo(int mintIndex, String walletId) { + return QueryBuilder.apply(this, (query) { + return query.addWhereClause(IndexWhereClause.equalTo( + indexName: r'mintIndex_walletId', + value: [mintIndex, walletId], + )); + }); + } + + QueryBuilder + mintIndexEqualToWalletIdNotEqualTo(int mintIndex, String walletId) { + return QueryBuilder.apply(this, (query) { + if (query.whereSort == Sort.asc) { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + upper: [mintIndex, walletId], + includeUpper: false, + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex, walletId], + includeLower: false, + upper: [mintIndex], + )); + } else { + return query + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex, walletId], + includeLower: false, + upper: [mintIndex], + )) + .addWhereClause(IndexWhereClause.between( + indexName: r'mintIndex_walletId', + lower: [mintIndex], + upper: [mintIndex, walletId], + includeUpper: false, + )); + } + }); + } } extension LelantusCoinQueryFilter @@ -665,60 +931,16 @@ extension LelantusCoinQueryFilter }); } - QueryBuilder indexEqualTo( - int value) { + QueryBuilder + isJMintEqualTo(bool value) { return QueryBuilder.apply(this, (query) { return query.addFilterCondition(FilterCondition.equalTo( - property: r'index', + property: r'isJMint', value: value, )); }); } - QueryBuilder - indexGreaterThan( - int value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'index', - value: value, - )); - }); - } - - QueryBuilder indexLessThan( - int value, { - bool include = false, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'index', - value: value, - )); - }); - } - - QueryBuilder indexBetween( - int lower, - int upper, { - bool includeLower = true, - bool includeUpper = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'index', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - )); - }); - } - QueryBuilder isUsedEqualTo( bool value) { return QueryBuilder.apply(this, (query) { @@ -729,6 +951,216 @@ extension LelantusCoinQueryFilter }); } + QueryBuilder + mintIndexEqualTo(int value) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'mintIndex', + value: value, + )); + }); + } + + QueryBuilder + mintIndexGreaterThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'mintIndex', + value: value, + )); + }); + } + + QueryBuilder + mintIndexLessThan( + int value, { + bool include = false, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'mintIndex', + value: value, + )); + }); + } + + QueryBuilder + mintIndexBetween( + int lower, + int upper, { + bool includeLower = true, + bool includeUpper = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'mintIndex', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + )); + }); + } + + QueryBuilder + otherDataIsNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNull( + property: r'otherData', + )); + }); + } + + QueryBuilder + otherDataIsNotNull() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(const FilterCondition.isNotNull( + property: r'otherData', + )); + }); + } + + QueryBuilder + otherDataEqualTo( + String? value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataGreaterThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + include: include, + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataLessThan( + String? value, { + bool include = false, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.lessThan( + include: include, + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataBetween( + String? lower, + String? upper, { + bool includeLower = true, + bool includeUpper = true, + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.between( + property: r'otherData', + lower: lower, + includeLower: includeLower, + upper: upper, + includeUpper: includeUpper, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataStartsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.startsWith( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataEndsWith( + String value, { + bool caseSensitive = true, + }) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.endsWith( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataContains(String value, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.contains( + property: r'otherData', + value: value, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataMatches(String pattern, {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.matches( + property: r'otherData', + wildcard: pattern, + caseSensitive: caseSensitive, + )); + }); + } + + QueryBuilder + otherDataIsEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.equalTo( + property: r'otherData', + value: '', + )); + }); + } + + QueryBuilder + otherDataIsNotEmpty() { + return QueryBuilder.apply(this, (query) { + return query.addFilterCondition(FilterCondition.greaterThan( + property: r'otherData', + value: '', + )); + }); + } + QueryBuilder publicCoinEqualTo( String value, { @@ -1292,15 +1724,15 @@ extension LelantusCoinQuerySortBy }); } - QueryBuilder sortByIndex() { + QueryBuilder sortByIsJMint() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'index', Sort.asc); + return query.addSortBy(r'isJMint', Sort.asc); }); } - QueryBuilder sortByIndexDesc() { + QueryBuilder sortByIsJMintDesc() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'index', Sort.desc); + return query.addSortBy(r'isJMint', Sort.desc); }); } @@ -1316,6 +1748,30 @@ extension LelantusCoinQuerySortBy }); } + QueryBuilder sortByMintIndex() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.asc); + }); + } + + QueryBuilder sortByMintIndexDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.desc); + }); + } + + QueryBuilder sortByOtherData() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.asc); + }); + } + + QueryBuilder sortByOtherDataDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.desc); + }); + } + QueryBuilder sortByPublicCoin() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'publicCoin', Sort.asc); @@ -1394,15 +1850,15 @@ extension LelantusCoinQuerySortThenBy }); } - QueryBuilder thenByIndex() { + QueryBuilder thenByIsJMint() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'index', Sort.asc); + return query.addSortBy(r'isJMint', Sort.asc); }); } - QueryBuilder thenByIndexDesc() { + QueryBuilder thenByIsJMintDesc() { return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'index', Sort.desc); + return query.addSortBy(r'isJMint', Sort.desc); }); } @@ -1418,6 +1874,30 @@ extension LelantusCoinQuerySortThenBy }); } + QueryBuilder thenByMintIndex() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.asc); + }); + } + + QueryBuilder thenByMintIndexDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'mintIndex', Sort.desc); + }); + } + + QueryBuilder thenByOtherData() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.asc); + }); + } + + QueryBuilder thenByOtherDataDesc() { + return QueryBuilder.apply(this, (query) { + return query.addSortBy(r'otherData', Sort.desc); + }); + } + QueryBuilder thenByPublicCoin() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'publicCoin', Sort.asc); @@ -1477,9 +1957,9 @@ extension LelantusCoinQueryWhereDistinct }); } - QueryBuilder distinctByIndex() { + QueryBuilder distinctByIsJMint() { return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'index'); + return query.addDistinctBy(r'isJMint'); }); } @@ -1489,6 +1969,19 @@ extension LelantusCoinQueryWhereDistinct }); } + QueryBuilder distinctByMintIndex() { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'mintIndex'); + }); + } + + QueryBuilder distinctByOtherData( + {bool caseSensitive = true}) { + return QueryBuilder.apply(this, (query) { + return query.addDistinctBy(r'otherData', caseSensitive: caseSensitive); + }); + } + QueryBuilder distinctByPublicCoin( {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { @@ -1532,9 +2025,9 @@ extension LelantusCoinQueryProperty }); } - QueryBuilder indexProperty() { + QueryBuilder isJMintProperty() { return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'index'); + return query.addPropertyName(r'isJMint'); }); } @@ -1544,6 +2037,18 @@ extension LelantusCoinQueryProperty }); } + QueryBuilder mintIndexProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'mintIndex'); + }); + } + + QueryBuilder otherDataProperty() { + return QueryBuilder.apply(this, (query) { + return query.addPropertyName(r'otherData'); + }); + } + QueryBuilder publicCoinProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'publicCoin'); diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 004e5399c..67882a6b6 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -269,18 +269,20 @@ Future> isolateRestore( lelantusCoins.removeWhere((e) => e.txid == txId && - e.index == currentIndex && + e.mintIndex == currentIndex && e.anonymitySetId != setId); lelantusCoins.add( isar_models.LelantusCoin( walletId: walletId, - index: currentIndex, + mintIndex: currentIndex, value: amount.toString(), publicCoin: publicCoin, txid: txId, anonymitySetId: setId, isUsed: isUsed, + isJMint: false, + otherData: null, ), ); Logging.instance.log( @@ -317,18 +319,20 @@ Future> isolateRestore( bool isUsed = usedSerialNumbersSet.contains(serialNumber); lelantusCoins.removeWhere((e) => e.txid == txId && - e.index == currentIndex && + e.mintIndex == currentIndex && e.anonymitySetId != setId); lelantusCoins.add( isar_models.LelantusCoin( walletId: walletId, - index: currentIndex, + mintIndex: currentIndex, value: amount.toString(), publicCoin: publicCoin, txid: txId, anonymitySetId: setId, isUsed: isUsed, + isJMint: true, + otherData: null, ), ); jindexes.add(currentIndex); @@ -2325,7 +2329,7 @@ class FiroWallet extends CoinServiceAPI final derivePath = constructDerivePath( networkWIF: _network.wif, chain: MINT_INDEX, - index: coin.index, + index: coin.mintIndex, ); final keyPair = await Bip32Utils.getBip32NodeFromRoot(root, derivePath); @@ -2335,7 +2339,7 @@ class FiroWallet extends CoinServiceAPI } final String privateKey = Format.uint8listToString(keyPair.privateKey!); return DartLelantusEntry(coin.isUsed ? 1 : 0, 0, coin.anonymitySetId, - int.parse(coin.value), coin.index, privateKey); + int.parse(coin.value), coin.mintIndex, privateKey); }).toList(); final lelantusEntries = await Future.wait(waitLelantusEntries); @@ -3054,12 +3058,14 @@ class FiroWallet extends CoinServiceAPI // if a jmint was made add it to the unspent coin index final jmint = isar_models.LelantusCoin( walletId: walletId, - index: index, + mintIndex: nextFreeMintIndex, value: (transactionInfo['jmintValue'] as int? ?? 0).toString(), publicCoin: transactionInfo['publicCoin'] as String, txid: transactionInfo['txid'] as String, anonymitySetId: latestSetId, isUsed: false, + isJMint: true, + otherData: null, ); if (int.parse(jmint.value) > 0) { updatedCoins.add(jmint); @@ -3143,12 +3149,14 @@ class FiroWallet extends CoinServiceAPI final index = mintMap['index'] as int; final mint = isar_models.LelantusCoin( walletId: walletId, - index: index, + mintIndex: index, value: (mintMap['value'] as int).toString(), publicCoin: mintMap['publicCoin'] as String, txid: transactionInfo['txid'] as String, anonymitySetId: latestSetId, isUsed: false, + isJMint: false, + otherData: null, ); if (int.parse(mint.value) > 0) { updatedCoins.add(mint); diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index dac1dbe82..d2c0adbe8 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -386,19 +386,28 @@ class DbVersionMigrator with WalletDB { ) as List? ?? []; + final jindexes = (DB.instance + .get(boxName: walletId, key: "jindex") as List? ?? + []) + .cast(); + final List coins = []; for (final e in hiveLCoins) { final map = e as Map; final lcoin = map.values.first as LelantusCoin; + final isJMint = jindexes.contains(lcoin.index); + final coin = isar_models.LelantusCoin( walletId: walletId, publicCoin: lcoin.publicCoin, txid: lcoin.txId, value: lcoin.value.toString(), - index: lcoin.index, + mintIndex: lcoin.index, anonymitySetId: lcoin.anonymitySetId, isUsed: lcoin.isUsed, + isJMint: isJMint, + otherData: null, ); coins.add(coin); diff --git a/test/services/coins/manager_test.mocks.dart b/test/services/coins/manager_test.mocks.dart index 8b48a38a9..25fd46d9d 100644 --- a/test/services/coins/manager_test.mocks.dart +++ b/test/services/coins/manager_test.mocks.dart @@ -14,7 +14,6 @@ import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart' as _i5; import 'package:stackwallet/electrumx_rpc/electrumx.dart' as _i4; import 'package:stackwallet/models/balance.dart' as _i6; import 'package:stackwallet/models/isar/models/isar_models.dart' as _i13; -import 'package:stackwallet/models/lelantus_coin.dart' as _i15; import 'package:stackwallet/models/paymint/fee_object_model.dart' as _i3; import 'package:stackwallet/models/signing_data.dart' as _i14; import 'package:stackwallet/services/coins/firo/firo_wallet.dart' as _i10; @@ -589,15 +588,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); @override - List> getLelantusCoinMap() => - (super.noSuchMethod( - Invocation.method( - #getLelantusCoinMap, - [], - ), - returnValue: >[], - ) as List>); - @override _i11.Future anonymizeAllPublicFunds() => (super.noSuchMethod( Invocation.method( #anonymizeAllPublicFunds, @@ -1061,51 +1051,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { ), returnValueForMissingStub: null, ); - @override - void initFiroHive(String? walletId) => super.noSuchMethod( - Invocation.method( - #initFiroHive, - [walletId], - ), - returnValueForMissingStub: null, - ); - @override - _i11.Future firoUpdateJIndex(List? jIndex) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateJIndex, - [jIndex], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); - @override - _i11.Future firoUpdateLelantusCoins(List? lelantusCoins) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateLelantusCoins, - [lelantusCoins], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); - @override - int firoGetMintIndex() => (super.noSuchMethod( - Invocation.method( - #firoGetMintIndex, - [], - ), - returnValue: 0, - ) as int); - @override - _i11.Future firoUpdateMintIndex(int? mintIndex) => (super.noSuchMethod( - Invocation.method( - #firoUpdateMintIndex, - [mintIndex], - ), - returnValue: _i11.Future.value(), - returnValueForMissingStub: _i11.Future.value(), - ) as _i11.Future); } /// A class which mocks [ElectrumX]. diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 4fc6f8832..29bd7bd29 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -1602,15 +1602,6 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet { returnValueForMissingStub: _i19.Future.value(), ) as _i19.Future); @override - List> getLelantusCoinMap() => - (super.noSuchMethod( - Invocation.method( - #getLelantusCoinMap, - [], - ), - returnValue: >[], - ) as List>); - @override _i19.Future anonymizeAllPublicFunds() => (super.noSuchMethod( Invocation.method( #anonymizeAllPublicFunds, @@ -2075,51 +2066,6 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet { ), returnValueForMissingStub: null, ); - @override - void initFiroHive(String? walletId) => super.noSuchMethod( - Invocation.method( - #initFiroHive, - [walletId], - ), - returnValueForMissingStub: null, - ); - @override - _i19.Future firoUpdateJIndex(List? jIndex) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateJIndex, - [jIndex], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future firoUpdateLelantusCoins(List? lelantusCoins) => - (super.noSuchMethod( - Invocation.method( - #firoUpdateLelantusCoins, - [lelantusCoins], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - int firoGetMintIndex() => (super.noSuchMethod( - Invocation.method( - #firoGetMintIndex, - [], - ), - returnValue: 0, - ) as int); - @override - _i19.Future firoUpdateMintIndex(int? mintIndex) => (super.noSuchMethod( - Invocation.method( - #firoUpdateMintIndex, - [mintIndex], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); } /// A class which mocks [LocaleService]. From 2fb94444c46965c34d5adcaaa9257fda649a718a Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 16:49:11 -0600 Subject: [PATCH 06/37] no more firo specific hive --- lib/services/coins/firo/firo_wallet.dart | 322 ++++++++++------------- lib/services/mixins/firo_hive.dart | 61 ----- 2 files changed, 145 insertions(+), 238 deletions(-) delete mode 100644 lib/services/mixins/firo_hive.dart diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 67882a6b6..a5a7b3b47 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -35,7 +35,6 @@ import 'package:stackwallet/services/event_bus/events/global/refresh_percent_cha import 'package:stackwallet/services/event_bus/events/global/updated_in_background_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; -import 'package:stackwallet/services/mixins/firo_hive.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart'; import 'package:stackwallet/services/mixins/xpubable.dart'; @@ -752,7 +751,7 @@ Future _setTestnetWrapper(bool isTestnet) async { /// Handles a single instance of a firo wallet class FiroWallet extends CoinServiceAPI - with WalletCache, WalletDB, FiroHive + with WalletCache, WalletDB implements XPubAble { // Constructor FiroWallet({ @@ -773,7 +772,6 @@ class FiroWallet extends CoinServiceAPI _cachedElectrumXClient = cachedClient; _secureStore = secureStore; initCache(walletId, coin); - initFiroHive(walletId); initWalletDB(mockableOverride: mockableOverride); Logging.instance.log("$walletName isolates length: ${isolates.length}", @@ -2181,7 +2179,6 @@ class FiroWallet extends CoinServiceAPI value: "", ); - await firoUpdateJIndex([]); // Generate and add addresses to relevant arrays final initialReceivingAddress = await _generateAddressForChain(0, 0); final initialChangeAddress = await _generateAddressForChain(1, 0); @@ -2352,57 +2349,60 @@ class FiroWallet extends CoinServiceAPI } Future> _getUnspentCoins() async { - final jindexes = firoGetJIndex() ?? []; + // final jindexes = firoGetJIndex() ?? []; final lelantusCoinsList = await db.isar.lelantusCoins .where() .walletIdEqualTo(walletId) .filter() + .isUsedEqualTo(false) // TODO add this? .not() .valueEqualTo("0") .findAll(); - List coins = []; + return lelantusCoinsList; - final currentChainHeight = await chainHeight; - - for (int i = 0; i < lelantusCoinsList.length; i++) { - // Logging.instance.log("lelantusCoinsList[$i]: ${lelantusCoinsList[i]}"); - final coin = lelantusCoinsList[i]; - - final tx = await db.getTransaction(walletId, coin.txid); - - // TODO check if sane default - bool isUnconfirmed = false; - - if (tx != null) { - bool isConfirmed = tx.isConfirmed( - currentChainHeight, - MINIMUM_CONFIRMATIONS, - ); - if (!jindexes.contains(coin.index) && - tx.isLelantus == true && - !isConfirmed) { - isUnconfirmed = true; - } else if (!isConfirmed) { - continue; - } - } else { - final txn = await cachedElectrumXClient.getTransaction( - txHash: coin.txid, - verbose: true, - coin: this.coin, - ); - final confirmations = txn["confirmations"]; - isUnconfirmed = confirmations is int && confirmations < 1; - } - if (!coin.isUsed && - coin.anonymitySetId != ANONYMITY_SET_EMPTY_ID && - !isUnconfirmed) { - coins.add(coin); - } - } - return coins; + // List coins = []; + // + // final currentChainHeight = await chainHeight; + // + // for (int i = 0; i < lelantusCoinsList.length; i++) { + // // Logging.instance.log("lelantusCoinsList[$i]: ${lelantusCoinsList[i]}"); + // final coin = lelantusCoinsList[i]; + // + // final tx = await db.getTransaction(walletId, coin.txid); + // + // // TODO check if sane default + // bool isUnconfirmed = false; + // + // if (tx != null) { + // bool isConfirmed = tx.isConfirmed( + // currentChainHeight, + // MINIMUM_CONFIRMATIONS, + // ); + // if (!jindexes.contains(coin.index) && + // tx.isLelantus == true && + // !isConfirmed) { + // isUnconfirmed = true; + // } else if (!isConfirmed) { + // continue; + // } + // } else { + // final txn = await cachedElectrumXClient.getTransaction( + // txHash: coin.txid, + // verbose: true, + // coin: this.coin, + // ); + // final confirmations = txn["confirmations"]; + // isUnconfirmed = confirmations is int && confirmations < 1; + // } + // if (!coin.isUsed && + // coin.anonymitySetId != ANONYMITY_SET_EMPTY_ID && + // !isUnconfirmed) { + // coins.add(coin); + // } + // } + // return coins; } // index 0 and 1 for the funds available to spend. @@ -2415,12 +2415,12 @@ class FiroWallet extends CoinServiceAPI .where() .walletIdEqualTo(walletId) .filter() + .isUsedEqualTo(false) .not() .valueEqualTo(0.toString()) .findAll(); final currentChainHeight = await chainHeight; - final jindexes = firoGetJIndex(); int intLelantusBalance = 0; int unconfirmedLelantusBalance = 0; @@ -2440,33 +2440,17 @@ class FiroWallet extends CoinServiceAPI level: LogLevel.Fatal, ); } else { - bool isLelantus = txn.isLelantus == true; - if (!jindexes!.contains(lelantusCoin.index) && isLelantus) { - if (!lelantusCoin.isUsed && - txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - // mint tx, add value to balance - intLelantusBalance += int.parse(lelantusCoin.value); - } /* else { - // This coin is not confirmed and may be replaced - }*/ - } else if (jindexes.contains(lelantusCoin.index) && - isLelantus && - !lelantusCoin.isUsed && - !txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - unconfirmedLelantusBalance += int.parse(lelantusCoin.value); - } else if (jindexes.contains(lelantusCoin.index) && - !lelantusCoin.isUsed) { + if (txn.isLelantus != true) { + Logging.instance.log( + "Bad database state found in $walletName $walletId for _refreshBalance lelantus", + level: LogLevel.Fatal, + ); + } + + if (txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { + // mint tx, add value to balance intLelantusBalance += int.parse(lelantusCoin.value); - } else if (!lelantusCoin.isUsed && - (txn.isLelantus == true - ? true - : txn.isConfirmed( - currentChainHeight, MINIMUM_CONFIRMATIONS) != - false)) { - intLelantusBalance += int.parse(lelantusCoin.value); - } else if (!isLelantus && - txn.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) == - false) { + } else { unconfirmedLelantusBalance += int.parse(lelantusCoin.value); } } @@ -2615,10 +2599,16 @@ class FiroWallet extends CoinServiceAPI } Future>> createMintsFromAmount(int total) async { - var tmpTotal = total; - var index = 1; - var mints = >[]; - final nextFreeMintIndex = firoGetMintIndex(); + int tmpTotal = total; + int index = 0; + final mints = >[]; + final lastUsedIndex = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .sortByMintIndexDesc() + .mintIndexProperty() + .findFirst(); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; while (tmpTotal > 0) { final mintValue = min(tmpTotal, MINT_LIMIT); final mint = await _getMintHex( @@ -2783,9 +2773,6 @@ class FiroWallet extends CoinServiceAPI amount += utxosToUse[i].value; } - final index = firoGetMintIndex(); - Logging.instance.log("index of mint $index", level: LogLevel.Info); - for (var mintsElement in mintsMap) { Logging.instance.log("using $mintsElement", level: LogLevel.Info); Uint8List mintu8 = @@ -2837,45 +2824,39 @@ class FiroWallet extends CoinServiceAPI .where() .walletIdEqualTo(walletId) .filter() + .isUsedEqualTo(false) + .and() + .isJMintEqualTo(true) .not() .valueEqualTo(0.toString()) .findAll(); - final jindexes = firoGetJIndex(); - // Get all joinsplit transaction ids - final listLelantusTxData = await db + final lelantusJoinSplitTxns = await db .getTransactions(walletId) .filter() .isLelantusEqualTo(true) + .and() + .subTypeEqualTo(isar_models.TransactionSubType.join) .findAll(); - List joinsplits = []; - for (final tx in listLelantusTxData) { - if (tx.subType == isar_models.TransactionSubType.join) { - joinsplits.add(tx.txid); - } - } + Set joinSplitTXIDs = {}; + + // for (final tx in lelantusJoinSplitTxns) { + // joinSplitTXIDs.add(tx.txid); + // } for (final coin in lelantusCoins) { - if (jindexes != null) { - if (jindexes.contains(coin.index) && !joinsplits.contains(coin.txid)) { - joinsplits.add(coin.txid); - } - } + joinSplitTXIDs.add(coin.txid); } - Map> data = - {}; - for (final entry in listLelantusTxData) { - data[entry.txid] = Tuple2(entry.address.value, entry); - } + Map> + updatedData = {}; // Grab the most recent information on all the joinsplits - final updatedJSplit = await getJMintTransactions( cachedElectrumXClient, - joinsplits, + joinSplitTXIDs.toList(), coin, ); @@ -2887,7 +2868,7 @@ class FiroWallet extends CoinServiceAPI try { currentTx = - listLelantusTxData.firstWhere((e) => e.txid == tx.value.txid); + lelantusJoinSplitTxns.firstWhere((e) => e.txid == tx.value.txid); } catch (_) { currentTx = null; } @@ -2895,54 +2876,23 @@ class FiroWallet extends CoinServiceAPI if (currentTx == null) { // this send was accidentally not included in the list tx.value.isLelantus = true; - data[tx.value.txid] = + updatedData[tx.value.txid] = Tuple2(tx.value.address.value ?? tx.key, tx.value); - - continue; - } - if (currentTx.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS) != + } else if (currentTx.isConfirmed( + currentChainHeight, MINIMUM_CONFIRMATIONS) != tx.value.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { tx.value.isLelantus = true; - data[tx.value.txid] = + updatedData[tx.value.txid] = Tuple2(tx.value.address.value ?? tx.key, tx.value); } } - // Logging.instance.log(txData.txChunks); - final listTxData = await _txnData; - for (final value in listTxData) { - // ignore change addresses - // bool hasAtLeastOneReceive = false; - // int howManyReceiveInputs = 0; - // for (var element in value.inputs) { - // if (listLelantusTxData.containsKey(element.txid) && - // listLelantusTxData[element.txid]!.txType == "Received" - // // && - // // listLelantusTxData[element.txid].subType != "mint" - // ) { - // // hasAtLeastOneReceive = true; - // // howManyReceiveInputs++; - // } - // } - - if (value.type == isar_models.TransactionType.incoming && - value.subType != isar_models.TransactionSubType.mint) { - // Every receive other than a mint should be shown. Mints will be collected and shown from the send side - value.isLelantus = true; - data[value.txid] = Tuple2(value.address.value, value); - } else if (value.type == isar_models.TransactionType.outgoing) { - // all sends should be shown, mints will be displayed correctly in the ui - value.isLelantus = true; - data[value.txid] = Tuple2(value.address.value, value); - } - } - // TODO: optimize this whole lelantus process final List> txnsData = []; - for (final value in data.values) { + for (final value in updatedData.values) { // allow possible null address on mints as we don't display address // this should normally never be null anyways but old (dbVersion up to 4) // migrated transactions may not have had an address (full rescan should @@ -2967,15 +2917,6 @@ class FiroWallet extends CoinServiceAPI } await db.addNewTransactionData(txnsData, walletId); - - // // update the _lelantusTransactionData - // final models.TransactionData newTxData = - // models.TransactionData.fromMap(listLelantusTxData); - // // Logging.instance.log(newTxData.txChunks); - // _lelantusTransactionData = Future(() => newTxData); - // await DB.instance.put( - // boxName: walletId, key: 'latest_lelantus_tx_model', value: newTxData); - // return newTxData; } Future _getMintHex(int amount, int index) async { @@ -3025,7 +2966,13 @@ class FiroWallet extends CoinServiceAPI level: LogLevel.Info); if (txid == transactionInfo['txid']) { - final index = firoGetMintIndex(); + final lastUsedIndex = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .sortByMintIndexDesc() + .mintIndexProperty() + .findFirst(); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; if (transactionInfo['spendCoinIndexes'] != null) { // This is a joinsplit @@ -3041,7 +2988,7 @@ class FiroWallet extends CoinServiceAPI .where() .walletIdEqualTo(walletId) .filter() - .indexEqualTo(index) + .mintIndexEqualTo(index) .findAll(); if (possibleCoins.isNotEmpty) { @@ -3069,22 +3016,26 @@ class FiroWallet extends CoinServiceAPI ); if (int.parse(jmint.value) > 0) { updatedCoins.add(jmint); - final jindexes = firoGetJIndex()!; - jindexes.add(index); - await firoUpdateJIndex(jindexes); - await firoUpdateMintIndex(index + 1); } - await db.isar.writeTxn(() async { - for (final c in updatedCoins) { - await db.isar.lelantusCoins.deleteByPublicCoinWalletIdTxid( - c.publicCoin, - c.walletId, - c.txid, - ); - } - await db.isar.lelantusCoins.putAll(updatedCoins); - }); + try { + await db.isar.writeTxn(() async { + for (final c in updatedCoins) { + await db.isar.lelantusCoins.deleteByPublicCoinWalletIdTxid( + c.publicCoin, + c.walletId, + c.txid, + ); + } + await db.isar.lelantusCoins.putAll(updatedCoins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; + } final amount = Amount.fromDecimal( Decimal.parse(transactionInfo["amount"].toString()), @@ -3160,13 +3111,20 @@ class FiroWallet extends CoinServiceAPI ); if (int.parse(mint.value) > 0) { updatedCoins.add(mint); - await firoUpdateMintIndex(index + 1); } } // Logging.instance.log(coins); - await db.isar.writeTxn(() async { - await db.isar.lelantusCoins.putAll(updatedCoins); - }); + try { + await db.isar.writeTxn(() async { + await db.isar.lelantusCoins.putAll(updatedCoins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; + } } return true; } else { @@ -3933,7 +3891,6 @@ class FiroWallet extends CoinServiceAPI coin: coin, ); - // todo check here if we should mark as blocked final utxo = isar_models.UTXO( walletId: walletId, txid: txn["txid"] as String, @@ -4586,14 +4543,19 @@ class FiroWallet extends CoinServiceAPI await chainHeight, ); - await Future.wait([ - firoUpdateMintIndex(message['mintIndex'] as int), - db.isar.writeTxn(() async { - await db.isar.lelantusCoins.putAll( - message['_lelantus_coins'] as List); - }), - firoUpdateJIndex(message['jindex'] as List), - ]); + final coins = message['_lelantus_coins'] as List; + + try { + await db.isar.writeTxn(() async { + await db.isar.lelantusCoins.putAll(coins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; + } final transactionMap = message["newTxMap"] as Map; @@ -4677,7 +4639,13 @@ class FiroWallet extends CoinServiceAPI int spendAmount, String address, bool subtractFeeFromAmount) async { final _mnemonic = await mnemonicString; final _mnemonicPassphrase = await mnemonicPassphrase; - final index = firoGetMintIndex(); + final lastUsedIndex = await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .sortByMintIndexDesc() + .mintIndexProperty() + .findFirst(); + final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; final lelantusEntry = await _getLelantusEntry(); final anonymitySets = await fetchAnonymitySets(); final locktime = await getBlockHead(electrumXClient); @@ -4691,7 +4659,7 @@ class FiroWallet extends CoinServiceAPI "subtractFeeFromAmount": subtractFeeFromAmount, "mnemonic": _mnemonic, "mnemonicPassphrase": _mnemonicPassphrase, - "index": index, + "index": nextFreeMintIndex, // "price": price, "lelantusEntries": lelantusEntry, "locktime": locktime, diff --git a/lib/services/mixins/firo_hive.dart b/lib/services/mixins/firo_hive.dart deleted file mode 100644 index 631aebe6d..000000000 --- a/lib/services/mixins/firo_hive.dart +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of Stack Wallet. - * - * Copyright (c) 2023 Cypher Stack - * All Rights Reserved. - * The code is distributed under GPLv3 license, see LICENSE file for details. - * Generated by Cypher Stack on 2023-05-26 - * - */ - -import 'package:stackwallet/db/hive/db.dart'; - -mixin FiroHive { - late final String _walletId; - - void initFiroHive(String walletId) { - _walletId = walletId; - } - - // jindex - List? firoGetJIndex() { - return DB.instance.get(boxName: _walletId, key: "jindex") as List?; - } - - Future firoUpdateJIndex(List jIndex) async { - await DB.instance.put( - boxName: _walletId, - key: "jindex", - value: jIndex, - ); - } - - // // _lelantus_coins - // List? firoGetLelantusCoins() { - // return DB.instance.get(boxName: _walletId, key: "_lelantus_coins") - // as List?; - // } - // - // Future firoUpdateLelantusCoins(List lelantusCoins) async { - // await DB.instance.put( - // boxName: _walletId, - // key: "_lelantus_coins", - // value: lelantusCoins, - // ); - // } - - // mintIndex - int firoGetMintIndex() { - return DB.instance.get(boxName: _walletId, key: "mintIndex") - as int? ?? - 0; - } - - Future firoUpdateMintIndex(int mintIndex) async { - await DB.instance.put( - boxName: _walletId, - key: "mintIndex", - value: mintIndex, - ); - } -} From 462b845bd40b1f448d86d75e366363e53d459512 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 24 Jul 2023 16:54:38 -0600 Subject: [PATCH 07/37] clean up unspent coins --- lib/services/coins/firo/firo_wallet.dart | 51 +++--------------------- 1 file changed, 5 insertions(+), 46 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index a5a7b3b47..fe1956ce9 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2349,60 +2349,19 @@ class FiroWallet extends CoinServiceAPI } Future> _getUnspentCoins() async { - // final jindexes = firoGetJIndex() ?? []; - final lelantusCoinsList = await db.isar.lelantusCoins .where() .walletIdEqualTo(walletId) .filter() - .isUsedEqualTo(false) // TODO add this? + .isUsedEqualTo(false) .not() - .valueEqualTo("0") + .group((q) => q + .valueEqualTo("0") + .or() + .anonymitySetIdEqualTo(ANONYMITY_SET_EMPTY_ID)) .findAll(); return lelantusCoinsList; - - // List coins = []; - // - // final currentChainHeight = await chainHeight; - // - // for (int i = 0; i < lelantusCoinsList.length; i++) { - // // Logging.instance.log("lelantusCoinsList[$i]: ${lelantusCoinsList[i]}"); - // final coin = lelantusCoinsList[i]; - // - // final tx = await db.getTransaction(walletId, coin.txid); - // - // // TODO check if sane default - // bool isUnconfirmed = false; - // - // if (tx != null) { - // bool isConfirmed = tx.isConfirmed( - // currentChainHeight, - // MINIMUM_CONFIRMATIONS, - // ); - // if (!jindexes.contains(coin.index) && - // tx.isLelantus == true && - // !isConfirmed) { - // isUnconfirmed = true; - // } else if (!isConfirmed) { - // continue; - // } - // } else { - // final txn = await cachedElectrumXClient.getTransaction( - // txHash: coin.txid, - // verbose: true, - // coin: this.coin, - // ); - // final confirmations = txn["confirmations"]; - // isUnconfirmed = confirmations is int && confirmations < 1; - // } - // if (!coin.isUsed && - // coin.anonymitySetId != ANONYMITY_SET_EMPTY_ID && - // !isUnconfirmed) { - // coins.add(coin); - // } - // } - // return coins; } // index 0 and 1 for the funds available to spend. From 18f3a2056c382a9a31446d8be81135d367e0c4f1 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 08:55:50 -0600 Subject: [PATCH 08/37] check anon set when creating list of mints --- lib/services/coins/firo/firo_wallet.dart | 89 ++++++++++++++++++++---- 1 file changed, 75 insertions(+), 14 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index fe1956ce9..331b5b519 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2559,8 +2559,7 @@ class FiroWallet extends CoinServiceAPI Future>> createMintsFromAmount(int total) async { int tmpTotal = total; - int index = 0; - final mints = >[]; + int counter = 0; final lastUsedIndex = await db.isar.lelantusCoins .where() .walletIdEqualTo(walletId) @@ -2568,20 +2567,82 @@ class FiroWallet extends CoinServiceAPI .mintIndexProperty() .findFirst(); final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; + + final root = await Bip32Utils.getBip32Root( + (await mnemonic).join(" "), + (await mnemonicPassphrase)!, + _network, + ); + + final mints = >[]; while (tmpTotal > 0) { - final mintValue = min(tmpTotal, MINT_LIMIT); - final mint = await _getMintHex( - mintValue, - nextFreeMintIndex + index, + final index = nextFreeMintIndex + counter; + + final bip32.BIP32 mintKeyPair = await Bip32Utils.getBip32NodeFromRoot( + root, + constructDerivePath( + networkWIF: _network.wif, + chain: MINT_INDEX, + index: index, + ), ); - mints.add({ - "value": mintValue, - "script": mint, - "index": nextFreeMintIndex + index, - "publicCoin": "", - }); - tmpTotal = tmpTotal - MINT_LIMIT; - index++; + + final String mintTag = CreateTag( + Format.uint8listToString(mintKeyPair.privateKey!), + index, + Format.uint8listToString(mintKeyPair.identifier), + isTestnet: coin == Coin.firoTestNet, + ); + final List> anonymitySets; + try { + anonymitySets = await fetchAnonymitySets(); + } catch (e, s) { + Logging.instance.log( + "Firo needs better internet to create mints: $e\n$s", + level: LogLevel.Fatal, + ); + rethrow; + } + + bool isUsedMintTag = false; + + // stupid dynamic maps + for (final set in anonymitySets) { + final setCoins = set["coins"] as List; + for (final coin in setCoins) { + if (coin[1] == mintTag) { + isUsedMintTag = true; + break; + } + } + if (isUsedMintTag) { + break; + } + } + + if (isUsedMintTag) { + Logging.instance.log( + "Found used index when minting", + level: LogLevel.Warning, + ); + } + + if (!isUsedMintTag) { + final mintValue = min(tmpTotal, MINT_LIMIT); + final mint = await _getMintHex( + mintValue, + index, + ); + mints.add({ + "value": mintValue, + "script": mint, + "index": index, + "publicCoin": "", + }); + tmpTotal = tmpTotal - MINT_LIMIT; + } + + counter++; } return mints; } From 99a36f158731fad192f7c30bc59ba64dfd671479 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 09:03:26 -0600 Subject: [PATCH 09/37] fix mint limit constant --- lib/services/coins/firo/firo_wallet.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 331b5b519..b02816497 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -58,8 +58,8 @@ import 'package:uuid/uuid.dart'; const DUST_LIMIT = 1000; const MINIMUM_CONFIRMATIONS = 1; -const MINT_LIMIT = 100100000000; -const int LELANTUS_VALUE_SPEND_LIMIT_PER_TRANSACTION = 5001 * 100000000; +const MINT_LIMIT = 5001 * 100000000; +const MINT_LIMIT_TESTNET = 1001 * 100000000; const JMINT_INDEX = 5; const MINT_INDEX = 2; @@ -2628,7 +2628,8 @@ class FiroWallet extends CoinServiceAPI } if (!isUsedMintTag) { - final mintValue = min(tmpTotal, MINT_LIMIT); + final mintValue = min(tmpTotal, + (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT)); final mint = await _getMintHex( mintValue, index, @@ -2639,7 +2640,8 @@ class FiroWallet extends CoinServiceAPI "index": index, "publicCoin": "", }); - tmpTotal = tmpTotal - MINT_LIMIT; + tmpTotal = tmpTotal - + (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT); } counter++; From 140f1468f9d3e243d80a128d65f305b3521b3d07 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 09:04:09 -0600 Subject: [PATCH 10/37] remove unused code --- lib/services/coins/firo/firo_wallet.dart | 69 ------------------------ 1 file changed, 69 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index b02816497..659a6d94b 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -4769,75 +4769,6 @@ class FiroWallet extends CoinServiceAPI this.isActive = isActive; }; - Future getCoinsToJoinSplit( - int required, - ) async { - List coins = await _getLelantusEntry(); - if (required > LELANTUS_VALUE_SPEND_LIMIT_PER_TRANSACTION) { - return false; - } - - int availableBalance = coins.fold( - 0, (previousValue, element) => previousValue + element.amount); - - if (required > availableBalance) { - return false; - } - - // sort by biggest amount. if it is same amount we will prefer the older block - coins.sort((a, b) => - (a.amount != b.amount ? a.amount > b.amount : a.height < b.height) - ? -1 - : 1); - int spendVal = 0; - - List coinsToSpend = []; - - while (spendVal < required) { - if (coins.isEmpty) { - break; - } - - DartLelantusEntry? chosen; - int need = required - spendVal; - - var itr = coins.first; - if (need >= itr.amount) { - chosen = itr; - coins.remove(itr); - } else { - for (int index = coins.length - 1; index != 0; index--) { - var coinIt = coins[index]; - var nextItr = coins[index - 1]; - - if (coinIt.amount >= need && - (index - 1 == 0 || nextItr.amount != coinIt.amount)) { - chosen = coinIt; - coins.remove(chosen); - break; - } - } - } - - // TODO: investigate the bug here where chosen is null, conditions, given one mint - spendVal += chosen!.amount; - coinsToSpend.insert(coinsToSpend.length, chosen); - } - - // sort by group id ay ascending order. it is mandatory for creating proper joinsplit - coinsToSpend.sort((a, b) => a.anonymitySetId < b.anonymitySetId ? 1 : -1); - - int changeToMint = spendVal - required; - List indices = []; - for (var l in coinsToSpend) { - indices.add(l.index); - } - List coinsToBeSpentOut = []; - coinsToBeSpentOut.addAll(coinsToSpend); - - return {"changeToMint": changeToMint, "coinsToSpend": coinsToBeSpentOut}; - } - Future estimateJoinSplitFee( int spendAmount, ) async { From a0b42226f1f9937e4bbbacc3e8f8e473e41e0636 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 10:39:23 -0600 Subject: [PATCH 11/37] fix test --- test/services/coins/firo/firo_wallet_test.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/services/coins/firo/firo_wallet_test.dart b/test/services/coins/firo/firo_wallet_test.dart index 68db15437..fcd7bd50e 100644 --- a/test/services/coins/firo/firo_wallet_test.dart +++ b/test/services/coins/firo/firo_wallet_test.dart @@ -72,6 +72,7 @@ void main() { setData, List.from(usedSerials), firoNetwork, + "walletId", ); const currentHeight = 100000000000; @@ -133,6 +134,7 @@ void main() { setData, List.from(usedSerials), firoNetwork, + "walletId", ), throwsA(isA())); }); From c7f1392734df24cd177324f44cdee12f6af8c789 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 10:49:49 -0600 Subject: [PATCH 12/37] cache boxes hive error fix --- lib/db/hive/db.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 931a1cee4..25efcdb3e 100644 --- a/lib/db/hive/db.dart +++ b/lib/db/hive/db.dart @@ -177,6 +177,9 @@ class DB { } Future> getTxCacheBox({required Coin coin}) async { + if (_txCacheBoxes[coin]?.isOpen != true) { + _txCacheBoxes.remove(coin); + } return _txCacheBoxes[coin] ??= await Hive.openBox(_boxNameTxCache(coin: coin)); } @@ -186,6 +189,9 @@ class DB { } Future> getAnonymitySetCacheBox({required Coin coin}) async { + if (_setCacheBoxes[coin]?.isOpen != true) { + _setCacheBoxes.remove(coin); + } return _setCacheBoxes[coin] ??= await Hive.openBox(_boxNameSetCache(coin: coin)); } @@ -195,6 +201,9 @@ class DB { } Future> getUsedSerialsCacheBox({required Coin coin}) async { + if (_usedSerialsCacheBoxes[coin]?.isOpen != true) { + _usedSerialsCacheBoxes.remove(coin); + } return _usedSerialsCacheBoxes[coin] ??= await Hive.openBox(_boxNameUsedSerialsCache(coin: coin)); } From a8abd388271f0e6ebfaee51d44490cd374e3e408 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 11:17:30 -0600 Subject: [PATCH 13/37] invalid vsize fix --- lib/services/coins/firo/firo_wallet.dart | 26 +++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 659a6d94b..7b42698dc 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -1429,16 +1429,32 @@ class FiroWallet extends CoinServiceAPI feeRatePerKB: selectedTxFeeRate, ); - if (feeForOneOutput < vSizeForOneOutput + 1) { - feeForOneOutput = vSizeForOneOutput + 1; - } - - final int amount = satoshiAmountToSend - feeForOneOutput; + int amount = satoshiAmountToSend - feeForOneOutput; dynamic txn = await buildTransaction( utxoSigningData: utxoSigningData, recipients: recipientsArray, satoshiAmounts: [amount], ); + + int count = 0; + int fee = feeForOneOutput; + int vsize = txn["vSize"] as int; + + while (fee < vsize && count < 10) { + // 10 being some reasonable max + count++; + fee += count; + amount = satoshiAmountToSend - fee; + + txn = await buildTransaction( + utxoSigningData: utxoSigningData, + recipients: recipientsArray, + satoshiAmounts: [amount], + ); + + vsize = txn["vSize"] as int; + } + Map transactionObject = { "hex": txn["hex"], "recipient": recipientsArray[0], From 5f9b1f77f58114eb0f39b2f10911da15b169b791 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 11:17:54 -0600 Subject: [PATCH 14/37] clearer exception --- lib/services/coins/firo/firo_wallet.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 7b42698dc..10cf4473c 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -4592,7 +4592,8 @@ class FiroWallet extends CoinServiceAPI "$e\n$s", level: LogLevel.Fatal, ); - rethrow; + // don't just rethrow since isar likes to strip stack traces for some reason + throw Exception("e=$e & s=$s"); } final transactionMap = From e1140e6fa6dc863ca09b43ea2b3596d899be026d Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 11:18:21 -0600 Subject: [PATCH 15/37] clear lelantus coins on full rescan --- lib/db/isar/main_db.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index fa565c0cd..4b60e2cda 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -371,6 +371,7 @@ class MainDB { final transactionCount = await getTransactions(walletId).count(); final addressCount = await getAddresses(walletId).count(); final utxoCount = await getUTXOs(walletId).count(); + final lelantusCoinCount = await isar.lelantusCoins.where().walletIdEqualTo(walletId).count(); await isar.writeTxn(() async { const paginateLimit = 50; @@ -404,6 +405,16 @@ class MainDB { .findAll(); await isar.utxos.deleteAll(utxoIds); } + + // lelantusCoins + for (int i = 0; i < lelantusCoinCount; i += paginateLimit) { + final lelantusCoinIds = await isar.lelantusCoins.where().walletIdEqualTo(walletId) + .offset(i) + .limit(paginateLimit) + .idProperty() + .findAll(); + await isar.lelantusCoins.deleteAll(lelantusCoinIds); + } }); } From 5439ce895433bbf9c9e5dee3cb6e1596954c38b9 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 12:06:06 -0600 Subject: [PATCH 16/37] show error before data in case dart decides string is too long and cuts it off --- lib/electrumx_rpc/electrumx.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/electrumx_rpc/electrumx.dart b/lib/electrumx_rpc/electrumx.dart index e0a3118eb..804a1364c 100644 --- a/lib/electrumx_rpc/electrumx.dart +++ b/lib/electrumx_rpc/electrumx.dart @@ -159,8 +159,8 @@ class ElectrumX { throw Exception( "JSONRPC response\n" " command: $command\n" - " args: $args\n" - " error: ${response.data}", + " error: ${response.data}" + " args: $args\n", ); } From 3c1a3f23e379fb187e40e1738ce34154f17a8b43 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 12:07:09 -0600 Subject: [PATCH 17/37] clean up --- lib/services/coins/firo/firo_wallet.dart | 47 ------------------------ 1 file changed, 47 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 10cf4473c..a31e0f62c 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -1224,18 +1224,6 @@ class FiroWallet extends CoinServiceAPI try { final txid = txData["txid"] as String; - // temporarily update apdate available balance until a full refresh is done - - // TODO: something here causes an exception to be thrown giving user false info that the tx failed - // Decimal sendTotal = - // Format.satoshisToAmount(txData["value"] as int, coin: coin); - // sendTotal += Decimal.parse(txData["fees"].toString()); - - // TODO: is this needed? - // final bals = await balances; - // bals[0] -= sendTotal; - // _balances = Future(() => bals); - return txid; } catch (e, s) { //todo: come back to this @@ -2409,7 +2397,6 @@ class FiroWallet extends CoinServiceAPI .findFirstSync(); if (txn == null) { - // TODO: ?????????????????????????????????????? Logging.instance.log( "Transaction not found in DB for lelantus coin: $lelantusCoin", level: LogLevel.Fatal, @@ -2686,8 +2673,6 @@ class FiroWallet extends CoinServiceAPI int satoshisPerRecipient, List> mintsMap, ) async { - //todo: check if print needed - // debugPrint(utxosToUse.toString()); List addressStringsToGet = []; // Populating the addresses to derive @@ -3966,7 +3951,6 @@ class FiroWallet extends CoinServiceAPI Logging.instance .log('Outputs fetched: $outputArray', level: LogLevel.Info); - // TODO move this out of here and into IDB await db.isar.writeTxn(() async { await db.isar.utxos.where().walletIdEqualTo(walletId).deleteAll(); await db.isar.utxos.putAll(outputArray); @@ -4816,36 +4800,6 @@ class FiroWallet extends CoinServiceAPI Logging.instance.log('Closing estimateJoinSplit!', level: LogLevel.Info); return (message as LelantusFeeData).fee; } - // int fee; - // int size; - // - // for (fee = 0;;) { - // int currentRequired = spendAmount; - // - // TODO: investigate the bug here - // var map = await getCoinsToJoinSplit(currentRequired); - // if (map is bool && !map) { - // return 0; - // } - // - // List coinsToBeSpent = - // map['coinsToSpend'] as List; - // - // // 1054 is constant part, mainly Schnorr and Range proofs, 2560 is for each sigma/aux data - // // 179 other parts of tx, assuming 1 utxo and 1 jmint - // size = 1054 + 2560 * coinsToBeSpent.length + 180; - // // uint64_t feeNeeded = GetMinimumFee(size, DEFAULT_TX_CONFIRM_TARGET); - // int feeNeeded = - // size; //TODO(Levon) temporary, use real estimation methods here - // - // if (fee >= feeNeeded) { - // break; - // } - // - // fee = feeNeeded; - // } - // - // return fee; @override Future estimateFeeFor(Amount amount, int feeRate) async { @@ -4908,7 +4862,6 @@ class FiroWallet extends CoinServiceAPI } } - // TODO: correct formula for firo? Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( rawValue: BigInt.from(((181 * inputCount) + (34 * outputCount) + 10) * From 7d1d7cb8049c08195b146e77e0c77131e2f1c08b Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 12:07:44 -0600 Subject: [PATCH 18/37] auto format on save --- lib/db/isar/main_db.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index 4b60e2cda..93c9052da 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -371,7 +371,8 @@ class MainDB { final transactionCount = await getTransactions(walletId).count(); final addressCount = await getAddresses(walletId).count(); final utxoCount = await getUTXOs(walletId).count(); - final lelantusCoinCount = await isar.lelantusCoins.where().walletIdEqualTo(walletId).count(); + final lelantusCoinCount = + await isar.lelantusCoins.where().walletIdEqualTo(walletId).count(); await isar.writeTxn(() async { const paginateLimit = 50; @@ -408,7 +409,9 @@ class MainDB { // lelantusCoins for (int i = 0; i < lelantusCoinCount; i += paginateLimit) { - final lelantusCoinIds = await isar.lelantusCoins.where().walletIdEqualTo(walletId) + final lelantusCoinIds = await isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) .offset(i) .limit(paginateLimit) .idProperty() From 590717560e3469ab1b65ea80b355c601285e1023 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 12:12:10 -0600 Subject: [PATCH 19/37] notif error fix? --- lib/widgets/crypto_notifications.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/widgets/crypto_notifications.dart b/lib/widgets/crypto_notifications.dart index f94c74ed9..88a96d422 100644 --- a/lib/widgets/crypto_notifications.dart +++ b/lib/widgets/crypto_notifications.dart @@ -13,6 +13,7 @@ import 'dart:async'; import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/providers/global/prefs_provider.dart'; import 'package:stackwallet/services/notifications_api.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -84,6 +85,7 @@ class _CryptoNotificationsState extends ConsumerState { @override void initState() { + NotificationApi.prefs = ref.read(prefsChangeNotifierProvider); _streamSubscription = CryptoNotificationsEventBus.instance .on() .listen( From 20c706c7e5558ca6a170b739234f79021b8d4e0d Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 12:16:08 -0600 Subject: [PATCH 20/37] add todo check --- lib/services/coins/firo/firo_wallet.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index a31e0f62c..ea0a255d4 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2637,6 +2637,8 @@ class FiroWallet extends CoinServiceAPI mintValue, index, ); + + // TODO publicCoin prob shouldn't be empty? mints.add({ "value": mintValue, "script": mint, From c9470a5078168f1359f9b771bbb3d7d9e4d86f09 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 13:48:19 -0600 Subject: [PATCH 21/37] desktop notifications fix --- lib/widgets/crypto_notifications.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/widgets/crypto_notifications.dart b/lib/widgets/crypto_notifications.dart index 88a96d422..b570ae4f9 100644 --- a/lib/widgets/crypto_notifications.dart +++ b/lib/widgets/crypto_notifications.dart @@ -13,7 +13,7 @@ import 'dart:async'; import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/services/notifications_api.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -86,6 +86,7 @@ class _CryptoNotificationsState extends ConsumerState { @override void initState() { NotificationApi.prefs = ref.read(prefsChangeNotifierProvider); + NotificationApi.notificationsService = ref.read(notificationsProvider); _streamSubscription = CryptoNotificationsEventBus.instance .on() .listen( From 128fa9db6cb79dc6c5dc8ecfadb2afcc8c6121dc Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 13:48:39 -0600 Subject: [PATCH 22/37] rpc timeout mod --- lib/electrumx_rpc/rpc.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/electrumx_rpc/rpc.dart b/lib/electrumx_rpc/rpc.dart index 0c3834ae8..e865356d6 100644 --- a/lib/electrumx_rpc/rpc.dart +++ b/lib/electrumx_rpc/rpc.dart @@ -79,7 +79,7 @@ class JsonRPC { // TODO different timeout length? req.initiateTimeout( - const Duration(seconds: 10), + Duration(seconds: connectionTimeout.inSeconds ~/ 2), onTimedOut: () { _requestQueue.remove(req); }, From 04658f8eef091b31ba78da10d1a9ab194544fb83 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 13:55:19 -0600 Subject: [PATCH 23/37] null error bandaid --- lib/services/notifications_api.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/services/notifications_api.dart b/lib/services/notifications_api.dart index 41e95387b..3ef97462a 100644 --- a/lib/services/notifications_api.dart +++ b/lib/services/notifications_api.dart @@ -77,8 +77,10 @@ class NotificationApi { final id = prefs.currentNotificationId; String confirms = ""; - if (txid != null) { - confirms = " (${confirmations!}/${requiredConfirmations!})"; + if (txid != null && + confirmations != null && + requiredConfirmations != null) { + confirms = " ($confirmations/$requiredConfirmations)"; } final NotificationModel model = NotificationModel( From 2cca59532e43fc84a0d637ad3bd35032418ed0fc Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 14:14:07 -0600 Subject: [PATCH 24/37] delete by index instead of public Coin since who tf knows what it could be in our code --- lib/services/coins/firo/firo_wallet.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index ea0a255d4..44a29ee96 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -3046,10 +3046,9 @@ class FiroWallet extends CoinServiceAPI try { await db.isar.writeTxn(() async { for (final c in updatedCoins) { - await db.isar.lelantusCoins.deleteByPublicCoinWalletIdTxid( - c.publicCoin, + await db.isar.lelantusCoins.deleteByMintIndexWalletId( + c.mintIndex, c.walletId, - c.txid, ); } await db.isar.lelantusCoins.putAll(updatedCoins); From ce1dce113062ead4fbd74ef70aa5f08e19ad91ae Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 14:56:31 -0600 Subject: [PATCH 25/37] comment and fix error messages --- lib/services/coins/firo/firo_wallet.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 44a29ee96..861263625 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -1138,7 +1138,8 @@ class FiroWallet extends CoinServiceAPI } } } catch (e, s) { - Logging.instance.log("Exception rethrown from prepareSend(): $e\n$s", + Logging.instance.log( + "Exception rethrown from prepareSendPublic(): $e\n$s", level: LogLevel.Error); rethrow; } @@ -1146,7 +1147,8 @@ class FiroWallet extends CoinServiceAPI throw ArgumentError("Invalid fee rate argument provided!"); } } catch (e, s) { - Logging.instance.log("Exception rethrown from prepareSend(): $e\n$s", + Logging.instance.log( + "Exception rethrown from prepareSendPublic(): $e\n$s", level: LogLevel.Error); rethrow; } @@ -2346,6 +2348,8 @@ class FiroWallet extends CoinServiceAPI final lelantusEntries = await Future.wait(waitLelantusEntries); if (lelantusEntries.isNotEmpty) { + // should be redundant as _getUnspentCoins() should + // already remove all where value=0 lelantusEntries.removeWhere((element) => element.amount == 0); } From 759e5624e43d139381027264deebb7b4c444cb0b Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 15:11:34 -0600 Subject: [PATCH 26/37] stop storing public coin --- .../models/firo_specific/lelantus_coin.dart | 13 - .../models/firo_specific/lelantus_coin.g.dart | 464 +----------------- lib/services/coins/firo/firo_wallet.dart | 14 +- lib/utilities/db_version_migration.dart | 1 - 4 files changed, 16 insertions(+), 476 deletions(-) diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.dart b/lib/models/isar/models/firo_specific/lelantus_coin.dart index 9ad5bc5a3..ca4c11919 100644 --- a/lib/models/isar/models/firo_specific/lelantus_coin.dart +++ b/lib/models/isar/models/firo_specific/lelantus_coin.dart @@ -9,16 +9,6 @@ class LelantusCoin { @Index() final String walletId; - @Index( - unique: true, - composite: [ - CompositeIndex("walletId"), - CompositeIndex("txid"), - ], - replace: false, - ) - final String publicCoin; - final String txid; final String value; // can't use BigInt in isar :shrug: @@ -42,7 +32,6 @@ class LelantusCoin { LelantusCoin({ required this.walletId, - required this.publicCoin, required this.txid, required this.value, required this.mintIndex, @@ -65,7 +54,6 @@ class LelantusCoin { }) { return LelantusCoin( walletId: walletId ?? this.walletId, - publicCoin: publicCoin ?? this.publicCoin, txid: txid ?? this.txid, value: value ?? this.value, mintIndex: mintIndex ?? this.mintIndex, @@ -81,7 +69,6 @@ class LelantusCoin { return 'LelantusCoin{' 'id: $id, ' 'walletId: $walletId, ' - 'publicCoin: $publicCoin, ' 'txid: $txid, ' 'value: $value, ' 'mintIndex: $mintIndex, ' diff --git a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart index d2e27638d..4b9214889 100644 --- a/lib/models/isar/models/firo_specific/lelantus_coin.g.dart +++ b/lib/models/isar/models/firo_specific/lelantus_coin.g.dart @@ -42,23 +42,18 @@ const LelantusCoinSchema = CollectionSchema( name: r'otherData', type: IsarType.string, ), - r'publicCoin': PropertySchema( - id: 5, - name: r'publicCoin', - type: IsarType.string, - ), r'txid': PropertySchema( - id: 6, + id: 5, name: r'txid', type: IsarType.string, ), r'value': PropertySchema( - id: 7, + id: 6, name: r'value', type: IsarType.string, ), r'walletId': PropertySchema( - id: 8, + id: 7, name: r'walletId', type: IsarType.string, ) @@ -82,29 +77,6 @@ const LelantusCoinSchema = CollectionSchema( ) ], ), - r'publicCoin_walletId_txid': IndexSchema( - id: 5610740154835640070, - name: r'publicCoin_walletId_txid', - unique: true, - replace: false, - properties: [ - IndexPropertySchema( - name: r'publicCoin', - type: IndexType.hash, - caseSensitive: true, - ), - IndexPropertySchema( - name: r'walletId', - type: IndexType.hash, - caseSensitive: true, - ), - IndexPropertySchema( - name: r'txid', - type: IndexType.hash, - caseSensitive: true, - ) - ], - ), r'mintIndex_walletId': IndexSchema( id: -9147309777276196770, name: r'mintIndex_walletId', @@ -144,7 +116,6 @@ int _lelantusCoinEstimateSize( bytesCount += 3 + value.length * 3; } } - bytesCount += 3 + object.publicCoin.length * 3; bytesCount += 3 + object.txid.length * 3; bytesCount += 3 + object.value.length * 3; bytesCount += 3 + object.walletId.length * 3; @@ -162,10 +133,9 @@ void _lelantusCoinSerialize( writer.writeBool(offsets[2], object.isUsed); writer.writeLong(offsets[3], object.mintIndex); writer.writeString(offsets[4], object.otherData); - writer.writeString(offsets[5], object.publicCoin); - writer.writeString(offsets[6], object.txid); - writer.writeString(offsets[7], object.value); - writer.writeString(offsets[8], object.walletId); + writer.writeString(offsets[5], object.txid); + writer.writeString(offsets[6], object.value); + writer.writeString(offsets[7], object.walletId); } LelantusCoin _lelantusCoinDeserialize( @@ -180,10 +150,9 @@ LelantusCoin _lelantusCoinDeserialize( isUsed: reader.readBool(offsets[2]), mintIndex: reader.readLong(offsets[3]), otherData: reader.readStringOrNull(offsets[4]), - publicCoin: reader.readString(offsets[5]), - txid: reader.readString(offsets[6]), - value: reader.readString(offsets[7]), - walletId: reader.readString(offsets[8]), + txid: reader.readString(offsets[5]), + value: reader.readString(offsets[6]), + walletId: reader.readString(offsets[7]), ); object.id = id; return object; @@ -212,8 +181,6 @@ P _lelantusCoinDeserializeProp

( return (reader.readString(offset)) as P; case 7: return (reader.readString(offset)) as P; - case 8: - return (reader.readString(offset)) as P; default: throw IsarError('Unknown property with id $propertyId'); } @@ -233,106 +200,6 @@ void _lelantusCoinAttach( } extension LelantusCoinByIndex on IsarCollection { - Future getByPublicCoinWalletIdTxid( - String publicCoin, String walletId, String txid) { - return getByIndex( - r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); - } - - LelantusCoin? getByPublicCoinWalletIdTxidSync( - String publicCoin, String walletId, String txid) { - return getByIndexSync( - r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); - } - - Future deleteByPublicCoinWalletIdTxid( - String publicCoin, String walletId, String txid) { - return deleteByIndex( - r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); - } - - bool deleteByPublicCoinWalletIdTxidSync( - String publicCoin, String walletId, String txid) { - return deleteByIndexSync( - r'publicCoin_walletId_txid', [publicCoin, walletId, txid]); - } - - Future> getAllByPublicCoinWalletIdTxid( - List publicCoinValues, - List walletIdValues, - List txidValues) { - final len = publicCoinValues.length; - assert(walletIdValues.length == len && txidValues.length == len, - 'All index values must have the same length'); - final values = >[]; - for (var i = 0; i < len; i++) { - values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); - } - - return getAllByIndex(r'publicCoin_walletId_txid', values); - } - - List getAllByPublicCoinWalletIdTxidSync( - List publicCoinValues, - List walletIdValues, - List txidValues) { - final len = publicCoinValues.length; - assert(walletIdValues.length == len && txidValues.length == len, - 'All index values must have the same length'); - final values = >[]; - for (var i = 0; i < len; i++) { - values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); - } - - return getAllByIndexSync(r'publicCoin_walletId_txid', values); - } - - Future deleteAllByPublicCoinWalletIdTxid(List publicCoinValues, - List walletIdValues, List txidValues) { - final len = publicCoinValues.length; - assert(walletIdValues.length == len && txidValues.length == len, - 'All index values must have the same length'); - final values = >[]; - for (var i = 0; i < len; i++) { - values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); - } - - return deleteAllByIndex(r'publicCoin_walletId_txid', values); - } - - int deleteAllByPublicCoinWalletIdTxidSync(List publicCoinValues, - List walletIdValues, List txidValues) { - final len = publicCoinValues.length; - assert(walletIdValues.length == len && txidValues.length == len, - 'All index values must have the same length'); - final values = >[]; - for (var i = 0; i < len; i++) { - values.add([publicCoinValues[i], walletIdValues[i], txidValues[i]]); - } - - return deleteAllByIndexSync(r'publicCoin_walletId_txid', values); - } - - Future putByPublicCoinWalletIdTxid(LelantusCoin object) { - return putByIndex(r'publicCoin_walletId_txid', object); - } - - Id putByPublicCoinWalletIdTxidSync(LelantusCoin object, - {bool saveLinks = true}) { - return putByIndexSync(r'publicCoin_walletId_txid', object, - saveLinks: saveLinks); - } - - Future> putAllByPublicCoinWalletIdTxid(List objects) { - return putAllByIndex(r'publicCoin_walletId_txid', objects); - } - - List putAllByPublicCoinWalletIdTxidSync(List objects, - {bool saveLinks = true}) { - return putAllByIndexSync(r'publicCoin_walletId_txid', objects, - saveLinks: saveLinks); - } - Future getByMintIndexWalletId(int mintIndex, String walletId) { return getByIndex(r'mintIndex_walletId', [mintIndex, walletId]); } @@ -543,144 +410,6 @@ extension LelantusCoinQueryWhere }); } - QueryBuilder - publicCoinEqualToAnyWalletIdTxid(String publicCoin) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'publicCoin_walletId_txid', - value: [publicCoin], - )); - }); - } - - QueryBuilder - publicCoinNotEqualToAnyWalletIdTxid(String publicCoin) { - return QueryBuilder.apply(this, (query) { - if (query.whereSort == Sort.asc) { - return query - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [], - upper: [publicCoin], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin], - includeLower: false, - upper: [], - )); - } else { - return query - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin], - includeLower: false, - upper: [], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [], - upper: [publicCoin], - includeUpper: false, - )); - } - }); - } - - QueryBuilder - publicCoinWalletIdEqualToAnyTxid(String publicCoin, String walletId) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'publicCoin_walletId_txid', - value: [publicCoin, walletId], - )); - }); - } - - QueryBuilder - publicCoinEqualToWalletIdNotEqualToAnyTxid( - String publicCoin, String walletId) { - return QueryBuilder.apply(this, (query) { - if (query.whereSort == Sort.asc) { - return query - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin], - upper: [publicCoin, walletId], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin, walletId], - includeLower: false, - upper: [publicCoin], - )); - } else { - return query - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin, walletId], - includeLower: false, - upper: [publicCoin], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin], - upper: [publicCoin, walletId], - includeUpper: false, - )); - } - }); - } - - QueryBuilder - publicCoinWalletIdTxidEqualTo( - String publicCoin, String walletId, String txid) { - return QueryBuilder.apply(this, (query) { - return query.addWhereClause(IndexWhereClause.equalTo( - indexName: r'publicCoin_walletId_txid', - value: [publicCoin, walletId, txid], - )); - }); - } - - QueryBuilder - publicCoinWalletIdEqualToTxidNotEqualTo( - String publicCoin, String walletId, String txid) { - return QueryBuilder.apply(this, (query) { - if (query.whereSort == Sort.asc) { - return query - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin, walletId], - upper: [publicCoin, walletId, txid], - includeUpper: false, - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin, walletId, txid], - includeLower: false, - upper: [publicCoin, walletId], - )); - } else { - return query - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin, walletId, txid], - includeLower: false, - upper: [publicCoin, walletId], - )) - .addWhereClause(IndexWhereClause.between( - indexName: r'publicCoin_walletId_txid', - lower: [publicCoin, walletId], - upper: [publicCoin, walletId, txid], - includeUpper: false, - )); - } - }); - } - QueryBuilder mintIndexEqualToAnyWalletId(int mintIndex) { return QueryBuilder.apply(this, (query) { @@ -1161,142 +890,6 @@ extension LelantusCoinQueryFilter }); } - QueryBuilder - publicCoinEqualTo( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'publicCoin', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinGreaterThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - include: include, - property: r'publicCoin', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinLessThan( - String value, { - bool include = false, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.lessThan( - include: include, - property: r'publicCoin', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinBetween( - String lower, - String upper, { - bool includeLower = true, - bool includeUpper = true, - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.between( - property: r'publicCoin', - lower: lower, - includeLower: includeLower, - upper: upper, - includeUpper: includeUpper, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinStartsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.startsWith( - property: r'publicCoin', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinEndsWith( - String value, { - bool caseSensitive = true, - }) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.endsWith( - property: r'publicCoin', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinContains(String value, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.contains( - property: r'publicCoin', - value: value, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinMatches(String pattern, {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.matches( - property: r'publicCoin', - wildcard: pattern, - caseSensitive: caseSensitive, - )); - }); - } - - QueryBuilder - publicCoinIsEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.equalTo( - property: r'publicCoin', - value: '', - )); - }); - } - - QueryBuilder - publicCoinIsNotEmpty() { - return QueryBuilder.apply(this, (query) { - return query.addFilterCondition(FilterCondition.greaterThan( - property: r'publicCoin', - value: '', - )); - }); - } - QueryBuilder txidEqualTo( String value, { bool caseSensitive = true, @@ -1772,19 +1365,6 @@ extension LelantusCoinQuerySortBy }); } - QueryBuilder sortByPublicCoin() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'publicCoin', Sort.asc); - }); - } - - QueryBuilder - sortByPublicCoinDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'publicCoin', Sort.desc); - }); - } - QueryBuilder sortByTxid() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'txid', Sort.asc); @@ -1898,19 +1478,6 @@ extension LelantusCoinQuerySortThenBy }); } - QueryBuilder thenByPublicCoin() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'publicCoin', Sort.asc); - }); - } - - QueryBuilder - thenByPublicCoinDesc() { - return QueryBuilder.apply(this, (query) { - return query.addSortBy(r'publicCoin', Sort.desc); - }); - } - QueryBuilder thenByTxid() { return QueryBuilder.apply(this, (query) { return query.addSortBy(r'txid', Sort.asc); @@ -1982,13 +1549,6 @@ extension LelantusCoinQueryWhereDistinct }); } - QueryBuilder distinctByPublicCoin( - {bool caseSensitive = true}) { - return QueryBuilder.apply(this, (query) { - return query.addDistinctBy(r'publicCoin', caseSensitive: caseSensitive); - }); - } - QueryBuilder distinctByTxid( {bool caseSensitive = true}) { return QueryBuilder.apply(this, (query) { @@ -2049,12 +1609,6 @@ extension LelantusCoinQueryProperty }); } - QueryBuilder publicCoinProperty() { - return QueryBuilder.apply(this, (query) { - return query.addPropertyName(r'publicCoin'); - }); - } - QueryBuilder txidProperty() { return QueryBuilder.apply(this, (query) { return query.addPropertyName(r'txid'); diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 861263625..3b67d96c3 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -276,7 +276,7 @@ Future> isolateRestore( walletId: walletId, mintIndex: currentIndex, value: amount.toString(), - publicCoin: publicCoin, + // publicCoin: publicCoin, txid: txId, anonymitySetId: setId, isUsed: isUsed, @@ -326,7 +326,7 @@ Future> isolateRestore( walletId: walletId, mintIndex: currentIndex, value: amount.toString(), - publicCoin: publicCoin, + // publicCoin: publicCoin, txid: txId, anonymitySetId: setId, isUsed: isUsed, @@ -682,7 +682,7 @@ Future isolateCreateJoinSplitTransaction( "fee": fee, "vSize": extTx.virtualSize(), "jmintValue": changeToMint, - "publicCoin": "jmintData.publicCoin", + // "publicCoin": "jmintData.publicCoin", "spendCoinIndexes": spendCoinIndexes, "height": locktime, "txType": "Sent", @@ -2647,7 +2647,7 @@ class FiroWallet extends CoinServiceAPI "value": mintValue, "script": mint, "index": index, - "publicCoin": "", + // "publicCoin": "", }); tmpTotal = tmpTotal - (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT); @@ -2834,7 +2834,7 @@ class FiroWallet extends CoinServiceAPI rawValue: BigInt.from(fee), fractionDigits: coin.decimals, ).decimal.toDouble(), - "publicCoin": "", + // "publicCoin": "", "height": height, "txType": "Sent", "confirmed_status": false, @@ -3036,7 +3036,7 @@ class FiroWallet extends CoinServiceAPI walletId: walletId, mintIndex: nextFreeMintIndex, value: (transactionInfo['jmintValue'] as int? ?? 0).toString(), - publicCoin: transactionInfo['publicCoin'] as String, + // publicCoin: transactionInfo['publicCoin'] as String, txid: transactionInfo['txid'] as String, anonymitySetId: latestSetId, isUsed: false, @@ -3130,7 +3130,7 @@ class FiroWallet extends CoinServiceAPI walletId: walletId, mintIndex: index, value: (mintMap['value'] as int).toString(), - publicCoin: mintMap['publicCoin'] as String, + // publicCoin: mintMap['publicCoin'] as String, txid: transactionInfo['txid'] as String, anonymitySetId: latestSetId, isUsed: false, diff --git a/lib/utilities/db_version_migration.dart b/lib/utilities/db_version_migration.dart index d2c0adbe8..f54c2b85a 100644 --- a/lib/utilities/db_version_migration.dart +++ b/lib/utilities/db_version_migration.dart @@ -400,7 +400,6 @@ class DbVersionMigrator with WalletDB { final coin = isar_models.LelantusCoin( walletId: walletId, - publicCoin: lcoin.publicCoin, txid: lcoin.txId, value: lcoin.value.toString(), mintIndex: lcoin.index, From 7919353881c132684d23cc74a033be91836210e0 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 15:11:56 -0600 Subject: [PATCH 27/37] update mocks --- test/services/coins/manager_test.mocks.dart | 9 --------- test/widget_tests/transaction_card_test.mocks.dart | 9 --------- 2 files changed, 18 deletions(-) diff --git a/test/services/coins/manager_test.mocks.dart b/test/services/coins/manager_test.mocks.dart index 25fd46d9d..5656feb88 100644 --- a/test/services/coins/manager_test.mocks.dart +++ b/test/services/coins/manager_test.mocks.dart @@ -745,15 +745,6 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet { returnValueForMissingStub: _i11.Future.value(), ) as _i11.Future); @override - _i11.Future getCoinsToJoinSplit(int? required) => - (super.noSuchMethod( - Invocation.method( - #getCoinsToJoinSplit, - [required], - ), - returnValue: _i11.Future.value(), - ) as _i11.Future); - @override _i11.Future estimateJoinSplitFee(int? spendAmount) => (super.noSuchMethod( Invocation.method( diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 29bd7bd29..74fa6e89e 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -1759,15 +1759,6 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet { returnValueForMissingStub: _i19.Future.value(), ) as _i19.Future); @override - _i19.Future getCoinsToJoinSplit(int? required) => - (super.noSuchMethod( - Invocation.method( - #getCoinsToJoinSplit, - [required], - ), - returnValue: _i19.Future.value(), - ) as _i19.Future); - @override _i19.Future estimateJoinSplitFee(int? spendAmount) => (super.noSuchMethod( Invocation.method( From 90b16c576e2cacc9437230ecd9b8d066b814f98a Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 15:36:37 -0600 Subject: [PATCH 28/37] clean up --- lib/services/coins/firo/firo_wallet.dart | 84 +++--------------------- 1 file changed, 10 insertions(+), 74 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 3b67d96c3..7dedf2958 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -276,12 +276,12 @@ Future> isolateRestore( walletId: walletId, mintIndex: currentIndex, value: amount.toString(), - // publicCoin: publicCoin, txid: txId, anonymitySetId: setId, isUsed: isUsed, isJMint: false, - otherData: null, + otherData: + publicCoin, // not really needed but saved just in case ), ); Logging.instance.log( @@ -326,12 +326,12 @@ Future> isolateRestore( walletId: walletId, mintIndex: currentIndex, value: amount.toString(), - // publicCoin: publicCoin, txid: txId, anonymitySetId: setId, isUsed: isUsed, isJMint: true, - otherData: null, + otherData: + publicCoin, // not really needed but saved just in case ), ); jindexes.add(currentIndex); @@ -370,7 +370,6 @@ Future> isolateRestore( // Logging.instance.log("jmints $spendTxIds", addToDebugMessagesDB: false); result['_lelantus_coins'] = lelantusCoins; - result['mintIndex'] = lastFoundIndex + 1; result['jindex'] = jindexes; result['spendTxIds'] = spendTxIds; @@ -682,7 +681,6 @@ Future isolateCreateJoinSplitTransaction( "fee": fee, "vSize": extTx.virtualSize(), "jmintValue": changeToMint, - // "publicCoin": "jmintData.publicCoin", "spendCoinIndexes": spendCoinIndexes, "height": locktime, "txType": "Sent", @@ -1241,53 +1239,6 @@ class FiroWallet extends CoinServiceAPI } } - // /// returns txid on successful send - // /// - // /// can throw - // @override - // Future send({ - // required String toAddress, - // required int amount, - // Map args = const {}, - // }) async { - // try { - // dynamic txHexOrError = - // await _createJoinSplitTransaction(amount, toAddress, false); - // Logging.instance.log("txHexOrError $txHexOrError", level: LogLevel.Error); - // if (txHexOrError is int) { - // // Here, we assume that transaction crafting returned an error - // switch (txHexOrError) { - // case 1: - // throw Exception("Insufficient balance!"); - // default: - // throw Exception("Error Creating Transaction!"); - // } - // } else { - // if (await _submitLelantusToNetwork( - // txHexOrError as Map)) { - // final txid = txHexOrError["txid"] as String; - // - // // temporarily update apdate available balance until a full refresh is done - // Decimal sendTotal = - // Format.satoshisToAmount(txHexOrError["value"] as int, coin: coin); - // sendTotal += Decimal.parse(txHexOrError["fees"].toString()); - // final bals = await balances; - // bals[0] -= sendTotal; - // _balances = Future(() => bals); - // - // return txid; - // } else { - // //TODO provide more info - // throw Exception("Transaction failed."); - // } - // } - // } catch (e, s) { - // Logging.instance.log("Exception rethrown in firo send(): $e\n$s", - // level: LogLevel.Error); - // rethrow; - // } - // } - Future> _getMnemonicList() async { final _mnemonicString = await mnemonicString; if (_mnemonicString == null) { @@ -2642,12 +2593,10 @@ class FiroWallet extends CoinServiceAPI index, ); - // TODO publicCoin prob shouldn't be empty? mints.add({ "value": mintValue, "script": mint, "index": index, - // "publicCoin": "", }); tmpTotal = tmpTotal - (coin == Coin.firoTestNet ? MINT_LIMIT_TESTNET : MINT_LIMIT); @@ -2834,7 +2783,6 @@ class FiroWallet extends CoinServiceAPI rawValue: BigInt.from(fee), fractionDigits: coin.decimals, ).decimal.toDouble(), - // "publicCoin": "", "height": height, "txType": "Sent", "confirmed_status": false, @@ -2848,6 +2796,7 @@ class FiroWallet extends CoinServiceAPI }; } + // TODO: verify this function does what we think it does Future _refreshLelantusData() async { final lelantusCoins = await db.isar.lelantusCoins .where() @@ -2916,8 +2865,6 @@ class FiroWallet extends CoinServiceAPI } } - // TODO: optimize this whole lelantus process - final List> txnsData = []; @@ -3013,21 +2960,13 @@ class FiroWallet extends CoinServiceAPI // Update all of the coins that have been spent. for (final index in spentCoinIndexes) { - final possibleCoins = await db.isar.lelantusCoins + final possibleCoin = await db.isar.lelantusCoins .where() - .walletIdEqualTo(walletId) - .filter() - .mintIndexEqualTo(index) - .findAll(); + .mintIndexWalletIdEqualTo(index, walletId) + .findFirst(); - if (possibleCoins.isNotEmpty) { - if (possibleCoins.length > 1) { - print( - "======================= possibleCoins.length > 1 !!! ================================="); - } else { - final spentCoin = possibleCoins.first; - updatedCoins.add(spentCoin.copyWith(isUsed: true)); - } + if (possibleCoin != null) { + updatedCoins.add(possibleCoin.copyWith(isUsed: true)); } } @@ -3036,7 +2975,6 @@ class FiroWallet extends CoinServiceAPI walletId: walletId, mintIndex: nextFreeMintIndex, value: (transactionInfo['jmintValue'] as int? ?? 0).toString(), - // publicCoin: transactionInfo['publicCoin'] as String, txid: transactionInfo['txid'] as String, anonymitySetId: latestSetId, isUsed: false, @@ -3122,7 +3060,6 @@ class FiroWallet extends CoinServiceAPI final List updatedCoins = []; - // TODO: transactionInfo['mintsMap'] for (final mintMap in transactionInfo['mintsMap'] as List>) { final index = mintMap['index'] as int; @@ -3130,7 +3067,6 @@ class FiroWallet extends CoinServiceAPI walletId: walletId, mintIndex: index, value: (mintMap['value'] as int).toString(), - // publicCoin: mintMap['publicCoin'] as String, txid: transactionInfo['txid'] as String, anonymitySetId: latestSetId, isUsed: false, From c1f73a89be16827fddd36343029c60212061f948 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 15:41:06 -0600 Subject: [PATCH 29/37] store jmint separately from updated coins --- lib/services/coins/firo/firo_wallet.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 7dedf2958..2a17d0849 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2981,9 +2981,6 @@ class FiroWallet extends CoinServiceAPI isJMint: true, otherData: null, ); - if (int.parse(jmint.value) > 0) { - updatedCoins.add(jmint); - } try { await db.isar.writeTxn(() async { @@ -2994,6 +2991,10 @@ class FiroWallet extends CoinServiceAPI ); } await db.isar.lelantusCoins.putAll(updatedCoins); + + if (int.parse(jmint.value) > 0) { + await db.isar.lelantusCoins.put(jmint); + } }); } catch (e, s) { Logging.instance.log( From 5d3e976601e7b2e9f63f456931a26301107d48e3 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 16:08:54 -0600 Subject: [PATCH 30/37] always add lelantus coin to locale db --- lib/services/coins/firo/firo_wallet.dart | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 2a17d0849..37c31dce4 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -370,7 +370,6 @@ Future> isolateRestore( // Logging.instance.log("jmints $spendTxIds", addToDebugMessagesDB: false); result['_lelantus_coins'] = lelantusCoins; - result['jindex'] = jindexes; result['spendTxIds'] = spendTxIds; return result; @@ -2992,9 +2991,7 @@ class FiroWallet extends CoinServiceAPI } await db.isar.lelantusCoins.putAll(updatedCoins); - if (int.parse(jmint.value) > 0) { - await db.isar.lelantusCoins.put(jmint); - } + await db.isar.lelantusCoins.put(jmint); }); } catch (e, s) { Logging.instance.log( @@ -3074,9 +3071,8 @@ class FiroWallet extends CoinServiceAPI isJMint: false, otherData: null, ); - if (int.parse(mint.value) > 0) { - updatedCoins.add(mint); - } + + updatedCoins.add(mint); } // Logging.instance.log(coins); try { From c3054ca753d314ad5eab3752a76804a4e105d1ab Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 20:28:27 -0600 Subject: [PATCH 31/37] fix broken used serials call and optimize cache a bit --- lib/electrumx_rpc/cached_electrumx.dart | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/electrumx_rpc/cached_electrumx.dart b/lib/electrumx_rpc/cached_electrumx.dart index 67f170bb4..91b7d1bc8 100644 --- a/lib/electrumx_rpc/cached_electrumx.dart +++ b/lib/electrumx_rpc/cached_electrumx.dart @@ -164,14 +164,16 @@ class CachedElectrumX { final _list = box.get("serials") as List?; - List cachedSerials = - _list == null ? [] : List.from(_list); + Set cachedSerials = + _list == null ? {} : List.from(_list).toSet(); - final startNumber = cachedSerials.length; + // startNumber is broken currently + final startNumber = 0; // cachedSerials.length; - final serials = - await electrumXClient.getUsedCoinSerials(startNumber: startNumber); - List newSerials = []; + final serials = await electrumXClient.getUsedCoinSerials( + startNumber: startNumber, + ); + Set newSerials = {}; for (final element in (serials["serials"] as List)) { if (!isHexadecimal(element as String)) { @@ -182,12 +184,14 @@ class CachedElectrumX { } cachedSerials.addAll(newSerials); + final resultingList = cachedSerials.toList(); + await box.put( "serials", - cachedSerials, + resultingList, ); - return cachedSerials; + return resultingList; } catch (e, s) { Logging.instance.log( "Failed to process CachedElectrumX.getTransaction(): $e\n$s", From c9da22601ef0d92057d5ebe0ad58dd5d69dab5b2 Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 20:28:51 -0600 Subject: [PATCH 32/37] clean up spam logs a bit --- lib/services/coins/firo/firo_wallet.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 37c31dce4..6b2bf733f 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -349,11 +349,6 @@ Future> isolateRestore( level: LogLevel.Warning, ); } - } else { - Logging.instance.log( - "Coin not found in data with the mint tag: $mintTag", - level: LogLevel.Warning, - ); } } From 57839c2d181a06461b8a8640968af0269802220e Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 20:29:44 -0600 Subject: [PATCH 33/37] WIP fixing _refreshLelantusData --- lib/services/coins/firo/firo_wallet.dart | 116 +++++++++-------------- 1 file changed, 44 insertions(+), 72 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 6b2bf733f..da0919570 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2797,96 +2797,68 @@ class FiroWallet extends CoinServiceAPI .walletIdEqualTo(walletId) .filter() .isUsedEqualTo(false) - .and() - .isJMintEqualTo(true) .not() .valueEqualTo(0.toString()) .findAll(); - // Get all joinsplit transaction ids + final List updatedCoins = []; - final lelantusJoinSplitTxns = await db - .getTransactions(walletId) - .filter() - .isLelantusEqualTo(true) - .and() - .subTypeEqualTo(isar_models.TransactionSubType.join) - .findAll(); + final usedSerialNumbersSet = (await getUsedCoinSerials()).toSet(); - Set joinSplitTXIDs = {}; - - // for (final tx in lelantusJoinSplitTxns) { - // joinSplitTXIDs.add(tx.txid); - // } - for (final coin in lelantusCoins) { - joinSplitTXIDs.add(coin.txid); - } - - Map> - updatedData = {}; - - // Grab the most recent information on all the joinsplits - final updatedJSplit = await getJMintTransactions( - cachedElectrumXClient, - joinSplitTXIDs.toList(), - coin, + final root = await Bip32Utils.getBip32Root( + (await mnemonic).join(" "), + (await mnemonicPassphrase)!, + _network, ); - final currentChainHeight = await chainHeight; + for (final coin in lelantusCoins) { + final _derivePath = constructDerivePath( + networkWIF: _network.wif, + chain: MINT_INDEX, + index: coin.mintIndex, + ); + final bip32.BIP32 mintKeyPair = await Bip32Utils.getBip32NodeFromRoot( + root, + _derivePath, + ); - // update all of joinsplits that are now confirmed. - for (final tx in updatedJSplit.entries) { - isar_models.Transaction? currentTx; + final String serialNumber = GetSerialNumber( + int.parse(coin.value), + Format.uint8listToString(mintKeyPair.privateKey!), + coin.mintIndex, + isTestnet: this.coin == Coin.firoTestNet, + ); + final bool isUsed = usedSerialNumbersSet.contains(serialNumber); - try { - currentTx = - lelantusJoinSplitTxns.firstWhere((e) => e.txid == tx.value.txid); - } catch (_) { - currentTx = null; + if (isUsed) { + updatedCoins.add(coin.copyWith(isUsed: isUsed)); } - if (currentTx == null) { - // this send was accidentally not included in the list - tx.value.isLelantus = true; - updatedData[tx.value.txid] = - Tuple2(tx.value.address.value ?? tx.key, tx.value); - } else if (currentTx.isConfirmed( - currentChainHeight, MINIMUM_CONFIRMATIONS) != - tx.value.isConfirmed(currentChainHeight, MINIMUM_CONFIRMATIONS)) { - tx.value.isLelantus = true; - updatedData[tx.value.txid] = - Tuple2(tx.value.address.value ?? tx.key, tx.value); + final tx = await db.getTransaction(walletId, coin.txid); + if (tx == null) { + print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); } } - final List> txnsData = - []; - - for (final value in updatedData.values) { - // allow possible null address on mints as we don't display address - // this should normally never be null anyways but old (dbVersion up to 4) - // migrated transactions may not have had an address (full rescan should - // fix this) - isar_models.Address? transactionAddress; + if (updatedCoins.isNotEmpty) { try { - transactionAddress = - value.item2.subType == isar_models.TransactionSubType.mint - ? value.item1 - : value.item1!; - } catch (_) { - Logging.instance - .log("_refreshLelantusData value: $value", level: LogLevel.Fatal); + await db.isar.writeTxn(() async { + for (final c in updatedCoins) { + await db.isar.lelantusCoins.deleteByMintIndexWalletId( + c.mintIndex, + c.walletId, + ); + } + await db.isar.lelantusCoins.putAll(updatedCoins); + }); + } catch (e, s) { + Logging.instance.log( + "$e\n$s", + level: LogLevel.Fatal, + ); + rethrow; } - final outs = - value.item2.outputs.where((_) => true).toList(growable: false); - final ins = value.item2.inputs.where((_) => true).toList(growable: false); - - txnsData.add(Tuple2( - value.item2.copyWith(inputs: ins, outputs: outs).item1, - transactionAddress)); } - - await db.addNewTransactionData(txnsData, walletId); } Future _getMintHex(int amount, int index) async { From 7b8f26206e7c5883a363d2f76976fef74b0f197c Mon Sep 17 00:00:00 2001 From: julian Date: Tue, 25 Jul 2023 20:48:03 -0600 Subject: [PATCH 34/37] show sent to self jmint transactions correctly --- lib/services/coins/firo/firo_wallet.dart | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index da0919570..0ccc6436c 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -3538,11 +3538,21 @@ class FiroWallet extends CoinServiceAPI ), ); } + final txid = txObject["txid"] as String; const subType = isar_models.TransactionSubType.join; + final type = nonWalletAddressFoundInOutputs ? isar_models.TransactionType.outgoing - : isar_models.TransactionType.incoming; + : (await db.isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .filter() + .txidEqualTo(txid) + .findFirst()) == + null + ? isar_models.TransactionType.incoming + : isar_models.TransactionType.sentToSelf; final amount = nonWalletAddressFoundInOutputs ? totalOutputValue @@ -3569,7 +3579,7 @@ class FiroWallet extends CoinServiceAPI final tx = isar_models.Transaction( walletId: walletId, - txid: txObject["txid"] as String, + txid: txid, timestamp: txObject["blocktime"] as int? ?? (DateTime.now().millisecondsSinceEpoch ~/ 1000), type: type, From 6733a367e3461ab40a14ac0a6fb53b22de65701b Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 11:47:49 -0600 Subject: [PATCH 35/37] create mockable wrapper function --- lib/db/isar/main_db.dart | 11 +++++++++++ lib/services/coins/firo/firo_wallet.dart | 22 ++++------------------ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/lib/db/isar/main_db.dart b/lib/db/isar/main_db.dart index 93c9052da..c7a593d03 100644 --- a/lib/db/isar/main_db.dart +++ b/lib/db/isar/main_db.dart @@ -512,4 +512,15 @@ class MainDB { isar.writeTxn(() async { await isar.ethContracts.putAll(contracts); }); + + // ========== Lelantus ======================================================= + + Future getHighestUsedMintIndex({required String walletId}) async { + return await isar.lelantusCoins + .where() + .walletIdEqualTo(walletId) + .sortByMintIndexDesc() + .mintIndexProperty() + .findFirst(); + } } diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index 0ccc6436c..079b96ff4 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -2512,12 +2512,7 @@ class FiroWallet extends CoinServiceAPI Future>> createMintsFromAmount(int total) async { int tmpTotal = total; int counter = 0; - final lastUsedIndex = await db.isar.lelantusCoins - .where() - .walletIdEqualTo(walletId) - .sortByMintIndexDesc() - .mintIndexProperty() - .findFirst(); + final lastUsedIndex = await db.getHighestUsedMintIndex(walletId: walletId); final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; final root = await Bip32Utils.getBip32Root( @@ -2908,12 +2903,8 @@ class FiroWallet extends CoinServiceAPI level: LogLevel.Info); if (txid == transactionInfo['txid']) { - final lastUsedIndex = await db.isar.lelantusCoins - .where() - .walletIdEqualTo(walletId) - .sortByMintIndexDesc() - .mintIndexProperty() - .findFirst(); + final lastUsedIndex = + await db.getHighestUsedMintIndex(walletId: walletId); final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; if (transactionInfo['spendCoinIndexes'] != null) { @@ -4577,12 +4568,7 @@ class FiroWallet extends CoinServiceAPI int spendAmount, String address, bool subtractFeeFromAmount) async { final _mnemonic = await mnemonicString; final _mnemonicPassphrase = await mnemonicPassphrase; - final lastUsedIndex = await db.isar.lelantusCoins - .where() - .walletIdEqualTo(walletId) - .sortByMintIndexDesc() - .mintIndexProperty() - .findFirst(); + final lastUsedIndex = await db.getHighestUsedMintIndex(walletId: walletId); final nextFreeMintIndex = (lastUsedIndex ?? 0) + 1; final lelantusEntry = await _getLelantusEntry(); final anonymitySets = await fetchAnonymitySets(); From dbcb567d6bcdff7c7905023b2479d9955d6440d4 Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 11:54:46 -0600 Subject: [PATCH 36/37] fix tests --- .../services/coins/firo/firo_wallet_test.dart | 37 +++++++++++-------- .../coins/firo/firo_wallet_test.mocks.dart | 10 +++++ .../transaction_card_test.mocks.dart | 10 +++++ 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/test/services/coins/firo/firo_wallet_test.dart b/test/services/coins/firo/firo_wallet_test.dart index fcd7bd50e..05b4aeeff 100644 --- a/test/services/coins/firo/firo_wallet_test.dart +++ b/test/services/coins/firo/firo_wallet_test.dart @@ -11,9 +11,7 @@ import 'package:mockito/mockito.dart'; import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart'; import 'package:stackwallet/electrumx_rpc/electrumx.dart'; -import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; -import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; -import 'package:stackwallet/models/lelantus_coin.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/lelantus_fee_data.dart'; import 'package:stackwallet/models/paymint/transactions_model.dart' as old; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; @@ -114,10 +112,7 @@ void main() { final result = await staticProcessRestore(txData, message, currentHeight); expect(result, isA>()); - expect(result["mintIndex"], 8); - expect(result["jindex"], [2, 4, 6]); - expect( - result["_lelantus_coins"], isA>>()); + expect(result["_lelantus_coins"], isA>()); expect(result["newTxMap"], isA>()); }); @@ -532,18 +527,10 @@ void main() { group("FiroWallet service class functions that depend on shared storage", () { const testWalletId = "testWalletID"; const testWalletName = "Test Wallet"; - bool hiveAdaptersRegistered = false; setUp(() async { await setUpTestHive(); - if (!hiveAdaptersRegistered) { - hiveAdaptersRegistered = true; - - // Registering Lelantus Model Adapters - Hive.registerAdapter(LelantusCoinAdapter()); - } - final wallets = await Hive.openBox('wallets'); await wallets.put('currentWalletName', testWalletName); }); @@ -1204,13 +1191,33 @@ void main() { txHash: BuildMintTxTestParams.utxoInfo["txid"] as String, coin: Coin.firo, )).thenAnswer((_) async => BuildMintTxTestParams.cachedClientResponse); + when(cachedClient.getAnonymitySet( + groupId: "1", + coin: Coin.firo, + )).thenAnswer( + (_) async => GetAnonymitySetSampleData.data, + ); + when(cachedClient.getAnonymitySet( + groupId: "2", + coin: Coin.firo, + )).thenAnswer( + (_) async => GetAnonymitySetSampleData.data, + ); when(client.getBlockHeadTip()).thenAnswer( (_) async => {"height": 455873, "hex": "this value not used here"}); + when(client.getLatestCoinId()).thenAnswer((_) async => 2); when(mainDB.getAddress("${testWalletId}buildMintTransaction", any)) .thenAnswer((realInvocation) async => null); + when(mainDB.getHighestUsedMintIndex( + walletId: "${testWalletId}submitHexToNetwork")) + .thenAnswer((_) async => null); + when(mainDB.getHighestUsedMintIndex( + walletId: "testWalletIDbuildMintTransaction")) + .thenAnswer((_) async => null); + final firo = FiroWallet( walletName: testWalletName, walletId: "${testWalletId}buildMintTransaction", diff --git a/test/services/coins/firo/firo_wallet_test.mocks.dart b/test/services/coins/firo/firo_wallet_test.mocks.dart index 95d1750e9..2365f83b1 100644 --- a/test/services/coins/firo/firo_wallet_test.mocks.dart +++ b/test/services/coins/firo/firo_wallet_test.mocks.dart @@ -1151,4 +1151,14 @@ class MockMainDB extends _i1.Mock implements _i9.MainDB { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override + _i5.Future getHighestUsedMintIndex({required String? walletId}) => + (super.noSuchMethod( + Invocation.method( + #getHighestUsedMintIndex, + [], + {#walletId: walletId}, + ), + returnValue: _i5.Future.value(), + ) as _i5.Future); } diff --git a/test/widget_tests/transaction_card_test.mocks.dart b/test/widget_tests/transaction_card_test.mocks.dart index 74fa6e89e..35e8a2560 100644 --- a/test/widget_tests/transaction_card_test.mocks.dart +++ b/test/widget_tests/transaction_card_test.mocks.dart @@ -3496,4 +3496,14 @@ class MockMainDB extends _i1.Mock implements _i14.MainDB { returnValue: _i19.Future.value(), returnValueForMissingStub: _i19.Future.value(), ) as _i19.Future); + @override + _i19.Future getHighestUsedMintIndex({required String? walletId}) => + (super.noSuchMethod( + Invocation.method( + #getHighestUsedMintIndex, + [], + {#walletId: walletId}, + ), + returnValue: _i19.Future.value(), + ) as _i19.Future); } From 36747ca479da7ad04b35e2fd7434ce931255576d Mon Sep 17 00:00:00 2001 From: julian Date: Wed, 26 Jul 2023 14:50:38 -0600 Subject: [PATCH 37/37] another bandaid fix for hive box isn't open --- lib/db/hive/db.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/db/hive/db.dart b/lib/db/hive/db.dart index 25efcdb3e..9d3f73abd 100644 --- a/lib/db/hive/db.dart +++ b/lib/db/hive/db.dart @@ -274,8 +274,15 @@ class DB { {required dynamic key, required String boxName}) async => await mutex.protect(() async => await Hive.box(boxName).delete(key)); - Future deleteAll({required String boxName}) async => - await mutex.protect(() async => await Hive.box(boxName).clear()); + Future deleteAll({required String boxName}) async { + await mutex.protect(() async { + Box box = Hive.box(boxName); + if (!box.isOpen) { + box = await Hive.openBox(boxName); + } + await box.clear(); + }); + } Future deleteBoxFromDisk({required String boxName}) async => await mutex.protect(() async => await Hive.deleteBoxFromDisk(boxName));