Cw 872 nano enhancements (#1909)

* fix headers on all api calls * fix duplicate nano nodes on fresh install, api key fixes * fix liveness indicators + false positive responses to queries

Matthew Fosse committed Dec 30, 2024 at 11:46 UTC 831a6d9f9a4449c2971b00e3297f9715e962cdd3
3 files changed +62 -28
cw_core/lib/node.dart
+42 -10
@@ -99,8 +99,8 @@ class Node extends HiveObject with Keyable {
99 case WalletType.polygon:
100 case WalletType.solana:
101 case WalletType.tron:
102 - return Uri.parse(
103 - "http${isSSL ? "s" : ""}://$uriRaw${path!.startsWith("/") ? path : "/$path"}");
102 + return Uri.parse(
103 + "http${isSSL ? "s" : ""}://$uriRaw${path!.startsWith("/") ? path : "/$path"}");
104 case WalletType.none:
105 throw Exception('Unexpected type ${type.toString()} for Node uri');
106 }
@@ -152,6 +152,7 @@ class Node extends HiveObject with Keyable {
152 return requestMoneroNode();
153 case WalletType.nano:
154 case WalletType.banano:
155 + return requestNanoNode();
156 case WalletType.bitcoin:
157 case WalletType.litecoin:
158 case WalletType.bitcoinCash:
@@ -198,14 +199,16 @@ class Node extends HiveObject with Keyable {
199 );
200 client.close();
201
201 - if ((
202 - response.body.contains("400 Bad Request") // Some other generic error
203 - || response.body.contains("plain HTTP request was sent to HTTPS port") // Cloudflare
204 - || response.headers["location"] != null // Generic reverse proxy
205 - || response.body.contains("301 Moved Permanently") // Poorly configured generic reverse proxy
206 - ) && !(useSSL??false)
207 - ) {
208 -
202 + if ((response.body.contains("400 Bad Request") // Some other generic error
203 + ||
204 + response.body.contains("plain HTTP request was sent to HTTPS port") // Cloudflare
205 + ||
206 + response.headers["location"] != null // Generic reverse proxy
207 + ||
208 + response.body
209 + .contains("301 Moved Permanently") // Poorly configured generic reverse proxy
210 + ) &&
211 + !(useSSL ?? false)) {
212 final oldUseSSL = useSSL;
213 useSSL = true;
214 try {
@@ -271,6 +274,35 @@ class Node extends HiveObject with Keyable {
274 }
275 }
276
277 + Future<bool> requestNanoNode() async {
278 + try {
279 + final response = await http.post(
280 + uri,
281 + headers: {
282 + "Content-Type": "application/json",
283 + "nano-app": "cake-wallet"
284 + },
285 + body: jsonEncode(
286 + {
287 + "action": "account_balance",
288 + "account": "nano_38713x95zyjsqzx6nm1dsom1jmm668owkeb9913ax6nfgj15az3nu8xkx579",
289 + },
290 + ),
291 + );
292 + final data = await jsonDecode(response.body);
293 + if (response.statusCode != 200 ||
294 + data["error"] != null ||
295 + data["balance"] == null ||
296 + data["receivable"] == null) {
297 + throw Exception(
298 + "Error while trying to get balance! ${data["error"] != null ? data["error"] : ""}");
299 + }
300 + return true;
301 + } catch (_) {
302 + return false;
303 + }
304 + }
305 +
306 Future<bool> requestEthereumServer() async {
307 try {
308 final response = await http.get(
cw_nano/lib/nano_client.dart
+19 -17
@@ -54,12 +54,12 @@ class NanoClient {
54 }
55 }
56
57 - Map<String, String> getHeaders() {
57 + Map<String, String> getHeaders(String host) {
58 final headers = Map<String, String>.from(CAKE_HEADERS);
59 - if (_node!.uri.host == "rpc.nano.to") {
59 + if (host == "rpc.nano.to") {
60 headers["key"] = nano_secrets.nano2ApiKey;
61 }
62 - if (_node!.uri.host == "nano.nownodes.io") {
62 + if (host == "nano.nownodes.io") {
63 headers["api-key"] = nano_secrets.nanoNowNodesApiKey;
64 }
65 return headers;
@@ -68,7 +68,7 @@ class NanoClient {
68 Future<NanoBalance> getBalance(String address) async {
69 final response = await http.post(
70 _node!.uri,
71 - headers: getHeaders(),
71 + headers: getHeaders(_node!.uri.host),
72 body: jsonEncode(
73 {
74 "action": "account_balance",
@@ -95,7 +95,7 @@ class NanoClient {
95 try {
96 final response = await http.post(
97 _node!.uri,
98 - headers: getHeaders(),
98 + headers: getHeaders(_node!.uri.host),
99 body: jsonEncode(
100 {
101 "action": "account_info",
@@ -116,7 +116,7 @@ class NanoClient {
116 try {
117 final response = await http.post(
118 _node!.uri,
119 - headers: CAKE_HEADERS,
119 + headers: getHeaders(_node!.uri.host),
120 body: jsonEncode(
121 {
122 "action": "block_info",
@@ -183,7 +183,7 @@ class NanoClient {
183 Future<String> requestWork(String hash) async {
184 final response = await http.post(
185 _powNode!.uri,
186 - headers: getHeaders(),
186 + headers: getHeaders(_powNode!.uri.host),
187 body: json.encode(
188 {
189 "action": "work_generate",
@@ -226,7 +226,7 @@ class NanoClient {
226
227 final processResponse = await http.post(
228 _node!.uri,
229 - headers: getHeaders(),
229 + headers: getHeaders(_node!.uri.host),
230 body: processBody,
231 );
232
@@ -425,7 +425,7 @@ class NanoClient {
425 });
426 final processResponse = await http.post(
427 _node!.uri,
428 - headers: getHeaders(),
428 + headers: getHeaders(_node!.uri.host),
429 body: processBody,
430 );
431
@@ -441,7 +441,7 @@ class NanoClient {
441 required String privateKey,
442 }) async {
443 final receivableResponse = await http.post(_node!.uri,
444 - headers: getHeaders(),
444 + headers: getHeaders(_node!.uri.host),
445 body: jsonEncode({
446 "action": "receivable",
447 "account": destinationAddress,
@@ -493,7 +493,7 @@ class NanoClient {
493 Future<List<NanoTransactionModel>> fetchTransactions(String address) async {
494 try {
495 final response = await http.post(_node!.uri,
496 - headers: getHeaders(),
496 + headers: getHeaders(_node!.uri.host),
497 body: jsonEncode({
498 "action": "account_history",
499 "account": address,
@@ -509,15 +509,16 @@ class NanoClient {
509 .map<NanoTransactionModel>((transaction) => NanoTransactionModel.fromJson(transaction))
510 .toList();
511 } catch (e) {
512 - printV(e);
513 - return [];
512 + printV("error fetching transactions: $e");
513 + rethrow;
514 }
515 }
516
517 Future<List<N2Node>> getN2Reps() async {
518 + final uri = Uri.parse(N2_REPS_ENDPOINT);
519 final response = await http.post(
519 - Uri.parse(N2_REPS_ENDPOINT),
520 - headers: CAKE_HEADERS,
520 + uri,
521 + headers: getHeaders(uri.host),
522 body: jsonEncode({"action": "reps"}),
523 );
524 try {
@@ -531,9 +532,10 @@ class NanoClient {
532 }
533
534 Future<int> getRepScore(String rep) async {
535 + final uri = Uri.parse(N2_REPS_ENDPOINT);
536 final response = await http.post(
535 - Uri.parse(N2_REPS_ENDPOINT),
536 - headers: CAKE_HEADERS,
537 + uri,
538 + headers: getHeaders(uri.host),
539 body: jsonEncode({
540 "action": "rep_info",
541 "account": rep,
lib/entities/default_settings_migration.dart
+1 -1
@@ -505,7 +505,7 @@ Future<void> updateNanoNodeList({required Box<Node> nodes}) async {
505 ];
506 // add new nodes:
507 for (final node in nodeList) {
508 - if (listOfNewEndpoints.contains(node.uriRaw)) {
508 + if (listOfNewEndpoints.contains(node.uriRaw) && !nodes.values.contains(node)) {
509 await nodes.add(node);
510 }
511 }