psf-bch-api 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.env-local +4 -0
  2. package/LICENSE.md +8 -0
  3. package/README.md +8 -0
  4. package/apidoc.json +9 -0
  5. package/bin/server.js +183 -0
  6. package/dev-docs/README.md +4 -0
  7. package/dev-docs/creation-prompt.md +34 -0
  8. package/dev-docs/rest2nostr-poxy-api.plan.md +163 -0
  9. package/dev-docs/test-plan-for-rest2nostr.plan.md +161 -0
  10. package/dev-docs/unit-test-prompt.md +13 -0
  11. package/examples/01-create-account.js +67 -0
  12. package/examples/02-read-posts.js +44 -0
  13. package/examples/03-write-post.js +55 -0
  14. package/examples/04-read-alice-posts.js +49 -0
  15. package/examples/05-get-follow-list.js +53 -0
  16. package/examples/06-update-follow-list.js +63 -0
  17. package/examples/07-liking-event.js +59 -0
  18. package/examples/README.md +90 -0
  19. package/index.js +11 -0
  20. package/package.json +37 -0
  21. package/production/docker/Dockerfile +85 -0
  22. package/production/docker/cleanup-images.sh +5 -0
  23. package/production/docker/docker-compose.yml +19 -0
  24. package/production/docker/start-rest2nostr.sh +3 -0
  25. package/src/adapters/full-node-rpc.js +133 -0
  26. package/src/adapters/index.js +217 -0
  27. package/src/adapters/wlogger.js +79 -0
  28. package/src/config/env/common.js +64 -0
  29. package/src/config/env/development.js +7 -0
  30. package/src/config/env/production.js +7 -0
  31. package/src/config/index.js +14 -0
  32. package/src/controllers/index.js +56 -0
  33. package/src/controllers/rest-api/full-node/blockchain/controller.js +553 -0
  34. package/src/controllers/rest-api/full-node/blockchain/index.js +66 -0
  35. package/src/controllers/rest-api/index.js +55 -0
  36. package/src/controllers/timer-controller.js +72 -0
  37. package/src/entities/event.js +71 -0
  38. package/src/use-cases/full-node-blockchain-use-cases.js +134 -0
  39. package/src/use-cases/index.js +29 -0
  40. package/test/integration/api/event-integration.js +250 -0
  41. package/test/integration/api/req-integration.js +173 -0
  42. package/test/integration/api/subscription-integration.js +198 -0
  43. package/test/integration/use-cases/manage-subscription-integration.js +163 -0
  44. package/test/integration/use-cases/publish-event-integration.js +104 -0
  45. package/test/integration/use-cases/query-events-integration.js +95 -0
  46. package/test/unit/adapters/full-node-rpc-unit.js +122 -0
  47. package/test/unit/bin/server-unit.js +63 -0
  48. package/test/unit/controllers/blockchain-controller-unit.js +215 -0
  49. package/test/unit/controllers/rest-api-index-unit.js +85 -0
  50. package/test/unit/entities/event-unit.js +139 -0
  51. package/test/unit/mocks/controller-mocks.js +98 -0
  52. package/test/unit/mocks/event-mocks.js +194 -0
  53. package/test/unit/use-cases/full-node-blockchain-use-cases-unit.js +137 -0
@@ -0,0 +1,553 @@
1
+ /*
2
+ REST API Controller for the /full-node/blockchain routes.
3
+ */
4
+
5
+ import wlogger from '../../../../adapters/wlogger.js'
6
+
7
+ class BlockchainRESTController {
8
+ constructor (localConfig = {}) {
9
+ this.adapters = localConfig.adapters
10
+ if (!this.adapters) {
11
+ throw new Error(
12
+ 'Instance of Adapters library required when instantiating Blockchain REST Controller.'
13
+ )
14
+ }
15
+
16
+ this.useCases = localConfig.useCases
17
+ if (!this.useCases || !this.useCases.blockchain) {
18
+ throw new Error(
19
+ 'Instance of Blockchain use cases required when instantiating Blockchain REST Controller.'
20
+ )
21
+ }
22
+
23
+ this.blockchainUseCases = this.useCases.blockchain
24
+
25
+ // Bind functions
26
+ this.root = this.root.bind(this)
27
+ this.getBestBlockHash = this.getBestBlockHash.bind(this)
28
+ this.getBlockchainInfo = this.getBlockchainInfo.bind(this)
29
+ this.getBlockCount = this.getBlockCount.bind(this)
30
+ this.getBlockHeaderSingle = this.getBlockHeaderSingle.bind(this)
31
+ this.getBlockHeaderBulk = this.getBlockHeaderBulk.bind(this)
32
+ this.getChainTips = this.getChainTips.bind(this)
33
+ this.getDifficulty = this.getDifficulty.bind(this)
34
+ this.getMempoolEntrySingle = this.getMempoolEntrySingle.bind(this)
35
+ this.getMempoolEntryBulk = this.getMempoolEntryBulk.bind(this)
36
+ this.getMempoolAncestorsSingle = this.getMempoolAncestorsSingle.bind(this)
37
+ this.getMempoolInfo = this.getMempoolInfo.bind(this)
38
+ this.getRawMempool = this.getRawMempool.bind(this)
39
+ this.getTxOut = this.getTxOut.bind(this)
40
+ this.getTxOutPost = this.getTxOutPost.bind(this)
41
+ this.getTxOutProofSingle = this.getTxOutProofSingle.bind(this)
42
+ this.getTxOutProofBulk = this.getTxOutProofBulk.bind(this)
43
+ this.verifyTxOutProofSingle = this.verifyTxOutProofSingle.bind(this)
44
+ this.verifyTxOutProofBulk = this.verifyTxOutProofBulk.bind(this)
45
+ this.getBlock = this.getBlock.bind(this)
46
+ this.getBlockHash = this.getBlockHash.bind(this)
47
+ this.handleError = this.handleError.bind(this)
48
+ }
49
+
50
+ /**
51
+ * @api {get} /full-node/blockchain/ Service status
52
+ * @apiName BlockchainRoot
53
+ * @apiGroup Blockchain
54
+ *
55
+ * @apiDescription Returns the status of the blockchain service.
56
+ *
57
+ * @apiSuccess {String} status Service identifier
58
+ */
59
+ async root (req, res) {
60
+ return res.status(200).json({ status: 'blockchain' })
61
+ }
62
+
63
+ /**
64
+ * @api {get} /full-node/blockchain/getBestBlockHash Get best block hash
65
+ * @apiName GetBestBlockHash
66
+ * @apiGroup Blockchain
67
+ * @apiDescription Returns the hash of the best (tip) block in the longest block chain.
68
+ *
69
+ * @apiExample Example usage:
70
+ * curl -X GET "https://api.fullstack.cash/v5/blockchain/getBestBlockHash" -H "accept: application/json"
71
+ *
72
+ * @apiSuccess {String} bestBlockHash Hash of the best block
73
+ */
74
+ async getBestBlockHash (req, res) {
75
+ try {
76
+ const result = await this.blockchainUseCases.getBestBlockHash()
77
+ return res.status(200).json(result)
78
+ } catch (err) {
79
+ return this.handleError(err, res)
80
+ }
81
+ }
82
+
83
+ /**
84
+ * @api {get} /full-node/blockchain/getBlockchainInfo Get blockchain info
85
+ * @apiName GetBlockchainInfo
86
+ * @apiGroup Blockchain
87
+ * @apiDescription Returns various state info regarding blockchain processing.
88
+ */
89
+ async getBlockchainInfo (req, res) {
90
+ try {
91
+ const result = await this.blockchainUseCases.getBlockchainInfo()
92
+ return res.status(200).json(result)
93
+ } catch (err) {
94
+ return this.handleError(err, res)
95
+ }
96
+ }
97
+
98
+ /**
99
+ * @api {get} /full-node/blockchain/getBlockCount Get block count
100
+ * @apiName GetBlockCount
101
+ * @apiGroup Blockchain
102
+ * @apiDescription Returns the number of blocks in the longest blockchain.
103
+ */
104
+ async getBlockCount (req, res) {
105
+ try {
106
+ const result = await this.blockchainUseCases.getBlockCount()
107
+ return res.status(200).json(result)
108
+ } catch (err) {
109
+ return this.handleError(err, res)
110
+ }
111
+ }
112
+
113
+ /**
114
+ * @api {get} /full-node/blockchain/getBlockHeader/:hash Get single block header
115
+ * @apiName GetSingleBlockHeader
116
+ * @apiGroup Blockchain
117
+ * @apiDescription Returns serialized block header data.
118
+ *
119
+ * @apiParam {String} hash Block hash
120
+ * @apiParam {Boolean} verbose Return verbose data (default false)
121
+ */
122
+ async getBlockHeaderSingle (req, res) {
123
+ try {
124
+ const hash = req.params.hash
125
+ if (!hash) {
126
+ return res.status(400).json({ error: 'hash can not be empty' })
127
+ }
128
+
129
+ const verbose = req.query.verbose?.toString() === 'true'
130
+ const result = await this.blockchainUseCases.getBlockHeader({ hash, verbose })
131
+
132
+ return res.status(200).json(result)
133
+ } catch (err) {
134
+ return this.handleError(err, res)
135
+ }
136
+ }
137
+
138
+ /**
139
+ * @api {post} /full-node/blockchain/getBlockHeader Get multiple block headers
140
+ * @apiName GetBulkBlockHeader
141
+ * @apiGroup Blockchain
142
+ * @apiDescription Returns serialized block header data for multiple hashes.
143
+ *
144
+ * @apiParam {String[]} hashes Block hashes
145
+ * @apiParam {Boolean} verbose Return verbose data (default false)
146
+ */
147
+ async getBlockHeaderBulk (req, res) {
148
+ try {
149
+ const hashes = req.body.hashes
150
+ const verbose = !!req.body.verbose
151
+
152
+ if (!Array.isArray(hashes)) {
153
+ return res.status(400).json({
154
+ error: 'hashes needs to be an array. Use GET for single hash.'
155
+ })
156
+ }
157
+
158
+ if (!this.adapters.fullNode.validateArraySize(hashes.length, { isProUser: Boolean(req.locals?.proLimit) })) {
159
+ return res.status(400).json({ error: 'Array too large.' })
160
+ }
161
+
162
+ for (const hash of hashes) {
163
+ if (!hash || hash.length !== 64) {
164
+ return res.status(400).json({ error: `This is not a hash: ${hash}` })
165
+ }
166
+ }
167
+
168
+ const result = await this.blockchainUseCases.getBlockHeaders({ hashes, verbose })
169
+ return res.status(200).json(result)
170
+ } catch (err) {
171
+ return this.handleError(err, res)
172
+ }
173
+ }
174
+
175
+ /**
176
+ * @api {get} /full-node/blockchain/getChainTips Get chain tips
177
+ * @apiName GetChainTips
178
+ * @apiGroup Blockchain
179
+ * @apiDescription Returns information about known tips in the block tree.
180
+ */
181
+ async getChainTips (req, res) {
182
+ try {
183
+ const result = await this.blockchainUseCases.getChainTips()
184
+ return res.status(200).json(result)
185
+ } catch (err) {
186
+ return this.handleError(err, res)
187
+ }
188
+ }
189
+
190
+ /**
191
+ * @api {get} /full-node/blockchain/getDifficulty Get difficulty
192
+ * @apiName GetDifficulty
193
+ * @apiGroup Blockchain
194
+ * @apiDescription Returns the current difficulty value.
195
+ */
196
+ async getDifficulty (req, res) {
197
+ try {
198
+ const result = await this.blockchainUseCases.getDifficulty()
199
+ return res.status(200).json(result)
200
+ } catch (err) {
201
+ return this.handleError(err, res)
202
+ }
203
+ }
204
+
205
+ /**
206
+ * @api {get} /full-node/blockchain/getMempoolEntry/:txid Get single mempool entry
207
+ * @apiName GetMempoolEntry
208
+ * @apiGroup Blockchain
209
+ * @apiDescription Returns mempool data for a transaction.
210
+ */
211
+ async getMempoolEntrySingle (req, res) {
212
+ try {
213
+ const txid = req.params.txid
214
+ if (!txid) {
215
+ return res.status(400).json({ error: 'txid can not be empty' })
216
+ }
217
+
218
+ const result = await this.blockchainUseCases.getMempoolEntry({ txid })
219
+ return res.status(200).json(result)
220
+ } catch (err) {
221
+ return this.handleError(err, res)
222
+ }
223
+ }
224
+
225
+ /**
226
+ * @api {post} /full-node/blockchain/getMempoolEntry Get bulk mempool entry
227
+ * @apiName GetMempoolEntryBulk
228
+ * @apiGroup Blockchain
229
+ * @apiDescription Returns mempool data for multiple transactions.
230
+ */
231
+ async getMempoolEntryBulk (req, res) {
232
+ try {
233
+ const txids = req.body.txids
234
+
235
+ if (!Array.isArray(txids)) {
236
+ return res.status(400).json({
237
+ error: 'txids needs to be an array. Use GET for single txid.'
238
+ })
239
+ }
240
+
241
+ if (!this.adapters.fullNode.validateArraySize(txids.length, { isProUser: Boolean(req.locals?.proLimit) })) {
242
+ return res.status(400).json({ error: 'Array too large.' })
243
+ }
244
+
245
+ for (const txid of txids) {
246
+ if (!txid || txid.length !== 64) {
247
+ return res.status(400).json({ error: 'This is not a txid' })
248
+ }
249
+ }
250
+
251
+ const result = await this.blockchainUseCases.getMempoolEntries({ txids })
252
+ return res.status(200).json(result)
253
+ } catch (err) {
254
+ return this.handleError(err, res)
255
+ }
256
+ }
257
+
258
+ /**
259
+ * @api {get} /full-node/blockchain/getMempoolAncestors/:txid Get mempool ancestors
260
+ * @apiName GetMempoolAncestors
261
+ * @apiGroup Blockchain
262
+ * @apiDescription Returns mempool ancestor data for a transaction.
263
+ */
264
+ async getMempoolAncestorsSingle (req, res) {
265
+ try {
266
+ const txid = req.params.txid
267
+ if (!txid) {
268
+ return res.status(400).json({ error: 'txid can not be empty' })
269
+ }
270
+
271
+ let verbose = false
272
+ if (req.query.verbose && req.query.verbose.toString() === 'true') {
273
+ verbose = true
274
+ }
275
+
276
+ const result = await this.blockchainUseCases.getMempoolAncestors({ txid, verbose })
277
+ return res.status(200).json(result)
278
+ } catch (err) {
279
+ return this.handleError(err, res)
280
+ }
281
+ }
282
+
283
+ /**
284
+ * @api {get} /full-node/blockchain/getMempoolInfo Get mempool info
285
+ * @apiName GetMempoolInfo
286
+ * @apiGroup Blockchain
287
+ * @apiDescription Returns details on the state of the mempool.
288
+ */
289
+ async getMempoolInfo (req, res) {
290
+ try {
291
+ const result = await this.blockchainUseCases.getMempoolInfo()
292
+ return res.status(200).json(result)
293
+ } catch (err) {
294
+ return this.handleError(err, res)
295
+ }
296
+ }
297
+
298
+ /**
299
+ * @api {get} /full-node/blockchain/getRawMempool Get raw mempool
300
+ * @apiName GetRawMempool
301
+ * @apiGroup Blockchain
302
+ * @apiDescription Returns all transaction ids in the mempool.
303
+ *
304
+ * @apiParam {Boolean} verbose Return verbose data (default false)
305
+ */
306
+ async getRawMempool (req, res) {
307
+ try {
308
+ const verbose = req.query.verbose === 'true'
309
+ const result = await this.blockchainUseCases.getRawMempool({ verbose })
310
+ return res.status(200).json(result)
311
+ } catch (err) {
312
+ return this.handleError(err, res)
313
+ }
314
+ }
315
+
316
+ /**
317
+ * @api {get} /full-node/blockchain/getTxOut/:txid/:n Get transaction output
318
+ * @apiName GetTxOut
319
+ * @apiGroup Blockchain
320
+ * @apiDescription Returns details about an unspent transaction output.
321
+ */
322
+ async getTxOut (req, res) {
323
+ try {
324
+ const txid = req.params.txid
325
+ if (!txid) {
326
+ return res.status(400).json({ error: 'txid can not be empty' })
327
+ }
328
+
329
+ const nRaw = req.params.n
330
+ if (nRaw === undefined || nRaw === '') {
331
+ return res.status(400).json({ error: 'n can not be empty' })
332
+ }
333
+
334
+ const n = parseInt(nRaw)
335
+ const includeMempool = req.query.includeMempool === 'true'
336
+
337
+ const result = await this.blockchainUseCases.getTxOut({
338
+ txid,
339
+ n,
340
+ includeMempool
341
+ })
342
+
343
+ return res.status(200).json(result)
344
+ } catch (err) {
345
+ return this.handleError(err, res)
346
+ }
347
+ }
348
+
349
+ /**
350
+ * @api {post} /full-node/blockchain/getTxOut Validate a UTXO
351
+ * @apiName GetTxOutPost
352
+ * @apiGroup Blockchain
353
+ * @apiDescription Returns details about an unspent transaction output.
354
+ */
355
+ async getTxOutPost (req, res) {
356
+ try {
357
+ const txid = req.body.txid
358
+ if (!txid) {
359
+ return res.status(400).json({ error: 'txid can not be empty' })
360
+ }
361
+
362
+ const voutRaw = req.body.vout
363
+ if (voutRaw === undefined || voutRaw === '') {
364
+ return res.status(400).json({ error: 'vout can not be empty' })
365
+ }
366
+
367
+ const n = parseInt(voutRaw)
368
+ const mempool = req.body.mempool !== undefined ? !!req.body.mempool : true
369
+
370
+ const result = await this.blockchainUseCases.getTxOut({
371
+ txid,
372
+ n,
373
+ includeMempool: mempool
374
+ })
375
+
376
+ return res.status(200).json(result)
377
+ } catch (err) {
378
+ return this.handleError(err, res)
379
+ }
380
+ }
381
+
382
+ /**
383
+ * @api {get} /full-node/blockchain/getTxOutProof/:txid Get TxOut proof
384
+ * @apiName GetTxOutProofSingle
385
+ * @apiGroup Blockchain
386
+ * @apiDescription Returns a hex-encoded proof that the transaction was included in a block.
387
+ */
388
+ async getTxOutProofSingle (req, res) {
389
+ try {
390
+ const txid = req.params.txid
391
+ if (!txid) {
392
+ return res.status(400).json({ error: 'txid can not be empty' })
393
+ }
394
+
395
+ const result = await this.blockchainUseCases.getTxOutProof({ txid })
396
+ return res.status(200).json(result)
397
+ } catch (err) {
398
+ return this.handleError(err, res)
399
+ }
400
+ }
401
+
402
+ /**
403
+ * @api {post} /full-node/blockchain/getTxOutProof Get TxOut proofs
404
+ * @apiName GetTxOutProofBulk
405
+ * @apiGroup Blockchain
406
+ * @apiDescription Returns hex-encoded proofs for transactions.
407
+ */
408
+ async getTxOutProofBulk (req, res) {
409
+ try {
410
+ const txids = req.body.txids
411
+
412
+ if (!Array.isArray(txids)) {
413
+ return res.status(400).json({
414
+ error: 'txids needs to be an array. Use GET for single txid.'
415
+ })
416
+ }
417
+
418
+ if (!this.adapters.fullNode.validateArraySize(txids.length, { isProUser: Boolean(req.locals?.proLimit) })) {
419
+ return res.status(400).json({ error: 'Array too large.' })
420
+ }
421
+
422
+ for (const txid of txids) {
423
+ if (!txid || txid.length !== 64) {
424
+ return res.status(400).json({
425
+ error: `Invalid txid. Double check your txid is valid: ${txid}`
426
+ })
427
+ }
428
+ }
429
+
430
+ const result = await this.blockchainUseCases.getTxOutProofs({ txids })
431
+ return res.status(200).json(result)
432
+ } catch (err) {
433
+ return this.handleError(err, res)
434
+ }
435
+ }
436
+
437
+ /**
438
+ * @api {get} /full-node/blockchain/verifyTxOutProof/:proof Verify TxOut proof
439
+ * @apiName VerifyTxOutProofSingle
440
+ * @apiGroup Blockchain
441
+ * @apiDescription Verifies a hex-encoded proof was included in a block.
442
+ */
443
+ async verifyTxOutProofSingle (req, res) {
444
+ try {
445
+ const proof = req.params.proof
446
+ if (!proof) {
447
+ return res.status(400).json({ error: 'proof can not be empty' })
448
+ }
449
+
450
+ const result = await this.blockchainUseCases.verifyTxOutProof({ proof })
451
+ return res.status(200).json(result)
452
+ } catch (err) {
453
+ return this.handleError(err, res)
454
+ }
455
+ }
456
+
457
+ /**
458
+ * @api {post} /full-node/blockchain/verifyTxOutProof Verify TxOut proofs
459
+ * @apiName VerifyTxOutProofBulk
460
+ * @apiGroup Blockchain
461
+ * @apiDescription Verifies hex-encoded proofs were included in blocks.
462
+ */
463
+ async verifyTxOutProofBulk (req, res) {
464
+ try {
465
+ const proofs = req.body.proofs
466
+
467
+ if (!Array.isArray(proofs)) {
468
+ return res.status(400).json({
469
+ error: 'proofs needs to be an array. Use GET for single proof.'
470
+ })
471
+ }
472
+
473
+ if (!this.adapters.fullNode.validateArraySize(proofs.length, { isProUser: Boolean(req.locals?.proLimit) })) {
474
+ return res.status(400).json({ error: 'Array too large.' })
475
+ }
476
+
477
+ for (const proof of proofs) {
478
+ if (!proof) {
479
+ return res.status(400).json({ error: `proof can not be empty: ${proof}` })
480
+ }
481
+ }
482
+
483
+ const result = await this.blockchainUseCases.verifyTxOutProofs({ proofs })
484
+ const flattened = result.map(entry => Array.isArray(entry) ? entry[0] : entry)
485
+
486
+ return res.status(200).json(flattened)
487
+ } catch (err) {
488
+ return this.handleError(err, res)
489
+ }
490
+ }
491
+
492
+ /**
493
+ * @api {post} /full-node/blockchain/getBlock Get block details
494
+ * @apiName GetBlock
495
+ * @apiGroup Blockchain
496
+ * @apiDescription Returns block details for a hash.
497
+ */
498
+ async getBlock (req, res) {
499
+ try {
500
+ const blockhash = req.body.blockhash
501
+ if (!blockhash) {
502
+ return res.status(400).json({ error: 'blockhash can not be empty' })
503
+ }
504
+
505
+ let verbosity = req.body.verbosity
506
+ if (verbosity === undefined || verbosity === null) {
507
+ verbosity = 1
508
+ }
509
+
510
+ const result = await this.blockchainUseCases.getBlock({
511
+ blockhash,
512
+ verbosity
513
+ })
514
+
515
+ return res.status(200).json(result)
516
+ } catch (err) {
517
+ return this.handleError(err, res)
518
+ }
519
+ }
520
+
521
+ /**
522
+ * @api {get} /full-node/blockchain/getBlockHash/:height Get block hash
523
+ * @apiName GetBlockHash
524
+ * @apiGroup Blockchain
525
+ * @apiDescription Returns the hash of a block by height.
526
+ */
527
+ async getBlockHash (req, res) {
528
+ try {
529
+ const heightRaw = req.params.height
530
+ if (!heightRaw) {
531
+ return res.status(400).json({ error: 'height can not be empty' })
532
+ }
533
+
534
+ const height = parseInt(heightRaw)
535
+ const result = await this.blockchainUseCases.getBlockHash({ height })
536
+
537
+ return res.status(200).json(result)
538
+ } catch (err) {
539
+ return this.handleError(err, res)
540
+ }
541
+ }
542
+
543
+ handleError (err, res) {
544
+ wlogger.error('Error in BlockchainRESTController:', err)
545
+
546
+ const status = err.status || 500
547
+ const message = err.message || 'Internal server error'
548
+
549
+ return res.status(status).json({ error: message })
550
+ }
551
+ }
552
+
553
+ export default BlockchainRESTController
@@ -0,0 +1,66 @@
1
+ /*
2
+ REST API router for /full-node/blockchain routes.
3
+ */
4
+
5
+ import express from 'express'
6
+ import BlockchainRESTController from './controller.js'
7
+
8
+ class BlockchainRouter {
9
+ constructor (localConfig = {}) {
10
+ this.adapters = localConfig.adapters
11
+ if (!this.adapters) {
12
+ throw new Error(
13
+ 'Instance of Adapters library required when instantiating Blockchain REST Router.'
14
+ )
15
+ }
16
+
17
+ this.useCases = localConfig.useCases
18
+ if (!this.useCases) {
19
+ throw new Error(
20
+ 'Instance of Use Cases library required when instantiating Blockchain REST Router.'
21
+ )
22
+ }
23
+
24
+ const dependencies = {
25
+ adapters: this.adapters,
26
+ useCases: this.useCases
27
+ }
28
+
29
+ this.blockchainController = new BlockchainRESTController(dependencies)
30
+
31
+ this.baseUrl = '/full-node/blockchain'
32
+ this.router = express.Router()
33
+ }
34
+
35
+ attach (app) {
36
+ if (!app) {
37
+ throw new Error('Must pass app object when attaching REST API controllers.')
38
+ }
39
+
40
+ this.router.get('/', this.blockchainController.root)
41
+ this.router.get('/getBestBlockHash', this.blockchainController.getBestBlockHash)
42
+ this.router.get('/getBlockchainInfo', this.blockchainController.getBlockchainInfo)
43
+ this.router.get('/getBlockCount', this.blockchainController.getBlockCount)
44
+ this.router.get('/getBlockHeader/:hash', this.blockchainController.getBlockHeaderSingle)
45
+ this.router.post('/getBlockHeader', this.blockchainController.getBlockHeaderBulk)
46
+ this.router.get('/getChainTips', this.blockchainController.getChainTips)
47
+ this.router.get('/getDifficulty', this.blockchainController.getDifficulty)
48
+ this.router.get('/getMempoolEntry/:txid', this.blockchainController.getMempoolEntrySingle)
49
+ this.router.post('/getMempoolEntry', this.blockchainController.getMempoolEntryBulk)
50
+ this.router.get('/getMempoolAncestors/:txid', this.blockchainController.getMempoolAncestorsSingle)
51
+ this.router.get('/getMempoolInfo', this.blockchainController.getMempoolInfo)
52
+ this.router.get('/getRawMempool', this.blockchainController.getRawMempool)
53
+ this.router.get('/getTxOut/:txid/:n', this.blockchainController.getTxOut)
54
+ this.router.post('/getTxOut', this.blockchainController.getTxOutPost)
55
+ this.router.get('/getTxOutProof/:txid', this.blockchainController.getTxOutProofSingle)
56
+ this.router.post('/getTxOutProof', this.blockchainController.getTxOutProofBulk)
57
+ this.router.get('/verifyTxOutProof/:proof', this.blockchainController.verifyTxOutProofSingle)
58
+ this.router.post('/verifyTxOutProof', this.blockchainController.verifyTxOutProofBulk)
59
+ this.router.post('/getBlock', this.blockchainController.getBlock)
60
+ this.router.get('/getBlockHash/:height', this.blockchainController.getBlockHash)
61
+
62
+ app.use(this.baseUrl, this.router)
63
+ }
64
+ }
65
+
66
+ export default BlockchainRouter
@@ -0,0 +1,55 @@
1
+ /*
2
+ This index file for the Clean Architecture Controllers loads dependencies,
3
+ creates instances, and attaches the controller to REST API endpoints for
4
+ Express.
5
+ */
6
+
7
+ // Local libraries
8
+ // import EventRouter from './event/index.js'
9
+ // import ReqRouter from './req/index.js'
10
+ import BlockchainRouter from './full-node/blockchain/index.js'
11
+ import config from '../../config/index.js'
12
+
13
+ class RESTControllers {
14
+ constructor (localConfig = {}) {
15
+ // Dependency Injection.
16
+ this.adapters = localConfig.adapters
17
+ if (!this.adapters) {
18
+ throw new Error(
19
+ 'Instance of Adapters library required when instantiating REST Controller libraries.'
20
+ )
21
+ }
22
+ this.useCases = localConfig.useCases
23
+ if (!this.useCases) {
24
+ throw new Error(
25
+ 'Instance of Use Cases library required when instantiating REST Controller libraries.'
26
+ )
27
+ }
28
+
29
+ // Bind 'this' object to all subfunctions.
30
+ this.attachRESTControllers = this.attachRESTControllers.bind(this)
31
+
32
+ // Encapsulate dependencies
33
+ this.config = config
34
+ }
35
+
36
+ attachRESTControllers (app) {
37
+ const dependencies = {
38
+ adapters: this.adapters,
39
+ useCases: this.useCases
40
+ }
41
+
42
+ // Attach the REST API Controllers associated with the /event route
43
+ // const eventRouter = new EventRouter(dependencies)
44
+ // eventRouter.attach(app)
45
+
46
+ // Attach the REST API Controllers associated with the /req route
47
+ // const reqRouter = new ReqRouter(dependencies)
48
+ // reqRouter.attach(app)
49
+
50
+ const blockchainRouter = new BlockchainRouter(dependencies)
51
+ blockchainRouter.attach(app)
52
+ }
53
+ }
54
+
55
+ export default RESTControllers