nano clean up and optimisations

This commit is contained in:
julian 2023-05-30 10:00:24 -06:00
parent 91d330e57b
commit 3275039d01

View file

@ -8,41 +8,35 @@
* *
*/ */
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:nanodart/nanodart.dart'; import 'package:nanodart/nanodart.dart';
import 'package:http/http.dart' as http; import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/balance.dart'; import 'package:stackwallet/models/balance.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/utxo.dart'; import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/models/paymint/fee_object_model.dart'; import 'package:stackwallet/models/paymint/fee_object_model.dart';
import 'package:stackwallet/services/coins/coin_service.dart'; import 'package:stackwallet/services/coins/coin_service.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_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/event_bus/global_event_bus.dart';
import 'package:stackwallet/services/mixins/coin_control_interface.dart'; import 'package:stackwallet/services/mixins/coin_control_interface.dart';
import 'package:stackwallet/services/mixins/wallet_cache.dart'; import 'package:stackwallet/services/mixins/wallet_cache.dart';
import 'package:stackwallet/services/mixins/wallet_db.dart'; import 'package:stackwallet/services/mixins/wallet_db.dart';
import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/log_level_enum.dart'; import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
import '../../../db/isar/main_db.dart';
import '../../../models/isar/models/blockchain_data/address.dart';
import '../../../models/node_model.dart';
import '../../../utilities/default_nodes.dart';
import '../../../utilities/flutter_secure_storage_interface.dart';
import '../../../utilities/prefs.dart';
import '../../node_service.dart';
import '../../transaction_notification_tracker.dart';
import 'dart:async';
import 'package:stackwallet/models/isar/models/isar_models.dart';
const int MINIMUM_CONFIRMATIONS = 1; const int MINIMUM_CONFIRMATIONS = 1;
const String DEFAULT_REPRESENTATIVE = const String DEFAULT_REPRESENTATIVE =
"nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579"; "nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579";
@ -135,13 +129,26 @@ class NanoWallet extends CoinServiceAPI
late final TransactionNotificationTracker txTracker; late final TransactionNotificationTracker txTracker;
final _prefs = Prefs.instance; final _prefs = Prefs.instance;
Timer? timer;
bool _shouldAutoSync = false; bool _shouldAutoSync = false;
@override @override
bool get shouldAutoSync => _shouldAutoSync; bool get shouldAutoSync => _shouldAutoSync;
@override @override
set shouldAutoSync(bool shouldAutoSync) => _shouldAutoSync = shouldAutoSync; set shouldAutoSync(bool shouldAutoSync) {
if (_shouldAutoSync != shouldAutoSync) {
_shouldAutoSync = shouldAutoSync;
if (!shouldAutoSync) {
timer?.cancel();
timer = null;
stopNetworkAlivePinging();
} else {
startNetworkAlivePinging();
refresh();
}
}
}
@override @override
Balance get balance => _balance ??= getCachedBalance(); Balance get balance => _balance ??= getCachedBalance();
@ -177,7 +184,7 @@ class NanoWallet extends CoinServiceAPI
Future<String> confirmSend({required Map<String, dynamic> txData}) async { Future<String> confirmSend({required Map<String, dynamic> txData}) async {
try { try {
// our address: // our address:
final String publicAddress = await getAddressFromMnemonic(); final String publicAddress = await currentReceivingAddress;
// first get the account balance: // first get the account balance:
final balanceBody = jsonEncode({ final balanceBody = jsonEncode({
@ -279,8 +286,18 @@ class NanoWallet extends CoinServiceAPI
} }
} }
Future<Address?> get _currentReceivingAddress => db
.getAddresses(walletId)
.filter()
.typeEqualTo(AddressType.nano)
.and()
.subTypeEqualTo(AddressSubType.receiving)
.sortByDerivationIndexDesc()
.findFirst();
@override @override
Future<String> get currentReceivingAddress => getAddressFromMnemonic(); Future<String> get currentReceivingAddress async =>
(await _currentReceivingAddress)?.value ?? await getAddressFromMnemonic();
@override @override
Future<Amount> estimateFeeFor(Amount amount, int feeRate) { Future<Amount> estimateFeeFor(Amount amount, int feeRate) {
@ -295,13 +312,13 @@ class NanoWallet extends CoinServiceAPI
} }
@override @override
// TODO: implement fees // Nano has no fees
Future<FeeObject> get fees => throw UnimplementedError(); Future<FeeObject> get fees => throw UnimplementedError();
Future<void> updateBalance() async { Future<void> updateBalance() async {
final body = jsonEncode({ final body = jsonEncode({
"action": "account_balance", "action": "account_balance",
"account": await getAddressFromMnemonic(), "account": await currentReceivingAddress,
}); });
final headers = { final headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
@ -336,7 +353,7 @@ class NanoWallet extends CoinServiceAPI
}; };
// our address: // our address:
final String publicAddress = await getAddressFromMnemonic(); final String publicAddress = await currentReceivingAddress;
// first check if the account is open: // first check if the account is open:
// get the account info (we need the frontier and representative): // get the account info (we need the frontier and representative):
@ -455,7 +472,7 @@ class NanoWallet extends CoinServiceAPI
body: jsonEncode({ body: jsonEncode({
"action": "receivable", "action": "receivable",
"source": "true", "source": "true",
"account": await getAddressFromMnemonic(), "account": await currentReceivingAddress,
"count": "-1", "count": "-1",
})); }));
@ -477,7 +494,8 @@ class NanoWallet extends CoinServiceAPI
Future<void> updateTransactions() async { Future<void> updateTransactions() async {
await confirmAllReceivable(); await confirmAllReceivable();
final String publicAddress = await getAddressFromMnemonic(); final receivingAddress = (await _currentReceivingAddress)!;
final String publicAddress = receivingAddress.value;
final response = await http.post(Uri.parse(getCurrentNode().host), final response = await http.post(Uri.parse(getCurrentNode().host),
headers: {"Content-Type": "application/json"}, headers: {"Content-Type": "application/json"},
body: jsonEncode({ body: jsonEncode({
@ -521,22 +539,19 @@ class NanoWallet extends CoinServiceAPI
inputs: [], inputs: [],
outputs: [], outputs: [],
nonce: 0, nonce: 0,
numberOfMessages: null,
); );
Address address = Address( Address address = transactionType == TransactionType.incoming
? receivingAddress
: Address(
walletId: walletId, walletId: walletId,
publicKey: [], publicKey: [],
value: transactionType == TransactionType.incoming value: tx["account"].toString(),
? publicAddress
: tx["account"].toString(),
derivationIndex: 0, derivationIndex: 0,
derivationPath: null, derivationPath: null,
type: transactionType == TransactionType.incoming type: AddressType.nano,
? AddressType.nonWallet subType: AddressSubType.nonWallet,
: AddressType.nano,
subType: transactionType == TransactionType.incoming
? AddressSubType.receiving
: AddressSubType.nonWallet,
); );
Tuple2<Transaction, Address> tuple = Tuple2(transaction, address); Tuple2<Transaction, Address> tuple = Tuple2(transaction, address);
transactionList.add(tuple); transactionList.add(tuple);
@ -550,7 +565,9 @@ class NanoWallet extends CoinServiceAPI
@override @override
Future<void> fullRescan( Future<void> fullRescan(
int maxUnusedAddressGap, int maxNumberOfIndexesToCheck) async { int maxUnusedAddressGap,
int maxNumberOfIndexesToCheck,
) async {
await _prefs.init(); await _prefs.init();
await updateTransactions(); await updateTransactions();
await updateBalance(); await updateBalance();
@ -592,23 +609,27 @@ class NanoWallet extends CoinServiceAPI
); );
String privateKey = NanoKeys.seedToPrivate(seed, 0); String privateKey = NanoKeys.seedToPrivate(seed, 0);
String publicKey = NanoKeys.createPublicKey(privateKey); String publicKey = NanoKeys.createPublicKey(privateKey);
String publicAddress = String publicAddress = NanoAccounts.createAccount(
NanoAccounts.createAccount(NanoAccountType.NANO, publicKey); NanoAccountType.NANO,
publicKey,
);
final address = Address( final address = Address(
walletId: walletId, walletId: walletId,
value: publicAddress, value: publicAddress,
publicKey: [], // TODO: add public key publicKey: [],
derivationIndex: 0, derivationIndex: 0,
derivationPath: null, derivationPath: null,
type: AddressType.unknown, type: AddressType.nano,
subType: AddressSubType.receiving, subType: AddressSubType.receiving,
); );
await db.putAddress(address); await db.putAddress(address);
await Future.wait( await Future.wait([
[updateCachedId(walletId), updateCachedIsFavorite(false)]); updateCachedId(walletId),
updateCachedIsFavorite(false),
]);
} }
@override @override
@ -643,20 +664,14 @@ class NanoWallet extends CoinServiceAPI
Map<String, dynamic>? args, Map<String, dynamic>? args,
}) async { }) async {
try { try {
int satAmount = amount.raw.toInt(); if (amount.decimals != coin.decimals) {
int realfee = 0; throw ArgumentError("Nano prepareSend attempted with invalid Amount");
if (balance.spendable == amount) {
satAmount = balance.spendable.raw.toInt() - realfee;
} }
Map<String, dynamic> txData = { Map<String, dynamic> txData = {
"fee": realfee, "fee": 0,
"addresss": address, "addresss": address,
"recipientAmt": Amount( "recipientAmt": amount,
rawValue: BigInt.from(satAmount),
fractionDigits: coin.decimals,
),
}; };
Logging.instance.log("prepare send: $txData", level: LogLevel.Info); Logging.instance.log("prepare send: $txData", level: LogLevel.Info);
@ -696,17 +711,19 @@ class NanoWallet extends CoinServiceAPI
final address = Address( final address = Address(
walletId: walletId, walletId: walletId,
value: publicAddress, value: publicAddress,
publicKey: [], // TODO: add public key publicKey: [],
derivationIndex: 0, derivationIndex: 0,
derivationPath: null, derivationPath: null,
type: AddressType.unknown, type: AddressType.nano,
subType: AddressSubType.receiving, subType: AddressSubType.receiving,
); );
await db.putAddress(address); await db.putAddress(address);
await Future.wait( await Future.wait([
[updateCachedId(walletId), updateCachedIsFavorite(false)]); updateCachedId(walletId),
updateCachedIsFavorite(false),
]);
} catch (e) { } catch (e) {
rethrow; rethrow;
} }
@ -769,6 +786,49 @@ class NanoWallet extends CoinServiceAPI
return Future.value(false); return Future.value(false);
} }
Timer? _networkAliveTimer;
void startNetworkAlivePinging() {
// call once on start right away
_periodicPingCheck();
// then periodically check
_networkAliveTimer = Timer.periodic(
Constants.networkAliveTimerDuration,
(_) async {
_periodicPingCheck();
},
);
}
void _periodicPingCheck() async {
bool hasNetwork = await testNetworkConnection();
if (_isConnected != hasNetwork) {
NodeConnectionStatus status = hasNetwork
? NodeConnectionStatus.connected
: NodeConnectionStatus.disconnected;
GlobalEventBus.instance.fire(
NodeConnectionStatusChangedEvent(
status,
walletId,
coin,
),
);
_isConnected = hasNetwork;
if (hasNetwork) {
unawaited(refresh());
}
}
}
void stopNetworkAlivePinging() {
_networkAliveTimer?.cancel();
_networkAliveTimer = null;
}
@override @override
Future<List<Transaction>> get transactions => Future<List<Transaction>> get transactions =>
db.getTransactions(walletId).findAll(); db.getTransactions(walletId).findAll();
@ -785,9 +845,9 @@ class NanoWallet extends CoinServiceAPI
} }
@override @override
Future<void> updateSentCachedTxData(Map<String, dynamic> txData) { Future<void> updateSentCachedTxData(Map<String, dynamic> txData) async {
// TODO: implement updateSentCachedTxData // not currently used for nano
throw UnimplementedError(); return;
} }
@override @override
@ -800,7 +860,7 @@ class NanoWallet extends CoinServiceAPI
} }
Future<void> updateChainHeight() async { Future<void> updateChainHeight() async {
final String publicAddress = await getAddressFromMnemonic(); final String publicAddress = await currentReceivingAddress;
// first get the account balance: // first get the account balance:
final infoBody = jsonEncode({ final infoBody = jsonEncode({
"action": "account_info", "action": "account_info",