├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── docs ├── README.md ├── classes │ ├── DbBlocks.md │ ├── DbHeaders.md │ ├── DbListener.md │ ├── DbMempool.md │ ├── DbNodes.md │ ├── Listener.md │ ├── Master.md │ ├── Server.md │ ├── Spv.md │ └── Worker.md └── interfaces │ ├── ListenerOptions.md │ ├── MasterOptions.md │ └── SpvOptions.md ├── package-lock.json ├── package.json ├── src ├── cluster_master.ts ├── cluster_worker.ts ├── db_blocks.ts ├── db_headers.ts ├── db_listener.ts ├── db_mempool.ts ├── db_nodes.ts ├── helpers.ts ├── index.ts ├── listener.ts ├── server.ts ├── spv.ts └── types │ ├── SpvEmitter.ts │ └── TypedEventEmitter.ts ├── tests ├── cluster.ts ├── listener.ts ├── mempool.ts └── server.ts ├── tsconfig.json └── typedoc.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.mdb 3 | *.bin 4 | tests/data 5 | /lib 6 | .DS_Store -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | tests 3 | docs 4 | *.mdb 5 | *.bin -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2022 Kevin Johnson (KevinEJohn) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bsv-spv 2 | 3 | [![NPM Package](https://img.shields.io/npm/v/bsv-spv.svg?style=flat-square)](https://www.npmjs.org/package/bsv-spv) 4 | 5 | Stay in sync with latest bitcoin headers, blocks and mempool txs 6 | 7 | ## Install 8 | 9 | ```sh 10 | npm i bsv-spv 11 | ``` 12 | 13 | ### Docs 14 | 15 | - [View TypeScript documentation here](docs/README.md) 16 | 17 | ## Use 18 | 19 | ### Listen to Bitcoin P2P network 20 | 21 | ```ts 22 | const { Master, Worker } = require("bsv-spv"); 23 | const cluster = require("cluster"); 24 | 25 | const port = 8080; // Server that new blocks nad mempool txs are announced on 26 | 27 | const config = { 28 | ticker: "BSV", // BTC, BCH, XEC, BSV 29 | nodes: [`95.217.197.54:8333`], // Set to your favorite node IP addresses. Will ask for other peers after connected 30 | // enableIpv6: true, // Connect to ipv6 nodes 31 | forceUserAgent: `Bitcoin SV`, // Disconnects with nodes that do not string match with user agent 32 | // user_agent: 'Bitcoin SV', 33 | invalidBlocks: [], // Set if you want to force a specific fork (see examples below) 34 | dataDir: __dirname, // Directory to store files 35 | pruneBlocks: 0, // Number of newest blocks you want saved to local disk. 0 to keeping all blocks back to genesis. 36 | blockHeight: -10, // Sync to block height. 0 to sync to genesis. Negative to sync to X blocks from current heightafter 2 hours 37 | mempool: 1, // Number of mempool tx threads 38 | blocks: 1, // Number of bitcoin block threads 39 | }; 40 | 41 | if (cluster.isWorker) { 42 | const worker = new Worker(); 43 | } else if (cluster.isPrimary) { 44 | const master = new Master(config); 45 | master.startServer({ port }); 46 | } 47 | ``` 48 | 49 | ### Listener process to read downloaded data from the Master process 50 | 51 | ```ts 52 | const { Listener } = require("bsv-spv"); 53 | 54 | const name = "test-plugin"; 55 | const ticker = "BSV"; 56 | const blockHeight = -10; // Number. If negative then it's number from the tip. 57 | const dataDir = __dirname; 58 | const port = 8080; // Same as Masters port above 59 | const listener = new Listener({ name, ticker, blockHeight, dataDir }); 60 | 61 | const onBlock = ({ 62 | header, 63 | started, 64 | finished, 65 | size, 66 | height, 67 | txCount, 68 | transactions, 69 | startDate, 70 | }) => { 71 | for (const [index, tx, pos, len] of transactions) { 72 | console.log(`#${index} tx ${tx.getTxid()} in block ${height}`); 73 | } 74 | }; 75 | 76 | listener.on("mempool_tx", ({ transaction, size }) => { 77 | console.log( 78 | `new mempool tx ${transaction.getTxid()} ${size.toLocaleString( 79 | "en-US" 80 | )} bytes.` 81 | ); 82 | }); 83 | listener.on("block_reorg", ({ height, hash }) => { 84 | // Re-org after height 85 | }); 86 | listener.on("block_saved", ({ height, hash }) => { 87 | listener.syncBlocks(onBlock); 88 | }); 89 | 90 | listener.syncBlocks(onBlock); 91 | listener.connect({ port }); 92 | ``` 93 | 94 | ### Serve txs from express server 95 | 96 | ```ts 97 | const { Server } = require("bsv-spv"); 98 | 99 | const name = "test-server"; 100 | const ticker = "BSV"; 101 | const dataDir = __dirname; 102 | const port = 8080; // Same as Masters port above 103 | const server = new Server({ name, ticker, dataDir }); 104 | server.connect({ port }); 105 | server.listen({ port: 8081 }); // Express server to server txs 106 | 107 | // Get tx from block 108 | // wget 'http://localhost:8081/txid/11bcd81b9c0d9082799e83b29617c1d3e2d663ef4351754c40c5efa0f33e2e91?block=00000000000000000a62b5c6a75e5c24f8bdb1f21501ee5651a09d11ecaaadca&len=173&pos=81' 109 | // or 110 | // wget 'http://localhost:8081/txid/11bcd81b9c0d9082799e83b29617c1d3e2d663ef4351754c40c5efa0f33e2e91?height=757931&len=173&pos=81' 111 | 112 | // Get tx from mempool 113 | // wget 'http://localhost:8081/txid/11bcd81b9c0d9082799e83b29617c1d3e2d663ef4351754c40c5efa0f33e2e91' 114 | ``` 115 | 116 | ### Forcing specific forks with headers 117 | 118 | Set `invalidBlocks` to the desired fork you wish to use. It will invalidate any other chain listed. 119 | 120 | ```ts 121 | let invalidBlocks; 122 | 123 | // Use BSV only 124 | invalidBlocks = [ 125 | "00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148", // BTC fork 478559 126 | "0000000000000000004626ff6e3b936941d341c5932ece4357eeccac44e6d56c", // BCH fork 556767 127 | ]; 128 | 129 | // Use BTC only 130 | invalidBlocks = [ 131 | "000000000000000000651ef99cb9fcbe0dadde1d424bd9f15ff20136191a5eec", // BCH fork 478559 132 | ]; 133 | 134 | // Use BCH only 135 | invalidBlocks = [ 136 | "00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148", // BTC fork 478559 137 | "000000000000000001d956714215d96ffc00e0afda4cd0a96c96f8d802b1662b", // BSV fork 556767 138 | "000000000000000004284c9d8b2c8ff731efeaec6be50729bdc9bd07f910757d", // XEC fork 661648 139 | ]; 140 | 141 | // Use XEC only 142 | invalidBlocks = [ 143 | "00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148", // BTC fork 478559 144 | "000000000000000001d956714215d96ffc00e0afda4cd0a96c96f8d802b1662b", // BSV fork 556767 145 | "0000000000000000029e471c41818d24b8b74c911071c4ef0b4a0509f9b5a8ce", // BCH fork 661648 146 | ]; 147 | ``` 148 | 149 | ## Tests 150 | 151 | ```sh 152 | ts-node ./tests/cluster.js 153 | ``` 154 | 155 | ```sh 156 | ts-node ./tests/listener.js 157 | ``` 158 | 159 | ```sh 160 | ts-node ./tests/server.js 161 | ``` 162 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | bsv-spv 2 | 3 | # bsv-spv 4 | 5 | ## Table of contents 6 | 7 | ### Classes 8 | 9 | - [DbBlocks](classes/DbBlocks.md) 10 | - [DbHeaders](classes/DbHeaders.md) 11 | - [DbListener](classes/DbListener.md) 12 | - [DbMempool](classes/DbMempool.md) 13 | - [DbNodes](classes/DbNodes.md) 14 | - [Listener](classes/Listener.md) 15 | - [Master](classes/Master.md) 16 | - [Server](classes/Server.md) 17 | - [Spv](classes/Spv.md) 18 | - [Worker](classes/Worker.md) 19 | 20 | ### Interfaces 21 | 22 | - [ListenerOptions](interfaces/ListenerOptions.md) 23 | - [MasterOptions](interfaces/MasterOptions.md) 24 | - [SpvOptions](interfaces/SpvOptions.md) 25 | -------------------------------------------------------------------------------- /docs/classes/DbBlocks.md: -------------------------------------------------------------------------------- 1 | [bsv-spv](../README.md) / DbBlocks 2 | 3 | # Class: DbBlocks 4 | 5 | ## Table of contents 6 | 7 | ### Constructors 8 | 9 | - [constructor](DbBlocks.md#constructor) 10 | 11 | ### Properties 12 | 13 | - [blocksDir](DbBlocks.md#blocksdir) 14 | - [dbPath](DbBlocks.md#dbpath) 15 | - [dbi\_blocks](DbBlocks.md#dbi_blocks) 16 | - [dbi\_root](DbBlocks.md#dbi_root) 17 | - [writeDir](DbBlocks.md#writedir) 18 | - [writeStream](DbBlocks.md#writestream) 19 | 20 | ### Methods 21 | 22 | - [blockExists](DbBlocks.md#blockexists) 23 | - [blockFileExists](DbBlocks.md#blockfileexists) 24 | - [close](DbBlocks.md#close) 25 | - [delBlock](DbBlocks.md#delblock) 26 | - [fileExists](DbBlocks.md#fileexists) 27 | - [getBlocks](DbBlocks.md#getblocks) 28 | - [getBlocksSync](DbBlocks.md#getblockssync) 29 | - [getSavedBlocks](DbBlocks.md#getsavedblocks) 30 | - [getSavedBlocksSync](DbBlocks.md#getsavedblockssync) 31 | - [getTx](DbBlocks.md#gettx) 32 | - [markBlockSaved](DbBlocks.md#markblocksaved) 33 | - [streamBlock](DbBlocks.md#streamblock) 34 | - [syncDb](DbBlocks.md#syncdb) 35 | - [writeBlockChunk](DbBlocks.md#writeblockchunk) 36 | 37 | ## Constructors 38 | 39 | ### constructor 40 | 41 | • **new DbBlocks**(`«destructured»`) 42 | 43 | #### Parameters 44 | 45 | | Name | Type | 46 | | :------ | :------ | 47 | | `«destructured»` | `Object` | 48 | | › `blocksDir` | `string` | 49 | | › `readOnly?` | `boolean` | 50 | 51 | #### Defined in 52 | 53 | [src/db_blocks.ts:14](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L14) 54 | 55 | ## Properties 56 | 57 | ### blocksDir 58 | 59 | • **blocksDir**: `string` 60 | 61 | #### Defined in 62 | 63 | [src/db_blocks.ts:7](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L7) 64 | 65 | ___ 66 | 67 | ### dbPath 68 | 69 | • **dbPath**: `string` 70 | 71 | #### Defined in 72 | 73 | [src/db_blocks.ts:12](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L12) 74 | 75 | ___ 76 | 77 | ### dbi\_blocks 78 | 79 | • **dbi\_blocks**: `Database`<`Buffer`, `Key`\> 80 | 81 | #### Defined in 82 | 83 | [src/db_blocks.ts:10](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L10) 84 | 85 | ___ 86 | 87 | ### dbi\_root 88 | 89 | • **dbi\_root**: `RootDatabase`<`Buffer`, `Key`\> 90 | 91 | #### Defined in 92 | 93 | [src/db_blocks.ts:11](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L11) 94 | 95 | ___ 96 | 97 | ### writeDir 98 | 99 | • `Optional` **writeDir**: `string` 100 | 101 | #### Defined in 102 | 103 | [src/db_blocks.ts:8](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L8) 104 | 105 | ___ 106 | 107 | ### writeStream 108 | 109 | • `Optional` **writeStream**: `WriteStream` 110 | 111 | #### Defined in 112 | 113 | [src/db_blocks.ts:9](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L9) 114 | 115 | ## Methods 116 | 117 | ### blockExists 118 | 119 | ▸ **blockExists**(`hash`): `boolean` 120 | 121 | #### Parameters 122 | 123 | | Name | Type | 124 | | :------ | :------ | 125 | | `hash` | `string` | 126 | 127 | #### Returns 128 | 129 | `boolean` 130 | 131 | #### Defined in 132 | 133 | [src/db_blocks.ts:246](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L246) 134 | 135 | ___ 136 | 137 | ### blockFileExists 138 | 139 | ▸ **blockFileExists**(`hash`): `Promise`<`boolean`\> 140 | 141 | #### Parameters 142 | 143 | | Name | Type | 144 | | :------ | :------ | 145 | | `hash` | `string` | 146 | 147 | #### Returns 148 | 149 | `Promise`<`boolean`\> 150 | 151 | #### Defined in 152 | 153 | [src/db_blocks.ts:250](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L250) 154 | 155 | ___ 156 | 157 | ### close 158 | 159 | ▸ **close**(): `Promise`<`void`\> 160 | 161 | #### Returns 162 | 163 | `Promise`<`void`\> 164 | 165 | #### Defined in 166 | 167 | [src/db_blocks.ts:58](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L58) 168 | 169 | ___ 170 | 171 | ### delBlock 172 | 173 | ▸ **delBlock**(`hash`): `Promise`<`void`\> 174 | 175 | #### Parameters 176 | 177 | | Name | Type | 178 | | :------ | :------ | 179 | | `hash` | `string` \| `Buffer` | 180 | 181 | #### Returns 182 | 183 | `Promise`<`void`\> 184 | 185 | #### Defined in 186 | 187 | [src/db_blocks.ts:231](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L231) 188 | 189 | ___ 190 | 191 | ### fileExists 192 | 193 | ▸ **fileExists**(`dir`): `Promise`<`boolean`\> 194 | 195 | #### Parameters 196 | 197 | | Name | Type | 198 | | :------ | :------ | 199 | | `dir` | `string` | 200 | 201 | #### Returns 202 | 203 | `Promise`<`boolean`\> 204 | 205 | #### Defined in 206 | 207 | [src/db_blocks.ts:98](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L98) 208 | 209 | ___ 210 | 211 | ### getBlocks 212 | 213 | ▸ **getBlocks**(): `Promise`<`string`[]\> 214 | 215 | #### Returns 216 | 217 | `Promise`<`string`[]\> 218 | 219 | #### Defined in 220 | 221 | [src/db_blocks.ts:75](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L75) 222 | 223 | ___ 224 | 225 | ### getBlocksSync 226 | 227 | ▸ **getBlocksSync**(): `string`[] 228 | 229 | #### Returns 230 | 231 | `string`[] 232 | 233 | #### Defined in 234 | 235 | [src/db_blocks.ts:80](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L80) 236 | 237 | ___ 238 | 239 | ### getSavedBlocks 240 | 241 | ▸ **getSavedBlocks**(): `string`[] 242 | 243 | #### Returns 244 | 245 | `string`[] 246 | 247 | #### Defined in 248 | 249 | [src/db_blocks.ts:67](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L67) 250 | 251 | ___ 252 | 253 | ### getSavedBlocksSync 254 | 255 | ▸ **getSavedBlocksSync**(): `Set`<`string`\> 256 | 257 | #### Returns 258 | 259 | `Set`<`string`\> 260 | 261 | #### Defined in 262 | 263 | [src/db_blocks.ts:85](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L85) 264 | 265 | ___ 266 | 267 | ### getTx 268 | 269 | ▸ **getTx**(`«destructured»`): `Promise`<{ `buffer`: `Buffer` ; `bytesRead`: `number` ; `tx`: `undefined` \| `default` }\> 270 | 271 | #### Parameters 272 | 273 | | Name | Type | 274 | | :------ | :------ | 275 | | `«destructured»` | `Object` | 276 | | › `block` | `string` \| `Buffer` | 277 | | › `len` | `number` | 278 | | › `pos` | `number` | 279 | | › `txid?` | `string` \| `Buffer` | 280 | 281 | #### Returns 282 | 283 | `Promise`<{ `buffer`: `Buffer` ; `bytesRead`: `number` ; `tx`: `undefined` \| `default` }\> 284 | 285 | #### Defined in 286 | 287 | [src/db_blocks.ts:255](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L255) 288 | 289 | ___ 290 | 291 | ### markBlockSaved 292 | 293 | ▸ **markBlockSaved**(`hash`): `undefined` \| `Promise`<`boolean`\> 294 | 295 | #### Parameters 296 | 297 | | Name | Type | 298 | | :------ | :------ | 299 | | `hash` | `string` | 300 | 301 | #### Returns 302 | 303 | `undefined` \| `Promise`<`boolean`\> 304 | 305 | #### Defined in 306 | 307 | [src/db_blocks.ts:107](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L107) 308 | 309 | ___ 310 | 311 | ### streamBlock 312 | 313 | ▸ **streamBlock**(`«destructured»`, `callback`): `Promise`<`boolean`\> 314 | 315 | #### Parameters 316 | 317 | | Name | Type | 318 | | :------ | :------ | 319 | | `«destructured»` | `Object` | 320 | | › `hash` | `string` \| `Buffer` | 321 | | › `height` | `number` | 322 | | › `highWaterMark?` | `number` | 323 | | `callback` | (`params`: `BlockStream`) => `any` | 324 | 325 | #### Returns 326 | 327 | `Promise`<`boolean`\> 328 | 329 | #### Defined in 330 | 331 | [src/db_blocks.ts:169](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L169) 332 | 333 | ___ 334 | 335 | ### syncDb 336 | 337 | ▸ **syncDb**(): `Promise`<`void`\> 338 | 339 | #### Returns 340 | 341 | `Promise`<`void`\> 342 | 343 | #### Defined in 344 | 345 | [src/db_blocks.ts:40](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L40) 346 | 347 | ___ 348 | 349 | ### writeBlockChunk 350 | 351 | ▸ **writeBlockChunk**(`«destructured»`): `Promise`<`unknown`\> 352 | 353 | #### Parameters 354 | 355 | | Name | Type | 356 | | :------ | :------ | 357 | | `«destructured»` | `Object` | 358 | | › `blockHash` | `Buffer` | 359 | | › `chunk` | `Buffer` | 360 | | › `finished` | `boolean` | 361 | | › `started` | `boolean` | 362 | 363 | #### Returns 364 | 365 | `Promise`<`unknown`\> 366 | 367 | #### Defined in 368 | 369 | [src/db_blocks.ts:113](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_blocks.ts#L113) 370 | -------------------------------------------------------------------------------- /docs/classes/DbHeaders.md: -------------------------------------------------------------------------------- 1 | [bsv-spv](../README.md) / DbHeaders 2 | 3 | # Class: DbHeaders 4 | 5 | ## Table of contents 6 | 7 | ### Constructors 8 | 9 | - [constructor](DbHeaders.md#constructor) 10 | 11 | ### Properties 12 | 13 | - [dbi\_headers](DbHeaders.md#dbi_headers) 14 | - [dbi\_root](DbHeaders.md#dbi_root) 15 | - [env](DbHeaders.md#env) 16 | - [headers](DbHeaders.md#headers) 17 | - [headersDir](DbHeaders.md#headersdir) 18 | 19 | ### Methods 20 | 21 | - [close](DbHeaders.md#close) 22 | - [getHeader](DbHeaders.md#getheader) 23 | - [loadHeaders](DbHeaders.md#loadheaders) 24 | - [saveHeaders](DbHeaders.md#saveheaders) 25 | 26 | ## Constructors 27 | 28 | ### constructor 29 | 30 | • **new DbHeaders**(`«destructured»`) 31 | 32 | #### Parameters 33 | 34 | | Name | Type | 35 | | :------ | :------ | 36 | | `«destructured»` | `Object` | 37 | | › `headers` | `any` | 38 | | › `headersDir` | `string` | 39 | | › `readOnly?` | `boolean` | 40 | 41 | #### Defined in 42 | 43 | [src/db_headers.ts:13](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_headers.ts#L13) 44 | 45 | ## Properties 46 | 47 | ### dbi\_headers 48 | 49 | • **dbi\_headers**: `Database`<`Buffer`, `Key`\> 50 | 51 | #### Defined in 52 | 53 | [src/db_headers.ts:10](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_headers.ts#L10) 54 | 55 | ___ 56 | 57 | ### dbi\_root 58 | 59 | • **dbi\_root**: `RootDatabase`<`any`, `Key`\> 60 | 61 | #### Defined in 62 | 63 | [src/db_headers.ts:9](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_headers.ts#L9) 64 | 65 | ___ 66 | 67 | ### env 68 | 69 | • **env**: `any` 70 | 71 | #### Defined in 72 | 73 | [src/db_headers.ts:8](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_headers.ts#L8) 74 | 75 | ___ 76 | 77 | ### headers 78 | 79 | • **headers**: `default` 80 | 81 | #### Defined in 82 | 83 | [src/db_headers.ts:7](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_headers.ts#L7) 84 | 85 | ___ 86 | 87 | ### headersDir 88 | 89 | • **headersDir**: `string` 90 | 91 | #### Defined in 92 | 93 | [src/db_headers.ts:11](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_headers.ts#L11) 94 | 95 | ## Methods 96 | 97 | ### close 98 | 99 | ▸ **close**(): `Promise`<`void`\> 100 | 101 | #### Returns 102 | 103 | `Promise`<`void`\> 104 | 105 | #### Defined in 106 | 107 | [src/db_headers.ts:37](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_headers.ts#L37) 108 | 109 | ___ 110 | 111 | ### getHeader 112 | 113 | ▸ **getHeader**(`hash`): `default` 114 | 115 | #### Parameters 116 | 117 | | Name | Type | 118 | | :------ | :------ | 119 | | `hash` | `string` \| `Buffer` | 120 | 121 | #### Returns 122 | 123 | `default` 124 | 125 | #### Defined in 126 | 127 | [src/db_headers.ts:59](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_headers.ts#L59) 128 | 129 | ___ 130 | 131 | ### loadHeaders 132 | 133 | ▸ **loadHeaders**(): `void` 134 | 135 | #### Returns 136 | 137 | `void` 138 | 139 | #### Defined in 140 | 141 | [src/db_headers.ts:70](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_headers.ts#L70) 142 | 143 | ___ 144 | 145 | ### saveHeaders 146 | 147 | ▸ **saveHeaders**(`headerArray`): `Promise`<`Buffer`[]\> 148 | 149 | #### Parameters 150 | 151 | | Name | Type | 152 | | :------ | :------ | 153 | | `headerArray` | `default`[] | 154 | 155 | #### Returns 156 | 157 | `Promise`<`Buffer`[]\> 158 | 159 | #### Defined in 160 | 161 | [src/db_headers.ts:46](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_headers.ts#L46) 162 | -------------------------------------------------------------------------------- /docs/classes/DbListener.md: -------------------------------------------------------------------------------- 1 | [bsv-spv](../README.md) / DbListener 2 | 3 | # Class: DbListener 4 | 5 | ## Table of contents 6 | 7 | ### Constructors 8 | 9 | - [constructor](DbListener.md#constructor) 10 | 11 | ### Properties 12 | 13 | - [dbi\_blocks](DbListener.md#dbi_blocks) 14 | - [dbi\_heights](DbListener.md#dbi_heights) 15 | - [dbi\_root](DbListener.md#dbi_root) 16 | - [listenerDir](DbListener.md#listenerdir) 17 | 18 | ### Methods 19 | 20 | - [batchBlocksProcessed](DbListener.md#batchblocksprocessed) 21 | - [blocksProcessed](DbListener.md#blocksprocessed) 22 | - [close](DbListener.md#close) 23 | - [delBlocks](DbListener.md#delblocks) 24 | - [getBlockHash](DbListener.md#getblockhash) 25 | - [getBlockInfo](DbListener.md#getblockinfo) 26 | - [getHash](DbListener.md#gethash) 27 | - [getSize](DbListener.md#getsize) 28 | - [isProcessed](DbListener.md#isprocessed) 29 | - [markBlockProcessed](DbListener.md#markblockprocessed) 30 | 31 | ## Constructors 32 | 33 | ### constructor 34 | 35 | • **new DbListener**(`«destructured»`) 36 | 37 | #### Parameters 38 | 39 | | Name | Type | 40 | | :------ | :------ | 41 | | `«destructured»` | `Object` | 42 | | › `listenerDir` | `string` | 43 | | › `readOnly?` | `boolean` | 44 | 45 | #### Defined in 46 | 47 | [src/db_listener.ts:20](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L20) 48 | 49 | ## Properties 50 | 51 | ### dbi\_blocks 52 | 53 | • **dbi\_blocks**: `Database`<`Buffer`, `Key`\> 54 | 55 | #### Defined in 56 | 57 | [src/db_listener.ts:16](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L16) 58 | 59 | ___ 60 | 61 | ### dbi\_heights 62 | 63 | • **dbi\_heights**: `Database`<`Buffer`, `Key`\> 64 | 65 | #### Defined in 66 | 67 | [src/db_listener.ts:17](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L17) 68 | 69 | ___ 70 | 71 | ### dbi\_root 72 | 73 | • **dbi\_root**: `RootDatabase`<`any`, `Key`\> 74 | 75 | #### Defined in 76 | 77 | [src/db_listener.ts:15](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L15) 78 | 79 | ___ 80 | 81 | ### listenerDir 82 | 83 | • **listenerDir**: `string` 84 | 85 | #### Defined in 86 | 87 | [src/db_listener.ts:18](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L18) 88 | 89 | ## Methods 90 | 91 | ### batchBlocksProcessed 92 | 93 | ▸ **batchBlocksProcessed**(`array`): `Promise`<`boolean`\> 94 | 95 | #### Parameters 96 | 97 | | Name | Type | 98 | | :------ | :------ | 99 | | `array` | `ListenerOptions`[] | 100 | 101 | #### Returns 102 | 103 | `Promise`<`boolean`\> 104 | 105 | #### Defined in 106 | 107 | [src/db_listener.ts:81](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L81) 108 | 109 | ___ 110 | 111 | ### blocksProcessed 112 | 113 | ▸ **blocksProcessed**(): `number` 114 | 115 | #### Returns 116 | 117 | `number` 118 | 119 | #### Defined in 120 | 121 | [src/db_listener.ts:99](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L99) 122 | 123 | ___ 124 | 125 | ### close 126 | 127 | ▸ **close**(): `Promise`<`void`\> 128 | 129 | #### Returns 130 | 131 | `Promise`<`void`\> 132 | 133 | #### Defined in 134 | 135 | [src/db_listener.ts:44](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L44) 136 | 137 | ___ 138 | 139 | ### delBlocks 140 | 141 | ▸ **delBlocks**(`from`, `to`): `Promise`<`boolean`\> 142 | 143 | #### Parameters 144 | 145 | | Name | Type | 146 | | :------ | :------ | 147 | | `from` | `number` | 148 | | `to` | `number` | 149 | 150 | #### Returns 151 | 152 | `Promise`<`boolean`\> 153 | 154 | #### Defined in 155 | 156 | [src/db_listener.ts:121](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L121) 157 | 158 | ___ 159 | 160 | ### getBlockHash 161 | 162 | ▸ **getBlockHash**(`height`): `string` 163 | 164 | #### Parameters 165 | 166 | | Name | Type | 167 | | :------ | :------ | 168 | | `height` | `number` | 169 | 170 | #### Returns 171 | 172 | `string` 173 | 174 | #### Defined in 175 | 176 | [src/db_listener.ts:115](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L115) 177 | 178 | ___ 179 | 180 | ### getBlockInfo 181 | 182 | ▸ **getBlockInfo**(`blockHash`): `any` 183 | 184 | #### Parameters 185 | 186 | | Name | Type | 187 | | :------ | :------ | 188 | | `blockHash` | `string` \| `Buffer` | 189 | 190 | #### Returns 191 | 192 | `any` 193 | 194 | #### Defined in 195 | 196 | [src/db_listener.ts:108](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L108) 197 | 198 | ___ 199 | 200 | ### getHash 201 | 202 | ▸ **getHash**(`height`): `string` 203 | 204 | #### Parameters 205 | 206 | | Name | Type | 207 | | :------ | :------ | 208 | | `height` | `number` | 209 | 210 | #### Returns 211 | 212 | `string` 213 | 214 | #### Defined in 215 | 216 | [src/db_listener.ts:102](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L102) 217 | 218 | ___ 219 | 220 | ### getSize 221 | 222 | ▸ **getSize**(): `void` 223 | 224 | #### Returns 225 | 226 | `void` 227 | 228 | #### Defined in 229 | 230 | [src/db_listener.ts:128](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L128) 231 | 232 | ___ 233 | 234 | ### isProcessed 235 | 236 | ▸ **isProcessed**(`height`): `boolean` 237 | 238 | #### Parameters 239 | 240 | | Name | Type | 241 | | :------ | :------ | 242 | | `height` | `number` | 243 | 244 | #### Returns 245 | 246 | `boolean` 247 | 248 | #### Defined in 249 | 250 | [src/db_listener.ts:96](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L96) 251 | 252 | ___ 253 | 254 | ### markBlockProcessed 255 | 256 | ▸ **markBlockProcessed**(`«destructured»`): `Promise`<[`boolean`, `boolean`]\> 257 | 258 | #### Parameters 259 | 260 | | Name | Type | 261 | | :------ | :------ | 262 | | `«destructured»` | `ListenerOptions` | 263 | 264 | #### Returns 265 | 266 | `Promise`<[`boolean`, `boolean`]\> 267 | 268 | #### Defined in 269 | 270 | [src/db_listener.ts:56](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_listener.ts#L56) 271 | -------------------------------------------------------------------------------- /docs/classes/DbMempool.md: -------------------------------------------------------------------------------- 1 | [bsv-spv](../README.md) / DbMempool 2 | 3 | # Class: DbMempool 4 | 5 | ## Table of contents 6 | 7 | ### Constructors 8 | 9 | - [constructor](DbMempool.md#constructor) 10 | 11 | ### Properties 12 | 13 | - [batch](DbMempool.md#batch) 14 | - [db](DbMempool.md#db) 15 | - [intervalBatch](DbMempool.md#intervalbatch) 16 | - [intervalPrune](DbMempool.md#intervalprune) 17 | - [pruneAfter](DbMempool.md#pruneafter) 18 | - [txs](DbMempool.md#txs) 19 | 20 | ### Methods 21 | 22 | - [close](DbMempool.md#close) 23 | - [delTxs](DbMempool.md#deltxs) 24 | - [getTx](DbMempool.md#gettx) 25 | - [getTxids](DbMempool.md#gettxids) 26 | - [pruneTxs](DbMempool.md#prunetxs) 27 | - [saveTx](DbMempool.md#savetx) 28 | - [saveTxs](DbMempool.md#savetxs) 29 | 30 | ## Constructors 31 | 32 | ### constructor 33 | 34 | • **new DbMempool**(`«destructured»`) 35 | 36 | #### Parameters 37 | 38 | | Name | Type | 39 | | :------ | :------ | 40 | | `«destructured»` | `Object` | 41 | | › `mempoolDir` | `string` | 42 | | › `pruneAfter?` | `number` | 43 | | › `readOnly?` | `boolean` | 44 | 45 | #### Defined in 46 | 47 | [src/db_mempool.ts:15](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L15) 48 | 49 | ## Properties 50 | 51 | ### batch 52 | 53 | • **batch**: `AbstractBatch`[] 54 | 55 | #### Defined in 56 | 57 | [src/db_mempool.ts:10](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L10) 58 | 59 | ___ 60 | 61 | ### db 62 | 63 | • **db**: `LevelUp`<`AbstractLevelDOWN`<`any`, `any`\>, `AbstractIterator`<`any`, `any`\>\> 64 | 65 | #### Defined in 66 | 67 | [src/db_mempool.ts:8](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L8) 68 | 69 | ___ 70 | 71 | ### intervalBatch 72 | 73 | • `Optional` **intervalBatch**: `Timer` 74 | 75 | #### Defined in 76 | 77 | [src/db_mempool.ts:11](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L11) 78 | 79 | ___ 80 | 81 | ### intervalPrune 82 | 83 | • `Optional` **intervalPrune**: `Timer` 84 | 85 | #### Defined in 86 | 87 | [src/db_mempool.ts:12](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L12) 88 | 89 | ___ 90 | 91 | ### pruneAfter 92 | 93 | • **pruneAfter**: `number` 94 | 95 | #### Defined in 96 | 97 | [src/db_mempool.ts:13](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L13) 98 | 99 | ___ 100 | 101 | ### txs 102 | 103 | • **txs**: `Object` 104 | 105 | #### Index signature 106 | 107 | ▪ [key: `string`]: `Buffer` 108 | 109 | #### Defined in 110 | 111 | [src/db_mempool.ts:9](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L9) 112 | 113 | ## Methods 114 | 115 | ### close 116 | 117 | ▸ **close**(): `void` 118 | 119 | #### Returns 120 | 121 | `void` 122 | 123 | #### Defined in 124 | 125 | [src/db_mempool.ts:32](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L32) 126 | 127 | ___ 128 | 129 | ### delTxs 130 | 131 | ▸ **delTxs**(`txids`): `Promise`<`unknown`\> 132 | 133 | #### Parameters 134 | 135 | | Name | Type | 136 | | :------ | :------ | 137 | | `txids` | `Buffer`[] | 138 | 139 | #### Returns 140 | 141 | `Promise`<`unknown`\> 142 | 143 | #### Defined in 144 | 145 | [src/db_mempool.ts:68](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L68) 146 | 147 | ___ 148 | 149 | ### getTx 150 | 151 | ▸ **getTx**(`txid`): `Promise`<{ `size`: `number` ; `time`: `number` ; `tx`: `default` }\> 152 | 153 | #### Parameters 154 | 155 | | Name | Type | 156 | | :------ | :------ | 157 | | `txid` | `string` \| `Buffer` | 158 | 159 | #### Returns 160 | 161 | `Promise`<{ `size`: `number` ; `time`: `number` ; `tx`: `default` }\> 162 | 163 | #### Defined in 164 | 165 | [src/db_mempool.ts:112](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L112) 166 | 167 | ___ 168 | 169 | ### getTxids 170 | 171 | ▸ **getTxids**(`«destructured»`): `Promise`<`Buffer`[]\> 172 | 173 | #### Parameters 174 | 175 | | Name | Type | 176 | | :------ | :------ | 177 | | `«destructured»` | `Object` | 178 | | › `newerThan?` | `number` | 179 | | › `olderThan?` | `number` | 180 | 181 | #### Returns 182 | 183 | `Promise`<`Buffer`[]\> 184 | 185 | #### Defined in 186 | 187 | [src/db_mempool.ts:80](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L80) 188 | 189 | ___ 190 | 191 | ### pruneTxs 192 | 193 | ▸ **pruneTxs**(`olderThan?`): `Promise`<`void`\> 194 | 195 | #### Parameters 196 | 197 | | Name | Type | 198 | | :------ | :------ | 199 | | `olderThan?` | `number` | 200 | 201 | #### Returns 202 | 203 | `Promise`<`void`\> 204 | 205 | #### Defined in 206 | 207 | [src/db_mempool.ts:131](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L131) 208 | 209 | ___ 210 | 211 | ### saveTx 212 | 213 | ▸ **saveTx**(`tx`): `void` 214 | 215 | #### Parameters 216 | 217 | | Name | Type | 218 | | :------ | :------ | 219 | | `tx` | `default` | 220 | 221 | #### Returns 222 | 223 | `void` 224 | 225 | #### Defined in 226 | 227 | [src/db_mempool.ts:38](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L38) 228 | 229 | ___ 230 | 231 | ### saveTxs 232 | 233 | ▸ **saveTxs**(): `Promise`<`unknown`\> 234 | 235 | #### Returns 236 | 237 | `Promise`<`unknown`\> 238 | 239 | #### Defined in 240 | 241 | [src/db_mempool.ts:55](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_mempool.ts#L55) 242 | -------------------------------------------------------------------------------- /docs/classes/DbNodes.md: -------------------------------------------------------------------------------- 1 | [bsv-spv](../README.md) / DbNodes 2 | 3 | # Class: DbNodes 4 | 5 | ## Table of contents 6 | 7 | ### Constructors 8 | 9 | - [constructor](DbNodes.md#constructor) 10 | 11 | ### Properties 12 | 13 | - [blacklistTime](DbNodes.md#blacklisttime) 14 | - [dbi\_blacklisted](DbNodes.md#dbi_blacklisted) 15 | - [dbi\_connected](DbNodes.md#dbi_connected) 16 | - [dbi\_meta](DbNodes.md#dbi_meta) 17 | - [dbi\_root](DbNodes.md#dbi_root) 18 | - [dbi\_seen](DbNodes.md#dbi_seen) 19 | - [enableIpv6](DbNodes.md#enableipv6) 20 | - [env](DbNodes.md#env) 21 | - [nodesDir](DbNodes.md#nodesdir) 22 | 23 | ### Methods 24 | 25 | - [blacklist](DbNodes.md#blacklist) 26 | - [close](DbNodes.md#close) 27 | - [connected](DbNodes.md#connected) 28 | - [formatUrl](DbNodes.md#formaturl) 29 | - [getBlacklistedNodes](DbNodes.md#getblacklistednodes) 30 | - [getConnectedNodes](DbNodes.md#getconnectednodes) 31 | - [getSeenNodes](DbNodes.md#getseennodes) 32 | - [hasConnected](DbNodes.md#hasconnected) 33 | - [hasSavedSeen](DbNodes.md#hassavedseen) 34 | - [isBlacklisted](DbNodes.md#isblacklisted) 35 | - [markSavedSeen](DbNodes.md#marksavedseen) 36 | - [saveSeenNodes](DbNodes.md#saveseennodes) 37 | 38 | ## Constructors 39 | 40 | ### constructor 41 | 42 | • **new DbNodes**(`«destructured»`) 43 | 44 | #### Parameters 45 | 46 | | Name | Type | 47 | | :------ | :------ | 48 | | `«destructured»` | `Object` | 49 | | › `blacklistTime?` | `number` | 50 | | › `enableIpv6?` | `boolean` | 51 | | › `nodesDir` | `string` | 52 | | › `readOnly?` | `boolean` | 53 | 54 | #### Defined in 55 | 56 | [src/db_nodes.ts:16](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L16) 57 | 58 | ## Properties 59 | 60 | ### blacklistTime 61 | 62 | • **blacklistTime**: `number` 63 | 64 | #### Defined in 65 | 66 | [src/db_nodes.ts:6](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L6) 67 | 68 | ___ 69 | 70 | ### dbi\_blacklisted 71 | 72 | • **dbi\_blacklisted**: `Database`<`number`, `string`\> 73 | 74 | #### Defined in 75 | 76 | [src/db_nodes.ts:12](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L12) 77 | 78 | ___ 79 | 80 | ### dbi\_connected 81 | 82 | • **dbi\_connected**: `Database`<`number`, `string`\> 83 | 84 | #### Defined in 85 | 86 | [src/db_nodes.ts:11](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L11) 87 | 88 | ___ 89 | 90 | ### dbi\_meta 91 | 92 | • **dbi\_meta**: `Database`<`any`, `string`\> 93 | 94 | #### Defined in 95 | 96 | [src/db_nodes.ts:9](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L9) 97 | 98 | ___ 99 | 100 | ### dbi\_root 101 | 102 | • **dbi\_root**: `RootDatabase`<`any`, `Key`\> 103 | 104 | #### Defined in 105 | 106 | [src/db_nodes.ts:8](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L8) 107 | 108 | ___ 109 | 110 | ### dbi\_seen 111 | 112 | • **dbi\_seen**: `Database`<`any`, `string`\> 113 | 114 | #### Defined in 115 | 116 | [src/db_nodes.ts:10](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L10) 117 | 118 | ___ 119 | 120 | ### enableIpv6 121 | 122 | • **enableIpv6**: `boolean` 123 | 124 | #### Defined in 125 | 126 | [src/db_nodes.ts:14](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L14) 127 | 128 | ___ 129 | 130 | ### env 131 | 132 | • **env**: `any` 133 | 134 | #### Defined in 135 | 136 | [src/db_nodes.ts:7](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L7) 137 | 138 | ___ 139 | 140 | ### nodesDir 141 | 142 | • **nodesDir**: `string` 143 | 144 | #### Defined in 145 | 146 | [src/db_nodes.ts:13](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L13) 147 | 148 | ## Methods 149 | 150 | ### blacklist 151 | 152 | ▸ **blacklist**(`«destructured»`): `void` 153 | 154 | #### Parameters 155 | 156 | | Name | Type | 157 | | :------ | :------ | 158 | | `«destructured»` | `Object` | 159 | | › `node` | `string` | 160 | | › `port?` | `number` | 161 | 162 | #### Returns 163 | 164 | `void` 165 | 166 | #### Defined in 167 | 168 | [src/db_nodes.ts:126](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L126) 169 | 170 | ___ 171 | 172 | ### close 173 | 174 | ▸ **close**(): `Promise`<`void`\> 175 | 176 | #### Returns 177 | 178 | `Promise`<`void`\> 179 | 180 | #### Defined in 181 | 182 | [src/db_nodes.ts:51](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L51) 183 | 184 | ___ 185 | 186 | ### connected 187 | 188 | ▸ **connected**(`«destructured»`): `void` 189 | 190 | #### Parameters 191 | 192 | | Name | Type | 193 | | :------ | :------ | 194 | | `«destructured»` | `Object` | 195 | | › `node` | `string` | 196 | | › `port?` | `number` | 197 | 198 | #### Returns 199 | 200 | `void` 201 | 202 | #### Defined in 203 | 204 | [src/db_nodes.ts:114](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L114) 205 | 206 | ___ 207 | 208 | ### formatUrl 209 | 210 | ▸ **formatUrl**(`«destructured»`): `string` 211 | 212 | #### Parameters 213 | 214 | | Name | Type | 215 | | :------ | :------ | 216 | | `«destructured»` | `Object` | 217 | | › `node` | `string` | 218 | | › `port?` | `number` | 219 | 220 | #### Returns 221 | 222 | `string` 223 | 224 | #### Defined in 225 | 226 | [src/db_nodes.ts:69](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L69) 227 | 228 | ___ 229 | 230 | ### getBlacklistedNodes 231 | 232 | ▸ **getBlacklistedNodes**(): `string`[] 233 | 234 | #### Returns 235 | 236 | `string`[] 237 | 238 | #### Defined in 239 | 240 | [src/db_nodes.ts:140](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L140) 241 | 242 | ___ 243 | 244 | ### getConnectedNodes 245 | 246 | ▸ **getConnectedNodes**(): `string`[] 247 | 248 | #### Returns 249 | 250 | `string`[] 251 | 252 | #### Defined in 253 | 254 | [src/db_nodes.ts:151](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L151) 255 | 256 | ___ 257 | 258 | ### getSeenNodes 259 | 260 | ▸ **getSeenNodes**(): `string`[] 261 | 262 | #### Returns 263 | 264 | `string`[] 265 | 266 | #### Defined in 267 | 268 | [src/db_nodes.ts:160](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L160) 269 | 270 | ___ 271 | 272 | ### hasConnected 273 | 274 | ▸ **hasConnected**(`«destructured»`): `boolean` 275 | 276 | #### Parameters 277 | 278 | | Name | Type | 279 | | :------ | :------ | 280 | | `«destructured»` | `Object` | 281 | | › `node` | `string` | 282 | | › `port?` | `number` | 283 | 284 | #### Returns 285 | 286 | `boolean` 287 | 288 | #### Defined in 289 | 290 | [src/db_nodes.ts:120](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L120) 291 | 292 | ___ 293 | 294 | ### hasSavedSeen 295 | 296 | ▸ **hasSavedSeen**(`secondsAgo?`): `boolean` 297 | 298 | #### Parameters 299 | 300 | | Name | Type | Default value | 301 | | :------ | :------ | :------ | 302 | | `secondsAgo` | `number` | `60` | 303 | 304 | #### Returns 305 | 306 | `boolean` 307 | 308 | #### Defined in 309 | 310 | [src/db_nodes.ts:104](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L104) 311 | 312 | ___ 313 | 314 | ### isBlacklisted 315 | 316 | ▸ **isBlacklisted**(`«destructured»`): `boolean` 317 | 318 | #### Parameters 319 | 320 | | Name | Type | 321 | | :------ | :------ | 322 | | `«destructured»` | `Object` | 323 | | › `node` | `string` | 324 | | › `port?` | `number` | 325 | 326 | #### Returns 327 | 328 | `boolean` 329 | 330 | #### Defined in 331 | 332 | [src/db_nodes.ts:134](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L134) 333 | 334 | ___ 335 | 336 | ### markSavedSeen 337 | 338 | ▸ **markSavedSeen**(): `void` 339 | 340 | #### Returns 341 | 342 | `void` 343 | 344 | #### Defined in 345 | 346 | [src/db_nodes.ts:101](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L101) 347 | 348 | ___ 349 | 350 | ### saveSeenNodes 351 | 352 | ▸ **saveSeenNodes**(`addrs`): `Promise`<`number`\> 353 | 354 | #### Parameters 355 | 356 | | Name | Type | 357 | | :------ | :------ | 358 | | `addrs` | `string`[] \| `NetAddress`[] | 359 | 360 | #### Returns 361 | 362 | `Promise`<`number`\> 363 | 364 | #### Defined in 365 | 366 | [src/db_nodes.ts:82](https://github.com/kevinejohn/bsv-spv/blob/master/src/db_nodes.ts#L82) 367 | -------------------------------------------------------------------------------- /docs/classes/Listener.md: -------------------------------------------------------------------------------- 1 | [bsv-spv](../README.md) / Listener 2 | 3 | # Class: Listener 4 | 5 | ## Hierarchy 6 | 7 | - `TypedEventEmitter`<`SpvEvents`, `this`\> 8 | 9 | ↳ **`Listener`** 10 | 11 | ↳↳ [`Server`](Server.md) 12 | 13 | ## Table of contents 14 | 15 | ### Constructors 16 | 17 | - [constructor](Listener.md#constructor) 18 | 19 | ### Properties 20 | 21 | - [blockHeight](Listener.md#blockheight) 22 | - [client](Listener.md#client) 23 | - [db\_blocks](Listener.md#db_blocks) 24 | - [db\_headers](Listener.md#db_headers) 25 | - [db\_listener](Listener.md#db_listener) 26 | - [disableInterval](Listener.md#disableinterval) 27 | - [headers](Listener.md#headers) 28 | - [host](Listener.md#host) 29 | - [interval](Listener.md#interval) 30 | - [mempool\_txs](Listener.md#mempool_txs) 31 | - [multithread](Listener.md#multithread) 32 | - [name](Listener.md#name) 33 | - [port](Listener.md#port) 34 | - [reconnectTime](Listener.md#reconnecttime) 35 | - [reconnectTimeout](Listener.md#reconnecttimeout) 36 | - [syncingBlocks](Listener.md#syncingblocks) 37 | - [ticker](Listener.md#ticker) 38 | - [txsSeen](Listener.md#txsseen) 39 | - [txsSize](Listener.md#txssize) 40 | 41 | ### Methods 42 | 43 | - [addListener](Listener.md#addlistener) 44 | - [connect](Listener.md#connect) 45 | - [disconnect](Listener.md#disconnect) 46 | - [emit](Listener.md#emit) 47 | - [eventNames](Listener.md#eventnames) 48 | - [getBlockInfo](Listener.md#getblockinfo) 49 | - [getMaxListeners](Listener.md#getmaxlisteners) 50 | - [listenerCount](Listener.md#listenercount) 51 | - [listeners](Listener.md#listeners) 52 | - [off](Listener.md#off) 53 | - [on](Listener.md#on) 54 | - [onMessage](Listener.md#onmessage) 55 | - [once](Listener.md#once) 56 | - [prependListener](Listener.md#prependlistener) 57 | - [prependOnceListener](Listener.md#prependoncelistener) 58 | - [rawListeners](Listener.md#rawlisteners) 59 | - [readBlock](Listener.md#readblock) 60 | - [reconnect](Listener.md#reconnect) 61 | - [removeAllListeners](Listener.md#removealllisteners) 62 | - [removeListener](Listener.md#removelistener) 63 | - [rewindHeight](Listener.md#rewindheight) 64 | - [setMaxListeners](Listener.md#setmaxlisteners) 65 | - [syncBlocks](Listener.md#syncblocks) 66 | 67 | ## Constructors 68 | 69 | ### constructor 70 | 71 | • **new Listener**(`«destructured»`) 72 | 73 | #### Parameters 74 | 75 | | Name | Type | 76 | | :------ | :------ | 77 | | `«destructured»` | [`ListenerOptions`](../interfaces/ListenerOptions.md) | 78 | 79 | #### Overrides 80 | 81 | (EventEmitter as new () =\> SpvEmitter).constructor 82 | 83 | #### Defined in 84 | 85 | [src/listener.ts:48](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L48) 86 | 87 | ## Properties 88 | 89 | ### blockHeight 90 | 91 | • **blockHeight**: `number` 92 | 93 | #### Defined in 94 | 95 | [src/listener.ts:30](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L30) 96 | 97 | ___ 98 | 99 | ### client 100 | 101 | • `Optional` **client**: `Socket` 102 | 103 | #### Defined in 104 | 105 | [src/listener.ts:43](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L43) 106 | 107 | ___ 108 | 109 | ### db\_blocks 110 | 111 | • **db\_blocks**: [`DbBlocks`](DbBlocks.md) 112 | 113 | #### Defined in 114 | 115 | [src/listener.ts:37](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L37) 116 | 117 | ___ 118 | 119 | ### db\_headers 120 | 121 | • **db\_headers**: [`DbHeaders`](DbHeaders.md) 122 | 123 | #### Defined in 124 | 125 | [src/listener.ts:38](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L38) 126 | 127 | ___ 128 | 129 | ### db\_listener 130 | 131 | • **db\_listener**: [`DbListener`](DbListener.md) 132 | 133 | #### Defined in 134 | 135 | [src/listener.ts:39](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L39) 136 | 137 | ___ 138 | 139 | ### disableInterval 140 | 141 | • **disableInterval**: `undefined` \| `boolean` 142 | 143 | #### Defined in 144 | 145 | [src/listener.ts:31](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L31) 146 | 147 | ___ 148 | 149 | ### headers 150 | 151 | • **headers**: `default` 152 | 153 | #### Defined in 154 | 155 | [src/listener.ts:40](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L40) 156 | 157 | ___ 158 | 159 | ### host 160 | 161 | • **host**: `string` 162 | 163 | #### Defined in 164 | 165 | [src/listener.ts:33](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L33) 166 | 167 | ___ 168 | 169 | ### interval 170 | 171 | • `Optional` **interval**: `Timeout` 172 | 173 | #### Defined in 174 | 175 | [src/listener.ts:42](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L42) 176 | 177 | ___ 178 | 179 | ### mempool\_txs 180 | 181 | • **mempool\_txs**: `boolean` 182 | 183 | #### Defined in 184 | 185 | [src/listener.ts:35](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L35) 186 | 187 | ___ 188 | 189 | ### multithread 190 | 191 | • `Optional` **multithread**: `Object` 192 | 193 | #### Type declaration 194 | 195 | | Name | Type | 196 | | :------ | :------ | 197 | | `index` | `number` | 198 | | `threads` | `number` | 199 | 200 | #### Defined in 201 | 202 | [src/listener.ts:32](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L32) 203 | 204 | ___ 205 | 206 | ### name 207 | 208 | • **name**: `string` 209 | 210 | #### Defined in 211 | 212 | [src/listener.ts:29](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L29) 213 | 214 | ___ 215 | 216 | ### port 217 | 218 | • **port**: `number` 219 | 220 | #### Defined in 221 | 222 | [src/listener.ts:34](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L34) 223 | 224 | ___ 225 | 226 | ### reconnectTime 227 | 228 | • **reconnectTime**: `number` 229 | 230 | #### Defined in 231 | 232 | [src/listener.ts:36](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L36) 233 | 234 | ___ 235 | 236 | ### reconnectTimeout 237 | 238 | • `Optional` **reconnectTimeout**: `Timeout` 239 | 240 | #### Defined in 241 | 242 | [src/listener.ts:41](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L41) 243 | 244 | ___ 245 | 246 | ### syncingBlocks 247 | 248 | • **syncingBlocks**: `boolean` 249 | 250 | #### Defined in 251 | 252 | [src/listener.ts:46](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L46) 253 | 254 | ___ 255 | 256 | ### ticker 257 | 258 | • **ticker**: `string` 259 | 260 | #### Defined in 261 | 262 | [src/listener.ts:28](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L28) 263 | 264 | ___ 265 | 266 | ### txsSeen 267 | 268 | • **txsSeen**: `number` 269 | 270 | #### Defined in 271 | 272 | [src/listener.ts:44](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L44) 273 | 274 | ___ 275 | 276 | ### txsSize 277 | 278 | • **txsSize**: `number` 279 | 280 | #### Defined in 281 | 282 | [src/listener.ts:45](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L45) 283 | 284 | ## Methods 285 | 286 | ### addListener 287 | 288 | ▸ **addListener**<`E`\>(`event`, `listener`): [`Listener`](Listener.md) 289 | 290 | #### Type parameters 291 | 292 | | Name | Type | 293 | | :------ | :------ | 294 | | `E` | extends keyof `SpvEvents` | 295 | 296 | #### Parameters 297 | 298 | | Name | Type | 299 | | :------ | :------ | 300 | | `event` | `E` | 301 | | `listener` | `SpvEvents`[`E`] | 302 | 303 | #### Returns 304 | 305 | [`Listener`](Listener.md) 306 | 307 | #### Inherited from 308 | 309 | (EventEmitter as new () =\> SpvEmitter).addListener 310 | 311 | #### Defined in 312 | 313 | [src/types/TypedEventEmitter.ts:24](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L24) 314 | 315 | ___ 316 | 317 | ### connect 318 | 319 | ▸ **connect**(`«destructured»`): `void` 320 | 321 | #### Parameters 322 | 323 | | Name | Type | 324 | | :------ | :------ | 325 | | `«destructured»` | `Object` | 326 | | › `host?` | `string` | 327 | | › `mempool_txs?` | `boolean` | 328 | | › `port` | `number` | 329 | 330 | #### Returns 331 | 332 | `void` 333 | 334 | #### Defined in 335 | 336 | [src/listener.ts:189](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L189) 337 | 338 | ___ 339 | 340 | ### disconnect 341 | 342 | ▸ **disconnect**(): `void` 343 | 344 | #### Returns 345 | 346 | `void` 347 | 348 | #### Defined in 349 | 350 | [src/listener.ts:118](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L118) 351 | 352 | ___ 353 | 354 | ### emit 355 | 356 | ▸ **emit**<`E`\>(`event`, `...args`): `boolean` 357 | 358 | #### Type parameters 359 | 360 | | Name | Type | 361 | | :------ | :------ | 362 | | `E` | extends keyof `SpvEvents` | 363 | 364 | #### Parameters 365 | 366 | | Name | Type | 367 | | :------ | :------ | 368 | | `event` | `E` | 369 | | `...args` | `Parameters`<`SpvEvents`[`E`]\> | 370 | 371 | #### Returns 372 | 373 | `boolean` 374 | 375 | #### Inherited from 376 | 377 | (EventEmitter as new () =\> SpvEmitter).emit 378 | 379 | #### Defined in 380 | 381 | [src/types/TypedEventEmitter.ts:37](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L37) 382 | 383 | ___ 384 | 385 | ### eventNames 386 | 387 | ▸ **eventNames**(): (`string` \| `symbol`)[] 388 | 389 | #### Returns 390 | 391 | (`string` \| `symbol`)[] 392 | 393 | #### Inherited from 394 | 395 | (EventEmitter as new () =\> SpvEmitter).eventNames 396 | 397 | #### Defined in 398 | 399 | [src/types/TypedEventEmitter.ts:42](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L42) 400 | 401 | ___ 402 | 403 | ### getBlockInfo 404 | 405 | ▸ **getBlockInfo**(`«destructured»`): `any` 406 | 407 | #### Parameters 408 | 409 | | Name | Type | 410 | | :------ | :------ | 411 | | `«destructured»` | `Object` | 412 | | › `hash` | `string` | 413 | | › `height` | `number` | 414 | 415 | #### Returns 416 | 417 | `any` 418 | 419 | #### Defined in 420 | 421 | [src/listener.ts:391](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L391) 422 | 423 | ___ 424 | 425 | ### getMaxListeners 426 | 427 | ▸ **getMaxListeners**(): `number` 428 | 429 | #### Returns 430 | 431 | `number` 432 | 433 | #### Inherited from 434 | 435 | (EventEmitter as new () =\> SpvEmitter).getMaxListeners 436 | 437 | #### Defined in 438 | 439 | [src/types/TypedEventEmitter.ts:47](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L47) 440 | 441 | ___ 442 | 443 | ### listenerCount 444 | 445 | ▸ **listenerCount**<`E`\>(`event`): `number` 446 | 447 | #### Type parameters 448 | 449 | | Name | Type | 450 | | :------ | :------ | 451 | | `E` | extends keyof `SpvEvents` | 452 | 453 | #### Parameters 454 | 455 | | Name | Type | 456 | | :------ | :------ | 457 | | `event` | `E` | 458 | 459 | #### Returns 460 | 461 | `number` 462 | 463 | #### Inherited from 464 | 465 | (EventEmitter as new () =\> SpvEmitter).listenerCount 466 | 467 | #### Defined in 468 | 469 | [src/types/TypedEventEmitter.ts:45](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L45) 470 | 471 | ___ 472 | 473 | ### listeners 474 | 475 | ▸ **listeners**<`E`\>(`event`): `SpvEvents`[`E`][] 476 | 477 | #### Type parameters 478 | 479 | | Name | Type | 480 | | :------ | :------ | 481 | | `E` | extends keyof `SpvEvents` | 482 | 483 | #### Parameters 484 | 485 | | Name | Type | 486 | | :------ | :------ | 487 | | `event` | `E` | 488 | 489 | #### Returns 490 | 491 | `SpvEvents`[`E`][] 492 | 493 | #### Inherited from 494 | 495 | (EventEmitter as new () =\> SpvEmitter).listeners 496 | 497 | #### Defined in 498 | 499 | [src/types/TypedEventEmitter.ts:44](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L44) 500 | 501 | ___ 502 | 503 | ### off 504 | 505 | ▸ **off**<`E`\>(`event`, `listener`): [`Listener`](Listener.md) 506 | 507 | #### Type parameters 508 | 509 | | Name | Type | 510 | | :------ | :------ | 511 | | `E` | extends keyof `SpvEvents` | 512 | 513 | #### Parameters 514 | 515 | | Name | Type | 516 | | :------ | :------ | 517 | | `event` | `E` | 518 | | `listener` | `SpvEvents`[`E`] | 519 | 520 | #### Returns 521 | 522 | [`Listener`](Listener.md) 523 | 524 | #### Inherited from 525 | 526 | (EventEmitter as new () =\> SpvEmitter).off 527 | 528 | #### Defined in 529 | 530 | [src/types/TypedEventEmitter.ts:33](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L33) 531 | 532 | ___ 533 | 534 | ### on 535 | 536 | ▸ **on**<`E`\>(`event`, `listener`): [`Listener`](Listener.md) 537 | 538 | #### Type parameters 539 | 540 | | Name | Type | 541 | | :------ | :------ | 542 | | `E` | extends keyof `SpvEvents` | 543 | 544 | #### Parameters 545 | 546 | | Name | Type | 547 | | :------ | :------ | 548 | | `event` | `E` | 549 | | `listener` | `SpvEvents`[`E`] | 550 | 551 | #### Returns 552 | 553 | [`Listener`](Listener.md) 554 | 555 | #### Inherited from 556 | 557 | (EventEmitter as new () =\> SpvEmitter).on 558 | 559 | #### Defined in 560 | 561 | [src/types/TypedEventEmitter.ts:25](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L25) 562 | 563 | ___ 564 | 565 | ### onMessage 566 | 567 | ▸ **onMessage**(`obj`): `void` 568 | 569 | #### Parameters 570 | 571 | | Name | Type | 572 | | :------ | :------ | 573 | | `obj` | `Object` | 574 | | `obj.command` | `string` | 575 | | `obj.data` | `any` | 576 | 577 | #### Returns 578 | 579 | `void` 580 | 581 | #### Defined in 582 | 583 | [src/listener.ts:139](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L139) 584 | 585 | ___ 586 | 587 | ### once 588 | 589 | ▸ **once**<`E`\>(`event`, `listener`): [`Listener`](Listener.md) 590 | 591 | #### Type parameters 592 | 593 | | Name | Type | 594 | | :------ | :------ | 595 | | `E` | extends keyof `SpvEvents` | 596 | 597 | #### Parameters 598 | 599 | | Name | Type | 600 | | :------ | :------ | 601 | | `event` | `E` | 602 | | `listener` | `SpvEvents`[`E`] | 603 | 604 | #### Returns 605 | 606 | [`Listener`](Listener.md) 607 | 608 | #### Inherited from 609 | 610 | (EventEmitter as new () =\> SpvEmitter).once 611 | 612 | #### Defined in 613 | 614 | [src/types/TypedEventEmitter.ts:26](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L26) 615 | 616 | ___ 617 | 618 | ### prependListener 619 | 620 | ▸ **prependListener**<`E`\>(`event`, `listener`): [`Listener`](Listener.md) 621 | 622 | #### Type parameters 623 | 624 | | Name | Type | 625 | | :------ | :------ | 626 | | `E` | extends keyof `SpvEvents` | 627 | 628 | #### Parameters 629 | 630 | | Name | Type | 631 | | :------ | :------ | 632 | | `event` | `E` | 633 | | `listener` | `SpvEvents`[`E`] | 634 | 635 | #### Returns 636 | 637 | [`Listener`](Listener.md) 638 | 639 | #### Inherited from 640 | 641 | (EventEmitter as new () =\> SpvEmitter).prependListener 642 | 643 | #### Defined in 644 | 645 | [src/types/TypedEventEmitter.ts:27](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L27) 646 | 647 | ___ 648 | 649 | ### prependOnceListener 650 | 651 | ▸ **prependOnceListener**<`E`\>(`event`, `listener`): [`Listener`](Listener.md) 652 | 653 | #### Type parameters 654 | 655 | | Name | Type | 656 | | :------ | :------ | 657 | | `E` | extends keyof `SpvEvents` | 658 | 659 | #### Parameters 660 | 661 | | Name | Type | 662 | | :------ | :------ | 663 | | `event` | `E` | 664 | | `listener` | `SpvEvents`[`E`] | 665 | 666 | #### Returns 667 | 668 | [`Listener`](Listener.md) 669 | 670 | #### Inherited from 671 | 672 | (EventEmitter as new () =\> SpvEmitter).prependOnceListener 673 | 674 | #### Defined in 675 | 676 | [src/types/TypedEventEmitter.ts:28](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L28) 677 | 678 | ___ 679 | 680 | ### rawListeners 681 | 682 | ▸ **rawListeners**<`E`\>(`event`): `SpvEvents`[`E`][] 683 | 684 | #### Type parameters 685 | 686 | | Name | Type | 687 | | :------ | :------ | 688 | | `E` | extends keyof `SpvEvents` | 689 | 690 | #### Parameters 691 | 692 | | Name | Type | 693 | | :------ | :------ | 694 | | `event` | `E` | 695 | 696 | #### Returns 697 | 698 | `SpvEvents`[`E`][] 699 | 700 | #### Inherited from 701 | 702 | (EventEmitter as new () =\> SpvEmitter).rawListeners 703 | 704 | #### Defined in 705 | 706 | [src/types/TypedEventEmitter.ts:43](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L43) 707 | 708 | ___ 709 | 710 | ### readBlock 711 | 712 | ▸ **readBlock**(`«destructured»`, `callback`): `Promise`<`boolean`\> 713 | 714 | #### Parameters 715 | 716 | | Name | Type | 717 | | :------ | :------ | 718 | | `«destructured»` | `Object` | 719 | | › `hash` | `string` | 720 | | › `height` | `number` | 721 | | › `highWaterMark?` | `number` | 722 | | `callback` | (`params`: `BlockStream`) => `Promise`<`any`\> | 723 | 724 | #### Returns 725 | 726 | `Promise`<`boolean`\> 727 | 728 | #### Defined in 729 | 730 | [src/listener.ts:396](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L396) 731 | 732 | ___ 733 | 734 | ### reconnect 735 | 736 | ▸ **reconnect**(): `void` 737 | 738 | #### Returns 739 | 740 | `void` 741 | 742 | #### Defined in 743 | 744 | [src/listener.ts:111](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L111) 745 | 746 | ___ 747 | 748 | ### removeAllListeners 749 | 750 | ▸ **removeAllListeners**<`E`\>(`event?`): [`Listener`](Listener.md) 751 | 752 | #### Type parameters 753 | 754 | | Name | Type | 755 | | :------ | :------ | 756 | | `E` | extends keyof `SpvEvents` | 757 | 758 | #### Parameters 759 | 760 | | Name | Type | 761 | | :------ | :------ | 762 | | `event?` | `E` | 763 | 764 | #### Returns 765 | 766 | [`Listener`](Listener.md) 767 | 768 | #### Inherited from 769 | 770 | (EventEmitter as new () =\> SpvEmitter).removeAllListeners 771 | 772 | #### Defined in 773 | 774 | [src/types/TypedEventEmitter.ts:34](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L34) 775 | 776 | ___ 777 | 778 | ### removeListener 779 | 780 | ▸ **removeListener**<`E`\>(`event`, `listener`): [`Listener`](Listener.md) 781 | 782 | #### Type parameters 783 | 784 | | Name | Type | 785 | | :------ | :------ | 786 | | `E` | extends keyof `SpvEvents` | 787 | 788 | #### Parameters 789 | 790 | | Name | Type | 791 | | :------ | :------ | 792 | | `event` | `E` | 793 | | `listener` | `SpvEvents`[`E`] | 794 | 795 | #### Returns 796 | 797 | [`Listener`](Listener.md) 798 | 799 | #### Inherited from 800 | 801 | (EventEmitter as new () =\> SpvEmitter).removeListener 802 | 803 | #### Defined in 804 | 805 | [src/types/TypedEventEmitter.ts:35](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L35) 806 | 807 | ___ 808 | 809 | ### rewindHeight 810 | 811 | ▸ **rewindHeight**(`fromHeight`): `Promise`<`boolean`\> 812 | 813 | #### Parameters 814 | 815 | | Name | Type | 816 | | :------ | :------ | 817 | | `fromHeight` | `number` | 818 | 819 | #### Returns 820 | 821 | `Promise`<`boolean`\> 822 | 823 | #### Defined in 824 | 825 | [src/listener.ts:270](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L270) 826 | 827 | ___ 828 | 829 | ### setMaxListeners 830 | 831 | ▸ **setMaxListeners**(`maxListeners`): [`Listener`](Listener.md) 832 | 833 | #### Parameters 834 | 835 | | Name | Type | 836 | | :------ | :------ | 837 | | `maxListeners` | `number` | 838 | 839 | #### Returns 840 | 841 | [`Listener`](Listener.md) 842 | 843 | #### Inherited from 844 | 845 | (EventEmitter as new () =\> SpvEmitter).setMaxListeners 846 | 847 | #### Defined in 848 | 849 | [src/types/TypedEventEmitter.ts:48](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L48) 850 | 851 | ___ 852 | 853 | ### syncBlocks 854 | 855 | ▸ **syncBlocks**(`callback`, `options?`): `Promise`<`undefined` \| { `blockSize`: `number` ; `processed`: `number` ; `skipped`: `number` }\> 856 | 857 | #### Parameters 858 | 859 | | Name | Type | 860 | | :------ | :------ | 861 | | `callback` | (`params`: `BlockStream`) => `void` \| `Promise`<{ `errors?`: `number` ; `matches`: `number` }\> \| { `errors?`: `number` ; `matches`: `number` } | 862 | | `options?` | `Object` | 863 | | `options.highWaterMark?` | `number` | 864 | 865 | #### Returns 866 | 867 | `Promise`<`undefined` \| { `blockSize`: `number` ; `processed`: `number` ; `skipped`: `number` }\> 868 | 869 | #### Defined in 870 | 871 | [src/listener.ts:275](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L275) 872 | -------------------------------------------------------------------------------- /docs/classes/Master.md: -------------------------------------------------------------------------------- 1 | [bsv-spv](../README.md) / Master 2 | 3 | # Class: Master 4 | 5 | ## Table of contents 6 | 7 | ### Constructors 8 | 9 | - [constructor](Master.md#constructor) 10 | 11 | ### Properties 12 | 13 | - [block\_nodes](Master.md#block_nodes) 14 | - [blocks](Master.md#blocks) 15 | - [db\_nodes](Master.md#db_nodes) 16 | - [mempool](Master.md#mempool) 17 | - [mempool\_nodes](Master.md#mempool_nodes) 18 | - [mempool\_sockets](Master.md#mempool_sockets) 19 | - [queue\_block\_nodes](Master.md#queue_block_nodes) 20 | - [queue\_mempool\_nodes](Master.md#queue_mempool_nodes) 21 | - [seedNodesOnly](Master.md#seednodesonly) 22 | - [seed\_nodes](Master.md#seed_nodes) 23 | - [server](Master.md#server) 24 | - [sockets](Master.md#sockets) 25 | - [workers](Master.md#workers) 26 | 27 | ### Methods 28 | 29 | - [getNextNode](Master.md#getnextnode) 30 | - [getNodes](Master.md#getnodes) 31 | - [onMempoolTxMessage](Master.md#onmempooltxmessage) 32 | - [onMessage](Master.md#onmessage) 33 | - [startServer](Master.md#startserver) 34 | 35 | ## Constructors 36 | 37 | ### constructor 38 | 39 | • **new Master**(`«destructured»`) 40 | 41 | #### Parameters 42 | 43 | | Name | Type | 44 | | :------ | :------ | 45 | | `«destructured»` | [`MasterOptions`](../interfaces/MasterOptions.md) | 46 | 47 | #### Defined in 48 | 49 | [src/cluster_master.ts:53](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L53) 50 | 51 | ## Properties 52 | 53 | ### block\_nodes 54 | 55 | • **block\_nodes**: `Set`<`string`\> 56 | 57 | #### Defined in 58 | 59 | [src/cluster_master.ts:46](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L46) 60 | 61 | ___ 62 | 63 | ### blocks 64 | 65 | • **blocks**: `number` 66 | 67 | #### Defined in 68 | 69 | [src/cluster_master.ts:50](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L50) 70 | 71 | ___ 72 | 73 | ### db\_nodes 74 | 75 | • **db\_nodes**: [`DbNodes`](DbNodes.md) 76 | 77 | #### Defined in 78 | 79 | [src/cluster_master.ts:43](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L43) 80 | 81 | ___ 82 | 83 | ### mempool 84 | 85 | • **mempool**: `number` 86 | 87 | #### Defined in 88 | 89 | [src/cluster_master.ts:49](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L49) 90 | 91 | ___ 92 | 93 | ### mempool\_nodes 94 | 95 | • **mempool\_nodes**: `Set`<`string`\> 96 | 97 | #### Defined in 98 | 99 | [src/cluster_master.ts:47](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L47) 100 | 101 | ___ 102 | 103 | ### mempool\_sockets 104 | 105 | • **mempool\_sockets**: `Object` 106 | 107 | #### Index signature 108 | 109 | ▪ [key: `string`]: `Net.Socket` 110 | 111 | #### Defined in 112 | 113 | [src/cluster_master.ts:40](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L40) 114 | 115 | ___ 116 | 117 | ### queue\_block\_nodes 118 | 119 | • **queue\_block\_nodes**: `string`[] 120 | 121 | #### Defined in 122 | 123 | [src/cluster_master.ts:44](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L44) 124 | 125 | ___ 126 | 127 | ### queue\_mempool\_nodes 128 | 129 | • **queue\_mempool\_nodes**: `string`[] 130 | 131 | #### Defined in 132 | 133 | [src/cluster_master.ts:45](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L45) 134 | 135 | ___ 136 | 137 | ### seedNodesOnly 138 | 139 | • **seedNodesOnly**: `boolean` 140 | 141 | #### Defined in 142 | 143 | [src/cluster_master.ts:51](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L51) 144 | 145 | ___ 146 | 147 | ### seed\_nodes 148 | 149 | • **seed\_nodes**: `string`[] 150 | 151 | #### Defined in 152 | 153 | [src/cluster_master.ts:48](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L48) 154 | 155 | ___ 156 | 157 | ### server 158 | 159 | • `Optional` **server**: `Server` 160 | 161 | #### Defined in 162 | 163 | [src/cluster_master.ts:42](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L42) 164 | 165 | ___ 166 | 167 | ### sockets 168 | 169 | • **sockets**: `Object` 170 | 171 | #### Index signature 172 | 173 | ▪ [key: `string`]: `Net.Socket` 174 | 175 | #### Defined in 176 | 177 | [src/cluster_master.ts:39](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L39) 178 | 179 | ___ 180 | 181 | ### workers 182 | 183 | • **workers**: `Object` 184 | 185 | #### Index signature 186 | 187 | ▪ [key: `string`]: `Worker` 188 | 189 | #### Defined in 190 | 191 | [src/cluster_master.ts:41](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L41) 192 | 193 | ## Methods 194 | 195 | ### getNextNode 196 | 197 | ▸ **getNextNode**(`type`): `string` 198 | 199 | #### Parameters 200 | 201 | | Name | Type | 202 | | :------ | :------ | 203 | | `type` | ``"block"`` \| ``"mempool"`` | 204 | 205 | #### Returns 206 | 207 | `string` 208 | 209 | #### Defined in 210 | 211 | [src/cluster_master.ts:347](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L347) 212 | 213 | ___ 214 | 215 | ### getNodes 216 | 217 | ▸ **getNodes**(): `string`[] 218 | 219 | #### Returns 220 | 221 | `string`[] 222 | 223 | #### Defined in 224 | 225 | [src/cluster_master.ts:316](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L316) 226 | 227 | ___ 228 | 229 | ### onMempoolTxMessage 230 | 231 | ▸ **onMempoolTxMessage**(`data`): `void` 232 | 233 | #### Parameters 234 | 235 | | Name | Type | 236 | | :------ | :------ | 237 | | `data` | `any` | 238 | 239 | #### Returns 240 | 241 | `void` 242 | 243 | #### Defined in 244 | 245 | [src/cluster_master.ts:305](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L305) 246 | 247 | ___ 248 | 249 | ### onMessage 250 | 251 | ▸ **onMessage**(`data`): `void` 252 | 253 | #### Parameters 254 | 255 | | Name | Type | 256 | | :------ | :------ | 257 | | `data` | `any` | 258 | 259 | #### Returns 260 | 261 | `void` 262 | 263 | #### Defined in 264 | 265 | [src/cluster_master.ts:293](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L293) 266 | 267 | ___ 268 | 269 | ### startServer 270 | 271 | ▸ **startServer**(`«destructured»`): `void` 272 | 273 | #### Parameters 274 | 275 | | Name | Type | 276 | | :------ | :------ | 277 | | `«destructured»` | `Object` | 278 | | › `host?` | `string` | 279 | | › `port` | `number` | 280 | 281 | #### Returns 282 | 283 | `void` 284 | 285 | #### Defined in 286 | 287 | [src/cluster_master.ts:212](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L212) 288 | -------------------------------------------------------------------------------- /docs/classes/Server.md: -------------------------------------------------------------------------------- 1 | [bsv-spv](../README.md) / Server 2 | 3 | # Class: Server 4 | 5 | ## Hierarchy 6 | 7 | - [`Listener`](Listener.md) 8 | 9 | ↳ **`Server`** 10 | 11 | ## Table of contents 12 | 13 | ### Constructors 14 | 15 | - [constructor](Server.md#constructor) 16 | 17 | ### Properties 18 | 19 | - [MAX\_FILE\_SIZE](Server.md#max_file_size) 20 | - [SHOW\_LOGS](Server.md#show_logs) 21 | - [app](Server.md#app) 22 | - [blockHeight](Server.md#blockheight) 23 | - [client](Server.md#client) 24 | - [db\_blocks](Server.md#db_blocks) 25 | - [db\_headers](Server.md#db_headers) 26 | - [db\_listener](Server.md#db_listener) 27 | - [db\_mempool](Server.md#db_mempool) 28 | - [disableInterval](Server.md#disableinterval) 29 | - [headers](Server.md#headers) 30 | - [host](Server.md#host) 31 | - [interval](Server.md#interval) 32 | - [mempool\_txs](Server.md#mempool_txs) 33 | - [multithread](Server.md#multithread) 34 | - [name](Server.md#name) 35 | - [port](Server.md#port) 36 | - [reconnectTime](Server.md#reconnecttime) 37 | - [reconnectTimeout](Server.md#reconnecttimeout) 38 | - [server](Server.md#server) 39 | - [syncingBlocks](Server.md#syncingblocks) 40 | - [ticker](Server.md#ticker) 41 | - [txsSeen](Server.md#txsseen) 42 | - [txsSize](Server.md#txssize) 43 | 44 | ### Methods 45 | 46 | - [addListener](Server.md#addlistener) 47 | - [connect](Server.md#connect) 48 | - [disconnect](Server.md#disconnect) 49 | - [emit](Server.md#emit) 50 | - [eventNames](Server.md#eventnames) 51 | - [getBlockInfo](Server.md#getblockinfo) 52 | - [getMaxListeners](Server.md#getmaxlisteners) 53 | - [listen](Server.md#listen) 54 | - [listenerCount](Server.md#listenercount) 55 | - [listeners](Server.md#listeners) 56 | - [off](Server.md#off) 57 | - [on](Server.md#on) 58 | - [onMessage](Server.md#onmessage) 59 | - [once](Server.md#once) 60 | - [prependListener](Server.md#prependlistener) 61 | - [prependOnceListener](Server.md#prependoncelistener) 62 | - [rawListeners](Server.md#rawlisteners) 63 | - [readBlock](Server.md#readblock) 64 | - [reconnect](Server.md#reconnect) 65 | - [removeAllListeners](Server.md#removealllisteners) 66 | - [removeListener](Server.md#removelistener) 67 | - [rewindHeight](Server.md#rewindheight) 68 | - [setMaxListeners](Server.md#setmaxlisteners) 69 | - [syncBlocks](Server.md#syncblocks) 70 | 71 | ## Constructors 72 | 73 | ### constructor 74 | 75 | • **new Server**(`«destructured»`) 76 | 77 | #### Parameters 78 | 79 | | Name | Type | 80 | | :------ | :------ | 81 | | `«destructured»` | `Object` | 82 | | › `DEBUG_MEMORY?` | `boolean` | 83 | | › `MAX_FILE_SIZE?` | `number` | 84 | | › `dataDir` | `string` | 85 | | › `disableInterval?` | `boolean` | 86 | | › `genesisHeader?` | `string` | 87 | | › `mempool?` | `boolean` | 88 | | › `name` | `string` | 89 | | › `ticker` | `string` | 90 | 91 | #### Overrides 92 | 93 | [Listener](Listener.md).[constructor](Listener.md#constructor) 94 | 95 | #### Defined in 96 | 97 | [src/server.ts:16](https://github.com/kevinejohn/bsv-spv/blob/master/src/server.ts#L16) 98 | 99 | ## Properties 100 | 101 | ### MAX\_FILE\_SIZE 102 | 103 | • **MAX\_FILE\_SIZE**: `number` 104 | 105 | #### Defined in 106 | 107 | [src/server.ts:14](https://github.com/kevinejohn/bsv-spv/blob/master/src/server.ts#L14) 108 | 109 | ___ 110 | 111 | ### SHOW\_LOGS 112 | 113 | • **SHOW\_LOGS**: `boolean` 114 | 115 | #### Defined in 116 | 117 | [src/server.ts:12](https://github.com/kevinejohn/bsv-spv/blob/master/src/server.ts#L12) 118 | 119 | ___ 120 | 121 | ### app 122 | 123 | • **app**: `Express` 124 | 125 | #### Defined in 126 | 127 | [src/server.ts:10](https://github.com/kevinejohn/bsv-spv/blob/master/src/server.ts#L10) 128 | 129 | ___ 130 | 131 | ### blockHeight 132 | 133 | • **blockHeight**: `number` 134 | 135 | #### Inherited from 136 | 137 | [Listener](Listener.md).[blockHeight](Listener.md#blockheight) 138 | 139 | #### Defined in 140 | 141 | [src/listener.ts:30](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L30) 142 | 143 | ___ 144 | 145 | ### client 146 | 147 | • `Optional` **client**: `Socket` 148 | 149 | #### Inherited from 150 | 151 | [Listener](Listener.md).[client](Listener.md#client) 152 | 153 | #### Defined in 154 | 155 | [src/listener.ts:43](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L43) 156 | 157 | ___ 158 | 159 | ### db\_blocks 160 | 161 | • **db\_blocks**: [`DbBlocks`](DbBlocks.md) 162 | 163 | #### Inherited from 164 | 165 | [Listener](Listener.md).[db_blocks](Listener.md#db_blocks) 166 | 167 | #### Defined in 168 | 169 | [src/listener.ts:37](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L37) 170 | 171 | ___ 172 | 173 | ### db\_headers 174 | 175 | • **db\_headers**: [`DbHeaders`](DbHeaders.md) 176 | 177 | #### Inherited from 178 | 179 | [Listener](Listener.md).[db_headers](Listener.md#db_headers) 180 | 181 | #### Defined in 182 | 183 | [src/listener.ts:38](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L38) 184 | 185 | ___ 186 | 187 | ### db\_listener 188 | 189 | • **db\_listener**: [`DbListener`](DbListener.md) 190 | 191 | #### Inherited from 192 | 193 | [Listener](Listener.md).[db_listener](Listener.md#db_listener) 194 | 195 | #### Defined in 196 | 197 | [src/listener.ts:39](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L39) 198 | 199 | ___ 200 | 201 | ### db\_mempool 202 | 203 | • `Optional` **db\_mempool**: [`DbMempool`](DbMempool.md) 204 | 205 | #### Defined in 206 | 207 | [src/server.ts:13](https://github.com/kevinejohn/bsv-spv/blob/master/src/server.ts#L13) 208 | 209 | ___ 210 | 211 | ### disableInterval 212 | 213 | • **disableInterval**: `undefined` \| `boolean` 214 | 215 | #### Inherited from 216 | 217 | [Listener](Listener.md).[disableInterval](Listener.md#disableinterval) 218 | 219 | #### Defined in 220 | 221 | [src/listener.ts:31](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L31) 222 | 223 | ___ 224 | 225 | ### headers 226 | 227 | • **headers**: `default` 228 | 229 | #### Inherited from 230 | 231 | [Listener](Listener.md).[headers](Listener.md#headers) 232 | 233 | #### Defined in 234 | 235 | [src/listener.ts:40](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L40) 236 | 237 | ___ 238 | 239 | ### host 240 | 241 | • **host**: `string` 242 | 243 | #### Inherited from 244 | 245 | [Listener](Listener.md).[host](Listener.md#host) 246 | 247 | #### Defined in 248 | 249 | [src/listener.ts:33](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L33) 250 | 251 | ___ 252 | 253 | ### interval 254 | 255 | • `Optional` **interval**: `Timeout` 256 | 257 | #### Inherited from 258 | 259 | [Listener](Listener.md).[interval](Listener.md#interval) 260 | 261 | #### Defined in 262 | 263 | [src/listener.ts:42](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L42) 264 | 265 | ___ 266 | 267 | ### mempool\_txs 268 | 269 | • **mempool\_txs**: `boolean` 270 | 271 | #### Inherited from 272 | 273 | [Listener](Listener.md).[mempool_txs](Listener.md#mempool_txs) 274 | 275 | #### Defined in 276 | 277 | [src/listener.ts:35](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L35) 278 | 279 | ___ 280 | 281 | ### multithread 282 | 283 | • `Optional` **multithread**: `Object` 284 | 285 | #### Type declaration 286 | 287 | | Name | Type | 288 | | :------ | :------ | 289 | | `index` | `number` | 290 | | `threads` | `number` | 291 | 292 | #### Inherited from 293 | 294 | [Listener](Listener.md).[multithread](Listener.md#multithread) 295 | 296 | #### Defined in 297 | 298 | [src/listener.ts:32](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L32) 299 | 300 | ___ 301 | 302 | ### name 303 | 304 | • **name**: `string` 305 | 306 | #### Inherited from 307 | 308 | [Listener](Listener.md).[name](Listener.md#name) 309 | 310 | #### Defined in 311 | 312 | [src/listener.ts:29](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L29) 313 | 314 | ___ 315 | 316 | ### port 317 | 318 | • **port**: `number` 319 | 320 | #### Inherited from 321 | 322 | [Listener](Listener.md).[port](Listener.md#port) 323 | 324 | #### Defined in 325 | 326 | [src/listener.ts:34](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L34) 327 | 328 | ___ 329 | 330 | ### reconnectTime 331 | 332 | • **reconnectTime**: `number` 333 | 334 | #### Inherited from 335 | 336 | [Listener](Listener.md).[reconnectTime](Listener.md#reconnecttime) 337 | 338 | #### Defined in 339 | 340 | [src/listener.ts:36](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L36) 341 | 342 | ___ 343 | 344 | ### reconnectTimeout 345 | 346 | • `Optional` **reconnectTimeout**: `Timeout` 347 | 348 | #### Inherited from 349 | 350 | [Listener](Listener.md).[reconnectTimeout](Listener.md#reconnecttimeout) 351 | 352 | #### Defined in 353 | 354 | [src/listener.ts:41](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L41) 355 | 356 | ___ 357 | 358 | ### server 359 | 360 | • **server**: `Server` 361 | 362 | #### Defined in 363 | 364 | [src/server.ts:11](https://github.com/kevinejohn/bsv-spv/blob/master/src/server.ts#L11) 365 | 366 | ___ 367 | 368 | ### syncingBlocks 369 | 370 | • **syncingBlocks**: `boolean` 371 | 372 | #### Inherited from 373 | 374 | [Listener](Listener.md).[syncingBlocks](Listener.md#syncingblocks) 375 | 376 | #### Defined in 377 | 378 | [src/listener.ts:46](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L46) 379 | 380 | ___ 381 | 382 | ### ticker 383 | 384 | • **ticker**: `string` 385 | 386 | #### Inherited from 387 | 388 | [Listener](Listener.md).[ticker](Listener.md#ticker) 389 | 390 | #### Defined in 391 | 392 | [src/listener.ts:28](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L28) 393 | 394 | ___ 395 | 396 | ### txsSeen 397 | 398 | • **txsSeen**: `number` 399 | 400 | #### Inherited from 401 | 402 | [Listener](Listener.md).[txsSeen](Listener.md#txsseen) 403 | 404 | #### Defined in 405 | 406 | [src/listener.ts:44](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L44) 407 | 408 | ___ 409 | 410 | ### txsSize 411 | 412 | • **txsSize**: `number` 413 | 414 | #### Inherited from 415 | 416 | [Listener](Listener.md).[txsSize](Listener.md#txssize) 417 | 418 | #### Defined in 419 | 420 | [src/listener.ts:45](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L45) 421 | 422 | ## Methods 423 | 424 | ### addListener 425 | 426 | ▸ **addListener**<`E`\>(`event`, `listener`): [`Server`](Server.md) 427 | 428 | #### Type parameters 429 | 430 | | Name | Type | 431 | | :------ | :------ | 432 | | `E` | extends keyof `SpvEvents` | 433 | 434 | #### Parameters 435 | 436 | | Name | Type | 437 | | :------ | :------ | 438 | | `event` | `E` | 439 | | `listener` | `SpvEvents`[`E`] | 440 | 441 | #### Returns 442 | 443 | [`Server`](Server.md) 444 | 445 | #### Inherited from 446 | 447 | [Listener](Listener.md).[addListener](Listener.md#addlistener) 448 | 449 | #### Defined in 450 | 451 | [src/types/TypedEventEmitter.ts:24](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L24) 452 | 453 | ___ 454 | 455 | ### connect 456 | 457 | ▸ **connect**(`«destructured»`): `void` 458 | 459 | #### Parameters 460 | 461 | | Name | Type | 462 | | :------ | :------ | 463 | | `«destructured»` | `Object` | 464 | | › `host?` | `string` | 465 | | › `mempool_txs?` | `boolean` | 466 | | › `port` | `number` | 467 | 468 | #### Returns 469 | 470 | `void` 471 | 472 | #### Inherited from 473 | 474 | [Listener](Listener.md).[connect](Listener.md#connect) 475 | 476 | #### Defined in 477 | 478 | [src/listener.ts:189](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L189) 479 | 480 | ___ 481 | 482 | ### disconnect 483 | 484 | ▸ **disconnect**(): `void` 485 | 486 | #### Returns 487 | 488 | `void` 489 | 490 | #### Inherited from 491 | 492 | [Listener](Listener.md).[disconnect](Listener.md#disconnect) 493 | 494 | #### Defined in 495 | 496 | [src/listener.ts:118](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L118) 497 | 498 | ___ 499 | 500 | ### emit 501 | 502 | ▸ **emit**<`E`\>(`event`, `...args`): `boolean` 503 | 504 | #### Type parameters 505 | 506 | | Name | Type | 507 | | :------ | :------ | 508 | | `E` | extends keyof `SpvEvents` | 509 | 510 | #### Parameters 511 | 512 | | Name | Type | 513 | | :------ | :------ | 514 | | `event` | `E` | 515 | | `...args` | `Parameters`<`SpvEvents`[`E`]\> | 516 | 517 | #### Returns 518 | 519 | `boolean` 520 | 521 | #### Inherited from 522 | 523 | [Listener](Listener.md).[emit](Listener.md#emit) 524 | 525 | #### Defined in 526 | 527 | [src/types/TypedEventEmitter.ts:37](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L37) 528 | 529 | ___ 530 | 531 | ### eventNames 532 | 533 | ▸ **eventNames**(): (`string` \| `symbol`)[] 534 | 535 | #### Returns 536 | 537 | (`string` \| `symbol`)[] 538 | 539 | #### Inherited from 540 | 541 | [Listener](Listener.md).[eventNames](Listener.md#eventnames) 542 | 543 | #### Defined in 544 | 545 | [src/types/TypedEventEmitter.ts:42](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L42) 546 | 547 | ___ 548 | 549 | ### getBlockInfo 550 | 551 | ▸ **getBlockInfo**(`«destructured»`): `any` 552 | 553 | #### Parameters 554 | 555 | | Name | Type | 556 | | :------ | :------ | 557 | | `«destructured»` | `Object` | 558 | | › `hash` | `string` | 559 | | › `height` | `number` | 560 | 561 | #### Returns 562 | 563 | `any` 564 | 565 | #### Inherited from 566 | 567 | [Listener](Listener.md).[getBlockInfo](Listener.md#getblockinfo) 568 | 569 | #### Defined in 570 | 571 | [src/listener.ts:391](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L391) 572 | 573 | ___ 574 | 575 | ### getMaxListeners 576 | 577 | ▸ **getMaxListeners**(): `number` 578 | 579 | #### Returns 580 | 581 | `number` 582 | 583 | #### Inherited from 584 | 585 | [Listener](Listener.md).[getMaxListeners](Listener.md#getmaxlisteners) 586 | 587 | #### Defined in 588 | 589 | [src/types/TypedEventEmitter.ts:47](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L47) 590 | 591 | ___ 592 | 593 | ### listen 594 | 595 | ▸ **listen**(`«destructured»`): `void` 596 | 597 | #### Parameters 598 | 599 | | Name | Type | 600 | | :------ | :------ | 601 | | `«destructured»` | `Object` | 602 | 603 | #### Returns 604 | 605 | `void` 606 | 607 | #### Defined in 608 | 609 | [src/server.ts:63](https://github.com/kevinejohn/bsv-spv/blob/master/src/server.ts#L63) 610 | 611 | ___ 612 | 613 | ### listenerCount 614 | 615 | ▸ **listenerCount**<`E`\>(`event`): `number` 616 | 617 | #### Type parameters 618 | 619 | | Name | Type | 620 | | :------ | :------ | 621 | | `E` | extends keyof `SpvEvents` | 622 | 623 | #### Parameters 624 | 625 | | Name | Type | 626 | | :------ | :------ | 627 | | `event` | `E` | 628 | 629 | #### Returns 630 | 631 | `number` 632 | 633 | #### Inherited from 634 | 635 | [Listener](Listener.md).[listenerCount](Listener.md#listenercount) 636 | 637 | #### Defined in 638 | 639 | [src/types/TypedEventEmitter.ts:45](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L45) 640 | 641 | ___ 642 | 643 | ### listeners 644 | 645 | ▸ **listeners**<`E`\>(`event`): `SpvEvents`[`E`][] 646 | 647 | #### Type parameters 648 | 649 | | Name | Type | 650 | | :------ | :------ | 651 | | `E` | extends keyof `SpvEvents` | 652 | 653 | #### Parameters 654 | 655 | | Name | Type | 656 | | :------ | :------ | 657 | | `event` | `E` | 658 | 659 | #### Returns 660 | 661 | `SpvEvents`[`E`][] 662 | 663 | #### Inherited from 664 | 665 | [Listener](Listener.md).[listeners](Listener.md#listeners) 666 | 667 | #### Defined in 668 | 669 | [src/types/TypedEventEmitter.ts:44](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L44) 670 | 671 | ___ 672 | 673 | ### off 674 | 675 | ▸ **off**<`E`\>(`event`, `listener`): [`Server`](Server.md) 676 | 677 | #### Type parameters 678 | 679 | | Name | Type | 680 | | :------ | :------ | 681 | | `E` | extends keyof `SpvEvents` | 682 | 683 | #### Parameters 684 | 685 | | Name | Type | 686 | | :------ | :------ | 687 | | `event` | `E` | 688 | | `listener` | `SpvEvents`[`E`] | 689 | 690 | #### Returns 691 | 692 | [`Server`](Server.md) 693 | 694 | #### Inherited from 695 | 696 | [Listener](Listener.md).[off](Listener.md#off) 697 | 698 | #### Defined in 699 | 700 | [src/types/TypedEventEmitter.ts:33](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L33) 701 | 702 | ___ 703 | 704 | ### on 705 | 706 | ▸ **on**<`E`\>(`event`, `listener`): [`Server`](Server.md) 707 | 708 | #### Type parameters 709 | 710 | | Name | Type | 711 | | :------ | :------ | 712 | | `E` | extends keyof `SpvEvents` | 713 | 714 | #### Parameters 715 | 716 | | Name | Type | 717 | | :------ | :------ | 718 | | `event` | `E` | 719 | | `listener` | `SpvEvents`[`E`] | 720 | 721 | #### Returns 722 | 723 | [`Server`](Server.md) 724 | 725 | #### Inherited from 726 | 727 | [Listener](Listener.md).[on](Listener.md#on) 728 | 729 | #### Defined in 730 | 731 | [src/types/TypedEventEmitter.ts:25](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L25) 732 | 733 | ___ 734 | 735 | ### onMessage 736 | 737 | ▸ **onMessage**(`obj`): `void` 738 | 739 | #### Parameters 740 | 741 | | Name | Type | 742 | | :------ | :------ | 743 | | `obj` | `Object` | 744 | | `obj.command` | `string` | 745 | | `obj.data` | `any` | 746 | 747 | #### Returns 748 | 749 | `void` 750 | 751 | #### Inherited from 752 | 753 | [Listener](Listener.md).[onMessage](Listener.md#onmessage) 754 | 755 | #### Defined in 756 | 757 | [src/listener.ts:139](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L139) 758 | 759 | ___ 760 | 761 | ### once 762 | 763 | ▸ **once**<`E`\>(`event`, `listener`): [`Server`](Server.md) 764 | 765 | #### Type parameters 766 | 767 | | Name | Type | 768 | | :------ | :------ | 769 | | `E` | extends keyof `SpvEvents` | 770 | 771 | #### Parameters 772 | 773 | | Name | Type | 774 | | :------ | :------ | 775 | | `event` | `E` | 776 | | `listener` | `SpvEvents`[`E`] | 777 | 778 | #### Returns 779 | 780 | [`Server`](Server.md) 781 | 782 | #### Inherited from 783 | 784 | [Listener](Listener.md).[once](Listener.md#once) 785 | 786 | #### Defined in 787 | 788 | [src/types/TypedEventEmitter.ts:26](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L26) 789 | 790 | ___ 791 | 792 | ### prependListener 793 | 794 | ▸ **prependListener**<`E`\>(`event`, `listener`): [`Server`](Server.md) 795 | 796 | #### Type parameters 797 | 798 | | Name | Type | 799 | | :------ | :------ | 800 | | `E` | extends keyof `SpvEvents` | 801 | 802 | #### Parameters 803 | 804 | | Name | Type | 805 | | :------ | :------ | 806 | | `event` | `E` | 807 | | `listener` | `SpvEvents`[`E`] | 808 | 809 | #### Returns 810 | 811 | [`Server`](Server.md) 812 | 813 | #### Inherited from 814 | 815 | [Listener](Listener.md).[prependListener](Listener.md#prependlistener) 816 | 817 | #### Defined in 818 | 819 | [src/types/TypedEventEmitter.ts:27](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L27) 820 | 821 | ___ 822 | 823 | ### prependOnceListener 824 | 825 | ▸ **prependOnceListener**<`E`\>(`event`, `listener`): [`Server`](Server.md) 826 | 827 | #### Type parameters 828 | 829 | | Name | Type | 830 | | :------ | :------ | 831 | | `E` | extends keyof `SpvEvents` | 832 | 833 | #### Parameters 834 | 835 | | Name | Type | 836 | | :------ | :------ | 837 | | `event` | `E` | 838 | | `listener` | `SpvEvents`[`E`] | 839 | 840 | #### Returns 841 | 842 | [`Server`](Server.md) 843 | 844 | #### Inherited from 845 | 846 | [Listener](Listener.md).[prependOnceListener](Listener.md#prependoncelistener) 847 | 848 | #### Defined in 849 | 850 | [src/types/TypedEventEmitter.ts:28](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L28) 851 | 852 | ___ 853 | 854 | ### rawListeners 855 | 856 | ▸ **rawListeners**<`E`\>(`event`): `SpvEvents`[`E`][] 857 | 858 | #### Type parameters 859 | 860 | | Name | Type | 861 | | :------ | :------ | 862 | | `E` | extends keyof `SpvEvents` | 863 | 864 | #### Parameters 865 | 866 | | Name | Type | 867 | | :------ | :------ | 868 | | `event` | `E` | 869 | 870 | #### Returns 871 | 872 | `SpvEvents`[`E`][] 873 | 874 | #### Inherited from 875 | 876 | [Listener](Listener.md).[rawListeners](Listener.md#rawlisteners) 877 | 878 | #### Defined in 879 | 880 | [src/types/TypedEventEmitter.ts:43](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L43) 881 | 882 | ___ 883 | 884 | ### readBlock 885 | 886 | ▸ **readBlock**(`«destructured»`, `callback`): `Promise`<`boolean`\> 887 | 888 | #### Parameters 889 | 890 | | Name | Type | 891 | | :------ | :------ | 892 | | `«destructured»` | `Object` | 893 | | › `hash` | `string` | 894 | | › `height` | `number` | 895 | | › `highWaterMark?` | `number` | 896 | | `callback` | (`params`: `BlockStream`) => `Promise`<`any`\> | 897 | 898 | #### Returns 899 | 900 | `Promise`<`boolean`\> 901 | 902 | #### Inherited from 903 | 904 | [Listener](Listener.md).[readBlock](Listener.md#readblock) 905 | 906 | #### Defined in 907 | 908 | [src/listener.ts:396](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L396) 909 | 910 | ___ 911 | 912 | ### reconnect 913 | 914 | ▸ **reconnect**(): `void` 915 | 916 | #### Returns 917 | 918 | `void` 919 | 920 | #### Inherited from 921 | 922 | [Listener](Listener.md).[reconnect](Listener.md#reconnect) 923 | 924 | #### Defined in 925 | 926 | [src/listener.ts:111](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L111) 927 | 928 | ___ 929 | 930 | ### removeAllListeners 931 | 932 | ▸ **removeAllListeners**<`E`\>(`event?`): [`Server`](Server.md) 933 | 934 | #### Type parameters 935 | 936 | | Name | Type | 937 | | :------ | :------ | 938 | | `E` | extends keyof `SpvEvents` | 939 | 940 | #### Parameters 941 | 942 | | Name | Type | 943 | | :------ | :------ | 944 | | `event?` | `E` | 945 | 946 | #### Returns 947 | 948 | [`Server`](Server.md) 949 | 950 | #### Inherited from 951 | 952 | [Listener](Listener.md).[removeAllListeners](Listener.md#removealllisteners) 953 | 954 | #### Defined in 955 | 956 | [src/types/TypedEventEmitter.ts:34](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L34) 957 | 958 | ___ 959 | 960 | ### removeListener 961 | 962 | ▸ **removeListener**<`E`\>(`event`, `listener`): [`Server`](Server.md) 963 | 964 | #### Type parameters 965 | 966 | | Name | Type | 967 | | :------ | :------ | 968 | | `E` | extends keyof `SpvEvents` | 969 | 970 | #### Parameters 971 | 972 | | Name | Type | 973 | | :------ | :------ | 974 | | `event` | `E` | 975 | | `listener` | `SpvEvents`[`E`] | 976 | 977 | #### Returns 978 | 979 | [`Server`](Server.md) 980 | 981 | #### Inherited from 982 | 983 | [Listener](Listener.md).[removeListener](Listener.md#removelistener) 984 | 985 | #### Defined in 986 | 987 | [src/types/TypedEventEmitter.ts:35](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L35) 988 | 989 | ___ 990 | 991 | ### rewindHeight 992 | 993 | ▸ **rewindHeight**(`fromHeight`): `Promise`<`boolean`\> 994 | 995 | #### Parameters 996 | 997 | | Name | Type | 998 | | :------ | :------ | 999 | | `fromHeight` | `number` | 1000 | 1001 | #### Returns 1002 | 1003 | `Promise`<`boolean`\> 1004 | 1005 | #### Inherited from 1006 | 1007 | [Listener](Listener.md).[rewindHeight](Listener.md#rewindheight) 1008 | 1009 | #### Defined in 1010 | 1011 | [src/listener.ts:270](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L270) 1012 | 1013 | ___ 1014 | 1015 | ### setMaxListeners 1016 | 1017 | ▸ **setMaxListeners**(`maxListeners`): [`Server`](Server.md) 1018 | 1019 | #### Parameters 1020 | 1021 | | Name | Type | 1022 | | :------ | :------ | 1023 | | `maxListeners` | `number` | 1024 | 1025 | #### Returns 1026 | 1027 | [`Server`](Server.md) 1028 | 1029 | #### Inherited from 1030 | 1031 | [Listener](Listener.md).[setMaxListeners](Listener.md#setmaxlisteners) 1032 | 1033 | #### Defined in 1034 | 1035 | [src/types/TypedEventEmitter.ts:48](https://github.com/kevinejohn/bsv-spv/blob/master/src/types/TypedEventEmitter.ts#L48) 1036 | 1037 | ___ 1038 | 1039 | ### syncBlocks 1040 | 1041 | ▸ **syncBlocks**(`callback`, `options?`): `Promise`<`undefined` \| { `blockSize`: `number` ; `processed`: `number` ; `skipped`: `number` }\> 1042 | 1043 | #### Parameters 1044 | 1045 | | Name | Type | 1046 | | :------ | :------ | 1047 | | `callback` | (`params`: `BlockStream`) => `void` \| `Promise`<{ `errors?`: `number` ; `matches`: `number` }\> \| { `errors?`: `number` ; `matches`: `number` } | 1048 | | `options?` | `Object` | 1049 | | `options.highWaterMark?` | `number` | 1050 | 1051 | #### Returns 1052 | 1053 | `Promise`<`undefined` \| { `blockSize`: `number` ; `processed`: `number` ; `skipped`: `number` }\> 1054 | 1055 | #### Inherited from 1056 | 1057 | [Listener](Listener.md).[syncBlocks](Listener.md#syncblocks) 1058 | 1059 | #### Defined in 1060 | 1061 | [src/listener.ts:275](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L275) 1062 | -------------------------------------------------------------------------------- /docs/classes/Worker.md: -------------------------------------------------------------------------------- 1 | [bsv-spv](../README.md) / Worker 2 | 3 | # Class: Worker 4 | 5 | ## Table of contents 6 | 7 | ### Constructors 8 | 9 | - [constructor](Worker.md#constructor) 10 | 11 | ### Properties 12 | 13 | - [spv](Worker.md#spv) 14 | 15 | ### Methods 16 | 17 | - [sendToMaster](Worker.md#sendtomaster) 18 | - [start](Worker.md#start) 19 | 20 | ## Constructors 21 | 22 | ### constructor 23 | 24 | • **new Worker**() 25 | 26 | #### Defined in 27 | 28 | [src/cluster_worker.ts:15](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_worker.ts#L15) 29 | 30 | ## Properties 31 | 32 | ### spv 33 | 34 | • `Optional` **spv**: [`Spv`](Spv.md) 35 | 36 | #### Defined in 37 | 38 | [src/cluster_worker.ts:13](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_worker.ts#L13) 39 | 40 | ## Methods 41 | 42 | ### sendToMaster 43 | 44 | ▸ **sendToMaster**(`obj`): `void` 45 | 46 | #### Parameters 47 | 48 | | Name | Type | 49 | | :------ | :------ | 50 | | `obj` | `any` | 51 | 52 | #### Returns 53 | 54 | `void` 55 | 56 | #### Defined in 57 | 58 | [src/cluster_worker.ts:36](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_worker.ts#L36) 59 | 60 | ___ 61 | 62 | ### start 63 | 64 | ▸ **start**(`config`): `Promise`<`void`\> 65 | 66 | #### Parameters 67 | 68 | | Name | Type | 69 | | :------ | :------ | 70 | | `config` | [`SpvOptions`](../interfaces/SpvOptions.md) | 71 | 72 | #### Returns 73 | 74 | `Promise`<`void`\> 75 | 76 | #### Defined in 77 | 78 | [src/cluster_worker.ts:40](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_worker.ts#L40) 79 | -------------------------------------------------------------------------------- /docs/interfaces/ListenerOptions.md: -------------------------------------------------------------------------------- 1 | [bsv-spv](../README.md) / ListenerOptions 2 | 3 | # Interface: ListenerOptions 4 | 5 | ## Table of contents 6 | 7 | ### Properties 8 | 9 | - [DEBUG\_MEMORY](ListenerOptions.md#debug_memory) 10 | - [blockHeight](ListenerOptions.md#blockheight) 11 | - [dataDir](ListenerOptions.md#datadir) 12 | - [disableInterval](ListenerOptions.md#disableinterval) 13 | - [genesisHeader](ListenerOptions.md#genesisheader) 14 | - [multithread](ListenerOptions.md#multithread) 15 | - [name](ListenerOptions.md#name) 16 | - [ticker](ListenerOptions.md#ticker) 17 | 18 | ## Properties 19 | 20 | ### DEBUG\_MEMORY 21 | 22 | • `Optional` **DEBUG\_MEMORY**: `boolean` 23 | 24 | #### Defined in 25 | 26 | [src/listener.ts:19](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L19) 27 | 28 | ___ 29 | 30 | ### blockHeight 31 | 32 | • **blockHeight**: `number` 33 | 34 | #### Defined in 35 | 36 | [src/listener.ts:14](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L14) 37 | 38 | ___ 39 | 40 | ### dataDir 41 | 42 | • **dataDir**: `string` 43 | 44 | #### Defined in 45 | 46 | [src/listener.ts:15](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L15) 47 | 48 | ___ 49 | 50 | ### disableInterval 51 | 52 | • `Optional` **disableInterval**: `boolean` 53 | 54 | #### Defined in 55 | 56 | [src/listener.ts:18](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L18) 57 | 58 | ___ 59 | 60 | ### genesisHeader 61 | 62 | • `Optional` **genesisHeader**: `string` 63 | 64 | #### Defined in 65 | 66 | [src/listener.ts:17](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L17) 67 | 68 | ___ 69 | 70 | ### multithread 71 | 72 | • `Optional` **multithread**: `Object` 73 | 74 | #### Type declaration 75 | 76 | | Name | Type | 77 | | :------ | :------ | 78 | | `index` | `number` | 79 | | `threads` | `number` | 80 | 81 | #### Defined in 82 | 83 | [src/listener.ts:20](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L20) 84 | 85 | ___ 86 | 87 | ### name 88 | 89 | • **name**: `string` 90 | 91 | #### Defined in 92 | 93 | [src/listener.ts:13](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L13) 94 | 95 | ___ 96 | 97 | ### ticker 98 | 99 | • **ticker**: `string` 100 | 101 | #### Defined in 102 | 103 | [src/listener.ts:16](https://github.com/kevinejohn/bsv-spv/blob/master/src/listener.ts#L16) 104 | -------------------------------------------------------------------------------- /docs/interfaces/MasterOptions.md: -------------------------------------------------------------------------------- 1 | [bsv-spv](../README.md) / MasterOptions 2 | 3 | # Interface: MasterOptions 4 | 5 | ## Table of contents 6 | 7 | ### Properties 8 | 9 | - [DEBUG\_LOG](MasterOptions.md#debug_log) 10 | - [DEBUG\_MEMORY](MasterOptions.md#debug_memory) 11 | - [blockHeight](MasterOptions.md#blockheight) 12 | - [blocks](MasterOptions.md#blocks) 13 | - [dataDir](MasterOptions.md#datadir) 14 | - [enableIpv6](MasterOptions.md#enableipv6) 15 | - [forceUserAgent](MasterOptions.md#forceuseragent) 16 | - [genesisHeader](MasterOptions.md#genesisheader) 17 | - [invalidBlocks](MasterOptions.md#invalidblocks) 18 | - [magic](MasterOptions.md#magic) 19 | - [mempool](MasterOptions.md#mempool) 20 | - [nodes](MasterOptions.md#nodes) 21 | - [pruneBlocks](MasterOptions.md#pruneblocks) 22 | - [seedNodesOnly](MasterOptions.md#seednodesonly) 23 | - [ticker](MasterOptions.md#ticker) 24 | - [user\_agent](MasterOptions.md#user_agent) 25 | - [validate](MasterOptions.md#validate) 26 | - [version](MasterOptions.md#version) 27 | 28 | ## Properties 29 | 30 | ### DEBUG\_LOG 31 | 32 | • `Optional` **DEBUG\_LOG**: `boolean` 33 | 34 | #### Defined in 35 | 36 | [src/cluster_master.ts:34](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L34) 37 | 38 | ___ 39 | 40 | ### DEBUG\_MEMORY 41 | 42 | • `Optional` **DEBUG\_MEMORY**: `boolean` 43 | 44 | #### Defined in 45 | 46 | [src/cluster_master.ts:35](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L35) 47 | 48 | ___ 49 | 50 | ### blockHeight 51 | 52 | • **blockHeight**: `number` 53 | 54 | #### Defined in 55 | 56 | [src/cluster_master.ts:33](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L33) 57 | 58 | ___ 59 | 60 | ### blocks 61 | 62 | • **blocks**: `number` 63 | 64 | #### Defined in 65 | 66 | [src/cluster_master.ts:23](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L23) 67 | 68 | ___ 69 | 70 | ### dataDir 71 | 72 | • **dataDir**: `string` 73 | 74 | #### Defined in 75 | 76 | [src/cluster_master.ts:31](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L31) 77 | 78 | ___ 79 | 80 | ### enableIpv6 81 | 82 | • `Optional` **enableIpv6**: `boolean` 83 | 84 | #### Defined in 85 | 86 | [src/cluster_master.ts:21](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L21) 87 | 88 | ___ 89 | 90 | ### forceUserAgent 91 | 92 | • `Optional` **forceUserAgent**: `string` 93 | 94 | #### Defined in 95 | 96 | [src/cluster_master.ts:25](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L25) 97 | 98 | ___ 99 | 100 | ### genesisHeader 101 | 102 | • `Optional` **genesisHeader**: `string` 103 | 104 | #### Defined in 105 | 106 | [src/cluster_master.ts:28](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L28) 107 | 108 | ___ 109 | 110 | ### invalidBlocks 111 | 112 | • `Optional` **invalidBlocks**: `string`[] 113 | 114 | #### Defined in 115 | 116 | [src/cluster_master.ts:30](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L30) 117 | 118 | ___ 119 | 120 | ### magic 121 | 122 | • `Optional` **magic**: `string` 123 | 124 | #### Defined in 125 | 126 | [src/cluster_master.ts:27](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L27) 127 | 128 | ___ 129 | 130 | ### mempool 131 | 132 | • **mempool**: `number` 133 | 134 | #### Defined in 135 | 136 | [src/cluster_master.ts:22](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L22) 137 | 138 | ___ 139 | 140 | ### nodes 141 | 142 | • **nodes**: `string`[] 143 | 144 | #### Defined in 145 | 146 | [src/cluster_master.ts:19](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L19) 147 | 148 | ___ 149 | 150 | ### pruneBlocks 151 | 152 | • **pruneBlocks**: `number` 153 | 154 | #### Defined in 155 | 156 | [src/cluster_master.ts:32](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L32) 157 | 158 | ___ 159 | 160 | ### seedNodesOnly 161 | 162 | • `Optional` **seedNodesOnly**: `boolean` 163 | 164 | #### Defined in 165 | 166 | [src/cluster_master.ts:20](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L20) 167 | 168 | ___ 169 | 170 | ### ticker 171 | 172 | • **ticker**: `string` 173 | 174 | #### Defined in 175 | 176 | [src/cluster_master.ts:18](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L18) 177 | 178 | ___ 179 | 180 | ### user\_agent 181 | 182 | • `Optional` **user\_agent**: `string` 183 | 184 | #### Defined in 185 | 186 | [src/cluster_master.ts:26](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L26) 187 | 188 | ___ 189 | 190 | ### validate 191 | 192 | • `Optional` **validate**: `boolean` 193 | 194 | #### Defined in 195 | 196 | [src/cluster_master.ts:24](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L24) 197 | 198 | ___ 199 | 200 | ### version 201 | 202 | • `Optional` **version**: `number` 203 | 204 | #### Defined in 205 | 206 | [src/cluster_master.ts:29](https://github.com/kevinejohn/bsv-spv/blob/master/src/cluster_master.ts#L29) 207 | -------------------------------------------------------------------------------- /docs/interfaces/SpvOptions.md: -------------------------------------------------------------------------------- 1 | [bsv-spv](../README.md) / SpvOptions 2 | 3 | # Interface: SpvOptions 4 | 5 | ## Table of contents 6 | 7 | ### Properties 8 | 9 | - [DEBUG\_LOG](SpvOptions.md#debug_log) 10 | - [DEBUG\_MEMORY](SpvOptions.md#debug_memory) 11 | - [autoReconnect](SpvOptions.md#autoreconnect) 12 | - [autoReconnectWait](SpvOptions.md#autoreconnectwait) 13 | - [blockHeight](SpvOptions.md#blockheight) 14 | - [blocks](SpvOptions.md#blocks) 15 | - [dataDir](SpvOptions.md#datadir) 16 | - [forceUserAgent](SpvOptions.md#forceuseragent) 17 | - [genesisHeader](SpvOptions.md#genesisheader) 18 | - [invalidBlocks](SpvOptions.md#invalidblocks) 19 | - [magic](SpvOptions.md#magic) 20 | - [mempool](SpvOptions.md#mempool) 21 | - [node](SpvOptions.md#node) 22 | - [pruneBlocks](SpvOptions.md#pruneblocks) 23 | - [ticker](SpvOptions.md#ticker) 24 | - [timeoutConnect](SpvOptions.md#timeoutconnect) 25 | - [uid](SpvOptions.md#uid) 26 | - [user\_agent](SpvOptions.md#user_agent) 27 | - [validate](SpvOptions.md#validate) 28 | - [version](SpvOptions.md#version) 29 | - [versionOptions](SpvOptions.md#versionoptions) 30 | 31 | ## Properties 32 | 33 | ### DEBUG\_LOG 34 | 35 | • `Optional` **DEBUG\_LOG**: `boolean` 36 | 37 | #### Defined in 38 | 39 | [src/spv.ts:34](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L34) 40 | 41 | ___ 42 | 43 | ### DEBUG\_MEMORY 44 | 45 | • `Optional` **DEBUG\_MEMORY**: `boolean` 46 | 47 | #### Defined in 48 | 49 | [src/spv.ts:35](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L35) 50 | 51 | ___ 52 | 53 | ### autoReconnect 54 | 55 | • `Optional` **autoReconnect**: `boolean` 56 | 57 | #### Defined in 58 | 59 | [src/spv.ts:27](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L27) 60 | 61 | ___ 62 | 63 | ### autoReconnectWait 64 | 65 | • `Optional` **autoReconnectWait**: `number` 66 | 67 | #### Defined in 68 | 69 | [src/spv.ts:28](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L28) 70 | 71 | ___ 72 | 73 | ### blockHeight 74 | 75 | • `Optional` **blockHeight**: `number` 76 | 77 | #### Defined in 78 | 79 | [src/spv.ts:33](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L33) 80 | 81 | ___ 82 | 83 | ### blocks 84 | 85 | • `Optional` **blocks**: `boolean` 86 | 87 | #### Defined in 88 | 89 | [src/spv.ts:24](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L24) 90 | 91 | ___ 92 | 93 | ### dataDir 94 | 95 | • **dataDir**: `string` 96 | 97 | #### Defined in 98 | 99 | [src/spv.ts:18](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L18) 100 | 101 | ___ 102 | 103 | ### forceUserAgent 104 | 105 | • `Optional` **forceUserAgent**: `string` 106 | 107 | #### Defined in 108 | 109 | [src/spv.ts:19](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L19) 110 | 111 | ___ 112 | 113 | ### genesisHeader 114 | 115 | • `Optional` **genesisHeader**: `string` 116 | 117 | #### Defined in 118 | 119 | [src/spv.ts:22](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L22) 120 | 121 | ___ 122 | 123 | ### invalidBlocks 124 | 125 | • `Optional` **invalidBlocks**: `string`[] 126 | 127 | #### Defined in 128 | 129 | [src/spv.ts:31](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L31) 130 | 131 | ___ 132 | 133 | ### magic 134 | 135 | • `Optional` **magic**: `string` 136 | 137 | #### Defined in 138 | 139 | [src/spv.ts:21](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L21) 140 | 141 | ___ 142 | 143 | ### mempool 144 | 145 | • `Optional` **mempool**: `boolean` 146 | 147 | #### Defined in 148 | 149 | [src/spv.ts:25](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L25) 150 | 151 | ___ 152 | 153 | ### node 154 | 155 | • **node**: `string` 156 | 157 | #### Defined in 158 | 159 | [src/spv.ts:17](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L17) 160 | 161 | ___ 162 | 163 | ### pruneBlocks 164 | 165 | • `Optional` **pruneBlocks**: `number` 166 | 167 | #### Defined in 168 | 169 | [src/spv.ts:32](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L32) 170 | 171 | ___ 172 | 173 | ### ticker 174 | 175 | • **ticker**: `string` 176 | 177 | #### Defined in 178 | 179 | [src/spv.ts:16](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L16) 180 | 181 | ___ 182 | 183 | ### timeoutConnect 184 | 185 | • `Optional` **timeoutConnect**: `number` 186 | 187 | #### Defined in 188 | 189 | [src/spv.ts:29](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L29) 190 | 191 | ___ 192 | 193 | ### uid 194 | 195 | • `Optional` **uid**: `string` 196 | 197 | #### Defined in 198 | 199 | [src/spv.ts:15](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L15) 200 | 201 | ___ 202 | 203 | ### user\_agent 204 | 205 | • `Optional` **user\_agent**: `string` 206 | 207 | #### Defined in 208 | 209 | [src/spv.ts:20](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L20) 210 | 211 | ___ 212 | 213 | ### validate 214 | 215 | • `Optional` **validate**: `boolean` 216 | 217 | #### Defined in 218 | 219 | [src/spv.ts:26](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L26) 220 | 221 | ___ 222 | 223 | ### version 224 | 225 | • `Optional` **version**: `number` 226 | 227 | #### Defined in 228 | 229 | [src/spv.ts:23](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L23) 230 | 231 | ___ 232 | 233 | ### versionOptions 234 | 235 | • `Optional` **versionOptions**: `VersionOptions` 236 | 237 | #### Defined in 238 | 239 | [src/spv.ts:30](https://github.com/kevinejohn/bsv-spv/blob/master/src/spv.ts#L30) 240 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bsv-spv", 3 | "version": "1.0.8", 4 | "description": "Stay in sync with latest bitcoin headers, blocks and mempool txs", 5 | "main": "./lib/index.js", 6 | "types": "./lib/index.d.ts", 7 | "files": [ 8 | "lib", 9 | "src" 10 | ], 11 | "scripts": { 12 | "test": "npm run test-cluster", 13 | "test-cluster": "ts-node ./tests/cluster.ts", 14 | "test-lister": "ts-node ./tests/lister.ts", 15 | "build": "tsc", 16 | "docs": "typedoc", 17 | "prepare": "npm run build" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/kevinejohn/bsv-spv.git" 22 | }, 23 | "author": "Kevin Johnson", 24 | "license": "MIT", 25 | "keywords": [ 26 | "bitcoin", 27 | "spv", 28 | "headers" 29 | ], 30 | "dependencies": { 31 | "bsv-headers": "^1.0.2", 32 | "bsv-minimal": "^2.0.0", 33 | "bsv-p2p": "^2.0.1", 34 | "cors": "^2.8.5", 35 | "express": "^4.18.2", 36 | "levelup": "^5.1.1", 37 | "lmdb": "^2.7.11", 38 | "rocksdb": "^5.2.1", 39 | "source-map-support": "^0.5.21" 40 | }, 41 | "devDependencies": { 42 | "@types/cors": "^2.8.17", 43 | "@types/express": "^4.17.20", 44 | "@types/levelup": "^5.1.4", 45 | "@types/node": "^20.8.9", 46 | "@types/rocksdb": "^3.0.3", 47 | "ts-node": "^10.9.1", 48 | "typedoc": "^0.25.2", 49 | "typedoc-plugin-markdown": "^3.16.0", 50 | "typescript": "^5.2.2" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/cluster_master.ts: -------------------------------------------------------------------------------- 1 | import cluster, { Worker } from "cluster"; 2 | import { SpvOptions } from "./spv"; 3 | import DbNodes from "./db_nodes"; 4 | import DbBlocks from "./db_blocks"; 5 | import * as Helpers from "./helpers"; 6 | import Net from "net"; 7 | import path from "path"; 8 | 9 | process.on("unhandledRejection", (reason, p) => { 10 | console.error(reason, "Master Unhandled Rejection at Promise", p); 11 | }); 12 | process.on("uncaughtException", (err) => { 13 | console.error(err, "Master Uncaught Exception thrown"); 14 | process.exit(1); 15 | }); 16 | 17 | export interface MasterOptions { 18 | ticker: string; 19 | nodes: string[]; 20 | seedNodesOnly?: boolean; 21 | enableIpv6?: boolean; 22 | mempool: number; 23 | blocks: number; 24 | validate?: boolean; 25 | forceUserAgent?: string; 26 | user_agent?: string; 27 | magic?: string; 28 | genesisHeader?: string; 29 | version?: number; 30 | invalidBlocks?: string[]; 31 | dataDir: string; 32 | pruneBlocks: number; 33 | blockHeight: number; 34 | DEBUG_LOG?: boolean; 35 | DEBUG_MEMORY?: boolean; 36 | } 37 | 38 | export default class Master { 39 | sockets: { [key: string]: Net.Socket }; 40 | mempool_sockets: { [key: string]: Net.Socket }; 41 | workers: { [key: string]: Worker }; 42 | server?: Net.Server; 43 | db_nodes: DbNodes; 44 | queue_block_nodes: string[]; 45 | queue_mempool_nodes: string[]; 46 | block_nodes: Set; 47 | mempool_nodes: Set; 48 | seed_nodes: string[]; 49 | mempool: number; 50 | blocks: number; 51 | seedNodesOnly: boolean; 52 | 53 | constructor({ 54 | ticker, 55 | nodes, 56 | seedNodesOnly = false, 57 | enableIpv6 = false, 58 | forceUserAgent, 59 | user_agent, 60 | magic, 61 | genesisHeader, 62 | version, 63 | invalidBlocks, 64 | dataDir, 65 | pruneBlocks, 66 | blockHeight, 67 | mempool = 0, 68 | blocks = 0, 69 | validate, 70 | DEBUG_LOG, 71 | DEBUG_MEMORY, 72 | }: MasterOptions) { 73 | this.sockets = {}; 74 | this.mempool_sockets = {}; 75 | this.workers = {}; 76 | 77 | cluster.on("exit", (worker, code, signal) => { 78 | console.error( 79 | `master: Worker ${worker.id} exited with code: ${code}, signal: ${signal}` 80 | ); 81 | process.exit(code); // TODO: Recover instead of shutting down 82 | }); 83 | 84 | const nodesDir = path.join(dataDir, ticker, "nodes"); 85 | this.db_nodes = new DbNodes({ nodesDir, enableIpv6, readOnly: false }); 86 | this.seed_nodes = nodes; 87 | this.mempool = mempool; 88 | this.blocks = blocks; 89 | this.seedNodesOnly = seedNodesOnly; 90 | this.queue_block_nodes = []; 91 | this.queue_mempool_nodes = []; 92 | this.block_nodes = new Set(); 93 | this.mempool_nodes = new Set(); 94 | 95 | if (!nodes || nodes.length === 0) throw Error(`Missing nodes array`); 96 | if (!blocks && !mempool) 97 | throw Error(`Must set blocks > 0 and/or mempool > 0`); 98 | 99 | const blocksDir = path.join(dataDir, ticker, "blocks"); 100 | const db_blocks = new DbBlocks({ blocksDir, readOnly: false }); 101 | db_blocks.syncDb().then(() => db_blocks.close()); 102 | this.db_nodes.saveSeenNodes(nodes); 103 | 104 | const workerConfig: SpvOptions = { 105 | ticker, 106 | node: "", 107 | forceUserAgent, 108 | user_agent, 109 | magic, 110 | genesisHeader, 111 | version, 112 | invalidBlocks, 113 | dataDir, 114 | pruneBlocks, 115 | blockHeight, 116 | validate, 117 | autoReconnectWait: 100, 118 | DEBUG_LOG, 119 | DEBUG_MEMORY, 120 | }; 121 | 122 | for (let i = 0; i < blocks; i++) { 123 | let node = this.getNextNode("block"); 124 | let worker = cluster.fork(); 125 | worker.on("message", async (data) => { 126 | if (typeof data !== "string") return; 127 | try { 128 | const { command } = JSON.parse(data); 129 | if (command === "send_new_node") { 130 | const oldNode = node; 131 | this.block_nodes.delete(node); 132 | node = this.getNextNode("block"); 133 | this.block_nodes.add(node); 134 | // await new Promise((r) => setTimeout(r, 100)); 135 | worker.send( 136 | JSON.stringify({ command: "new_node", data: { node } }) 137 | ); 138 | console.log( 139 | `master: #${worker.id} blocks-${oldNode} disconnected trying node: ${node}` 140 | ); 141 | return; 142 | } 143 | } catch (err) {} 144 | this.onMessage(data); 145 | }); 146 | this.block_nodes.add(node); 147 | worker.send( 148 | `${JSON.stringify({ 149 | ...workerConfig, 150 | uid: worker.id, 151 | command: "init", 152 | node, 153 | blocks: true, 154 | })}\n\n` 155 | ); 156 | this.workers[`blocks-${node}`] = worker; 157 | console.log(`master: Forked blocks-${node}`); 158 | } 159 | 160 | for (let i = 0; i < mempool; i++) { 161 | let node = this.getNextNode("mempool"); 162 | let worker = cluster.fork(); 163 | worker.on("message", async (data) => { 164 | if (typeof data !== "string") return; 165 | try { 166 | const { command } = JSON.parse(data); 167 | if (command === "mempool_tx") { 168 | return this.onMempoolTxMessage(data); 169 | } else if (command === "send_new_node") { 170 | const oldNode = node; 171 | this.mempool_nodes.delete(node); 172 | node = this.getNextNode("mempool"); 173 | this.mempool_nodes.add(node); 174 | // await new Promise((r) => setTimeout(r, 100)); 175 | worker.send( 176 | JSON.stringify({ command: "new_node", data: { node } }) 177 | ); 178 | console.log( 179 | `master: #${worker.id} mempool-${oldNode} disconnected trying node: ${node}` 180 | ); 181 | return; 182 | } 183 | } catch (err) {} 184 | this.onMessage(data); 185 | }); 186 | this.mempool_nodes.add(node); 187 | worker.send( 188 | `${JSON.stringify({ 189 | ...workerConfig, 190 | uid: worker.id, 191 | command: "init", 192 | node, 193 | mempool: true, 194 | })}\n\n` 195 | ); 196 | this.workers[`mempool-${node}`] = worker; 197 | console.log(`master: Forked mempool-${node}`); 198 | } 199 | 200 | if (DEBUG_MEMORY) { 201 | setInterval(() => { 202 | const m: any = process.memoryUsage(); 203 | console.log( 204 | `master: Memory: ${Object.keys(m) 205 | .map((key: string) => `${key}: ${Helpers.formatBytes(m[key])}`) 206 | .join(", ")}` 207 | ); 208 | }, 1000 * 60); 209 | } 210 | } 211 | 212 | startServer({ 213 | port = 8080, 214 | host = "localhost", 215 | }: { 216 | port: number; 217 | host?: string; 218 | }) { 219 | const server = new Net.Server(); 220 | this.server = server; 221 | 222 | server.listen(port, host, () => { 223 | console.log(`master: opened socket ${host}:${port}`); 224 | }); 225 | 226 | server.on("connection", (socket) => { 227 | const uid = `${Math.random()}`; 228 | this.sockets[uid] = socket; 229 | console.log( 230 | `master: A new listener has connected at ${new Date().toLocaleString()}` 231 | ); 232 | 233 | let messageBuffer = ""; 234 | socket.on("data", (message) => { 235 | try { 236 | const msgs = `${messageBuffer}${message.toString()}` 237 | .toString() 238 | .split("\n\n"); 239 | messageBuffer = msgs[msgs.length - 1]; 240 | for (let i = 0; i < msgs.length - 1; i++) { 241 | const msg = msgs[i]; 242 | try { 243 | if (!msg.trim()) continue; 244 | const obj = JSON.parse(msg.trim()); 245 | const { command } = obj; 246 | if (command === "mempool_txs") { 247 | this.mempool_sockets[uid] = socket; 248 | } 249 | } catch (err) { 250 | console.error( 251 | `master: socket message parse error`, 252 | err, 253 | message.length, 254 | message.toString(), 255 | msg 256 | ); 257 | } 258 | } 259 | } catch (err) { 260 | console.error( 261 | `master: socket message error`, 262 | err, 263 | message.length, 264 | message.toString() 265 | ); 266 | } 267 | }); 268 | 269 | socket.on("end", () => { 270 | console.log( 271 | `master: Listener disconnected at ${new Date().toLocaleString()}` 272 | ); 273 | try { 274 | socket.destroy(); 275 | } catch (err) { 276 | console.error(`master socket end`, err); 277 | } 278 | delete this.sockets[uid]; 279 | delete this.mempool_sockets[uid]; 280 | }); 281 | 282 | socket.on("error", (err) => { 283 | console.error(`master: Socket error`, err); 284 | delete this.sockets[uid]; 285 | delete this.mempool_sockets[uid]; 286 | try { 287 | socket.destroy(); 288 | } catch (err) {} 289 | }); 290 | }); 291 | } 292 | 293 | onMessage(data: any) { 294 | if (typeof data !== "string") return; 295 | 296 | try { 297 | for (const key in this.sockets) { 298 | this.sockets[key].write(data); 299 | } 300 | } catch (err) { 301 | console.log(`master: Could not send message`, err, typeof data, data); 302 | } 303 | } 304 | 305 | onMempoolTxMessage(data: any) { 306 | if (typeof data !== "string") return; 307 | try { 308 | for (const key in this.mempool_sockets) { 309 | this.mempool_sockets[key].write(data); 310 | } 311 | } catch (err) { 312 | console.log(`master: Could not send message`, err, typeof data, data); 313 | } 314 | } 315 | 316 | getNodes() { 317 | const nodes: string[] = []; 318 | const added: Set = new Set(); 319 | this.seed_nodes 320 | .filter((node) => !added.has(node)) 321 | .map((node) => { 322 | nodes.push(node); 323 | added.add(node); 324 | }); 325 | if (!this.seedNodesOnly) { 326 | const blacklisted = new Set(this.db_nodes.getBlacklistedNodes()); 327 | this.db_nodes 328 | .getSeenNodes() 329 | .filter((node) => !added.has(node) && !blacklisted.has(node)) 330 | .sort(() => Math.random() - Math.random()) 331 | .map((node) => { 332 | nodes.push(node); 333 | added.add(node); 334 | }); 335 | this.db_nodes 336 | .getConnectedNodes() 337 | .filter((node) => !added.has(node) && !blacklisted.has(node)) 338 | .sort(() => Math.random() - Math.random()) 339 | .map((node) => { 340 | nodes.push(node); 341 | added.add(node); 342 | }); 343 | } 344 | return nodes.reverse(); 345 | } 346 | 347 | getNextNode(type: "block" | "mempool") { 348 | let node; 349 | for (let i = 0; i < this.mempool + this.blocks; i++) { 350 | if (type === "block") { 351 | if (this.queue_block_nodes.length === 0) { 352 | this.queue_block_nodes = this.getNodes(); 353 | console.log( 354 | `master: Refilling block queue`, 355 | this.queue_block_nodes.length 356 | ); 357 | } 358 | node = this.queue_block_nodes.pop(); 359 | if (node && !this.block_nodes.has(node)) break; 360 | } else if (type === "mempool") { 361 | if (this.queue_mempool_nodes.length === 0) { 362 | this.queue_mempool_nodes = this.getNodes(); 363 | console.log( 364 | `master: Refilling mempool queue`, 365 | this.queue_mempool_nodes.length 366 | ); 367 | } 368 | node = this.queue_mempool_nodes.pop(); 369 | if (node && !this.mempool_nodes.has(node)) break; 370 | } else { 371 | throw Error(`Invalid type`); 372 | } 373 | } 374 | return ( 375 | node || 376 | this.seed_nodes[Math.floor(Math.random() * this.seed_nodes.length)] 377 | ); 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /src/cluster_worker.ts: -------------------------------------------------------------------------------- 1 | import Spv, { SpvOptions } from "./spv"; 2 | import * as Helpers from "./helpers"; 3 | 4 | process.on("unhandledRejection", (reason, p) => { 5 | console.error(reason, "Worker Unhandled Rejection at Promise", p); 6 | }); 7 | process.on("uncaughtException", (err) => { 8 | console.error(err, "Worker Uncaught Exception thrown"); 9 | process.exit(1); 10 | }); 11 | 12 | export default class Worker { 13 | spv?: Spv; 14 | 15 | constructor() { 16 | process.on("message", (message: any) => { 17 | try { 18 | const msgs = message.toString().split("\n\n"); 19 | for (const msg of msgs) { 20 | if (!msg.trim()) continue; 21 | const obj = JSON.parse(msg.trim()); 22 | if (obj.command === "init") { 23 | this.start(obj).catch((err) => console.error(err)); 24 | } else if (obj.command === "new_node") { 25 | const { node } = obj.data; 26 | this.spv?.disconnect(); 27 | this.spv?.connect(node); 28 | } 29 | } 30 | } catch (err) { 31 | console.error(err); 32 | } 33 | }); 34 | } 35 | 36 | sendToMaster(obj: any) { 37 | if (process.send) process.send(`${JSON.stringify(obj)}\n\n`); 38 | } 39 | 40 | async start(config: SpvOptions) { 41 | const { mempool, blocks, DEBUG_MEMORY } = config; 42 | const REFRESH = 10; // console.log status every 10 seconds 43 | let txsSeen = 0; 44 | let txsSaved = 0; 45 | let txsSize = 0; 46 | let blockInterval: NodeJS.Timeout | undefined; 47 | let hasConnected = false; 48 | 49 | let date = +new Date(); 50 | const spv = new Spv(config); 51 | this.spv = spv; 52 | 53 | if (DEBUG_MEMORY) { 54 | setInterval(() => { 55 | const m: any = process.memoryUsage(); 56 | console.log( 57 | `${spv.id} Memory: ${Object.keys(m) 58 | .map((key: string) => `${key}: ${Helpers.formatBytes(m[key])}`) 59 | .join(", ")}` 60 | ); 61 | }, 1000 * 60); 62 | } 63 | 64 | let { height, hash } = spv.getTip(); 65 | console.log( 66 | `${spv.id} Loaded headers in ${ 67 | (+new Date() - date) / 1000 68 | } seconds. Latest tip: ${height}, ${hash}` 69 | ); 70 | spv.on("version", async ({ node, version }) => { 71 | console.log(`${spv.id} ${node} version`, version); 72 | }); 73 | spv.on("version_invalid", ({ error }) => { 74 | // console.error( 75 | // `${spv.id} has invalid user_agent: ${user_agent}. Will only connect to nodes that match "${expected_user_agent}"` 76 | // ); 77 | this.sendToMaster({ command: `send_new_node` }); 78 | }); 79 | let resetInterval: NodeJS.Timeout; 80 | spv.on("disconnected", ({ node, disconnects }) => { 81 | if (hasConnected) 82 | console.error(`${spv.id} disconnected ${disconnects} times`); 83 | clearInterval(resetInterval); 84 | clearInterval(blockInterval); 85 | blockInterval = undefined; 86 | hasConnected = false; 87 | 88 | this.sendToMaster({ 89 | command: `disconnected`, 90 | data: { node, disconnects }, 91 | }); 92 | if (disconnects >= 3) this.sendToMaster({ command: `send_new_node` }); 93 | }); 94 | spv.on("connected", async ({ node }) => { 95 | console.log(`${spv.id} connected at ${new Date().toLocaleString()}`); 96 | hasConnected = true; 97 | 98 | this.sendToMaster({ 99 | command: `connected`, 100 | data: { node }, 101 | }); 102 | resetInterval = setInterval(() => { 103 | // Reset disconect count if connected for longer than a minute 104 | if (spv.isConnected() && spv.peer) { 105 | spv.peer.disconnects = 0; 106 | } 107 | }, 1000 * 60); 108 | 109 | if (mempool) { 110 | try { 111 | const newHeaders = await spv.syncHeaders(); 112 | console.log( 113 | `${spv.id} Synced ${newHeaders} new headers. Listening for mempool txs...` 114 | ); 115 | } catch (err) { 116 | console.error(err); 117 | } 118 | } 119 | if (blocks) { 120 | try { 121 | await spv.warningPruneBlocks(); // Delete blocks older that the number of `pruneBlocks` from the tip 122 | const newHeaders = await spv.syncHeaders(); 123 | console.log( 124 | `${spv.id} Synced ${newHeaders} new headers. Syncing blocks...` 125 | ); 126 | const newBlocks = await spv.syncBlocks(); 127 | console.log( 128 | `${spv.id} Synced ${newBlocks} new blocks. Listening for new blocks...` 129 | ); 130 | } catch (err) { 131 | console.error(err); 132 | } 133 | } 134 | }); 135 | spv.on("could_not_connect", () => { 136 | this.sendToMaster({ command: `send_new_node` }); 137 | }); 138 | 139 | spv.on("headers_new", ({ headers }) => { 140 | const { height, hash } = spv.getTip(); 141 | console.log( 142 | `${spv.id} ${ 143 | headers.length 144 | } new headers. Tip: ${height}, ${hash} at ${new Date().toLocaleString()}` 145 | ); 146 | }); 147 | spv.on("headers_saved", ({ hashes }) => { 148 | console.log( 149 | `${spv.id} ${ 150 | hashes.length 151 | } new headers saved to disk at ${new Date().toLocaleString()}` 152 | ); 153 | this.sendToMaster({ 154 | command: `headers_saved`, 155 | data: { hashes: hashes.map((h: Buffer) => h.toString("hex")) }, 156 | }); 157 | }); 158 | spv.on( 159 | "peer_error", 160 | ({ error, buffer }: { error: any; buffer: Buffer }) => { 161 | console.error(`${spv.id} peer_error`, error, buffer.toString("hex")); 162 | } 163 | ); 164 | spv.on("block_reorg", async ({ height, hash }) => { 165 | console.log( 166 | `${ 167 | spv.id 168 | } Re-org detected after block height ${height}, ${hash} at ${new Date().toLocaleString()}!` 169 | ); 170 | this.sendToMaster({ 171 | command: `block_reorg`, 172 | data: { hash, height }, 173 | }); 174 | if (blocks) { 175 | try { 176 | await spv.syncHeaders(); 177 | await spv.syncBlocks(); 178 | } catch (err) { 179 | // console.error(err); 180 | } 181 | } 182 | }); 183 | spv.on("block_seen", async ({ hashes }) => { 184 | console.log( 185 | `${spv.id} New block seen: ${hashes 186 | .map((h: Buffer) => h.toString("hex")) 187 | .join(", ")} at ${new Date().toLocaleString()}` 188 | ); 189 | if (blocks) { 190 | try { 191 | await spv.syncHeaders(); 192 | await spv.syncBlocks(); 193 | } catch (err) { 194 | // console.error(err); 195 | } 196 | } 197 | }); 198 | // spv.on("block_downloading", ({ hash, height }) => { 199 | // console.log(`${spv.id} Downloading block: ${height}, ${hash}...`); 200 | // }); 201 | // spv.on("mempool_pruned", ({ header, height, finished, txCount }) => { 202 | // if (!header) { 203 | // console.log(`${spv.id} Pruned ${txCount} mempool txs`); 204 | // } else { 205 | // console.log( 206 | // `${spv.id} Pruned ${txCount} mempool txs included in block ${height}` 207 | // ); 208 | // } 209 | // }); 210 | 211 | spv.on("mempool_txs_seen", ({ txids }) => { 212 | // console.log(`${spv.id} ${txids.length} txs seen in mempool`); 213 | txsSeen += txids.length; 214 | }); 215 | spv.on("mempool_tx", ({ transaction }) => { 216 | txsSaved++; 217 | txsSize += transaction.length; 218 | this.sendToMaster({ 219 | command: `mempool_tx`, 220 | data: { 221 | transaction: transaction.toBuffer().toString("base64"), 222 | size: transaction.length, 223 | }, 224 | }); 225 | }); 226 | spv.on("block_pruned", ({ height, hash }) => { 227 | console.log(`${spv.id} Pruned block ${height}, ${hash}`); 228 | }); 229 | spv.on("block_saved", ({ height, hash, size, txCount, startDate }) => { 230 | const seconds = (+new Date() - startDate) / 1000; 231 | console.log( 232 | `${ 233 | spv.id 234 | } Downloaded block ${height}/${spv.getHeight()}, ${hash}, ${txCount} txs in ${seconds} seconds. ${Helpers.formatSpeeds( 235 | size, 236 | seconds 237 | )}. ${new Date().toLocaleString()}` 238 | ); 239 | this.sendToMaster({ 240 | command: `block_saved`, 241 | data: { hash, height, size, txCount }, 242 | }); 243 | }); 244 | spv.on( 245 | "block_already_saved", 246 | ({ height, hash, size, txCount, startDate }) => { 247 | const seconds = (+new Date() - startDate) / 1000; 248 | console.log( 249 | `${ 250 | spv.id 251 | } Downloaded block ${height}/${spv.getHeight()}, ${hash}, ${txCount} txs in ${seconds} seconds. ${Helpers.formatSpeeds( 252 | size, 253 | seconds 254 | )}. Block already saved. ${new Date().toLocaleString()}` 255 | ); 256 | this.sendToMaster({ 257 | command: `block_already_saved`, 258 | data: { hash, height, size, txCount }, 259 | }); 260 | } 261 | ); 262 | let chunkParams: any; 263 | spv.on("block_chunk", (params) => { 264 | chunkParams = params; 265 | if (params.started) { 266 | clearInterval(blockInterval); 267 | blockInterval = setInterval(() => { 268 | const { blockHash, height, size, startDate } = chunkParams; 269 | const seconds = (+new Date() - startDate) / 1000; 270 | console.log( 271 | `${ 272 | spv.id 273 | } downloading block ${height}/${spv.getHeight()} ${blockHash.toString( 274 | "hex" 275 | )} taking ${Number(seconds).toFixed( 276 | 0 277 | )} seconds so far. ${Helpers.formatSpeeds(size, seconds)}` 278 | ); 279 | }, 1000 * 10); // TODO: Change to 10 seconds 280 | } 281 | if (params.finished) { 282 | clearInterval(blockInterval); 283 | blockInterval = undefined; 284 | } 285 | }); 286 | // spv.on( 287 | // "block_txs", 288 | // ({ transactions, header, started, finished, height, size }) => { 289 | // for (const [index, transaction] of transactions) { 290 | // console.log( 291 | // `tx ${transaction 292 | // .getHash() 293 | // .toString("hex")} in index ${index} of block ${height}` 294 | // ); 295 | // } 296 | // } 297 | // ); 298 | 299 | setInterval(() => { 300 | if (!spv.isConnected()) { 301 | console.log(`${spv.id} is disconnected.`); 302 | } else if (mempool) { 303 | console.log( 304 | `${ 305 | spv.id 306 | } is connected. tip ${spv.headers.getHeight()}. ${txsSaved}/${txsSeen} txs. ${Helpers.formatSpeeds( 307 | txsSize, 308 | REFRESH 309 | )} ` 310 | ); 311 | txsSeen = 0; 312 | txsSaved = 0; 313 | txsSize = 0; 314 | } else if (blocks && !blockInterval) { 315 | console.log(`${spv.id} is connected. tip ${spv.headers.getHeight()}.`); 316 | } 317 | }, REFRESH * 1000); 318 | 319 | console.log(`${spv.id} Connecting to node ${config.node}...`); 320 | await spv.connect(); 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /src/db_blocks.ts: -------------------------------------------------------------------------------- 1 | import * as bsv from "bsv-minimal"; 2 | import path from "path"; 3 | import * as lmdb from "lmdb"; 4 | import fs from "fs"; 5 | 6 | export default class DbBlocks { 7 | blocksDir: string; 8 | writeDir?: string; 9 | writeStream?: fs.WriteStream; 10 | dbi_blocks: lmdb.Database; 11 | dbi_root: lmdb.RootDatabase; 12 | dbPath: string; 13 | 14 | constructor({ 15 | blocksDir, 16 | readOnly = true, 17 | }: { 18 | blocksDir: string; 19 | readOnly?: boolean; 20 | }) { 21 | if (!blocksDir) throw Error(`Missing blocksDir`); 22 | this.blocksDir = blocksDir; 23 | fs.mkdirSync(blocksDir, { recursive: true }); 24 | const dbPath = path.join(blocksDir, "meta"); 25 | this.dbPath = dbPath; 26 | 27 | fs.mkdirSync(this.dbPath, { recursive: true }); 28 | this.dbi_root = lmdb.open({ 29 | path: this.dbPath, 30 | readOnly, 31 | }); 32 | this.dbi_blocks = this.dbi_root.openDB({ 33 | name: "blocks", 34 | encoding: "binary", 35 | keyEncoding: "binary", 36 | cache: true, 37 | }); 38 | } 39 | 40 | async syncDb() { 41 | const startDate = +new Date(); 42 | console.log(`Syncing block files with db...`); 43 | const count = this.dbi_blocks.getKeysCount({ limit: 10 }); 44 | if (count === 0) { 45 | const hashes = Array.from(this.getSavedBlocksSync()); 46 | for (const hash of hashes) { 47 | this.markBlockSaved(hash); 48 | } 49 | await this.dbi_blocks.flushed; 50 | console.log( 51 | `Synced ${hashes.length} block files with db in ${ 52 | (+new Date() - startDate) / 1000 53 | } seconds` 54 | ); 55 | } 56 | } 57 | 58 | async close() { 59 | try { 60 | await this.dbi_blocks.close(); 61 | } catch (err) {} 62 | try { 63 | await this.dbi_root.close(); 64 | } catch (err) {} 65 | } 66 | 67 | getSavedBlocks() { 68 | const hashes: string[] = []; 69 | for (const key of this.dbi_blocks.getKeys()) { 70 | if (Buffer.isBuffer(key)) hashes.push(key.toString("hex")); 71 | } 72 | return hashes; 73 | } 74 | 75 | async getBlocks() { 76 | const files = await fs.promises.readdir(this.blocksDir); 77 | return files; 78 | } 79 | 80 | getBlocksSync() { 81 | const files = fs.readdirSync(this.blocksDir); 82 | return files; 83 | } 84 | 85 | getSavedBlocksSync() { 86 | const hashes: Set = new Set(); 87 | const files = this.getBlocksSync(); 88 | for (const file of files) { 89 | const split = file.split("."); 90 | if (split.length === 2 && split[0].length === 64) { 91 | const hash = split[0]; 92 | hashes.add(hash); 93 | } 94 | } 95 | return hashes; 96 | } 97 | 98 | async fileExists(dir: string) { 99 | try { 100 | await fs.promises.access(dir, fs.constants.F_OK); 101 | return true; 102 | } catch { 103 | return false; 104 | } 105 | } 106 | 107 | markBlockSaved(hash: string) { 108 | if (!this.blockExists(hash)) { 109 | return this.dbi_blocks.put(Buffer.from(hash, "hex"), Buffer.from("")); 110 | } 111 | } 112 | 113 | writeBlockChunk({ 114 | chunk, 115 | blockHash, 116 | started, 117 | finished, 118 | }: { 119 | chunk: Buffer; 120 | blockHash: Buffer; 121 | started: boolean; 122 | finished: boolean; 123 | }) { 124 | return new Promise((resolve, reject) => { 125 | if (started) { 126 | this.writeDir = path.join( 127 | this.blocksDir, 128 | `${blockHash.toString("hex")}.bin` 129 | ); 130 | this.writeStream = fs.createWriteStream( 131 | `${this.writeDir}.${process.pid}` 132 | ); 133 | } 134 | if (!this.writeStream) throw Error(`No WriteStream`); 135 | 136 | this.writeStream.write(chunk); 137 | 138 | if (finished) { 139 | if (!this.writeDir) return reject(Error(`Missing writeDir`)); 140 | const dir = this.writeDir; 141 | this.writeStream.close(async (err: any) => { 142 | try { 143 | if (err) throw err; 144 | const fileExists = await this.fileExists(dir); 145 | if (!fileExists) { 146 | // Save block to disk 147 | await fs.promises.rename(`${dir}.${process.pid}`, dir); 148 | this.markBlockSaved(blockHash.toString("hex")); 149 | return resolve(true); 150 | } else { 151 | // Block already saved. Delete copy 152 | throw Error(`Block already saved`); 153 | } 154 | } catch (err) { 155 | // console.error(err); 156 | try { 157 | await fs.promises.unlink(`${dir}.${process.pid}`); 158 | } catch (err) {} 159 | } 160 | resolve(false); 161 | }); 162 | this.writeStream = undefined; 163 | } else { 164 | resolve(false); 165 | } 166 | }); 167 | } 168 | 169 | streamBlock( 170 | { 171 | hash, 172 | height, 173 | highWaterMark = 1024 * 1024 * 128, // ~128MB chunks. Change to fit in memory limits 174 | }: { hash: string | Buffer; height: number; highWaterMark?: number }, 175 | callback: (params: bsv.BlockStream) => Promise | any 176 | ): Promise { 177 | return new Promise(async (resolve, reject) => { 178 | try { 179 | if (typeof callback !== "function") throw Error(`Missing callback`); 180 | hash = hash.toString("hex"); 181 | hash = hash.split(".").length > 1 ? hash : `${hash}.bin`; 182 | const dir = path.join(this.blocksDir, hash); 183 | const fileExists = await this.fileExists(dir); 184 | if (!fileExists) { 185 | await this.delBlock(hash); 186 | throw Error(`Missing block ${hash}`); 187 | } 188 | 189 | let fileEnd = false; 190 | let readingData = false; 191 | const block = new bsv.Block(); 192 | const stream = fs.createReadStream(dir, { highWaterMark }); 193 | stream.on("data", async (data: Buffer) => { 194 | readingData = true; 195 | try { 196 | const result = block.addBufferChunk(data); 197 | // const { transactions, header, started, finished, height, size } = result; 198 | 199 | if (height >= 0) result.height = height; 200 | const promise = callback(result); 201 | if (promise instanceof Promise) { 202 | stream.pause(); 203 | await promise; 204 | stream.resume(); 205 | } 206 | if (result.finished) { 207 | resolve(true); 208 | } else if (fileEnd) { 209 | throw Error(`Block is missing bytes`); 210 | } 211 | } catch (err) { 212 | stream.destroy(); 213 | reject(err); 214 | } 215 | readingData = false; 216 | }); 217 | stream.on("end", () => { 218 | fileEnd = true; 219 | if (!readingData) { 220 | const err = Error(`Block is missing bytes`); 221 | reject(err); 222 | } 223 | }); 224 | stream.on("error", (err) => reject(err)); 225 | } catch (err) { 226 | reject(err); 227 | } 228 | }); 229 | } 230 | 231 | async delBlock(hash: string | Buffer) { 232 | let dir; 233 | hash = hash.toString("hex"); 234 | if (hash.split(".").length > 1) { 235 | dir = path.join(this.blocksDir, hash); 236 | } else { 237 | dir = path.join(this.blocksDir, `${hash}.bin`); 238 | } 239 | await fs.promises.unlink(dir); 240 | hash = hash.split(".")[0]; 241 | if (this.blockExists(hash)) { 242 | await this.dbi_blocks.remove(Buffer.from(hash, "hex")); 243 | } 244 | } 245 | 246 | blockExists(hash: string) { 247 | return this.dbi_blocks.doesExist(Buffer.from(hash, "hex")); 248 | } 249 | 250 | blockFileExists(hash: string) { 251 | const dir = path.join(this.blocksDir, `${hash}.bin`); 252 | return this.fileExists(dir); 253 | } 254 | 255 | async getTx({ 256 | txid, 257 | block, 258 | pos, 259 | len = 1000000, 260 | }: { 261 | txid?: string | Buffer; 262 | block: string | Buffer; 263 | pos: number; 264 | len: number; 265 | }) { 266 | const dir = path.join(this.blocksDir, `${block.toString("hex")}.bin`); 267 | const file = await fs.promises.open(dir, "r"); 268 | const { bytesRead, buffer } = await file.read( 269 | Buffer.alloc(len), 270 | 0, 271 | len, 272 | pos 273 | ); 274 | await file.close(); 275 | let tx; 276 | if (txid) { 277 | tx = bsv.Transaction.fromBuffer(buffer); 278 | if (txid && txid.toString("hex") !== tx.getTxid()) { 279 | throw Error(`Invalid txid`); 280 | } 281 | } 282 | return { tx, buffer, bytesRead }; 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/db_headers.ts: -------------------------------------------------------------------------------- 1 | import * as bsv from "bsv-minimal"; 2 | import * as lmdb from "lmdb"; 3 | import Headers from "bsv-headers"; 4 | import fs from "fs"; 5 | 6 | export default class DbHeaders { 7 | headers: Headers; 8 | env: any; 9 | dbi_root: lmdb.RootDatabase; 10 | dbi_headers: lmdb.Database; 11 | headersDir: string; 12 | 13 | constructor({ 14 | headersDir, 15 | headers, 16 | readOnly = true, 17 | }: { 18 | headersDir: string; 19 | headers: any; 20 | readOnly?: boolean; 21 | }) { 22 | if (!headersDir) throw Error(`Missing headersDir`); 23 | if (!headers) throw Error(`Missing headers param`); 24 | fs.mkdirSync(headersDir, { recursive: true }); 25 | this.headers = headers; 26 | this.headersDir = headersDir; 27 | 28 | this.dbi_root = lmdb.open({ path: headersDir, readOnly }); 29 | this.dbi_headers = this.dbi_root.openDB({ 30 | name: "headers", 31 | encoding: "binary", 32 | keyEncoding: "binary", 33 | }); 34 | this.loadHeaders(); 35 | } 36 | 37 | async close() { 38 | try { 39 | await this.dbi_headers.close(); 40 | } catch (err) {} 41 | try { 42 | await this.dbi_root.close(); 43 | } catch (err) {} 44 | } 45 | 46 | async saveHeaders(headerArray: bsv.Header[]): Promise { 47 | const hashes: Buffer[] = []; 48 | for (const header of headerArray) { 49 | const hash = header.getHash(); 50 | if (!this.dbi_headers.get(hash)) { 51 | this.dbi_headers.put(hash, header.toBuffer()); 52 | hashes.push(hash); 53 | } 54 | } 55 | await this.dbi_headers.flushed; 56 | return hashes; 57 | } 58 | 59 | getHeader(hash: string | Buffer): bsv.Header { 60 | if (!Buffer.isBuffer(hash)) hash = Buffer.from(hash, "hex"); 61 | if (hash.toString("hex") === this.headers.genesis) { 62 | return this.headers.genesisHeader; 63 | } 64 | const buf = this.dbi_headers.getBinary(hash); 65 | if (!buf) throw Error(`Missing header: ${hash.toString("hex")}`); 66 | const header = bsv.Header.fromBuffer(buf); 67 | return header; 68 | } 69 | 70 | loadHeaders() { 71 | for (const { key: hash, value: buf } of this.dbi_headers.getRange()) { 72 | if (Buffer.isBuffer(hash) && Buffer.isBuffer(buf)) 73 | this.headers.addHeader({ buf, hash }); 74 | } 75 | this.headers.process(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/db_listener.ts: -------------------------------------------------------------------------------- 1 | import * as lmdb from "lmdb"; 2 | import fs from "fs"; 3 | 4 | export interface ListenerOptions { 5 | blockHash: string | Buffer; 6 | height: number; 7 | matches?: number; 8 | errors?: number; 9 | txCount?: number; 10 | size?: number; 11 | timer?: number; 12 | } 13 | 14 | export default class DbListener { 15 | dbi_root: lmdb.RootDatabase; 16 | dbi_blocks: lmdb.Database; 17 | dbi_heights: lmdb.Database; 18 | listenerDir: string; 19 | 20 | constructor({ 21 | listenerDir, 22 | readOnly = false, 23 | }: { 24 | listenerDir: string; 25 | readOnly?: boolean; 26 | }) { 27 | if (!listenerDir) throw Error(`Missing listenerDir`); 28 | fs.mkdirSync(listenerDir, { recursive: true }); 29 | this.listenerDir = listenerDir; 30 | 31 | this.dbi_root = lmdb.open({ path: listenerDir, noSubdir: false, readOnly }); 32 | this.dbi_blocks = this.dbi_root.openDB({ 33 | name: "block_info", 34 | keyEncoding: "binary", 35 | encoding: "binary", 36 | }); 37 | this.dbi_heights = this.dbi_root.openDB({ 38 | name: "block_heights", 39 | keyEncoding: "uint32", 40 | encoding: "binary", 41 | }); 42 | } 43 | 44 | async close() { 45 | try { 46 | await this.dbi_blocks.close(); 47 | } catch (err) {} 48 | try { 49 | await this.dbi_heights.close(); 50 | } catch (err) {} 51 | try { 52 | await this.dbi_root.close(); 53 | } catch (err) {} 54 | } 55 | 56 | markBlockProcessed({ 57 | blockHash, 58 | height, 59 | matches, 60 | errors, 61 | txCount, 62 | size, 63 | timer, 64 | }: ListenerOptions) { 65 | if (!Buffer.isBuffer(blockHash)) blockHash = Buffer.from(blockHash, "hex"); 66 | const date = +new Date(); 67 | const value = Buffer.from( 68 | JSON.stringify({ matches, errors, txCount, size, date, timer }) 69 | ); 70 | let buf = this.dbi_heights.get(height); 71 | if (!buf || buf.toString("hex") !== blockHash.toString("hex")) { 72 | this.dbi_heights.put(height, blockHash); 73 | } 74 | buf = this.dbi_blocks.get(blockHash); 75 | if (!buf || buf.toString() !== value.toString()) { 76 | this.dbi_blocks.put(blockHash, value); 77 | } 78 | return Promise.all([this.dbi_heights.flushed, this.dbi_blocks.flushed]); 79 | } 80 | 81 | batchBlocksProcessed(array: ListenerOptions[]): Promise { 82 | const date = +new Date(); 83 | for (const obj of array) { 84 | let { blockHash, height, matches, errors, txCount, size, timer } = obj; 85 | const value = Buffer.from( 86 | JSON.stringify({ matches, errors, txCount, size, date, timer }) 87 | ); 88 | if (!Buffer.isBuffer(blockHash)) 89 | blockHash = Buffer.from(blockHash, "hex"); 90 | this.dbi_heights.put(height, blockHash); 91 | this.dbi_blocks.put(blockHash, value); 92 | } 93 | return this.dbi_root.flushed; 94 | } 95 | 96 | isProcessed(height: number): boolean { 97 | return this.dbi_heights.doesExist(height); 98 | } 99 | blocksProcessed() { 100 | return this.dbi_heights.getCount(); 101 | } 102 | getHash(height: number): string { 103 | const buf = this.dbi_heights.get(height); 104 | if (!Buffer.isBuffer(buf)) throw Error(`Missing height`); 105 | return buf.toString("hex"); 106 | } 107 | 108 | getBlockInfo(blockHash: string | Buffer) { 109 | if (!Buffer.isBuffer(blockHash)) blockHash = Buffer.from(blockHash, "hex"); 110 | const value = this.dbi_blocks.get(blockHash); 111 | if (!value) throw Error(`Missing block info`); 112 | return JSON.parse(value.toString()); 113 | } 114 | 115 | getBlockHash(height: number) { 116 | const value = this.dbi_heights.get(height); 117 | if (!Buffer.isBuffer(value)) throw Error(`Missing block height`); 118 | return value.toString("hex"); 119 | } 120 | 121 | delBlocks(from: number, to: number) { 122 | for (let height = from; height <= to; height++) { 123 | this.dbi_heights.remove(height); 124 | } 125 | return this.dbi_root.flushed; 126 | } 127 | 128 | getSize() { 129 | console.log(`Calculating size...`); 130 | console.log(`dbi_blocks: ${this.dbi_blocks.getCount()}`); 131 | console.log(`dbi_heights: ${this.dbi_blocks.getCount()}`); 132 | let count = 0; 133 | let size = 0; 134 | for (const { key, value } of this.dbi_blocks.getRange()) { 135 | if (!Buffer.isBuffer(key) || !Buffer.isBuffer(value)) continue; 136 | size += key.length + value.length; 137 | count++; 138 | if (count % 1000 === 0) 139 | console.log( 140 | `${count}: ${key.toString("hex")}. value: ${value.length} bytes` 141 | ); 142 | } 143 | console.log( 144 | `Size of dbi_blocks: ${size.toLocaleString( 145 | "en-US" 146 | )} bytes. ${count} total` 147 | ); 148 | count = 0; 149 | size = 0; 150 | for (const { key, value } of this.dbi_heights.getRange()) { 151 | if (typeof key !== "number" || !Buffer.isBuffer(value)) continue; 152 | size += value.length; 153 | count++; 154 | if (count % 1000 === 0) 155 | console.log(`${count}: ${key} value: ${value.length} bytes`); 156 | } 157 | console.log( 158 | `Size of dbi_heights: ${size.toLocaleString( 159 | "en-US" 160 | )} bytes. ${count} total` 161 | ); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/db_mempool.ts: -------------------------------------------------------------------------------- 1 | import * as bsv from "bsv-minimal"; 2 | import levelup, { LevelUp } from "levelup"; 3 | import { AbstractBatch } from "abstract-leveldown"; 4 | import rocksdb from "rocksdb"; 5 | import fs from "fs"; 6 | 7 | export default class DbMempool { 8 | db: LevelUp; 9 | txs: { [key: string]: Buffer }; 10 | batch: AbstractBatch[]; 11 | intervalBatch?: NodeJS.Timer; 12 | intervalPrune?: NodeJS.Timer; 13 | pruneAfter: number; 14 | 15 | constructor({ 16 | mempoolDir, 17 | pruneAfter = 1000 * 60 * 60 * 4, // After 4 hours 18 | readOnly = false, 19 | }: { 20 | mempoolDir: string; 21 | pruneAfter?: number; 22 | readOnly?: boolean; 23 | }) { 24 | if (!mempoolDir) throw Error(`Missing mempoolDir`); 25 | fs.mkdirSync(mempoolDir, { recursive: true }); 26 | this.pruneAfter = pruneAfter; 27 | this.batch = []; 28 | this.db = levelup(rocksdb(mempoolDir), { readOnly }); 29 | this.txs = {}; 30 | } 31 | 32 | close() { 33 | try { 34 | this.db.close(); 35 | } catch (err) {} 36 | } 37 | 38 | saveTx(tx: bsv.Transaction) { 39 | this.txs[tx.getTxid()] = tx.toBuffer(); 40 | const bw = new bsv.utils.BufferWriter(); 41 | bw.writeVarintNum(+new Date()); 42 | bw.writeVarLengthBuffer(tx.toBuffer()); 43 | this.batch.push({ 44 | type: "put", 45 | key: tx.getHash(), 46 | value: bw.toBuffer(), 47 | }); 48 | if (this.batch.length > 10000) this.saveTxs(); 49 | if (!this.intervalBatch) 50 | this.intervalBatch = setInterval(() => this.saveTxs(), 1000 * 10); 51 | if (!this.intervalPrune) 52 | this.intervalPrune = setInterval(() => this.pruneTxs(), 1000 * 60 * 20); 53 | } 54 | 55 | saveTxs() { 56 | return new Promise((resolve, reject) => { 57 | if (this.batch.length === 0) return resolve(null); 58 | const operations = this.batch; 59 | this.batch = []; 60 | this.db.batch(operations, (err: any) => { 61 | if (err) return reject(err); 62 | resolve(null); 63 | operations.map(({ key }) => delete this.txs[key.toString("hex")]); 64 | }); 65 | }); 66 | } 67 | 68 | delTxs(txids: Buffer[]) { 69 | return new Promise((resolve, reject) => { 70 | if (txids.length === 0) return resolve(null); 71 | const operations: any = []; 72 | txids.map((txid) => operations.push({ type: "del", key: txid })); 73 | this.db.batch(operations, (err: any) => { 74 | if (err) return reject(err); 75 | resolve(null); 76 | }); 77 | }); 78 | } 79 | 80 | getTxids({ 81 | olderThan, 82 | newerThan, 83 | }: { 84 | olderThan?: number; 85 | newerThan?: number; 86 | }): Promise { 87 | return new Promise((resolve, reject) => { 88 | try { 89 | const txids: Buffer[] = []; 90 | const stream = this.db.createReadStream({ keys: true, values: true }); 91 | stream.on("data", ({ key, value }: { key: Buffer; value: Buffer }) => { 92 | const br = new bsv.utils.BufferReader(value); 93 | const time = br.readVarintNum(); 94 | if (olderThan && newerThan && time < olderThan && time > newerThan) { 95 | txids.push(key); 96 | } else if (olderThan && time < olderThan) { 97 | txids.push(key); 98 | } else if (newerThan && time > newerThan) { 99 | txids.push(key); 100 | } else { 101 | txids.push(key); 102 | } 103 | }); 104 | stream.on("error", (err: any) => reject(err)); 105 | stream.on("end", () => resolve(txids)); 106 | } catch (err) { 107 | reject(err); 108 | } 109 | }); 110 | } 111 | 112 | async getTx(txid: string | Buffer) { 113 | if (this.txs[txid.toString("hex")]) { 114 | const buf = this.txs[txid.toString("hex")]; 115 | const tx = bsv.Transaction.fromBuffer(buf); 116 | const size = tx.length; 117 | const time = Math.floor(+new Date() / 1000); 118 | return { tx, size, time }; 119 | } 120 | if (!Buffer.isBuffer(txid)) txid = Buffer.from(txid, "hex"); 121 | const buf = await this.db.get(txid); 122 | if (!buf) throw Error(`Tx not found`); 123 | const br = new bsv.utils.BufferReader(buf); 124 | const time = Math.floor(br.readVarintNum() / 1000); 125 | const txBuf = br.readVarLengthBuffer(); 126 | const tx = bsv.Transaction.fromBuffer(txBuf); 127 | const size = tx.length; 128 | return { tx, time, size }; 129 | } 130 | 131 | async pruneTxs(olderThan?: number) { 132 | if (!olderThan) olderThan = +new Date() - this.pruneAfter; 133 | const txids = await this.getTxids({ olderThan }); 134 | await this.delTxs(txids); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/db_nodes.ts: -------------------------------------------------------------------------------- 1 | import * as lmdb from "lmdb"; 2 | import fs from "fs"; 3 | import { NetAddress } from "bsv-p2p/lib/messages/address"; 4 | 5 | export default class DbNodes { 6 | blacklistTime: number; 7 | env: any; 8 | dbi_root: lmdb.RootDatabase; 9 | dbi_meta: lmdb.Database; 10 | dbi_seen: lmdb.Database; 11 | dbi_connected: lmdb.Database; 12 | dbi_blacklisted: lmdb.Database; 13 | nodesDir: string; 14 | enableIpv6: boolean; 15 | 16 | constructor({ 17 | nodesDir, 18 | blacklistTime = (+new Date() - 1000 * 60 * 60 * 24) / 1000, // 24 hour blacklist 19 | enableIpv6 = false, 20 | readOnly = true, 21 | }: { 22 | nodesDir: string; 23 | blacklistTime?: number; 24 | enableIpv6?: boolean; 25 | readOnly?: boolean; 26 | }) { 27 | if (!nodesDir) throw Error(`Missing nodesDir`); 28 | fs.mkdirSync(nodesDir, { recursive: true }); 29 | this.nodesDir = nodesDir; 30 | this.blacklistTime = blacklistTime; 31 | this.enableIpv6 = enableIpv6; 32 | 33 | this.dbi_root = lmdb.open({ 34 | path: nodesDir, 35 | readOnly, 36 | }); 37 | this.dbi_meta = this.dbi_root.openDB({ 38 | name: "meta", 39 | }); 40 | this.dbi_seen = this.dbi_root.openDB({ 41 | name: "peers_seen", 42 | }); 43 | this.dbi_connected = this.dbi_root.openDB({ 44 | name: "peers_connected", 45 | }); 46 | this.dbi_blacklisted = this.dbi_root.openDB({ 47 | name: "peers_blacklisted", 48 | }); 49 | } 50 | 51 | async close() { 52 | try { 53 | await this.dbi_seen.close(); 54 | } catch (err) {} 55 | try { 56 | await this.dbi_connected.close(); 57 | } catch (err) {} 58 | try { 59 | await this.dbi_blacklisted.close(); 60 | } catch (err) {} 61 | try { 62 | await this.dbi_meta.close(); 63 | } catch (err) {} 64 | try { 65 | await this.dbi_root.close(); 66 | } catch (err) {} 67 | } 68 | 69 | formatUrl({ node, port }: { node: string; port?: number }) { 70 | if (!port) { 71 | // assume port is already in node 72 | return node; 73 | } else if (node.split(":").length === 7) { 74 | // ipv6 address 75 | return `[${node}]:${port}`; 76 | } else { 77 | // ipv4 address 78 | return `${node}:${port}`; 79 | } 80 | } 81 | 82 | async saveSeenNodes(addrs: NetAddress[] | string[]) { 83 | let count = 0; 84 | for (const addr of addrs) { 85 | let url; 86 | if (typeof addr === "string") { 87 | url = addr; 88 | } else { 89 | const node = addr.ipv4 || addr.ipv6; 90 | if (node) url = this.formatUrl({ node, port: addr.port }); 91 | } 92 | if (url) { 93 | if (!this.dbi_seen.get(url)) count++; 94 | this.dbi_seen.put(url, `${+new Date()}`); 95 | } 96 | } 97 | if (count) await this.dbi_seen.flushed; 98 | return count; 99 | } 100 | 101 | markSavedSeen() { 102 | this.dbi_meta.put("saved_seen", { date: +new Date() }); 103 | } 104 | hasSavedSeen(secondsAgo = 60) { 105 | const result = this.dbi_meta.get("saved_seen"); 106 | if (result) { 107 | const { date } = result; 108 | const compare = +new Date() - secondsAgo * 1000; 109 | if (date > compare) return true; 110 | } 111 | return false; 112 | } 113 | 114 | connected({ node, port }: { node: string; port?: number }) { 115 | const url = this.formatUrl({ node, port }); 116 | let count = this.dbi_connected.get(url) || 0; 117 | this.dbi_connected.put(url, count + 1); 118 | } 119 | 120 | hasConnected({ node, port }: { node: string; port?: number }): boolean { 121 | const url = this.formatUrl({ node, port }); 122 | const data = this.dbi_connected.get(url); 123 | return !!data; 124 | } 125 | 126 | blacklist({ node, port }: { node: string; port?: number }) { 127 | if (!this.isBlacklisted({ node, port })) { 128 | const date = Math.round(+new Date() / 1000); 129 | const url = this.formatUrl({ node, port }); 130 | this.dbi_blacklisted.put(url, date); 131 | } 132 | } 133 | 134 | isBlacklisted({ node, port }: { node: string; port?: number }): boolean { 135 | const url = this.formatUrl({ node, port }); 136 | const date = this.dbi_blacklisted.get(url); 137 | return typeof date === "number" && date > this.blacklistTime; 138 | } 139 | 140 | getBlacklistedNodes() { 141 | const nodes = []; 142 | for (const { key: url, value: date } of this.dbi_blacklisted.getRange()) { 143 | if (typeof url !== "string" || typeof date !== "number") continue; 144 | if (date > this.blacklistTime) { 145 | if (this.enableIpv6 || url.split(":").length === 2) nodes.push(url); 146 | } 147 | } 148 | return nodes; 149 | } 150 | 151 | getConnectedNodes() { 152 | const nodes: string[] = []; 153 | for (const url of this.dbi_connected.getKeys()) { 154 | if (typeof url !== "string") continue; 155 | if (this.enableIpv6 || url.split(":").length === 2) nodes.push(url); 156 | } 157 | return nodes; 158 | } 159 | 160 | getSeenNodes() { 161 | const nodes: string[] = []; 162 | for (const url of this.dbi_seen.getKeys()) { 163 | if (typeof url !== "string") continue; 164 | if (this.enableIpv6 || url.split(":").length === 2) nodes.push(url); 165 | } 166 | return nodes; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/helpers.ts: -------------------------------------------------------------------------------- 1 | export function formatBytes(bytes: number) { 2 | if (bytes === 0) { 3 | return ``; 4 | } else if (bytes >= 1000000000) { 5 | return `${Number(bytes / 1000000000).toFixed(1)}GB`; 6 | } else if (bytes >= 1000000) { 7 | return `${Number(bytes / 1000000).toFixed(1)}MB`; 8 | } else if (bytes >= 1000) { 9 | return `${Number(bytes / 1000).toFixed(1)}KB`; 10 | } else { 11 | return `${Number(bytes).toFixed(0)}Bytes`; 12 | } 13 | } 14 | 15 | export function formatSpeeds(bytes: number, seconds: number) { 16 | if (seconds === 0 || bytes === 0) return ""; 17 | return `${formatBytes(bytes)}, ${formatBytes(bytes / seconds)}/s ${Number( 18 | ((bytes / seconds) * 8) / (1024 * 1024) 19 | ).toFixed(1)}Mbps`; 20 | } 21 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | require("source-map-support").install(); 2 | import Spv, { SpvOptions } from "./spv"; 3 | import DbBlocks from "./db_blocks"; 4 | import DbHeaders from "./db_headers"; 5 | import DbMempool from "./db_mempool"; 6 | import DbNodes from "./db_nodes"; 7 | import DbListener from "./db_listener"; 8 | import Worker from "./cluster_worker"; 9 | import Master, { MasterOptions } from "./cluster_master"; 10 | import Listener, { ListenerOptions } from "./listener"; 11 | import Server from "./server"; 12 | 13 | export { 14 | Spv, 15 | SpvOptions, 16 | DbBlocks, 17 | DbHeaders, 18 | DbMempool, 19 | DbNodes, 20 | DbListener, 21 | Worker, 22 | Master, 23 | MasterOptions, 24 | Listener, 25 | ListenerOptions, 26 | Server, 27 | }; 28 | -------------------------------------------------------------------------------- /src/listener.ts: -------------------------------------------------------------------------------- 1 | import EventEmitter from "events"; 2 | import DbBlocks from "./db_blocks"; 3 | import DbHeaders from "./db_headers"; 4 | import DbListener from "./db_listener"; 5 | import Headers from "bsv-headers"; 6 | import Net from "net"; 7 | import path from "path"; 8 | import * as Helpers from "./helpers"; 9 | import * as bsv from "bsv-minimal"; 10 | import { SpvEmitter, SpvEvents } from "./types/SpvEmitter"; 11 | 12 | export interface ListenerOptions { 13 | name: string; 14 | blockHeight: number; 15 | dataDir: string; 16 | ticker: string; 17 | genesisHeader?: string; 18 | disableInterval?: boolean; 19 | DEBUG_MEMORY?: boolean; 20 | multithread?: { 21 | // Optional. Sync only blocks that equals: (((height + index) % threads) === 0). Used for speeding up block syncing using multi-threading 22 | threads: number; // Number of threads 23 | index: number; // #0-#(threads - 1) index of thread 24 | }; 25 | } 26 | 27 | export default class Listener extends (EventEmitter as new () => SpvEmitter) { 28 | ticker: ListenerOptions["ticker"]; 29 | name: ListenerOptions["name"]; 30 | blockHeight: ListenerOptions["blockHeight"]; 31 | disableInterval: ListenerOptions["disableInterval"]; 32 | multithread?: ListenerOptions["multithread"]; 33 | host: string; 34 | port: number; 35 | mempool_txs: boolean; 36 | reconnectTime: number; 37 | db_blocks: DbBlocks; 38 | db_headers: DbHeaders; 39 | db_listener: DbListener; 40 | headers: Headers; // Fix 41 | reconnectTimeout?: NodeJS.Timeout; 42 | interval?: NodeJS.Timeout; 43 | client?: Net.Socket; 44 | txsSeen: number; 45 | txsSize: number; 46 | syncingBlocks: boolean; 47 | 48 | constructor({ 49 | name, // Name of plugin 50 | blockHeight = 0, // Number. Block height you want to start reading from 51 | dataDir, 52 | ticker, 53 | disableInterval = false, 54 | multithread, 55 | genesisHeader, 56 | DEBUG_MEMORY = false, 57 | }: ListenerOptions) { 58 | super(); 59 | this.setMaxListeners(0); 60 | if (DEBUG_MEMORY) { 61 | setInterval(() => { 62 | const m: any = process.memoryUsage(); 63 | console.log( 64 | `Memory: ${Object.keys(m) 65 | .map((key: string) => `${key}: ${Helpers.formatBytes(m[key])}`) 66 | .join(", ")}` 67 | ); 68 | }, 1000 * 60); 69 | } 70 | if (!name) throw Error(`Missing plugin name`); 71 | if (!ticker) throw Error(`Missing ticker!`); 72 | if (!dataDir) throw Error(`Missing dataDir`); 73 | this.ticker = ticker; 74 | this.name = name; 75 | this.mempool_txs = true; 76 | this.blockHeight = blockHeight; 77 | this.reconnectTime = 1; // 1 second 78 | this.host = "localhost"; 79 | this.port = 8080; 80 | this.disableInterval = disableInterval; 81 | this.txsSeen = 0; 82 | this.txsSize = 0; 83 | this.syncingBlocks = false; 84 | this.multithread = multithread; 85 | const startDate = +new Date(); 86 | 87 | const blocksDir = path.join(dataDir, ticker, "blocks"); 88 | const headersDir = path.join(dataDir, ticker, "headers"); 89 | const listenerDir = path.join(dataDir, ticker, "listeners", name); 90 | 91 | console.log(`Loading headers from disk....`); 92 | const headers = new Headers({ genesisHeader }); 93 | this.headers = headers; 94 | this.db_blocks = new DbBlocks({ blocksDir }); 95 | this.db_headers = new DbHeaders({ 96 | headersDir, 97 | headers, 98 | }); 99 | this.db_listener = new DbListener({ listenerDir }); 100 | 101 | this.db_headers.loadHeaders(); 102 | const { height, hash } = this.headers.getTip(); 103 | if (blockHeight < 0) this.blockHeight = height + blockHeight; 104 | console.log( 105 | `Loaded headers in ${ 106 | (+new Date() - startDate) / 1000 107 | } seconds. Latest tip: ${height}, ${hash}` 108 | ); 109 | } 110 | 111 | reconnect() { 112 | this.disconnect(); 113 | this.reconnectTimeout = setTimeout( 114 | () => this.connect({ host: this.host, port: this.port }), 115 | this.reconnectTime * 1000 116 | ); 117 | } 118 | disconnect() { 119 | try { 120 | if (this.client) { 121 | this.client.destroy(); 122 | console.log( 123 | `Disconnected from ${this.host}:${this.port}${ 124 | this.reconnectTime > 0 125 | ? ` at ${new Date().toLocaleString()}. Reconnecting in ${ 126 | this.reconnectTime 127 | } seconds...` 128 | : "" 129 | }` 130 | ); 131 | } 132 | } catch (err) {} 133 | this.client = undefined; 134 | this.syncingBlocks = false; 135 | clearInterval(this.interval); 136 | clearTimeout(this.reconnectTimeout); 137 | } 138 | 139 | onMessage(obj: { command: string; data: any }) { 140 | let { command, data } = obj; 141 | if (command === "headers_saved") { 142 | const { hashes } = data; 143 | for (const hash of hashes) { 144 | try { 145 | const header = this.db_headers.getHeader(hash); 146 | this.headers.addHeader({ header }); 147 | } catch (err) { 148 | console.error(err); 149 | } 150 | } 151 | this.headers.process(); 152 | const tip = this.headers.getTip(); 153 | console.log( 154 | `New headers loaded. Tip ${tip.height}, ${ 155 | tip.hash 156 | } at ${new Date().toLocaleString()}` 157 | ); 158 | } else if (command === "mempool_tx") { 159 | const buf = Buffer.from(data.transaction, "base64"); 160 | const transaction = bsv.Transaction.fromBuffer(buf); 161 | this.txsSeen++; 162 | this.txsSize += transaction.length; 163 | data.transaction = transaction; 164 | } else if (command === "block_reorg") { 165 | const { height, hash } = data; 166 | console.warn( 167 | `Block re-org after height ${height}, ${hash} at ${new Date().toLocaleString()}!` 168 | ); 169 | const from = height + 1; 170 | this.rewindHeight(from); 171 | } else if (command === "block_saved") { 172 | const { height, hash } = data; 173 | // console.log( 174 | // `New block saved ${height}, ${hash} at ${new Date().toLocaleString()}` 175 | // ); 176 | try { 177 | const header = this.db_headers.getHeader(hash); 178 | this.headers.addHeader({ header }); 179 | } catch (err) { 180 | console.error(err); 181 | } 182 | if (height < this.blockHeight) return; // Ignore event if block height is less than listeners start block 183 | } else { 184 | // console.log(`Unknown command: ${JSON.stringify(obj)}`); 185 | } 186 | this.emit(command as keyof SpvEvents, data); 187 | } 188 | 189 | connect({ 190 | host = this.host, 191 | port = this.port, 192 | mempool_txs = true, 193 | }: { 194 | host?: string; 195 | port: number; 196 | mempool_txs?: boolean; 197 | }) { 198 | if (this.client) return; 199 | this.host = host; 200 | this.port = port; 201 | this.mempool_txs = mempool_txs; 202 | 203 | this.txsSeen = 0; 204 | this.txsSize = 0; 205 | 206 | const client = new Net.Socket(); 207 | this.client = client; 208 | client.connect(port, host, () => { 209 | console.log( 210 | `Connected to ${host}:${port} at ${new Date().toLocaleString()}!` 211 | ); 212 | if (mempool_txs) 213 | client.write(`${JSON.stringify({ command: "mempool_txs" })}\n\n`); 214 | this.db_headers.loadHeaders(); 215 | }); 216 | 217 | let messageBuffer = ""; 218 | client.on("data", (message) => { 219 | try { 220 | const msgs = `${messageBuffer}${message.toString()}` 221 | .toString() 222 | .split("\n\n"); 223 | messageBuffer = msgs[msgs.length - 1]; 224 | for (let i = 0; i < msgs.length - 1; i++) { 225 | const msg = msgs[i]; 226 | try { 227 | if (!msg.trim()) continue; 228 | const obj = JSON.parse(msg.trim()); 229 | this.onMessage(obj); 230 | } catch (err) { 231 | console.error(err, message.length, message.toString(), msg); 232 | } 233 | } 234 | } catch (err) { 235 | console.error(err, message.length, message.toString()); 236 | } 237 | }); 238 | 239 | client.on("close", () => { 240 | // console.error( 241 | // `Disconnected! Reconnecting to ${host}:${port} ${this.reconnectTime} in seconds...` 242 | // ); 243 | this.reconnect(); 244 | }); 245 | 246 | client.on("error", (err) => { 247 | // console.error( 248 | // `Socket error! Reconnecting in ${this.reconnectTime} seconds...`, 249 | // err 250 | // ); 251 | this.reconnect(); 252 | }); 253 | 254 | if (!this.disableInterval) { 255 | const REFRESH = 10; // 10 seconds 256 | this.interval = setInterval(() => { 257 | console.log( 258 | `Seen ${ 259 | this.txsSeen 260 | } mempool txs in ${REFRESH} seconds. ${Helpers.formatBytes( 261 | this.txsSize 262 | )}` 263 | ); 264 | this.txsSeen = 0; 265 | this.txsSize = 0; 266 | }, REFRESH * 1000); 267 | } 268 | } 269 | 270 | rewindHeight(fromHeight: number) { 271 | const to = this.headers.getHeight(); 272 | return this.db_listener.delBlocks(fromHeight, to); 273 | } 274 | 275 | async syncBlocks( 276 | callback: ( 277 | params: bsv.BlockStream 278 | ) => 279 | | Promise<{ matches: number; errors?: number }> 280 | | { matches: number; errors?: number } 281 | | void, 282 | options?: { highWaterMark?: number } 283 | ) { 284 | if (this.syncingBlocks) return; 285 | this.syncingBlocks = true; 286 | try { 287 | let processed = 0; 288 | let skipped = 0; 289 | let blockSize = 0; 290 | const date = +new Date(); 291 | let tip = this.headers.getTip(); 292 | // console.log( 293 | // `Syncing blocks from ${this.blockHeight} to ${tip.height}...` 294 | // ); 295 | for (let height = this.blockHeight; height <= tip.height; height++) { 296 | if (this.multithread) { 297 | if ( 298 | (height + this.multithread.index) % this.multithread.threads !== 299 | 0 300 | ) { 301 | continue; 302 | } 303 | } 304 | 305 | const hash = this.headers.getHash(height); 306 | if (this.db_listener.isProcessed(height)) { 307 | if (height < tip.height - 1000) continue; 308 | if (hash === this.db_listener.getHash(height)) continue; 309 | } 310 | const blockDate = +new Date(); 311 | const interval = setInterval(() => { 312 | console.log( 313 | `Syncing block: ${height}/${tip.height} ${hash} in ${ 314 | (+new Date() - blockDate) / 1000 315 | } seconds...` 316 | ); 317 | }, 1000 * 10); 318 | try { 319 | let errors = 0; 320 | let matches = 0; 321 | await this.readBlock( 322 | { height, hash, highWaterMark: options?.highWaterMark }, 323 | async (params: bsv.BlockStream) => { 324 | // Fix any 325 | // if (params.started) { 326 | // console.log( 327 | // `Streaming block ${height}, ${params.header 328 | // .getHash() 329 | // .toString("hex")}...` 330 | // ); 331 | // } 332 | const result = await callback(params); 333 | if (result) { 334 | if (result.matches) matches += result.matches; 335 | if (result.errors) errors += result.errors; 336 | } 337 | if (params.finished) { 338 | const { header, size, txCount, startDate } = params; 339 | const blockHash = header ? header.getHash(true) : ""; 340 | const timer = +new Date() - startDate; 341 | await this.db_listener.markBlockProcessed({ 342 | blockHash, 343 | height, 344 | matches, 345 | errors, 346 | size, 347 | txCount, 348 | timer, 349 | }); 350 | processed++; 351 | blockSize += size; 352 | const seconds = (+new Date() - startDate) / 1000; 353 | console.log( 354 | `Streamed block ${height}/${this.headers.getHeight()} ${blockHash}, ${txCount} txs in ${seconds} seconds. ${Helpers.formatSpeeds( 355 | size, 356 | seconds 357 | )} at ${new Date().toLocaleString()}.` 358 | ); 359 | } 360 | } 361 | ); 362 | } catch (err: any) { 363 | // console.error( 364 | // `Block ${height}/${tip.height} ${hash} not saved: ${err.message}` 365 | // ); 366 | // Block not saved 367 | skipped++; 368 | } 369 | tip = this.headers.getTip(); 370 | clearInterval(interval); 371 | } 372 | const seconds = (+new Date() - date) / 1000; 373 | if (processed) { 374 | console.log( 375 | `Synced ${processed} blocks (${Helpers.formatSpeeds( 376 | blockSize, 377 | seconds 378 | )} in ${seconds} seconds. ${skipped} blocks missing. ${ 379 | this.db_listener.blocksProcessed() - 1 380 | }/${tip.height} blocks processed at ${new Date().toLocaleString()}` 381 | ); 382 | } 383 | this.syncingBlocks = false; 384 | return { skipped, processed, blockSize }; 385 | } catch (err) { 386 | console.error(err); 387 | this.syncingBlocks = false; 388 | throw err; 389 | } 390 | } 391 | getBlockInfo({ height, hash }: { height: number; hash: string }) { 392 | if (!hash) hash = this.headers.getHash(height); 393 | return this.db_listener.getBlockInfo(hash); 394 | } 395 | 396 | async readBlock( 397 | { 398 | hash, 399 | height, 400 | highWaterMark, 401 | }: { height: number; hash: string; highWaterMark?: number }, 402 | callback: (params: bsv.BlockStream) => Promise 403 | ): Promise { 404 | if (!hash) hash = this.headers.getHash(height); 405 | if (typeof height !== "number") { 406 | try { 407 | height = this.headers.getHeight(hash); 408 | } catch (err) {} 409 | } 410 | if (!this.db_blocks.blockExists(hash)) throw Error(`Block not saved`); 411 | return this.db_blocks.streamBlock( 412 | { hash, height, highWaterMark }, 413 | callback 414 | ); 415 | } 416 | } 417 | -------------------------------------------------------------------------------- /src/server.ts: -------------------------------------------------------------------------------- 1 | import express, { Express } from "express"; 2 | import Http from "http"; 3 | import * as Helpers from "./helpers"; 4 | import Listener from "./listener"; 5 | import * as bsv from "bsv-minimal"; 6 | import DbMempool from "./db_mempool"; 7 | import path from "path"; 8 | import cors from "cors"; 9 | 10 | export default class Server extends Listener { 11 | app: Express; 12 | server: Http.Server; 13 | SHOW_LOGS: boolean; 14 | db_mempool?: DbMempool; 15 | MAX_FILE_SIZE: number; 16 | 17 | constructor({ 18 | name, 19 | ticker, 20 | dataDir, 21 | genesisHeader, 22 | MAX_FILE_SIZE = 1024 * 1024 * 500, // 500MB 23 | disableInterval = false, 24 | mempool = true, 25 | DEBUG_MEMORY = false, 26 | }: { 27 | name: string; 28 | ticker: string; 29 | dataDir: string; 30 | mempool?: boolean; 31 | genesisHeader?: string; 32 | MAX_FILE_SIZE?: number; 33 | disableInterval?: boolean; 34 | DEBUG_MEMORY?: boolean; 35 | }) { 36 | super({ 37 | name, 38 | ticker, 39 | blockHeight: -1, 40 | dataDir, 41 | genesisHeader, 42 | disableInterval, 43 | DEBUG_MEMORY, 44 | }); 45 | 46 | if (mempool) { 47 | const mempoolDir = path.join(dataDir, ticker, "mempools", name); 48 | this.db_mempool = new DbMempool({ mempoolDir }); 49 | this.on( 50 | "mempool_tx", 51 | ({ transaction }: { transaction: bsv.Transaction }) => { 52 | this.db_mempool && this.db_mempool.saveTx(transaction); 53 | } 54 | ); 55 | } 56 | 57 | this.SHOW_LOGS = true; 58 | this.MAX_FILE_SIZE = MAX_FILE_SIZE; 59 | const app = express(); 60 | app.use(cors()); 61 | this.app = app; 62 | this.server = Http.createServer(app); 63 | } 64 | 65 | listen({ port = 8081, host = "localhost", SHOW_LOGS = true }) { 66 | this.app.get("/txid/:txid", async (req, res) => { 67 | const { txid } = req.params; 68 | let { pos, len, block, height } = req.query; 69 | try { 70 | if (!txid.match(/^[a-f0-9]{64}$/)) throw Error(`Invalid txid`); 71 | res.setHeader("x-ticker", `${this.ticker}`); 72 | 73 | try { 74 | if (!pos) { 75 | if (!this.db_mempool) throw Error(`Mempool txs not available`); 76 | const { tx, size, time } = await this.db_mempool.getTx(txid); 77 | res.setHeader("x-mempool-time", `${time}`); 78 | res.send(tx.toBuffer()); 79 | this.SHOW_LOGS && 80 | console.log( 81 | `${req.ip} /txid/${txid} ${Helpers.formatBytes( 82 | size 83 | )} from mempool` 84 | ); 85 | } else { 86 | let heightInt = parseInt(`${height}`); 87 | let lenInt = parseInt(`${len}`); 88 | let posInt = parseInt(`${pos}`); 89 | if (!(lenInt > 0 && lenInt <= this.MAX_FILE_SIZE)) 90 | throw Error(`Invalid len`); 91 | if (!(posInt > 0)) throw Error(`Invalid pos`); 92 | if (block && !`${block}`.match(/^[a-f0-9]{64}$/)) 93 | throw Error(`Invalid block`); 94 | if (!block) block = this.headers.getHash(heightInt); 95 | 96 | const { tx } = await this.db_blocks.getTx({ 97 | txid, 98 | block: `${block}`, 99 | pos: posInt, 100 | len: lenInt, 101 | }); 102 | if (!tx) throw Error(`Not found`); 103 | const size = tx.toBuffer().length; 104 | res.setHeader("x-block-hash", `${block}`); 105 | res.setHeader("x-block-pos", `${pos}`); 106 | res.setHeader("x-block-len", `${lenInt}`); 107 | try { 108 | heightInt = this.headers.getHeight(`${block}`); 109 | res.setHeader("x-block-height", `${heightInt}`); 110 | } catch (err) {} 111 | try { 112 | const time = this.db_headers.getHeader(`${block}`).time; 113 | res.setHeader("x-block-time", `${time}`); 114 | } catch (err) {} 115 | res.send(tx.toBuffer()); 116 | this.SHOW_LOGS && 117 | console.log( 118 | `${req.ip} /txid/${txid} ${Helpers.formatBytes( 119 | size 120 | )} from block ${height} ${block}` 121 | ); 122 | } 123 | } catch (err) { 124 | throw Error(`Tx not found`); 125 | } 126 | } catch (err: any) { 127 | this.SHOW_LOGS && 128 | console.error( 129 | `${req.ip} /txid/${txid} error: ${err.message} ${ 130 | pos ? `in block ${height} ${block}, ${pos}, ${len}` : "mempool" 131 | }` 132 | ); 133 | res.status(404).send(err.message); 134 | } 135 | }); 136 | 137 | this.SHOW_LOGS = SHOW_LOGS; 138 | this.server.listen(port, host, () => { 139 | console.log(`Tx server listening on ${host}:${port}`); 140 | }); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/spv.ts: -------------------------------------------------------------------------------- 1 | import Peer from "bsv-p2p"; 2 | import { VersionOptions } from "bsv-p2p/lib/messages/version"; 3 | import { NetAddress } from "bsv-p2p/lib/messages/address"; 4 | import * as bsvMin from "bsv-minimal"; 5 | import Headers from "bsv-headers"; 6 | import { EventEmitter } from "events"; 7 | import DbHeaders from "./db_headers"; 8 | import DbBlocks from "./db_blocks"; 9 | import DbNodes from "./db_nodes"; 10 | import DbListener from "./db_listener"; 11 | import * as path from "path"; 12 | import { SpvEmitter } from "./types/SpvEmitter"; 13 | 14 | export interface SpvOptions { 15 | uid?: string; 16 | ticker: string; 17 | node: string; 18 | dataDir: string; 19 | forceUserAgent?: string; 20 | user_agent?: string; 21 | magic?: string; 22 | genesisHeader?: string; 23 | version?: number; 24 | blocks?: boolean; 25 | mempool?: boolean; 26 | validate?: boolean; 27 | autoReconnect?: boolean; 28 | autoReconnectWait?: number; 29 | timeoutConnect?: number; 30 | versionOptions?: VersionOptions; 31 | invalidBlocks?: string[]; 32 | pruneBlocks?: number; 33 | blockHeight?: number; 34 | DEBUG_LOG?: boolean; 35 | DEBUG_MEMORY?: boolean; 36 | } 37 | 38 | export default class Spv extends (EventEmitter as new () => SpvEmitter) { 39 | id: string; 40 | uid: string; 41 | ticker: string; 42 | node: string; 43 | versionOptions?: VersionOptions; 44 | queue_nodes?: string[]; 45 | saveMempool: boolean; 46 | pruneBlocks: number; 47 | blockHeight: number; 48 | forceUserAgent?: string; 49 | autoReconnect: boolean; 50 | autoReconnectWait?: number; 51 | timeoutConnect: number; 52 | peer?: Peer; 53 | headers: Headers; 54 | db_blocks: DbBlocks; 55 | db_headers: DbHeaders; 56 | db_nodes: DbNodes; 57 | db_listener?: DbListener; 58 | syncingHeaders?: Promise; 59 | syncingBlocks: boolean; 60 | connecting: boolean; 61 | user_agent?: string; 62 | magic?: string; 63 | genesisHeader?: string; 64 | version?: number; 65 | mempool: boolean; 66 | blocks: boolean; 67 | validate: boolean; 68 | dataDir: string; 69 | DEBUG_LOG: boolean; 70 | getPeersTimeout?: NodeJS.Timeout; 71 | peerPingInterval?: NodeJS.Timeout; 72 | 73 | constructor({ 74 | uid, 75 | ticker, 76 | node, 77 | dataDir, 78 | forceUserAgent, 79 | user_agent, 80 | magic, 81 | genesisHeader, 82 | version, 83 | blocks = false, 84 | mempool = false, 85 | validate = true, 86 | autoReconnect = true, 87 | autoReconnectWait, 88 | timeoutConnect = 1000 * 15, // 15 seconds. Shorter than default 89 | versionOptions, 90 | invalidBlocks = [], 91 | pruneBlocks = 0, // Maximum number of new blocks to keep. 0 for keeping all blocks 92 | blockHeight = -10, // Number. Lowest block height syncBlocks will sync to 93 | DEBUG_LOG = false, 94 | }: SpvOptions) { 95 | super(); 96 | this.setMaxListeners(0); 97 | this.uid = uid || `${process.pid}`; 98 | if (!dataDir) throw Error(`Missing dataDir`); 99 | this.dataDir = dataDir; 100 | this.saveMempool = mempool; 101 | this.pruneBlocks = pruneBlocks; 102 | this.blockHeight = blockHeight; 103 | this.autoReconnect = autoReconnect; 104 | this.autoReconnectWait = autoReconnectWait; 105 | this.versionOptions = versionOptions; 106 | this.user_agent = user_agent; 107 | this.magic = magic; 108 | this.version = version; 109 | this.timeoutConnect = timeoutConnect; 110 | this.DEBUG_LOG = DEBUG_LOG; 111 | this.connecting = false; 112 | this.ticker = ticker; 113 | this.node = node; 114 | this.mempool = mempool; 115 | this.blocks = blocks; 116 | this.validate = validate; 117 | this.syncingHeaders; 118 | this.syncingBlocks = false; 119 | this.forceUserAgent = forceUserAgent; 120 | this.id = ""; 121 | this.updateId(); 122 | console.log(`${this.id} Loading headers from disk...`); 123 | const headers = new Headers({ invalidBlocks, genesisHeader }); 124 | this.headers = headers; 125 | const headersDir = path.join(dataDir, ticker, "headers"); 126 | const blocksDir = path.join(dataDir, ticker, "blocks"); 127 | const nodesDir = path.join(dataDir, ticker, "nodes"); 128 | this.db_blocks = new DbBlocks({ blocksDir, readOnly: false }); 129 | this.db_headers = new DbHeaders({ headersDir, headers, readOnly: false }); 130 | this.db_nodes = new DbNodes({ nodesDir, readOnly: false }); 131 | } 132 | 133 | updateId() { 134 | if (!this.isConnected()) { 135 | this.id = `#${this.uid} ${this.mempool ? "mempool " : ""}${ 136 | this.blocks ? "blocks" : "" 137 | }`.trim(); 138 | } else { 139 | this.id = `#${this.uid} ${this.mempool ? "mempool-" : ""}${ 140 | this.blocks ? "blocks-" : "" 141 | }${this.node}`; 142 | } 143 | } 144 | 145 | async addHeaders({ headers }: { headers: bsvMin.Header[] }) { 146 | let newHeaders = 0; 147 | const prevTip = this.headers.getTip(); 148 | headers.map((header) => this.headers.addHeader({ header })); 149 | const lastTip = this.headers.process(); 150 | const newTip = this.headers.getTip(); 151 | const hashes = await this.db_headers.saveHeaders(headers); 152 | if (hashes.length > 0) this.emit("headers_saved", { hashes }); 153 | if (lastTip && lastTip.height && lastTip.height < prevTip.height) { 154 | // Chain re-org detected! 155 | const { height, hash } = lastTip; 156 | this.emit("block_reorg", { height, hash }); 157 | newHeaders += newTip.height - lastTip.height; 158 | this.emit("headers_new", { headers }); 159 | } else { 160 | newHeaders += newTip.height - prevTip.height; 161 | if (newTip.height - prevTip.height > 0) { 162 | this.emit("headers_new", { headers }); 163 | } 164 | } 165 | return newHeaders; 166 | } 167 | 168 | async syncHeaders(): Promise { 169 | if (!this.syncingHeaders) { 170 | this.syncingHeaders = new Promise(async (resolve, reject) => { 171 | try { 172 | let newHeaders = 0; 173 | // try { 174 | let from = this.headers 175 | .getFromHeaderArray() 176 | .map((o) => Buffer.from(o, "hex")); 177 | do { 178 | let lastHash = from[0]; 179 | 180 | if (!this.peer) throw Error(`Not connected`); 181 | const headers: bsvMin.Header[] = await this.peer.getHeaders({ 182 | from, 183 | }); 184 | if (headers.length === 0) break; 185 | newHeaders += await this.addHeaders({ headers }); 186 | const lastHeader = headers[headers.length - 1]; 187 | if (lastHash.toString("hex") === lastHeader.getHash(true)) break; 188 | from = [lastHeader.getHash()]; 189 | } while (true); 190 | // } catch (err: any) { 191 | // const RETRY = 3; 192 | // console.error( 193 | // `${this.id} could not sync headers: ${err.message}. Retrying in ${RETRY} seconds.... (${err.message})` 194 | // ); 195 | // await new Promise((r) => setTimeout(r, RETRY * 1000)); 196 | // } 197 | this.syncingHeaders = undefined; 198 | resolve(newHeaders); 199 | } catch (err) { 200 | // console.error(err); 201 | this.syncingHeaders = undefined; 202 | reject(err); 203 | } 204 | }); 205 | } 206 | return this.syncingHeaders; 207 | } 208 | 209 | async connect(node = this.node) { 210 | if (this.connecting) return; 211 | this.connecting = true; 212 | 213 | this.node = node; 214 | let { 215 | ticker, 216 | dataDir, 217 | versionOptions, 218 | autoReconnect, 219 | autoReconnectWait, 220 | user_agent, 221 | magic, 222 | version, 223 | mempool, 224 | blocks, 225 | validate, 226 | DEBUG_LOG, 227 | } = this; 228 | 229 | this.updateId(); 230 | 231 | if (blocks) { 232 | try { 233 | const listenerDir = path.join( 234 | dataDir, 235 | ticker, 236 | "history", 237 | `node-${node}` 238 | ); 239 | this.db_listener = new DbListener({ listenerDir }); 240 | } catch (err: any) { 241 | console.error(`Skipping db_listener: ${err.message}`); 242 | } 243 | } 244 | 245 | this.peer = new Peer({ 246 | node, 247 | ticker, 248 | autoReconnect, 249 | autoReconnectWait, 250 | start_height: this.headers.getHeight(), 251 | user_agent, 252 | magic: magic ? Buffer.from(magic, "hex") : undefined, 253 | version, 254 | validate, 255 | mempoolTxs: mempool, 256 | DEBUG_LOG, 257 | }); 258 | this.peer.timeoutConnect = this.timeoutConnect; // Shorted connect timeout 259 | let hasConnected = false; 260 | this.peer.on( 261 | "disconnected", 262 | ({ 263 | node, 264 | port, 265 | ticker, 266 | disconnects, 267 | }: { 268 | node: string; 269 | port: number; 270 | ticker: string; 271 | disconnects: number; 272 | }) => { 273 | // clearInterval(this.peerPingInterval); 274 | clearTimeout(this.getPeersTimeout); 275 | if ( 276 | disconnects >= 3 && 277 | !hasConnected && 278 | !this.db_nodes.hasConnected({ node, port }) 279 | ) { 280 | this.db_nodes.blacklist({ node, port }); 281 | } 282 | this.emit("disconnected", { node, ticker, disconnects }); 283 | this.updateId(); 284 | } 285 | ); 286 | this.peer.on("error_message", (params) => { 287 | this.emit("peer_error", params); 288 | }); 289 | 290 | this.peer.on("connected", (params) => { 291 | hasConnected = true; 292 | clearTimeout(this.getPeersTimeout); 293 | this.getPeersTimeout = setTimeout(() => { 294 | try { 295 | const { node, port } = params; 296 | this.db_nodes.connected({ node, port }); // Mark as connected 297 | if (!this.db_nodes.hasSavedSeen()) { 298 | console.log(`${this.id} Getting node peers...`); 299 | this.getNodePeers(); 300 | } 301 | } catch (err) {} 302 | }, 1000 * 65); 303 | clearInterval(this.peerPingInterval); 304 | let failedPings = 0; 305 | this.peerPingInterval = setInterval(async () => { 306 | try { 307 | if (!this.peer) return; 308 | if (this.syncingBlocks || this.syncingHeaders) return; 309 | const response_ms = await this.peer.ping(30); 310 | // console.log( 311 | // `${this.id} Ping responded in ${response_ms / 1000} seconds` 312 | // ); 313 | failedPings = 0; 314 | } catch (err) { 315 | if (!this.peer) return; 316 | if (failedPings++ >= 2) { 317 | // Reconnect 318 | console.error( 319 | `${this.id} Node did not respond to ping ${failedPings} times. Reconnecting...` 320 | ); 321 | this.peer.disconnects = 0; // Reset disconnects 322 | this.disconnect(); 323 | this.connect(); 324 | } 325 | } 326 | }, 1000 * 60 * 1); 327 | this.updateId(); 328 | this.emit("connected", params); 329 | }); 330 | this.peer.on( 331 | "version", 332 | ({ 333 | node, 334 | port, 335 | version, 336 | }: { 337 | node: string; 338 | port: number; 339 | version: VersionOptions; 340 | }) => { 341 | try { 342 | if (typeof this.forceUserAgent === "string") { 343 | const { user_agent } = version; 344 | const expected_user_agent = this.forceUserAgent.toLowerCase(); 345 | if ( 346 | !user_agent || 347 | !user_agent.toLowerCase().includes(expected_user_agent) 348 | ) { 349 | this.db_nodes.blacklist({ node, port }); 350 | this.emit("version_invalid", { 351 | error: `user_agent does not match ${expected_user_agent}: ${user_agent}`, 352 | user_agent, 353 | expected_user_agent, 354 | version, 355 | node, 356 | }); 357 | return this.disconnect(); 358 | } 359 | } 360 | if ( 361 | !version.start_height || 362 | version.start_height < this.headers.getHeight() - 5 363 | ) { 364 | this.db_nodes.blacklist({ node, port }); 365 | this.emit("version_invalid", { 366 | error: `start_height (${ 367 | version.start_height 368 | }) is less than our height ${this.headers.getHeight()}`, 369 | version, 370 | node, 371 | }); 372 | return this.disconnect(); 373 | } 374 | this.emit("version", { version, node }); 375 | } catch (err) { 376 | console.error(err); 377 | } 378 | } 379 | ); 380 | this.peer.on("block_hashes", async ({ hashes }) => { 381 | try { 382 | this.emit("block_seen", { hashes }); 383 | await this.syncHeaders(); 384 | } catch (err) { 385 | // console.error(err); 386 | } 387 | }); 388 | this.peer.on("addr", async ({ addrs }: { addrs: NetAddress[] }) => { 389 | try { 390 | const nodes = await this.db_nodes.saveSeenNodes(addrs); 391 | this.db_nodes.markSavedSeen(); 392 | if (nodes > 0) 393 | console.log(`${this.id} Saved ${nodes} new seen peer nodes`); 394 | } catch (err) { 395 | console.error(err); 396 | } 397 | }); 398 | // this.peer.on("block", async ({ block }) => { 399 | // // Called if streamBlock = false instead of block_chunk 400 | // try { 401 | // if (!block.header) throw Error(`Block missing header`); 402 | // const header = block.header; 403 | // const blockHash = header.getHash(); 404 | // let blockHeight = block.height; 405 | // const txCount = block.txCount || 0; 406 | // const size = block.size; 407 | 408 | // this.addHeaders({ headers: [header] }); 409 | // const success = await this.db_blocks.writeBlockChunk({ 410 | // chunk: block.toBuffer(), 411 | // blockHash, 412 | // started: true, 413 | // finished: true, 414 | // }); 415 | 416 | // const hash = blockHash.toString("hex"); 417 | // try { 418 | // // Height was not included in blocks until v2 419 | // // https://en.bitcoin.it/wiki/BIP_0034 420 | // // More reliable if we calculate the height 421 | // blockHeight = this.headers.getHeight(hash); 422 | // } catch (err) {} 423 | // if (typeof blockHeight !== "number") throw Error(`Missing blockHeight`); 424 | // if (this.db_listener) { 425 | // await this.db_listener.markBlockProcessed({ 426 | // blockHash, 427 | // height: blockHeight, 428 | // txCount, 429 | // size, 430 | // // timer: 0, 431 | // }); 432 | // } 433 | // if (success) { 434 | // this.emit("block_saved", { 435 | // height: blockHeight, 436 | // hash, 437 | // size, 438 | // startDate: +new Date(), 439 | // txCount, 440 | // }); 441 | // } else { 442 | // this.emit("block_already_saved", { 443 | // height: blockHeight, 444 | // hash, 445 | // size, 446 | // startDate: +new Date(), 447 | // txCount, 448 | // }); 449 | // } 450 | 451 | // if (this.pruneBlocks > 0) { 452 | // const tipHeight = 453 | // blockHeight > 0 ? blockHeight : this.headers.getHeight(); 454 | // const height = tipHeight - this.pruneBlocks; 455 | // const hash = this.headers.getHash(height); 456 | // this.db_blocks 457 | // .delBlock(hash) 458 | // .then(() => { 459 | // this.emit("pruned_block", { height, hash }); 460 | // }) 461 | // .catch((err) => console.error(err)); 462 | // } 463 | // } catch (err) { 464 | // console.error(err); 465 | // } 466 | // }); 467 | this.peer.on( 468 | "block_chunk", 469 | async ({ 470 | header, 471 | chunk, 472 | blockHash, 473 | finished, 474 | started, 475 | blockSize, 476 | height: blockHeight, 477 | txCount, 478 | startDate, 479 | }) => { 480 | try { 481 | this.emit("block_chunk", { 482 | header, 483 | chunk, 484 | blockHash, 485 | finished, 486 | started, 487 | size: blockSize, 488 | height: blockHeight, 489 | txCount, 490 | startDate, 491 | }); 492 | if (started) this.addHeaders({ headers: [header] }); 493 | const success = await this.db_blocks.writeBlockChunk({ 494 | chunk, 495 | blockHash, 496 | started, 497 | finished, 498 | }); 499 | if (finished) { 500 | const hash = blockHash.toString("hex"); 501 | try { 502 | // Height was not included in blocks until v2 503 | // https://en.bitcoin.it/wiki/BIP_0034 504 | // More reliable if we calculate the height 505 | blockHeight = this.headers.getHeight(hash); 506 | } catch (err) {} 507 | if (typeof blockHeight !== "number") 508 | throw Error(`Missing blockHeight`); 509 | if (this.db_listener) { 510 | await this.db_listener.markBlockProcessed({ 511 | blockHash, 512 | height: blockHeight, 513 | txCount, 514 | size: blockSize, 515 | timer: +new Date() - startDate, 516 | }); 517 | } 518 | if (success) { 519 | this.emit("block_saved", { 520 | height: blockHeight, 521 | hash, 522 | size: blockSize, 523 | startDate, 524 | txCount, 525 | }); 526 | } else { 527 | this.emit("block_already_saved", { 528 | height: blockHeight, 529 | hash, 530 | size: blockSize, 531 | startDate, 532 | txCount, 533 | }); 534 | } 535 | 536 | if (this.pruneBlocks > 0) { 537 | const tipHeight = 538 | blockHeight > 0 ? blockHeight : this.headers.getHeight(); 539 | const height = tipHeight - this.pruneBlocks; 540 | const hash = this.headers.getHash(height); 541 | this.db_blocks 542 | .delBlock(hash) 543 | .then(() => { 544 | this.emit("pruned_block", { height, hash }); 545 | }) 546 | .catch((err) => console.error(err)); 547 | } 548 | } 549 | } catch (err) { 550 | console.error(err); 551 | } 552 | } 553 | ); 554 | this.peer.on("tx_mempool", ({ tx }) => { 555 | // Mempool transactions 556 | this.emit(`mempool_tx`, { transaction: tx }); 557 | }); 558 | this.peer.on( 559 | "tx_block", 560 | ({ 561 | header, 562 | started, 563 | finished, 564 | blockSize, 565 | height, 566 | txs, 567 | txCount, 568 | startDate, 569 | }) => { 570 | // Block transactions 571 | this.emit(`block_txs`, { 572 | header, 573 | started, 574 | finished, 575 | size: blockSize, 576 | height, 577 | txs, 578 | startDate, 579 | txCount, 580 | }); 581 | } 582 | ); 583 | if (mempool) { 584 | this.peer.fetchMempoolTxs((txids) => { 585 | this.emit(`mempool_txs_seen`, { txids }); 586 | return txids; 587 | }); 588 | } 589 | // if (blocks) { 590 | // this.peer.fetchNewBlocks((hashes) => { 591 | // this.emit(`blocks_seen`, { hashes }); 592 | // return hashes; 593 | // }); 594 | // } 595 | try { 596 | await this.peer.connect(versionOptions); 597 | } catch (err) { 598 | if (!hasConnected && !this.db_nodes.hasConnected({ node })) { 599 | this.db_nodes.blacklist({ node }); 600 | } 601 | // console.error(err); 602 | this.emit("could_not_connect", { ticker, node }); 603 | } 604 | } 605 | disconnect() { 606 | clearInterval(this.peerPingInterval); 607 | clearTimeout(this.getPeersTimeout); 608 | this.syncingBlocks = false; 609 | this.syncingHeaders = undefined; 610 | this.connecting = false; 611 | if (this.peer) { 612 | try { 613 | this.peer.disconnect(); 614 | } catch (err) { 615 | // console.error(err); 616 | } 617 | this.peer = undefined; 618 | } 619 | } 620 | isConnected() { 621 | return this.peer ? this.peer.connected : false; 622 | } 623 | getHeight(hash?: string) { 624 | return this.headers.getHeight(hash); 625 | } 626 | getHash(height: number) { 627 | return this.headers.getHash(height); 628 | } 629 | getTip() { 630 | return this.headers.getTip(); 631 | } 632 | getHeader({ height, hash }: { height: number; hash?: string }) { 633 | if (!hash) hash = this.headers.getHash(height); 634 | if (!hash) throw Error(`Missing hash`); 635 | return this.db_headers.getHeader(hash); 636 | } 637 | async getNodePeers() { 638 | if (!this.peer) throw Error(`Peer not connected`); 639 | // Get list of connected peers 640 | await this.peer.connect(); 641 | return this.peer.getAddr(); 642 | } 643 | 644 | // getMempoolTxs(txids: Buffer[], getTime = true) { 645 | // const { txs, times } = this.db_mempool.getTxs(txids, getTime); 646 | // return { txs, times }; 647 | // } 648 | 649 | async getBlockTx({ 650 | txid, 651 | block, 652 | pos, 653 | len, 654 | }: { 655 | txid?: string; 656 | block: string; 657 | pos: number; 658 | len: number; 659 | }) { 660 | const { tx } = await this.db_blocks.getTx({ txid, block, pos, len }); 661 | return { tx }; 662 | } 663 | 664 | async downloadBlock({ height, hash }: { height: number; hash: string }) { 665 | if (!this.db_blocks.blockExists(hash)) { 666 | if (await this.db_blocks.blockFileExists(hash)) { 667 | this.db_blocks.markBlockSaved(hash); 668 | } else { 669 | if (!this.peer || !this.isConnected()) 670 | throw Error(`Peer not connected`); 671 | this.emit(`block_downloading`, { hash, height }); 672 | await this.peer.getBlock(hash); 673 | return true; 674 | } 675 | } 676 | return false; 677 | } 678 | readBlock( 679 | { 680 | hash, 681 | height, 682 | highWaterMark, 683 | }: { height: number; hash: string; highWaterMark?: number }, 684 | callback: (params: any) => Promise 685 | ) { 686 | if (!hash) hash = this.headers.getHash(height); 687 | if (typeof height !== "number") { 688 | try { 689 | height = this.headers.getHeight(hash); 690 | } catch (err) {} 691 | } 692 | return this.db_blocks.streamBlock( 693 | { hash, height, highWaterMark }, 694 | callback 695 | ); 696 | } 697 | 698 | async syncBlocks() { 699 | let blocksDled = 0; 700 | if (this.syncingBlocks) return blocksDled; 701 | this.syncingBlocks = true; 702 | 703 | let tipHeight = this.headers.getHeight(); 704 | if (typeof this.blockHeight !== "number") { 705 | this.blockHeight = tipHeight + 1; 706 | } else if (this.blockHeight < 0) { 707 | this.blockHeight += tipHeight; 708 | } 709 | 710 | for (let height = this.blockHeight; height <= tipHeight; height++) { 711 | try { 712 | const hash = this.headers.getHash(height); 713 | const blockDownloaded = await this.downloadBlock({ height, hash }); 714 | if (blockDownloaded) blocksDled++; 715 | tipHeight = this.headers.getHeight(); 716 | } catch (err: any) { 717 | if (!this.isConnected()) break; 718 | const RETRY = 3; 719 | console.error( 720 | `${this.id} syncBlocks error: ${err.message}. Retrying in ${RETRY} seconds...` 721 | ); 722 | await new Promise((r) => setTimeout(r, RETRY * 1000)); 723 | height--; // Retry height 724 | } 725 | } 726 | this.syncingBlocks = false; 727 | return blocksDled; 728 | } 729 | 730 | async warningPruneBlocks() { 731 | let prunedCount = 0; 732 | if (!(this.pruneBlocks > 0)) return prunedCount; 733 | const files = await this.db_blocks.getBlocks(); 734 | const pruneHeight = this.headers.getHeight() - this.pruneBlocks; 735 | for (const file of files) { 736 | const hash = file.split(".")[0]; 737 | if (hash.length === 64) { 738 | let height; 739 | try { 740 | height = this.headers.getHeight(hash); 741 | if (height <= pruneHeight) throw Error(`Prune`); 742 | } catch (err) { 743 | await this.db_blocks.delBlock(file); 744 | this.emit("block_pruned", { height, hash }); 745 | prunedCount++; 746 | } 747 | } 748 | } 749 | return prunedCount; 750 | } 751 | 752 | // async pruneMempool(olderThan?: number) { 753 | // const txids = await this.db_mempool.pruneTxs(olderThan); 754 | // if (txids.length > 0) 755 | // this.emit(`mempool_pruned`, { txids, txCount: txids.length }); 756 | // return { txids }; 757 | // } 758 | } 759 | -------------------------------------------------------------------------------- /src/types/SpvEmitter.ts: -------------------------------------------------------------------------------- 1 | import { Header, Transaction, TxIndex } from "bsv-minimal"; 2 | import TypedEventEmitter from "./TypedEventEmitter"; 3 | import { PeerEmitter } from "bsv-p2p/lib/types/PeerEmitter"; 4 | import Peer from "bsv-p2p"; 5 | import { VersionOptions } from "bsv-p2p/lib/messages/version"; 6 | 7 | export type SpvEvents = { 8 | headers_saved: (data: { hashes: Buffer[] }) => void; 9 | block_reorg: (data: { hash: string; height: number }) => void; 10 | headers_new: (data: { headers: Header[] }) => void; 11 | disconnected: (data: { 12 | node: string; 13 | ticker: string; 14 | disconnects: number; 15 | }) => void; 16 | peer_error: (data: { 17 | ticker: string; 18 | node: string; 19 | port: number; 20 | error: Error; 21 | magic: Buffer; 22 | extmsg: boolean; 23 | buffer: Buffer; 24 | }) => void; 25 | connected: (data: { ticker: string; port: number; node: string }) => void; 26 | version_invalid: (data: { 27 | error: string; 28 | user_agent?: string; 29 | expected_user_agent?: string; 30 | version: VersionOptions; 31 | node: string; 32 | }) => void; 33 | version: (data: { version: VersionOptions; node: string }) => void; 34 | block_seen: (data: { hashes: Buffer[] }) => void; 35 | block_chunk: (data: { 36 | header: Header; 37 | chunk: Buffer; 38 | blockHash: Buffer; 39 | finished?: boolean; 40 | started?: boolean; 41 | size: number; 42 | height?: number; 43 | txCount: number; 44 | startDate: number; 45 | }) => void; 46 | block_saved: (data: { 47 | height?: number; 48 | hash: string; 49 | size: number; 50 | startDate: number; 51 | txCount: number; 52 | }) => void; 53 | block_already_saved: (data: { 54 | height?: number; 55 | hash: string; 56 | size: number; 57 | startDate: number; 58 | txCount: number; 59 | }) => void; 60 | pruned_block: (data: { height: number; hash: string }) => void; 61 | block_txs: (data: { 62 | header: Header; 63 | started?: boolean; 64 | finished?: boolean; 65 | size?: number; 66 | height?: number; 67 | txs: TxIndex[]; 68 | startDate?: number; 69 | txCount?: number; 70 | }) => void; 71 | mempool_tx: (data: { transaction: Transaction }) => void; 72 | mempool_txs_seen: (data: { txids: Buffer[] }) => void; 73 | could_not_connect: (data: { ticker: string; node: string }) => void; 74 | block_downloading: (data: { hash: string; height: number }) => void; 75 | block_pruned: (data: { height?: number; hash: string }) => void; 76 | }; 77 | 78 | export type SpvEmitter = TypedEventEmitter; 79 | -------------------------------------------------------------------------------- /src/types/TypedEventEmitter.ts: -------------------------------------------------------------------------------- 1 | // Source: https://github.com/andywer/typed-emitter 2 | 3 | export type EventMap = { 4 | [key: string]: (...args: any[]) => void; 5 | }; 6 | 7 | /** 8 | * Type-safe event emitter. 9 | * 10 | * Use it like this: 11 | * 12 | * ```typescript 13 | * type MyEvents = { 14 | * error: (error: Error) => void; 15 | * message: (from: string, content: string) => void; 16 | * } 17 | * 18 | * const myEmitter = new EventEmitter() as TypedEmitter; 19 | * 20 | * myEmitter.emit("error", "x") // <- Will catch this type error; 21 | * ``` 22 | */ 23 | interface TypedEventEmitter { 24 | addListener(event: E, listener: Events[E]): this; 25 | on(event: E, listener: Events[E]): this; 26 | once(event: E, listener: Events[E]): this; 27 | prependListener(event: E, listener: Events[E]): this; 28 | prependOnceListener( 29 | event: E, 30 | listener: Events[E] 31 | ): this; 32 | 33 | off(event: E, listener: Events[E]): this; 34 | removeAllListeners(event?: E): this; 35 | removeListener(event: E, listener: Events[E]): this; 36 | 37 | emit( 38 | event: E, 39 | ...args: Parameters 40 | ): boolean; 41 | // The sloppy `eventNames()` return type is to mitigate type incompatibilities - see #5 42 | eventNames(): (keyof Events | string | symbol)[]; 43 | rawListeners(event: E): Events[E][]; 44 | listeners(event: E): Events[E][]; 45 | listenerCount(event: E): number; 46 | 47 | getMaxListeners(): number; 48 | setMaxListeners(maxListeners: number): this; 49 | } 50 | 51 | export default TypedEventEmitter; 52 | -------------------------------------------------------------------------------- /tests/cluster.ts: -------------------------------------------------------------------------------- 1 | import { Worker, Master, MasterOptions } from "../src"; 2 | import path from "path"; 3 | import cluster from "cluster"; 4 | 5 | const port = 8080; 6 | 7 | const config: MasterOptions = { 8 | ticker: "BSV", 9 | // nodes: [`18.192.253.59:8333`, `95.217.197.54:8333`], 10 | nodes: [`95.217.197.54:8333`], 11 | // enableIpv6: true, 12 | // nodes: [`18.192.253.59:8333`, `100.11.101.138:8333`, `100.25.248.168:8333`], 13 | forceUserAgent: `Bitcoin SV`, 14 | user_agent: "Bitcoin SV", 15 | invalidBlocks: [ 16 | "00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148", // BTC fork 478559 17 | "0000000000000000004626ff6e3b936941d341c5932ece4357eeccac44e6d56c", // BCH fork 556767 18 | ], 19 | dataDir: path.join(__dirname, "data"), 20 | pruneBlocks: 0, // 0 for no block pruning 21 | blockHeight: -1, // Sync to block height 22 | mempool: 0, // Watch mempool 23 | blocks: 1, // Watch blocks 24 | // DEBUG_LOG: true, // p2p network console.logs 25 | }; 26 | 27 | if (cluster.isWorker) { 28 | const worker = new Worker(); 29 | } else if (cluster.isPrimary) { 30 | const master = new Master(config); 31 | master.startServer({ port }); 32 | } 33 | -------------------------------------------------------------------------------- /tests/listener.ts: -------------------------------------------------------------------------------- 1 | import { Listener } from "../src"; 2 | import path from "path"; 3 | import { BlockStream } from "bsv-minimal"; 4 | 5 | const name = "test-plugin"; 6 | const ticker = "BSV"; 7 | const blockHeight = -10; // Number. If negative then it's number from the tip. 8 | 9 | const dataDir = path.join(__dirname, "data"); 10 | const listener = new Listener({ name, ticker, blockHeight, dataDir }); 11 | 12 | const onBlock = ({ 13 | header, 14 | started, 15 | finished, 16 | size, 17 | height, 18 | txCount, 19 | txRead, 20 | transactions, 21 | startDate, 22 | }: BlockStream) => { 23 | for (const [index, tx, pos, len] of transactions) { 24 | console.log(`#${index} tx ${tx.getTxid()} in block ${height}`); 25 | } 26 | }; 27 | 28 | listener.on("headers_saved", ({ hashes }) => {}); 29 | listener.on("mempool_tx", ({ transaction }) => { 30 | console.log( 31 | `new mempool tx ${transaction.getTxid()} ${transaction.sizeTxOuts.toLocaleString( 32 | "en-US" 33 | )} bytes.` 34 | ); 35 | }); 36 | listener.on("block_reorg", ({ height, hash }) => { 37 | // Re-org after height 38 | }); 39 | listener.on("block_saved", ({ height, hash }) => { 40 | listener.syncBlocks(onBlock); 41 | }); 42 | 43 | listener.connect({ port: 8080 }); 44 | listener.syncBlocks(onBlock); 45 | -------------------------------------------------------------------------------- /tests/mempool.ts: -------------------------------------------------------------------------------- 1 | import { DbMempool } from "../src"; 2 | import path from "path"; 3 | 4 | const mempoolDir = path.join(__dirname, "data/BSV/mempool"); 5 | const db = new DbMempool({ mempoolDir }); 6 | const { txs, size } = db.getTxs(); 7 | const hashes = db.getTxids({}); 8 | 9 | console.log( 10 | `${Object.keys(txs).length} txs (${ 11 | hashes.length 12 | } times) in mempool db. ${Number(size).toLocaleString("en-US")} bytes total` 13 | ); 14 | -------------------------------------------------------------------------------- /tests/server.ts: -------------------------------------------------------------------------------- 1 | import { Server } from "../src"; 2 | import path from "path"; 3 | 4 | const name = "test-server"; 5 | const ticker = "BSV"; 6 | const dataDir = path.join(__dirname, "data"); 7 | 8 | const server = new Server({ name, ticker, dataDir }); 9 | server.connect({ port: 8080 }); 10 | server.listen({ port: 8081 }); 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 5 | "module": "commonjs" /* Specify what module code is generated. */, 6 | "rootDir": "./src" /* Specify the root folder within your source files. */, 7 | "moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */, 8 | "allowJs": true /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */, 9 | "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */, 10 | "sourceMap": true /* Create source map files for emitted JavaScript files. */, 11 | "declarationMap": true, 12 | "outDir": "./lib" /* Specify an output folder for all emitted files. */, 13 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, 14 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 15 | "strict": true /* Enable all strict type-checking options. */, 16 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 17 | }, 18 | "include": ["src/**/*"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /typedoc.js: -------------------------------------------------------------------------------- 1 | // typedoc.js 2 | /** 3 | * @type {import('typedoc').TypeDocOptions} 4 | */ 5 | module.exports = { 6 | entryPoints: ["./src/index.ts"], 7 | plugin: ["typedoc-plugin-markdown"], 8 | readme: "none", 9 | githubPages: false, 10 | gitRevision: "master", 11 | }; 12 | --------------------------------------------------------------------------------