@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 +1 -0
- package/README.md +397 -0
- package/examples/block-parser-example.mjs +55 -0
- package/index.d.ts +49 -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/src/block_parser_napi.rs +265 -0
- package/src/lib.rs +2 -0
package/Cargo.toml
CHANGED
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()
|
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.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.
|
|
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.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;
|