Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use mempoolInstance across the app. #684

Merged
merged 3 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions lib/bloc/network/network_settings_bloc.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import 'dart:convert';
import 'package:c_breez/bloc/network/network_settings_state.dart';
import 'package:c_breez/config.dart' as lib;
import 'package:c_breez/config.dart';
import 'package:c_breez/services/injector.dart';
import 'package:c_breez/utils/blockchain_explorer_utils.dart';
import 'package:c_breez/utils/preferences.dart';
import 'package:logging/logging.dart';
import 'package:http/http.dart' as http;
Expand Down Expand Up @@ -47,7 +51,7 @@ class NetworkSettingsBloc extends Cubit<NetworkSettingsState> with HydratedMixin
_log.warning("Invalid mempool url: $mempoolUrl");
return false;
}
if (!await _testUri(uri)) {
if (!await _testUriSupportsMempoolApi(uri)) {
_log.warning("Mempool url is not reachable: $mempoolUrl");
return false;
}
Expand Down Expand Up @@ -83,10 +87,28 @@ class NetworkSettingsBloc extends Cubit<NetworkSettingsState> with HydratedMixin
));
}

Future<bool> _testUri(Uri uri) async {
Future<String> get mempoolInstance async {
String? mempoolInstance = await ServiceInjector().preferences.getMempoolSpaceUrl();
if (mempoolInstance == null) {
final config = await Config.instance();
mempoolInstance = config.defaultMempoolUrl;
}
return mempoolInstance;
}

Future<bool> _testUriSupportsMempoolApi(Uri uri) async {
// We need to make sure that the mempool rest api is supported
// as the sdk depends on it.
final mempoolUri =
Uri.tryParse(BlockChainExplorerUtils().formatRecommendedFeesUrl(mempoolInstance: uri.toString()));
if (mempoolUri == null) return false;
try {
final response = await _httpClient.get(uri);
return response.statusCode < 400;
final response = await _httpClient.get(mempoolUri);
if (response.statusCode != 200) {
return false;
}
final Map<String, dynamic> body = jsonDecode(response.body);
return body.containsKey("fastestFee");
} catch (e) {
_log.warning("Failed to test mempool url: $uri", e);
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:breez_sdk/bridge_generated.dart' as sdk;
import 'package:breez_translations/breez_translations_locales.dart';
import 'package:c_breez/config.dart';
import 'package:c_breez/bloc/network/network_settings_bloc.dart';
import 'package:c_breez/models/payment_minutiae.dart';
import 'package:c_breez/routes/home/widgets/payments_list/dialog/tx_widget.dart';
import 'package:c_breez/utils/blockchain_explorer_utils.dart';
import 'package:c_breez/widgets/loader.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class ClosedChannelPaymentDetailsWidget extends StatelessWidget {
final PaymentMinutiae paymentMinutiae;
Expand All @@ -18,61 +20,66 @@ class ClosedChannelPaymentDetailsWidget extends StatelessWidget {
Widget build(BuildContext context) {
final themeData = Theme.of(context);
final texts = context.texts();
return FutureBuilder<Config>(
future: Config.instance(),
builder: (BuildContext context, AsyncSnapshot<Config> snapshot) {
if (snapshot.hasData) {
final blockExplorer = snapshot.data!.defaultMempoolUrl;
ademar111190 marked this conversation as resolved.
Show resolved Hide resolved
if (paymentMinutiae.status == sdk.PaymentStatus.Complete) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
RichText(
text: TextSpan(
style: themeData.dialogTheme.contentTextStyle,
text: texts.payment_details_dialog_closed_channel_local_wallet,
),
),
if (paymentMinutiae.paymentType == sdk.PaymentType.ClosedChannel &&
paymentMinutiae.closingTxid != null) ...[
TxWidget(
txURL: "$blockExplorer/tx/${paymentMinutiae.closingTxid!}",
txID: paymentMinutiae.closingTxid!,
),
],
],
);
}
// TODO pendingExpirationHeight
// TODO hoursToExpire
String estimation = texts.payment_details_dialog_closed_channel_transfer_no_estimation;
final networkSettingsBloc = context.read<NetworkSettingsBloc>();

return FutureBuilder<String>(
future: networkSettingsBloc.mempoolInstance,
ubbabeck marked this conversation as resolved.
Show resolved Hide resolved
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const Loader();
}

final mempoolInstance = snapshot.data!;
if (paymentMinutiae.status == sdk.PaymentStatus.Complete) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
RichText(
text: TextSpan(
style: themeData.dialogTheme.contentTextStyle,
text: estimation,
text: texts.payment_details_dialog_closed_channel_local_wallet,
),
),
if (paymentMinutiae.fundingTxid != null) ...[
TxWidget(
txURL: "$blockExplorer/tx/${paymentMinutiae.fundingTxid!}",
txID: paymentMinutiae.fundingTxid!,
),
],
if (paymentMinutiae.closingTxid != null) ...[
if (paymentMinutiae.paymentType == sdk.PaymentType.ClosedChannel &&
paymentMinutiae.closingTxid != null) ...[
TxWidget(
txURL: "$blockExplorer/tx/${paymentMinutiae.closingTxid!}",
txURL: BlockChainExplorerUtils().formatTransactionUrl(
txid: paymentMinutiae.closingTxid!, mempoolInstance: mempoolInstance),
txID: paymentMinutiae.closingTxid!,
),
]
],
],
);
} else {
return const Loader();
}
// TODO pendingExpirationHeight
// TODO hoursToExpire
String estimation = texts.payment_details_dialog_closed_channel_transfer_no_estimation;

return Column(
mainAxisSize: MainAxisSize.min,
children: [
RichText(
text: TextSpan(
style: themeData.dialogTheme.contentTextStyle,
text: estimation,
),
),
if (paymentMinutiae.fundingTxid != null) ...[
TxWidget(
txURL: BlockChainExplorerUtils().formatTransactionUrl(
txid: paymentMinutiae.fundingTxid!, mempoolInstance: mempoolInstance),
txID: paymentMinutiae.fundingTxid!,
),
],
if (paymentMinutiae.closingTxid != null) ...[
TxWidget(
txURL: BlockChainExplorerUtils().formatTransactionUrl(
txid: paymentMinutiae.closingTxid!, mempoolInstance: mempoolInstance),
txID: paymentMinutiae.closingTxid!,
),
]
],
);
},
);
}
Expand Down
34 changes: 25 additions & 9 deletions lib/routes/subswap/swap/widgets/inprogress_swap.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import 'package:breez_sdk/bridge_generated.dart';
import 'package:breez_translations/breez_translations_locales.dart';
import 'package:c_breez/bloc/network/network_settings_bloc.dart';
import 'package:c_breez/services/injector.dart';
import 'package:c_breez/utils/blockchain_explorer_utils.dart';
import 'package:c_breez/widgets/flushbar.dart';
import 'package:c_breez/widgets/link_launcher.dart';
import 'package:c_breez/widgets/loader.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class SwapInprogress extends StatelessWidget {
final SwapInfo swap;
Expand Down Expand Up @@ -48,16 +52,28 @@ class _TxLink extends StatelessWidget {
@override
Widget build(BuildContext context) {
final text = context.texts();
final networkSettingsBloc = context.read<NetworkSettingsBloc>();

return LinkLauncher(
linkName: txid,
linkAddress: "https://blockstream.info/tx/$txid",
onCopy: () {
ServiceInjector().device.setClipboardText(txid);
showFlushbar(
context,
message: text.add_funds_transaction_id_copied,
duration: const Duration(seconds: 3),
return FutureBuilder(
future: networkSettingsBloc.mempoolInstance,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const Loader();
}
final mempoolInstance = snapshot.data!;

return LinkLauncher(
linkName: txid,
linkAddress:
BlockChainExplorerUtils().formatTransactionUrl(txid: txid, mempoolInstance: mempoolInstance),
onCopy: () {
ServiceInjector().device.setClipboardText(txid);
showFlushbar(
context,
message: text.add_funds_transaction_id_copied,
duration: const Duration(seconds: 3),
);
},
);
},
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'package:breez_sdk/bridge_generated.dart' as sdk;
import 'package:breez_translations/breez_translations_locales.dart';
import 'package:c_breez/config.dart';
import 'package:c_breez/bloc/network/network_settings_bloc.dart';
import 'package:c_breez/services/injector.dart';
import 'package:c_breez/utils/blockchain_explorer_utils.dart';
import 'package:c_breez/widgets/flushbar.dart';
import 'package:c_breez/widgets/link_launcher.dart';
import 'package:c_breez/widgets/loader.dart';
Expand Down Expand Up @@ -46,19 +47,21 @@ class _TxLink extends StatelessWidget {
@override
Widget build(BuildContext context) {
final text = context.texts();
final networkSettingsBloc = context.read<NetworkSettingsBloc>();

return FutureBuilder(
future: Config.instance(),
future: networkSettingsBloc.mempoolInstance,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const Loader();
}

final blockExplorer = snapshot.data!.defaultMempoolUrl;
final transactionUrl =
BlockChainExplorerUtils().formatTransactionUrl(mempoolInstance: snapshot.data!, txid: txid);

return LinkLauncher(
linkName: txid,
linkAddress: "$blockExplorer/tx/$txid",
linkAddress: transactionUrl,
onCopy: () {
ServiceInjector().device.setClipboardText(txid);
showFlushbar(
Expand Down
9 changes: 9 additions & 0 deletions lib/utils/blockchain_explorer_utils.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class BlockChainExplorerUtils {
String formatTransactionUrl({required String txid, required String mempoolInstance}) {
return "$mempoolInstance/tx/$txid";
}

String formatRecommendedFeesUrl({required String mempoolInstance}) {
return "$mempoolInstance/api/v1/fees/recommended";
}
}
33 changes: 29 additions & 4 deletions test/bloc/network/network_settings_bloc_test.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import 'dart:convert';

import 'package:c_breez/bloc/network/network_settings_bloc.dart';
import 'package:c_breez/bloc/network/network_settings_state.dart';
import 'package:c_breez/services/injector.dart';
import 'package:c_breez/utils/blockchain_explorer_utils.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
Expand All @@ -12,6 +15,17 @@ import '../../unit_logger.dart';
import '../../utils/fake_path_provider_platform.dart';
import '../../utils/hydrated_bloc_storage.dart';

String get _recomendedMockFeesResponse {
final Map<String, dynamic> recomendedFees = {
"fastestFee": 1,
"halfHourFee": 1,
"hourFee": 1,
"economyFee": 1,
"minimumFee": 1
};
return jsonEncode(recomendedFees);
}

void main() {
TestWidgetsFlutterBinding.ensureInitialized();
final platform = FakePathProviderPlatform();
Expand Down Expand Up @@ -75,17 +89,24 @@ void main() {

test('set mempool space url with a valid url should set on the preferences', () async {
const url = "https://mempool.space";

httpClient.getAnswer[url] = http.Response("{}", 200);
final bloc = make();

httpClient.getAnswer[BlockChainExplorerUtils().formatRecommendedFeesUrl(mempoolInstance: url)] =
http.Response(_recomendedMockFeesResponse, 200);
final result = await bloc.setMempoolUrl(url);
expect(result, true);
expect(injector.preferencesMock.setMempoolSpaceUrlUrl, url);
});

test('set mempool space url with a valid url missing scheme should set on the preferences', () async {
const url = "mempool.space";
httpClient.getAnswer["https://$url"] = http.Response("{}", 200);

final bloc = make();
httpClient.getAnswer[
"https://${BlockChainExplorerUtils().formatRecommendedFeesUrl(mempoolInstance: url)}"] =
http.Response(_recomendedMockFeesResponse, 200);
final result = await bloc.setMempoolUrl(url);
expect(result, true);
expect(injector.preferencesMock.setMempoolSpaceUrlUrl, "https://$url");
Expand All @@ -101,26 +122,30 @@ void main() {

test('set mempool space url with an ip should should set on the preferences', () async {
const url = "https://192.168.15.2";
httpClient.getAnswer[url] = http.Response("{}", 200);
final bloc = make();
httpClient.getAnswer[BlockChainExplorerUtils().formatRecommendedFeesUrl(mempoolInstance: url)] =
http.Response(_recomendedMockFeesResponse, 200);
final result = await bloc.setMempoolUrl(url);
expect(result, true);
expect(injector.preferencesMock.setMempoolSpaceUrlUrl, url);
});

test('set mempool space url with an ip and port should should set on the preferences', () async {
const url = "https://192.168.15.2:3006";
httpClient.getAnswer[url] = http.Response("{}", 200);
final bloc = make();
httpClient.getAnswer[BlockChainExplorerUtils().formatRecommendedFeesUrl(mempoolInstance: url)] =
http.Response(_recomendedMockFeesResponse, 200);
final result = await bloc.setMempoolUrl(url);
expect(result, true);
expect(injector.preferencesMock.setMempoolSpaceUrlUrl, url);
});

test('set mempool space url with an ip missing scheme should should set on the preferences', () async {
const url = "192.168.15.2";
httpClient.getAnswer["https://$url"] = http.Response("{}", 200);
final bloc = make();
httpClient.getAnswer[
"https://${BlockChainExplorerUtils().formatRecommendedFeesUrl(mempoolInstance: url)}"] =
http.Response(_recomendedMockFeesResponse, 200);
final result = await bloc.setMempoolUrl(url);
expect(result, true);
expect(injector.preferencesMock.setMempoolSpaceUrlUrl, "https://$url");
Expand Down
Loading