@dignetwork/chia-block-listener 0.1.17 → 0.1.18

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/Cargo.toml CHANGED
@@ -26,6 +26,7 @@ native-tls = { version = "0.2", features = ["vendored"] }
26
26
 
27
27
  # Serialization
28
28
  serde = { version = "1", features = ["derive"] }
29
+ hex = "0.4"
29
30
 
30
31
  # Utilities
31
32
  thiserror = "1"
package/README.md CHANGED
@@ -757,12 +757,358 @@ try {
757
757
  - **IPv6 URL Formatting**: Automatic bracket formatting for IPv6 addresses
758
758
  - **Type Safety**: Full TypeScript support with detailed type definitions
759
759
 
760
+ ## ChiaBlockParser Usage
761
+
762
+ The `ChiaBlockParser` provides direct access to the Rust-based block parsing engine, enabling efficient parsing of Chia FullBlock data with complete control over the parsing process. This is ideal for applications that need to process block data from external sources or implement custom block analysis.
763
+
764
+ ### Basic Block Parsing
765
+
766
+ ```javascript
767
+ const { ChiaBlockParser, initTracing } = require('@dignetwork/chia-block-listener')
768
+
769
+ async function parseBlockData() {
770
+ // Initialize tracing
771
+ initTracing()
772
+
773
+ // Create a block parser instance
774
+ const parser = new ChiaBlockParser()
775
+
776
+ // Parse a block from hex string
777
+ const blockHex = "your_full_block_hex_data_here"
778
+ const parsedBlock = parser.parseFullBlockFromHex(blockHex)
779
+
780
+ console.log(`Parsed block ${parsedBlock.height}:`)
781
+ console.log(` Header hash: ${parsedBlock.headerHash}`)
782
+ console.log(` Weight: ${parsedBlock.weight}`)
783
+ console.log(` Timestamp: ${new Date(parsedBlock.timestamp * 1000)}`)
784
+ console.log(` Coin additions: ${parsedBlock.coinAdditions.length}`)
785
+ console.log(` Coin removals: ${parsedBlock.coinRemovals.length}`)
786
+ console.log(` Coin spends: ${parsedBlock.coinSpends.length}`)
787
+ console.log(` Has generator: ${parsedBlock.hasTransactionsGenerator}`)
788
+
789
+ // Access detailed coin spend information
790
+ parsedBlock.coinSpends.forEach((spend, index) => {
791
+ console.log(` Spend ${index + 1}:`)
792
+ console.log(` Coin: ${spend.coin.amount} mojos`)
793
+ console.log(` Puzzle hash: ${spend.coin.puzzleHash}`)
794
+ console.log(` Puzzle reveal: ${spend.puzzleReveal.substring(0, 100)}...`)
795
+ console.log(` Solution: ${spend.solution.substring(0, 100)}...`)
796
+ console.log(` Created coins: ${spend.createdCoins.length}`)
797
+ })
798
+ }
799
+
800
+ parseBlockData().catch(console.error)
801
+ ```
802
+
803
+ ### ChiaBlockParser Class
804
+
805
+ #### Constructor
806
+
807
+ ```javascript
808
+ const parser = new ChiaBlockParser()
809
+ ```
810
+
811
+ Creates a new block parser instance with access to the full Rust parsing engine.
812
+
813
+ #### Methods
814
+
815
+ ##### `parseFullBlockFromBytes(blockBytes): ParsedBlockJs`
816
+
817
+ Parses a FullBlock from raw bytes.
818
+
819
+ **Parameters:**
820
+ - `blockBytes` (Buffer): The serialized FullBlock data
821
+
822
+ **Returns:** A `ParsedBlockJs` object containing all parsed block information
823
+
824
+ ```javascript
825
+ const fs = require('fs')
826
+ const blockData = fs.readFileSync('block.bin')
827
+ const parsedBlock = parser.parseFullBlockFromBytes(blockData)
828
+ ```
829
+
830
+ ##### `parseFullBlockFromHex(blockHex): ParsedBlockJs`
831
+
832
+ Parses a FullBlock from a hex-encoded string.
833
+
834
+ **Parameters:**
835
+ - `blockHex` (string): The hex-encoded FullBlock data
836
+
837
+ **Returns:** A `ParsedBlockJs` object containing all parsed block information
838
+
839
+ ```javascript
840
+ const blockHex = "deadbeef..." // Your hex-encoded block data
841
+ const parsedBlock = parser.parseFullBlockFromHex(blockHex)
842
+ ```
843
+
844
+ ##### `extractGeneratorFromBlockBytes(blockBytes): string | null`
845
+
846
+ Extracts only the transactions generator from a block without full parsing.
847
+
848
+ **Parameters:**
849
+ - `blockBytes` (Buffer): The serialized FullBlock data
850
+
851
+ **Returns:** Hex-encoded generator bytecode or `null` if no generator exists
852
+
853
+ ```javascript
854
+ const blockData = fs.readFileSync('block.bin')
855
+ const generator = parser.extractGeneratorFromBlockBytes(blockData)
856
+ if (generator) {
857
+ console.log(`Generator size: ${generator.length / 2} bytes`)
858
+ }
859
+ ```
860
+
861
+ ##### `getHeightAndTxStatusFromBlockBytes(blockBytes): BlockHeightInfoJs`
862
+
863
+ Quickly extracts basic block information without full parsing.
864
+
865
+ **Parameters:**
866
+ - `blockBytes` (Buffer): The serialized FullBlock data
867
+
868
+ **Returns:** A `BlockHeightInfoJs` object with height and transaction status
869
+
870
+ ```javascript
871
+ const blockData = fs.readFileSync('block.bin')
872
+ const info = parser.getHeightAndTxStatusFromBlockBytes(blockData)
873
+ console.log(`Block ${info.height}, has transactions: ${info.isTransactionBlock}`)
874
+ ```
875
+
876
+ ##### `parseBlockInfoFromBytes(blockBytes): GeneratorBlockInfoJs`
877
+
878
+ Extracts generator-related block metadata.
879
+
880
+ **Parameters:**
881
+ - `blockBytes` (Buffer): The serialized FullBlock data
882
+
883
+ **Returns:** A `GeneratorBlockInfoJs` object with generator metadata
884
+
885
+ ```javascript
886
+ const blockData = fs.readFileSync('block.bin')
887
+ const blockInfo = parser.parseBlockInfoFromBytes(blockData)
888
+ console.log(`Previous hash: ${blockInfo.prevHeaderHash}`)
889
+ console.log(`Generator refs: ${blockInfo.transactionsGeneratorRefList.length}`)
890
+ ```
891
+
892
+ ### Advanced Parsing Features
893
+
894
+ #### Batch Block Processing
895
+
896
+ ```javascript
897
+ const parser = new ChiaBlockParser()
898
+ const fs = require('fs')
899
+ const path = require('path')
900
+
901
+ // Process multiple block files
902
+ const blockFiles = fs.readdirSync('./blocks/').filter(f => f.endsWith('.bin'))
903
+
904
+ const stats = {
905
+ totalBlocks: 0,
906
+ totalSpends: 0,
907
+ totalCoins: 0,
908
+ generatorBlocks: 0
909
+ }
910
+
911
+ for (const filename of blockFiles) {
912
+ const blockData = fs.readFileSync(path.join('./blocks/', filename))
913
+
914
+ try {
915
+ const parsed = parser.parseFullBlockFromBytes(blockData)
916
+
917
+ stats.totalBlocks++
918
+ stats.totalSpends += parsed.coinSpends.length
919
+ stats.totalCoins += parsed.coinAdditions.length
920
+
921
+ if (parsed.hasTransactionsGenerator) {
922
+ stats.generatorBlocks++
923
+ }
924
+
925
+ console.log(`Processed block ${parsed.height} with ${parsed.coinSpends.length} spends`)
926
+
927
+ } catch (error) {
928
+ console.error(`Failed to parse ${filename}:`, error.message)
929
+ }
930
+ }
931
+
932
+ console.log('\nBatch processing complete:')
933
+ console.log(` Blocks processed: ${stats.totalBlocks}`)
934
+ console.log(` Total spends: ${stats.totalSpends}`)
935
+ console.log(` Total coins: ${stats.totalCoins}`)
936
+ console.log(` Generator blocks: ${stats.generatorBlocks}`)
937
+ ```
938
+
939
+ #### Generator Analysis
940
+
941
+ ```javascript
942
+ const parser = new ChiaBlockParser()
943
+
944
+ function analyzeBlockGenerator(blockHex) {
945
+ // Parse the full block
946
+ const parsed = parser.parseFullBlockFromHex(blockHex)
947
+
948
+ if (!parsed.hasTransactionsGenerator) {
949
+ console.log('Block has no generator')
950
+ return
951
+ }
952
+
953
+ // Extract just the generator for analysis
954
+ const blockBytes = Buffer.from(blockHex, 'hex')
955
+ const generator = parser.extractGeneratorFromBlockBytes(blockBytes)
956
+
957
+ console.log(`Generator Analysis for Block ${parsed.height}:`)
958
+ console.log(` Generator size: ${parsed.generatorSize} bytes`)
959
+ console.log(` Hex length: ${generator.length} characters`)
960
+ console.log(` Coin spends extracted: ${parsed.coinSpends.length}`)
961
+ console.log(` Coins created: ${parsed.coinCreations.length}`)
962
+
963
+ // Analyze coin spends
964
+ parsed.coinSpends.forEach((spend, i) => {
965
+ console.log(` Spend ${i + 1}:`)
966
+ console.log(` Amount: ${spend.coin.amount} mojos`)
967
+ console.log(` Puzzle size: ${spend.puzzleReveal.length / 2} bytes`)
968
+ console.log(` Solution size: ${spend.solution.length / 2} bytes`)
969
+ console.log(` Creates ${spend.createdCoins.length} new coins`)
970
+ })
971
+ }
972
+
973
+ // Example usage
974
+ const blockHex = "your_generator_block_hex"
975
+ analyzeBlockGenerator(blockHex)
976
+ ```
977
+
978
+ #### Integration with Other Components
979
+
980
+ ```javascript
981
+ const { ChiaBlockParser, ChiaPeerPool, DnsDiscoveryClient } = require('@dignetwork/chia-block-listener')
982
+
983
+ async function integratedBlockAnalysis() {
984
+ // Set up components
985
+ const parser = new ChiaBlockParser()
986
+ const pool = new ChiaPeerPool()
987
+ const discovery = new DnsDiscoveryClient()
988
+
989
+ // Discover and connect to peers
990
+ const peers = await discovery.discoverMainnetPeers()
991
+ for (const peer of peers.ipv4Peers.slice(0, 3)) {
992
+ await pool.addPeer(peer.host, peer.port, 'mainnet')
993
+ }
994
+
995
+ // Fetch blocks and parse with enhanced detail
996
+ const heights = [5000000, 5000001, 5000002]
997
+
998
+ for (const height of heights) {
999
+ // Get block using peer pool
1000
+ const blockEvent = await pool.getBlockByHeight(height)
1001
+
1002
+ // For more detailed analysis, you can also parse with ChiaBlockParser
1003
+ // if you have access to the raw block bytes
1004
+ console.log(`Block ${height}:`)
1005
+ console.log(` From peer: ${blockEvent.peerId}`)
1006
+ console.log(` Coin spends: ${blockEvent.coinSpends.length}`)
1007
+ console.log(` Has generator: ${blockEvent.hasTransactionsGenerator}`)
1008
+
1009
+ // Analyze puzzle patterns
1010
+ const puzzleHashes = new Set()
1011
+ blockEvent.coinSpends.forEach(spend => {
1012
+ puzzleHashes.add(spend.coin.puzzleHash)
1013
+ })
1014
+ console.log(` Unique puzzle hashes: ${puzzleHashes.size}`)
1015
+ }
1016
+
1017
+ await pool.shutdown()
1018
+ }
1019
+
1020
+ integratedBlockAnalysis().catch(console.error)
1021
+ ```
1022
+
1023
+ ### Data Types
1024
+
1025
+ #### `ParsedBlockJs`
1026
+
1027
+ ```typescript
1028
+ interface ParsedBlockJs {
1029
+ height: number // Block height
1030
+ weight: string // Block weight as string
1031
+ headerHash: string // Block header hash (hex)
1032
+ timestamp?: number // Block timestamp (Unix time)
1033
+ coinAdditions: CoinInfoJs[] // New coins created
1034
+ coinRemovals: CoinInfoJs[] // Coins spent
1035
+ coinSpends: CoinSpendInfoJs[] // Detailed spend information
1036
+ coinCreations: CoinInfoJs[] // Coins created by spends
1037
+ hasTransactionsGenerator: boolean // Whether block has generator
1038
+ generatorSize?: number // Generator size in bytes
1039
+ }
1040
+ ```
1041
+
1042
+ #### `CoinInfoJs`
1043
+
1044
+ ```typescript
1045
+ interface CoinInfoJs {
1046
+ parentCoinInfo: string // Parent coin ID (hex)
1047
+ puzzleHash: string // Puzzle hash (hex)
1048
+ amount: string // Amount as string (to avoid JS precision issues)
1049
+ }
1050
+ ```
1051
+
1052
+ #### `CoinSpendInfoJs`
1053
+
1054
+ ```typescript
1055
+ interface CoinSpendInfoJs {
1056
+ coin: CoinInfoJs // The coin being spent
1057
+ puzzleReveal: string // CLVM puzzle bytecode (hex)
1058
+ solution: string // CLVM solution bytecode (hex)
1059
+ realData: boolean // Whether this is real transaction data
1060
+ parsingMethod: string // Method used for parsing
1061
+ offset: number // Offset in generator bytecode
1062
+ createdCoins: CoinInfoJs[] // Coins created by this spend
1063
+ }
1064
+ ```
1065
+
1066
+ #### `GeneratorBlockInfoJs`
1067
+
1068
+ ```typescript
1069
+ interface GeneratorBlockInfoJs {
1070
+ prevHeaderHash: string // Previous block hash (hex)
1071
+ transactionsGenerator?: string // Generator bytecode (hex)
1072
+ transactionsGeneratorRefList: number[] // Referenced block heights
1073
+ }
1074
+ ```
1075
+
1076
+ #### `BlockHeightInfoJs`
1077
+
1078
+ ```typescript
1079
+ interface BlockHeightInfoJs {
1080
+ height: number // Block height
1081
+ isTransactionBlock: boolean // Whether block contains transactions
1082
+ }
1083
+ ```
1084
+
1085
+ ### When to Use ChiaBlockParser
1086
+
1087
+ - **Use ChiaBlockParser when:**
1088
+ - You have raw block data from external sources
1089
+ - You need detailed CLVM puzzle and solution analysis
1090
+ - You're implementing custom block processing logic
1091
+ - You need to extract generators for analysis
1092
+ - You want fine-grained control over parsing
1093
+
1094
+ - **Use with ChiaPeerPool when:**
1095
+ - You need both block retrieval and detailed parsing
1096
+ - You're analyzing historical blocks in detail
1097
+ - You want to combine network access with parsing
1098
+
1099
+ - **Use with ChiaBlockListener when:**
1100
+ - You're processing real-time blocks with custom logic
1101
+ - You need both live monitoring and detailed parsing
1102
+
1103
+ The `ChiaBlockParser` complements the other classes by providing the lowest-level access to the Chia block parsing engine, enabling sophisticated analysis and processing workflows.
1104
+
760
1105
  ## TypeScript Usage
761
1106
 
762
1107
  ```typescript
763
1108
  import {
764
1109
  ChiaBlockListener,
765
1110
  ChiaPeerPool,
1111
+ ChiaBlockParser,
766
1112
  DnsDiscoveryClient,
767
1113
  BlockReceivedEvent,
768
1114
  PeerConnectedEvent,
@@ -773,6 +1119,11 @@ import {
773
1119
  AddressResult,
774
1120
  CoinRecord,
775
1121
  CoinSpend,
1122
+ ParsedBlockJs,
1123
+ CoinInfoJs,
1124
+ CoinSpendInfoJs,
1125
+ GeneratorBlockInfoJs,
1126
+ BlockHeightInfoJs,
776
1127
  initTracing,
777
1128
  getEventTypes
778
1129
  } from '@dignetwork/chia-block-listener'
@@ -877,6 +1228,52 @@ async function typedDnsDiscovery(): Promise<void> {
877
1228
 
878
1229
  console.log(`IPv4 count: ${ipv4Result.count}, IPv6 count: ${ipv6Result.count}`)
879
1230
  }
1231
+
1232
+ // TypeScript ChiaBlockParser
1233
+ async function typedBlockParsing(): Promise<void> {
1234
+ const parser = new ChiaBlockParser()
1235
+
1236
+ // Type-safe block parsing from hex
1237
+ const blockHex = "your_block_hex_data"
1238
+ const parsedBlock: ParsedBlockJs = parser.parseFullBlockFromHex(blockHex)
1239
+
1240
+ console.log(`Parsed block ${parsedBlock.height}:`)
1241
+ console.log(` Header hash: ${parsedBlock.headerHash}`)
1242
+ console.log(` Weight: ${parsedBlock.weight}`)
1243
+ console.log(` Coin additions: ${parsedBlock.coinAdditions.length}`)
1244
+ console.log(` Coin spends: ${parsedBlock.coinSpends.length}`)
1245
+ console.log(` Has generator: ${parsedBlock.hasTransactionsGenerator}`)
1246
+
1247
+ // Type-safe access to coin spend details
1248
+ parsedBlock.coinSpends.forEach((spend: CoinSpendInfoJs) => {
1249
+ const coin: CoinInfoJs = spend.coin
1250
+ console.log(`Spend: ${coin.amount} mojos from ${coin.puzzleHash}`)
1251
+ console.log(` Puzzle reveal size: ${spend.puzzleReveal.length / 2} bytes`)
1252
+ console.log(` Solution size: ${spend.solution.length / 2} bytes`)
1253
+ console.log(` Creates ${spend.createdCoins.length} new coins`)
1254
+ console.log(` Real data: ${spend.realData}`)
1255
+ console.log(` Parsing method: ${spend.parsingMethod}`)
1256
+ })
1257
+
1258
+ // Type-safe generator extraction
1259
+ const blockBytes = Buffer.from(blockHex, 'hex')
1260
+ const generator: string | null = parser.extractGeneratorFromBlockBytes(blockBytes)
1261
+ if (generator) {
1262
+ console.log(`Generator found: ${generator.length / 2} bytes`)
1263
+ }
1264
+
1265
+ // Type-safe height and tx status
1266
+ const heightInfo: BlockHeightInfoJs = parser.getHeightAndTxStatusFromBlockBytes(blockBytes)
1267
+ console.log(`Block ${heightInfo.height}, is transaction block: ${heightInfo.isTransactionBlock}`)
1268
+
1269
+ // Type-safe block info extraction
1270
+ const blockInfo: GeneratorBlockInfoJs = parser.parseBlockInfoFromBytes(blockBytes)
1271
+ console.log(`Previous hash: ${blockInfo.prevHeaderHash}`)
1272
+ console.log(`Generator refs: ${blockInfo.transactionsGeneratorRefList.length}`)
1273
+ if (blockInfo.transactionsGenerator) {
1274
+ console.log(`Generator size: ${blockInfo.transactionsGenerator.length / 2} bytes`)
1275
+ }
1276
+ }
880
1277
  ```
881
1278
 
882
1279
  ## Advanced Usage
@@ -0,0 +1,55 @@
1
+ import { ChiaBlockParser } from '../index.js';
2
+
3
+ async function demonstrateBlockParser() {
4
+ console.log('🔧 ChiaBlockParser Demo');
5
+ console.log('=======================\n');
6
+
7
+ // Create a new block parser instance
8
+ const parser = new ChiaBlockParser();
9
+ console.log('✅ Created ChiaBlockParser instance\n');
10
+
11
+ // Example: Parse a block from hex (you would replace this with actual block data)
12
+ try {
13
+ console.log('📄 Available methods:');
14
+ console.log('• parseFullBlockFromBytes(blockBytes: Buffer): ParsedBlockJs');
15
+ console.log('• parseFullBlockFromHex(blockHex: string): ParsedBlockJs');
16
+ console.log('• extractGeneratorFromBlockBytes(blockBytes: Buffer): string | null');
17
+ console.log('• getHeightAndTxStatusFromBlockBytes(blockBytes: Buffer): BlockHeightInfoJs');
18
+ console.log('• parseBlockInfoFromBytes(blockBytes: Buffer): GeneratorBlockInfoJs\n');
19
+
20
+ console.log('💡 Example usage:');
21
+ console.log(`
22
+ // Parse a block from hex string
23
+ const blockHex = "your_block_hex_here";
24
+ const parsedBlock = parser.parseFullBlockFromHex(blockHex);
25
+ console.log('Block height:', parsedBlock.height);
26
+ console.log('Coin additions:', parsedBlock.coinAdditions.length);
27
+ console.log('Coin spends:', parsedBlock.coinSpends.length);
28
+
29
+ // Parse from buffer
30
+ const blockBuffer = Buffer.from(blockHex, 'hex');
31
+ const parsedFromBuffer = parser.parseFullBlockFromBytes(blockBuffer);
32
+
33
+ // Extract just the generator
34
+ const generator = parser.extractGeneratorFromBlockBytes(blockBuffer);
35
+ if (generator) {
36
+ console.log('Generator found:', generator.substring(0, 100) + '...');
37
+ } else {
38
+ console.log('No generator in this block');
39
+ }
40
+
41
+ // Get height and transaction status
42
+ const heightInfo = parser.getHeightAndTxStatusFromBlockBytes(blockBuffer);
43
+ console.log('Height:', heightInfo.height, 'Is transaction block:', heightInfo.isTransactionBlock);
44
+ `);
45
+
46
+ console.log('\n🎯 The ChiaBlockParser provides direct access to the Rust parser');
47
+ console.log(' with full type safety and all parsing methods available.');
48
+
49
+ } catch (error) {
50
+ console.error('❌ Error demonstrating parser:', error.message);
51
+ }
52
+ }
53
+
54
+ // Run the demo
55
+ demonstrateBlockParser().catch(console.error);
package/index.d.ts CHANGED
@@ -22,6 +22,41 @@ export interface AddressResult {
22
22
  addresses: Array<string>
23
23
  count: number
24
24
  }
25
+ export interface CoinInfoJs {
26
+ parentCoinInfo: string
27
+ puzzleHash: string
28
+ amount: string
29
+ }
30
+ export interface CoinSpendInfoJs {
31
+ coin: CoinInfoJs
32
+ puzzleReveal: string
33
+ solution: string
34
+ realData: boolean
35
+ parsingMethod: string
36
+ offset: number
37
+ createdCoins: Array<CoinInfoJs>
38
+ }
39
+ export interface ParsedBlockJs {
40
+ height: number
41
+ weight: string
42
+ headerHash: string
43
+ timestamp?: number
44
+ coinAdditions: Array<CoinInfoJs>
45
+ coinRemovals: Array<CoinInfoJs>
46
+ coinSpends: Array<CoinSpendInfoJs>
47
+ coinCreations: Array<CoinInfoJs>
48
+ hasTransactionsGenerator: boolean
49
+ generatorSize?: number
50
+ }
51
+ export interface GeneratorBlockInfoJs {
52
+ prevHeaderHash: string
53
+ transactionsGenerator?: string
54
+ transactionsGeneratorRefList: Array<number>
55
+ }
56
+ export interface BlockHeightInfoJs {
57
+ height: number
58
+ isTransactionBlock: boolean
59
+ }
25
60
  export interface EventTypes {
26
61
  blockReceived: string
27
62
  peerConnected: string
@@ -69,6 +104,20 @@ export interface NewPeakHeightEvent {
69
104
  peerId: string
70
105
  }
71
106
  export declare function initTracing(): void
107
+ export declare class ChiaBlockParser {
108
+ /** Create a new block parser */
109
+ constructor()
110
+ /** Parse a FullBlock from bytes */
111
+ parseFullBlockFromBytes(blockBytes: Buffer): ParsedBlockJs
112
+ /** Parse a FullBlock from hex string */
113
+ parseFullBlockFromHex(blockHex: string): ParsedBlockJs
114
+ /** Extract generator from block bytes */
115
+ extractGeneratorFromBlockBytes(blockBytes: Buffer): string | null
116
+ /** Get block height and transaction status from block bytes */
117
+ getHeightAndTxStatusFromBlockBytes(blockBytes: Buffer): BlockHeightInfoJs
118
+ /** Parse block info from block bytes */
119
+ parseBlockInfoFromBytes(blockBytes: Buffer): GeneratorBlockInfoJs
120
+ }
72
121
  export declare class DnsDiscoveryClient {
73
122
  /** Create a new DNS discovery client */
74
123
  constructor()
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dignetwork/chia-block-listener-darwin-arm64",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/DIG-Network/chia-block-listener"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dignetwork/chia-block-listener-darwin-x64",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/DIG-Network/chia-block-listener"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dignetwork/chia-block-listener-linux-arm64-gnu",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/DIG-Network/chia-block-listener"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dignetwork/chia-block-listener-linux-x64-gnu",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/DIG-Network/chia-block-listener"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dignetwork/chia-block-listener-win32-x64-msvc",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/DIG-Network/chia-block-listener"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dignetwork/chia-block-listener",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
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.17",
65
- "@dignetwork/chia-block-listener-darwin-x64": "0.1.17",
66
- "@dignetwork/chia-block-listener-linux-x64-gnu": "0.1.17",
67
- "@dignetwork/chia-block-listener-darwin-arm64": "0.1.17",
68
- "@dignetwork/chia-block-listener-linux-arm64-gnu": "0.1.17"
64
+ "@dignetwork/chia-block-listener-win32-x64-msvc": "0.1.18",
65
+ "@dignetwork/chia-block-listener-darwin-x64": "0.1.18",
66
+ "@dignetwork/chia-block-listener-linux-x64-gnu": "0.1.18",
67
+ "@dignetwork/chia-block-listener-darwin-arm64": "0.1.18",
68
+ "@dignetwork/chia-block-listener-linux-arm64-gnu": "0.1.18"
69
69
  }
70
70
  }
@@ -0,0 +1,265 @@
1
+ use chia_generator_parser::{
2
+ parser::BlockParser as RustBlockParser,
3
+ types::{BlockHeightInfo, CoinInfo, CoinSpendInfo, GeneratorBlockInfo, ParsedBlock},
4
+ };
5
+ use chia_protocol::FullBlock;
6
+ use chia_traits::streamable::Streamable;
7
+ use napi::bindgen_prelude::*;
8
+ use napi_derive::napi;
9
+ use tracing::{debug, info};
10
+
11
+ // Export CoinInfo for TypeScript
12
+ #[napi(object)]
13
+ #[derive(Clone)]
14
+ pub struct CoinInfoJS {
15
+ #[napi(js_name = "parentCoinInfo")]
16
+ pub parent_coin_info: String,
17
+ #[napi(js_name = "puzzleHash")]
18
+ pub puzzle_hash: String,
19
+ pub amount: String, // Use string for u64 to avoid JS precision issues
20
+ }
21
+
22
+ impl From<&CoinInfo> for CoinInfoJS {
23
+ fn from(coin: &CoinInfo) -> Self {
24
+ Self {
25
+ parent_coin_info: coin.parent_coin_info.clone(),
26
+ puzzle_hash: coin.puzzle_hash.clone(),
27
+ amount: coin.amount.to_string(),
28
+ }
29
+ }
30
+ }
31
+
32
+ // Export CoinSpendInfo for TypeScript
33
+ #[napi(object)]
34
+ #[derive(Clone)]
35
+ pub struct CoinSpendInfoJS {
36
+ pub coin: CoinInfoJS,
37
+ #[napi(js_name = "puzzleReveal")]
38
+ pub puzzle_reveal: String,
39
+ pub solution: String,
40
+ #[napi(js_name = "realData")]
41
+ pub real_data: bool,
42
+ #[napi(js_name = "parsingMethod")]
43
+ pub parsing_method: String,
44
+ pub offset: u32,
45
+ #[napi(js_name = "createdCoins")]
46
+ pub created_coins: Vec<CoinInfoJS>,
47
+ }
48
+
49
+ impl From<&CoinSpendInfo> for CoinSpendInfoJS {
50
+ fn from(spend: &CoinSpendInfo) -> Self {
51
+ Self {
52
+ coin: (&spend.coin).into(),
53
+ puzzle_reveal: spend.puzzle_reveal.clone(),
54
+ solution: spend.solution.clone(),
55
+ real_data: spend.real_data,
56
+ parsing_method: spend.parsing_method.clone(),
57
+ offset: spend.offset,
58
+ created_coins: spend.created_coins.iter().map(|c| c.into()).collect(),
59
+ }
60
+ }
61
+ }
62
+
63
+ // Export ParsedBlock for TypeScript
64
+ #[napi(object)]
65
+ #[derive(Clone)]
66
+ pub struct ParsedBlockJS {
67
+ pub height: u32,
68
+ pub weight: String,
69
+ #[napi(js_name = "headerHash")]
70
+ pub header_hash: String,
71
+ pub timestamp: Option<u32>,
72
+ #[napi(js_name = "coinAdditions")]
73
+ pub coin_additions: Vec<CoinInfoJS>,
74
+ #[napi(js_name = "coinRemovals")]
75
+ pub coin_removals: Vec<CoinInfoJS>,
76
+ #[napi(js_name = "coinSpends")]
77
+ pub coin_spends: Vec<CoinSpendInfoJS>,
78
+ #[napi(js_name = "coinCreations")]
79
+ pub coin_creations: Vec<CoinInfoJS>,
80
+ #[napi(js_name = "hasTransactionsGenerator")]
81
+ pub has_transactions_generator: bool,
82
+ #[napi(js_name = "generatorSize")]
83
+ pub generator_size: Option<u32>,
84
+ }
85
+
86
+ impl From<&ParsedBlock> for ParsedBlockJS {
87
+ fn from(block: &ParsedBlock) -> Self {
88
+ Self {
89
+ height: block.height,
90
+ weight: block.weight.clone(),
91
+ header_hash: block.header_hash.clone(),
92
+ timestamp: block.timestamp,
93
+ coin_additions: block.coin_additions.iter().map(|c| c.into()).collect(),
94
+ coin_removals: block.coin_removals.iter().map(|c| c.into()).collect(),
95
+ coin_spends: block.coin_spends.iter().map(|s| s.into()).collect(),
96
+ coin_creations: block.coin_creations.iter().map(|c| c.into()).collect(),
97
+ has_transactions_generator: block.has_transactions_generator,
98
+ generator_size: block.generator_size,
99
+ }
100
+ }
101
+ }
102
+
103
+ // Export GeneratorBlockInfo for TypeScript
104
+ #[napi(object)]
105
+ #[derive(Clone)]
106
+ pub struct GeneratorBlockInfoJS {
107
+ #[napi(js_name = "prevHeaderHash")]
108
+ pub prev_header_hash: String,
109
+ #[napi(js_name = "transactionsGenerator")]
110
+ pub transactions_generator: Option<String>, // Hex-encoded bytes
111
+ #[napi(js_name = "transactionsGeneratorRefList")]
112
+ pub transactions_generator_ref_list: Vec<u32>,
113
+ }
114
+
115
+ impl From<&GeneratorBlockInfo> for GeneratorBlockInfoJS {
116
+ fn from(info: &GeneratorBlockInfo) -> Self {
117
+ Self {
118
+ prev_header_hash: hex::encode(info.prev_header_hash),
119
+ transactions_generator: info.transactions_generator.as_ref().map(hex::encode),
120
+ transactions_generator_ref_list: info.transactions_generator_ref_list.clone(),
121
+ }
122
+ }
123
+ }
124
+
125
+ // Export BlockHeightInfo for TypeScript
126
+ #[napi(object)]
127
+ #[derive(Clone)]
128
+ pub struct BlockHeightInfoJS {
129
+ pub height: u32,
130
+ #[napi(js_name = "isTransactionBlock")]
131
+ pub is_transaction_block: bool,
132
+ }
133
+
134
+ impl From<&BlockHeightInfo> for BlockHeightInfoJS {
135
+ fn from(info: &BlockHeightInfo) -> Self {
136
+ Self {
137
+ height: info.height,
138
+ is_transaction_block: info.is_transaction_block,
139
+ }
140
+ }
141
+ }
142
+
143
+ #[napi]
144
+ pub struct ChiaBlockParser {
145
+ parser: RustBlockParser,
146
+ }
147
+
148
+ impl Default for ChiaBlockParser {
149
+ fn default() -> Self {
150
+ Self::new()
151
+ }
152
+ }
153
+
154
+ #[napi]
155
+ impl ChiaBlockParser {
156
+ /// Create a new block parser
157
+ #[napi(constructor)]
158
+ pub fn new() -> Self {
159
+ info!("Creating new ChiaBlockParser");
160
+ Self {
161
+ parser: RustBlockParser::new(),
162
+ }
163
+ }
164
+
165
+ /// Parse a FullBlock from bytes
166
+ #[napi]
167
+ pub fn parse_full_block_from_bytes(&self, block_bytes: Buffer) -> Result<ParsedBlockJS> {
168
+ debug!("Parsing FullBlock from {} bytes", block_bytes.len());
169
+
170
+ let parsed_block = self
171
+ .parser
172
+ .parse_full_block_from_bytes(&block_bytes)
173
+ .map_err(|e| Error::new(Status::GenericFailure, format!("Parse error: {e}")))?;
174
+
175
+ Ok((&parsed_block).into())
176
+ }
177
+
178
+ /// Parse a FullBlock from hex string
179
+ #[napi]
180
+ pub fn parse_full_block_from_hex(&self, block_hex: String) -> Result<ParsedBlockJS> {
181
+ debug!(
182
+ "Parsing FullBlock from hex string of length {}",
183
+ block_hex.len()
184
+ );
185
+
186
+ let block_bytes = hex::decode(&block_hex)
187
+ .map_err(|e| Error::new(Status::InvalidArg, format!("Hex decode error: {e}")))?;
188
+
189
+ let parsed_block = self
190
+ .parser
191
+ .parse_full_block_from_bytes(&block_bytes)
192
+ .map_err(|e| Error::new(Status::GenericFailure, format!("Parse error: {e}")))?;
193
+
194
+ Ok((&parsed_block).into())
195
+ }
196
+
197
+ /// Extract generator from block bytes
198
+ #[napi]
199
+ pub fn extract_generator_from_block_bytes(
200
+ &self,
201
+ block_bytes: Buffer,
202
+ ) -> Result<Option<String>> {
203
+ debug!("Extracting generator from {} bytes", block_bytes.len());
204
+
205
+ let block = FullBlock::from_bytes(&block_bytes).map_err(|e| {
206
+ Error::new(
207
+ Status::InvalidArg,
208
+ format!("Failed to deserialize FullBlock: {e}"),
209
+ )
210
+ })?;
211
+
212
+ let generator = self
213
+ .parser
214
+ .extract_generator_from_block(&block)
215
+ .map_err(|e| Error::new(Status::GenericFailure, format!("Extract error: {e}")))?;
216
+
217
+ Ok(generator.map(hex::encode))
218
+ }
219
+
220
+ /// Get block height and transaction status from block bytes
221
+ #[napi]
222
+ pub fn get_height_and_tx_status_from_block_bytes(
223
+ &self,
224
+ block_bytes: Buffer,
225
+ ) -> Result<BlockHeightInfoJS> {
226
+ debug!(
227
+ "Getting height and tx status from {} bytes",
228
+ block_bytes.len()
229
+ );
230
+
231
+ let block = FullBlock::from_bytes(&block_bytes).map_err(|e| {
232
+ Error::new(
233
+ Status::InvalidArg,
234
+ format!("Failed to deserialize FullBlock: {e}"),
235
+ )
236
+ })?;
237
+
238
+ let height_info = self
239
+ .parser
240
+ .get_height_and_tx_status_from_block(&block)
241
+ .map_err(|e| Error::new(Status::GenericFailure, format!("Parse error: {e}")))?;
242
+
243
+ Ok((&height_info).into())
244
+ }
245
+
246
+ /// Parse block info from block bytes
247
+ #[napi]
248
+ pub fn parse_block_info_from_bytes(&self, block_bytes: Buffer) -> Result<GeneratorBlockInfoJS> {
249
+ debug!("Parsing block info from {} bytes", block_bytes.len());
250
+
251
+ let block = FullBlock::from_bytes(&block_bytes).map_err(|e| {
252
+ Error::new(
253
+ Status::InvalidArg,
254
+ format!("Failed to deserialize FullBlock: {e}"),
255
+ )
256
+ })?;
257
+
258
+ let block_info = self
259
+ .parser
260
+ .parse_block_info(&block)
261
+ .map_err(|e| Error::new(Status::GenericFailure, format!("Parse error: {e}")))?;
262
+
263
+ Ok((&block_info).into())
264
+ }
265
+ }
package/src/lib.rs CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  use napi_derive::napi;
4
4
 
5
+ mod block_parser_napi;
5
6
  mod dns_discovery_napi;
6
7
  mod error;
7
8
  mod event_emitter;
@@ -11,6 +12,7 @@ mod peer_pool_napi;
11
12
  mod protocol;
12
13
  mod tls;
13
14
 
15
+ pub use block_parser_napi::ChiaBlockParser;
14
16
  pub use dns_discovery_napi::DnsDiscoveryClient;
15
17
  pub use event_emitter::ChiaBlockListener;
16
18
  pub use peer_pool_napi::ChiaPeerPool;