diff --git a/lib/services/nano_api.dart b/lib/services/nano_api.dart index bc2e2b84d..8627b67b3 100644 --- a/lib/services/nano_api.dart +++ b/lib/services/nano_api.dart @@ -1,9 +1,10 @@ import 'dart:convert'; import 'package:nanodart/nanodart.dart'; + import '../networking/http.dart'; -import 'tor_service.dart'; import '../utilities/prefs.dart'; +import 'tor_service.dart'; class NanoAPI { static Future< @@ -14,6 +15,7 @@ class NanoAPI { required Uri server, required bool representative, required String account, + required Map headers, }) async { NAccountInfo? accountInfo; Exception? exception; @@ -23,9 +25,7 @@ class NanoAPI { try { final response = await client.post( url: server, - headers: { - "Content-Type": "application/json", - }, + headers: headers, body: jsonEncode({ "action": "account_info", "representative": "true", @@ -64,6 +64,7 @@ class NanoAPI { required String balance, required String privateKey, required String work, + required Map headers, }) async { final Map block = { "type": "state", @@ -98,7 +99,11 @@ class NanoAPI { block["signature"] = signature; - final map = await postBlock(server: server, block: block); + final map = await postBlock( + server: server, + block: block, + headers: headers, + ); if (map is Map && map["error"] != null) { throw Exception(map["error"].toString()); @@ -111,14 +116,13 @@ class NanoAPI { static Future postBlock({ required Uri server, required Map block, + required Map headers, }) async { final HTTP client = HTTP(); final response = await client.post( url: server, - headers: { - "Content-Type": "application/json", - }, + headers: headers, body: jsonEncode({ "action": "process", "json_block": "true", diff --git a/lib/wallets/crypto_currency/coins/banano.dart b/lib/wallets/crypto_currency/coins/banano.dart index 2dec17825..c372e760a 100644 --- a/lib/wallets/crypto_currency/coins/banano.dart +++ b/lib/wallets/crypto_currency/coins/banano.dart @@ -1,4 +1,5 @@ import 'package:nanodart/nanodart.dart'; + import '../../../models/isar/models/blockchain_data/address.dart'; import '../../../models/node_model.dart'; import '../../../utilities/default_nodes.dart'; @@ -66,7 +67,8 @@ class Banano extends NanoCurrency { switch (network) { case CryptoCurrencyNetwork.main: return NodeModel( - host: "https://kaliumapi.appditto.com/api", + // host: "https://kaliumapi.appditto.com/api", + host: "https://nodes.nanswap.com/BAN", port: 443, name: DefaultNodes.defaultName, id: DefaultNodes.buildId(this), diff --git a/lib/wallets/crypto_currency/coins/nano.dart b/lib/wallets/crypto_currency/coins/nano.dart index b14992812..b11e68192 100644 --- a/lib/wallets/crypto_currency/coins/nano.dart +++ b/lib/wallets/crypto_currency/coins/nano.dart @@ -1,4 +1,5 @@ import 'package:nanodart/nanodart.dart'; + import '../../../models/isar/models/isar_models.dart'; import '../../../models/node_model.dart'; import '../../../utilities/default_nodes.dart'; @@ -66,7 +67,8 @@ class Nano extends NanoCurrency { switch (network) { case CryptoCurrencyNetwork.main: return NodeModel( - host: "https://rainstorm.city/api", + // host: "https://rainstorm.city/api", + host: "https://nodes.nanswap.com/XNO", port: 443, name: DefaultNodes.defaultName, id: DefaultNodes.buildId(this), diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart index 94f34c121..120f964ef 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/nano_interface.dart @@ -25,6 +25,17 @@ import '../intermediate/bip39_wallet.dart'; // const _kWorkServer = "https://rpc.nano.to"; const _kWorkServer = "https://nodes.nanswap.com/XNO"; +Map _buildHeaders(String url) { + final result = { + 'Content-type': 'application/json', + }; + if (url + case "https://nodes.nanswap.com/XNO" || "https://nodes.nanswap.com/BAN") { + result["nodes-api-key"] = kNanoSwapRpcApiKey; + } + return result; +} + mixin NanoInterface on Bip39Wallet { // since nano based coins only have a single address/account we can cache // the address instead of fetching from db every time we need it in certain @@ -39,10 +50,7 @@ mixin NanoInterface on Bip39Wallet { return _httpClient .post( url: Uri.parse(_kWorkServer), // this should be a - headers: { - 'Content-type': 'application/json', - "nodes-api-key": kNanoSwapRpcApiKey, - }, + headers: _buildHeaders(_kWorkServer), body: json.encode( { "action": "work_generate", @@ -99,10 +107,6 @@ mixin NanoInterface on Bip39Wallet { // TODO: the opening block of an account is a special case bool openBlock = false; - final headers = { - "Content-Type": "application/json", - }; - // first check if the account is open: // get the account info (we need the frontier and representative): final infoBody = jsonEncode({ @@ -110,9 +114,10 @@ mixin NanoInterface on Bip39Wallet { "representative": "true", "account": publicAddress, }); + final node = getCurrentNode(); final infoResponse = await _httpClient.post( - url: Uri.parse(getCurrentNode().host), - headers: headers, + url: Uri.parse(node.host), + headers: _buildHeaders(node.host), body: infoBody, proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -130,8 +135,8 @@ mixin NanoInterface on Bip39Wallet { }); final balanceResponse = await _httpClient.post( - url: Uri.parse(getCurrentNode().host), - headers: headers, + url: Uri.parse(node.host), + headers: _buildHeaders(node.host), body: balanceBody, proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -204,8 +209,8 @@ mixin NanoInterface on Bip39Wallet { "block": receiveBlock, }); final processResponse = await _httpClient.post( - url: Uri.parse(getCurrentNode().host), - headers: headers, + url: Uri.parse(node.host), + headers: _buildHeaders(node.host), body: processBody, proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -218,14 +223,14 @@ mixin NanoInterface on Bip39Wallet { } Future _confirmAllReceivable(String accountAddress) async { + final node = getCurrentNode(); final receivableResponse = await _httpClient.post( - url: Uri.parse(getCurrentNode().host), - headers: {"Content-Type": "application/json"}, + url: Uri.parse(node.host), + headers: _buildHeaders(node.host), body: jsonEncode({ "action": "receivable", "source": "true", "account": accountAddress, - "count": "-1", }), proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, ); @@ -253,10 +258,12 @@ mixin NanoInterface on Bip39Wallet { final address = (_cachedAddress ?? await getCurrentReceivingAddress())!.value; + final node = getCurrentNode(); final response = await NanoAPI.getAccountInfo( server: serverURI, representative: true, account: address, + headers: _buildHeaders(node.host), ); return response.accountInfo?.representative ?? @@ -265,7 +272,8 @@ mixin NanoInterface on Bip39Wallet { Future changeRepresentative(String newRepresentative) async { try { - final serverURI = Uri.parse(getCurrentNode().host); + final node = getCurrentNode(); + final serverURI = Uri.parse(node.host); await updateBalance(); final balance = info.cachedBalance.spendable.raw.toString(); final String privateKey = await _getPrivateKeyFromMnemonic(); @@ -276,6 +284,7 @@ mixin NanoInterface on Bip39Wallet { server: serverURI, representative: true, account: address, + headers: _buildHeaders(node.host), ); if (response.accountInfo == null) { @@ -293,6 +302,7 @@ mixin NanoInterface on Bip39Wallet { balance: balance, privateKey: privateKey, work: work!, + headers: _buildHeaders(node.host), ); } catch (_) { rethrow; @@ -337,11 +347,11 @@ mixin NanoInterface on Bip39Wallet { @override Future pingCheck() async { - final uri = Uri.parse(getCurrentNode().host); - + final node = getCurrentNode(); + final uri = Uri.parse(node.host); final response = await _httpClient.post( url: uri, - headers: {"Content-Type": "application/json"}, + headers: _buildHeaders(node.host), body: jsonEncode( { "action": "version", @@ -390,13 +400,10 @@ mixin NanoInterface on Bip39Wallet { "account": publicAddress, }); - final headers = { - "Content-Type": "application/json", - }; - + final node = getCurrentNode(); final infoResponse = await _httpClient.post( - url: Uri.parse(getCurrentNode().host), - headers: headers, + url: Uri.parse(node.host), + headers: _buildHeaders(node.host), body: infoBody, proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, @@ -449,8 +456,8 @@ mixin NanoInterface on Bip39Wallet { "block": sendBlock, }); final processResponse = await _httpClient.post( - url: Uri.parse(getCurrentNode().host), - headers: headers, + url: Uri.parse(node.host), + headers: _buildHeaders(node.host), body: processBody, proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, @@ -491,6 +498,49 @@ mixin NanoInterface on Bip39Wallet { } } + // recurse over api calls if required + // (if more than 200 history items) + Future> _fetchAll( + String publicAddress, + String? previous, + Map? data, + ) async { + final node = getCurrentNode(); + final body = { + "action": "account_history", + "account": publicAddress, + "count": "200", + }; + + if (previous is String) { + body["head"] = previous; + } + + final response = await _httpClient.post( + url: Uri.parse(node.host), + headers: _buildHeaders(node.host), + body: jsonEncode(body), + proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, + ); + + // this should really have proper type checking and error propagation but I'm out of time + final newData = + Map.from((await jsonDecode(response.body)) as Map); + + if (newData["previous"] is String) { + if (data?["history"] is List) { + (newData["history"] as List).addAll(data!["history"] as List); + } + return await _fetchAll( + publicAddress, + newData["previous"] as String, + newData, + ); + } + + return newData; + } + @override Future updateTransactions() async { await updateChainHeight(); @@ -498,17 +548,9 @@ mixin NanoInterface on Bip39Wallet { (_cachedAddress ?? await getCurrentReceivingAddress())!; final String publicAddress = receivingAddress.value; await _confirmAllReceivable(publicAddress); - final response = await _httpClient.post( - url: Uri.parse(getCurrentNode().host), - headers: {"Content-Type": "application/json"}, - body: jsonEncode({ - "action": "account_history", - "account": publicAddress, - "count": "-1", - }), - proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, - ); - final data = await jsonDecode(response.body); + + final data = await _fetchAll(publicAddress, null, null); + final transactions = data["history"] is List ? data["history"] as List : []; @@ -577,13 +619,11 @@ mixin NanoInterface on Bip39Wallet { "action": "account_balance", "account": addressString, }); - final headers = { - "Content-Type": "application/json", - }; + final node = getCurrentNode(); final response = await _httpClient.post( - url: Uri.parse(getCurrentNode().host), - headers: headers, + url: Uri.parse(node.host), + headers: _buildHeaders(node.host), body: body, proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null, @@ -628,12 +668,11 @@ mixin NanoInterface on Bip39Wallet { "action": "account_info", "account": publicAddress, }); - final headers = { - "Content-Type": "application/json", - }; + + final node = getCurrentNode(); final infoResponse = await _httpClient.post( - url: Uri.parse(getCurrentNode().host), - headers: headers, + url: Uri.parse(node.host), + headers: _buildHeaders(node.host), body: infoBody, proxyInfo: prefs.useTor ? TorService.sharedInstance.getProxyInfo() : null,