@dignetwork/chia-block-listener 0.1.9 → 0.1.11
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 +301 -4
- package/crate/chia-generator-parser/src/parser.rs +52 -34
- package/crate/chia-generator-parser/src/types.rs +25 -117
- package/examples/historical-coin-monitor.js +308 -0
- package/examples/peer-pool-events-example.js +164 -0
- package/examples/peer-pool-example.js +151 -0
- package/examples/peer-pool-peak-tracking.js +179 -0
- package/examples/test-connection-diagnostic.js +267 -0
- package/index.d.ts +44 -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 +492 -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
|
@@ -10,6 +10,7 @@ A high-performance Chia blockchain listener for Node.js, built with Rust and NAP
|
|
|
10
10
|
- **Event-Driven Architecture**: TypeScript-friendly event system with full type safety
|
|
11
11
|
- **Transaction Analysis**: Parse CLVM puzzles and solutions from coin spends
|
|
12
12
|
- **Historical Block Access**: Retrieve blocks by height or ranges
|
|
13
|
+
- **Connection Pool**: ChiaPeerPool provides automatic load balancing and rate limiting for historical queries
|
|
13
14
|
- **Cross-platform Support**: Works on Windows, macOS, and Linux (x64 and ARM64)
|
|
14
15
|
- **TypeScript Support**: Complete TypeScript definitions with IntelliSense
|
|
15
16
|
|
|
@@ -33,11 +34,11 @@ const listener = new ChiaBlockListener()
|
|
|
33
34
|
// Listen for block events
|
|
34
35
|
listener.on('blockReceived', (block) => {
|
|
35
36
|
console.log(`New block received: ${block.height}`)
|
|
36
|
-
console.log(`Header hash: ${block.
|
|
37
|
+
console.log(`Header hash: ${block.headerHash}`)
|
|
37
38
|
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.
|
|
39
|
+
console.log(`Coin additions: ${block.coinAdditions.length}`)
|
|
40
|
+
console.log(`Coin removals: ${block.coinRemovals.length}`)
|
|
41
|
+
console.log(`Coin spends: ${block.coinSpends.length}`)
|
|
41
42
|
})
|
|
42
43
|
|
|
43
44
|
// Listen for peer connection events
|
|
@@ -127,6 +128,80 @@ Retrieves a range of blocks from a connected peer.
|
|
|
127
128
|
|
|
128
129
|
**Returns:** An array of `BlockReceivedEvent` objects
|
|
129
130
|
|
|
131
|
+
### ChiaPeerPool Class
|
|
132
|
+
|
|
133
|
+
The `ChiaPeerPool` provides a managed pool of peer connections for retrieving historical blocks with automatic load balancing and rate limiting.
|
|
134
|
+
|
|
135
|
+
#### Constructor
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
const pool = new ChiaPeerPool()
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Creates a new peer pool instance with built-in rate limiting (500ms per peer).
|
|
142
|
+
|
|
143
|
+
#### Methods
|
|
144
|
+
|
|
145
|
+
##### `addPeer(host, port, networkId): Promise<string>`
|
|
146
|
+
|
|
147
|
+
Adds a peer to the connection pool.
|
|
148
|
+
|
|
149
|
+
**Parameters:**
|
|
150
|
+
- `host` (string): The hostname or IP address of the Chia node
|
|
151
|
+
- `port` (number): The port number (typically 8444 for mainnet)
|
|
152
|
+
- `networkId` (string): The network identifier ('mainnet', 'testnet', etc.)
|
|
153
|
+
|
|
154
|
+
**Returns:** A Promise that resolves to a unique peer ID string
|
|
155
|
+
|
|
156
|
+
##### `getBlockByHeight(height): Promise<BlockReceivedEvent>`
|
|
157
|
+
|
|
158
|
+
Retrieves a specific block by height using automatic peer selection and load balancing.
|
|
159
|
+
|
|
160
|
+
**Parameters:**
|
|
161
|
+
- `height` (number): The block height to retrieve
|
|
162
|
+
|
|
163
|
+
**Returns:** A Promise that resolves to a `BlockReceivedEvent` object
|
|
164
|
+
|
|
165
|
+
##### `removePeer(peerId): Promise<boolean>`
|
|
166
|
+
|
|
167
|
+
Removes a peer from the pool.
|
|
168
|
+
|
|
169
|
+
**Parameters:**
|
|
170
|
+
- `peerId` (string): The peer ID to remove
|
|
171
|
+
|
|
172
|
+
**Returns:** A Promise that resolves to `true` if the peer was removed, `false` otherwise
|
|
173
|
+
|
|
174
|
+
##### `shutdown(): Promise<void>`
|
|
175
|
+
|
|
176
|
+
Shuts down the pool and disconnects all peers.
|
|
177
|
+
|
|
178
|
+
##### `getConnectedPeers(): Promise<string[]>`
|
|
179
|
+
|
|
180
|
+
Gets the list of currently connected peer IDs.
|
|
181
|
+
|
|
182
|
+
**Returns:** Array of peer ID strings (format: "host:port")
|
|
183
|
+
|
|
184
|
+
##### `getPeakHeight(): Promise<number | null>`
|
|
185
|
+
|
|
186
|
+
Gets the highest blockchain peak height seen across all connected peers.
|
|
187
|
+
|
|
188
|
+
**Returns:** The highest peak height as a number, or null if no peaks have been received yet
|
|
189
|
+
|
|
190
|
+
##### `on(event, callback): void`
|
|
191
|
+
|
|
192
|
+
Registers an event handler for pool events.
|
|
193
|
+
|
|
194
|
+
**Parameters:**
|
|
195
|
+
- `event` (string): The event name ('peerConnected' or 'peerDisconnected')
|
|
196
|
+
- `callback` (function): The event handler function
|
|
197
|
+
|
|
198
|
+
##### `off(event, callback): void`
|
|
199
|
+
|
|
200
|
+
Removes an event handler.
|
|
201
|
+
|
|
202
|
+
**Parameters:**
|
|
203
|
+
- `event` (string): The event name to stop listening for
|
|
204
|
+
|
|
130
205
|
### Events
|
|
131
206
|
|
|
132
207
|
The `ChiaBlockListener` emits the following events:
|
|
@@ -149,6 +224,28 @@ Fired when a peer connection is lost.
|
|
|
149
224
|
|
|
150
225
|
**Callback:** `(event: PeerDisconnectedEvent) => void`
|
|
151
226
|
|
|
227
|
+
### ChiaPeerPool Events
|
|
228
|
+
|
|
229
|
+
The `ChiaPeerPool` emits the following events:
|
|
230
|
+
|
|
231
|
+
#### `peerConnected`
|
|
232
|
+
|
|
233
|
+
Fired when a peer is successfully added to the pool.
|
|
234
|
+
|
|
235
|
+
**Callback:** `(event: PeerConnectedEvent) => void`
|
|
236
|
+
|
|
237
|
+
#### `peerDisconnected`
|
|
238
|
+
|
|
239
|
+
Fired when a peer is removed from the pool or disconnects.
|
|
240
|
+
|
|
241
|
+
**Callback:** `(event: PeerDisconnectedEvent) => void`
|
|
242
|
+
|
|
243
|
+
#### `newPeakHeight`
|
|
244
|
+
|
|
245
|
+
Fired when a new highest blockchain peak is discovered.
|
|
246
|
+
|
|
247
|
+
**Callback:** `(event: NewPeakHeightEvent) => void`
|
|
248
|
+
|
|
152
249
|
### Event Data Types
|
|
153
250
|
|
|
154
251
|
#### `BlockReceivedEvent`
|
|
@@ -190,6 +287,16 @@ interface PeerDisconnectedEvent {
|
|
|
190
287
|
}
|
|
191
288
|
```
|
|
192
289
|
|
|
290
|
+
#### `NewPeakHeightEvent`
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
interface NewPeakHeightEvent {
|
|
294
|
+
oldPeak: number | null // Previous highest peak (null if first peak)
|
|
295
|
+
newPeak: number // New highest peak height
|
|
296
|
+
peerId: string // Peer that discovered this peak
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
193
300
|
#### `CoinRecord`
|
|
194
301
|
|
|
195
302
|
```typescript
|
|
@@ -211,14 +318,181 @@ interface CoinSpend {
|
|
|
211
318
|
}
|
|
212
319
|
```
|
|
213
320
|
|
|
321
|
+
## ChiaPeerPool Usage
|
|
322
|
+
|
|
323
|
+
The `ChiaPeerPool` is designed for efficiently retrieving historical blocks with automatic load balancing across multiple peers.
|
|
324
|
+
|
|
325
|
+
### Basic Usage
|
|
326
|
+
|
|
327
|
+
```javascript
|
|
328
|
+
const { ChiaPeerPool, initTracing } = require('@dignetwork/chia-block-listener')
|
|
329
|
+
|
|
330
|
+
async function main() {
|
|
331
|
+
// Initialize tracing
|
|
332
|
+
initTracing()
|
|
333
|
+
|
|
334
|
+
// Create a peer pool
|
|
335
|
+
const pool = new ChiaPeerPool()
|
|
336
|
+
|
|
337
|
+
// Listen for pool events
|
|
338
|
+
pool.on('peerConnected', (event) => {
|
|
339
|
+
console.log(`Peer connected to pool: ${event.peerId}`)
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
pool.on('peerDisconnected', (event) => {
|
|
343
|
+
console.log(`Peer disconnected from pool: ${event.peerId}`)
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
pool.on('newPeakHeight', (event) => {
|
|
347
|
+
console.log(`New blockchain peak detected!`)
|
|
348
|
+
console.log(` Previous: ${event.oldPeak || 'None'}`)
|
|
349
|
+
console.log(` New: ${event.newPeak}`)
|
|
350
|
+
console.log(` Discovered by: ${event.peerId}`)
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
// Add multiple peers
|
|
354
|
+
await pool.addPeer('node1.chia.net', 8444, 'mainnet')
|
|
355
|
+
await pool.addPeer('node2.chia.net', 8444, 'mainnet')
|
|
356
|
+
await pool.addPeer('node3.chia.net', 8444, 'mainnet')
|
|
357
|
+
|
|
358
|
+
// Fetch blocks with automatic load balancing
|
|
359
|
+
const block1 = await pool.getBlockByHeight(5000000)
|
|
360
|
+
const block2 = await pool.getBlockByHeight(5000001)
|
|
361
|
+
const block3 = await pool.getBlockByHeight(5000002)
|
|
362
|
+
|
|
363
|
+
console.log(`Block ${block1.height}: ${block1.coinSpends.length} spends`)
|
|
364
|
+
console.log(`Block ${block2.height}: ${block2.coinSpends.length} spends`)
|
|
365
|
+
console.log(`Block ${block3.height}: ${block3.coinSpends.length} spends`)
|
|
366
|
+
|
|
367
|
+
// Shutdown the pool
|
|
368
|
+
await pool.shutdown()
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
main().catch(console.error)
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Advanced Pool Features
|
|
375
|
+
|
|
376
|
+
#### Rate Limiting
|
|
377
|
+
|
|
378
|
+
The pool automatically enforces a 500ms rate limit per peer to prevent overwhelming any single node:
|
|
379
|
+
|
|
380
|
+
```javascript
|
|
381
|
+
// Rapid requests are automatically queued and distributed
|
|
382
|
+
const promises = []
|
|
383
|
+
for (let i = 5000000; i < 5000100; i++) {
|
|
384
|
+
promises.push(pool.getBlockByHeight(i))
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// All requests will be processed efficiently across all peers
|
|
388
|
+
const blocks = await Promise.all(promises)
|
|
389
|
+
console.log(`Retrieved ${blocks.length} blocks`)
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
#### Dynamic Peer Management
|
|
393
|
+
|
|
394
|
+
```javascript
|
|
395
|
+
// Monitor pool health
|
|
396
|
+
const peers = await pool.getConnectedPeers()
|
|
397
|
+
console.log(`Active peers in pool: ${peers.length}`)
|
|
398
|
+
|
|
399
|
+
// Remove underperforming peers
|
|
400
|
+
if (slowPeer) {
|
|
401
|
+
await pool.removePeer(slowPeer)
|
|
402
|
+
console.log('Removed slow peer from pool')
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Add new peers dynamically
|
|
406
|
+
if (peers.length < 3) {
|
|
407
|
+
await pool.addPeer('backup-node.chia.net', 8444, 'mainnet')
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
#### Error Handling
|
|
412
|
+
|
|
413
|
+
```javascript
|
|
414
|
+
try {
|
|
415
|
+
const block = await pool.getBlockByHeight(5000000)
|
|
416
|
+
console.log(`Retrieved block ${block.height}`)
|
|
417
|
+
} catch (error) {
|
|
418
|
+
console.error('Failed to retrieve block:', error)
|
|
419
|
+
|
|
420
|
+
// The pool will automatically try other peers
|
|
421
|
+
// You can also add more peers if needed
|
|
422
|
+
const peers = await pool.getConnectedPeers()
|
|
423
|
+
if (peers.length === 0) {
|
|
424
|
+
console.log('No peers available, adding new ones...')
|
|
425
|
+
await pool.addPeer('node1.chia.net', 8444, 'mainnet')
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
#### Peak Height Tracking
|
|
431
|
+
|
|
432
|
+
```javascript
|
|
433
|
+
// Monitor blockchain sync progress
|
|
434
|
+
const pool = new ChiaPeerPool()
|
|
435
|
+
|
|
436
|
+
// Track peak changes
|
|
437
|
+
let currentPeak = null
|
|
438
|
+
pool.on('newPeakHeight', (event) => {
|
|
439
|
+
currentPeak = event.newPeak
|
|
440
|
+
const progress = event.oldPeak
|
|
441
|
+
? `+${event.newPeak - event.oldPeak} blocks`
|
|
442
|
+
: 'Initial peak'
|
|
443
|
+
console.log(`Peak update: ${event.newPeak} (${progress})`)
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
// Add peers
|
|
447
|
+
await pool.addPeer('node1.chia.net', 8444, 'mainnet')
|
|
448
|
+
await pool.addPeer('node2.chia.net', 8444, 'mainnet')
|
|
449
|
+
|
|
450
|
+
// Check current peak
|
|
451
|
+
const peak = await pool.getPeakHeight()
|
|
452
|
+
console.log(`Current highest peak: ${peak || 'None yet'}`)
|
|
453
|
+
|
|
454
|
+
// Fetch some blocks to trigger peak updates
|
|
455
|
+
await pool.getBlockByHeight(5000000)
|
|
456
|
+
await pool.getBlockByHeight(5100000)
|
|
457
|
+
await pool.getBlockByHeight(5200000)
|
|
458
|
+
|
|
459
|
+
// Monitor sync status
|
|
460
|
+
setInterval(async () => {
|
|
461
|
+
const peak = await pool.getPeakHeight()
|
|
462
|
+
if (peak) {
|
|
463
|
+
const estimatedCurrent = 5200000 + Math.floor((Date.now() / 1000 - 1700000000) / 18.75)
|
|
464
|
+
const syncPercentage = (peak / estimatedCurrent * 100).toFixed(2)
|
|
465
|
+
console.log(`Sync status: ${syncPercentage}% (peak: ${peak})`)
|
|
466
|
+
}
|
|
467
|
+
}, 60000) // Check every minute
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### When to Use ChiaPeerPool vs ChiaBlockListener
|
|
471
|
+
|
|
472
|
+
- **Use ChiaPeerPool when:**
|
|
473
|
+
- You need to fetch historical blocks
|
|
474
|
+
- You want automatic load balancing across multiple peers
|
|
475
|
+
- You're making many block requests and need rate limiting
|
|
476
|
+
- You don't need real-time block notifications
|
|
477
|
+
|
|
478
|
+
- **Use ChiaBlockListener when:**
|
|
479
|
+
- You need real-time notifications of new blocks
|
|
480
|
+
- You want to monitor the blockchain as it grows
|
|
481
|
+
- You need to track specific addresses or puzzle hashes in real-time
|
|
482
|
+
- You're building applications that react to blockchain events
|
|
483
|
+
|
|
484
|
+
Both classes can be used together in the same application for different purposes.
|
|
485
|
+
|
|
214
486
|
## TypeScript Usage
|
|
215
487
|
|
|
216
488
|
```typescript
|
|
217
489
|
import {
|
|
218
490
|
ChiaBlockListener,
|
|
491
|
+
ChiaPeerPool,
|
|
219
492
|
BlockReceivedEvent,
|
|
220
493
|
PeerConnectedEvent,
|
|
221
494
|
PeerDisconnectedEvent,
|
|
495
|
+
NewPeakHeightEvent,
|
|
222
496
|
CoinRecord,
|
|
223
497
|
CoinSpend,
|
|
224
498
|
initTracing,
|
|
@@ -279,6 +553,29 @@ async function getHistoricalBlocks() {
|
|
|
279
553
|
// Get event type constants
|
|
280
554
|
const eventTypes = getEventTypes()
|
|
281
555
|
console.log('Available events:', eventTypes)
|
|
556
|
+
|
|
557
|
+
// TypeScript support for ChiaPeerPool
|
|
558
|
+
const pool = new ChiaPeerPool()
|
|
559
|
+
|
|
560
|
+
// Type-safe event handling
|
|
561
|
+
pool.on('peerConnected', (event: PeerConnectedEvent) => {
|
|
562
|
+
console.log(`Pool peer connected: ${event.peerId}`)
|
|
563
|
+
})
|
|
564
|
+
|
|
565
|
+
pool.on('newPeakHeight', (event: NewPeakHeightEvent) => {
|
|
566
|
+
console.log(`New peak: ${event.oldPeak} → ${event.newPeak}`)
|
|
567
|
+
})
|
|
568
|
+
|
|
569
|
+
// Async/await with proper typing
|
|
570
|
+
async function fetchHistoricalData() {
|
|
571
|
+
const block: BlockReceivedEvent = await pool.getBlockByHeight(5000000)
|
|
572
|
+
const peers: string[] = await pool.getConnectedPeers()
|
|
573
|
+
const peak: number | null = await pool.getPeakHeight()
|
|
574
|
+
|
|
575
|
+
console.log(`Block ${block.height} has ${block.coinSpends.length} spends`)
|
|
576
|
+
console.log(`Pool has ${peers.length} active peers`)
|
|
577
|
+
console.log(`Current peak: ${peak || 'No peak yet'}`)
|
|
578
|
+
}
|
|
282
579
|
```
|
|
283
580
|
|
|
284
581
|
## Advanced Usage
|
|
@@ -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;
|