Skip to content
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
19 changes: 3 additions & 16 deletions integration_test/sqlite_transactions_test.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import 'package:bb_mobile/core/electrum/domain/value_objects/electrum_server_network.dart';
import 'package:bb_mobile/core/electrum/frameworks/drift/datasources/electrum_remote_datasource.dart';
import 'package:bb_mobile/core/electrum/frameworks/drift/models/electrum_server_model.dart';
import 'package:bb_mobile/core/storage/sqlite_database.dart';
import 'package:bb_mobile/core/transactions/bitcoin_transaction_repository.dart';
import 'package:bb_mobile/core/utils/constants.dart';
import 'package:bb_mobile/core/transactions/domain/transaction_port.dart';
import 'package:bb_mobile/locator.dart';
import 'package:bb_mobile/main.dart';
import 'package:flutter_test/flutter_test.dart';
Expand All @@ -13,16 +9,7 @@ Future<void> main({bool isInitialized = false}) async {
if (!isInitialized) await Bull.init();

final sqlite = locator<SqliteDatabase>();
final electrumDatasource = ElectrumRemoteDatasource(
server: ElectrumServerModel(
url: ApiServiceConstants.bbElectrumUrl,
network: ElectrumServerNetwork.bitcoinMainnet,
),
sqlite: sqlite,
);
final transactionRepository = BitcoinTransactionRepository(
electrumRemoteDatasource: electrumDatasource,
);
final transactionPort = locator<TransactionPort>();

const txid =
'ff47a0a1dfdcf68327242d2cbfb229a5ba7e3e67572c2d4f390c51b1a89d56e5';
Expand All @@ -39,7 +26,7 @@ Future<void> main({bool isInitialized = false}) async {
expect(sqliteTx, isNull);

// Fetch a transaction and cache it in sqlite if not present
final tx = await transactionRepository.fetch(txid: txid);
final tx = await transactionPort.fetch(txid: txid);
expect(tx.txid, txid);

// Ensure the tx is now stored in sqlite
Expand Down
56 changes: 56 additions & 0 deletions lib/core/blockchain/adapters/electrum_server_adapter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'package:bb_mobile/core/blockchain/domain/ports/electrum_server_port.dart';
import 'package:bb_mobile/core/electrum/domain/repositories/electrum_server_repository.dart';
import 'package:bb_mobile/core/electrum/domain/repositories/electrum_settings_repository.dart';
import 'package:bb_mobile/core/electrum/domain/value_objects/electrum_server_network.dart';
import 'package:bb_mobile/core/settings/data/settings_repository.dart';

class ElectrumServerAdapter implements ElectrumServerPort {
final ElectrumServerRepository _electrumServerRepository;
final ElectrumSettingsRepository _electrumSettingsRepository;
final SettingsRepository _settingsRepository;

ElectrumServerAdapter({
required ElectrumServerRepository electrumServerRepository,
required ElectrumSettingsRepository electrumSettingsRepository,
required SettingsRepository settingsRepository,
}) : _electrumServerRepository = electrumServerRepository,
_electrumSettingsRepository = electrumSettingsRepository,
_settingsRepository = settingsRepository;

@override
Future<List<ElectrumServer>> getElectrumServers({
required bool isTestnet,
required bool isLiquid,
}) async {
final network = ElectrumServerNetwork.fromEnvironment(
isTestnet: isTestnet,
isLiquid: isLiquid,
);

final (activeServers, settings, appSettings) = await (
_electrumServerRepository.fetchActiveServers(network: network),
_electrumSettingsRepository.fetchByNetwork(network),
_settingsRepository.fetch(),
).wait;

// Tor proxy applies to Bitcoin only, never Liquid.
final socks5Url = (appSettings.useTorProxy && !isLiquid)
? (settings.socks5 ?? '127.0.0.1:${appSettings.torProxyPort}')
: settings.socks5;

return activeServers
.map(
(e) => ElectrumServer(
url: e.url,
priority: e.priority,
retry: settings.retry,
timeout: settings.timeout,
stopGap: settings.stopGap,
validateDomain: settings.validateDomain,
socks5: socks5Url,
isCustom: e.isCustom,
),
)
.toList();
}
}
10 changes: 6 additions & 4 deletions lib/core/blockchain/blockchain_locator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import 'package:bb_mobile/core/blockchain/domain/ports/electrum_server_port.dart
import 'package:bb_mobile/core/blockchain/domain/repositories/liquid_blockchain_repository.dart';
import 'package:bb_mobile/core/blockchain/domain/usecases/broadcast_bitcoin_transaction_usecase.dart';
import 'package:bb_mobile/core/blockchain/domain/usecases/broadcast_liquid_transaction_usecase.dart';
import 'package:bb_mobile/core/blockchain/interface_adapters/adapters/electrum_server_adapter.dart';
import 'package:bb_mobile/core/electrum/application/usecases/get_electrum_servers_to_use_usecase.dart';
import 'package:bb_mobile/core/blockchain/adapters/electrum_server_adapter.dart';
import 'package:bb_mobile/core/electrum/domain/repositories/electrum_server_repository.dart';
import 'package:bb_mobile/core/electrum/domain/repositories/electrum_settings_repository.dart';
import 'package:bb_mobile/core/settings/data/settings_repository.dart';
import 'package:get_it/get_it.dart';

Expand Down Expand Up @@ -39,8 +40,9 @@ class BlockchainLocator {
static void registerPorts(GetIt locator) {
locator.registerLazySingleton<ElectrumServerPort>(
() => ElectrumServerAdapter(
getElectrumServersToUseUsecase:
locator<GetElectrumServersToUseUsecase>(),
electrumServerRepository: locator<ElectrumServerRepository>(),
electrumSettingsRepository: locator<ElectrumSettingsRepository>(),
settingsRepository: locator<SettingsRepository>(),
),
);
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ class DriftElectrumServerRepository implements ElectrumServerRepository {
.then((models) => models.map((e) => e.toEntity()).toList());
}

@override
Future<List<ElectrumServer>> fetchActiveServers({
required ElectrumServerNetwork network,
}) async {
final servers = await fetchAll(
isTestnet: network.isTestnet,
isLiquid: network.isLiquid,
);
final customServers = servers.where((s) => s.isCustom).toList();
final activeServers = customServers.isNotEmpty ? customServers : servers;
activeServers.sort((a, b) => a.priority.compareTo(b.priority));
return activeServers;
}

@override
Future<void> delete({required String url}) {
return _datasource.deleteServer(url);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:bb_mobile/core/electrum/domain/repositories/electrum_transaction_repository.dart';
import 'package:bb_mobile/core/electrum/frameworks/drift/datasources/electrum_remote_datasource.dart';
import 'package:bb_mobile/core/storage/tables/transactions_table.dart';
import 'package:bb_mobile/core/utils/bitcoin_tx.dart';

class DriftElectrumTransactionRepository
implements ElectrumTransactionRepository {
final ElectrumRemoteDatasource _datasource;

DriftElectrumTransactionRepository({
required ElectrumRemoteDatasource datasource,
}) : _datasource = datasource;

@override
Future<BitcoinTx> fetch({
required String serverUrl,
required String txid,
}) async {
final model = await _datasource.fetch(serverUrl: serverUrl, txid: txid);
return TransactionModelExtension.toEntity(model);
}
}
18 changes: 18 additions & 0 deletions lib/core/electrum/adapters/electrum_servers_adapter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:bb_mobile/core/electrum/domain/entities/electrum_server.dart';
import 'package:bb_mobile/core/electrum/domain/ports/electrum_servers_port.dart';
import 'package:bb_mobile/core/electrum/domain/repositories/electrum_server_repository.dart';
import 'package:bb_mobile/core/electrum/domain/value_objects/electrum_server_network.dart';

class ElectrumServersAdapter implements ElectrumServersPort {
final ElectrumServerRepository _repository;

ElectrumServersAdapter({required ElectrumServerRepository repository})
: _repository = repository;

@override
Future<List<ElectrumServer>> getServersToUse({
required ElectrumServerNetwork network,
}) {
return _repository.fetchActiveServers(network: network);
}
}
64 changes: 64 additions & 0 deletions lib/core/electrum/adapters/electrum_transaction_port_adapter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'package:bb_mobile/core/electrum/domain/ports/electrum_servers_port.dart';
import 'package:bb_mobile/core/electrum/domain/ports/environment_port.dart';
import 'package:bb_mobile/core/electrum/domain/repositories/electrum_transaction_repository.dart';
import 'package:bb_mobile/core/electrum/domain/value_objects/electrum_environment.dart';
import 'package:bb_mobile/core/electrum/domain/value_objects/electrum_server_network.dart';
import 'package:bb_mobile/core/transactions/adapters/transaction_mapper.dart';
import 'package:bb_mobile/core/transactions/domain/domain_errors.dart';
import 'package:bb_mobile/core/transactions/domain/entities/transaction.dart';
import 'package:bb_mobile/core/transactions/domain/transaction_port.dart';

/// Adapter implementing [TransactionPort] for the Electrum module.
///
/// Iterates the configured Electrum servers in priority order, falling back
/// on failure, then maps the parsed [BitcoinTx] into a [Transaction] domain
/// entity. Surfaces failures as [TransactionPortError] so consumers never
/// see electrum's error types.
class ElectrumTransactionPortAdapter implements TransactionPort {
final ElectrumServersPort _serversPort;
final ElectrumTransactionRepository _repository;
final EnvironmentPort _environmentPort;

const ElectrumTransactionPortAdapter({
required ElectrumServersPort serversPort,
required ElectrumTransactionRepository repository,
required EnvironmentPort environmentPort,
}) : _serversPort = serversPort,
_repository = repository,
_environmentPort = environmentPort;

@override
Future<Transaction> fetch({required String txid}) async {
final environment = await _environmentPort.getEnvironment();
final network = ElectrumServerNetwork.fromEnvironment(
isTestnet: environment.isTestnet,
isLiquid: false,
);

final servers = await _serversPort.getServersToUse(network: network);
if (servers.isEmpty) {
throw TransactionPortError.noServersAvailable(
network: network.toString(),
);
}

Object? lastError;
for (final server in servers) {
try {
final bitcoinTx = await _repository.fetch(
serverUrl: server.url,
txid: txid,
);
return TransactionMapper.fromBitcoinTx(
bitcoinTx,
isTestnet: environment.isTestnet,
);
} catch (e) {
lastError = e;
continue;
}
}

throw TransactionPortError.fetchFailed(txid: txid, message: '$lastError');
}
}
18 changes: 18 additions & 0 deletions lib/core/electrum/adapters/environment_adapter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:bb_mobile/core/electrum/domain/ports/environment_port.dart';
import 'package:bb_mobile/core/electrum/domain/value_objects/electrum_environment.dart';
import 'package:bb_mobile/core/settings/domain/repositories/settings_repository.dart';

class EnvironmentAdapter implements EnvironmentPort {
final SettingsRepository _settingsRepository;

EnvironmentAdapter({required SettingsRepository settingsRepository})
: _settingsRepository = settingsRepository;

@override
Future<ElectrumEnvironment> getEnvironment() async {
final settings = await _settingsRepository.fetch();
return settings.environment.isMainnet
? ElectrumEnvironment.mainnet
: ElectrumEnvironment.testnet;
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Loading