@pioneer-platform/blockbook 8.12.8 → 8.12.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1 @@
1
-
2
- $ tsc -p .
1
+ $ tsc -p .
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @pioneer-platform/blockbook
2
2
 
3
+ ## 8.12.10
4
+
5
+ ### Patch Changes
6
+
7
+ - CRITICAL FIX: Prevent node deactivation from transaction validation errors
8
+
9
+ Fixed critical bug where blockbook nodes were incorrectly marked as inactive when broadcast transactions failed due to transaction validation errors (e.g., "min relay fee not met"). These validation errors represent successful node responses with invalid transactions, not node failures.
10
+
11
+ Changes:
12
+
13
+ - Modified broadcast error handling to distinguish between transaction validation errors and actual node failures
14
+ - Added hardcoded Digibyte fees (100-150 sat/byte) to meet minimum relay requirements
15
+ - Nodes are now only penalized for real connection/network/server errors
16
+
17
+ ## 8.12.9
18
+
19
+ ### Patch Changes
20
+
21
+ - fix(blockbook): Add dgub to xpub conversion for DGB
22
+
3
23
  ## 8.12.8
4
24
 
5
25
  ### Patch Changes
package/lib/index.js CHANGED
@@ -514,7 +514,7 @@ var get_txs_by_xpub = function (coin, xpub) {
514
514
  };
515
515
  var broadcast_transaction = function (coin, hex) {
516
516
  return __awaiter(this, void 0, void 0, function () {
517
- var tag, symbol, nodes_2, activeNodes, MAX_RETRIES, RETRY_DELAY_MS_1, allErrors, retry, i, node, startTime, url, body, resp, responseTime, txid, error_1, responseTime, errorMessage, statusCode, attemptInfo, errorSummary, e_7;
517
+ var tag, symbol, nodes_2, activeNodes, MAX_RETRIES, RETRY_DELAY_MS_1, allErrors, retry, _loop_1, i, state_1, errorSummary, e_7;
518
518
  var _a, _b;
519
519
  return __generator(this, function (_c) {
520
520
  switch (_c.label) {
@@ -522,7 +522,7 @@ var broadcast_transaction = function (coin, hex) {
522
522
  tag = TAG + " | broadcast_transaction | ";
523
523
  _c.label = 1;
524
524
  case 1:
525
- _c.trys.push([1, 12, , 13]);
525
+ _c.trys.push([1, 10, , 11]);
526
526
  symbol = coin.toUpperCase();
527
527
  nodes_2 = BLOCKBOOK_NODES[symbol];
528
528
  if (!nodes_2 || nodes_2.length === 0) {
@@ -539,95 +539,126 @@ var broadcast_transaction = function (coin, hex) {
539
539
  retry = 0;
540
540
  _c.label = 2;
541
541
  case 2:
542
- if (!(retry < MAX_RETRIES)) return [3 /*break*/, 11];
542
+ if (!(retry < MAX_RETRIES)) return [3 /*break*/, 9];
543
543
  log.info(tag, "Broadcast attempt ".concat(retry + 1, "/").concat(MAX_RETRIES));
544
+ _loop_1 = function (i) {
545
+ var node, startTime, url, body, resp, responseTime, txid, error_1, responseTime, errorMessage_1, statusCode, isNodeFailure, txValidationErrors, attemptInfo;
546
+ return __generator(this, function (_d) {
547
+ switch (_d.label) {
548
+ case 0:
549
+ node = activeNodes[i];
550
+ startTime = Date.now();
551
+ _d.label = 1;
552
+ case 1:
553
+ _d.trys.push([1, 3, , 4]);
554
+ log.info(tag, "Trying node ".concat(i + 1, "/").concat(activeNodes.length, ": ").concat(node.url, " (priority: ").concat(node.priority, ") [attempt ").concat(retry + 1, "]"));
555
+ url = node.url + "/api/v2/sendtx/";
556
+ body = {
557
+ url: url,
558
+ headers: {
559
+ 'content-type': 'text/plain',
560
+ 'User-Agent': fakeUa()
561
+ },
562
+ method: 'POST',
563
+ data: hex,
564
+ timeout: 15000 // 15s timeout per node
565
+ };
566
+ return [4 /*yield*/, axios(body)];
567
+ case 2:
568
+ resp = _d.sent();
569
+ responseTime = Date.now() - startTime;
570
+ // Update node performance metrics
571
+ update_node_performance(symbol, node.url, true, responseTime);
572
+ txid = ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.result) || resp.data;
573
+ log.info(tag, "\u2705 Broadcast succeeded via node ".concat(i + 1, " in ").concat(responseTime, "ms on attempt ").concat(retry + 1, ". TXID: ").concat(txid));
574
+ return [2 /*return*/, { value: {
575
+ success: true,
576
+ txid: txid
577
+ } }];
578
+ case 3:
579
+ error_1 = _d.sent();
580
+ responseTime = Date.now() - startTime;
581
+ errorMessage_1 = 'Unknown error occurred';
582
+ statusCode = null;
583
+ isNodeFailure = true;
584
+ if (error_1.response) {
585
+ statusCode = error_1.response.status;
586
+ log.error(tag, "Node ".concat(i + 1, " HTTP Status: "), statusCode);
587
+ log.error(tag, "Node ".concat(i + 1, " Response headers: "), error_1.response.headers);
588
+ if (error_1.response.data) {
589
+ log.error(tag, "Node ".concat(i + 1, " Response data: "), error_1.response.data);
590
+ if (error_1.response.data.error) {
591
+ errorMessage_1 = error_1.response.data.error;
592
+ }
593
+ else if (typeof error_1.response.data === 'string') {
594
+ errorMessage_1 = error_1.response.data;
595
+ }
596
+ else {
597
+ errorMessage_1 = "HTTP ".concat(statusCode, ": ").concat(error_1.response.statusText || 'Request failed');
598
+ log.error(tag, "Node ".concat(i + 1, " Full response object: "), JSON.stringify(error_1.response.data));
599
+ }
600
+ txValidationErrors = [
601
+ 'min relay fee',
602
+ 'insufficient',
603
+ 'bad-txns',
604
+ 'txn-mempool-conflict',
605
+ 'dust',
606
+ 'absurdly-high-fee',
607
+ 'mandatory-script-verify-flag-failed',
608
+ 'non-final',
609
+ 'too-long-mempool-chain'
610
+ ];
611
+ if (txValidationErrors.some(function (pattern) { return errorMessage_1.toLowerCase().includes(pattern); })) {
612
+ isNodeFailure = false;
613
+ log.warn(tag, "\u26A0\uFE0F Transaction validation error (NOT node failure): ".concat(errorMessage_1));
614
+ }
615
+ }
616
+ else {
617
+ errorMessage_1 = "HTTP ".concat(statusCode, ": ").concat(error_1.response.statusText || 'Request failed');
618
+ }
619
+ }
620
+ else if (error_1.request) {
621
+ errorMessage_1 = 'Network error: No response received';
622
+ log.error(tag, "Node ".concat(i + 1, " Request config: "), (_b = error_1.config) === null || _b === void 0 ? void 0 : _b.url);
623
+ }
624
+ else {
625
+ errorMessage_1 = error_1.message || 'Request setup error';
626
+ }
627
+ // Update node performance metrics ONLY if this was an actual node failure
628
+ // Don't penalize nodes for transaction validation errors
629
+ update_node_performance(symbol, node.url, !isNodeFailure, responseTime);
630
+ attemptInfo = "[Node ".concat(i + 1, ", Attempt ").concat(retry + 1, "/").concat(MAX_RETRIES, "]");
631
+ log.error(tag, "\u274C ".concat(attemptInfo, " failed after ").concat(responseTime, "ms: ").concat(errorMessage_1));
632
+ allErrors.push("".concat(attemptInfo, " ").concat(errorMessage_1));
633
+ return [2 /*return*/, "continue"];
634
+ case 4: return [2 /*return*/];
635
+ }
636
+ });
637
+ };
544
638
  i = 0;
545
639
  _c.label = 3;
546
640
  case 3:
547
- if (!(i < activeNodes.length)) return [3 /*break*/, 8];
548
- node = activeNodes[i];
549
- startTime = Date.now();
550
- _c.label = 4;
641
+ if (!(i < activeNodes.length)) return [3 /*break*/, 6];
642
+ return [5 /*yield**/, _loop_1(i)];
551
643
  case 4:
552
- _c.trys.push([4, 6, , 7]);
553
- log.info(tag, "Trying node ".concat(i + 1, "/").concat(activeNodes.length, ": ").concat(node.url, " (priority: ").concat(node.priority, ") [attempt ").concat(retry + 1, "]"));
554
- url = node.url + "/api/v2/sendtx/";
555
- body = {
556
- url: url,
557
- headers: {
558
- 'content-type': 'text/plain',
559
- 'User-Agent': fakeUa()
560
- },
561
- method: 'POST',
562
- data: hex,
563
- timeout: 15000 // 15s timeout per node
564
- };
565
- return [4 /*yield*/, axios(body)];
644
+ state_1 = _c.sent();
645
+ if (typeof state_1 === "object")
646
+ return [2 /*return*/, state_1.value];
647
+ _c.label = 5;
566
648
  case 5:
567
- resp = _c.sent();
568
- responseTime = Date.now() - startTime;
569
- // Update node performance metrics
570
- update_node_performance(symbol, node.url, true, responseTime);
571
- txid = ((_a = resp.data) === null || _a === void 0 ? void 0 : _a.result) || resp.data;
572
- log.info(tag, "\u2705 Broadcast succeeded via node ".concat(i + 1, " in ").concat(responseTime, "ms on attempt ").concat(retry + 1, ". TXID: ").concat(txid));
573
- return [2 /*return*/, {
574
- success: true,
575
- txid: txid
576
- }];
577
- case 6:
578
- error_1 = _c.sent();
579
- responseTime = Date.now() - startTime;
580
- // Update node performance metrics
581
- update_node_performance(symbol, node.url, false, responseTime);
582
- errorMessage = 'Unknown error occurred';
583
- statusCode = null;
584
- if (error_1.response) {
585
- statusCode = error_1.response.status;
586
- log.error(tag, "Node ".concat(i + 1, " HTTP Status: "), statusCode);
587
- log.error(tag, "Node ".concat(i + 1, " Response headers: "), error_1.response.headers);
588
- if (error_1.response.data) {
589
- log.error(tag, "Node ".concat(i + 1, " Response data: "), error_1.response.data);
590
- if (error_1.response.data.error) {
591
- errorMessage = error_1.response.data.error;
592
- }
593
- else if (typeof error_1.response.data === 'string') {
594
- errorMessage = error_1.response.data;
595
- }
596
- else {
597
- errorMessage = "HTTP ".concat(statusCode, ": ").concat(error_1.response.statusText || 'Request failed');
598
- log.error(tag, "Node ".concat(i + 1, " Full response object: "), JSON.stringify(error_1.response.data));
599
- }
600
- }
601
- else {
602
- errorMessage = "HTTP ".concat(statusCode, ": ").concat(error_1.response.statusText || 'Request failed');
603
- }
604
- }
605
- else if (error_1.request) {
606
- errorMessage = 'Network error: No response received';
607
- log.error(tag, "Node ".concat(i + 1, " Request config: "), (_b = error_1.config) === null || _b === void 0 ? void 0 : _b.url);
608
- }
609
- else {
610
- errorMessage = error_1.message || 'Request setup error';
611
- }
612
- attemptInfo = "[Node ".concat(i + 1, ", Attempt ").concat(retry + 1, "/").concat(MAX_RETRIES, "]");
613
- log.error(tag, "\u274C ".concat(attemptInfo, " failed after ").concat(responseTime, "ms: ").concat(errorMessage));
614
- allErrors.push("".concat(attemptInfo, " ").concat(errorMessage));
615
- // Continue to next node in this retry round
616
- return [3 /*break*/, 7];
617
- case 7:
618
649
  i++;
619
650
  return [3 /*break*/, 3];
620
- case 8:
621
- if (!(retry < MAX_RETRIES - 1)) return [3 /*break*/, 10];
651
+ case 6:
652
+ if (!(retry < MAX_RETRIES - 1)) return [3 /*break*/, 8];
622
653
  log.info(tag, "All nodes failed in attempt ".concat(retry + 1, ". Waiting ").concat(RETRY_DELAY_MS_1, "ms before retry..."));
623
654
  return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, RETRY_DELAY_MS_1); })];
624
- case 9:
655
+ case 7:
625
656
  _c.sent();
626
- _c.label = 10;
627
- case 10:
657
+ _c.label = 8;
658
+ case 8:
628
659
  retry++;
629
660
  return [3 /*break*/, 2];
630
- case 11:
661
+ case 9:
631
662
  errorSummary = "All ".concat(activeNodes.length, " nodes failed after ").concat(MAX_RETRIES, " attempts each. Errors:\n").concat(allErrors.join('\n'));
632
663
  log.error(tag, errorSummary);
633
664
  return [2 /*return*/, {
@@ -635,11 +666,11 @@ var broadcast_transaction = function (coin, hex) {
635
666
  error: errorSummary,
636
667
  statusCode: 500
637
668
  }];
638
- case 12:
669
+ case 10:
639
670
  e_7 = _c.sent();
640
671
  console.error(tag, 'error: ', e_7);
641
672
  throw e_7;
642
- case 13: return [2 /*return*/];
673
+ case 11: return [2 /*return*/];
643
674
  }
644
675
  });
645
676
  });
@@ -680,7 +711,7 @@ var get_transaction = function (coin, txid) {
680
711
  // Enhanced UTXO function with priority-based sequential failover
681
712
  var get_utxos_by_xpub = function (coin, xpub) {
682
713
  return __awaiter(this, void 0, void 0, function () {
683
- var tag, symbol, isBitcoin, nodes_3, activeNodes, i, node, startTime, url, body, resp, responseTime, error_2, responseTime, errorMessage, e_9;
714
+ var tag, symbol, isBitcoin, b58, data, payload, xpubPrefix, convertedData, convertedXpub, nodes_3, activeNodes, i, node, startTime, url, body, resp, responseTime, error_2, responseTime, errorMessage, e_9;
684
715
  var _a;
685
716
  return __generator(this, function (_b) {
686
717
  switch (_b.label) {
@@ -691,6 +722,25 @@ var get_utxos_by_xpub = function (coin, xpub) {
691
722
  _b.label = 1;
692
723
  case 1:
693
724
  _b.trys.push([1, 8, , 9]);
725
+ // Convert dgub to xpub for DGB (NowNodes blockbook doesn't accept dgub format)
726
+ if (symbol === 'DGB' && xpub.startsWith('dgub')) {
727
+ b58 = require('bs58');
728
+ try {
729
+ data = b58.decode(xpub);
730
+ payload = data.slice(4);
731
+ xpubPrefix = Buffer.from('0488b21e', 'hex');
732
+ convertedData = Buffer.concat([xpubPrefix, payload]);
733
+ convertedXpub = b58.encode(convertedData);
734
+ log.info(tag, "Converting DGB dgub to xpub format for blockbook compatibility");
735
+ log.info(tag, "Original: ".concat(xpub.substring(0, 20), "..."));
736
+ log.info(tag, "Converted: ".concat(convertedXpub.substring(0, 20), "..."));
737
+ xpub = convertedXpub;
738
+ }
739
+ catch (conversionError) {
740
+ log.error(tag, 'Failed to convert dgub to xpub:', conversionError);
741
+ // Continue with original xpub and let blockbook handle it
742
+ }
743
+ }
694
744
  nodes_3 = BLOCKBOOK_NODES[symbol];
695
745
  if (isBitcoin)
696
746
  log.info(tag, '🔍 [BITCOIN BLOCKBOOK] Starting UTXO query for xpub:', xpub.substring(0, 20) + '...');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pioneer-platform/blockbook",
3
- "version": "8.12.8",
3
+ "version": "8.12.10",
4
4
  "main": "./lib/index.js",
5
5
  "types": "./lib/index.d.ts",
6
6
  "scripts": {