From 3b538c919d5b10851dfe3ce2203d18f98a75e915 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 23 Apr 2024 22:15:21 -0500 Subject: [PATCH 1/2] feat: proxy (Tor/SOCKS5) support: add optional proxy parameter add optional proxy param to TezartClient, RpcInterface, etc --- lib/src/core/client/impl/tezart_client.dart | 29 +++--- lib/src/core/rpc/impl/rpc_interface.dart | 91 +++++++++++++------ lib/src/core/rpc/impl/tezart_http_client.dart | 42 ++++++--- 3 files changed, 105 insertions(+), 57 deletions(-) diff --git a/lib/src/core/client/impl/tezart_client.dart b/lib/src/core/client/impl/tezart_client.dart index 40cd5ecd..03baaa0c 100644 --- a/lib/src/core/client/impl/tezart_client.dart +++ b/lib/src/core/client/impl/tezart_client.dart @@ -23,7 +23,8 @@ class TezartClient { final RpcInterface rpcInterface; /// Default constructor. - TezartClient(String url) : rpcInterface = RpcInterface(url); + TezartClient(String url, {String? proxy}) + : rpcInterface = RpcInterface(url, proxy: proxy); /// Returns an [OperationsList] containing a [TransactionOperation] that transfers [amount] from [source] /// to [destination] and returns the operation group id.\ @@ -51,18 +52,20 @@ class TezartClient { bool reveal = true, }) async { return _catchHttpError(() async { - log.info('request transfer $amount µtz from $source.address to the destination $destination'); - - final operationsList = OperationsList(source: source, rpcInterface: rpcInterface) - ..appendOperation( - TransactionOperation( - amount: amount, - destination: destination, - customFee: customFee, - customGasLimit: customGasLimit, - customStorageLimit: customStorageLimit, - ), - ); + log.info( + 'request transfer $amount µtz from $source.address to the destination $destination'); + + final operationsList = + OperationsList(source: source, rpcInterface: rpcInterface) + ..appendOperation( + TransactionOperation( + amount: amount, + destination: destination, + customFee: customFee, + customGasLimit: customGasLimit, + customStorageLimit: customStorageLimit, + ), + ); if (reveal) { await _prependRevealIfNotRevealed( operationsList, diff --git a/lib/src/core/rpc/impl/rpc_interface.dart b/lib/src/core/rpc/impl/rpc_interface.dart index 920ac031..b02b301c 100644 --- a/lib/src/core/rpc/impl/rpc_interface.dart +++ b/lib/src/core/rpc/impl/rpc_interface.dart @@ -1,12 +1,13 @@ import 'dart:async'; import 'dart:convert'; + import 'package:logging/logging.dart'; import 'package:memoize/memoize.dart'; import 'package:tezart/src/core/rpc/impl/operations_monitor.dart'; import 'package:tezart/src/models/operations_list/operations_list.dart'; -import 'tezart_http_client.dart'; import 'rpc_interface_paths.dart' as paths; +import 'tezart_http_client.dart'; /// A class that handles the calls to a Tezos RPC Node /// @@ -17,12 +18,14 @@ class RpcInterface { final TezartHttpClient httpClient; final log = Logger('RpcInterface'); - RpcInterface(String url) : httpClient = TezartHttpClient(url); + RpcInterface(String url, {String? proxy}) + : httpClient = TezartHttpClient(url, proxy: proxy); /// Returns the block's hash of [chain] and [level] Future branch([chain = 'main', level = 'head']) async { log.info('request for branch [ chain:$chain, level:$level]'); - var response = await httpClient.get(paths.branch(chain: chain, level: level)); + var response = + await httpClient.get(paths.branch(chain: chain, level: level)); return response.data; } @@ -38,7 +41,8 @@ class RpcInterface { /// Returns the protocol of [chain] and [level] Future protocol([chain = 'main', level = 'head']) async { log.info('request for protocol [ chain:$chain, level:$level]'); - var response = await httpClient.get(paths.protocol(chain: chain, level: level)); + var response = + await httpClient.get(paths.protocol(chain: chain, level: level)); return response.data['protocol']; } @@ -46,7 +50,8 @@ class RpcInterface { /// Returns the counter of [source] in the chain defined by [chain] and [level] Future counter(String source, [chain = 'main', level = 'head']) async { log.info('request for counter [ chain:$chain, level:$level]'); - final response = await httpClient.get(paths.counter(source: source, chain: chain, level: level)); + final response = await httpClient + .get(paths.counter(source: source, chain: chain, level: level)); return int.parse(response.data); } @@ -62,21 +67,28 @@ class RpcInterface { /// Injects the forged operation [data] in [chain] and returns the operation id Future injectOperation(String data, [chain = 'main']) async { log.info('request for injectOperation [ chain:$chain]'); - final response = await httpClient.post(paths.injectOperation(chain), data: jsonEncode(data)); + final response = await httpClient.post(paths.injectOperation(chain), + data: jsonEncode(data)); return response.data; } /// Returns the forged operation of [operationsList] in the chain defined by [chain] and [level] - Future forgeOperations(OperationsList operationsList, [chain = 'main', level = 'head']) async { + Future forgeOperations(OperationsList operationsList, + [chain = 'main', level = 'head']) async { log.info('request for forgeOperations [ chain:$chain, level:$level]'); var content = { 'branch': await branch(), - 'contents': operationsList.operations.map((operation) => operation.toJson()).toList(), + 'contents': operationsList.operations + .map((operation) => operation.toJson()) + .toList(), }; - return memo1, Future>((Map content) async { - final response = await httpClient.post(paths.forgeOperations(chain: chain, level: level), data: content); + return memo1, Future>( + (Map content) async { + final response = await httpClient.post( + paths.forgeOperations(chain: chain, level: level), + data: content); return response.data; })(content); } @@ -92,7 +104,9 @@ class RpcInterface { final content = [ { 'branch': await branch(), - 'contents': operationsList.operations.map((operation) => operation.toJson()).toList(), + 'contents': operationsList.operations + .map((operation) => operation.toJson()) + .toList(), 'signature': signature, 'protocol': await protocol(chain, level), } @@ -111,18 +125,22 @@ class RpcInterface { } /// Same as [preapplyOperations] but uses a random signature - Future> runOperations(OperationsList operationsList, [chain = 'main', level = 'head']) async { + Future> runOperations(OperationsList operationsList, + [chain = 'main', level = 'head']) async { log.info('request for runOperations [ chain:$chain, level:$level]'); var content = { 'operation': { 'branch': await branch(), - 'contents': operationsList.operations.map((operation) => operation.toJson()).toList(), + 'contents': operationsList.operations + .map((operation) => operation.toJson()) + .toList(), 'signature': _randomSignature }, 'chain_id': await chainId() }; - var response = await httpClient.post(paths.runOperations(chain: chain, level: level), data: content); + var response = await httpClient + .post(paths.runOperations(chain: chain, level: level), data: content); return response.data['contents']; } @@ -130,9 +148,11 @@ class RpcInterface { /// Returns the public key of [address] /// /// If the address is unknown by the node, it returns null - Future managerKey(String address, [chain = 'main', level = 'head']) async { + Future managerKey(String address, + [chain = 'main', level = 'head']) async { log.info('request for managerKey [ chain:$chain, level:$level]'); - var response = await httpClient.get(paths.managerKey(address: address, chain: chain, level: level)); + var response = await httpClient + .get(paths.managerKey(address: address, chain: chain, level: level)); return response.data; } @@ -140,22 +160,26 @@ class RpcInterface { /// Returns the balance of [address] in the chain defined by [chain] and [level] Future balance(String address, [chain = 'main', level = 'head']) async { log.info('request for balance [ chain:$chain, level:$level]'); - var response = await httpClient.get(paths.balance(chain: chain, level: level, address: address)); + var response = await httpClient + .get(paths.balance(chain: chain, level: level, address: address)); return int.parse(response.data['balance']); } /// Returns the complete status of the contract whom address is [address] - Future> getContract(String address, [chain = 'main', level = 'head']) async { + Future> getContract(String address, + [chain = 'main', level = 'head']) async { log.info('request for contract : $address'); - var response = await httpClient.get(paths.contract(chain: chain, level: level, contractAddress: address)); + var response = await httpClient.get( + paths.contract(chain: chain, level: level, contractAddress: address)); return response.data; } /// Returns a map containing the entrypoints and their types of a contract defined by [address] - Future> getContractEntrypoints(String address, [chain = 'main', level = 'head']) async { + Future> getContractEntrypoints(String address, + [chain = 'main', level = 'head']) async { log.info('request for contract entrypoints : $address'); return memo1>>((String address) async { @@ -178,7 +202,8 @@ class RpcInterface { }) async { log.info('request for contract : $address, entrypoint: $entrypoint'); - return memo2>>((String address, String entrypoint) async { + return memo2>>( + (String address, String entrypoint) async { var response = await httpClient.get(paths.contractEntrypoint( chain: chain, level: level, @@ -212,23 +237,30 @@ class RpcInterface { chain = 'main', level = 'head', }) async { - return _operationsMonitor.monitor(chain: chain, level: level, operationId: operationId); + return _operationsMonitor.monitor( + chain: chain, level: level, operationId: operationId); } - OperationsMonitor get _operationsMonitor => memo0(() => OperationsMonitor(this))(); + OperationsMonitor get _operationsMonitor => + memo0(() => OperationsMonitor(this))(); /// Returns the block hash of [chain] and [level] - Future> block({required String chain, required String level}) async { - final response = await httpClient.get(paths.block(chain: chain, level: level)); + Future> block( + {required String chain, required String level}) async { + final response = + await httpClient.get(paths.block(chain: chain, level: level)); return response.data; } /// Returns the constants of the chain defined by [chain] and [level] - Future> constants([chain = 'main', level = 'head']) async { - return memo2>>((String chain, String level) async { + Future> constants( + [chain = 'main', level = 'head']) async { + return memo2>>( + (String chain, String level) async { log.info('request to constants'); - final response = await httpClient.get(paths.constants(chain: chain, level: level)); + final response = + await httpClient.get(paths.constants(chain: chain, level: level)); return response.data; })(chain, level); @@ -248,7 +280,8 @@ class RpcInterface { 'data': data, 'type': type, }; - final response = await httpClient.post(paths.pack(chain: chain, level: level), data: content); + final response = await httpClient + .post(paths.pack(chain: chain, level: level), data: content); return response.data['packed']; } diff --git a/lib/src/core/rpc/impl/tezart_http_client.dart b/lib/src/core/rpc/impl/tezart_http_client.dart index 10000102..11ab2992 100644 --- a/lib/src/core/rpc/impl/tezart_http_client.dart +++ b/lib/src/core/rpc/impl/tezart_http_client.dart @@ -1,8 +1,9 @@ import 'dart:io'; +import 'package:dio/adapter.dart'; import 'package:dio/dio.dart' as http_client; -import 'package:pretty_dio_logger/pretty_dio_logger.dart'; import 'package:logging/logging.dart'; +import 'package:pretty_dio_logger/pretty_dio_logger.dart'; import 'package:retry/retry.dart'; import 'tezart_http_error.dart'; @@ -13,26 +14,36 @@ class TezartHttpClient { final String url; // Add client as optional parameter for testing - TezartHttpClient(this.url, {http_client.Dio? client}) { + TezartHttpClient(this.url, {http_client.Dio? client, String? proxy}) { // ensure that the url ends with '/' (double / is ok) final baseUrl = '$url/'; if (client != null) { this.client = client; this.client.options.baseUrl = baseUrl; - return; - } + } else { + var options = http_client.BaseOptions( + baseUrl: baseUrl, contentType: 'application/json'); - final options = http_client.BaseOptions(baseUrl: baseUrl, contentType: 'application/json'); - this.client = http_client.Dio(options); - this.client.interceptors.add(PrettyDioLogger( - logPrint: log.finest, - requestHeader: true, - requestBody: true, - responseBody: true, - responseHeader: false, - compact: false, - )); + this.client = http_client.Dio(options); + this.client.interceptors.add(PrettyDioLogger( + logPrint: log.finest, + requestHeader: true, + requestBody: true, + responseBody: true, + responseHeader: false, + compact: false, + )); + + if (proxy != null) { + this.client.httpClientAdapter = DefaultHttpClientAdapter() + ..onHttpClientCreate = (HttpClient httpClient) { + httpClient.findProxy = (_) => 'PROXY $proxy'; + // Ignore SSL errors if required for development with self-signed certificates + httpClient.badCertificateCallback = (cert, host, port) => true; + }; + } + } } Future post(String path, {dynamic data}) { @@ -44,7 +55,8 @@ class TezartHttpClient { ); } - Future get(String path, {Map? params}) { + Future get(String path, + {Map? params}) { log.info('request to get from path: $path'); return _retryOnSocketException( From 4e80aa62745dc660b76a61191f6ba21576bfb285 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 24 Apr 2024 18:11:00 -0500 Subject: [PATCH 2/2] revert formatting/linting changes --- lib/src/core/client/impl/tezart_client.dart | 26 +++--- lib/src/core/rpc/impl/rpc_interface.dart | 88 ++++++------------- lib/src/core/rpc/impl/tezart_http_client.dart | 5 +- 3 files changed, 42 insertions(+), 77 deletions(-) diff --git a/lib/src/core/client/impl/tezart_client.dart b/lib/src/core/client/impl/tezart_client.dart index 03baaa0c..39859a7e 100644 --- a/lib/src/core/client/impl/tezart_client.dart +++ b/lib/src/core/client/impl/tezart_client.dart @@ -52,20 +52,18 @@ class TezartClient { bool reveal = true, }) async { return _catchHttpError(() async { - log.info( - 'request transfer $amount µtz from $source.address to the destination $destination'); - - final operationsList = - OperationsList(source: source, rpcInterface: rpcInterface) - ..appendOperation( - TransactionOperation( - amount: amount, - destination: destination, - customFee: customFee, - customGasLimit: customGasLimit, - customStorageLimit: customStorageLimit, - ), - ); + log.info('request transfer $amount µtz from $source.address to the destination $destination'); + + final operationsList = OperationsList(source: source, rpcInterface: rpcInterface) + ..appendOperation( + TransactionOperation( + amount: amount, + destination: destination, + customFee: customFee, + customGasLimit: customGasLimit, + customStorageLimit: customStorageLimit, + ), + ); if (reveal) { await _prependRevealIfNotRevealed( operationsList, diff --git a/lib/src/core/rpc/impl/rpc_interface.dart b/lib/src/core/rpc/impl/rpc_interface.dart index b02b301c..309785d1 100644 --- a/lib/src/core/rpc/impl/rpc_interface.dart +++ b/lib/src/core/rpc/impl/rpc_interface.dart @@ -1,13 +1,12 @@ import 'dart:async'; import 'dart:convert'; - import 'package:logging/logging.dart'; import 'package:memoize/memoize.dart'; import 'package:tezart/src/core/rpc/impl/operations_monitor.dart'; import 'package:tezart/src/models/operations_list/operations_list.dart'; -import 'rpc_interface_paths.dart' as paths; import 'tezart_http_client.dart'; +import 'rpc_interface_paths.dart' as paths; /// A class that handles the calls to a Tezos RPC Node /// @@ -24,8 +23,7 @@ class RpcInterface { /// Returns the block's hash of [chain] and [level] Future branch([chain = 'main', level = 'head']) async { log.info('request for branch [ chain:$chain, level:$level]'); - var response = - await httpClient.get(paths.branch(chain: chain, level: level)); + var response = await httpClient.get(paths.branch(chain: chain, level: level)); return response.data; } @@ -41,8 +39,7 @@ class RpcInterface { /// Returns the protocol of [chain] and [level] Future protocol([chain = 'main', level = 'head']) async { log.info('request for protocol [ chain:$chain, level:$level]'); - var response = - await httpClient.get(paths.protocol(chain: chain, level: level)); + var response = await httpClient.get(paths.protocol(chain: chain, level: level)); return response.data['protocol']; } @@ -50,8 +47,7 @@ class RpcInterface { /// Returns the counter of [source] in the chain defined by [chain] and [level] Future counter(String source, [chain = 'main', level = 'head']) async { log.info('request for counter [ chain:$chain, level:$level]'); - final response = await httpClient - .get(paths.counter(source: source, chain: chain, level: level)); + final response = await httpClient.get(paths.counter(source: source, chain: chain, level: level)); return int.parse(response.data); } @@ -67,28 +63,21 @@ class RpcInterface { /// Injects the forged operation [data] in [chain] and returns the operation id Future injectOperation(String data, [chain = 'main']) async { log.info('request for injectOperation [ chain:$chain]'); - final response = await httpClient.post(paths.injectOperation(chain), - data: jsonEncode(data)); + final response = await httpClient.post(paths.injectOperation(chain), data: jsonEncode(data)); return response.data; } /// Returns the forged operation of [operationsList] in the chain defined by [chain] and [level] - Future forgeOperations(OperationsList operationsList, - [chain = 'main', level = 'head']) async { + Future forgeOperations(OperationsList operationsList, [chain = 'main', level = 'head']) async { log.info('request for forgeOperations [ chain:$chain, level:$level]'); var content = { 'branch': await branch(), - 'contents': operationsList.operations - .map((operation) => operation.toJson()) - .toList(), + 'contents': operationsList.operations.map((operation) => operation.toJson()).toList(), }; - return memo1, Future>( - (Map content) async { - final response = await httpClient.post( - paths.forgeOperations(chain: chain, level: level), - data: content); + return memo1, Future>((Map content) async { + final response = await httpClient.post(paths.forgeOperations(chain: chain, level: level), data: content); return response.data; })(content); } @@ -104,9 +93,7 @@ class RpcInterface { final content = [ { 'branch': await branch(), - 'contents': operationsList.operations - .map((operation) => operation.toJson()) - .toList(), + 'contents': operationsList.operations.map((operation) => operation.toJson()).toList(), 'signature': signature, 'protocol': await protocol(chain, level), } @@ -125,22 +112,18 @@ class RpcInterface { } /// Same as [preapplyOperations] but uses a random signature - Future> runOperations(OperationsList operationsList, - [chain = 'main', level = 'head']) async { + Future> runOperations(OperationsList operationsList, [chain = 'main', level = 'head']) async { log.info('request for runOperations [ chain:$chain, level:$level]'); var content = { 'operation': { 'branch': await branch(), - 'contents': operationsList.operations - .map((operation) => operation.toJson()) - .toList(), + 'contents': operationsList.operations.map((operation) => operation.toJson()).toList(), 'signature': _randomSignature }, 'chain_id': await chainId() }; - var response = await httpClient - .post(paths.runOperations(chain: chain, level: level), data: content); + var response = await httpClient.post(paths.runOperations(chain: chain, level: level), data: content); return response.data['contents']; } @@ -148,11 +131,9 @@ class RpcInterface { /// Returns the public key of [address] /// /// If the address is unknown by the node, it returns null - Future managerKey(String address, - [chain = 'main', level = 'head']) async { + Future managerKey(String address, [chain = 'main', level = 'head']) async { log.info('request for managerKey [ chain:$chain, level:$level]'); - var response = await httpClient - .get(paths.managerKey(address: address, chain: chain, level: level)); + var response = await httpClient.get(paths.managerKey(address: address, chain: chain, level: level)); return response.data; } @@ -160,26 +141,22 @@ class RpcInterface { /// Returns the balance of [address] in the chain defined by [chain] and [level] Future balance(String address, [chain = 'main', level = 'head']) async { log.info('request for balance [ chain:$chain, level:$level]'); - var response = await httpClient - .get(paths.balance(chain: chain, level: level, address: address)); + var response = await httpClient.get(paths.balance(chain: chain, level: level, address: address)); return int.parse(response.data['balance']); } /// Returns the complete status of the contract whom address is [address] - Future> getContract(String address, - [chain = 'main', level = 'head']) async { + Future> getContract(String address, [chain = 'main', level = 'head']) async { log.info('request for contract : $address'); - var response = await httpClient.get( - paths.contract(chain: chain, level: level, contractAddress: address)); + var response = await httpClient.get(paths.contract(chain: chain, level: level, contractAddress: address)); return response.data; } /// Returns a map containing the entrypoints and their types of a contract defined by [address] - Future> getContractEntrypoints(String address, - [chain = 'main', level = 'head']) async { + Future> getContractEntrypoints(String address, [chain = 'main', level = 'head']) async { log.info('request for contract entrypoints : $address'); return memo1>>((String address) async { @@ -202,8 +179,7 @@ class RpcInterface { }) async { log.info('request for contract : $address, entrypoint: $entrypoint'); - return memo2>>( - (String address, String entrypoint) async { + return memo2>>((String address, String entrypoint) async { var response = await httpClient.get(paths.contractEntrypoint( chain: chain, level: level, @@ -237,30 +213,23 @@ class RpcInterface { chain = 'main', level = 'head', }) async { - return _operationsMonitor.monitor( - chain: chain, level: level, operationId: operationId); + return _operationsMonitor.monitor(chain: chain, level: level, operationId: operationId); } - OperationsMonitor get _operationsMonitor => - memo0(() => OperationsMonitor(this))(); + OperationsMonitor get _operationsMonitor => memo0(() => OperationsMonitor(this))(); /// Returns the block hash of [chain] and [level] - Future> block( - {required String chain, required String level}) async { - final response = - await httpClient.get(paths.block(chain: chain, level: level)); + Future> block({required String chain, required String level}) async { + final response = await httpClient.get(paths.block(chain: chain, level: level)); return response.data; } /// Returns the constants of the chain defined by [chain] and [level] - Future> constants( - [chain = 'main', level = 'head']) async { - return memo2>>( - (String chain, String level) async { + Future> constants([chain = 'main', level = 'head']) async { + return memo2>>((String chain, String level) async { log.info('request to constants'); - final response = - await httpClient.get(paths.constants(chain: chain, level: level)); + final response = await httpClient.get(paths.constants(chain: chain, level: level)); return response.data; })(chain, level); @@ -280,8 +249,7 @@ class RpcInterface { 'data': data, 'type': type, }; - final response = await httpClient - .post(paths.pack(chain: chain, level: level), data: content); + final response = await httpClient.post(paths.pack(chain: chain, level: level), data: content); return response.data['packed']; } diff --git a/lib/src/core/rpc/impl/tezart_http_client.dart b/lib/src/core/rpc/impl/tezart_http_client.dart index 11ab2992..588703c5 100644 --- a/lib/src/core/rpc/impl/tezart_http_client.dart +++ b/lib/src/core/rpc/impl/tezart_http_client.dart @@ -2,8 +2,8 @@ import 'dart:io'; import 'package:dio/adapter.dart'; import 'package:dio/dio.dart' as http_client; -import 'package:logging/logging.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; +import 'package:logging/logging.dart'; import 'package:retry/retry.dart'; import 'tezart_http_error.dart'; @@ -55,8 +55,7 @@ class TezartHttpClient { ); } - Future get(String path, - {Map? params}) { + Future get(String path, {Map? params}) { log.info('request to get from path: $path'); return _retryOnSocketException(