@dignetwork/chia-block-listener 0.1.9 → 0.1.12
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 +374 -11
- package/crate/chia-generator-parser/src/parser.rs +52 -34
- package/crate/chia-generator-parser/src/types.rs +25 -117
- package/examples/basic-connection-test.js +55 -0
- package/examples/block-refusal-test.js +251 -0
- package/examples/failover-demo.js +225 -0
- package/examples/failover-test.js +122 -0
- package/examples/historical-coin-monitor.js +308 -0
- package/examples/optimized-performance-test.js +267 -0
- package/examples/peer-disconnection-test.js +232 -0
- package/examples/peer-discovery-performance-test.js +345 -0
- package/examples/peer-pool-events-example.js +164 -0
- package/examples/peer-pool-example.js +151 -0
- package/examples/peer-pool-parallel-demo.js +61 -0
- package/examples/peer-pool-peak-tracking.js +179 -0
- package/examples/peer-pool-performance-test.js +274 -0
- package/examples/port-test.js +55 -0
- package/examples/quick-performance-test.js +112 -0
- package/examples/simple-performance-test.js +83 -0
- package/examples/test-connection-diagnostic.js +267 -0
- package/examples/test-peak-height.js +99 -0
- package/index.d.ts +11 -0
- package/index.js +2 -1
- 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/src/error.rs +15 -8
- package/src/event_emitter.rs +21 -14
- package/src/lib.rs +17 -4
- package/src/peer.rs +9 -9
- package/src/peer_pool.rs +971 -0
- package/src/peer_pool_napi.rs +221 -0
- package/src/protocol.rs +46 -39
- package/src/tls.rs +4 -4
package/README.md
CHANGED
|
@@ -6,10 +6,15 @@ A high-performance Chia blockchain listener for Node.js, built with Rust and NAP
|
|
|
6
6
|
|
|
7
7
|
- **Real-time Block Monitoring**: Listen for new blocks as they're produced on the Chia network
|
|
8
8
|
- **Peer Management**: Connect to multiple Chia full nodes simultaneously
|
|
9
|
+
- **Automatic Failover**: Intelligent peer failover with automatic retry across multiple peers
|
|
10
|
+
- **Enhanced Error Handling**: Automatic disconnection of peers that refuse blocks or have protocol errors
|
|
9
11
|
- **Efficient Parsing**: Fast extraction of coin spends, additions, and removals from blocks
|
|
10
12
|
- **Event-Driven Architecture**: TypeScript-friendly event system with full type safety
|
|
11
13
|
- **Transaction Analysis**: Parse CLVM puzzles and solutions from coin spends
|
|
12
|
-
- **Historical Block Access**: Retrieve blocks by height or ranges
|
|
14
|
+
- **Historical Block Access**: Retrieve blocks by height or ranges with automatic load balancing
|
|
15
|
+
- **Connection Pool**: ChiaPeerPool provides automatic load balancing and rate limiting for historical queries
|
|
16
|
+
- **Peak Height Tracking**: Monitor blockchain sync progress across all connected peers
|
|
17
|
+
- **DNS Peer Discovery**: Automatic peer discovery using Chia network DNS introducers
|
|
13
18
|
- **Cross-platform Support**: Works on Windows, macOS, and Linux (x64 and ARM64)
|
|
14
19
|
- **TypeScript Support**: Complete TypeScript definitions with IntelliSense
|
|
15
20
|
|
|
@@ -33,11 +38,11 @@ const listener = new ChiaBlockListener()
|
|
|
33
38
|
// Listen for block events
|
|
34
39
|
listener.on('blockReceived', (block) => {
|
|
35
40
|
console.log(`New block received: ${block.height}`)
|
|
36
|
-
console.log(`Header hash: ${block.
|
|
41
|
+
console.log(`Header hash: ${block.headerHash}`)
|
|
37
42
|
console.log(`Timestamp: ${new Date(block.timestamp * 1000)}`)
|
|
38
|
-
console.log(`Coin additions: ${block.
|
|
39
|
-
console.log(`Coin removals: ${block.
|
|
40
|
-
console.log(`Coin spends: ${block.
|
|
43
|
+
console.log(`Coin additions: ${block.coinAdditions.length}`)
|
|
44
|
+
console.log(`Coin removals: ${block.coinRemovals.length}`)
|
|
45
|
+
console.log(`Coin spends: ${block.coinSpends.length}`)
|
|
41
46
|
})
|
|
42
47
|
|
|
43
48
|
// Listen for peer connection events
|
|
@@ -64,6 +69,30 @@ process.on('SIGINT', () => {
|
|
|
64
69
|
})
|
|
65
70
|
```
|
|
66
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
|
+
|
|
67
96
|
## API Reference
|
|
68
97
|
|
|
69
98
|
### ChiaBlockListener Class
|
|
@@ -127,6 +156,80 @@ Retrieves a range of blocks from a connected peer.
|
|
|
127
156
|
|
|
128
157
|
**Returns:** An array of `BlockReceivedEvent` objects
|
|
129
158
|
|
|
159
|
+
### ChiaPeerPool Class
|
|
160
|
+
|
|
161
|
+
The `ChiaPeerPool` provides a managed pool of peer connections for retrieving historical blocks with automatic load balancing and intelligent failover across multiple peers. When a peer fails to provide a block or experiences protocol errors, the pool automatically tries alternative peers and removes problematic peers from the pool.
|
|
162
|
+
|
|
163
|
+
#### Constructor
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
const pool = new ChiaPeerPool()
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Creates a new peer pool instance with built-in rate limiting (500ms per peer).
|
|
170
|
+
|
|
171
|
+
#### Methods
|
|
172
|
+
|
|
173
|
+
##### `addPeer(host, port, networkId): Promise<string>`
|
|
174
|
+
|
|
175
|
+
Adds a peer to the connection pool.
|
|
176
|
+
|
|
177
|
+
**Parameters:**
|
|
178
|
+
- `host` (string): The hostname or IP address of the Chia node
|
|
179
|
+
- `port` (number): The port number (typically 8444 for mainnet)
|
|
180
|
+
- `networkId` (string): The network identifier ('mainnet', 'testnet', etc.)
|
|
181
|
+
|
|
182
|
+
**Returns:** A Promise that resolves to a unique peer ID string
|
|
183
|
+
|
|
184
|
+
##### `getBlockByHeight(height): Promise<BlockReceivedEvent>`
|
|
185
|
+
|
|
186
|
+
Retrieves a specific block by height using automatic peer selection and load balancing.
|
|
187
|
+
|
|
188
|
+
**Parameters:**
|
|
189
|
+
- `height` (number): The block height to retrieve
|
|
190
|
+
|
|
191
|
+
**Returns:** A Promise that resolves to a `BlockReceivedEvent` object
|
|
192
|
+
|
|
193
|
+
##### `removePeer(peerId): Promise<boolean>`
|
|
194
|
+
|
|
195
|
+
Removes a peer from the pool.
|
|
196
|
+
|
|
197
|
+
**Parameters:**
|
|
198
|
+
- `peerId` (string): The peer ID to remove
|
|
199
|
+
|
|
200
|
+
**Returns:** A Promise that resolves to `true` if the peer was removed, `false` otherwise
|
|
201
|
+
|
|
202
|
+
##### `shutdown(): Promise<void>`
|
|
203
|
+
|
|
204
|
+
Shuts down the pool and disconnects all peers.
|
|
205
|
+
|
|
206
|
+
##### `getConnectedPeers(): Promise<string[]>`
|
|
207
|
+
|
|
208
|
+
Gets the list of currently connected peer IDs.
|
|
209
|
+
|
|
210
|
+
**Returns:** Array of peer ID strings (format: "host:port")
|
|
211
|
+
|
|
212
|
+
##### `getPeakHeight(): Promise<number | null>`
|
|
213
|
+
|
|
214
|
+
Gets the highest blockchain peak height seen across all connected peers.
|
|
215
|
+
|
|
216
|
+
**Returns:** The highest peak height as a number, or null if no peaks have been received yet
|
|
217
|
+
|
|
218
|
+
##### `on(event, callback): void`
|
|
219
|
+
|
|
220
|
+
Registers an event handler for pool events.
|
|
221
|
+
|
|
222
|
+
**Parameters:**
|
|
223
|
+
- `event` (string): The event name ('peerConnected' or 'peerDisconnected')
|
|
224
|
+
- `callback` (function): The event handler function
|
|
225
|
+
|
|
226
|
+
##### `off(event, callback): void`
|
|
227
|
+
|
|
228
|
+
Removes an event handler.
|
|
229
|
+
|
|
230
|
+
**Parameters:**
|
|
231
|
+
- `event` (string): The event name to stop listening for
|
|
232
|
+
|
|
130
233
|
### Events
|
|
131
234
|
|
|
132
235
|
The `ChiaBlockListener` emits the following events:
|
|
@@ -149,6 +252,28 @@ Fired when a peer connection is lost.
|
|
|
149
252
|
|
|
150
253
|
**Callback:** `(event: PeerDisconnectedEvent) => void`
|
|
151
254
|
|
|
255
|
+
### ChiaPeerPool Events
|
|
256
|
+
|
|
257
|
+
The `ChiaPeerPool` emits the following events:
|
|
258
|
+
|
|
259
|
+
#### `peerConnected`
|
|
260
|
+
|
|
261
|
+
Fired when a peer is successfully added to the pool.
|
|
262
|
+
|
|
263
|
+
**Callback:** `(event: PeerConnectedEvent) => void`
|
|
264
|
+
|
|
265
|
+
#### `peerDisconnected`
|
|
266
|
+
|
|
267
|
+
Fired when a peer is removed from the pool or disconnects.
|
|
268
|
+
|
|
269
|
+
**Callback:** `(event: PeerDisconnectedEvent) => void`
|
|
270
|
+
|
|
271
|
+
#### `newPeakHeight`
|
|
272
|
+
|
|
273
|
+
Fired when a new highest blockchain peak is discovered.
|
|
274
|
+
|
|
275
|
+
**Callback:** `(event: NewPeakHeightEvent) => void`
|
|
276
|
+
|
|
152
277
|
### Event Data Types
|
|
153
278
|
|
|
154
279
|
#### `BlockReceivedEvent`
|
|
@@ -190,6 +315,16 @@ interface PeerDisconnectedEvent {
|
|
|
190
315
|
}
|
|
191
316
|
```
|
|
192
317
|
|
|
318
|
+
#### `NewPeakHeightEvent`
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
interface NewPeakHeightEvent {
|
|
322
|
+
oldPeak: number | null // Previous highest peak (null if first peak)
|
|
323
|
+
newPeak: number // New highest peak height
|
|
324
|
+
peerId: string // Peer that discovered this peak
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
193
328
|
#### `CoinRecord`
|
|
194
329
|
|
|
195
330
|
```typescript
|
|
@@ -211,14 +346,219 @@ interface CoinSpend {
|
|
|
211
346
|
}
|
|
212
347
|
```
|
|
213
348
|
|
|
349
|
+
## ChiaPeerPool Usage
|
|
350
|
+
|
|
351
|
+
The `ChiaPeerPool` is designed for efficiently retrieving historical blocks with automatic load balancing and intelligent failover across multiple peers. When a peer fails to provide a block or experiences protocol errors, the pool automatically tries alternative peers and removes problematic peers from the pool.
|
|
352
|
+
|
|
353
|
+
### Basic Usage
|
|
354
|
+
|
|
355
|
+
```javascript
|
|
356
|
+
const { ChiaPeerPool, initTracing } = require('@dignetwork/chia-block-listener')
|
|
357
|
+
|
|
358
|
+
async function main() {
|
|
359
|
+
// Initialize tracing
|
|
360
|
+
initTracing()
|
|
361
|
+
|
|
362
|
+
// Create a peer pool
|
|
363
|
+
const pool = new ChiaPeerPool()
|
|
364
|
+
|
|
365
|
+
// Listen for pool events
|
|
366
|
+
pool.on('peerConnected', (event) => {
|
|
367
|
+
console.log(`Peer connected to pool: ${event.peerId}`)
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
pool.on('peerDisconnected', (event) => {
|
|
371
|
+
console.log(`Peer disconnected from pool: ${event.peerId}`)
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
pool.on('newPeakHeight', (event) => {
|
|
375
|
+
console.log(`New blockchain peak detected!`)
|
|
376
|
+
console.log(` Previous: ${event.oldPeak || 'None'}`)
|
|
377
|
+
console.log(` New: ${event.newPeak}`)
|
|
378
|
+
console.log(` Discovered by: ${event.peerId}`)
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
// Add multiple peers
|
|
382
|
+
await pool.addPeer('node1.chia.net', 8444, 'mainnet')
|
|
383
|
+
await pool.addPeer('node2.chia.net', 8444, 'mainnet')
|
|
384
|
+
await pool.addPeer('node3.chia.net', 8444, 'mainnet')
|
|
385
|
+
|
|
386
|
+
// Fetch blocks with automatic load balancing
|
|
387
|
+
const block1 = await pool.getBlockByHeight(5000000)
|
|
388
|
+
const block2 = await pool.getBlockByHeight(5000001)
|
|
389
|
+
const block3 = await pool.getBlockByHeight(5000002)
|
|
390
|
+
|
|
391
|
+
console.log(`Block ${block1.height}: ${block1.coinSpends.length} spends`)
|
|
392
|
+
console.log(`Block ${block2.height}: ${block2.coinSpends.length} spends`)
|
|
393
|
+
console.log(`Block ${block3.height}: ${block3.coinSpends.length} spends`)
|
|
394
|
+
|
|
395
|
+
// Shutdown the pool
|
|
396
|
+
await pool.shutdown()
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
main().catch(console.error)
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Advanced Pool Features
|
|
403
|
+
|
|
404
|
+
#### Rate Limiting
|
|
405
|
+
|
|
406
|
+
The pool automatically enforces a 50ms rate limit per peer for maximum performance while preventing node overload:
|
|
407
|
+
|
|
408
|
+
```javascript
|
|
409
|
+
// Rapid requests are automatically queued and distributed
|
|
410
|
+
const promises = []
|
|
411
|
+
for (let i = 5000000; i < 5000100; i++) {
|
|
412
|
+
promises.push(pool.getBlockByHeight(i))
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// All requests will be processed efficiently across all peers
|
|
416
|
+
// with automatic load balancing and rate limiting
|
|
417
|
+
const blocks = await Promise.all(promises)
|
|
418
|
+
console.log(`Retrieved ${blocks.length} blocks`)
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
#### Automatic Failover and Error Handling
|
|
422
|
+
|
|
423
|
+
The pool provides robust error handling with automatic failover:
|
|
424
|
+
|
|
425
|
+
```javascript
|
|
426
|
+
// The pool automatically handles various error scenarios:
|
|
427
|
+
|
|
428
|
+
// 1. Connection failures - automatically tries other peers
|
|
429
|
+
try {
|
|
430
|
+
const block = await pool.getBlockByHeight(5000000)
|
|
431
|
+
console.log(`Retrieved block ${block.height}`)
|
|
432
|
+
} catch (error) {
|
|
433
|
+
// If all peers fail, you'll get an error after all retry attempts
|
|
434
|
+
console.error('All peers failed:', error.message)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// 2. Protocol errors - peers that refuse blocks are automatically disconnected
|
|
438
|
+
pool.on('peerDisconnected', (event) => {
|
|
439
|
+
console.log(`Peer ${event.peerId} disconnected: ${event.reason}`)
|
|
440
|
+
// Reasons include: "Block request rejected", "Protocol error", "Connection timeout"
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
// 3. Automatic peer cleanup - problematic peers are removed from the pool
|
|
444
|
+
console.log('Active peers before:', await pool.getConnectedPeers())
|
|
445
|
+
await pool.getBlockByHeight(5000000) // May trigger peer removal
|
|
446
|
+
console.log('Active peers after:', await pool.getConnectedPeers())
|
|
447
|
+
|
|
448
|
+
// 4. Multiple retry attempts - tries up to 3 different peers per request
|
|
449
|
+
// This happens automatically and transparently
|
|
450
|
+
const block = await pool.getBlockByHeight(5000000) // Will try multiple peers if needed
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
**Error Types Handled Automatically:**
|
|
454
|
+
- **Connection Errors**: Timeouts, network failures, WebSocket errors
|
|
455
|
+
- **Protocol Errors**: Block rejections, parsing failures, handshake failures
|
|
456
|
+
- **Peer Misbehavior**: Unexpected responses, invalid data formats
|
|
457
|
+
|
|
458
|
+
#### Dynamic Peer Management
|
|
459
|
+
|
|
460
|
+
```javascript
|
|
461
|
+
// Monitor pool health
|
|
462
|
+
const peers = await pool.getConnectedPeers()
|
|
463
|
+
console.log(`Active peers in pool: ${peers.length}`)
|
|
464
|
+
|
|
465
|
+
// Remove underperforming peers
|
|
466
|
+
if (slowPeer) {
|
|
467
|
+
await pool.removePeer(slowPeer)
|
|
468
|
+
console.log('Removed slow peer from pool')
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Add new peers dynamically
|
|
472
|
+
if (peers.length < 3) {
|
|
473
|
+
await pool.addPeer('backup-node.chia.net', 8444, 'mainnet')
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
#### Error Handling
|
|
478
|
+
|
|
479
|
+
```javascript
|
|
480
|
+
try {
|
|
481
|
+
const block = await pool.getBlockByHeight(5000000)
|
|
482
|
+
console.log(`Retrieved block ${block.height}`)
|
|
483
|
+
} catch (error) {
|
|
484
|
+
console.error('Failed to retrieve block:', error)
|
|
485
|
+
|
|
486
|
+
// The pool will automatically try other peers
|
|
487
|
+
// You can also add more peers if needed
|
|
488
|
+
const peers = await pool.getConnectedPeers()
|
|
489
|
+
if (peers.length === 0) {
|
|
490
|
+
console.log('No peers available, adding new ones...')
|
|
491
|
+
await pool.addPeer('node1.chia.net', 8444, 'mainnet')
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
#### Peak Height Tracking
|
|
497
|
+
|
|
498
|
+
```javascript
|
|
499
|
+
// Monitor blockchain sync progress
|
|
500
|
+
const pool = new ChiaPeerPool()
|
|
501
|
+
|
|
502
|
+
// Track peak changes
|
|
503
|
+
let currentPeak = null
|
|
504
|
+
pool.on('newPeakHeight', (event) => {
|
|
505
|
+
currentPeak = event.newPeak
|
|
506
|
+
const progress = event.oldPeak
|
|
507
|
+
? `+${event.newPeak - event.oldPeak} blocks`
|
|
508
|
+
: 'Initial peak'
|
|
509
|
+
console.log(`Peak update: ${event.newPeak} (${progress})`)
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
// Add peers
|
|
513
|
+
await pool.addPeer('node1.chia.net', 8444, 'mainnet')
|
|
514
|
+
await pool.addPeer('node2.chia.net', 8444, 'mainnet')
|
|
515
|
+
|
|
516
|
+
// Check current peak
|
|
517
|
+
const peak = await pool.getPeakHeight()
|
|
518
|
+
console.log(`Current highest peak: ${peak || 'None yet'}`)
|
|
519
|
+
|
|
520
|
+
// Fetch some blocks to trigger peak updates
|
|
521
|
+
await pool.getBlockByHeight(5000000)
|
|
522
|
+
await pool.getBlockByHeight(5100000)
|
|
523
|
+
await pool.getBlockByHeight(5200000)
|
|
524
|
+
|
|
525
|
+
// Monitor sync status
|
|
526
|
+
setInterval(async () => {
|
|
527
|
+
const peak = await pool.getPeakHeight()
|
|
528
|
+
if (peak) {
|
|
529
|
+
const estimatedCurrent = 5200000 + Math.floor((Date.now() / 1000 - 1700000000) / 18.75)
|
|
530
|
+
const syncPercentage = (peak / estimatedCurrent * 100).toFixed(2)
|
|
531
|
+
console.log(`Sync status: ${syncPercentage}% (peak: ${peak})`)
|
|
532
|
+
}
|
|
533
|
+
}, 60000) // Check every minute
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### When to Use ChiaPeerPool vs ChiaBlockListener
|
|
537
|
+
|
|
538
|
+
- **Use ChiaPeerPool when:**
|
|
539
|
+
- You need to fetch historical blocks
|
|
540
|
+
- You want automatic load balancing across multiple peers
|
|
541
|
+
- You're making many block requests and need rate limiting
|
|
542
|
+
- You don't need real-time block notifications
|
|
543
|
+
|
|
544
|
+
- **Use ChiaBlockListener when:**
|
|
545
|
+
- You need real-time notifications of new blocks
|
|
546
|
+
- You want to monitor the blockchain as it grows
|
|
547
|
+
- You need to track specific addresses or puzzle hashes in real-time
|
|
548
|
+
- You're building applications that react to blockchain events
|
|
549
|
+
|
|
550
|
+
Both classes can be used together in the same application for different purposes.
|
|
551
|
+
|
|
214
552
|
## TypeScript Usage
|
|
215
553
|
|
|
216
554
|
```typescript
|
|
217
555
|
import {
|
|
218
556
|
ChiaBlockListener,
|
|
557
|
+
ChiaPeerPool,
|
|
219
558
|
BlockReceivedEvent,
|
|
220
559
|
PeerConnectedEvent,
|
|
221
560
|
PeerDisconnectedEvent,
|
|
561
|
+
NewPeakHeightEvent,
|
|
222
562
|
CoinRecord,
|
|
223
563
|
CoinSpend,
|
|
224
564
|
initTracing,
|
|
@@ -236,14 +576,14 @@ listener.on('blockReceived', (block: BlockReceivedEvent) => {
|
|
|
236
576
|
console.log(`Block ${block.height} from peer ${block.peerId}`)
|
|
237
577
|
|
|
238
578
|
// Process coin additions
|
|
239
|
-
block.
|
|
579
|
+
block.coinAdditions.forEach((coin: CoinRecord) => {
|
|
240
580
|
console.log(`New coin: ${coin.amount} mojos`)
|
|
241
581
|
})
|
|
242
582
|
|
|
243
583
|
// Process coin spends
|
|
244
|
-
block.
|
|
584
|
+
block.coinSpends.forEach((spend: CoinSpend) => {
|
|
245
585
|
console.log(`Spend: ${spend.coin.amount} mojos`)
|
|
246
|
-
console.log(`Puzzle: ${spend.
|
|
586
|
+
console.log(`Puzzle: ${spend.puzzleReveal}`)
|
|
247
587
|
console.log(`Solution: ${spend.solution}`)
|
|
248
588
|
})
|
|
249
589
|
})
|
|
@@ -267,7 +607,7 @@ const testnetPeer = listener.addPeer('testnet-node.chia.net', 58444, 'testnet')
|
|
|
267
607
|
async function getHistoricalBlocks() {
|
|
268
608
|
try {
|
|
269
609
|
const block = listener.getBlockByHeight(mainnetPeer, 1000000)
|
|
270
|
-
console.log(`Block 1000000 hash: ${block.
|
|
610
|
+
console.log(`Block 1000000 hash: ${block.headerHash}`)
|
|
271
611
|
|
|
272
612
|
const blocks = listener.getBlocksRange(mainnetPeer, 1000000, 1000010)
|
|
273
613
|
console.log(`Retrieved ${blocks.length} blocks`)
|
|
@@ -279,6 +619,29 @@ async function getHistoricalBlocks() {
|
|
|
279
619
|
// Get event type constants
|
|
280
620
|
const eventTypes = getEventTypes()
|
|
281
621
|
console.log('Available events:', eventTypes)
|
|
622
|
+
|
|
623
|
+
// TypeScript support for ChiaPeerPool
|
|
624
|
+
const pool = new ChiaPeerPool()
|
|
625
|
+
|
|
626
|
+
// Type-safe event handling
|
|
627
|
+
pool.on('peerConnected', (event: PeerConnectedEvent) => {
|
|
628
|
+
console.log(`Pool peer connected: ${event.peerId}`)
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
pool.on('newPeakHeight', (event: NewPeakHeightEvent) => {
|
|
632
|
+
console.log(`New peak: ${event.oldPeak} → ${event.newPeak}`)
|
|
633
|
+
})
|
|
634
|
+
|
|
635
|
+
// Async/await with proper typing
|
|
636
|
+
async function fetchHistoricalData() {
|
|
637
|
+
const block: BlockReceivedEvent = await pool.getBlockByHeight(5000000)
|
|
638
|
+
const peers: string[] = await pool.getConnectedPeers()
|
|
639
|
+
const peak: number | null = await pool.getPeakHeight()
|
|
640
|
+
|
|
641
|
+
console.log(`Block ${block.height} has ${block.coinSpends.length} spends`)
|
|
642
|
+
console.log(`Pool has ${peers.length} active peers`)
|
|
643
|
+
console.log(`Current peak: ${peak || 'No peak yet'}`)
|
|
644
|
+
}
|
|
282
645
|
```
|
|
283
646
|
|
|
284
647
|
## Advanced Usage
|
|
@@ -290,8 +653,8 @@ console.log('Available events:', eventTypes)
|
|
|
290
653
|
listener.on('blockReceived', (block) => {
|
|
291
654
|
const targetPuzzleHash = '0x1234...' // Your puzzle hash
|
|
292
655
|
|
|
293
|
-
block.
|
|
294
|
-
if (spend.coin.
|
|
656
|
+
block.coinSpends.forEach((spend) => {
|
|
657
|
+
if (spend.coin.puzzleHash === targetPuzzleHash) {
|
|
295
658
|
console.log('Found spend for our puzzle!')
|
|
296
659
|
console.log('Amount:', spend.coin.amount)
|
|
297
660
|
console.log('Solution:', spend.solution)
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
error::{GeneratorParserError, Result},
|
|
3
|
-
types::{
|
|
4
|
-
BlockHeightInfo, CoinInfo, CoinSpendInfo, GeneratorAnalysis, GeneratorBlockInfo,
|
|
5
|
-
ParsedBlock, ParsedGenerator,
|
|
6
|
-
},
|
|
3
|
+
types::{BlockHeightInfo, CoinInfo, CoinSpendInfo, GeneratorBlockInfo, ParsedBlock},
|
|
7
4
|
};
|
|
8
5
|
use chia_bls::Signature;
|
|
9
6
|
use chia_consensus::{
|
|
@@ -14,7 +11,7 @@ use chia_consensus::{
|
|
|
14
11
|
run_block_generator::{run_block_generator2, setup_generator_args},
|
|
15
12
|
validation_error::{atom, first, next, rest, ErrorCode},
|
|
16
13
|
};
|
|
17
|
-
use chia_protocol::
|
|
14
|
+
use chia_protocol::FullBlock;
|
|
18
15
|
use chia_traits::streamable::Streamable;
|
|
19
16
|
use clvm_utils::tree_hash;
|
|
20
17
|
use clvmr::{
|
|
@@ -63,7 +60,7 @@ impl BlockParser {
|
|
|
63
60
|
.map(|g| g.len() as u32);
|
|
64
61
|
|
|
65
62
|
// Extract generator info
|
|
66
|
-
let
|
|
63
|
+
let _generator_info = block
|
|
67
64
|
.transactions_generator
|
|
68
65
|
.as_ref()
|
|
69
66
|
.map(|gen| GeneratorBlockInfo {
|
|
@@ -101,7 +98,6 @@ impl BlockParser {
|
|
|
101
98
|
coin_creations,
|
|
102
99
|
has_transactions_generator,
|
|
103
100
|
generator_size,
|
|
104
|
-
generator_info,
|
|
105
101
|
})
|
|
106
102
|
}
|
|
107
103
|
|
|
@@ -373,15 +369,15 @@ impl BlockParser {
|
|
|
373
369
|
// Get created coins from conditions
|
|
374
370
|
let created_coins = self.extract_created_coins(spend_index, spend_bundle_conditions);
|
|
375
371
|
|
|
376
|
-
Some(CoinSpendInfo
|
|
377
|
-
|
|
378
|
-
puzzle_reveal,
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
372
|
+
Some(CoinSpendInfo::new(
|
|
373
|
+
coin_info,
|
|
374
|
+
hex::encode(puzzle_reveal),
|
|
375
|
+
hex::encode(solution_bytes),
|
|
376
|
+
true,
|
|
377
|
+
"From transaction generator".to_string(),
|
|
378
|
+
0,
|
|
383
379
|
created_coins,
|
|
384
|
-
|
|
380
|
+
))
|
|
385
381
|
}
|
|
386
382
|
|
|
387
383
|
/// Extract parent coin info from a coin spend node
|
|
@@ -464,42 +460,62 @@ impl BlockParser {
|
|
|
464
460
|
})
|
|
465
461
|
}
|
|
466
462
|
|
|
463
|
+
/*
|
|
467
464
|
/// Parse generator from hex string
|
|
468
465
|
pub fn parse_generator_from_hex(&self, generator_hex: &str) -> Result<ParsedGenerator> {
|
|
469
466
|
let generator_bytes =
|
|
470
|
-
hex::decode(generator_hex).map_err(|e|
|
|
467
|
+
hex::decode(generator_hex).map_err(|e| ParseError::HexDecodingError(e))?;
|
|
471
468
|
self.parse_generator_from_bytes(&generator_bytes)
|
|
472
469
|
}
|
|
473
470
|
|
|
474
471
|
/// Parse generator from bytes
|
|
475
472
|
pub fn parse_generator_from_bytes(&self, generator_bytes: &[u8]) -> Result<ParsedGenerator> {
|
|
476
|
-
|
|
477
|
-
|
|
473
|
+
// Create a dummy GeneratorBlockInfo for now
|
|
478
474
|
Ok(ParsedGenerator {
|
|
479
|
-
block_info: GeneratorBlockInfo
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
475
|
+
block_info: GeneratorBlockInfo::new(
|
|
476
|
+
[0u8; 32].into(),
|
|
477
|
+
Some(generator_bytes.to_vec()),
|
|
478
|
+
vec![],
|
|
479
|
+
),
|
|
484
480
|
generator_hex: Some(hex::encode(generator_bytes)),
|
|
485
|
-
analysis
|
|
481
|
+
analysis: self.analyze_generator(generator_bytes)?,
|
|
486
482
|
})
|
|
487
483
|
}
|
|
488
484
|
|
|
489
485
|
/// Analyze generator bytecode
|
|
490
486
|
pub fn analyze_generator(&self, generator_bytes: &[u8]) -> Result<GeneratorAnalysis> {
|
|
491
487
|
let size_bytes = generator_bytes.len();
|
|
492
|
-
let is_empty =
|
|
493
|
-
|
|
494
|
-
// Check for CLVM patterns
|
|
495
|
-
let contains_clvm_patterns = generator_bytes
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
488
|
+
let is_empty = generator_bytes.is_empty();
|
|
489
|
+
|
|
490
|
+
// Check for common CLVM patterns
|
|
491
|
+
let contains_clvm_patterns = generator_bytes.windows(2).any(|w| {
|
|
492
|
+
w == [0x01, 0x00] || // pair
|
|
493
|
+
w == [0x02, 0x00] || // cons
|
|
494
|
+
w == [0x03, 0x00] || // first
|
|
495
|
+
w == [0x04, 0x00] // rest
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
// Check for coin patterns (32-byte sequences)
|
|
499
|
+
let contains_coin_patterns = generator_bytes.len() >= 32;
|
|
500
|
+
|
|
501
|
+
// Calculate simple entropy
|
|
502
|
+
let mut byte_counts = [0u64; 256];
|
|
503
|
+
for &byte in generator_bytes {
|
|
504
|
+
byte_counts[byte as usize] += 1;
|
|
505
|
+
}
|
|
501
506
|
|
|
502
|
-
let
|
|
507
|
+
let total = generator_bytes.len() as f64;
|
|
508
|
+
let entropy = if total > 0.0 {
|
|
509
|
+
byte_counts.iter()
|
|
510
|
+
.filter(|&&count| count > 0)
|
|
511
|
+
.map(|&count| {
|
|
512
|
+
let p = count as f64 / total;
|
|
513
|
+
-p * p.log2()
|
|
514
|
+
})
|
|
515
|
+
.sum()
|
|
516
|
+
} else {
|
|
517
|
+
0.0
|
|
518
|
+
};
|
|
503
519
|
|
|
504
520
|
Ok(GeneratorAnalysis {
|
|
505
521
|
size_bytes,
|
|
@@ -509,8 +525,10 @@ impl BlockParser {
|
|
|
509
525
|
entropy,
|
|
510
526
|
})
|
|
511
527
|
}
|
|
528
|
+
*/
|
|
512
529
|
|
|
513
530
|
/// Calculate Shannon entropy of data
|
|
531
|
+
#[allow(dead_code)]
|
|
514
532
|
fn calculate_entropy(&self, data: &[u8]) -> f64 {
|
|
515
533
|
if data.is_empty() {
|
|
516
534
|
return 0.0;
|