diff --git a/.github/workflows/no_http_imports.yaml b/.github/workflows/no_http_imports.yaml
new file mode 100644
index 000000000..dad6821ac
--- /dev/null
+++ b/.github/workflows/no_http_imports.yaml
@@ -0,0 +1,21 @@
+name: No http imports
+
+on: [pull_request]
+
+jobs:
+ PR_test_build:
+ runs-on: ubuntu-24.04
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Check for http package usage
+ if: github.event_name == 'pull_request'
+ run: |
+ GIT_GREP_OUT="$(git grep package:http | (grep .dart: || test $? = 1) | (grep -v proxy_wrapper.dart || test $? = 1) | (grep -v very_insecure_http_do_not_use || test $? = 1) || true)"
+ [[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
+ echo "$GIT_GREP_OUT"
+ echo "There are .dart files which use http imports"
+ echo "Using http package breaks proxy integration"
+ echo "Please use ProxyWrapper.getHttpClient() from package:cw_core/utils/proxy_wrapper.dart"
+ exit 1
+
\ No newline at end of file
diff --git a/.github/workflows/no_print_in_dart.yaml b/.github/workflows/no_print_in_dart.yaml
index 9c3d82bc2..507793bd8 100644
--- a/.github/workflows/no_print_in_dart.yaml
+++ b/.github/workflows/no_print_in_dart.yaml
@@ -15,5 +15,5 @@ jobs:
[[ "x$GIT_GREP_OUT" == "x" ]] && exit 0
echo "$GIT_GREP_OUT"
echo "There are .dart files which use print() statements"
- echo "Please use printV from package: cw_core/utils/print_verbose.dart"
+ echo "Please use printV from package:cw_core/utils/print_verbose.dart"
exit 1
diff --git a/assets/images/tor_logo.svg b/assets/images/tor_logo.svg
new file mode 100644
index 000000000..ebd00324d
--- /dev/null
+++ b/assets/images/tor_logo.svg
@@ -0,0 +1,76 @@
+
+
+
+
diff --git a/cw_bitcoin/lib/electrum.dart b/cw_bitcoin/lib/electrum.dart
index 1f5c369e3..2ddd30df6 100644
--- a/cw_bitcoin/lib/electrum.dart
+++ b/cw_bitcoin/lib/electrum.dart
@@ -5,6 +5,8 @@ import 'dart:typed_data';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_core/utils/print_verbose.dart';
+import 'package:cw_core/utils/proxy_socket/abstract.dart';
+import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:flutter/foundation.dart';
import 'package:rxdart/rxdart.dart';
@@ -42,7 +44,7 @@ class ElectrumClient {
static const aliveTimerDuration = Duration(seconds: 4);
bool get isConnected => _isConnected;
- Socket? socket;
+ ProxySocket? socket;
void Function(ConnectionStatus)? onConnectionStatusChange;
int _id;
final Map _tasks;
@@ -72,18 +74,11 @@ class ElectrumClient {
} catch (_) {}
socket = null;
+ final ssl = !(useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum")));
try {
- if (useSSL == false || (useSSL == null && uri.toString().contains("btc-electrum"))) {
- socket = await Socket.connect(host, port, timeout: connectionTimeout);
- } else {
- socket = await SecureSocket.connect(
- host,
- port,
- timeout: connectionTimeout,
- onBadCertificate: (_) => true,
- );
- }
+ socket = await ProxyWrapper().getSocksSocket(ssl, host, port, connectionTimeout: connectionTimeout);
} catch (e) {
+ printV("connect: $e");
if (e is HandshakeException) {
useSSL = !(useSSL ?? false);
}
@@ -105,7 +100,6 @@ class ElectrumClient {
// use ping to determine actual connection status since we could've just not timed out yet:
// _setConnectionStatus(ConnectionStatus.connected);
-
socket!.listen(
(Uint8List event) {
try {
diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart
index 35c15682c..bb9cea1bc 100644
--- a/cw_bitcoin/lib/electrum_wallet.dart
+++ b/cw_bitcoin/lib/electrum_wallet.dart
@@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:isolate';
import 'package:bitcoin_base/bitcoin_base.dart';
+import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_core/format_amount.dart';
import 'package:cw_core/utils/print_verbose.dart';
@@ -49,7 +50,6 @@ import 'package:mobx/mobx.dart';
import 'package:rxdart/subjects.dart';
import 'package:sp_scanner/sp_scanner.dart';
import 'package:hex/hex.dart';
-import 'package:http/http.dart' as http;
part 'electrum_wallet.g.dart';
@@ -493,10 +493,9 @@ abstract class ElectrumWalletBase
Future updateFeeRates() async {
if (await checkIfMempoolAPIIsEnabled() && type == WalletType.bitcoin) {
try {
- final response = await http
- .get(Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
- .timeout(Duration(seconds: 5));
-
+ final response = await ProxyWrapper()
+ .get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/fees/recommended"))
+ .timeout(Duration(seconds: 15));
final result = json.decode(response.body) as Map;
final slowFee = (result['economyFee'] as num?)?.toInt() ?? 0;
int mediumFee = (result['hourFee'] as num?)?.toInt() ?? 0;
@@ -1176,20 +1175,18 @@ abstract class ElectrumWalletBase
}
});
- return PendingBitcoinTransaction(
- transaction,
- type,
- electrumClient: electrumClient,
- amount: estimatedTx.amount,
- fee: estimatedTx.fee,
- feeRate: feeRateInt.toString(),
- network: network,
- hasChange: estimatedTx.hasChange,
- isSendAll: estimatedTx.isSendAll,
- hasTaprootInputs: hasTaprootInputs,
- utxos: estimatedTx.utxos,
- publicKeys: estimatedTx.publicKeys
- )..addListener((transaction) async {
+ return PendingBitcoinTransaction(transaction, type,
+ electrumClient: electrumClient,
+ amount: estimatedTx.amount,
+ fee: estimatedTx.fee,
+ feeRate: feeRateInt.toString(),
+ network: network,
+ hasChange: estimatedTx.hasChange,
+ isSendAll: estimatedTx.isSendAll,
+ hasTaprootInputs: hasTaprootInputs,
+ utxos: estimatedTx.utxos,
+ publicKeys: estimatedTx.publicKeys)
+ ..addListener((transaction) async {
transactionHistory.addOne(transaction);
if (estimatedTx.spendsSilentPayment) {
transactionHistory.transactions.values.forEach((tx) {
@@ -1880,20 +1877,17 @@ abstract class ElectrumWalletBase
if (height != null && height > 0 && await checkIfMempoolAPIIsEnabled()) {
try {
- final blockHash = await http.get(
- Uri.parse(
- "https://mempool.cakewallet.com/api/v1/block-height/$height",
- ),
- );
+ final blockHash = await ProxyWrapper()
+ .get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block-height/$height"))
+ .timeout(Duration(seconds: 15));
if (blockHash.statusCode == 200 &&
blockHash.body.isNotEmpty &&
jsonDecode(blockHash.body) != null) {
- final blockResponse = await http.get(
- Uri.parse(
- "https://mempool.cakewallet.com/api/v1/block/${blockHash.body}",
- ),
- );
+ final blockResponse = await ProxyWrapper()
+ .get(clearnetUri: Uri.parse("https://mempool.cakewallet.com/api/v1/block/${blockHash}"))
+ .timeout(Duration(seconds: 15));
+
if (blockResponse.statusCode == 200 &&
blockResponse.body.isNotEmpty &&
jsonDecode(blockResponse.body)['timestamp'] != null) {
diff --git a/cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart b/cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart
index e4cd8a101..c56148de2 100644
--- a/cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart
+++ b/cw_bitcoin/lib/payjoin/payjoin_receive_worker.dart
@@ -8,11 +8,12 @@ import 'package:cw_bitcoin/payjoin/manager.dart';
import 'package:cw_bitcoin/payjoin/payjoin_session_errors.dart';
import 'package:cw_bitcoin/psbt/signer.dart';
import 'package:cw_core/utils/print_verbose.dart';
-import 'package:http/http.dart' as http;
+import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:payjoin_flutter/bitcoin_ffi.dart';
import 'package:payjoin_flutter/common.dart';
import 'package:payjoin_flutter/receive.dart';
import 'package:payjoin_flutter/src/generated/frb_generated.dart' as pj;
+import 'package:http/http.dart' as very_insecure_http_do_not_use; // for errors
enum PayjoinReceiverRequestTypes {
processOriginalTx,
@@ -28,7 +29,7 @@ class PayjoinReceiverWorker {
final pendingRequests = >{};
PayjoinReceiverWorker._(this.sendPort);
-
+ static final client = ProxyWrapper().getHttpIOClient();
static Future run(List