@dignetwork/chia-block-listener 0.1.12 → 0.1.15
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.
- package/README.md +0 -25
- package/index.d.ts +26 -0
- package/npm/darwin-arm64/package.json +1 -1
- package/npm/darwin-x64/package.json +1 -1
- package/npm/linux-arm64-gnu/package.json +1 -1
- package/npm/linux-x64-gnu/package.json +1 -1
- package/npm/win32-x64-msvc/package.json +1 -1
- package/package.json +6 -6
- package/scripts/post-build.js +47 -25
- package/src/peer_pool.rs +46 -28
package/README.md
CHANGED
|
@@ -68,31 +68,6 @@ process.on('SIGINT', () => {
|
|
|
68
68
|
process.exit(0)
|
|
69
69
|
})
|
|
70
70
|
```
|
|
71
|
-
|
|
72
|
-
## Recent Enhancements 🚀
|
|
73
|
-
|
|
74
|
-
This library includes several powerful enhancements for production use:
|
|
75
|
-
|
|
76
|
-
### Automatic Failover System
|
|
77
|
-
- **Intelligent Retry Logic**: Automatically tries up to 3 different peers when block requests fail
|
|
78
|
-
- **Smart Peer Selection**: Chooses the best available peer for each request based on response times
|
|
79
|
-
- **Transparent Operation**: Failover happens automatically without user intervention
|
|
80
|
-
|
|
81
|
-
### Enhanced Error Handling
|
|
82
|
-
- **Protocol Error Detection**: Automatically detects and disconnects peers that refuse blocks or send invalid data
|
|
83
|
-
- **Connection Health Monitoring**: Tracks peer connection health and removes problematic peers
|
|
84
|
-
- **Error Classification**: Distinguishes between temporary issues and permanent peer problems
|
|
85
|
-
|
|
86
|
-
### Performance Optimizations
|
|
87
|
-
- **Aggressive Rate Limiting**: 50ms rate limiting per peer for maximum throughput
|
|
88
|
-
- **Persistent Connections**: Reuses WebSocket connections for optimal performance
|
|
89
|
-
- **Parallel Processing**: Efficient handling of multiple concurrent block requests
|
|
90
|
-
|
|
91
|
-
### Developer Experience
|
|
92
|
-
- **camelCase API**: All JavaScript methods use proper camelCase naming conventions
|
|
93
|
-
- **Comprehensive Events**: Detailed event system for monitoring peer lifecycle and errors
|
|
94
|
-
- **TypeScript Support**: Full type safety with complete TypeScript definitions
|
|
95
|
-
|
|
96
71
|
## API Reference
|
|
97
72
|
|
|
98
73
|
### ChiaBlockListener Class
|
package/index.d.ts
CHANGED
|
@@ -44,6 +44,11 @@ export interface CoinSpend {
|
|
|
44
44
|
solution: string
|
|
45
45
|
offset: number
|
|
46
46
|
}
|
|
47
|
+
export interface NewPeakHeightEvent {
|
|
48
|
+
oldPeak?: number | null
|
|
49
|
+
newPeak: number
|
|
50
|
+
peerId: string
|
|
51
|
+
}
|
|
47
52
|
export declare function initTracing(): void
|
|
48
53
|
export declare class ChiaBlockListener {
|
|
49
54
|
constructor()
|
|
@@ -51,7 +56,21 @@ export declare class ChiaBlockListener {
|
|
|
51
56
|
disconnectPeer(peerId: string): boolean
|
|
52
57
|
disconnectAllPeers(): void
|
|
53
58
|
getConnectedPeers(): Array<string>
|
|
59
|
+
// Typed event method overloads
|
|
60
|
+
|
|
61
|
+
on(event: 'blockReceived', callback: (event: BlockReceivedEvent) => void): void
|
|
62
|
+
|
|
63
|
+
on(event: 'peerConnected', callback: (event: PeerConnectedEvent) => void): void
|
|
64
|
+
|
|
65
|
+
on(event: 'peerDisconnected', callback: (event: PeerDisconnectedEvent) => void): void
|
|
66
|
+
|
|
54
67
|
on(event: string, callback: (...args: any[]) => any): void
|
|
68
|
+
off(event: 'blockReceived', callback: (event: BlockReceivedEvent) => void): void
|
|
69
|
+
|
|
70
|
+
off(event: 'peerConnected', callback: (event: PeerConnectedEvent) => void): void
|
|
71
|
+
|
|
72
|
+
off(event: 'peerDisconnected', callback: (event: PeerDisconnectedEvent) => void): void
|
|
73
|
+
|
|
55
74
|
off(event: string, callback: (...args: any[]) => any): void
|
|
56
75
|
getBlockByHeight(peerId: string, height: number): BlockReceivedEvent
|
|
57
76
|
getBlocksRange(peerId: string, startHeight: number, endHeight: number): Array<BlockReceivedEvent>
|
|
@@ -64,6 +83,13 @@ export declare class ChiaPeerPool {
|
|
|
64
83
|
shutdown(): Promise<void>
|
|
65
84
|
getConnectedPeers(): Promise<Array<string>>
|
|
66
85
|
getPeakHeight(): Promise<number | null>
|
|
86
|
+
// Typed event method overloads for ChiaPeerPool
|
|
87
|
+
on(event: 'peerConnected', callback: (event: PeerConnectedEvent) => void): void
|
|
88
|
+
on(event: 'peerDisconnected', callback: (event: PeerDisconnectedEvent) => void): void
|
|
89
|
+
on(event: 'newPeakHeight', callback: (event: NewPeakHeightEvent) => void): void
|
|
67
90
|
on(event: string, callback: (...args: any[]) => any): void
|
|
91
|
+
off(event: 'peerConnected', callback: (event: PeerConnectedEvent) => void): void
|
|
92
|
+
off(event: 'peerDisconnected', callback: (event: PeerDisconnectedEvent) => void): void
|
|
93
|
+
off(event: 'newPeakHeight', callback: (event: NewPeakHeightEvent) => void): void
|
|
68
94
|
off(event: string, callback: (...args: any[]) => any): void
|
|
69
95
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dignetwork/chia-block-listener",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"types": "index.d.ts",
|
|
6
6
|
"repository": {
|
|
@@ -61,10 +61,10 @@
|
|
|
61
61
|
"@dignetwork/datalayer-driver": "^0.1.35"
|
|
62
62
|
},
|
|
63
63
|
"optionalDependencies": {
|
|
64
|
-
"@dignetwork/chia-block-listener-win32-x64-msvc": "0.1.
|
|
65
|
-
"@dignetwork/chia-block-listener-darwin-x64": "0.1.
|
|
66
|
-
"@dignetwork/chia-block-listener-linux-x64-gnu": "0.1.
|
|
67
|
-
"@dignetwork/chia-block-listener-darwin-arm64": "0.1.
|
|
68
|
-
"@dignetwork/chia-block-listener-linux-arm64-gnu": "0.1.
|
|
64
|
+
"@dignetwork/chia-block-listener-win32-x64-msvc": "0.1.15",
|
|
65
|
+
"@dignetwork/chia-block-listener-darwin-x64": "0.1.15",
|
|
66
|
+
"@dignetwork/chia-block-listener-linux-x64-gnu": "0.1.15",
|
|
67
|
+
"@dignetwork/chia-block-listener-darwin-arm64": "0.1.15",
|
|
68
|
+
"@dignetwork/chia-block-listener-linux-arm64-gnu": "0.1.15"
|
|
69
69
|
}
|
|
70
70
|
}
|
package/scripts/post-build.js
CHANGED
|
@@ -28,43 +28,65 @@ function addTypedOverloads() {
|
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
// Add NewPeakHeightEvent interface if not present
|
|
32
|
+
if (!content.includes('NewPeakHeightEvent')) {
|
|
33
|
+
const insertPosition = content.indexOf('export declare function initTracing(): void');
|
|
34
|
+
if (insertPosition !== -1) {
|
|
35
|
+
const newInterface = `export interface NewPeakHeightEvent {
|
|
36
|
+
oldPeak?: number | null
|
|
37
|
+
newPeak: number
|
|
38
|
+
peerId: string
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
content = content.substring(0, insertPosition) + newInterface + content.substring(insertPosition);
|
|
42
|
+
console.log('✅ Added NewPeakHeightEvent interface');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
34
45
|
|
|
35
|
-
//
|
|
36
|
-
|
|
46
|
+
// Handle ChiaBlockListener events - find the class and add typed overloads
|
|
47
|
+
let blockListenerMatch = content.match(/(export declare class ChiaBlockListener \{[\s\S]*?)(\s+)(on\(event: string, callback: \(\.\.\.args: any\[\]\) => any\): void)/);
|
|
48
|
+
if (blockListenerMatch) {
|
|
49
|
+
const replacement = `${blockListenerMatch[1]}${blockListenerMatch[2]}// Typed event method overloads for ChiaBlockListener
|
|
50
|
+
${blockListenerMatch[2]}on(event: 'blockReceived', callback: (event: BlockReceivedEvent) => void): void
|
|
51
|
+
${blockListenerMatch[2]}on(event: 'peerConnected', callback: (event: PeerConnectedEvent) => void): void
|
|
52
|
+
${blockListenerMatch[2]}on(event: 'peerDisconnected', callback: (event: PeerDisconnectedEvent) => void): void
|
|
53
|
+
${blockListenerMatch[2]}${blockListenerMatch[3]}`;
|
|
54
|
+
content = content.replace(blockListenerMatch[0], replacement);
|
|
55
|
+
|
|
56
|
+
// Also add off method overloads
|
|
37
57
|
content = content.replace(
|
|
38
|
-
|
|
39
|
-
`$1
|
|
40
|
-
$
|
|
41
|
-
$
|
|
42
|
-
$
|
|
43
|
-
$1$2`
|
|
58
|
+
/(export declare class ChiaBlockListener \{[\s\S]*?)(\s+)(off\(event: string, callback: \(\.\.\.args: any\[\]\) => any\): void)/,
|
|
59
|
+
`$1$2off(event: 'blockReceived', callback: (event: BlockReceivedEvent) => void): void
|
|
60
|
+
$2off(event: 'peerConnected', callback: (event: PeerConnectedEvent) => void): void
|
|
61
|
+
$2off(event: 'peerDisconnected', callback: (event: PeerDisconnectedEvent) => void): void
|
|
62
|
+
$2$3`
|
|
44
63
|
);
|
|
45
|
-
} else {
|
|
46
|
-
console.error('❌ Could not find on() method in index.d.ts');
|
|
47
|
-
return;
|
|
48
64
|
}
|
|
49
65
|
|
|
50
|
-
//
|
|
51
|
-
|
|
66
|
+
// Handle ChiaPeerPool events - find the class and add typed overloads
|
|
67
|
+
let peerPoolMatch = content.match(/(export declare class ChiaPeerPool \{[\s\S]*?)(\s+)(on\(event: string, callback: \(\.\.\.args: any\[\]\) => any\): void)/);
|
|
68
|
+
if (peerPoolMatch) {
|
|
69
|
+
const replacement = `${peerPoolMatch[1]}${peerPoolMatch[2]}// Typed event method overloads for ChiaPeerPool
|
|
70
|
+
${peerPoolMatch[2]}on(event: 'peerConnected', callback: (event: PeerConnectedEvent) => void): void
|
|
71
|
+
${peerPoolMatch[2]}on(event: 'peerDisconnected', callback: (event: PeerDisconnectedEvent) => void): void
|
|
72
|
+
${peerPoolMatch[2]}on(event: 'newPeakHeight', callback: (event: NewPeakHeightEvent) => void): void
|
|
73
|
+
${peerPoolMatch[2]}${peerPoolMatch[3]}`;
|
|
74
|
+
content = content.replace(peerPoolMatch[0], replacement);
|
|
75
|
+
|
|
76
|
+
// Also add off method overloads
|
|
52
77
|
content = content.replace(
|
|
53
|
-
|
|
54
|
-
`$
|
|
55
|
-
$
|
|
56
|
-
$
|
|
57
|
-
$
|
|
78
|
+
/(export declare class ChiaPeerPool \{[\s\S]*?)(\s+)(off\(event: string, callback: \(\.\.\.args: any\[\]\) => any\): void)/,
|
|
79
|
+
`$1$2off(event: 'peerConnected', callback: (event: PeerConnectedEvent) => void): void
|
|
80
|
+
$2off(event: 'peerDisconnected', callback: (event: PeerDisconnectedEvent) => void): void
|
|
81
|
+
$2off(event: 'newPeakHeight', callback: (event: NewPeakHeightEvent) => void): void
|
|
82
|
+
$2$3`
|
|
58
83
|
);
|
|
59
|
-
} else {
|
|
60
|
-
console.error('❌ Could not find off() method in index.d.ts');
|
|
61
|
-
return;
|
|
62
84
|
}
|
|
63
85
|
|
|
64
86
|
// Write the updated content back to the file
|
|
65
87
|
fs.writeFileSync(indexDtsPath, content, 'utf8');
|
|
66
88
|
|
|
67
|
-
console.log('✅ Successfully added typed event method overloads
|
|
89
|
+
console.log('✅ Successfully added typed event method overloads for both ChiaBlockListener and ChiaPeerPool');
|
|
68
90
|
} catch (error) {
|
|
69
91
|
console.error('❌ Error adding typed overloads:', error.message);
|
|
70
92
|
process.exit(1);
|
package/src/peer_pool.rs
CHANGED
|
@@ -6,6 +6,7 @@ use crate::peer::PeerConnection;
|
|
|
6
6
|
use chia_generator_parser::{BlockParser, ParsedBlock};
|
|
7
7
|
use chia_protocol::FullBlock;
|
|
8
8
|
use hex::encode as hex_encode;
|
|
9
|
+
use napi_derive::napi;
|
|
9
10
|
use std::collections::{HashMap, VecDeque};
|
|
10
11
|
use std::sync::Arc;
|
|
11
12
|
use std::time::{Duration, Instant};
|
|
@@ -15,7 +16,7 @@ use tokio::time::timeout;
|
|
|
15
16
|
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
|
16
17
|
use tracing::{debug, error, info, warn};
|
|
17
18
|
|
|
18
|
-
const RATE_LIMIT_MS: u64 =
|
|
19
|
+
const RATE_LIMIT_MS: u64 = 500; // 500ms cooldown between peer usage
|
|
19
20
|
const REQUEST_TIMEOUT_MS: u64 = 5000; // 5 second timeout for block requests (reduced from 10s)
|
|
20
21
|
const CONNECTION_TIMEOUT_MS: u64 = 3000; // 3 second timeout for connections (reduced from 5s)
|
|
21
22
|
|
|
@@ -24,9 +25,13 @@ pub type PeerDisconnectedCallback = Box<dyn Fn(PeerDisconnectedEvent) + Send + S
|
|
|
24
25
|
pub type NewPeakHeightCallback = Box<dyn Fn(NewPeakHeightEvent) + Send + Sync + 'static>;
|
|
25
26
|
|
|
26
27
|
#[derive(Debug, Clone)]
|
|
28
|
+
#[napi(object)]
|
|
27
29
|
pub struct NewPeakHeightEvent {
|
|
30
|
+
#[napi(js_name = "oldPeak")]
|
|
28
31
|
pub old_peak: Option<u32>,
|
|
32
|
+
#[napi(js_name = "newPeak")]
|
|
29
33
|
pub new_peak: u32,
|
|
34
|
+
#[napi(js_name = "peerId")]
|
|
30
35
|
pub peer_id: String,
|
|
31
36
|
}
|
|
32
37
|
|
|
@@ -50,7 +55,8 @@ pub struct ChiaPeerPool {
|
|
|
50
55
|
|
|
51
56
|
struct ChiaPeerPoolInner {
|
|
52
57
|
peers: HashMap<String, PeerInfo>,
|
|
53
|
-
peer_ids: Vec<String>,
|
|
58
|
+
peer_ids: Vec<String>, // For round-robin
|
|
59
|
+
round_robin_index: usize, // Track current position in round-robin
|
|
54
60
|
highest_peak: Option<u32>,
|
|
55
61
|
}
|
|
56
62
|
|
|
@@ -88,6 +94,7 @@ impl ChiaPeerPool {
|
|
|
88
94
|
let inner = Arc::new(RwLock::new(ChiaPeerPoolInner {
|
|
89
95
|
peers: HashMap::new(),
|
|
90
96
|
peer_ids: Vec::new(),
|
|
97
|
+
round_robin_index: 0,
|
|
91
98
|
highest_peak: None,
|
|
92
99
|
}));
|
|
93
100
|
|
|
@@ -362,6 +369,10 @@ impl ChiaPeerPool {
|
|
|
362
369
|
}
|
|
363
370
|
|
|
364
371
|
guard.peer_ids.retain(|id| id != &peer_id);
|
|
372
|
+
// Adjust round_robin_index if needed
|
|
373
|
+
if guard.round_robin_index >= guard.peer_ids.len() && !guard.peer_ids.is_empty() {
|
|
374
|
+
guard.round_robin_index = 0;
|
|
375
|
+
}
|
|
365
376
|
Ok(true)
|
|
366
377
|
} else {
|
|
367
378
|
Ok(false)
|
|
@@ -378,6 +389,7 @@ impl ChiaPeerPool {
|
|
|
378
389
|
}
|
|
379
390
|
|
|
380
391
|
guard.peer_ids.clear();
|
|
392
|
+
guard.round_robin_index = 0;
|
|
381
393
|
Ok(())
|
|
382
394
|
}
|
|
383
395
|
|
|
@@ -423,41 +435,43 @@ impl ChiaPeerPool {
|
|
|
423
435
|
if let Some(request) = request_queue.front() {
|
|
424
436
|
match request {
|
|
425
437
|
PoolRequest::GetBlockByHeight { .. } => {
|
|
426
|
-
//
|
|
438
|
+
// Round-robin peer selection with 500ms cooldown
|
|
427
439
|
let now = Instant::now();
|
|
428
|
-
let mut
|
|
429
|
-
let mut
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
//
|
|
433
|
-
|
|
434
|
-
if
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
best_peer = Some(peer_id.clone());
|
|
440
|
+
let mut selected_peer = None;
|
|
441
|
+
let mut attempts = 0;
|
|
442
|
+
let total_peers = guard.peer_ids.len();
|
|
443
|
+
|
|
444
|
+
// Try to find an available peer using round-robin
|
|
445
|
+
while attempts < total_peers {
|
|
446
|
+
if !guard.peer_ids.is_empty() {
|
|
447
|
+
let peer_id = &guard.peer_ids[guard.round_robin_index];
|
|
448
|
+
|
|
449
|
+
if let Some(peer_info) = guard.peers.get(peer_id) {
|
|
450
|
+
if peer_info.is_connected {
|
|
451
|
+
let time_since_last_use = now.duration_since(peer_info.last_used);
|
|
452
|
+
|
|
453
|
+
// Check if this peer is available (past cooldown)
|
|
454
|
+
if time_since_last_use >= Duration::from_millis(RATE_LIMIT_MS) {
|
|
455
|
+
selected_peer = Some(peer_id.clone());
|
|
456
|
+
// Move to next peer for next request
|
|
457
|
+
guard.round_robin_index = (guard.round_robin_index + 1) % total_peers;
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
449
460
|
}
|
|
450
461
|
}
|
|
462
|
+
|
|
463
|
+
// Move to next peer and try again
|
|
464
|
+
guard.round_robin_index = (guard.round_robin_index + 1) % total_peers;
|
|
451
465
|
}
|
|
466
|
+
attempts += 1;
|
|
452
467
|
}
|
|
453
468
|
|
|
454
|
-
if let Some(peer_id) =
|
|
469
|
+
if let Some(peer_id) = selected_peer {
|
|
455
470
|
if let Some(peer_info) = guard.peers.get(&peer_id) {
|
|
456
471
|
let time_since_last_use = now.duration_since(peer_info.last_used);
|
|
457
472
|
|
|
458
|
-
// Only proceed if peer is available
|
|
459
|
-
if time_since_last_use >= Duration::from_millis(RATE_LIMIT_MS)
|
|
460
|
-
shortest_wait < Duration::from_millis(25) {
|
|
473
|
+
// Only proceed if peer is available (we already checked this in round-robin)
|
|
474
|
+
if time_since_last_use >= Duration::from_millis(RATE_LIMIT_MS) {
|
|
461
475
|
|
|
462
476
|
if let Some(request) = request_queue.pop_front() {
|
|
463
477
|
if let Some(peer_info) = guard.peers.get_mut(&peer_id) {
|
|
@@ -956,6 +970,10 @@ impl ChiaPeerPool {
|
|
|
956
970
|
let _ = worker_tx.send(WorkerRequest::Shutdown).await;
|
|
957
971
|
}
|
|
958
972
|
guard.peer_ids.retain(|id| id != ¶ms.peer_id);
|
|
973
|
+
// Adjust round_robin_index if needed
|
|
974
|
+
if guard.round_robin_index >= guard.peer_ids.len() && !guard.peer_ids.is_empty() {
|
|
975
|
+
guard.round_robin_index = 0;
|
|
976
|
+
}
|
|
959
977
|
}
|
|
960
978
|
|
|
961
979
|
// Emit disconnected event
|