Skip to content

Commit 969809b

Browse files
Merge pull request #1244 from cypherstack/various
Various
2 parents 67ea8a1 + a088e2c commit 969809b

30 files changed

Lines changed: 2313 additions & 386 deletions

lib/electrumx_rpc/cached_electrumx_client.dart

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,13 @@ class CachedElectrumXClient {
2626
required ElectrumXClient electrumXClient,
2727
}) => CachedElectrumXClient(electrumXClient: electrumXClient);
2828

29-
String base64ToHex(String source) =>
30-
base64Decode(
31-
LineSplitter.split(source).join(),
32-
).map((e) => e.toRadixString(16).padLeft(2, '0')).join();
29+
String base64ToHex(String source) => base64Decode(
30+
LineSplitter.split(source).join(),
31+
).map((e) => e.toRadixString(16).padLeft(2, '0')).join();
3332

34-
String base64ToReverseHex(String source) =>
35-
base64Decode(
36-
LineSplitter.split(source).join(),
37-
).reversed.map((e) => e.toRadixString(16).padLeft(2, '0')).join();
33+
String base64ToReverseHex(String source) => base64Decode(
34+
LineSplitter.split(source).join(),
35+
).reversed.map((e) => e.toRadixString(16).padLeft(2, '0')).join();
3836

3937
/// Call electrumx getTransaction on a per coin basis, storing the result in local db if not already there.
4038
///
@@ -77,6 +75,55 @@ class CachedElectrumXClient {
7775
}
7876
}
7977

78+
Future<List<Map<String, dynamic>>> getBatchTransactions({
79+
required List<String> txHashes,
80+
required CryptoCurrency cryptoCurrency,
81+
}) async {
82+
try {
83+
final box = await DB.instance.getTxCacheBox(currency: cryptoCurrency);
84+
85+
final List<Map<String, dynamic>> result = [];
86+
final List<String> needsFetching = [];
87+
88+
for (final txHash in txHashes) {
89+
final cachedTx = box.get(txHash) as Map?;
90+
if (cachedTx == null) {
91+
needsFetching.add(txHash);
92+
} else {
93+
result.add(Map<String, dynamic>.from(cachedTx));
94+
}
95+
}
96+
97+
if (needsFetching.isNotEmpty) {
98+
final txns = await electrumXClient.getBatchTransactions(
99+
txHashes: needsFetching,
100+
);
101+
102+
for (final tx in txns) {
103+
tx.remove("hex");
104+
tx.remove("lelantusData");
105+
tx.remove("sparkData");
106+
107+
if (tx["confirmations"] != null &&
108+
tx["confirmations"] as int > minCacheConfirms) {
109+
await box.put(tx["txid"] as String, tx);
110+
}
111+
112+
result.add(tx);
113+
}
114+
}
115+
116+
return result;
117+
} catch (e, s) {
118+
Logging.instance.e(
119+
"Failed to process CachedElectrumX.getTransaction(): ",
120+
error: e,
121+
stackTrace: s,
122+
);
123+
rethrow;
124+
}
125+
}
126+
80127
/// Clear all cached transactions for the specified coin
81128
Future<void> clearSharedTransactionCache({
82129
required CryptoCurrency cryptoCurrency,

lib/electrumx_rpc/electrumx_client.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,28 @@ class ElectrumXClient {
828828
return Map<String, dynamic>.from(response as Map);
829829
}
830830

831+
Future<List<Map<String, dynamic>>> getBatchTransactions({
832+
required List<String> txHashes,
833+
String? requestID,
834+
}) async {
835+
Logging.instance.d(
836+
"attempting to fetch BATCHED blockchain.transaction.get...",
837+
);
838+
839+
final response = await batchRequest(
840+
command: 'blockchain.transaction.get',
841+
args: txHashes.map((e) => [e, true]).toList(),
842+
);
843+
final List<Map<String, dynamic>> result = [];
844+
for (int i = 0; i < response.length; i++) {
845+
result.add(Map<String, dynamic>.from(response[i] as Map));
846+
}
847+
848+
Logging.instance.d("Fetching blockchain.transaction.get BATCHED finished");
849+
850+
return result;
851+
}
852+
831853
/// Returns the whole Lelantus anonymity set for denomination in the groupId.
832854
///
833855
/// ex:

lib/pages/address_book_views/address_book_view.dart

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,7 @@ class _AddressBookViewState extends ConsumerState<AddressBookView> {
7373
} else {
7474
ref
7575
.read(addressBookFilterProvider)
76-
.addAll(
77-
coins.where((e) => e.network != CryptoCurrencyNetwork.test),
78-
false,
79-
);
76+
.addAll(coins.where((e) => !e.network.isTestNet), false);
8077
}
8178
} else {
8279
ref.read(addressBookFilterProvider).add(widget.coin!, false);
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_riverpod/flutter_riverpod.dart';
3+
4+
import '../../themes/stack_colors.dart';
5+
import '../../utilities/text_styles.dart';
6+
import '../../utilities/util.dart';
7+
import '../../widgets/background.dart';
8+
import '../../widgets/conditional_parent.dart';
9+
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
10+
import '../../widgets/desktop/desktop_dialog_close_button.dart';
11+
import 'sub_widgets/register_masternode_form.dart';
12+
13+
class CreateMasternodeView extends ConsumerStatefulWidget {
14+
const CreateMasternodeView({
15+
super.key,
16+
required this.firoWalletId,
17+
this.popTxidOnSuccess = true,
18+
});
19+
20+
static const routeName = "/createMasternodeView";
21+
22+
final String firoWalletId;
23+
final bool popTxidOnSuccess;
24+
25+
@override
26+
ConsumerState<CreateMasternodeView> createState() =>
27+
_CreateMasternodeDialogState();
28+
}
29+
30+
class _CreateMasternodeDialogState extends ConsumerState<CreateMasternodeView> {
31+
@override
32+
Widget build(BuildContext context) {
33+
return ConditionalParent(
34+
condition: Util.isDesktop,
35+
builder: (child) => SizedBox(
36+
width: 660,
37+
child: Column(
38+
crossAxisAlignment: CrossAxisAlignment.stretch,
39+
mainAxisSize: MainAxisSize.min,
40+
children: [
41+
Row(
42+
mainAxisAlignment: .spaceBetween,
43+
children: [
44+
Padding(
45+
padding: const EdgeInsets.only(left: 32),
46+
child: Text(
47+
"Create masternode",
48+
style: STextStyles.desktopH3(context),
49+
),
50+
),
51+
const DesktopDialogCloseButton(),
52+
],
53+
),
54+
Flexible(
55+
child: Padding(
56+
padding: const EdgeInsets.only(left: 32, bottom: 32, right: 32),
57+
child: child,
58+
),
59+
),
60+
],
61+
),
62+
),
63+
child: ConditionalParent(
64+
condition: !Util.isDesktop,
65+
builder: (child) => Background(
66+
child: Scaffold(
67+
backgroundColor: Theme.of(
68+
context,
69+
).extension<StackColors>()!.background,
70+
appBar: AppBar(
71+
backgroundColor: Theme.of(
72+
context,
73+
).extension<StackColors>()!.background,
74+
leading: AppBarBackButton(
75+
onPressed: () async {
76+
Navigator.of(context).pop();
77+
},
78+
),
79+
title: Text(
80+
"Create masternode",
81+
style: STextStyles.navBarTitle(context),
82+
),
83+
),
84+
body: SafeArea(
85+
child: LayoutBuilder(
86+
builder: (context, constraints) {
87+
return Padding(
88+
padding: const EdgeInsets.symmetric(horizontal: 16),
89+
child: SingleChildScrollView(
90+
child: ConstrainedBox(
91+
constraints: BoxConstraints(
92+
minHeight: constraints.maxHeight,
93+
),
94+
child: IntrinsicHeight(
95+
child: Padding(
96+
padding: const EdgeInsets.only(bottom: 16),
97+
child: child,
98+
),
99+
),
100+
),
101+
),
102+
);
103+
},
104+
),
105+
),
106+
),
107+
),
108+
child: RegisterMasternodeForm(
109+
firoWalletId: widget.firoWalletId,
110+
onRegistrationSuccess: (txid) {
111+
if (widget.popTxidOnSuccess && mounted) {
112+
Navigator.of(context, rootNavigator: Util.isDesktop).pop(txid);
113+
}
114+
},
115+
),
116+
),
117+
);
118+
}
119+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import 'package:flutter/material.dart';
2+
3+
import '../../themes/stack_colors.dart';
4+
import '../../utilities/text_styles.dart';
5+
import '../../wallets/wallet/impl/firo_wallet.dart';
6+
import '../../widgets/background.dart';
7+
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
8+
import 'sub_widgets/masternode_info_widget.dart';
9+
10+
class MasternodeDetailsView extends StatelessWidget {
11+
const MasternodeDetailsView({super.key, required this.node});
12+
13+
static const String routeName = "/masternodeDetailsView";
14+
15+
final MasternodeInfo node;
16+
17+
@override
18+
Widget build(BuildContext context) {
19+
return Background(
20+
child: Scaffold(
21+
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
22+
appBar: AppBar(
23+
leading: const AppBarBackButton(),
24+
title: Text(
25+
"Masternode details",
26+
style: STextStyles.navBarTitle(context),
27+
),
28+
),
29+
body: SafeArea(
30+
child: LayoutBuilder(
31+
builder: (context, constraints) {
32+
return Padding(
33+
padding: const EdgeInsets.symmetric(horizontal: 16),
34+
child: SingleChildScrollView(
35+
child: ConstrainedBox(
36+
constraints: BoxConstraints(
37+
minHeight: constraints.maxHeight,
38+
),
39+
child: IntrinsicHeight(
40+
child: Column(
41+
mainAxisSize: .min,
42+
children: [
43+
MasternodeInfoWidget(info: node),
44+
const SizedBox(height: 16),
45+
],
46+
),
47+
),
48+
),
49+
),
50+
);
51+
},
52+
),
53+
),
54+
),
55+
);
56+
}
57+
}

0 commit comments

Comments
 (0)