display xprivs along side mnemonic on backup screen

This commit is contained in:
julian 2024-07-03 10:32:20 -06:00
parent f22f34904a
commit cae27b3835
13 changed files with 326 additions and 91 deletions

View file

@ -105,6 +105,7 @@ class _UnlockWalletKeysDesktopState
WalletKeysDesktopPopup.routeName,
arguments: (
mnemonic: words ?? [],
walletId: widget.walletId,
frostData: frostData,
),
);
@ -344,6 +345,7 @@ class _UnlockWalletKeysDesktopState
WalletKeysDesktopPopup.routeName,
arguments: (
mnemonic: words ?? [],
walletId: widget.walletId,
frostData: frostData,
),
);

View file

@ -12,38 +12,46 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../notifications/show_flush_bar.dart';
import '../../../../pages/add_wallet_views/new_wallet_recovery_phrase_view/sub_widgets/mnemonic_table.dart';
import '../../../../pages/wallet_view/transaction_views/transaction_details_view.dart';
import '../../../../providers/providers.dart';
import '../../../../themes/stack_colors.dart';
import '../../../../utilities/address_utils.dart';
import '../../../../utilities/assets.dart';
import '../../../../utilities/clipboard_interface.dart';
import '../../../../utilities/text_styles.dart';
import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart';
import '../../../../widgets/custom_tab_view.dart';
import '../../../../widgets/desktop/desktop_dialog.dart';
import '../../../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../../../widgets/desktop/primary_button.dart';
import '../../../../widgets/desktop/secondary_button.dart';
import '../../../../widgets/detail_item.dart';
import '../../../../widgets/loading_indicator.dart';
import '../../../../widgets/rounded_white_container.dart';
import 'qr_code_desktop_popup_content.dart';
class WalletKeysDesktopPopup extends StatelessWidget {
class WalletKeysDesktopPopup extends ConsumerWidget {
const WalletKeysDesktopPopup({
super.key,
required this.words,
required this.walletId,
this.frostData,
this.clipboardInterface = const ClipboardWrapper(),
});
final List<String> words;
final String walletId;
final ({String keys, String config})? frostData;
final ClipboardInterface clipboardInterface;
static const String routeName = "walletKeysDesktopPopup";
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
return DesktopDialog(
maxWidth: 614,
maxHeight: double.infinity,
@ -168,7 +176,48 @@ class WalletKeysDesktopPopup extends StatelessWidget {
),
],
)
: Column(
: (ref.watch(pWallets).getWallet(walletId)
is ExtendedKeysInterface)
? CustomTabView(
titles: const ["Mnemonic", "XPriv(s)"],
children: [
Padding(
padding: const EdgeInsets.only(top: 16),
child: _Mnemonic(
words: words,
),
),
_MasterSeedPrivateKey(
words: words,
walletId: walletId,
),
],
)
: _Mnemonic(
words: words,
),
const SizedBox(
height: 32,
),
],
),
);
}
}
class _Mnemonic extends StatelessWidget {
const _Mnemonic({
super.key,
required this.words,
this.clipboardInterface = const ClipboardWrapper(),
});
final List<String> words;
final ClipboardInterface clipboardInterface;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
"Recovery phrase",
@ -183,9 +232,10 @@ class WalletKeysDesktopPopup extends StatelessWidget {
horizontal: 32,
),
child: Text(
"Please write down your recovery phrase in the correct order and save it to keep your funds secure. You will also be asked to verify the words on the next screen.",
style:
STextStyles.desktopTextExtraExtraSmall(context),
"Please write down your recovery phrase in the correct order and "
"save it to keep your funds secure. You will also be asked to"
" verify the words on the next screen.",
style: STextStyles.desktopTextExtraExtraSmall(context),
textAlign: TextAlign.center,
),
),
@ -200,9 +250,8 @@ class WalletKeysDesktopPopup extends StatelessWidget {
child: MnemonicTable(
words: words,
isDesktop: true,
itemBorderColor: Theme.of(context)
.extension<StackColors>()!
.buttonBackSecondary,
itemBorderColor:
Theme.of(context).extension<StackColors>()!.buttonBackSecondary,
),
),
const SizedBox(
@ -219,8 +268,7 @@ class WalletKeysDesktopPopup extends StatelessWidget {
label: "Show QR code",
onPressed: () {
// TODO: address utils
final String value =
AddressUtils.encodeQRSeedData(words);
final String value = AddressUtils.encodeQRSeedData(words);
Navigator.of(context).pushNamed(
QRCodeDesktopPopupContent.routeName,
arguments: value,
@ -255,9 +303,91 @@ class WalletKeysDesktopPopup extends StatelessWidget {
),
),
],
),
);
}
}
class _MasterSeedPrivateKey extends ConsumerStatefulWidget {
const _MasterSeedPrivateKey({
super.key,
required this.words,
required this.walletId,
this.clipboardInterface = const ClipboardWrapper(),
});
final List<String> words;
final String walletId;
final ClipboardInterface clipboardInterface;
@override
ConsumerState<_MasterSeedPrivateKey> createState() =>
_MasterSeedPrivateKeyState();
}
class _MasterSeedPrivateKeyState extends ConsumerState<_MasterSeedPrivateKey> {
final controller = TextEditingController();
@override
void initState() {
super.initState();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
children: [
const SizedBox(
height: 32,
height: 12,
),
FutureBuilder(
future: (ref.read(pWallets).getWallet(widget.walletId)
as ExtendedKeysInterface)
.getXPrivs(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
DetailItem(
title: "Master fingerprint",
detail: snapshot.data!.fingerprint,
),
...snapshot.data!.xprivs.map(
(e) => Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
DetailItem(
title: "Derivation",
detail: e.path,
),
DetailItem(
title: "xpriv",
detail: e.xpriv,
),
],
),
),
),
],
);
} else {
return const LoadingIndicator(
width: 100,
height: 100,
);
}
},
),
],
),

View file

@ -2293,11 +2293,13 @@ class RouteGenerator {
case WalletKeysDesktopPopup.routeName:
if (args is ({
List<String> mnemonic,
String walletId,
({String keys, String config})? frostData
})) {
return FadePageRoute(
WalletKeysDesktopPopup(
words: args.mnemonic,
walletId: args.walletId,
frostData: args.frostData,
),
RouteSettings(

View file

@ -8,12 +8,14 @@ import '../intermediate/bip39_hd_wallet.dart';
import '../wallet_mixin_interfaces/coin_control_interface.dart';
import '../wallet_mixin_interfaces/cpfp_interface.dart';
import '../wallet_mixin_interfaces/electrumx_interface.dart';
import '../wallet_mixin_interfaces/extended_keys_interface.dart';
import '../wallet_mixin_interfaces/paynym_interface.dart';
import '../wallet_mixin_interfaces/rbf_interface.dart';
class BitcoinWallet<T extends PaynymCurrencyInterface> extends Bip39HDWallet<T>
with
ElectrumXInterface<T>,
ExtendedKeysInterface<T>,
CoinControlInterface,
PaynymInterface<T>,
RbfInterface<T>,

View file

@ -19,11 +19,13 @@ import '../wallet_mixin_interfaces/bcash_interface.dart';
import '../wallet_mixin_interfaces/cash_fusion_interface.dart';
import '../wallet_mixin_interfaces/coin_control_interface.dart';
import '../wallet_mixin_interfaces/electrumx_interface.dart';
import '../wallet_mixin_interfaces/extended_keys_interface.dart';
class BitcoincashWallet<T extends ElectrumXCurrencyInterface>
extends Bip39HDWallet<T>
with
ElectrumXInterface<T>,
ExtendedKeysInterface<T>,
BCashInterface<T>,
CoinControlInterface<T>,
CashFusionInterface<T> {

View file

@ -12,9 +12,10 @@ import '../../crypto_currency/interfaces/electrumx_currency_interface.dart';
import '../intermediate/bip39_hd_wallet.dart';
import '../wallet_mixin_interfaces/coin_control_interface.dart';
import '../wallet_mixin_interfaces/electrumx_interface.dart';
import '../wallet_mixin_interfaces/extended_keys_interface.dart';
class DashWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
with ElectrumXInterface<T>, CoinControlInterface {
with ElectrumXInterface<T>, ExtendedKeysInterface<T>, CoinControlInterface {
DashWallet(CryptoCurrencyNetwork network) : super(Dash(network) as T);
@override

View file

@ -13,9 +13,11 @@ import '../../crypto_currency/interfaces/electrumx_currency_interface.dart';
import '../intermediate/bip39_hd_wallet.dart';
import '../wallet_mixin_interfaces/coin_control_interface.dart';
import '../wallet_mixin_interfaces/electrumx_interface.dart';
import '../wallet_mixin_interfaces/extended_keys_interface.dart';
class DogecoinWallet<T extends ElectrumXCurrencyInterface>
extends Bip39HDWallet<T> with ElectrumXInterface<T>, CoinControlInterface {
extends Bip39HDWallet<T>
with ElectrumXInterface<T>, ExtendedKeysInterface<T>, CoinControlInterface {
DogecoinWallet(CryptoCurrencyNetwork network) : super(Dogecoin(network) as T);
@override

View file

@ -19,10 +19,12 @@ import '../wallet_mixin_interfaces/bcash_interface.dart';
import '../wallet_mixin_interfaces/cash_fusion_interface.dart';
import '../wallet_mixin_interfaces/coin_control_interface.dart';
import '../wallet_mixin_interfaces/electrumx_interface.dart';
import '../wallet_mixin_interfaces/extended_keys_interface.dart';
class EcashWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
with
ElectrumXInterface<T>,
ExtendedKeysInterface<T>,
BCashInterface<T>,
CoinControlInterface<T>,
CashFusionInterface<T> {

View file

@ -22,6 +22,7 @@ import '../../models/tx_data.dart';
import '../intermediate/bip39_hd_wallet.dart';
import '../wallet_mixin_interfaces/coin_control_interface.dart';
import '../wallet_mixin_interfaces/electrumx_interface.dart';
import '../wallet_mixin_interfaces/extended_keys_interface.dart';
import '../wallet_mixin_interfaces/lelantus_interface.dart';
import '../wallet_mixin_interfaces/spark_interface.dart';
@ -30,6 +31,7 @@ const sparkStartBlock = 819300; // (approx 18 Jan 2024)
class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
with
ElectrumXInterface<T>,
ExtendedKeysInterface<T>,
LelantusInterface<T>,
SparkInterface<T>,
CoinControlInterface<T> {

View file

@ -15,6 +15,7 @@ import '../../crypto_currency/interfaces/electrumx_currency_interface.dart';
import '../intermediate/bip39_hd_wallet.dart';
import '../wallet_mixin_interfaces/coin_control_interface.dart';
import '../wallet_mixin_interfaces/electrumx_interface.dart';
import '../wallet_mixin_interfaces/extended_keys_interface.dart';
import '../wallet_mixin_interfaces/ordinals_interface.dart';
import '../wallet_mixin_interfaces/rbf_interface.dart';
@ -22,6 +23,7 @@ class LitecoinWallet<T extends ElectrumXCurrencyInterface>
extends Bip39HDWallet<T>
with
ElectrumXInterface<T>,
ExtendedKeysInterface<T>,
CoinControlInterface<T>,
RbfInterface<T>,
OrdinalsInterface<T> {

View file

@ -19,10 +19,14 @@ import '../../models/tx_data.dart';
import '../intermediate/bip39_hd_wallet.dart';
import '../wallet_mixin_interfaces/coin_control_interface.dart';
import '../wallet_mixin_interfaces/electrumx_interface.dart';
import '../wallet_mixin_interfaces/extended_keys_interface.dart';
class ParticlWallet<T extends ElectrumXCurrencyInterface>
extends Bip39HDWallet<T>
with ElectrumXInterface<T>, CoinControlInterface<T> {
with
ElectrumXInterface<T>,
ExtendedKeysInterface<T>,
CoinControlInterface<T> {
@override
int get isarTransactionVersion => 2;

View file

@ -1,4 +1,5 @@
import 'package:isar/isar.dart';
import '../../../models/isar/models/blockchain_data/address.dart';
import '../../../models/isar/models/blockchain_data/transaction.dart';
import '../../../models/isar/models/blockchain_data/v2/input_v2.dart';
@ -11,10 +12,14 @@ import '../../crypto_currency/interfaces/electrumx_currency_interface.dart';
import '../intermediate/bip39_hd_wallet.dart';
import '../wallet_mixin_interfaces/coin_control_interface.dart';
import '../wallet_mixin_interfaces/electrumx_interface.dart';
import '../wallet_mixin_interfaces/extended_keys_interface.dart';
class PeercoinWallet<T extends ElectrumXCurrencyInterface>
extends Bip39HDWallet<T>
with ElectrumXInterface<T>, CoinControlInterface<T> {
with
ElectrumXInterface<T>,
ExtendedKeysInterface<T>,
CoinControlInterface<T> {
@override
int get isarTransactionVersion => 2;

View file

@ -0,0 +1,79 @@
import '../../crypto_currency/interfaces/electrumx_currency_interface.dart';
import 'electrumx_interface.dart';
typedef XPub = ({String path, String xpub});
typedef XPriv = ({String path, String xpriv});
mixin ExtendedKeysInterface<T extends ElectrumXCurrencyInterface>
on ElectrumXInterface<T> {
Future<({List<XPub> xpubs, String fingerprint})> getXPubs() async {
final paths = cryptoCurrency.supportedDerivationPathTypes.map(
(e) => (
path: e,
addressType: e.getAddressType(),
),
);
final master = await getRootHDNode();
final fingerprint = master.fingerprint.toRadixString(16);
final futures = paths.map((e) async {
String path = cryptoCurrency.constructDerivePath(
derivePathType: e.path,
chain: 0,
index: 0,
);
// trim chain and address index
path = path.substring(0, path.lastIndexOf("'") + 1);
final node = master.derivePath(path);
return (
path: path,
xpub: node.hdPublicKey.encode(
cryptoCurrency.networkParams.pubHDPrefix,
// 0x04b24746,
),
);
});
return (
fingerprint: fingerprint,
xpubs: await Future.wait(futures),
);
}
Future<({List<XPriv> xprivs, String fingerprint})> getXPrivs() async {
final paths = cryptoCurrency.supportedDerivationPathTypes.map(
(e) => (
path: e,
addressType: e.getAddressType(),
),
);
final master = await getRootHDNode();
final fingerprint = master.fingerprint.toRadixString(16);
final futures = paths.map((e) async {
String path = cryptoCurrency.constructDerivePath(
derivePathType: e.path,
chain: 0,
index: 0,
);
// trim chain and address index
path = path.substring(0, path.lastIndexOf("'") + 1);
final node = master.derivePath(path);
return (
path: path,
xpriv: node.encode(
cryptoCurrency.networkParams.privHDPrefix,
),
);
});
return (
fingerprint: fingerprint,
xprivs: await Future.wait(futures),
);
}
}