diff --git a/packages/komodo_coin_updates/example/seed_nodes_example.dart b/packages/komodo_coin_updates/example/seed_nodes_example.dart index 4b193d5b..4bb21fb4 100644 --- a/packages/komodo_coin_updates/example/seed_nodes_example.dart +++ b/packages/komodo_coin_updates/example/seed_nodes_example.dart @@ -5,9 +5,10 @@ void main() async { try { // Fetch seed nodes from the remote source print('Fetching seed nodes from remote source...'); - final seedNodes = await SeedNodeUpdater.fetchSeedNodes(); + final (seedNodes: seedNodes, netId: netId) = + await SeedNodeUpdater.fetchSeedNodes(); - print('Found ${seedNodes.length} seed nodes:'); + print('Found ${seedNodes.length} seed nodes on netid $netId:'); for (final node in seedNodes) { print(' - ${node.name}: ${node.host}'); if (node.contact.isNotEmpty && node.contact.first.email.isNotEmpty) { diff --git a/packages/komodo_coin_updates/lib/src/seed_node_updater.dart b/packages/komodo_coin_updates/lib/src/seed_node_updater.dart index 571dbaec..41caf24d 100644 --- a/packages/komodo_coin_updates/lib/src/seed_node_updater.dart +++ b/packages/komodo_coin_updates/lib/src/seed_node_updater.dart @@ -4,7 +4,7 @@ import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; import 'package:komodo_defi_types/komodo_defi_types.dart'; /// Service responsible for fetching and managing seed nodes from remote sources. -/// +/// /// This service handles the downloading and parsing of seed node configurations /// from the Komodo Platform repository. class SeedNodeUpdater { @@ -15,7 +15,9 @@ class SeedNodeUpdater { /// Returns a list of [SeedNode] objects that can be used for P2P networking. /// /// Throws an exception if the seed nodes cannot be fetched or parsed. - static Future> fetchSeedNodes() async { + static Future<({List seedNodes, int netId})> fetchSeedNodes({ + bool filterForWeb = kIsWeb, + }) async { const seedNodesUrl = 'https://komodoplatform.github.io/coins/seed-nodes.json'; @@ -29,7 +31,16 @@ class SeedNodeUpdater { } final seedNodesJson = jsonListFromString(response.body); - return SeedNode.fromJsonList(seedNodesJson); + var seedNodes = SeedNode.fromJsonList(seedNodesJson); + + // Filter nodes to the configured netId + seedNodes = seedNodes.where((e) => e.netId == kDefaultNetId).toList(); + + if (filterForWeb && kIsWeb) { + seedNodes = seedNodes.where((e) => e.wss).toList(); + } + + return (seedNodes: seedNodes, netId: kDefaultNetId); } catch (e) { debugPrint('Error fetching seed nodes: $e'); throw Exception('Failed to fetch or process seed nodes: $e'); diff --git a/packages/komodo_coin_updates/test/seed_node_updater_test.dart b/packages/komodo_coin_updates/test/seed_node_updater_test.dart index 1a506eb0..a1407421 100644 --- a/packages/komodo_coin_updates/test/seed_node_updater_test.dart +++ b/packages/komodo_coin_updates/test/seed_node_updater_test.dart @@ -9,11 +9,17 @@ void main() { SeedNode( name: 'seed-node-1', host: 'seed01.kmdefi.net', + type: 'domain', + wss: true, + netId: 8762, contact: [SeedNodeContact(email: '')], ), SeedNode( name: 'seed-node-2', host: 'seed02.kmdefi.net', + type: 'domain', + wss: true, + netId: 8762, contact: [SeedNodeContact(email: '')], ), ]; diff --git a/packages/komodo_defi_framework/lib/src/config/kdf_startup_config.dart b/packages/komodo_defi_framework/lib/src/config/kdf_startup_config.dart index acc55b79..d47eb08d 100644 --- a/packages/komodo_defi_framework/lib/src/config/kdf_startup_config.dart +++ b/packages/komodo_defi_framework/lib/src/config/kdf_startup_config.dart @@ -8,6 +8,7 @@ import 'package:komodo_coins/komodo_coins.dart'; import 'package:komodo_defi_framework/src/config/seed_node_validator.dart'; import 'package:komodo_defi_framework/src/services/seed_node_service.dart' show SeedNodeService; +import 'package:komodo_defi_types/komodo_defi_types.dart'; import 'package:komodo_defi_types/komodo_defi_type_utils.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; @@ -82,7 +83,7 @@ class KdfStartupConfig { int? hdAccountId, bool allowWeakPassword = false, int rpcPort = 7783, - int netid = 8762, + int netid = kDefaultNetId, String gui = 'komodo-defi-flutter-auth', bool https = false, bool rpcLocalOnly = true, @@ -168,6 +169,11 @@ class KdfStartupConfig { }) async { final (String? home, String? dbDir) = await _getAndSetupUserHome(); + final ( + seedNodes: seeds, + netId: netId, + ) = await SeedNodeService.fetchSeedNodes(); + return KdfStartupConfig._( walletName: null, walletPassword: null, @@ -176,7 +182,7 @@ class KdfStartupConfig { userHome: home, dbDir: dbDir, allowWeakPassword: true, - netid: 8762, + netid: netId, gui: 'komodo-defi-flutter-auth', coins: await _fetchCoinsData(), https: false, @@ -187,7 +193,7 @@ class KdfStartupConfig { allowRegistrations: false, enableHd: false, disableP2p: false, - seedNodes: await SeedNodeService.fetchSeedNodes(), + seedNodes: seeds, iAmSeed: false, isBootstrapNode: false, ); diff --git a/packages/komodo_defi_framework/lib/src/services/seed_node_service.dart b/packages/komodo_defi_framework/lib/src/services/seed_node_service.dart index 20489a1f..1f7c2712 100644 --- a/packages/komodo_defi_framework/lib/src/services/seed_node_service.dart +++ b/packages/komodo_defi_framework/lib/src/services/seed_node_service.dart @@ -1,30 +1,44 @@ import 'package:komodo_coin_updates/komodo_coin_updates.dart'; +import 'package:flutter/foundation.dart'; import 'package:komodo_defi_framework/src/config/kdf_logging_config.dart'; import 'package:komodo_defi_framework/src/config/seed_node_validator.dart'; +import 'package:komodo_defi_types/komodo_defi_types.dart'; /// Service class responsible for fetching and managing seed nodes. -/// -/// This class follows the Single Responsibility Principle by focusing +/// +/// This class follows the Single Responsibility Principle by focusing /// solely on seed node acquisition and management. class SeedNodeService { /// Fetches seed nodes from the remote configuration with fallback to defaults. /// - /// This method attempts to fetch the latest seed nodes from the Komodo Platform - /// repository and converts them to the string format expected by the KDF startup + /// This method attempts to fetch the latest seed nodes from the Komodo Platform + /// repository and converts them to the string format expected by the KDF startup /// configuration. /// /// Returns a list of seed node host addresses. If fetching fails, returns /// the hardcoded default seed nodes as a fallback. - static Future> fetchSeedNodes() async { + static Future<({List seedNodes, int netId})> fetchSeedNodes({ + bool filterForWeb = kIsWeb, + }) async { try { - final seedNodes = await SeedNodeUpdater.fetchSeedNodes(); - return SeedNodeUpdater.seedNodesToStringList(seedNodes); + final ( + seedNodes: nodes, + netId: netId, + ) = await SeedNodeUpdater.fetchSeedNodes(filterForWeb: filterForWeb); + + return ( + seedNodes: SeedNodeUpdater.seedNodesToStringList(nodes), + netId: netId, + ); } catch (e) { if (KdfLoggingConfig.verboseLogging) { print('WARN Failed to fetch seed nodes from remote: $e'); print('WARN Falling back to default seed nodes'); } - return getDefaultSeedNodes(); + return ( + seedNodes: getDefaultSeedNodes(), + netId: kDefaultNetId, + ); } } @@ -63,7 +77,8 @@ class SeedNodeService { // Fetch remote seed nodes or use defaults if (fetchRemote) { - return await fetchSeedNodes(); + final result = await fetchSeedNodes(); + return result.seedNodes; } else { return getDefaultSeedNodes(); } diff --git a/packages/komodo_defi_local_auth/lib/src/auth/auth_service_kdf_extension.dart b/packages/komodo_defi_local_auth/lib/src/auth/auth_service_kdf_extension.dart index 431630d3..8687f42c 100644 --- a/packages/komodo_defi_local_auth/lib/src/auth/auth_service_kdf_extension.dart +++ b/packages/komodo_defi_local_auth/lib/src/auth/auth_service_kdf_extension.dart @@ -179,7 +179,8 @@ extension KdfExtensions on KdfAuthService { } // Fetch seed nodes using the dedicated service - final seedNodes = await SeedNodeService.fetchSeedNodes(); + final (seedNodes: seedNodes, netId: netId) = + await SeedNodeService.fetchSeedNodes(); return KdfStartupConfig.generateWithDefaults( walletName: walletName, @@ -190,6 +191,7 @@ extension KdfExtensions on KdfAuthService { enableHd: hdEnabled, allowWeakPassword: allowWeakPassword, seedNodes: seedNodes, + netid: netId, ); } } diff --git a/packages/komodo_defi_types/komodo_defi_constants.dart b/packages/komodo_defi_types/komodo_defi_constants.dart new file mode 100644 index 00000000..22fdaf9c --- /dev/null +++ b/packages/komodo_defi_types/komodo_defi_constants.dart @@ -0,0 +1,2 @@ +export 'package:komodo_defi_types/komodo_defi_types.dart' show kDefaultNetId; + diff --git a/packages/komodo_defi_types/lib/komodo_defi_types.dart b/packages/komodo_defi_types/lib/komodo_defi_types.dart index 3aa26995..a4bfb637 100644 --- a/packages/komodo_defi_types/lib/komodo_defi_types.dart +++ b/packages/komodo_defi_types/lib/komodo_defi_types.dart @@ -5,6 +5,8 @@ /// More dartdocs go here. library; +export 'src/constants.dart'; + export 'src/api/api_client.dart'; export 'src/assets/asset.dart'; export 'src/assets/asset_id.dart'; diff --git a/packages/komodo_defi_types/lib/src/constants.dart b/packages/komodo_defi_types/lib/src/constants.dart new file mode 100644 index 00000000..f9f959d5 --- /dev/null +++ b/packages/komodo_defi_types/lib/src/constants.dart @@ -0,0 +1,5 @@ +/// Shared constants used across the Komodo DeFi SDK packages. +library komodo_defi_types.constants; + +/// Default network identifier used by seed nodes and framework configuration. +const int kDefaultNetId = 8762; diff --git a/packages/komodo_defi_types/lib/src/seed_node/seed_node.dart b/packages/komodo_defi_types/lib/src/seed_node/seed_node.dart index b471d9b2..4b80dd12 100644 --- a/packages/komodo_defi_types/lib/src/seed_node/seed_node.dart +++ b/packages/komodo_defi_types/lib/src/seed_node/seed_node.dart @@ -5,6 +5,9 @@ class SeedNode { const SeedNode({ required this.name, required this.host, + required this.type, + required this.wss, + required this.netId, required this.contact, }); @@ -17,11 +20,23 @@ class SeedNode { /// Contact information for the seed node final List contact; + /// The connection type of the seed node (e.g. domain or ip) + final String type; + + /// Whether the seed node supports secure websockets + final bool wss; + + /// The network identifier for the seed node + final int netId; + /// Creates a [SeedNode] from a JSON map. factory SeedNode.fromJson(JsonMap json) { return SeedNode( name: json.value('name'), host: json.value('host'), + type: json.value('type'), + wss: json.value('wss'), + netId: json.value('netid'), contact: json .value>('contact') .cast() @@ -35,6 +50,9 @@ class SeedNode { return { 'name': name, 'host': host, + 'type': type, + 'wss': wss, + 'netid': netId, 'contact': contact.map((c) => c.toJson()).toList(), }; } @@ -50,14 +68,18 @@ class SeedNode { return other is SeedNode && other.name == name && other.host == host && + other.type == type && + other.wss == wss && + other.netId == netId && _listEquals(other.contact, contact); } @override - int get hashCode => Object.hash(name, host, contact); + int get hashCode => Object.hash(name, host, type, wss, netId, Object.hashAll(contact)); @override - String toString() => 'SeedNode(name: $name, host: $host, contact: $contact)'; + String toString() => + 'SeedNode(name: $name, host: $host, type: $type, wss: $wss, netId: $netId, contact: $contact)'; /// Helper method to compare lists bool _listEquals(List? a, List? b) { diff --git a/packages/komodo_defi_types/lib/src/types.dart b/packages/komodo_defi_types/lib/src/types.dart index e8a605f1..b5093d85 100644 --- a/packages/komodo_defi_types/lib/src/types.dart +++ b/packages/komodo_defi_types/lib/src/types.dart @@ -19,6 +19,7 @@ export 'auth/kdf_user.dart'; export 'coin/coin.dart'; export 'coin_classes/coin_subclasses.dart'; export 'coin_classes/protocol_class.dart'; +export 'constants.dart'; export 'cryptography/mnemonic.dart'; export 'exceptions/http_exceptions.dart'; export 'exported_rpc_types.dart'; @@ -45,6 +46,7 @@ export 'public_key/pubkey.dart'; export 'public_key/pubkey_strategy.dart'; export 'public_key/token_balance_map.dart'; export 'public_key/wallet_balance.dart'; +export 'seed_node/seed_node.dart'; export 'transactions/asset_transaction_history_id.dart'; export 'transactions/balance_changes.dart'; export 'transactions/fee_info.dart'; diff --git a/packages/komodo_defi_types/test/seed_node_test.dart b/packages/komodo_defi_types/test/seed_node_test.dart index ee7f0b10..75cee320 100644 --- a/packages/komodo_defi_types/test/seed_node_test.dart +++ b/packages/komodo_defi_types/test/seed_node_test.dart @@ -7,6 +7,9 @@ void main() { final json = { 'name': 'seed-node-1', 'host': 'seed01.kmdefi.net', + 'type': 'domain', + 'wss': true, + 'netid': 8762, 'contact': [ {'email': 'admin@example.com'} ] @@ -24,6 +27,9 @@ void main() { final seedNode = SeedNode( name: 'seed-node-2', host: 'seed02.kmdefi.net', + type: 'domain', + wss: true, + netId: 8762, contact: [ SeedNodeContact(email: 'test@example.com'), ], @@ -44,6 +50,9 @@ void main() { { 'name': 'seed-node-1', 'host': 'seed01.kmdefi.net', + 'type': 'domain', + 'wss': true, + 'netid': 8762, 'contact': [ {'email': ''} ] @@ -51,6 +60,9 @@ void main() { { 'name': 'seed-node-2', 'host': 'seed02.kmdefi.net', + 'type': 'domain', + 'wss': true, + 'netid': 8762, 'contact': [ {'email': ''} ] @@ -70,18 +82,27 @@ void main() { final seedNode1 = SeedNode( name: 'test', host: 'example.com', + type: 'domain', + wss: true, + netId: 8762, contact: [SeedNodeContact(email: 'test@example.com')], ); final seedNode2 = SeedNode( name: 'test', host: 'example.com', + type: 'domain', + wss: true, + netId: 8762, contact: [SeedNodeContact(email: 'test@example.com')], ); final seedNode3 = SeedNode( name: 'different', host: 'example.com', + type: 'domain', + wss: true, + netId: 8762, contact: [SeedNodeContact(email: 'test@example.com')], );