monero refactor with some fixes applied to wownero as well

This commit is contained in:
julian 2024-01-08 13:40:07 -06:00
parent 4232ef8d8a
commit f43ae8788d
11 changed files with 2469 additions and 1303 deletions

View file

@ -500,7 +500,13 @@ class _NewWalletRecoveryPhraseWarningViewState
Constants.defaultSeedPhraseLengthFor( Constants.defaultSeedPhraseLengthFor(
coin: info.coin, coin: info.coin,
); );
if (wordCount > 0) {
if (coin == Coin.monero ||
coin == Coin.wownero) {
// currently a special case due to the
// xmr/wow libraries handling their
// own mnemonic generation
} else if (wordCount > 0) {
if (ref if (ref
.read(pNewWalletOptions.state) .read(pNewWalletOptions.state)
.state != .state !=

View file

@ -8,6 +8,8 @@
* *
*/ */
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/desktop_wallet_view.dart';
@ -79,8 +81,8 @@ class CoinWalletsTable extends ConsumerWidget {
ref.read(pWallets).getWallet(walletIds[i]); ref.read(pWallets).getWallet(walletIds[i]);
if (wallet.info.coin == Coin.monero || if (wallet.info.coin == Coin.monero ||
wallet.info.coin == Coin.wownero) { wallet.info.coin == Coin.wownero) {
// TODO: this can cause ui lag // TODO: this can cause ui lag if awaited
await wallet.init(); unawaited(wallet.init());
} }
await Navigator.of(context).pushNamed( await Navigator.of(context).pushNamed(

View file

@ -16,7 +16,6 @@ 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/epiccash/epiccash_wallet.dart'; import 'package:stackwallet/services/coins/epiccash/epiccash_wallet.dart';
import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart'; import 'package:stackwallet/services/coins/ethereum/ethereum_wallet.dart';
import 'package:stackwallet/services/coins/monero/monero_wallet.dart';
import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart'; import 'package:stackwallet/services/coins/namecoin/namecoin_wallet.dart';
import 'package:stackwallet/services/coins/particl/particl_wallet.dart'; import 'package:stackwallet/services/coins/particl/particl_wallet.dart';
import 'package:stackwallet/services/coins/stellar/stellar_wallet.dart'; import 'package:stackwallet/services/coins/stellar/stellar_wallet.dart';
@ -120,13 +119,7 @@ abstract class CoinServiceAPI {
); );
case Coin.monero: case Coin.monero:
return MoneroWallet( throw UnimplementedError("moved");
walletId: walletId,
walletName: walletName,
coin: coin,
secureStorage: secureStorageInterface,
// tracker: tracker,
);
case Coin.particl: case Coin.particl:
return ParticlWallet( return ParticlWallet(

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,47 @@
import 'package:cw_monero/api/wallet.dart' as monero_wallet;
import 'package:stackwallet/models/node_model.dart';
import 'package:stackwallet/utilities/default_nodes.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
import 'package:stackwallet/wallets/crypto_currency/intermediate/cryptonote_currency.dart';
class Monero extends CryptonoteCurrency {
Monero(super.network) {
switch (network) {
case CryptoCurrencyNetwork.main:
coin = Coin.monero;
default:
throw Exception("Unsupported network: $network");
}
}
@override
int get minConfirms => 10;
@override
bool validateAddress(String address) {
return monero_wallet.validateAddress(address);
}
@override
NodeModel get defaultNode {
switch (network) {
case CryptoCurrencyNetwork.main:
return NodeModel(
host: "https://monero.stackwallet.com",
port: 18081,
name: DefaultNodes.defaultName,
id: DefaultNodes.buildId(Coin.monero),
useSSL: true,
enabled: true,
coinName: Coin.monero.name,
isFailover: true,
isDown: false,
trusted: true,
);
default:
throw UnimplementedError();
}
}
}

View file

@ -286,7 +286,7 @@ class WalletInfo implements IsarId {
} }
} }
/// copies this with a new name and updates the db /// Can be dangerous. Don't use unless you know the consequences
Future<void> setMnemonicVerified({ Future<void> setMnemonicVerified({
required Isar isar, required Isar isar,
}) async { }) async {
@ -294,7 +294,6 @@ class WalletInfo implements IsarId {
await isar.walletInfoMeta.where().walletIdEqualTo(walletId).findFirst(); await isar.walletInfoMeta.where().walletIdEqualTo(walletId).findFirst();
if (meta == null) { if (meta == null) {
await isar.writeTxn(() async { await isar.writeTxn(() async {
await isar.walletInfoMeta.deleteByWalletId(walletId);
await isar.walletInfoMeta.put( await isar.walletInfoMeta.put(
WalletInfoMeta( WalletInfoMeta(
walletId: walletId, walletId: walletId,
@ -304,6 +303,7 @@ class WalletInfo implements IsarId {
}); });
} else if (meta.isMnemonicVerified == false) { } else if (meta.isMnemonicVerified == false) {
await isar.writeTxn(() async { await isar.writeTxn(() async {
await isar.walletInfoMeta.deleteByWalletId(walletId);
await isar.walletInfoMeta.put( await isar.walletInfoMeta.put(
WalletInfoMeta( WalletInfoMeta(
walletId: walletId, walletId: walletId,
@ -319,6 +319,26 @@ class WalletInfo implements IsarId {
} }
} }
/// copies this with a new name and updates the db
Future<void> updateRestoreHeight({
required int newRestoreHeight,
required Isar isar,
}) async {
// don't allow empty names
if (newRestoreHeight < 0) {
throw Exception("Negative restore height not allowed!");
}
// only update if there were changes to the name
if (restoreHeight != newRestoreHeight) {
_restoreHeight = newRestoreHeight;
await isar.writeTxn(() async {
await isar.walletInfo.deleteByWalletId(walletId);
await isar.walletInfo.put(this);
});
}
}
//============================================================================ //============================================================================
WalletInfo({ WalletInfo({

View file

@ -1,3 +1,4 @@
import 'package:cw_monero/pending_monero_transaction.dart';
import 'package:cw_wownero/pending_wownero_transaction.dart'; import 'package:cw_wownero/pending_wownero_transaction.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart';
@ -45,6 +46,9 @@ class TxData {
// wownero specific // wownero specific
final PendingWowneroTransaction? pendingWowneroTransaction; final PendingWowneroTransaction? pendingWowneroTransaction;
// monero specific
final PendingMoneroTransaction? pendingMoneroTransaction;
// firo lelantus specific // firo lelantus specific
final int? jMintValue; final int? jMintValue;
final List<int>? spendCoinIndexes; final List<int>? spendCoinIndexes;
@ -91,6 +95,7 @@ class TxData {
this.chainId, this.chainId,
this.feeInWei, this.feeInWei,
this.pendingWowneroTransaction, this.pendingWowneroTransaction,
this.pendingMoneroTransaction,
this.jMintValue, this.jMintValue,
this.spendCoinIndexes, this.spendCoinIndexes,
this.height, this.height,
@ -165,6 +170,7 @@ class TxData {
BigInt? chainId, BigInt? chainId,
BigInt? feeInWei, BigInt? feeInWei,
PendingWowneroTransaction? pendingWowneroTransaction, PendingWowneroTransaction? pendingWowneroTransaction,
PendingMoneroTransaction? pendingMoneroTransaction,
int? jMintValue, int? jMintValue,
List<int>? spendCoinIndexes, List<int>? spendCoinIndexes,
int? height, int? height,
@ -207,6 +213,8 @@ class TxData {
feeInWei: feeInWei ?? this.feeInWei, feeInWei: feeInWei ?? this.feeInWei,
pendingWowneroTransaction: pendingWowneroTransaction:
pendingWowneroTransaction ?? this.pendingWowneroTransaction, pendingWowneroTransaction ?? this.pendingWowneroTransaction,
pendingMoneroTransaction:
pendingMoneroTransaction ?? this.pendingMoneroTransaction,
jMintValue: jMintValue ?? this.jMintValue, jMintValue: jMintValue ?? this.jMintValue,
spendCoinIndexes: spendCoinIndexes ?? this.spendCoinIndexes, spendCoinIndexes: spendCoinIndexes ?? this.spendCoinIndexes,
height: height ?? this.height, height: height ?? this.height,
@ -244,6 +252,7 @@ class TxData {
'chainId: $chainId, ' 'chainId: $chainId, '
'feeInWei: $feeInWei, ' 'feeInWei: $feeInWei, '
'pendingWowneroTransaction: $pendingWowneroTransaction, ' 'pendingWowneroTransaction: $pendingWowneroTransaction, '
'pendingMoneroTransaction: $pendingMoneroTransaction, '
'jMintValue: $jMintValue, ' 'jMintValue: $jMintValue, '
'spendCoinIndexes: $spendCoinIndexes, ' 'spendCoinIndexes: $spendCoinIndexes, '
'height: $height, ' 'height: $height, '

File diff suppressed because it is too large Load diff

View file

@ -42,6 +42,7 @@ import 'package:stackwallet/wallets/crypto_currency/coins/wownero.dart';
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
import 'package:stackwallet/wallets/models/tx_data.dart'; import 'package:stackwallet/wallets/models/tx_data.dart';
import 'package:stackwallet/wallets/wallet/intermediate/cryptonote_wallet.dart'; import 'package:stackwallet/wallets/wallet/intermediate/cryptonote_wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
@ -312,6 +313,7 @@ class WowneroWallet extends CryptonoteWallet with MultiAddressInterface {
} }
cwWalletBase = (await cwWalletService!.openWallet(walletId, password)) cwWalletBase = (await cwWalletService!.openWallet(walletId, password))
as WowneroWalletBase; as WowneroWalletBase;
unawaited(_start());
} else { } else {
WalletInfo walletInfo; WalletInfo walletInfo;
WalletCredentials credentials; WalletCredentials credentials;
@ -358,11 +360,21 @@ class WowneroWallet extends CryptonoteWallet with MultiAddressInterface {
// 2))); // subtract a couple days to ensure we have a buffer for SWB // 2))); // subtract a couple days to ensure we have a buffer for SWB
final bufferedCreateHeight = getSeedHeightSync(wallet!.seed.trim()); final bufferedCreateHeight = getSeedHeightSync(wallet!.seed.trim());
// TODO: info.updateRestoreHeight await info.updateRestoreHeight(
await DB.instance.put<dynamic>( newRestoreHeight: bufferedCreateHeight,
boxName: walletId, isar: mainDB.isar,
key: "restoreHeight", );
value: bufferedCreateHeight);
// special case for xmr/wow. Normally mnemonic + passphrase is saved
// before wallet.init() is called
await secureStorageInterface.write(
key: Wallet.mnemonicKey(walletId: walletId),
value: wallet.seed.trim(),
);
await secureStorageInterface.write(
key: Wallet.mnemonicPassphraseKey(walletId: walletId),
value: "",
);
walletInfo.restoreHeight = bufferedCreateHeight; walletInfo.restoreHeight = bufferedCreateHeight;
@ -372,6 +384,7 @@ class WowneroWallet extends CryptonoteWallet with MultiAddressInterface {
cwWalletBase?.close(); cwWalletBase?.close();
cwWalletBase = wallet as WowneroWalletBase; cwWalletBase = wallet as WowneroWalletBase;
unawaited(_start());
} catch (e, s) { } catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Fatal); Logging.instance.log("$e\n$s", level: LogLevel.Fatal);
cwWalletBase?.close(); cwWalletBase?.close();
@ -379,7 +392,7 @@ class WowneroWallet extends CryptonoteWallet with MultiAddressInterface {
await updateNode(); await updateNode();
await cwWalletBase?.startSync(); await cwWalletBase?.startSync();
cwWalletBase?.close(); // cwWalletBase?.close();
} }
return super.init(); return super.init();
@ -723,6 +736,31 @@ class WowneroWallet extends CryptonoteWallet with MultiAddressInterface {
// ====== private ============================================================ // ====== private ============================================================
Future<void> _start() async {
cwWalletBase?.onNewBlock = onNewBlock;
cwWalletBase?.onNewTransaction = onNewTransaction;
cwWalletBase?.syncStatusChanged = syncStatusChanged;
if (cwWalletBase != null && !(await cwWalletBase!.isConnected())) {
final node = getCurrentNode();
final host = Uri.parse(node.host).host;
await cwWalletBase?.connectToNode(
node: Node(
uri: "$host:${node.port}",
type: WalletType.monero,
trusted: node.trusted ?? false,
),
);
}
await cwWalletBase?.startSync();
unawaited(refresh());
_autoSaveTimer?.cancel();
_autoSaveTimer = Timer.periodic(
const Duration(seconds: 193),
(_) async => await cwWalletBase?.save(),
);
}
void onNewBlock({required int height, required int blocksLeft}) { void onNewBlock({required int height, required int blocksLeft}) {
_currentKnownChainHeight = height; _currentKnownChainHeight = height;
updateChainHeight(); updateChainHeight();
@ -742,7 +780,7 @@ class WowneroWallet extends CryptonoteWallet with MultiAddressInterface {
void syncStatusChanged() async { void syncStatusChanged() async {
final syncStatus = cwWalletBase?.syncStatus; final syncStatus = cwWalletBase?.syncStatus;
if (syncStatus != null) { if (syncStatus != null) {
if (syncStatus.progress() == 1) { if (syncStatus.progress() == 1 && refreshMutex.isLocked) {
refreshMutex.release(); refreshMutex.release();
} }

View file

@ -30,11 +30,13 @@ import 'package:stackwallet/wallets/wallet/impl/ecash_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/epiccash_wallet.dart'; import 'package:stackwallet/wallets/wallet/impl/epiccash_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart'; import 'package:stackwallet/wallets/wallet/impl/firo_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/litecoin_wallet.dart'; import 'package:stackwallet/wallets/wallet/impl/litecoin_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/monero_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/namecoin_wallet.dart'; import 'package:stackwallet/wallets/wallet/impl/namecoin_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/nano_wallet.dart'; import 'package:stackwallet/wallets/wallet/impl/nano_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/particl_wallet.dart'; import 'package:stackwallet/wallets/wallet/impl/particl_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/tezos_wallet.dart'; import 'package:stackwallet/wallets/wallet/impl/tezos_wallet.dart';
import 'package:stackwallet/wallets/wallet/impl/wownero_wallet.dart'; import 'package:stackwallet/wallets/wallet/impl/wownero_wallet.dart';
import 'package:stackwallet/wallets/wallet/intermediate/cryptonote_wallet.dart';
import 'package:stackwallet/wallets/wallet/private_key_based_wallet.dart'; import 'package:stackwallet/wallets/wallet/private_key_based_wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/lelantus_interface.dart';
@ -139,6 +141,10 @@ abstract class Wallet<T extends CryptoCurrency> {
); );
if (wallet is MnemonicInterface) { if (wallet is MnemonicInterface) {
if (wallet is CryptonoteWallet) {
// currently a special case due to the xmr/wow libraries handling their
// own mnemonic generation
} else {
await secureStorageInterface.write( await secureStorageInterface.write(
key: mnemonicKey(walletId: walletInfo.walletId), key: mnemonicKey(walletId: walletInfo.walletId),
value: mnemonic!, value: mnemonic!,
@ -148,6 +154,7 @@ abstract class Wallet<T extends CryptoCurrency> {
value: mnemonicPassphrase!, value: mnemonicPassphrase!,
); );
} }
}
if (wallet is PrivateKeyBasedWallet) { if (wallet is PrivateKeyBasedWallet) {
await secureStorageInterface.write( await secureStorageInterface.write(
@ -281,6 +288,9 @@ abstract class Wallet<T extends CryptoCurrency> {
case Coin.litecoinTestNet: case Coin.litecoinTestNet:
return LitecoinWallet(CryptoCurrencyNetwork.test); return LitecoinWallet(CryptoCurrencyNetwork.test);
case Coin.monero:
return MoneroWallet(CryptoCurrencyNetwork.main);
case Coin.namecoin: case Coin.namecoin:
return NamecoinWallet(CryptoCurrencyNetwork.main); return NamecoinWallet(CryptoCurrencyNetwork.main);

View file

@ -97,7 +97,8 @@ class SimpleWalletCard extends ConsumerWidget {
final wallet = ref.read(pWallets).getWallet(walletId); final wallet = ref.read(pWallets).getWallet(walletId);
if (wallet.info.coin == Coin.monero || wallet.info.coin == Coin.wownero) { if (wallet.info.coin == Coin.monero || wallet.info.coin == Coin.wownero) {
await wallet.init(); // TODO: this can cause ui lag if awaited
unawaited(wallet.init());
} }
if (context.mounted) { if (context.mounted) {
if (popPrevious) nav.pop(); if (popPrevious) nav.pop();