Compare commits

...
309 Commits
Author SHA1 Message Date
Chris Troutner c74b675bc9 Merge branch 'master' into nft-docs-fixes 2021-11-13 06:47:17 -08:00
Chris Troutner d0129c01f3 Merge pull request #196 from Permissionless-Software-Foundation/temp-documentation
Temp documentation
2021-11-13 06:46:59 -08:00
Chris Troutner ad40607e5b Merge pull request #186 from mr-zwets/documentation-fix
small fixes documentation
2021-11-13 06:43:55 -08:00
Chris Troutner a4f0ee6e42 Merge branch 'master' into documentation-fix 2021-11-13 06:42:57 -08:00
Chris Troutner 0b70ea108b Merge pull request #195 from Permissionless-Software-Foundation/temp-documentation
Temp documentation
2021-11-13 06:42:05 -08:00
Chris Troutner cef9a3cf52 Merge pull request #194 from mr-zwets/documentation
clarified some of the documentation
2021-11-13 06:38:04 -08:00
mathi c216f6c075 clarified some of the documentation 2021-11-13 11:37:56 +01:00
Chris Troutner b53f6bcfc6 Merge pull request #193 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Moving util.js integration tests to bchn specific folder
2021-11-10 18:16:45 -08:00
Chris Troutner cbc834cc1a fix(tests): Moving util.js integration tests to bchn specific folder 2021-11-10 17:32:17 -08:00
Chris Troutner 1cb5fe069b Merge pull request #192 from Permissionless-Software-Foundation/ct-unstable
Created Transaction.get3() to help with SLP indexer
2021-11-03 10:21:21 -07:00
Chris Troutner 425e2cbbc1 fix(Transaction.get3()): Created Transaction.get3() 2021-11-03 10:16:38 -07:00
Chris Troutner 9e92f77523 Making progress on Transaction.get3() 2021-11-03 07:53:56 -07:00
Chris Troutner dc85bb4cfe Making progress on Transaction.get3() 2021-11-03 07:53:42 -07:00
Chris Troutner 92960b6957 Merge branch 'master' into ct-unstable 2021-11-02 20:37:07 -07:00
Chris Troutner 6f34ea286a Merge pull request #191 from Permissionless-Software-Foundation/fix-bip32
fix(bip32-util): Moving to @chris.troutner/bip32-util
2021-11-01 06:21:49 -07:00
Chris Troutner 26f39023c4 fix(bip32-util): Moving to @chris.troutner/bip32-util 2021-11-01 06:18:55 -07:00
Chris Troutner 4a3baca72d Messing around with different variations of transaction.get2() 2021-11-01 06:12:14 -07:00
Chris Troutner fe37c3ca43 Merge pull request #190 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get2()): Improvement to vin token inputs
2021-10-31 18:56:09 -07:00
Chris Troutner ff4ffab670 fix(Transaction.get2()): Improvement to vin token inputs 2021-10-31 18:50:47 -07:00
Chris Troutner 400e7b31a8 Merge pull request #189 from Permissionless-Software-Foundation/ct-unstable
Adding Transaction.get2()
2021-10-31 17:02:14 -07:00
Chris Troutner 360fca0475 fix(Transaction.get2()): Added block height to tx data 2021-10-31 16:58:40 -07:00
Chris Troutner ef7056ee7a fix(Transaction.get2()): Added non-validating tx get 2021-10-31 16:29:50 -07:00
Chris Troutner d221077786 Merge pull request #188 from Permissionless-Software-Foundation/ct-unstable
fix(Jenkins): Attempting to fix CI/CD
2021-10-31 14:52:22 -07:00
Chris Troutner 046ab78c6a fix(Jenkins): Attempting to fix CI/CD 2021-10-31 14:44:07 -07:00
Chris Troutner 29e90915e7 Merge pull request #187 from Permissionless-Software-Foundation/ct-unstable
fix(bip-32): Updated bip-32 library to improve vulns
2021-10-31 14:31:52 -07:00
Chris Troutner 3d85dbfb22 Updating depds 2021-10-31 14:26:43 -07:00
Chris Troutner 2c0fa9dfbd Switched to exact dependency versions 2021-10-31 14:22:36 -07:00
Chris Troutner 6ca1d729dc fix(bip-32): Updated bip-32 library to improve vulns 2021-10-31 14:14:13 -07:00
Chris Troutner c095af8a29 Merge branch 'master' into documentation-fix 2021-10-30 09:07:46 -07:00
mathi 89e4ea1635 small fixes documentation 2021-10-30 17:06:46 +02:00
Chris Troutner 85a34a8305 Merge pull request #185 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get()): Adding token ID to tx token inputs
2021-10-28 16:58:56 -07:00
Chris Troutner ef6cfe4409 fix(Transaction.get()): Adding token ID to tx token inputs 2021-10-28 16:56:35 -07:00
Chris Troutner a29d6ed26a Merge pull request #184 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get()): Validating SLP inputs
2021-10-21 21:07:15 -07:00
Chris Troutner fa58a0764c fix(Transaction.get()): Validating SLP inputs 2021-10-21 21:04:11 -07:00
Chris Troutner 2c2fbeaa46 Merge pull request #183 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get()): Fixing another bug in MINT input handling
2021-10-21 20:47:03 -07:00
Chris Troutner 1e4cd5f99e fix(Transaction.get()): Fixing another bug in MINT input handling 2021-10-21 20:44:47 -07:00
Chris Troutner 52c1ebdafe Merge pull request #182 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get()): Fixing bug in previous PR
2021-10-21 19:45:36 -07:00
Chris Troutner 2e56d66f48 fix(Transaction.get()): Fixing bug in previous PR 2021-10-21 19:42:12 -07:00
Chris Troutner 9cf6c7e7a7 Merge pull request #181 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get()): Adding support for MINT corner-case
2021-10-21 19:15:08 -07:00
Chris Troutner 43043672c6 linting 2021-10-21 19:12:10 -07:00
Chris Troutner 4f4b2c864d fix(Transaction.get()): Adding support for MINT corner-case 2021-10-21 19:11:55 -07:00
Chris Troutner 951fea8f9b Merge pull request #180 from Permissionless-Software-Foundation/ct-unstable
fix(npm): Ran npm audit fix, to fix vulns
2021-10-16 09:49:50 -07:00
Chris Troutner 6d75dceffb fix(npm): Ran npm audit fix, to fix vulns 2021-10-16 09:46:55 -07:00
Chris Troutner 3a491c7aba Merge pull request #178 from Permissionless-Software-Foundation/ct-unstable
fix(generateGenesisOpReturn): Fixing bug when 0 decimal
2021-10-14 13:08:31 -07:00
Chris Troutner f03c020e4f fix(generateGenesisOpReturn): Fixing bug when 0 decimal 2021-10-14 13:06:15 -07:00
Chris Troutner 4aeb1148e5 Merge pull request #177 from Permissionless-Software-Foundation/review01
added toEcashAddress() & ecashtoCashAddress() for ecash format support
2021-10-04 07:55:41 -07:00
Chris Troutner f03ee13a1e Merge pull request #175 from mr-zwets/master
conversion between bitcoincash: and ecash:
2021-10-04 06:55:48 -07:00
mr-zwets cb98773d6a Merge branch 'Permissionless-Software-Foundation:master' into master 2021-09-23 07:43:07 +02:00
Chris Troutner c593d68ee5 Merge pull request #176 from Permissionless-Software-Foundation/ct-unstable
Improvements to Transaction.get()
2021-09-22 17:36:30 -07:00
Chris Troutner dc7d3a9339 fix(Transaction.get): Using BigNumber for better tokenqty calc 2021-09-22 17:33:27 -07:00
Chris Troutner 6f4cc0f439 fix(Transaction.get): Fixed bug when Genesis TX was input to queried TX 2021-09-22 16:58:14 -07:00
Chris Troutner 7aa0fa865e fix(decodeOpReturn): Fixed api docs 2021-09-22 15:40:23 -07:00
mathi e8b6d7fb36 added toEcashAddress() & ecashtoCashAddress() for ecash format support 2021-09-22 14:38:54 +02:00
Chris Troutner b13ef94ea3 Merge pull request #174 from Permissionless-Software-Foundation/ct-unstable
fix(npm): Ran npm audit fix, 0 vulns
2021-09-20 16:07:18 -07:00
Chris Troutner 912f899f4e fix(npm): Ran npm audit fix, 0 vulns 2021-09-20 16:04:14 -07:00
Chris Troutner 9063d8da0b Merge pull request #173 from Permissionless-Software-Foundation/feat/ecash-price
Feat/ecash price
2021-09-20 16:00:32 -07:00
Chris Troutner f4e2bd96f0 fix(XEC price): Fixing BCHA XEC price feeds 2021-09-20 15:56:42 -07:00
Chris Troutner 1c898a4ad3 Removing ninsight from integration tests 2021-09-20 15:54:54 -07:00
Chris Troutner c4db813f76 Merge pull request #172 from mr-zwets/master
changed getBchaUsd which returns the price for the BCHA denomination, added getXecUsd for the XEC denomination
2021-09-20 15:33:30 -07:00
mathi 38b96b441f changed getBchaUsd so it returns the BCHA denomination, added getXecUsd 2021-09-20 22:13:24 +02:00
Chris Troutner c94f78f510 Merge pull request #170 from Permissionless-Software-Foundation/ct-unstable
fix(minimal-slp-wallet): Fixed code that was breaking minimal-slp-wal…
2021-08-12 09:16:52 -07:00
Chris Troutner 6de2b712ec fix(minimal-slp-wallet): Fixed code that was breaking minimal-slp-wallet build 2021-08-12 09:14:29 -07:00
Chris Troutner 6f681fd7b0 Merge pull request #169 from Permissionless-Software-Foundation/ct-unstable
fix(npm): Ran npm audit fix. 0 vulns
2021-08-09 17:59:43 -07:00
Chris Troutner 9a82effb2b fix(npm): Ran npm audit fix. 0 vulns 2021-08-09 17:41:38 -07:00
Chris Troutner 955a5e428e Merge pull request #168 from Permissionless-Software-Foundation/ct-unstable
fix(getRawTransaction): Fixing integration test
2021-08-09 14:56:52 -07:00
Chris Troutner 44c3bf44f9 fix(getRawTransaction): Fixing integration test 2021-08-09 14:54:49 -07:00
Chris Troutner 31ce84abb0 Merge pull request #167 from Permissionless-Software-Foundation/ct-unstable
fix(waterfallValidateTxid): Skipping validateTxid3() if it throws an error
2021-08-09 10:39:18 -07:00
Chris Troutner 40fa4b6bc1 linting 2021-08-09 10:36:07 -07:00
Chris Troutner 8c5e111d60 fix(waterfallValidateTxid): Skipping validateTxid3() if it throws an error 2021-08-09 10:35:51 -07:00
Chris Troutner 8965dfc57f Merge pull request #166 from Permissionless-Software-Foundation/ct-unstable
fix(apiToken): Changed _this.axiosOptions to this.axiosOptions
2021-08-08 20:06:05 -07:00
Chris Troutner 93904a632f fix(apiToken): Changed _this.axiosOptions to this.axiosOptions 2021-08-08 20:01:38 -07:00
Chris Troutner 851d4c404c fix(rate limits): Fixing more bugs with rate limit handling axios.options 2021-08-08 19:51:01 -07:00
Chris Troutner 44bac45c32 Updating README 2021-08-08 18:42:04 -07:00
Chris Troutner 95ee8054be Merge pull request #165 from Permissionless-Software-Foundation/ct-unstable
fix(Transactions.get()): Adding extra error handling for rate limits
2021-08-08 18:40:43 -07:00
Chris Troutner 547b28cac8 fix(Transactions.get()): Adding extra error handling for rate limits 2021-08-08 18:38:46 -07:00
Chris Troutner 40ba334a41 Merge pull request #163 from Permissionless-Software-Foundation/ct-unstable
fix(validateTxid3): Throwing an error when rate limit encountered.
2021-08-08 18:06:52 -07:00
Chris Troutner ffd8ccaf2b fix(validateTxid3): Throwing an error when rate limit encountered. 2021-08-08 18:04:39 -07:00
Chris Troutner 9a16c0c6d6 Merge pull request #162 from Permissionless-Software-Foundation/ct-unstable
fix(raw-transaction): Fixing JWT token bug
2021-08-07 12:48:00 -07:00
Chris Troutner 52a891fd78 fix(raw-transaction): Fixing JWT token bug 2021-08-07 12:41:24 -07:00
Stoyan Zhekov ee7ab54d26 Some NFT-related calls sample usage fixes 2021-07-21 11:26:35 +09:00
Chris Troutner c09df08429 fix(npm): Ran npm audit fix 2021-07-16 15:56:36 -07:00
Chris Troutner 4dc88c0af3 Merge pull request #160 from Permissionless-Software-Foundation/ct-unstable
fix(transactions): Adding better error messages
2021-07-16 15:20:12 -07:00
Chris Troutner 85caa9556b fix(transactions): Adding better error messages 2021-07-16 15:18:35 -07:00
Chris Troutner bbdc417eb2 Merge pull request #159 from Permissionless-Software-Foundation/ct-unstable
Updating README
2021-07-12 19:57:47 -07:00
Chris Troutner cc3bc002e7 Updating README 2021-07-12 19:52:29 -07:00
Chris Troutner a75e1ab633 Merge pull request #158 from Permissionless-Software-Foundation/ct-unstable
fix(dsproof): Adding documentation
2021-06-22 15:49:17 -07:00
Chris Troutner 8c5f1e2274 fix(dsproof): Adding documentation 2021-06-22 15:47:22 -07:00
Chris Troutner c30e76c942 Merge pull request #157 from Permissionless-Software-Foundation/dh-dsproof
feat(DSProof): Added DSProof to bch-js
2021-06-22 12:28:40 -07:00
Chris Troutner a575621c50 Fixing ABC integration tests 2021-06-22 12:24:29 -07:00
Daniel Gonzalez d3a0c5948a feat(DSProof): Added DSProof to bch-js 2021-06-19 22:40:16 -04:00
Chris Troutner 61bb509195 Merge pull request #156 from Permissionless-Software-Foundation/ct-unstable
fix(raw-transactions): Adding better error handling for rate-limits
2021-06-19 10:43:24 -07:00
Chris Troutner a18e549694 fix(raw-transactions): Adding better error handling for rate-limits 2021-06-19 10:41:49 -07:00
Chris Troutner 7d53f47d8a Merge pull request #155 from Permissionless-Software-Foundation/ct-unstable
fix(error): Adding additional rate limit error handling
2021-06-19 10:09:51 -07:00
Chris Troutner e8e615795d fix(error): Adding additional rate limit error handling 2021-06-19 10:08:28 -07:00
Chris Troutner 5759b096d5 Merge pull request #153 from Permissionless-Software-Foundation/ct-unstable
fix(errors): Throwing rate limit errors as Error objects
2021-06-19 09:44:11 -07:00
Chris Troutner 5b300f68b2 fix(errors): Throwing rate limit errors as Error objects 2021-06-19 09:42:36 -07:00
Chris Troutner 3cce6f9754 Merge pull request #152 from Permissionless-Software-Foundation/ct-unstable
fix(testnet): Fixing testnet integration test
2021-06-18 09:43:06 -07:00
Chris Troutner 3da04d9327 fix(testnet): Fixing testnet integration test 2021-06-18 09:32:07 -07:00
Chris Troutner 79091815a2 Merge pull request #151 from Permissionless-Software-Foundation/ct-unstable
fix(Electrumx): Fixing integration test
2021-06-17 07:19:55 -07:00
Chris Troutner 258144d26d fix(Electrumx): Fixing integration test 2021-06-17 07:16:48 -07:00
Chris Troutner 4c4a45841b Merge pull request #150 from Permissionless-Software-Foundation/ct-unstable
fix(package.json): Using /v5 in npm scripts
2021-06-16 19:17:14 -07:00
Chris Troutner 010afd10a3 Merge branch 'master' into ct-unstable 2021-06-16 19:13:03 -07:00
Chris Troutner 82b4d2ab9e fix(package.json): Using /v5 in npm scripts 2021-06-16 19:12:48 -07:00
Chris Troutner 8eb6a21dc2 Merge pull request #149 from Permissionless-Software-Foundation/ct-unstable
feat(/v5 REST API): Using the new v5 route in bch-api
2021-06-16 19:03:45 -07:00
Chris Troutner 5f2a762d4f feat(/v5 REST API): Using the new v5 route in bch-api 2021-06-16 17:00:08 -07:00
Chris Troutner 24e2c78d03 Merge pull request #148 from Permissionless-Software-Foundation/ct-unstable
fix(testnet): Removing testnet ninsight tests as they rarely pass
2021-05-22 07:58:39 -07:00
Chris Troutner eab02fdbc9 fix(testnet): Removing testnet ninsight tests as they rarely pass 2021-05-22 07:55:02 -07:00
Chris Troutner f7bd9c75db Merge pull request #147 from Permissionless-Software-Foundation/ct-unstable
fix(nft docs): Fixing a typo in docs
2021-05-21 15:54:07 -07:00
Chris Troutner 2a96f3a4f5 fix(nft docs): Fixing a typo in docs 2021-05-21 15:51:43 -07:00
Chris Troutner 3dc4a8ba6f Merge pull request #146 from Permissionless-Software-Foundation/ct-unstable
fix(dependencies): Updating dependencies. 0 npm vulns
2021-05-21 15:39:46 -07:00
Chris Troutner 65916f2c5c fix(dependencies): Updating dependencies. 0 npm vulns 2021-05-21 15:38:02 -07:00
Chris Troutner 18188bfdce Merge pull request #145 from Permissionless-Software-Foundation/nft-group-get-call
Return parent NFT group token information
2021-05-18 12:40:16 -07:00
Stoyan Zhekov 45acc2b0b7 Return parent NFT group token information 2021-05-16 14:22:33 +09:00
Chris Troutner 31913c3fb7 Merge pull request #144 from Permissionless-Software-Foundation/nft-children-get-call
NFT children get call
2021-05-15 08:54:58 -07:00
Stoyan Zhekov 2311643398 Example fixed 2021-05-14 21:58:52 +09:00
Stoyan Zhekov 7501aa0238 Lint fixes 2021-05-14 21:18:08 +09:00
Stoyan Zhekov 92a89a543d List NFT children per group 2021-05-14 21:17:07 +09:00
Chris Troutner cdf8171994 Merge pull request #143 from Permissionless-Software-Foundation/ct-unstable
fix(integration tests): Adding integration tests for bchn with no whi…
2021-05-03 19:18:34 -07:00
Chris Troutner 385baf0afd fix(integration tests): Adding integration tests for bchn with no whitelist SLPDB 2021-05-03 19:15:41 -07:00
Chris Troutner 9a9aababd2 Merge pull request #142 from Permissionless-Software-Foundation/ct-unstable
fix(integration tests): Adding tests for bchn with no whitelist SLPDB
2021-05-03 19:06:14 -07:00
Chris Troutner c228acd28a fix(integration tests): Adding tests for bchn with no whitelist SLPDB 2021-05-03 18:53:41 -07:00
Chris Troutner c1c140bdce Merge pull request #139 from Permissionless-Software-Foundation/dh-getblock
feat(getBlock): Added getBlock endpoint to bch-js
2021-04-07 13:07:05 -07:00
Daniel Gonzalez 54d9b80f26 feat(getBlock): Added getBlock endpoint to bch-js 2021-04-06 21:33:33 -04:00
Chris Troutner 961a190309 Merge pull request #138 from Permissionless-Software-Foundation/ct-unstable
fix(debugging): Removing all the debugging statements
2021-03-26 18:05:04 -08:00
Chris Troutner 2d2a91dfde fix(debugging): Removing all the debugging statements 2021-03-26 18:57:08 -07:00
Chris Troutner fbac18d456 Merge pull request #137 from Permissionless-Software-Foundation/ct-unstable
More connection reset debugging
2021-03-26 17:32:23 -08:00
Chris Troutner 62ae67ceab Logging axios options in decodeOpReturn 2021-03-26 18:29:48 -07:00
Chris Troutner 8e64d854cf fix(hydrateUtxos): Adding debugging info for axiosOptions 2021-03-26 18:26:18 -07:00
Chris Troutner 5fe8e515ff Merge pull request #136 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Adding debugging information
2021-03-26 16:30:50 -08:00
Chris Troutner f11297339b fix(decodeOpReturn): Adding debugging information 2021-03-26 17:27:57 -07:00
Chris Troutner 61d52c8ebe Merge pull request #135 from Permissionless-Software-Foundation/ct-unstable
fix(debugging): Adding additional debugging info to SLP utils
2021-03-26 12:53:35 -08:00
Chris Troutner d89bbe0bf6 fix(debugging): Adding additional debugging info to SLP utils 2021-03-26 13:51:29 -07:00
Chris Troutner 2a1c2500e6 Merge pull request #134 from Permissionless-Software-Foundation/ct-unstable
fix(slp): Adding more verbose error messages for SLP utility functions
2021-03-26 12:16:55 -08:00
Chris Troutner 8879a5170a fix(slp): Adding more verbose error messages for SLP utility functions 2021-03-26 13:11:40 -07:00
Chris Troutner b29cb7c12b Merge pull request #133 from Permissionless-Software-Foundation/ct-unstable
fix(integration tests): slowing down integration tests for the sake o…
2021-03-26 08:28:46 -07:00
Chris Troutner 69fa10d3a6 fix(integration tests): slowing down integration tests for the sake of rate limits 2021-03-26 08:25:39 -07:00
Chris Troutner d16431d165 Merge pull request #132 from Permissionless-Software-Foundation/ct-unstable
Adding Transaction.get()
2021-03-24 07:30:48 -07:00
Chris Troutner faa2972460 fix(Transaction.get()): Added documentation 2021-03-24 07:28:38 -07:00
Chris Troutner 5d3ea2b559 fix(Transaction.get()): Added unit tests 2021-03-24 07:24:08 -07:00
Chris Troutner 40fa6440f4 feat(Transactions.get()): Added tx hydration method 2021-03-23 18:49:27 -07:00
Chris Troutner ce951afddb fix(RawTransactions.getTxData): Adding BCH value to tx inputs 2021-03-23 17:37:00 -07:00
Chris Troutner d2e847d3fb feat(transactions): Created transaction library 2021-03-23 17:11:37 -07:00
Chris Troutner fe697455c0 Merge branch 'master' into ct-unstable 2021-03-09 08:09:01 -08:00
Chris Troutner 25832d52fd fix(raw-transactions): Adding proper axios header for Basic Auth 2021-03-09 08:04:14 -08:00
Chris Troutner 9d0840c2fe Merge pull request #131 from Permissionless-Software-Foundation/ct-unstable
fix(tokenUtxoDetailsWL): Adding usrObj input variable
2021-03-08 17:37:31 -08:00
Chris Troutner 74dc3e7e1d Merge branch 'master' into ct-unstable 2021-03-08 17:33:25 -08:00
Chris Troutner 171786a470 fix(tokenUtxoDetailsWL): Adding usrObj input variable 2021-03-08 17:32:33 -08:00
Chris Troutner 5cbd685060 Merge pull request #130 from Permissionless-Software-Foundation/dh-bch-price
feat(price): Created getBchUsd() for bch-js
2021-03-05 07:51:32 -08:00
Daniel Gonzalez ef05aff7a3 feat(price): Created getBchUsd() for bch-js 2021-03-04 15:14:46 -04:00
Chris Troutner 3f01fdba7c Merge pull request #129 from Permissionless-Software-Foundation/ct-unstable
fix(hydrateUtxos): Stringifying bch-api object when passing error
2021-03-02 07:49:14 -08:00
Chris Troutner 232747280e Merge branch 'master' into ct-unstable 2021-03-02 07:47:03 -08:00
Chris Troutner e96da1615b fix(hydrateUtxos): Stringifying bch-api object when passing error 2021-03-02 07:46:51 -08:00
Chris Troutner 9538501f01 Merge pull request #128 from Permissionless-Software-Foundation/ct-unstable
fix(hydrateUtxos): Adding better error handler
2021-03-02 07:31:43 -08:00
Chris Troutner 0a4fee5716 fix(hydrateUtxos): Adding better error handler 2021-03-02 07:29:41 -08:00
Chris Troutner cec79be4fa Merge pull request #127 from Permissionless-Software-Foundation/ct-unstable
fix(hydrateUtxos): Adding usrObj input for delaying UTXO processing
2021-03-01 08:21:23 -08:00
Chris Troutner c839f0b891 fix(integration tests): Fixing broken ABC integration test 2021-03-01 08:19:16 -08:00
Chris Troutner 24d629ebe1 fix(hydrateUtxos): Adding usrObj input for delaying UTXO processing 2021-03-01 07:48:08 -08:00
Chris Troutner f729918385 fix(_hydrateUtxo): Adding user-configurable artifical delay 2021-03-01 06:55:35 -08:00
Chris Troutner b7ab02c0ed Merge pull request #126 from Permissionless-Software-Foundation/ct-unstable
fix(docs): Updating docs and commenting out debug logs
2021-02-27 15:40:58 -08:00
Chris Troutner 1c291880e0 fix(docs): Updating docs and commenting out debug logs 2021-02-27 15:38:24 -08:00
Chris Troutner 09b79c4d3e Merge pull request #125 from Permissionless-Software-Foundation/ct-unstable
Adding unit tests for 429 and 503 errors
2021-02-27 14:56:14 -08:00
Chris Troutner 828f6a1b1c fix(_hydrateUtxo): Added unit tests 2021-02-27 14:53:54 -08:00
Chris Troutner 1b8727da55 fix(decodeOpReturn): Adding test cases for 429 and 503 errors 2021-02-27 13:28:53 -08:00
Chris Troutner f84862d875 Merge pull request #124 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Added NFT child test case
2021-02-27 13:12:30 -08:00
Chris Troutner 179d924899 Adding respond.data in error 2021-02-27 13:10:33 -08:00
Chris Troutner 676fb85abf fix(decodeOpReturn): Added NFT child test case 2021-02-27 12:58:40 -08:00
Chris Troutner d59c1fe445 Merge pull request #123 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Trying different error configuration
2021-02-27 10:03:58 -08:00
Chris Troutner 0a34611557 fix(decodeOpReturn): Trying different error configuration 2021-02-27 10:02:12 -08:00
Chris Troutner c13b029e99 Merge pull request #122 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Replacing error message with better one
2021-02-27 09:47:20 -08:00
Chris Troutner b038e9a7fd fix(decodeOpReturn): Replacing error message with better one 2021-02-27 09:45:24 -08:00
Chris Troutner 7b728fb45d Merge pull request #121 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Adding additional debugging for errors
2021-02-27 09:33:00 -08:00
Chris Troutner d4dbce8df0 fix(decodeOpReturn): Adding additional debugging for errors 2021-02-27 09:31:16 -08:00
Chris Troutner 2419aeceaa Merge pull request #120 from Permissionless-Software-Foundation/ct-unstable
fix(_hydrateUtxo): Adding more debugging
2021-02-27 09:09:58 -08:00
Chris Troutner 158b009069 fix(_hydrateUtxo): Adding more debugging 2021-02-27 09:07:50 -08:00
Chris Troutner f3c3250a69 Merge pull request #119 from Permissionless-Software-Foundation/ct-unstable
fix(_hydrateUtxo): Debugging error handling
2021-02-27 09:00:20 -08:00
Chris Troutner 5ae4db88c3 Merge branch 'master' into ct-unstable 2021-02-27 08:58:23 -08:00
Chris Troutner 33e161df26 fix(_hydrateUtxo): Debugging error handling 2021-02-27 08:58:12 -08:00
Chris Troutner 81c910361c Merge pull request #118 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Debugging error handling by decodeOpReturn()
2021-02-27 08:41:57 -08:00
Chris Troutner 9751a39538 Merge branch 'master' into ct-unstable 2021-02-27 08:38:38 -08:00
Chris Troutner 7058f77ee9 fix(decodeOpReturn): Debugging error handling by decodeOpReturn() 2021-02-27 08:37:29 -08:00
Chris Troutner ad580d67a4 Adding decatur test script 2021-02-27 08:23:07 -08:00
Chris Troutner acc0300a44 Merge pull request #117 from Permissionless-Software-Foundation/ct-unstable
fix(bip32-utils): Bumping to v1.0.0 with updated keccak
2021-02-24 18:56:39 -08:00
Chris Troutner 4e1b806230 fix(bip32-utils): Bumping to v1.0.0 with updated keccak 2021-02-24 18:53:20 -08:00
Chris Troutner 357e350176 Merge pull request #116 from Permissionless-Software-Foundation/ct-unstable
fix(electrumx): Adding usrObj to Electrumx.Transactions()
2021-02-24 17:59:35 -08:00
Chris Troutner f6140acc76 fix(electrumx): Adding usrObj to Electrumx.Transactions() 2021-02-24 17:53:53 -08:00
Chris Troutner df65b12aab Merge pull request #115 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): replacing ip with usrObj
2021-02-24 16:44:58 -08:00
Chris Troutner 754b4cd807 fix(decodeOpReturn): replacing ip with usrObj 2021-02-24 16:42:54 -08:00
Chris Troutner 3c6079e54c Merge pull request #114 from Permissionless-Software-Foundation/ct-unstable
fix(usrObj): Fixing typo of usbObj to usrObj
2021-02-24 16:37:09 -08:00
Chris Troutner 18621cd76c fix(usrObj): Fixing typo of usbObj to usrObj 2021-02-24 16:35:09 -08:00
Chris Troutner 456159a4cc Merge pull request #113 from Permissionless-Software-Foundation/ct-unstable
fix(usbObj): Passing a usrObj when bch-api calls bch-js
2021-02-24 16:29:54 -08:00
Chris Troutner 33e723befe fix(usbObj): Passing a usrObj when bch-api calls bch-js 2021-02-24 16:27:49 -08:00
Chris Troutner 30fdee1920 Merge pull request #112 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Switching to POST from GET to getRawTx call
2021-02-24 15:14:51 -08:00
Chris Troutner feb336c331 fix(decodeOpReturn): Switching to POST from GET to getRawTx call 2021-02-24 15:11:58 -08:00
Chris Troutner 8bacc0a991 Merge pull request #111 from Permissionless-Software-Foundation/ct-unstable
fix(ip): Renaming origin to ip
2021-02-24 14:10:07 -08:00
Chris Troutner d22f1ec8a6 fix(ip): Renaming origin to ip 2021-02-24 14:05:25 -08:00
Chris Troutner 7eb3a796b8 Merge pull request #110 from Permissionless-Software-Foundation/ct-unstable
fix(origin): Testing idea around passing origin when bch-api calls bc…
2021-02-24 13:29:05 -08:00
Chris Troutner 9919f811b0 fix(origin): Testing idea around passing origin when bch-api calls bch-js 2021-02-24 13:26:08 -08:00
Chris Troutner e7854481a0 Merge pull request #109 from Permissionless-Software-Foundation/ct-unstable
fix(dependencies): Removing several unused dependencies
2021-02-23 10:29:22 -08:00
Chris Troutner 0b406c467e fix(dependencies): Removing more unnecessary dependencies 2021-02-23 10:26:12 -08:00
Chris Troutner e41217b710 fix(dependencies): Removing several unused dependencies 2021-02-23 09:04:49 -08:00
Chris Troutner c22ce8d57d fix(dependencies): Ran npm audit with 0 vulns 2021-02-23 08:46:27 -08:00
Chris Troutner bf3612583e Merge pull request #108 from Permissionless-Software-Foundation/ct-unstable
Sleep: Adding a Promise-based wait function to Util lib
2021-02-15 15:28:26 -08:00
Chris Troutner ef56452ec9 feat(sleep): Adding sleep function to Util lib 2021-02-15 15:16:30 -08:00
Chris Troutner b3be470c0e fix(utxo.get()): Fixing ABC integration test 2021-02-15 15:12:39 -08:00
Chris Troutner 7274475898 Merge pull request #107 from Permissionless-Software-Foundation/ct-unstable
fix(package-lock.json): Updating package-lock.json
2021-02-13 17:46:19 -08:00
Chris Troutner 7bb347a833 fix(package-lock.json): Updating package-lock.json 2021-02-13 17:44:14 -08:00
Chris Troutner fadceedd00 Merge pull request #106 from Permissionless-Software-Foundation/ct-unstable
fix(debug): Removing debugging lines
2021-02-13 17:33:43 -08:00
Chris Troutner 7f4ef60f8e fix(debug): Removing debugging lines 2021-02-13 17:30:55 -08:00
Chris Troutner 81e54e0590 Merge pull request #105 from Permissionless-Software-Foundation/ct-unstable
Porting bch-util into bch-js
2021-02-13 15:07:44 -08:00
Chris Troutner 1d5e3da976 feat(chunk20): bchjs.Util.chunk20() - chunk array into 20-element arrays 2021-02-13 15:04:39 -08:00
Chris Troutner 2cff305f46 feat(floor2): bchjs.Util.floor2(num) - round down to 2 decimal places 2021-02-13 14:59:33 -08:00
Chris Troutner 9495381cf4 feat(floor8): bchjs.Util.floor8(num) - Round down to 8 decimal places 2021-02-13 14:55:12 -08:00
Chris Troutner 32f89346c3 linting 2021-02-13 14:38:37 -08:00
Chris Troutner e8582fd0ab feat(findBiggestUtxo): Ported from bch-util. Finds biggest UTXO in an array 2021-02-13 14:38:01 -08:00
Chris Troutner 7049b96df1 Merge pull request #104 from Permissionless-Software-Foundation/ct-unstable
utxo.get(): Refined and finalized.
2021-02-12 19:48:13 -08:00
Chris Troutner 9cc6824591 feat(utxo.get whitelist): Added flag to validate against the whitelist filtered SLPDB 2021-02-12 19:44:24 -08:00
Chris Troutner bb82d520ed feat(utxo.get()): Output changed for different types of utxos 2021-02-12 18:30:25 -08:00
Chris Troutner fef05124f1 feat(utxo.get()): Changes to accept arrays of up to 20 addresses 2021-02-12 15:58:16 -08:00
Chris Troutner 978db07526 Merge pull request #103 from Permissionless-Software-Foundation/ct-unstable
feat(utxo): Added Utxo.get() for easy getting of utxos
2021-02-11 19:25:42 -08:00
Chris Troutner 68bab5dd26 feat(utxo): Added Utxo.get() for easy getting of utxos 2021-02-11 19:22:25 -08:00
Chris Troutner 20e685206d Removing the integration/implementations folder 2021-02-11 14:14:15 -08:00
Chris Troutner e90afac701 Updating local testnet integration test script 2021-02-10 13:31:21 -08:00
Chris Troutner 96f96f837e Merge pull request #102 from Permissionless-Software-Foundation/ct-unstable
fix(abc): Fixing testing scripts for ABC chain
2021-02-09 15:12:47 -08:00
Chris Troutner 03be1d5838 fix(abc): Fixing testing scripts for ABC chain 2021-02-09 15:08:14 -08:00
Chris Troutner 6b6bfa2ae2 Merge pull request #101 from Permissionless-Software-Foundation/ct-unstable
feat(hydrateUtxosWL): hydrateUtxo but uses SLPDB with whitelist filter
2021-02-09 12:37:15 -08:00
Chris Troutner da3af3656a feat(hydrateUtxosWL): hydrateUtxo but uses SLPDB with whitelist filter 2021-02-09 12:34:25 -08:00
Chris Troutner 26fdb5c5d5 Merge pull request #100 from Permissionless-Software-Foundation/ct-unstable
fix(package.json): Slight reconfiguration by npm
2021-02-09 09:12:41 -08:00
Chris Troutner 86d71d0f49 fix(package.json): Slight reconfiguration by npm 2021-02-09 09:10:11 -08:00
Chris Troutner 86351aaf4f Merge pull request #99 from Permissionless-Software-Foundation/ct-unstable
tokenUtxoDetailsWL() and waterfallValidateTxid()
2021-02-09 07:25:13 -08:00
Chris Troutner 36432af7d5 feat(tokenUtxoDetailsWL): tokenUtxoDetails that use whitelist SLPDB 2021-02-09 07:22:01 -08:00
Chris Troutner 95c88b5f29 fix(_hydrateUtxo): Moving utxo hydration into a private function 2021-02-08 20:33:27 -08:00
Chris Troutner 28f563b58c Updating unit tests to reflect use of waterfallValidateTxid() 2021-02-08 19:17:50 -08:00
Chris Troutner 79ea4b77b9 fix(tokenUtxoDetails): Replacing validation with waterfallValidateTxid() 2021-02-08 17:25:08 -08:00
Chris Troutner d37a2f8139 Merge pull request #98 from Permissionless-Software-Foundation/ct-unstable
feat(waterfallValidateTxid): SLP.Utils.waterfallValidateTxid(txid)
2021-02-08 17:16:35 -08:00
Chris Troutner 3b873b6064 feat(waterfallValidateTxid): SLP.Utils.waterfallValidateTxid(txid) 2021-02-08 17:10:32 -08:00
Chris Troutner 687b5bca02 feat(waterfallValidateTxid): Adding a new waterfall SLP validation method 2021-02-08 10:45:24 -08:00
Chris Troutner b80697500c Adding integration test script to gitignore 2021-02-08 09:43:25 -08:00
Chris Troutner 626d27033e Merge pull request #96 from Permissionless-Software-Foundation/ct-unstable
fix(typo): Fixing typo in IPFS class
2021-02-03 17:41:29 -08:00
Chris Troutner ce2ee3fc1b fix(typo): Fixing typo in IPFS class 2021-02-03 17:38:43 -08:00
Chris Troutner 4768dcadc6 Merge pull request #95 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs): Updating the server that handles bch-ipfs file uploads
2021-02-03 15:42:47 -08:00
Chris Troutner 5fef09edbf fix(ipfs): Updating the server that handles bch-ipfs file uploads 2021-02-03 15:40:38 -08:00
Chris Troutner 37109cd7ef Merge pull request #94 from Permissionless-Software-Foundation/ct-unstable
fix(getTxDataSlp): Adding prototype function
2021-01-30 14:43:13 -08:00
Chris Troutner 83d523260d fix(getTxDataSlp): Adding prototype function 2021-01-30 14:40:46 -08:00
Chris Troutner 8768a810c7 Merge pull request #93 from Permissionless-Software-Foundation/ct-unstable
RawTransactions.getTxData() returns tx info with vin addresses
2021-01-29 12:49:50 -08:00
Chris Troutner 8832bfc8df fix(decodeOpReturn): Fixing documentation 2021-01-29 12:47:22 -08:00
Chris Troutner a5208110b9 feat(getTxData): RawTransactions.getTxData() returns tx info with vin addresses 2021-01-29 12:43:02 -08:00
Chris Troutner 99ad7cd87f fix(_getInputAddrs): Added private function with tests get tx input addrs 2021-01-29 12:11:23 -08:00
Chris Troutner 68ae084fb0 Merge pull request #92 from Permissionless-Software-Foundation/ct-unstable
Merging documentation changes from @uncletammy
2021-01-29 07:20:32 -08:00
Chris Troutner b0b0e17abc Merge pull request #91 from uncletammy/master
Corrects example usages for HDNode.Mnemonic.toSeed method
2021-01-29 07:11:57 -08:00
Nicholas Crumrine a54306e382 corrects example usages for HDNode.toSeed method 2021-01-28 15:24:03 -06:00
Chris Troutner 1860232fa6 Merge pull request #90 from Permissionless-Software-Foundation/ct-unstable
fix(sortAllTxs): Renaming Electrumx.sort0ConfTxs to sortAllTxs
2021-01-27 07:28:53 -08:00
Chris Troutner d8437cf3fb fix(sortAllTxs): Renaming Electrumx.sort0ConfTxs to sortAllTxs 2021-01-27 07:24:28 -08:00
Chris Troutner 338788c46d Merge pull request #89 from Permissionless-Software-Foundation/ct-unstable
fix(sort0ConfTxs): Switching to Descending by default
2021-01-27 06:57:15 -08:00
Chris Troutner cad2c6378e fix(sort0ConfTxs): Switching to Descending by default 2021-01-27 06:51:36 -08:00
Chris Troutner 2e6938e21f Merge pull request #88 from Permissionless-Software-Foundation/ct-unstable
feat(tx sorting): added Electrumx.sort0ConfTxs()
2021-01-26 20:28:40 -08:00
Chris Troutner 1e13c71715 Added integration test 2021-01-26 20:25:03 -08:00
Chris Troutner d4b0807d16 feat(tx sorting): added Electrumx.sort0ConfTxs() 2021-01-26 20:20:49 -08:00
Chris Troutner 611c7977ef Merge pull request #87 from Permissionless-Software-Foundation/ct-unstable
fix(license): Updating license
2021-01-26 08:06:20 -08:00
Chris Troutner 39ac634675 fix(license): Updating license 2021-01-26 08:00:07 -08:00
Chris Troutner 278a54bd9b Merge pull request #86 from Permissionless-Software-Foundation/ct-unstable
Ct unstable
2021-01-19 14:08:03 -08:00
Chris Troutner 30b1f71d81 feat(OpenBazaar): Removing support for OpenBazaar API since it's been taken down 2021-01-19 13:43:07 -08:00
Chris Troutner e7d7fc1019 Merge branch 'master' into ct-unstable 2021-01-19 13:11:59 -08:00
Chris Troutner 524da532e2 Merge pull request #85 from Permissionless-Software-Foundation/fix/generateBurnOpReturn-tokenQty
fix(generateBurnOpReturn): Added handler when burning amount is higher than token amount
2021-01-16 11:14:42 -08:00
Gary Nadir aef56c5c14 fix(generateBurnOpReturn): Added handler when burning amount is higher than token amount 2021-01-16 14:22:02 -04:00
Chris Troutner e2ba265878 Updating README 2021-01-11 16:45:47 -08:00
Chris Troutner 2472dc3606 Updating bch-api paths to v4 in readme 2021-01-09 15:48:02 -08:00
Chris Troutner 2df36124b4 Merge pull request #84 from Permissionless-Software-Foundation/ct-unstable
fix(tokenUtxoDetails): Removing requirement to provide satoshis or va…
2021-01-09 15:45:43 -08:00
Chris Troutner c422d8db34 fix(tokenUtxoDetails): Removing requirement to provide satoshis or value in input 2021-01-09 15:43:29 -08:00
Chris Troutner ec2d8ea80a Merge pull request #83 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Updated unit and integration tests
2021-01-07 17:52:02 -08:00
Chris Troutner fc527834ea Merge branch 'master' into ct-unstable 2021-01-07 17:44:56 -08:00
Chris Troutner 3ceebc1f12 fix(tests): Updated unit and integration tests 2021-01-07 17:44:04 -08:00
Chris Troutner 2ff46c1f76 Merge pull request #82 from Permissionless-Software-Foundation/ct-unstable
fix(axios): Updating axios
2021-01-04 14:49:11 -08:00
Chris Troutner 6926bbe113 fix(axios): Updating axios 2021-01-04 14:44:48 -08:00
Chris Troutner 360f1509dd Merge pull request #81 from Permissionless-Software-Foundation/ct-unstable
Changes to Linting
2021-01-02 16:47:15 -08:00
Chris Troutner d5d2e43ad0 fix(husky): Adding husky for auto linting 2021-01-02 16:44:53 -08:00
Chris Troutner 1012f4c1bf Wrapping up linting 2021-01-02 16:39:44 -08:00
Chris Troutner 3cedd251e5 fix(linting): Adding standard linting rules 2021-01-02 15:41:12 -08:00
Chris Troutner bdb4de0353 Merge pull request #80 from Permissionless-Software-Foundation/ct-unstable
Converting dependencies targeting GitHub repos to npm libraries.
2021-01-01 17:15:43 -08:00
Chris Troutner 1de0bdcf14 Updating bitcoincashjs-lib 2021-01-01 17:12:35 -08:00
Chris Troutner 3a9e4f64b5 Updating bitcoincashjs-lib 2021-01-01 16:50:02 -08:00
Chris Troutner ea57f52b20 fixing dependency in test 2021-01-01 16:39:03 -08:00
Chris Troutner 903aae1091 Fixing references to npm libs 2021-01-01 16:36:12 -08:00
Chris Troutner 1af766cbda fix(coininfo): Switching to forked npm library 2021-01-01 16:29:50 -08:00
Chris Troutner 60ee10ef4d fix(bip21): Switching to npm lib 2021-01-01 16:14:55 -08:00
Chris Troutner 2d4bc6127e fix(bitcoincashjs-lib): Using npm package rather than github repo 2021-01-01 14:30:57 -08:00
Chris Troutner a2c2d41833 fix(bitcoincash-ops): Switching to PSF fork of library 2021-01-01 12:51:54 -08:00
Chris Troutner 0e7db7bf39 fix(bip32-utils): Replacing bip32-utils with PSF maintained fork 2021-01-01 12:39:19 -08:00
Chris Troutner 46de4f6a25 feat(BITBOX Shim): Removing Shim support for BITBOX 2021-01-01 11:56:09 -08:00
Chris Troutner 2acf47a550 feat(wallet): Removing wallet library 2021-01-01 11:45:34 -08:00
Chris Troutner 7fea5c2630 Merge pull request #79 from Permissionless-Software-Foundation/ct-test-joey-slp
Testing Joey's Updates to SLP Handling
2020-12-30 08:52:52 -08:00
Chris Troutner b297743521 feat(SLP): Improved SLP calculation to fix small burns 2020-12-30 08:24:18 -08:00
Chris Troutner 6c33222172 Merge pull request #60 from josephroyking/slp-utils-patch
Resolving correct decimal precision for slp token qty
2020-12-28 05:01:59 -08:00
Chris Troutner 9a36a8261a Merge pull request #54 from josephroyking/generateSendOpReturn-rounding-patch
Generate send op return rounding patch
2020-12-28 04:57:53 -08:00
Chris Troutner 1433c77fcc Merge pull request #78 from Permissionless-Software-Foundation/ct-unstable
feat(Blockbook): Removing support for Blockbook indexer
2020-12-24 10:18:55 -08:00
Chris Troutner b1c3311d50 feat(Blockbook): Removing support for Blockbook indexer 2020-12-24 10:16:41 -08:00
Chris Troutner b3e224e481 Cleaned up integration tests 2020-12-24 10:06:39 -08:00
Chris Troutner 06e2dad45c Ran npm audit fix 2020-12-24 09:26:36 -08:00
Chris Troutner d475f2220f feat(SLPDB status): Created bchjs.SLP.Utils.getStatus() to get health status of SLPDB 2020-12-24 09:25:35 -08:00
Chris Troutner 10520a2e08 Merge pull request #75 from Permissionless-Software-Foundation/ct-unstable
fix(tokenUtxoDetails): Renabling local caching of tx validation
2020-12-13 09:16:17 -08:00
Chris Troutner 05b654d54a fix(tokenUtxoDetails): Renabling local caching of tx validation 2020-12-13 09:14:06 -08:00
Joey King ad5fabf2e6 Remove stub test 2020-12-03 06:57:44 -08:00
Joey King f8f6db069b Implementing BigNumber for all tokenQty calcs 2020-12-03 06:56:05 -08:00
Joey King df620e0302 stub test method 2020-12-02 16:06:10 -08:00
Joey King 19f2e87528 Resolving correct decimal precision for slp token qty 2020-12-02 16:02:07 -08:00
Joey King b512b222dd Adding large test tx 2020-11-10 14:28:10 -08:00
Joey King 650e3b53fc Adding tests and checks 2020-11-10 13:38:41 -08:00
Joey King 905658ca28 format changes take two 2020-11-10 06:19:36 -08:00
Joey King b0c91c407d matching original formatting 2020-11-10 06:13:53 -08:00
Joey King d2c68efc0c Use BigNumber to eliminate rounding error token burns 2020-11-10 06:10:38 -08:00
128 changed files with 31377 additions and 14811 deletions
+30
View File
@@ -0,0 +1,30 @@
# http://editorconfig.org
# A special property that should be specified at the top of the file outside of
# any sections. Set to true to stop .editor config file search on current file
root = true
[*]
# Indentation style
# Possible values - tab, space
indent_style = space
# Indentation size in single-spaced characters
# Possible values - an integer, tab
indent_size = 2
# Line ending file format
# Possible values - lf, crlf, cr
end_of_line = lf
# File character encoding
# Possible values - latin1, utf-8, utf-16be, utf-16le
charset = utf-8
# Denotes whether to trim whitespace at the end of lines
# Possible values - true, false
trim_trailing_whitespace = true
# Denotes whether file should end with a newline
# Possible values - true, false
insert_final_newline = true
+6 -51
View File
@@ -1,55 +1,10 @@
{ {
"root": true, "extends": "standard",
"parserOptions": { "env": {
"ecmaVersion": 2018, "node": true,
"sourceType": "module" "mocha": true
}, },
"plugins": ["prettier"], "parserOptions": {
"rules": { "ecmaVersion": 8
"no-debugger": ["warn"],
"no-regex-spaces": ["error"],
"no-unsafe-negation": ["error"],
"curly": ["error", "multi-or-nest", "consistent"],
"dot-location": ["error", "property"],
"dot-notation": ["error"],
"eqeqeq": ["error", "smart"],
"no-else-return": ["error"],
"no-extra-bind": ["error"],
"no-extra-label": ["error"],
"no-floating-decimal": ["error"],
"no-implicit-coercion": ["error", { "allow": ["!!"] }],
"wrap-iife": ["error", "inside"],
"strict": ["error", "global"],
"func-call-spacing": ["error", "never"],
"comma-style": ["error", "last"],
"keyword-spacing": ["error"],
"linebreak-style": ["error", "unix"],
"new-parens": ["error"],
"no-lonely-if": ["error"],
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1 }],
"no-whitespace-before-property": ["error"],
"semi": ["error", "never"],
"arrow-body-style": ["error", "as-needed"],
"arrow-parens": ["error", "as-needed"],
"arrow-spacing": ["error"],
"no-useless-computed-key": ["error"],
"no-useless-rename": ["error"],
"no-var": ["off"],
"prefer-spread": ["off"],
"prefer-template": ["error"],
"rest-spread-spacing": ["error", "never"],
"prefer-const": ["warn", { "destructuring": "all" }],
"no-unreachable": ["warn"],
"no-unused-vars": ["warn", { "args": "none" }],
"prettier/prettier": [
"warn",
{
"printWidth": 80,
"trailingComma": "none",
"singleQuote": false,
"semi": false
}
]
} }
} }
+1
View File
@@ -3,6 +3,7 @@ node_modules/*
wallet-info.txt wallet-info.txt
wallet.json wallet.json
integration-test-sweet.sh integration-test-sweet.sh
integration-test-ss.sh
docs/ docs/
coverage/ coverage/
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2020 Chris Troutner Copyright 2021 Chris Troutner
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: 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:
+7 -9
View File
@@ -1,9 +1,9 @@
# bch-js # bch-js
[![Build Status](https://travis-ci.org/Permissionless-Software-Foundation/bch-js.svg?branch=master)](https://travis-ci.org/Permissionless-Software-Foundation/bch-js)
[![Version](https://img.shields.io/npm/v/@psf/bch-js)](https://www.npmjs.com/package/@psf/bch-js) [![Version](https://img.shields.io/npm/v/@psf/bch-js)](https://www.npmjs.com/package/@psf/bch-js)
[![Downloads/week](https://img.shields.io/npm/dw/@psf/bch-js)](https://npmjs.org/package/@psf/bch-js) [![Downloads/week](https://img.shields.io/npm/dw/@psf/bch-js)](https://npmjs.org/package/@psf/bch-js)
[![License](https://img.shields.io/npm/l/@psf/bch-js)](https://github.com/Permissionless-Software-Foundation/bch-js/blob/master/LICENSE.md) [![License](https://img.shields.io/npm/l/@psf/bch-js)](https://github.com/Permissionless-Software-Foundation/bch-js/blob/master/LICENSE.md)
[![js-standard-style](https://img.shields.io/badge/javascript-standard%20code%20style-green.svg?style=flat-square)](https://github.com/feross/standard)
[bch-js](https://www.npmjs.com/package/@psf/bch-js) is a JavaScript npm library for creating web and mobile apps that can interact with the Bitcoin Cash (BCH) blockchains. It can be used for free, but requires an account on [FullStack.cash](https://fullstack.cash) for increased rate limits. Learn more from [this article](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer) about Full Stack Bitcoin Cash development. [bch-js](https://www.npmjs.com/package/@psf/bch-js) is a JavaScript npm library for creating web and mobile apps that can interact with the Bitcoin Cash (BCH) blockchains. It can be used for free, but requires an account on [FullStack.cash](https://fullstack.cash) for increased rate limits. Learn more from [this article](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer) about Full Stack Bitcoin Cash development.
@@ -33,17 +33,13 @@ Here are two YouTube walk-through videos to help you get started:
``` ```
const BCHJS = require("@psf/bch-js") const BCHJS = require("@psf/bch-js")
let bchjs = new BCHJS() // Defaults to BCHN network. let bchjs = new BCHJS() // Defaults to BCHN network.
// testnet
bchjs = new BCHJS({ restURL: 'https://testnet3.fullstack.cash/v3/' })
``` ```
This library is intended to be paired with This library is intended to be paired with
the [bch-api](https://github.com/Permissionless-Software-Foundation/bch-api) REST API, and the infrastructure provided by [FullStack.cash](https://fullstack.cash). The `restURL` property can be changed to work with different Bitcoin Cash networks: the [bch-api](https://github.com/Permissionless-Software-Foundation/bch-api) REST API, and the infrastructure provided by [FullStack.cash](https://fullstack.cash). The `restURL` property can be changed to work with different Bitcoin Cash networks:
- BCHN Mainnet REST API server: https://bchn.fullstack.cash/v3/ - BCHN Mainnet REST API server: https://bchn.fullstack.cash/v5/
- ABC Mainnet REST API server: https://abc.fullstack.cash/v3/ - ABC Mainnet REST API server: https://abc.fullstack.cash/v5/
- Testnet3 REST API server: https://testnet3.fullstack.cash/v3/
- Check server status: https://metrics.fullstack.cash - Check server status: https://metrics.fullstack.cash
### API Key ### API Key
@@ -57,7 +53,7 @@ rate limits when interacting with the back end server. See [this article](https:
``` ```
const BCHJS = require("@psf/bch-js") const BCHJS = require("@psf/bch-js")
let bchjs = new BCHJS({ let bchjs = new BCHJS({
restURL: 'https://bchn.fullstack.cash/v3/', restURL: 'https://bchn.fullstack.cash/v5/',
apiToken: 'eyJhbGciO...' // Your JWT token here. apiToken: 'eyJhbGciO...' // Your JWT token here.
}) })
``` ```
@@ -117,8 +113,10 @@ Have questions? Need help? Join our community support
Copies of this repository are also published on [IPFS](https://ipfs.io). Copies of this repository are also published on [IPFS](https://ipfs.io).
- v3.8.10: QmauhggzKDo9A9je5y2K6WRAp32JmGRFCw2ZFTiSnW8yot - v4.5.4: QmWv3pxJy3MH8vU5nLUVyzqFxfNKXLQQEnz1rgStNuQijd
## License ## License
[MIT](LICENSE.md) [MIT](LICENSE.md)
test
+1 -1
View File
@@ -1,3 +1,3 @@
Examples have been moved to this repository: Examples have been moved to this repository
https://github.com/Permissionless-Software-Foundation/bch-js-examples https://github.com/Permissionless-Software-Foundation/bch-js-examples
+18278 -7098
View File
File diff suppressed because it is too large Load Diff
+64 -66
View File
@@ -4,23 +4,31 @@
"description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens", "description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
"author": "Chris Troutner <chris.troutner@gmail.com>", "author": "Chris Troutner <chris.troutner@gmail.com>",
"contributors": [ "contributors": [
"Gabriel Cardona <gabriel@bitcoin.com>" "Gabriel Cardona <gabriel@bitcoin.com>",
"Stoyan Zhekov <zh@zhware.net>"
], ],
"main": "src/bch-js", "main": "src/bch-js",
"scripts": { "scripts": {
"test": "nyc mocha --trace-warnings --unhandled-rejections=strict --timeout 30000 test/unit/", "test": "nyc mocha --trace-warnings --unhandled-rejections=strict --timeout 30000 test/unit/",
"test:integration": "npm run test:integration:bchn", "test:integration": "npm run test:integration:bchn",
"test:integration:abc": "export RESTURL=https://abc.fullstack.cash/v4/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/abc/", "test:integration:nft": "export RESTURL=https://bchn.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 -g '#nft1' test/integration/chains/bchn/slp.js",
"test:integration:bchn": "export RESTURL=https://bchn.fullstack.cash/v4/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/", "test:integration:abc": "export RESTURL=https://abc.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:bchn": "export RESTURL=https://bchn.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:testnet": "IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/chains/testnet/", "test:integration:testnet": "IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/chains/testnet/",
"test:integration:local": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration", "test:integration:local:abc": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:local:bchn": "RESTURL=http://localhost:3000/v4/ bash test/integration/test-bchn-integration.sh", "test:integration:local:bchn": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v4/ mocha --timeout 30000 test/integration/testnet", "test:integration:local:testnet": "RESTURL=http://localhost:4000/v5/ mocha --timeout 30000 test/integration/chains/testnet",
"test:temp": "export RESTURL=http://localhost:3000/v4/ && mocha --timeout 30000 -g '#validateTxid2' test/integration/", "test:integration:decatur:bchn": "export RESTURL=http://192.168.0.36:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:temp2": "mocha --timeout 30000 -g 'tokenUtxoDetails' test/unit/", "test:integration:decatur:abc": "export RESTURL=http://192.168.0.38:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:temp:bchn": "export RESTURL=http://157.90.174.219:3000/v5/ && mocha --timeout 30000 test/integration/",
"test:temp": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#Encryption' test/integration/",
"test:temp2": "mocha --timeout=30000 -g '#TransactionLib' test/unit/",
"test:temp3": "export RESTURL=https://bchn.fullstack.cash/v5/ && mocha --timeout 30000 -g '#DSProof' test/integration/chains/",
"test:temp4": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#decodeOpReturn' test/integration/",
"coverage": "nyc report --reporter=text-lcov | coveralls", "coverage": "nyc report --reporter=text-lcov | coveralls",
"coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/", "coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/",
"docs": "./node_modules/.bin/apidoc -i src/ -o docs" "docs": "./node_modules/.bin/apidoc -i src/ -o docs",
"lint": "standard --env mocha --fix"
}, },
"license": "MIT", "license": "MIT",
"homepage": "https://github.com/Permissionless-Software-Foundation/bch-js", "homepage": "https://github.com/Permissionless-Software-Foundation/bch-js",
@@ -29,70 +37,60 @@
"url": "git+https://github.com/Permissionless-Software-Foundation/bch-js.git" "url": "git+https://github.com/Permissionless-Software-Foundation/bch-js.git"
}, },
"dependencies": { "dependencies": {
"@uppy/core": "^1.10.4", "@psf/bip21": "2.0.1",
"@uppy/tus": "^1.5.12", "@chris.troutner/bip32-utils": "1.0.5",
"apidoc": "^0.17.7", "@psf/bitcoincash-ops": "2.0.0",
"assert": "^2.0.0", "@psf/bitcoincashjs-lib": "4.0.2",
"axios": "^0.19.0", "@psf/coininfo": "4.0.0",
"bc-bip68": "^1.0.5", "@uppy/core": "1.10.4",
"bch-wallet-bridge.js": "github:web3bch/bch-wallet-bridge.js#master", "@uppy/tus": "1.5.12",
"bchaddrjs-slp": "^0.2.5", "axios": "^0.21.4",
"bigi": "^1.4.2", "bc-bip68": "1.0.5",
"bignumber.js": "^9.0.0", "bchaddrjs-slp": "0.2.5",
"bip-schnorr": "^0.3.0", "bigi": "1.4.2",
"bip21": "Bitcoin-com/bip21", "bignumber.js": "9.0.0",
"bip32-utils": "Bitcoin-com/bip32-utils#0.13.1", "bip-schnorr": "0.3.0",
"bip38": "^2.0.2", "bip38": "2.0.2",
"bip39": "^3.0.2", "bip39": "3.0.2",
"bip66": "^1.1.5", "bip66": "1.1.5",
"bitcoincash-ops": "christroutner/bitcoincash-ops", "bitcoinjs-message": "2.0.0",
"bitcoincashjs-lib": "christroutner/bitcoincashjs-lib#master", "bs58": "4.0.1",
"bitcoinjs-message": "^2.0.0", "ecashaddrjs": "1.0.7",
"bs58": "^4.0.1", "ini": "1.3.8",
"buffer": "^5.1.0", "randombytes": "2.0.6",
"cashaddrjs": "^0.3.3", "safe-buffer": "5.1.2",
"chalk": "^2.3.0", "satoshi-bitcoin": "1.0.4",
"clear": "0.1.0",
"coininfo": "Bitcoin-com/coininfo",
"commander": "^3.0.0",
"cp-file": "^7.0.0",
"ecurve": "^1.0.6",
"figlet": "^1.2.0",
"git-clone": "^0.1.0",
"ini": "^1.3.5",
"mkdirp": "^0.5.1",
"node-cmd": "^3.0.0",
"node-emoji": "^1.8.1",
"qrcode": "^1.4.1",
"randombytes": "^2.0.6",
"repl.history": "^0.1.4",
"safe-buffer": "^5.1.2",
"satoshi-bitcoin": "^1.0.4",
"slp-mdm": "0.0.6", "slp-mdm": "0.0.6",
"slp-parser": "0.0.4", "slp-parser": "0.0.4",
"socket.io": "^2.1.1", "wif": "2.0.6"
"socket.io-client": "^2.1.1",
"touch": "^3.1.0",
"wif": "^2.0.6"
}, },
"devDependencies": { "devDependencies": {
"chai": "^4.1.2", "apidoc": "0.17.7",
"coveralls": "^3.0.2", "assert": "2.0.0",
"eslint": "5.16.0", "chai": "4.1.2",
"eslint-config-prettier": "^6.0.0", "coveralls": "3.0.2",
"eslint-plugin-node": "^9.1.0", "eslint": "7.17.0",
"eslint-plugin-prettier": "^3.1.0", "eslint-config-prettier": "7.1.0",
"lodash.clonedeep": "^4.5.0", "eslint-config-standard": "16.0.3",
"mocha": "^6.1.4", "eslint-plugin-node": "11.1.0",
"nock": "^10.0.6", "eslint-plugin-prettier": "3.3.1",
"node-mocks-http": "^1.7.0", "eslint-plugin-standard": "4.0.0",
"nyc": "^14.1.0", "husky": "^4.3.8",
"prettier": "^1.18.2", "lodash.clonedeep": "4.5.0",
"semantic-release": "^17.3.0", "mocha": "9.1.3",
"sinon": "^7.3.2" "node-mocks-http": "1.7.0",
"nyc": "15.1.0",
"semantic-release": "18.0.0",
"sinon": "9.2.2",
"standard": "^16.0.4"
}, },
"apidoc": { "apidoc": {
"title": "bch-js", "title": "bch-js",
"url": "bchjs." "url": "bchjs."
},
"husky": {
"hooks": {
"pre-commit": "npm run lint"
}
} }
} }
+155 -81
View File
@@ -1,12 +1,12 @@
//const axios = require("axios") // const axios = require("axios")
const Bitcoin = require("bitcoincashjs-lib") const Bitcoin = require('@psf/bitcoincashjs-lib')
const cashaddr = require("cashaddrjs") const cashaddr = require('ecashaddrjs')
const coininfo = require("coininfo") const coininfo = require('@psf/coininfo')
class Address { class Address {
constructor(config) { constructor (config) {
const tmp = {} const tmp = {}
if (!config || !config.restURL) tmp.restURL = `https://api.bchjs.cash/v4/` if (!config || !config.restURL) tmp.restURL = 'https://api.bchjs.cash/v5/'
else tmp.restURL = config.restURL else tmp.restURL = config.restURL
this.restURL = tmp.restURL this.restURL = tmp.restURL
@@ -54,34 +54,34 @@ class Address {
* // mqc1tmwY2368LLGktnePzEyPAsgADxbksi * // mqc1tmwY2368LLGktnePzEyPAsgADxbksi
*/ */
// Translate address from any address format into a specific format. // Translate address from any address format into a specific format.
toLegacyAddress(address) { toLegacyAddress (address) {
const { prefix, type, hash } = this._decode(address) const { prefix, type, hash } = this._decode(address)
let bitcoincash let bitcoincash
switch (prefix) { switch (prefix) {
case "bitcoincash": case 'bitcoincash':
bitcoincash = coininfo.bitcoincash.main bitcoincash = coininfo.bitcoincash.main
break break
case "bchtest": case 'bchtest':
bitcoincash = coininfo.bitcoincash.test bitcoincash = coininfo.bitcoincash.test
break break
case "bchreg": case 'bchreg':
bitcoincash = coininfo.bitcoincash.regtest bitcoincash = coininfo.bitcoincash.regtest
break break
default: default:
throw `unsupported prefix : ${prefix}` throw new Error(`unsupported prefix : ${prefix}`)
} }
let version let version
switch (type) { switch (type) {
case "P2PKH": case 'P2PKH':
version = bitcoincash.versions.public version = bitcoincash.versions.public
break break
case "P2SH": case 'P2SH':
version = bitcoincash.versions.scripthash version = bitcoincash.versions.scripthash
break break
default: default:
throw `unsupported address type : ${type}` throw new Error(`unsupported address type : ${type}`)
} }
const hashBuf = Buffer.from(hash) const hashBuf = Buffer.from(hash)
@@ -112,11 +112,11 @@ class Address {
* bchjs.Address.toCashAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx', false) * bchjs.Address.toCashAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx', false)
* // qzq9je6pntpva3wf6scr7mlnycr54sjgeqxgrr9ku3 * // qzq9je6pntpva3wf6scr7mlnycr54sjgeqxgrr9ku3
*/ */
toCashAddress(address, prefix = true, regtest = false) { toCashAddress (address, prefix = true, regtest = false) {
const decoded = this._decode(address) const decoded = this._decode(address)
let prefixString let prefixString
if (regtest) prefixString = "bchreg" if (regtest) prefixString = 'bchreg'
else prefixString = decoded.prefix else prefixString = decoded.prefix
const cashAddress = cashaddr.encode( const cashAddress = cashaddr.encode(
@@ -126,14 +126,71 @@ class Address {
) )
if (prefix) return cashAddress if (prefix) return cashAddress
return cashAddress.split(":")[1] return cashAddress.split(':')[1]
}
/**
* @api Address.toEcashAddress() toEcashAddress()
* @apiName toEcashAddress
* @apiGroup Address
* @apiDescription Convert legacy to cashAddress format
*
* @apiExample Example usage:
* // mainnet
* bchjs.Address.toEcashAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2')
* // ecash:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
*
* // mainnet no prefix
* bchjs.Address.toEcashAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2', false)
* // qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
*
*/
toEcashAddress (address, prefix = true) {
const decoded = this._decode(address)
const ecashAddress = cashaddr.encode(
'ecash',
decoded.type,
decoded.hash
)
if (prefix) return ecashAddress
return ecashAddress.split(':')[1]
}
/**
* @api Address.ecashtoCashAddress() ecashtoCashAddress()
* @apiName ecashtoCashAddress
* @apiGroup Address
* @apiDescription Convert legacy to cashAddress format
*
* @apiExample Example usage:
* // mainnet
* bchjs.Address.ecashtoCashAddress('ecash:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da')
* // bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2
*
* // mainnet no prefix
* bchjs.Address.ecashtoCashAddress('ecash:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da', false)
* // qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2
*/
ecashtoCashAddress (address, prefix = true) {
const decoded = this._decodeEcashAddress(address)
const cashAddress = cashaddr.encode(
'bitcoincash',
decoded.type,
decoded.hash
)
if (prefix) return cashAddress
return cashAddress.split(':')[1]
} }
// Converts any address format to hash160 // Converts any address format to hash160
toHash160(address) { toHash160 (address) {
const legacyAddress = this.toLegacyAddress(address) const legacyAddress = this.toLegacyAddress(address)
const bytes = Bitcoin.address.fromBase58Check(legacyAddress) const bytes = Bitcoin.address.fromBase58Check(legacyAddress)
return bytes.hash.toString("hex") return bytes.hash.toString('hex')
} }
/** /**
@@ -156,8 +213,8 @@ class Address {
* // mhTg9sgNgvAGfmJs192oUzQWqAXHH5nqLE * // mhTg9sgNgvAGfmJs192oUzQWqAXHH5nqLE
*/ */
// Converts hash160 to Legacy Address // Converts hash160 to Legacy Address
hash160ToLegacy(hash160, network = Bitcoin.networks.bitcoin.pubKeyHash) { hash160ToLegacy (hash160, network = Bitcoin.networks.bitcoin.pubKeyHash) {
const buffer = Buffer.from(hash160, "hex") const buffer = Buffer.from(hash160, 'hex')
const legacyAddress = Bitcoin.address.toBase58Check(buffer, network) const legacyAddress = Bitcoin.address.toBase58Check(buffer, network)
return legacyAddress return legacyAddress
} }
@@ -166,7 +223,7 @@ class Address {
* @api Address.hash160ToCash() hash160ToCash() * @api Address.hash160ToCash() hash160ToCash()
* @apiName hash160ToCash * @apiName hash160ToCash
* @apiGroup Address * @apiGroup Address
* @apiDescription Convert hash160 to cash address. * @apiDescription Convert hash160 to cash address. Accepts either hexadecimal or buffer.
* *
* @apiExample Example usage: * @apiExample Example usage:
* bchjs.Address.hash160ToCash("573d93b475be4f1925f3b74ed951201b0147eac1") * bchjs.Address.hash160ToCash("573d93b475be4f1925f3b74ed951201b0147eac1")
@@ -177,7 +234,7 @@ class Address {
* 'bchtest:qq24rpar9qas3vc9r8d4p0prhwaf7jmx2u22nzt946' * 'bchtest:qq24rpar9qas3vc9r8d4p0prhwaf7jmx2u22nzt946'
*/ */
// Converts hash160 to Cash Address // Converts hash160 to Cash Address
hash160ToCash( hash160ToCash (
hash160, hash160,
network = Bitcoin.networks.bitcoin.pubKeyHash, network = Bitcoin.networks.bitcoin.pubKeyHash,
regtest = false regtest = false
@@ -186,7 +243,7 @@ class Address {
return this.toCashAddress(legacyAddress, true, regtest) return this.toCashAddress(legacyAddress, true, regtest)
} }
_decode(address) { _decode (address) {
try { try {
return this._decodeLegacyAddress(address) return this._decodeLegacyAddress(address)
} catch (error) {} } catch (error) {}
@@ -202,56 +259,56 @@ class Address {
throw new Error(`Unsupported address format : ${address}`) throw new Error(`Unsupported address format : ${address}`)
} }
_decodeLegacyAddress(address) { _decodeLegacyAddress (address) {
const { version, hash } = Bitcoin.address.fromBase58Check(address) const { version, hash } = Bitcoin.address.fromBase58Check(address)
const info = coininfo.bitcoincash const info = coininfo.bitcoincash
switch (version) { switch (version) {
case info.main.versions.public: case info.main.versions.public:
return { return {
prefix: "bitcoincash", prefix: 'bitcoincash',
type: "P2PKH", type: 'P2PKH',
hash: hash, hash: hash,
format: "legacy" format: 'legacy'
} }
case info.main.versions.scripthash: case info.main.versions.scripthash:
return { return {
prefix: "bitcoincash", prefix: 'bitcoincash',
type: "P2SH", type: 'P2SH',
hash: hash, hash: hash,
format: "legacy" format: 'legacy'
} }
case info.test.versions.public: case info.test.versions.public:
return { return {
prefix: "bchtest", prefix: 'bchtest',
type: "P2PKH", type: 'P2PKH',
hash: hash, hash: hash,
format: "legacy" format: 'legacy'
} }
case info.test.versions.scripthash: case info.test.versions.scripthash:
return { return {
prefix: "bchtest", prefix: 'bchtest',
type: "P2SH", type: 'P2SH',
hash: hash, hash: hash,
format: "legacy" format: 'legacy'
} }
default: default:
throw new Error(`Invalid format : ${address}`) throw new Error(`Invalid format : ${address}`)
} }
} }
_decodeCashAddress(address) { _decodeCashAddress (address) {
if (address.indexOf(":") !== -1) { if (address.indexOf(':') !== -1) {
const decoded = cashaddr.decode(address) const decoded = cashaddr.decode(address)
decoded.format = "cashaddr" decoded.format = 'cashaddr'
return decoded return decoded
} }
const prefixes = ["bitcoincash", "bchtest", "bchreg"] const prefixes = ['bitcoincash', 'bchtest', 'bchreg']
for (let i = 0; i < prefixes.length; ++i) { for (let i = 0; i < prefixes.length; ++i) {
try { try {
const decoded = cashaddr.decode(`${prefixes[i]}:${address}`) const decoded = cashaddr.decode(`${prefixes[i]}:${address}`)
decoded.format = "cashaddr" decoded.format = 'cashaddr'
return decoded return decoded
} catch (error) {} } catch (error) {}
} }
@@ -259,18 +316,34 @@ class Address {
throw new Error(`Invalid format : ${address}`) throw new Error(`Invalid format : ${address}`)
} }
_encodeAddressFromHash160(address) { _encodeAddressFromHash160 (address) {
try { try {
return { return {
legacyAddress: this.hash160ToLegacy(address), legacyAddress: this.hash160ToLegacy(address),
cashAddress: this.hash160ToCash(address), cashAddress: this.hash160ToCash(address),
format: "hash160" format: 'hash160'
} }
} catch (error) {} } catch (error) {}
throw new Error(`Invalid format : ${address}`) throw new Error(`Invalid format : ${address}`)
} }
_decodeEcashAddress (address) {
if (address.indexOf(':') !== -1) {
const decoded = cashaddr.decode(address)
decoded.format = 'cashaddr'
return decoded
}
try {
const decoded = cashaddr.decode(`ecash:${address}`)
decoded.format = 'cashaddr'
return decoded
} catch (error) {}
throw new Error(`Invalid format : ${address}`)
}
/** /**
* @api Address.isLegacyAddress() isLegacyAddress() * @api Address.isLegacyAddress() isLegacyAddress()
* @apiName isLegacyAddress * @apiName isLegacyAddress
@@ -303,8 +376,8 @@ class Address {
* // true * // true
*/ */
// Test for address format. // Test for address format.
isLegacyAddress(address) { isLegacyAddress (address) {
return this.detectAddressFormat(address) === "legacy" return this.detectAddressFormat(address) === 'legacy'
} }
/** /**
@@ -338,8 +411,8 @@ class Address {
* bchjs.Address.isCashAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi') * bchjs.Address.isCashAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
* // false * // false
*/ */
isCashAddress(address) { isCashAddress (address) {
return this.detectAddressFormat(address) === "cashaddr" return this.detectAddressFormat(address) === 'cashaddr'
} }
/** /**
@@ -357,8 +430,8 @@ class Address {
* bchjs.Address.isHash160(notHash160Address); * bchjs.Address.isHash160(notHash160Address);
* // false * // false
*/ */
isHash160(address) { isHash160 (address) {
return this.detectAddressFormat(address) === "hash160" return this.detectAddressFormat(address) === 'hash160'
} }
/** /**
@@ -393,11 +466,11 @@ class Address {
* // false * // false
*/ */
// Test for address network. // Test for address network.
isMainnetAddress(address) { isMainnetAddress (address) {
if (address[0] === "x") return true if (address[0] === 'x') return true
else if (address[0] === "t") return false else if (address[0] === 't') return false
return this.detectAddressNetwork(address) === "mainnet" return this.detectAddressNetwork(address) === 'mainnet'
} }
/** /**
@@ -431,11 +504,11 @@ class Address {
* bchjs.Address.isTestnetAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi') * bchjs.Address.isTestnetAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
* // true * // true
*/ */
isTestnetAddress(address) { isTestnetAddress (address) {
if (address[0] === "x") return false if (address[0] === 'x') return false
else if (address[0] === "t") return true else if (address[0] === 't') return true
return this.detectAddressNetwork(address) === "testnet" return this.detectAddressNetwork(address) === 'testnet'
} }
/** /**
@@ -473,8 +546,8 @@ class Address {
* bchjs.Address.isRegTestAddress('qph2v4mkxjgdqgmlyjx6njmey0ftrxlnggt9t0a6zy') * bchjs.Address.isRegTestAddress('qph2v4mkxjgdqgmlyjx6njmey0ftrxlnggt9t0a6zy')
* // false * // false
*/ */
isRegTestAddress(address) { isRegTestAddress (address) {
return this.detectAddressNetwork(address) === "regtest" return this.detectAddressNetwork(address) === 'regtest'
} }
/** /**
@@ -510,8 +583,8 @@ class Address {
*/ */
// Test for address type. // Test for address type.
isP2PKHAddress(address) { isP2PKHAddress (address) {
return this.detectAddressType(address) === "p2pkh" return this.detectAddressType(address) === 'p2pkh'
} }
/** /**
@@ -546,8 +619,8 @@ class Address {
* // false * // false
*/ */
isP2SHAddress(address) { isP2SHAddress (address) {
return this.detectAddressType(address) === "p2sh" return this.detectAddressType(address) === 'p2sh'
} }
/** /**
@@ -582,7 +655,7 @@ class Address {
* // legacy * // legacy
*/ */
// Detect address format. // Detect address format.
detectAddressFormat(address) { detectAddressFormat (address) {
const decoded = this._decode(address) const decoded = this._decode(address)
return decoded.format return decoded.format
@@ -620,19 +693,19 @@ class Address {
* // testnet * // testnet
*/ */
// Detect address network. // Detect address network.
detectAddressNetwork(address) { detectAddressNetwork (address) {
if (address[0] === "x") return "mainnet" if (address[0] === 'x') return 'mainnet'
else if (address[0] === "t") return "testnet" else if (address[0] === 't') return 'testnet'
const decoded = this._decode(address) const decoded = this._decode(address)
switch (decoded.prefix) { switch (decoded.prefix) {
case "bitcoincash": case 'bitcoincash':
return "mainnet" return 'mainnet'
case "bchtest": case 'bchtest':
return "testnet" return 'testnet'
case "bchreg": case 'bchreg':
return "regtest" return 'regtest'
default: default:
throw new Error(`Invalid prefix : ${decoded.prefix}`) throw new Error(`Invalid prefix : ${decoded.prefix}`)
} }
@@ -670,7 +743,7 @@ class Address {
* // p2pkh * // p2pkh
*/ */
// Detect address type. // Detect address type.
detectAddressType(address) { detectAddressType (address) {
const decoded = this._decode(address) const decoded = this._decode(address)
return decoded.type.toLowerCase() return decoded.type.toLowerCase()
@@ -705,7 +778,7 @@ class Address {
* // bchtest:qzd7dvlnfukggjqsf5ju0qqwwltakfumjsck33js6m * // bchtest:qzd7dvlnfukggjqsf5ju0qqwwltakfumjsck33js6m
* // bchtest:qq322ataqeas4n0pdn4gz2sdereh5ae43ylk4qdvus * // bchtest:qq322ataqeas4n0pdn4gz2sdereh5ae43ylk4qdvus
*/ */
fromXPub(xpub, path = "0/0") { fromXPub (xpub, path = '0/0') {
const HDNode = Bitcoin.HDNode.fromBase58( const HDNode = Bitcoin.HDNode.fromBase58(
xpub, xpub,
Bitcoin.networks[this.detectAddressNetwork(xpub)] Bitcoin.networks[this.detectAddressNetwork(xpub)]
@@ -721,13 +794,13 @@ class Address {
* @apiDescription Detect an addess from an OutputScript.. * @apiDescription Detect an addess from an OutputScript..
* *
* @apiExample Example usage: * @apiExample Example usage:
* const script = bchjs.Script.encode([ * const scriptBuffer = bchjs.Script.encode([
* Buffer.from("BOX", "ascii"), * Buffer.from("BOX", "ascii"),
* bchjs.Script.opcodes.OP_CAT, * bchjs.Script.opcodes.OP_CAT,
* Buffer.from("BITBOX", "ascii"), * Buffer.from("BITBOX", "ascii"),
* bchjs.Script.opcodes.OP_EQUAL * bchjs.Script.opcodes.OP_EQUAL
* ]); * ]);
* const p2sh_hash160 = bchjs.Crypto.hash160(script); * const p2sh_hash160 = bchjs.Crypto.hash160(scriptBuffer);
* const scriptPubKey = bchjs.Script.scriptHash.output.encode(p2sh_hash160); * const scriptPubKey = bchjs.Script.scriptHash.output.encode(p2sh_hash160);
* *
* // mainnet address from output script * // mainnet address from output script
@@ -738,12 +811,13 @@ class Address {
* bchjs.Address.fromOutputScript(scriptPubKey, 'testnet'); * bchjs.Address.fromOutputScript(scriptPubKey, 'testnet');
* // bchtest:pz0qcslrqn7hr44hsszwl4lw5r6udkg6zqh2hmtpyr * // bchtest:pz0qcslrqn7hr44hsszwl4lw5r6udkg6zqh2hmtpyr
*/ */
fromOutputScript(scriptPubKey, network = "mainnet") { fromOutputScript (scriptPubKey, network = 'mainnet') {
let netParam let netParam
if (network !== "bitcoincash" && network !== "mainnet") if (network !== 'bitcoincash' && network !== 'mainnet') {
netParam = Bitcoin.networks.testnet netParam = Bitcoin.networks.testnet
}
const regtest = network === "bchreg" const regtest = network === 'bchreg'
return this.toCashAddress( return this.toCashAddress(
Bitcoin.address.fromOutputScript(scriptPubKey, netParam), Bitcoin.address.fromOutputScript(scriptPubKey, netParam),
+43 -128
View File
@@ -7,68 +7,69 @@
*/ */
// bch-api mainnet. // bch-api mainnet.
const DEFAULT_REST_API = "https://api.fullstack.cash/v4/" const DEFAULT_REST_API = 'https://api.fullstack.cash/v5/'
// const DEFAULT_REST_API = "http://localhost:3000/v4/" // const DEFAULT_REST_API = "http://localhost:3000/v5/"
// local deps // local deps
const BitcoinCash = require("./bitcoincash") const BitcoinCash = require('./bitcoincash')
const Crypto = require("./crypto") const Crypto = require('./crypto')
const Util = require("./util") const Util = require('./util')
const Blockchain = require("./blockchain") const Blockchain = require('./blockchain')
const Control = require("./control") const Control = require('./control')
const Generating = require("./generating") const Generating = require('./generating')
const Mining = require("./mining") const Mining = require('./mining')
const RawTransactions = require("./raw-transactions") const RawTransactions = require('./raw-transactions')
const Mnemonic = require("./mnemonic") const Mnemonic = require('./mnemonic')
const Address = require("./address") const Address = require('./address')
const HDNode = require("./hdnode") const HDNode = require('./hdnode')
const TransactionBuilder = require("./transaction-builder") const TransactionBuilder = require('./transaction-builder')
const ECPair = require("./ecpair") const ECPair = require('./ecpair')
const Script = require("./script") const Script = require('./script')
const Price = require("./price") const Price = require('./price')
const Socket = require("./socket") const Schnorr = require('./schnorr')
const Wallet = require("./wallet") const SLP = require('./slp/slp')
const Schnorr = require("./schnorr") const IPFS = require('./ipfs')
const SLP = require("./slp/slp") const Encryption = require('./encryption')
const IPFS = require("./ipfs") const Utxo = require('./utxo')
const Encryption = require("./encryption") const Transaction = require('./transaction')
const DSProof = require('./dsproof')
// Indexers // Indexers
const Blockbook = require("./blockbook") const Ninsight = require('./ninsight')
const OpenBazaar = require("./openbazaar") const Electrumx = require('./electrumx')
const Ninsight = require("./ninsight")
const Electrumx = require("./electrumx")
class BCHJS { class BCHJS {
constructor(config) { constructor (config) {
// Try to retrieve the REST API URL from different sources. // Try to retrieve the REST API URL from different sources.
if (config && config.restURL && config.restURL !== "") if (config && config.restURL && config.restURL !== '') {
this.restURL = config.restURL this.restURL = config.restURL
else if (process.env.RESTURL && process.env.RESTURL !== "") } else if (process.env.RESTURL && process.env.RESTURL !== '') {
this.restURL = process.env.RESTURL this.restURL = process.env.RESTURL
else this.restURL = DEFAULT_REST_API } else this.restURL = DEFAULT_REST_API
// Retrieve the apiToken // Retrieve the apiToken
this.apiToken = "" // default value. this.apiToken = '' // default value.
if (config && config.apiToken && config.apiToken !== "") if (config && config.apiToken && config.apiToken !== '') {
this.apiToken = config.apiToken this.apiToken = config.apiToken
else if (process.env.BCHJSTOKEN && process.env.BCHJSTOKEN !== "") } else if (process.env.BCHJSTOKEN && process.env.BCHJSTOKEN !== '') {
this.apiToken = process.env.BCHJSTOKEN this.apiToken = process.env.BCHJSTOKEN
}
// Retrieve the Basic Authentication password. // Retrieve the Basic Authentication password.
this.authPass = "" // default value. this.authPass = '' // default value.
if (config && config.authPass && config.authPass !== "") if (config && config.authPass && config.authPass !== '') {
this.authPass = config.authPass this.authPass = config.authPass
else if (process.env.BCHJSAUTHPASS && process.env.BCHJSAUTHPASS !== "") } else if (process.env.BCHJSAUTHPASS && process.env.BCHJSAUTHPASS !== '') {
this.authPass = process.env.BCHJSAUTHPASS this.authPass = process.env.BCHJSAUTHPASS
}
// Generate a Basic Authentication token from an auth password // Generate a Basic Authentication token from an auth password
this.authToken = "" this.authToken = ''
if (this.authPass) { if (this.authPass) {
// console.log(`bch-js initialized with authPass: ${this.authPass}`) // console.log(`bch-js initialized with authPass: ${this.authPass}`)
// Generate the header for Basic Authentication. // Generate the header for Basic Authentication.
const combined = `fullstackcash:${this.authPass}` const combined = `fullstackcash:${this.authPass}`
var base64Credential = Buffer.from(combined).toString("base64") const base64Credential = Buffer.from(combined).toString('base64')
this.authToken = `Basic ${base64Credential}` this.authToken = `Basic ${base64Credential}`
} }
@@ -80,12 +81,6 @@ class BCHJS {
// console.log(`apiToken: ${this.apiToken}`) // console.log(`apiToken: ${this.apiToken}`)
// Populate Blockbook endpoints.
this.Blockbook = new Blockbook(libConfig)
// Populate OpenBazaar endpoints
this.OpenBazaar = new OpenBazaar(libConfig)
// Bitcoin.com Ninsight indexer // Bitcoin.com Ninsight indexer
this.Ninsight = new Ninsight(config) this.Ninsight = new Ninsight(config)
@@ -113,96 +108,16 @@ class BCHJS {
this.TransactionBuilder = TransactionBuilder this.TransactionBuilder = TransactionBuilder
this.TransactionBuilder.setAddress(this.Address) this.TransactionBuilder.setAddress(this.Address)
this.Util = new Util(libConfig) this.Util = new Util(libConfig)
this.Socket = Socket
this.Wallet = Wallet
this.Schnorr = new Schnorr(libConfig) this.Schnorr = new Schnorr(libConfig)
this.SLP = new SLP(libConfig) this.SLP = new SLP(libConfig)
this.SLP.HDNode = this.HDNode this.SLP.HDNode = this.HDNode
this.IPFS = new IPFS() this.IPFS = new IPFS()
} this.Utxo = new Utxo(libConfig)
this.Transaction = new Transaction(libConfig)
static BitboxShim() { this.DSProof = new DSProof(libConfig)
return BitboxShim
}
}
// Creat a shim so that all the endpoints match BITBOX SDK and this library
// can be used as a drop-in replacement for BITBOX.
class BitboxShim {
constructor(config) {
// Try to retrieve the REST API URL from different sources.
if (config && config.restURL && config.restURL !== "")
this.restURL = config.restURL
else if (process.env.RESTURL && process.env.RESTURL !== "")
this.restURL = process.env.RESTURL
else this.restURL = DEFAULT_REST_API
// Retrieve the apiToken
this.apiToken = "" // default value.
if (config && config.apiToken && config.apiToken !== "")
this.apiToken = config.apiToken
else if (process.env.BCHJSTOKEN && process.env.BCHJSTOKEN !== "")
this.apiToken = process.env.BCHJSTOKEN
const libConfig = {
restURL: this.restURL,
apiToken: this.apiToken
}
// Populate utility functions
this.Address = new Address(libConfig)
this.BitcoinCash = new BitcoinCash(this.Address)
this.Blockchain = new Blockchain(libConfig)
this.Crypto = Crypto
this.ECPair = ECPair
this.ECPair.setAddress(this.Address)
this.Generating = new Generating(libConfig)
this.HDNode = new HDNode(this.Address)
this.Mnemonic = new Mnemonic(this.Address)
this.Price = new Price()
this.Script = new Script()
this.TransactionBuilder = TransactionBuilder
this.TransactionBuilder.setAddress(this.Address)
this.Util = new Util(libConfig)
this.Socket = Socket
this.Wallet = Wallet
this.Schnorr = new Schnorr(libConfig)
// Populate the SLP endpoints.
this.SLP = new SLP(libConfig)
this.Address = this.SLP.Address
this.ECPair.toSLPAddress = this.SLP.ECPair.toSLPAddress
this.Util.list = this.SLP.Utils.list
this.Util.balancesForAddress = this.SLP.Utils.balancesForAddress
this.Util.balancesForToken = this.SLP.Utils.balancesForToken
this.Util.balance = this.SLP.Utils.balance
this.Util.validateTxid = this.SLP.Utils.validateTxid
this.Util.tokenStats = this.SLP.Utils.tokenStats
this.Util.transactions = this.SLP.Utils.transactions
this.Util.burnTotal = this.SLP.Utils.burnTotal
this.Util.decodeOpReturn = this.SLP.Utils.decodeOpReturn
this.Util.isTokenUtxo = this.SLP.Utils.isTokenUtxo
this.Util.tokenUtxoDetails = this.SLP.Utils.tokenUtxoDetails
this.Util.txDetails = this.SLP.Utils.txDetails
// Populate Full Node
this.Control = new Control(libConfig)
this.Mining = new Mining(libConfig)
this.RawTransactions = new RawTransactions(libConfig)
// Populate Blockbook endpoints.
this.Blockbook = new Blockbook(libConfig)
// Populate OpenBazaar endpoints
this.OpenBazaar = new OpenBazaar(libConfig)
// Bitcoin.com Ninsight indexer
this.Ninsight = new Ninsight()
this.Electrumx = new Electrumx(libConfig)
} }
} }
+40 -40
View File
@@ -1,16 +1,16 @@
const Bitcoin = require("bitcoincashjs-lib") const Bitcoin = require('@psf/bitcoincashjs-lib')
const sb = require("satoshi-bitcoin") const sb = require('satoshi-bitcoin')
const bitcoinMessage = require("bitcoinjs-message") const bitcoinMessage = require('bitcoinjs-message')
const bs58 = require("bs58") const bs58 = require('bs58')
const bip21 = require("bip21") const bip21 = require('@psf/bip21')
const coininfo = require("coininfo") const coininfo = require('@psf/coininfo')
const bip38 = require("bip38") const bip38 = require('bip38')
const wif = require("wif") const wif = require('wif')
const Buffer = require("safe-buffer").Buffer const Buffer = require('safe-buffer').Buffer
class BitcoinCash { class BitcoinCash {
constructor(address) { constructor (address) {
this._address = address this._address = address
} }
@@ -43,7 +43,7 @@ class BitcoinCash {
* // 50700000000 * // 50700000000
*/ */
// Translate coins to satoshi value // Translate coins to satoshi value
toSatoshi(coins) { toSatoshi (coins) {
return sb.toSatoshi(coins) return sb.toSatoshi(coins)
} }
@@ -76,7 +76,7 @@ class BitcoinCash {
* // 507 * // 507
*/ */
// Translate satoshi to coin value // Translate satoshi to coin value
toBitcoinCash(satoshis) { toBitcoinCash (satoshis) {
return sb.toBitcoin(satoshis) return sb.toBitcoin(satoshis)
} }
@@ -105,13 +105,13 @@ class BitcoinCash {
* // 0.123 * // 0.123
*/ */
// Translate satoshi to bits denomination // Translate satoshi to bits denomination
toBits(satoshis) { toBits (satoshis) {
return parseFloat(satoshis) / 100 return parseFloat(satoshis) / 100
} }
// Translate satoshi to bits denomination // Translate satoshi to bits denomination
// TODO remove in 2.0 // TODO remove in 2.0
satsToBits(satoshis) { satsToBits (satoshis) {
return parseFloat(satoshis) / 100 return parseFloat(satoshis) / 100
} }
@@ -147,10 +147,10 @@ class BitcoinCash {
* // IIYVhlo2Z6TWFjYX1+YM+7vQKz0m+zYdSe4eYpFLuAQDEZXqll7lZC8Au22VI2LLP5x+IerZckVk3QQPsA3e8/8= * // IIYVhlo2Z6TWFjYX1+YM+7vQKz0m+zYdSe4eYpFLuAQDEZXqll7lZC8Au22VI2LLP5x+IerZckVk3QQPsA3e8/8=
*/ */
// sign message // sign message
signMessageWithPrivKey(privateKeyWIF, message) { signMessageWithPrivKey (privateKeyWIF, message) {
const network = privateKeyWIF.charAt(0) === "c" ? "testnet" : "mainnet" const network = privateKeyWIF.charAt(0) === 'c' ? 'testnet' : 'mainnet'
let bitcoincash let bitcoincash
if (network === "mainnet") bitcoincash = coininfo.bitcoincash.main if (network === 'mainnet') bitcoincash = coininfo.bitcoincash.main
else bitcoincash = coininfo.bitcoincash.test else bitcoincash = coininfo.bitcoincash.test
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS() const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
@@ -161,7 +161,7 @@ class BitcoinCash {
const privateKey = keyPair.d.toBuffer(32) const privateKey = keyPair.d.toBuffer(32)
return bitcoinMessage return bitcoinMessage
.sign(message, privateKey, keyPair.compressed) .sign(message, privateKey, keyPair.compressed)
.toString("base64") .toString('base64')
} }
/** /**
@@ -180,7 +180,7 @@ class BitcoinCash {
* // true * // true
*/ */
// verify message // verify message
verifyMessage(address, signature, message) { verifyMessage (address, signature, message) {
return bitcoinMessage.verify( return bitcoinMessage.verify(
message, message,
this._address.toLegacyAddress(address), this._address.toLegacyAddress(address),
@@ -222,8 +222,8 @@ class BitcoinCash {
* // 1Ly4gqPddveYHMNkfjoXHanVszXpD3duKg * // 1Ly4gqPddveYHMNkfjoXHanVszXpD3duKg
*/ */
// encode base58Check // encode base58Check
encodeBase58Check(hex) { encodeBase58Check (hex) {
return bs58.encode(Buffer.from(hex, "hex")) return bs58.encode(Buffer.from(hex, 'hex'))
} }
/** /**
@@ -260,8 +260,8 @@ class BitcoinCash {
* // 00db04c2e6f104997cb04c956bf25da6078e559d303127f08b * // 00db04c2e6f104997cb04c956bf25da6078e559d303127f08b
*/ */
// decode base58Check // decode base58Check
decodeBase58Check(address) { decodeBase58Check (address) {
return bs58.decode(address).toString("hex") return bs58.decode(address).toString('hex')
} }
/** /**
@@ -298,7 +298,7 @@ class BitcoinCash {
* // bitcoincash:qzw6tfrh8p0jh834uf9rhg77pjg5rgnt3qw0e54u03?amount=42&label=no%20prefix * // bitcoincash:qzw6tfrh8p0jh834uf9rhg77pjg5rgnt3qw0e54u03?amount=42&label=no%20prefix
*/ */
// encode bip21 url // encode bip21 url
encodeBIP21(address, options, regtest = false) { encodeBIP21 (address, options, regtest = false) {
return bip21.encode( return bip21.encode(
this._address.toCashAddress(address, true, regtest), this._address.toCashAddress(address, true, regtest),
options options
@@ -335,7 +335,7 @@ class BitcoinCash {
* // { address: 'qzw6tfrh8p0jh834uf9rhg77pjg5rgnt3qw0e54u03', options: { amount: 42, label: 'no prefix' } } * // { address: 'qzw6tfrh8p0jh834uf9rhg77pjg5rgnt3qw0e54u03', options: { amount: 42, label: 'no prefix' } }
*/ */
// decode bip21 url // decode bip21 url
decodeBIP21(url) { decodeBIP21 (url) {
return bip21.decode(url) return bip21.decode(url)
} }
@@ -405,19 +405,19 @@ class BitcoinCash {
* bchjs.BitcoinCash.getByteCount(inputs, outputs) * bchjs.BitcoinCash.getByteCount(inputs, outputs)
* // 1780 * // 1780
*/ */
getByteCount(inputs, outputs) { getByteCount (inputs, outputs) {
// from https://github.com/bitcoinjs/bitcoinjs-lib/issues/921#issuecomment-354394004 // from https://github.com/bitcoinjs/bitcoinjs-lib/issues/921#issuecomment-354394004
let totalWeight = 0 let totalWeight = 0
let hasWitness = false let hasWitness = false
// assumes compressed pubkeys in all cases. // assumes compressed pubkeys in all cases.
const types = { const types = {
inputs: { inputs: {
"MULTISIG-P2SH": 49 * 4, 'MULTISIG-P2SH': 49 * 4,
"MULTISIG-P2WSH": 6 + 41 * 4, 'MULTISIG-P2WSH': 6 + 41 * 4,
"MULTISIG-P2SH-P2WSH": 6 + 76 * 4, 'MULTISIG-P2SH-P2WSH': 6 + 76 * 4,
P2PKH: 148 * 4, P2PKH: 148 * 4,
P2WPKH: 108 + 41 * 4, P2WPKH: 108 + 41 * 4,
"P2SH-P2WPKH": 108 + 64 * 4 'P2SH-P2WPKH': 108 + 64 * 4
}, },
outputs: { outputs: {
P2SH: 32 * 4, P2SH: 32 * 4,
@@ -427,26 +427,26 @@ class BitcoinCash {
} }
} }
Object.keys(inputs).forEach(function(key) { Object.keys(inputs).forEach(function (key) {
if (key.slice(0, 8) === "MULTISIG") { if (key.slice(0, 8) === 'MULTISIG') {
// ex. "MULTISIG-P2SH:2-3" would mean 2 of 3 P2SH MULTISIG // ex. "MULTISIG-P2SH:2-3" would mean 2 of 3 P2SH MULTISIG
const keyParts = key.split(":") const keyParts = key.split(':')
if (keyParts.length !== 2) throw new Error(`invalid input: ${key}`) if (keyParts.length !== 2) throw new Error(`invalid input: ${key}`)
const newKey = keyParts[0] const newKey = keyParts[0]
const mAndN = keyParts[1].split("-").map(function(item) { const mAndN = keyParts[1].split('-').map(function (item) {
return parseInt(item) return parseInt(item)
}) })
totalWeight += types.inputs[newKey] * inputs[key] totalWeight += types.inputs[newKey] * inputs[key]
const multiplyer = newKey === "MULTISIG-P2SH" ? 4 : 1 const multiplyer = newKey === 'MULTISIG-P2SH' ? 4 : 1
totalWeight += (73 * mAndN[0] + 34 * mAndN[1]) * multiplyer totalWeight += (73 * mAndN[0] + 34 * mAndN[1]) * multiplyer
} else { } else {
totalWeight += types.inputs[key] * inputs[key] totalWeight += types.inputs[key] * inputs[key]
} }
if (key.indexOf("W") >= 0) hasWitness = true if (key.indexOf('W') >= 0) hasWitness = true
}) })
Object.keys(outputs).forEach(function(key) { Object.keys(outputs).forEach(function (key) {
totalWeight += types.outputs[key] * outputs[key] totalWeight += types.outputs[key] * outputs[key]
}) })
@@ -479,7 +479,7 @@ class BitcoinCash {
* ) * )
* // 6PYUAPLwLSEjWSAfoe9NTSPkMZXnJA8j8EFJtKaeSnP18RCouutBrS2735 * // 6PYUAPLwLSEjWSAfoe9NTSPkMZXnJA8j8EFJtKaeSnP18RCouutBrS2735
*/ */
encryptBIP38(privKeyWIF, passphrase) { encryptBIP38 (privKeyWIF, passphrase) {
const decoded = wif.decode(privKeyWIF) const decoded = wif.decode(privKeyWIF)
return bip38.encrypt(decoded.privateKey, decoded.compressed, passphrase) return bip38.encrypt(decoded.privateKey, decoded.compressed, passphrase)
@@ -509,10 +509,10 @@ class BitcoinCash {
* ) * )
* // cSx7KzdH9EcvDEireu2WYpGnXdFYpta7sJUNt5kVCJgA7kcAU8Gm * // cSx7KzdH9EcvDEireu2WYpGnXdFYpta7sJUNt5kVCJgA7kcAU8Gm
*/ */
decryptBIP38(encryptedKey, passphrase, network = "mainnet") { decryptBIP38 (encryptedKey, passphrase, network = 'mainnet') {
const decryptedKey = bip38.decrypt(encryptedKey, passphrase) const decryptedKey = bip38.decrypt(encryptedKey, passphrase)
let prefix let prefix
if (network === "testnet") prefix = 0xef if (network === 'testnet') prefix = 0xef
else prefix = 0x80 else prefix = 0x80
return wif.encode(prefix, decryptedKey.privateKey, decryptedKey.compressed) return wif.encode(prefix, decryptedKey.privateKey, decryptedKey.compressed)
-132
View File
@@ -1,132 +0,0 @@
/*
This library interacts with the REST API endpoints in bch-api that communicate
with the Blockbook API.
CT 11/10/20
These endpoints will be soon be deprecated. Blockbook is being fazed out in
favor of Fulcrum.
*/
const axios = require("axios")
let _this
class Blockbook {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// console.log(`Blockbook apiToken: ${this.apiToken}`)
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
}
}
_this = this
}
async balance(address) {
try {
// Handle single address.
if (typeof address === "string") {
const response = await axios.get(
`${this.restURL}blockbook/balance/${address}`,
_this.axiosOptions
)
return response.data
// Handle array of addresses.
} else if (Array.isArray(address)) {
const response = await axios.post(
`${this.restURL}blockbook/balance`,
{
addresses: address
},
_this.axiosOptions
)
return response.data
}
throw new Error(`Input address must be a string or array of strings.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
async utxo(address) {
try {
// Handle single address.
if (typeof address === "string") {
const response = await axios.get(
`${this.restURL}blockbook/utxos/${address}`,
_this.axiosOptions
)
return response.data
// Handle array of addresses.
} else if (Array.isArray(address)) {
const response = await axios.post(
`${this.restURL}blockbook/utxos`,
{
addresses: address
},
_this.axiosOptions
)
return response.data
}
throw new Error(`Input address must be a string or array of strings.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
async tx(txid) {
try {
// Handle single txid.
if (typeof txid === "string") {
const response = await axios.get(
`${this.restURL}blockbook/tx/${txid}`,
_this.axiosOptions
)
return response.data
// Handle array of addresses.
} else if (Array.isArray(txid)) {
const response = await axios.post(
`${this.restURL}blockbook/tx`,
{
txids: txid
},
_this.axiosOptions
)
return response.data
}
throw new Error(`Input txid must be a string or array of strings.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = Blockbook
+100 -88
View File
@@ -3,12 +3,12 @@
- Add blockhash functionality back into getTxOutProof - Add blockhash functionality back into getTxOutProof
*/ */
const axios = require("axios") const axios = require('axios')
let _this // let _this
class Blockchain { class Blockchain {
constructor(config) { constructor (config) {
this.restURL = config.restURL this.restURL = config.restURL
this.apiToken = config.apiToken this.apiToken = config.apiToken
this.authToken = config.authToken this.authToken = config.authToken
@@ -29,7 +29,7 @@ class Blockchain {
} }
} }
_this = this // _this = this
} }
/** /**
@@ -50,11 +50,11 @@ class Blockchain {
* })() * })()
* // 241decef88889efac8e6ce428a8ac696fdde5972eceed97e1fb58d6106af31d5 * // 241decef88889efac8e6ce428a8ac696fdde5972eceed97e1fb58d6106af31d5
*/ */
async getBestBlockHash() { async getBestBlockHash () {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/getBestBlockHash`, `${this.restURL}blockchain/getBestBlockHash`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -68,8 +68,8 @@ class Blockchain {
* @apiName getBlock * @apiName getBlock
* @apiGroup Blockchain * @apiGroup Blockchain
* @apiDescription * @apiDescription
* If verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'. If verbose is true, returns an Object with information about block hash. * If verbose is 0, returns a string that is serialized, hex-encoded data for block 'hash'. If verbose is 1, returns an Object with information about block hash.
* * If verbose is 2, returns an Object with information about block hash and information about tx.
* @apiExample Example usage: * @apiExample Example usage:
* (async () => { * (async () => {
* try { * try {
@@ -80,29 +80,39 @@ class Blockchain {
* } * }
* })() * })()
* *
* // { hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09', * // {
* // confirmations: 528236, * // hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
* // size: 216, * // confirmations: 528236,
* // height: 1000, * // size: 216,
* // version: 1, * // height: 1000,
* // versionHex: '00000001', * // version: 1,
* // merkleroot: 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33', * // versionHex: '00000001',
* // tx: * // merkleroot: 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
* // [ 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33' ], * // tx:
* // time: 1232346882, * // [ 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33' ],
* // mediantime: 1232344831, * // time: 1232346882,
* // nonce: 2595206198, * // mediantime: 1232344831,
* // bits: '1d00ffff', * // nonce: 2595206198,
* // difficulty: 1, * // bits: '1d00ffff',
* // chainwork: '000000000000000000000000000000000000000000000000000003e903e903e9', * // difficulty: 1,
* // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d', * // chainwork: '000000000000000000000000000000000000000000000000000003e903e903e9',
* // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6' } * // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
* // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6'
* // }
*/ */
async getBlock(blockhash, verbose = true) { async getBlock (blockhash, verbosity = 1) {
try { try {
const response = await axios.get( // Input validation
`${this.restURL}blockchain/getBlock/${blockhash}?verbose=${verbose}`, if (!blockhash || typeof blockhash !== 'string') {
_this.axiosOptions throw new Error('blockhash must be a string')
}
const response = await axios.post(
`${this.restURL}blockchain/getblock`,
{
blockhash,
verbosity
},
this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -148,11 +158,11 @@ class Blockchain {
* // timeout: 1493596800, * // timeout: 1493596800,
* // since: 419328 } } } * // since: 419328 } } }
*/ */
async getBlockchainInfo() { async getBlockchainInfo () {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/getBlockchainInfo`, `${this.restURL}blockchain/getBlockchainInfo`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -179,11 +189,11 @@ class Blockchain {
* })() * })()
* // 529235 * // 529235
*/ */
async getBlockCount() { async getBlockCount () {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/getBlockCount`, `${this.restURL}blockchain/getBlockCount`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -210,13 +220,13 @@ class Blockchain {
* })() * })()
* // [ '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f' ] * // [ '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f' ]
*/ */
async getBlockHash(height = 1) { async getBlockHash (height = 1) {
if (typeof height !== "string") height = JSON.stringify(height) if (typeof height !== 'string') height = JSON.stringify(height)
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/getBlockHash/${height}`, `${this.restURL}blockchain/getBlockHash/${height}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -257,13 +267,13 @@ class Blockchain {
* // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d', * // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
* // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6' }] * // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6' }]
*/ */
async getBlockHeader(hash, verbose = true) { async getBlockHeader (hash, verbose = true) {
try { try {
// Handle single hash. // Handle single hash.
if (typeof hash === "string") { if (typeof hash === 'string') {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/getBlockHeader/${hash}?verbose=${verbose}`, `${this.restURL}blockchain/getBlockHeader/${hash}?verbose=${verbose}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -277,13 +287,13 @@ class Blockchain {
hashes: hash, hashes: hash,
verbose: verbose verbose: verbose
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input hash must be a string or array of strings.`) throw new Error('Input hash must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
@@ -320,11 +330,11 @@ class Blockchain {
* // branchlen: 1, * // branchlen: 1,
* // status: 'valid-headers' } ] * // status: 'valid-headers' } ]
*/ */
async getChainTips() { async getChainTips () {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/getChainTips`, `${this.restURL}blockchain/getChainTips`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -352,11 +362,11 @@ class Blockchain {
* *
* // 702784497476.8376 * // 702784497476.8376
*/ */
async getDifficulty() { async getDifficulty () {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/getDifficulty`, `${this.restURL}blockchain/getDifficulty`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -366,13 +376,13 @@ class Blockchain {
} }
// getMempoolAncestors // getMempoolAncestors
async getMempoolAncestors(txid, verbose = false) { async getMempoolAncestors (txid, verbose = false) {
if (typeof txid !== "string") txid = JSON.stringify(txid) if (typeof txid !== 'string') txid = JSON.stringify(txid)
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/getMempoolAncestors/${txid}?verbose=${verbose}`, `${this.restURL}blockchain/getMempoolAncestors/${txid}?verbose=${verbose}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -381,13 +391,13 @@ class Blockchain {
} }
} }
async getMempoolDescendants(txid, verbose = false) { async getMempoolDescendants (txid, verbose = false) {
if (typeof txid !== "string") txid = JSON.stringify(txid) if (typeof txid !== 'string') txid = JSON.stringify(txid)
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/getMempoolDescendants/${txid}?verbose=${verbose}`, `${this.restURL}blockchain/getMempoolDescendants/${txid}?verbose=${verbose}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -477,14 +487,14 @@ class Blockchain {
* // } * // }
* // ] * // ]
*/ */
async getMempoolEntry(txid) { async getMempoolEntry (txid) {
//if (typeof txid !== "string") txid = JSON.stringify(txid) // if (typeof txid !== "string") txid = JSON.stringify(txid)
try { try {
if (typeof txid === "string") { if (typeof txid === 'string') {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/getMempoolEntry/${txid}`, `${this.restURL}blockchain/getMempoolEntry/${txid}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -495,13 +505,13 @@ class Blockchain {
{ {
txids: txid txids: txid
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input must be a string or array of strings.`) throw new Error('Input must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
@@ -531,11 +541,11 @@ class Blockchain {
* // maxmempool: 300000000, * // maxmempool: 300000000,
* // mempoolminfee: 0 } * // mempoolminfee: 0 }
*/ */
async getMempoolInfo() { async getMempoolInfo () {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/getMempoolInfo`, `${this.restURL}blockchain/getMempoolInfo`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -578,11 +588,11 @@ class Blockchain {
* // depends: * // depends:
* // [ 'e25682caafc7000645d59f4c11d8d594b2943979b9d8fafb9f946e2b35c21b7e' ] },] * // [ 'e25682caafc7000645d59f4c11d8d594b2943979b9d8fafb9f946e2b35c21b7e' ] },]
*/ */
async getRawMempool(verbose = false) { async getRawMempool (verbose = false) {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/getRawMempool?vebose=${verbose}`, `${this.restURL}blockchain/getRawMempool?vebose=${verbose}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -610,30 +620,32 @@ class Blockchain {
* *
* // null * // null
*/ */
async getTxOut(txid, n, include_mempool = true) { async getTxOut (txid, n, includeMempool = true) {
try { try {
// Input validation // Input validation
if (typeof txid !== "string" || txid.length !== 64) if (typeof txid !== 'string' || txid.length !== 64) {
throw new Error(`txid needs to be a proper transaction ID`) throw new Error('txid needs to be a proper transaction ID')
}
if (isNaN(n)) throw new Error(`n must be an integer`) if (isNaN(n)) throw new Error('n must be an integer')
if (typeof include_mempool !== "boolean") if (typeof includeMempool !== 'boolean') {
throw new Error(`include_mempool input must be of type boolean`) throw new Error('includeMempool input must be of type boolean')
}
// Send the request to the REST API. // Send the request to the REST API.
// const response = await axios.get( // const response = await axios.get(
// `${this.restURL}blockchain/getTxOut/${txid}/${n}?include_mempool=${include_mempool}`, // `${this.restURL}blockchain/getTxOut/${txid}/${n}?includeMempool=${includeMempool}`,
// _this.axiosOptions // this.axiosOptions
// ) // )
const response = await axios.post( const response = await axios.post(
`${this.restURL}blockchain/getTxOut`, `${this.restURL}blockchain/getTxOut`,
{ {
txid: txid, txid: txid,
vout: n, vout: n,
mempool: include_mempool mempool: includeMempool
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -679,14 +691,14 @@ class Blockchain {
* // "010000007de867cc8adc5cc8fb6b898ca4462cf9fd667d7830a275277447e60800000000338f121232e169d3100edd82004dc2a1f0e1f030c6c488fa61eafa930b0528fe021f7449ffff001d36b4af9a0100000001338f121232e169d3100edd82004dc2a1f0e1f030c6c488fa61eafa930b0528fe0101" * // "010000007de867cc8adc5cc8fb6b898ca4462cf9fd667d7830a275277447e60800000000338f121232e169d3100edd82004dc2a1f0e1f030c6c488fa61eafa930b0528fe021f7449ffff001d36b4af9a0100000001338f121232e169d3100edd82004dc2a1f0e1f030c6c488fa61eafa930b0528fe0101"
* // ] * // ]
*/ */
async getTxOutProof(txids) { async getTxOutProof (txids) {
try { try {
// Single txid. // Single txid.
if (typeof txids === "string") { if (typeof txids === 'string') {
const path = `${this.restURL}blockchain/getTxOutProof/${txids}` const path = `${this.restURL}blockchain/getTxOutProof/${txids}`
//if (blockhash) path = `${path}?blockhash=${blockhash}` // if (blockhash) path = `${path}?blockhash=${blockhash}`
const response = await axios.get(path, _this.axiosOptions) const response = await axios.get(path, this.axiosOptions)
return response.data return response.data
// Array of txids. // Array of txids.
@@ -697,24 +709,24 @@ class Blockchain {
{ {
txids: txids txids: txids
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input must be a string or array of strings.`) throw new Error('Input must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
} }
} }
async preciousBlock(blockhash) { async preciousBlock (blockhash) {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/preciousBlock/${blockhash}`, `${this.restURL}blockchain/preciousBlock/${blockhash}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -723,11 +735,11 @@ class Blockchain {
} }
} }
async pruneBlockchain(height) { async pruneBlockchain (height) {
try { try {
const response = await axios.post( const response = await axios.post(
`${this.restURL}blockchain/pruneBlockchain/${height}`, `${this.restURL}blockchain/pruneBlockchain/${height}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -736,11 +748,11 @@ class Blockchain {
} }
} }
async verifyChain(checklevel = 3, nblocks = 6) { async verifyChain (checklevel = 3, nblocks = 6) {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/verifyChain?checklevel=${checklevel}&nblocks=${nblocks}`, `${this.restURL}blockchain/verifyChain?checklevel=${checklevel}&nblocks=${nblocks}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -786,13 +798,13 @@ class Blockchain {
* // "03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7" * // "03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
* // ] * // ]
*/ */
async verifyTxOutProof(proof) { async verifyTxOutProof (proof) {
try { try {
// Single block // Single block
if (typeof proof === "string") { if (typeof proof === 'string') {
const response = await axios.get( const response = await axios.get(
`${this.restURL}blockchain/verifyTxOutProof/${proof}`, `${this.restURL}blockchain/verifyTxOutProof/${proof}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -804,13 +816,13 @@ class Blockchain {
{ {
proofs: proof proofs: proof
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input must be a string or array of strings.`) throw new Error('Input must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
+8 -8
View File
@@ -2,12 +2,12 @@
API endpoints for basic control and information of the full node. API endpoints for basic control and information of the full node.
*/ */
const axios = require("axios") const axios = require('axios')
let _this // Global reference to the instance of this class. // let _this // Global reference to the instance of this class.
class Control { class Control {
constructor(config) { constructor (config) {
this.restURL = config.restURL this.restURL = config.restURL
this.apiToken = config.apiToken this.apiToken = config.apiToken
this.authToken = config.authToken this.authToken = config.authToken
@@ -28,7 +28,7 @@ class Control {
} }
} }
_this = this // _this = this
} }
/** /**
@@ -77,11 +77,11 @@ class Control {
* warnings: * warnings:
* 'Warning: Unknown block versions being mined! It\'s possible unknown rules are in effect' }} * 'Warning: Unknown block versions being mined! It\'s possible unknown rules are in effect' }}
*/ */
async getNetworkInfo() { async getNetworkInfo () {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}control/getNetworkInfo`, `${this.restURL}control/getNetworkInfo`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -90,11 +90,11 @@ class Control {
} }
} }
async getMemoryInfo() { async getMemoryInfo () {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}control/getMemoryInfo`, `${this.restURL}control/getMemoryInfo`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
+9 -9
View File
@@ -1,5 +1,5 @@
const randomBytes = require("randombytes") const randomBytes = require('randombytes')
const Bitcoin = require("bitcoincashjs-lib") const Bitcoin = require('@psf/bitcoincashjs-lib')
class Crypto { class Crypto {
/** /**
@@ -26,7 +26,7 @@ class Crypto {
* *
* */ * */
// Translate address from any address format into a specific format. // Translate address from any address format into a specific format.
static sha256(buffer) { static sha256 (buffer) {
return Bitcoin.crypto.sha256(buffer) return Bitcoin.crypto.sha256(buffer)
} }
@@ -52,7 +52,7 @@ class Crypto {
* bchjs.Crypto.ripemd160(buffer) * bchjs.Crypto.ripemd160(buffer)
* // <Buffer 5f 95 6a 88 86 30 51 ea 52 15 d8 97 0c ed 8e 21 8e b6 15 cf> * // <Buffer 5f 95 6a 88 86 30 51 ea 52 15 d8 97 0c ed 8e 21 8e b6 15 cf>
* */ * */
static ripemd160(buffer) { static ripemd160 (buffer) {
return Bitcoin.crypto.ripemd160(buffer) return Bitcoin.crypto.ripemd160(buffer)
} }
@@ -60,7 +60,7 @@ class Crypto {
* @api Crypto.hash256() hash256() * @api Crypto.hash256() hash256()
* @apiName hash256 * @apiName hash256
* @apiGroup Crypto * @apiGroup Crypto
* @apiDescription Utility for creating double sha256 hash digests of data. * @apiDescription Utility for creating double sha256 hash digests of buffer encoded data.
* *
* @apiExample Example usage: * @apiExample Example usage:
* // buffer from hex * // buffer from hex
@@ -78,7 +78,7 @@ class Crypto {
* bchjs.Crypto.hash256(buffer) * bchjs.Crypto.hash256(buffer)
* // <Buffer 68 8f 1d 02 9e d5 4c 34 d0 32 0b 83 8b f6 fc 64 f6 2f 38 a6 e9 30 a0 af 5b db 4e 27 d1 a6 84 cd> * // <Buffer 68 8f 1d 02 9e d5 4c 34 d0 32 0b 83 8b f6 fc 64 f6 2f 38 a6 e9 30 a0 af 5b db 4e 27 d1 a6 84 cd>
* */ * */
static hash256(buffer) { static hash256 (buffer) {
return Bitcoin.crypto.hash256(buffer) return Bitcoin.crypto.hash256(buffer)
} }
@@ -86,7 +86,7 @@ class Crypto {
* @api Crypto.hash160() hash160() * @api Crypto.hash160() hash160()
* @apiName hash160 * @apiName hash160
* @apiGroup Crypto * @apiGroup Crypto
* @apiDescription Utility for creating ripemd160(sha256()) hash digests of data. * @apiDescription Utility for creating ripemd160(sha256()) hash digests of buffer encoded data.
* *
* @apiExample Example usage: * @apiExample Example usage:
* // buffer from hex * // buffer from hex
@@ -104,7 +104,7 @@ class Crypto {
* bchjs.Crypto.hash160(buffer) * bchjs.Crypto.hash160(buffer)
* *
* */ * */
static hash160(buffer) { static hash160 (buffer) {
return Bitcoin.crypto.hash160(buffer) return Bitcoin.crypto.hash160(buffer)
} }
@@ -130,7 +130,7 @@ class Crypto {
* bchjs.Crypto.randomBytes(32) * bchjs.Crypto.randomBytes(32)
* // <Buffer ec 44 73 72 ea 48 3e 08 a5 0a 62 b8 40 0f 69 64 a7 75 35 af 20 3d e1 6d ce 3b f9 37 11 19 2b c6> * // <Buffer ec 44 73 72 ea 48 3e 08 a5 0a 62 b8 40 0f 69 64 a7 75 35 af 20 3d e1 6d ce 3b f9 37 11 19 2b c6>
* */ * */
static randomBytes(size = 16) { static randomBytes (size = 16) {
return randomBytes(size) return randomBytes(size)
} }
} }
+81
View File
@@ -0,0 +1,81 @@
const axios = require('axios')
let _this
class DSProof {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
this.axios = axios
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
}
}
_this = this
}
/**
* @api DSProof.getDSProof() getDSProof()
* @apiName getDSProof
* @apiGroup DSProof
* @apiDescription Checks if a transaction generated a double-spend proof.
*
* If a double-spend is attempted, one of the transactions will generate a
* 'double spend proof'. This call can be used to check if a transaction
* generated such a proof.
*
* Merchants should wait 3-5 seconds after receiving notification of a
* transaction before calling this endpoint, to see if the TXID generated a
* proof. If this method returns no data, then the TX can be considered 'safe'
* and not a double spend. If proof data is returned by this method, then
* the transaction generated a proof and can be considered a 'double spend'.
*
* @apiExample Example usage:
* (async () => {
* try {
* const txid = 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
* const result = await bchjs.DSProof.getDSProof(txid)
* console.log(result);
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* null
*/
async getDSProof (txid) {
try {
if (!txid) {
throw new Error('txid is required')
}
if (txid.length !== 64) {
throw new Error(`txid must be of length 64 (not ${txid.length})`)
}
const response = await _this.axios.get(
`${this.restURL}dsproof/getdsproof/${txid}`,
this.axiosOptions
)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = DSProof
+19 -15
View File
@@ -1,10 +1,11 @@
const Bitcoin = require("bitcoincashjs-lib") const Bitcoin = require('@psf/bitcoincashjs-lib')
const coininfo = require("coininfo") const coininfo = require('@psf/coininfo')
class ECPair { class ECPair {
static setAddress(address) { static setAddress (address) {
ECPair._address = address ECPair._address = address
} }
/** /**
* @api Ecpair.fromWIF() fromWIF() * @api Ecpair.fromWIF() fromWIF()
* @apiName fromWIF * @apiName fromWIF
@@ -20,20 +21,19 @@ class ECPair {
* let wif = 'cSNLj6xeg3Yg2rfcgKoWNx4MiAgn9ugCUUro37UDEhn6CzeYqjWW' * let wif = 'cSNLj6xeg3Yg2rfcgKoWNx4MiAgn9ugCUUro37UDEhn6CzeYqjWW'
* bchjs.ECPair.fromWIF(wif) * bchjs.ECPair.fromWIF(wif)
* */ * */
static fromWIF(privateKeyWIF) { static fromWIF (privateKeyWIF) {
let network let network
if (privateKeyWIF[0] === "L" || privateKeyWIF[0] === "K") if (privateKeyWIF[0] === 'L' || privateKeyWIF[0] === 'K') { network = 'mainnet' } else if (privateKeyWIF[0] === 'c') network = 'testnet'
network = "mainnet"
else if (privateKeyWIF[0] === "c") network = "testnet"
let bitcoincash let bitcoincash
if (network === "mainnet") bitcoincash = coininfo.bitcoincash.main if (network === 'mainnet') bitcoincash = coininfo.bitcoincash.main
else bitcoincash = coininfo.bitcoincash.test else bitcoincash = coininfo.bitcoincash.test
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS() const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
return Bitcoin.ECPair.fromWIF(privateKeyWIF, bitcoincashBitcoinJSLib) return Bitcoin.ECPair.fromWIF(privateKeyWIF, bitcoincashBitcoinJSLib)
} }
/** /**
* @api Ecpair.toWIF() toWIF() * @api Ecpair.toWIF() toWIF()
* @apiName toWIF * @apiName toWIF
@@ -57,17 +57,18 @@ class ECPair {
* bchjs.ECPair.toWIF(ecpair); * bchjs.ECPair.toWIF(ecpair);
* // cT3tJP7BnjFJSAHbooMXrY8E9t2AFj37amSBAYFMeHfqPqPgD4ZA * // cT3tJP7BnjFJSAHbooMXrY8E9t2AFj37amSBAYFMeHfqPqPgD4ZA
* */ * */
static toWIF(ecpair) { static toWIF (ecpair) {
return ecpair.toWIF() return ecpair.toWIF()
} }
static sign(ecpair, buffer) { static sign (ecpair, buffer) {
return ecpair.sign(buffer) return ecpair.sign(buffer)
} }
static verify(ecpair, buffer, signature) { static verify (ecpair, buffer, signature) {
return ecpair.verify(buffer, signature) return ecpair.verify(buffer, signature)
} }
/** /**
* @api Ecpair.fromPublicKey() fromPublicKey() * @api Ecpair.fromPublicKey() fromPublicKey()
* @apiName fromPublicKey * @apiName fromPublicKey
@@ -83,9 +84,10 @@ class ECPair {
* let pubkeyBuffer = Buffer.from("024a6d0737a23c472d078d78c1cbc3c2bbf8767b48e72684ff03a911b463da7fa6", 'hex'); * let pubkeyBuffer = Buffer.from("024a6d0737a23c472d078d78c1cbc3c2bbf8767b48e72684ff03a911b463da7fa6", 'hex');
* bchjs.ECPair.fromPublicKey(pubkeyBuffer); * bchjs.ECPair.fromPublicKey(pubkeyBuffer);
* */ * */
static fromPublicKey(pubkeyBuffer) { static fromPublicKey (pubkeyBuffer) {
return Bitcoin.ECPair.fromPublicKeyBuffer(pubkeyBuffer) return Bitcoin.ECPair.fromPublicKeyBuffer(pubkeyBuffer)
} }
/** /**
* @api Ecpair.toPublicKey() toPublicKey() * @api Ecpair.toPublicKey() toPublicKey()
* @apiName toPublicKey * @apiName toPublicKey
@@ -105,9 +107,10 @@ class ECPair {
* bchjs.ECPair.toPublicKey(ecpair); * bchjs.ECPair.toPublicKey(ecpair);
* // * //
* */ * */
static toPublicKey(ecpair) { static toPublicKey (ecpair) {
return ecpair.getPublicKeyBuffer() return ecpair.getPublicKeyBuffer()
} }
/** /**
* @api Ecpair.toLegacyAddress() toLegacyAddress() * @api Ecpair.toLegacyAddress() toLegacyAddress()
* @apiName toLegacyAddress * @apiName toLegacyAddress
@@ -131,9 +134,10 @@ class ECPair {
* bchjs.ECPair.toLegacyAddress(ecpair); * bchjs.ECPair.toLegacyAddress(ecpair);
* // mg4PygFcXoyNJGJkM2Dcpe25av9wXzz1My * // mg4PygFcXoyNJGJkM2Dcpe25av9wXzz1My
* */ * */
static toLegacyAddress(ecpair) { static toLegacyAddress (ecpair) {
return ecpair.getAddress() return ecpair.getAddress()
} }
/** /**
* @api Ecpair.toCashAddress() toCashAddress() * @api Ecpair.toCashAddress() toCashAddress()
* @apiName toCashAddress * @apiName toCashAddress
@@ -157,7 +161,7 @@ class ECPair {
* bchjs.ECPair.toCashAddress(ecpair); * bchjs.ECPair.toCashAddress(ecpair);
* // bchtest:qqzly4vrcxcjw62u4yq4nv86ltk2mc9v0yvq8mvj6m * // bchtest:qqzly4vrcxcjw62u4yq4nv86ltk2mc9v0yvq8mvj6m
* */ * */
static toCashAddress(ecpair, regtest = false) { static toCashAddress (ecpair, regtest = false) {
return ECPair._address.toCashAddress(ecpair.getAddress(), true, regtest) return ECPair._address.toCashAddress(ecpair.getAddress(), true, regtest)
} }
} }
+145 -40
View File
@@ -2,13 +2,16 @@
This library interacts with the ElectrumX bch-api REST API endpoints operated This library interacts with the ElectrumX bch-api REST API endpoints operated
by FullStack.cash by FullStack.cash
*/ */
// Public npm libraries
const axios = require('axios')
const axios = require("axios") // Local libraries.
const Blockchain = require('./blockchain')
let _this // let _this
class ElectrumX { class ElectrumX {
constructor(config) { constructor (config) {
this.restURL = config.restURL this.restURL = config.restURL
this.apiToken = config.apiToken this.apiToken = config.apiToken
this.authToken = config.authToken this.authToken = config.authToken
@@ -29,7 +32,9 @@ class ElectrumX {
} }
} }
_this = this this.blockchain = new Blockchain(config)
// _this = this
} }
/** /**
@@ -97,13 +102,13 @@ class ElectrumX {
* } * }
* *
*/ */
async utxo(address) { async utxo (address) {
try { try {
// Handle single address. // Handle single address.
if (typeof address === "string") { if (typeof address === 'string') {
const response = await axios.get( const response = await axios.get(
`${this.restURL}electrumx/utxos/${address}`, `${this.restURL}electrumx/utxos/${address}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -114,13 +119,13 @@ class ElectrumX {
{ {
addresses: address addresses: address
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input address must be a string or array of strings.`) throw new Error('Input address must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
@@ -137,7 +142,7 @@ class ElectrumX {
* (async () => { * (async () => {
* try { * try {
* let balance = await bchjs.Electrumx.balance('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'); * let balance = await bchjs.Electrumx.balance('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf');
* console.log(utxo); * console.log(balance);
* } catch(error) { * } catch(error) {
* console.error(error) * console.error(error)
* } * }
@@ -154,7 +159,7 @@ class ElectrumX {
* (async () => { * (async () => {
* try { * try {
* let balance = await bchjs.Electrumx.balance(['bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v']); * let balance = await bchjs.Electrumx.balance(['bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v']);
* console.log(utxo); * console.log(balance);
* } catch(error) { * } catch(error) {
* console.error(error) * console.error(error)
* } * }
@@ -181,13 +186,13 @@ class ElectrumX {
* } * }
* *
*/ */
async balance(address) { async balance (address) {
try { try {
// Handle single address. // Handle single address.
if (typeof address === "string") { if (typeof address === 'string') {
const response = await axios.get( const response = await axios.get(
`${this.restURL}electrumx/balance/${address}`, `${this.restURL}electrumx/balance/${address}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -198,13 +203,13 @@ class ElectrumX {
{ {
addresses: address addresses: address
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input address must be a string or array of strings.`) throw new Error('Input address must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
@@ -279,13 +284,13 @@ class ElectrumX {
* } * }
* *
*/ */
async transactions(address) { async transactions (address, usrObj = null) {
try { try {
// Handle single address. // Handle single address.
if (typeof address === "string") { if (typeof address === 'string') {
const response = await axios.get( const response = await axios.get(
`${this.restURL}electrumx/transactions/${address}`, `${this.restURL}electrumx/transactions/${address}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -294,15 +299,16 @@ class ElectrumX {
const response = await axios.post( const response = await axios.post(
`${this.restURL}electrumx/transactions`, `${this.restURL}electrumx/transactions`,
{ {
addresses: address addresses: address,
usrObj // pass user data when making an internal call.
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input address must be a string or array of strings.`) throw new Error('Input address must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
@@ -371,13 +377,13 @@ class ElectrumX {
* } * }
* *
*/ */
async unconfirmed(address) { async unconfirmed (address) {
try { try {
// Handle single address. // Handle single address.
if (typeof address === "string") { if (typeof address === 'string') {
const response = await axios.get( const response = await axios.get(
`${this.restURL}electrumx/unconfirmed/${address}`, `${this.restURL}electrumx/unconfirmed/${address}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -388,13 +394,13 @@ class ElectrumX {
{ {
addresses: address addresses: address
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input address must be a string or array of strings.`) throw new Error('Input address must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
@@ -442,19 +448,19 @@ class ElectrumX {
* } * }
* *
*/ */
async blockHeader(height, count = 1) { async blockHeader (height, count = 1) {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}electrumx/block/headers/${height}?count=${count}`, `${this.restURL}electrumx/block/headers/${height}?count=${count}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
// console.log("error: ", error) // console.log("error: ", error)
if (error.response && error.response.data) { if (error.response && error.response.data) {
if (error.response && error.response.data) if (error.response && error.response.data) {
throw new Error(error.response.data.error) throw new Error(error.response.data.error)
else throw error.response.data } else throw error.response.data
} else { } else {
throw error throw error
} }
@@ -531,13 +537,13 @@ class ElectrumX {
* ] * ]
* } * }
*/ */
async txData(txid) { async txData (txid) {
try { try {
// Handle single transaction. // Handle single transaction.
if (typeof txid === "string") { if (typeof txid === 'string') {
const response = await axios.get( const response = await axios.get(
`${this.restURL}electrumx/tx/data/${txid}`, `${this.restURL}electrumx/tx/data/${txid}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} else if (Array.isArray(txid)) { } else if (Array.isArray(txid)) {
@@ -546,13 +552,13 @@ class ElectrumX {
{ {
txids: txid txids: txid
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input txId must be a string or array of strings.`) throw new Error('Input txId must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
@@ -580,24 +586,123 @@ class ElectrumX {
* "txid": "..." * "txid": "..."
* } * }
*/ */
async broadcast(txHex) { async broadcast (txHex) {
try { try {
if (typeof txHex === "string") { if (typeof txHex === 'string') {
const response = await axios.post( const response = await axios.post(
`${this.restURL}electrumx/tx/broadcast`, `${this.restURL}electrumx/tx/broadcast`,
{ txHex }, { txHex },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input txHex must be a string.`) throw new Error('Input txHex must be a string.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
} }
} }
/**
* @api Electrumx.sortConfTxs() sortConfTxs()
* @apiName ElectrumX sortConfTxs
* @apiGroup ElectrumX
* @apiDescription Sort the output of Electrum.transactions() by block height.
*
* A simple sort function for the output of Electrum.transactions(). Ignores
* unconfirmed transactions.
*
* Sorts in 'DESCENDING' order by default, or 'ASCENDING' can be specified.
* Descending makes the first element the newest (largest block height).
*
* @apiExample Example usage:
* (async () => {
* const txs = await bchjs.Electrumx.transactions('bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v')
* const sortedTxs = bchjs.Electrumx.sortConfTxs(txs.transactions, 'ASCENDING')
* console.log(sortedTxs)
* })()
*
* // [
* // {
* // "height": 560430,
* // "tx_hash": "3e1f3e882be9c03897eeb197224bf87f312be556a89f4308fabeeeabcf9bc851"
* // },
* // {
* // "height": 560534,
* // "tx_hash": "4ebbeaac51ce141e262964e3a0ce11b96ca72c0dffe9b4127ce80135f503a280"
* // }
* // ]
*/
// Sort confirmed Transactions by the block height
sortConfTxs (txs, sortingOrder = 'DESCENDING') {
try {
// Filter out unconfirmed transactions, with a height of 0 or less.
txs = txs.filter(elem => elem.height > 0)
if (sortingOrder === 'DESCENDING') {
return txs.sort((a, b) => b.height - a.height)
}
return txs.sort((a, b) => a.height - b.height)
} catch (err) {
console.log('Error in util.js/sortConfTxs()')
throw err
}
}
/**
* @api Electrumx.sortAllTxs() sortAllTxs()
* @apiName ElectrumX sortAllTxs
* @apiGroup ElectrumX
* @apiDescription Sort the output of Electrum.transactions() by block height.
*
* A simple sort function for the output of Electrum.transactions().
* Assumes that unconfirmed transactions will make it into the next block. Any
* unconfirmed transactions have their block height with the height of the next
* block. Returns a Promise.
*
* Sorts in 'ASCENDING' order by default, or 'DESCENDING' can be specified.
*
* @apiExample Example usage:
* (async () => {
* const txs = await bchjs.Electrumx.transactions('bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v')
* const sortedTxs = await bchjs.Electrumx.sortAllTxs(txs.transactions, 'ASCENDING')
* console.log(sortedTxs)
* })()
*
* // [
* // {
* // "height": 560430,
* // "tx_hash": "3e1f3e882be9c03897eeb197224bf87f312be556a89f4308fabeeeabcf9bc851"
* // },
* // {
* // "height": 560534,
* // "tx_hash": "4ebbeaac51ce141e262964e3a0ce11b96ca72c0dffe9b4127ce80135f503a280"
* // }
* // ]
*/
// Substitute zero-conf txs with the current block-height + 1
async sortAllTxs (txs, sortingOrder = 'DESCENDING') {
try {
// Calculate the height of the next block
const nextBlock = (await this.blockchain.getBlockCount()) + 1
// Replace the height of any zero-conf transactions with the height of
// the next block.
const modifiedTxs = txs.map(elem => {
if (elem.height <= 0) elem.height = nextBlock
return elem
})
// console.log(`modifiedTxs: ${JSON.stringify(modifiedTxs, null, 2)}`)
// Sort the modified array of transactions.
return this.sortConfTxs(modifiedTxs, sortingOrder)
} catch (err) {
console.log('Error in util.js/sort0ConfTxs')
throw err
}
}
} }
module.exports = ElectrumX module.exports = ElectrumX
+7 -6
View File
@@ -2,12 +2,12 @@
This library contains useful functions that deal with encryption. This library contains useful functions that deal with encryption.
*/ */
const axios = require("axios") const axios = require('axios')
let _this let _this
class Encryption { class Encryption {
constructor(config) { constructor (config) {
this.restURL = config.restURL this.restURL = config.restURL
this.apiToken = config.apiToken this.apiToken = config.apiToken
this.axios = axios this.axios = axios
@@ -33,14 +33,15 @@ class Encryption {
} }
// Search the blockchain for a public key associated with a BCH address. // Search the blockchain for a public key associated with a BCH address.
async getPubKey(addr) { async getPubKey (addr) {
try { try {
if (!addr || typeof addr !== "string") if (!addr || typeof addr !== 'string') {
throw new Error(`Input must be a valid Bitcoin Cash address.`) throw new Error('Input must be a valid Bitcoin Cash address.')
}
const response = await _this.axios.get( const response = await _this.axios.get(
`${this.restURL}encryption/publickey/${addr}`, `${this.restURL}encryption/publickey/${addr}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
+6 -6
View File
@@ -1,9 +1,9 @@
const axios = require("axios") const axios = require('axios')
let _this // let _this
class Generating { class Generating {
constructor(config) { constructor (config) {
this.restURL = config.restURL this.restURL = config.restURL
this.apiToken = config.apiToken this.apiToken = config.apiToken
this.authToken = config.authToken this.authToken = config.authToken
@@ -24,14 +24,14 @@ class Generating {
} }
} }
_this = this // _this = this
} }
async generateToAddress(blocks, address, maxtries = 1000000) { async generateToAddress (blocks, address, maxtries = 1000000) {
try { try {
const response = await axios.post( const response = await axios.post(
`${this.restURL}generating/generateToAddress/${blocks}/${address}?maxtries=${maxtries}`, `${this.restURL}generating/generateToAddress/${blocks}/${address}?maxtries=${maxtries}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
+55 -55
View File
@@ -1,10 +1,10 @@
const Bitcoin = require("bitcoincashjs-lib") const Bitcoin = require('@psf/bitcoincashjs-lib')
const coininfo = require("coininfo") const coininfo = require('@psf/coininfo')
const bip32utils = require("bip32-utils") const bip32utils = require('@chris.troutner/bip32-utils')
const bchaddrjs = require("bchaddrjs-slp") const bchaddrjs = require('bchaddrjs-slp')
class HDNode { class HDNode {
constructor(address) { constructor (address) {
this._address = address this._address = address
} }
@@ -19,7 +19,7 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* bchjs.HDNode.fromSeed(seedBuffer); * bchjs.HDNode.fromSeed(seedBuffer);
* *
@@ -28,15 +28,15 @@ class HDNode {
* // create mnemonic from entropy * // create mnemonic from entropy
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy); * let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* bchjs.HDNode.fromSeed(seedBuffer); * bchjs.HDNode.fromSeed(seedBuffer);
*/ */
fromSeed(rootSeedBuffer, network = "mainnet") { fromSeed (rootSeedBuffer, network = 'mainnet') {
let bitcoincash let bitcoincash
if (network === "bitcoincash" || network === "mainnet") if (network === 'bitcoincash' || network === 'mainnet') {
bitcoincash = coininfo.bitcoincash.main bitcoincash = coininfo.bitcoincash.main
else bitcoincash = coininfo.bitcoincash.test } else bitcoincash = coininfo.bitcoincash.test
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS() const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
return Bitcoin.HDNode.fromSeedBuffer( return Bitcoin.HDNode.fromSeedBuffer(
@@ -56,7 +56,7 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // to legacy address * // to legacy address
@@ -68,14 +68,14 @@ class HDNode {
* // create mnemonic from entropy * // create mnemonic from entropy
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy); * let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // to cash address * // to cash address
* bchjs.HDNode.toLegacyAddress(hdNode); * bchjs.HDNode.toLegacyAddress(hdNode);
* // 14mVsq3H5Ep2Jb6AqoKsmY1BFHKCBGPDLi * // 14mVsq3H5Ep2Jb6AqoKsmY1BFHKCBGPDLi
*/ */
toLegacyAddress(hdNode) { toLegacyAddress (hdNode) {
return hdNode.getAddress() return hdNode.getAddress()
} }
@@ -90,7 +90,7 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // to cash address * // to cash address
@@ -102,14 +102,14 @@ class HDNode {
* // create mnemonic from entropy * // create mnemonic from entropy
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy); * let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // to cash address * // to cash address
* bchjs.HDNode.toCashAddress(hdNode); * bchjs.HDNode.toCashAddress(hdNode);
* // bitcoincash:qq549jxsjv66kw0smdju4es2axnk7hhe9cquhjg4gt * // bitcoincash:qq549jxsjv66kw0smdju4es2axnk7hhe9cquhjg4gt
*/ */
toCashAddress(hdNode, regtest = false) { toCashAddress (hdNode, regtest = false) {
return this._address.toCashAddress(hdNode.getAddress(), true, regtest) return this._address.toCashAddress(hdNode.getAddress(), true, regtest)
} }
@@ -123,7 +123,7 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.SLP.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.SLP.HDNode.fromSeed(seedBuffer);
* // to cash address * // to cash address
@@ -135,14 +135,14 @@ class HDNode {
* // create mnemonic from entropy * // create mnemonic from entropy
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy); * let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.SLP.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.SLP.HDNode.fromSeed(seedBuffer);
* // to cash address * // to cash address
* bchjs.SLP.HDNode.toSLPAddress(hdNode); * bchjs.SLP.HDNode.toSLPAddress(hdNode);
* // simpleledger:qqxh2z2z397m4c6u9s5x6wjtku742q8rpvm6al2nrf * // simpleledger:qqxh2z2z397m4c6u9s5x6wjtku742q8rpvm6al2nrf
*/ */
toSLPAddress(hdNode) { toSLPAddress (hdNode) {
const cashAddr = this.toCashAddress(hdNode) const cashAddr = this.toCashAddress(hdNode)
return bchaddrjs.toSlpAddress(cashAddr) return bchaddrjs.toSlpAddress(cashAddr)
} }
@@ -158,7 +158,7 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // to WIF * // to WIF
@@ -170,14 +170,14 @@ class HDNode {
* // create mnemonic from entropy * // create mnemonic from entropy
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy); * let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // to WIF * // to WIF
* bchjs.HDNode.toWIF(hdNode); * bchjs.HDNode.toWIF(hdNode);
* // KwobPFhv3AuXc3ps6YtWfMVRpLBDBA7jnJddurfELTyTNcFhZYpJ * // KwobPFhv3AuXc3ps6YtWfMVRpLBDBA7jnJddurfELTyTNcFhZYpJ
*/ */
toWIF(hdNode) { toWIF (hdNode) {
return hdNode.keyPair.toWIF() return hdNode.keyPair.toWIF()
} }
@@ -192,7 +192,7 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // to extended public key * // to extended public key
@@ -204,14 +204,14 @@ class HDNode {
* // create mnemonic from entropy * // create mnemonic from entropy
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy); * let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // to extended public key * // to extended public key
* bchjs.HDNode.toXPub(hdNode); * bchjs.HDNode.toXPub(hdNode);
* // xpub661MyMwAqRbcFuMLeHkSbTNwNHG9MQyrAZqV1Q4MEAsmj9MYa5sxg8WC2LKqW6EHviHVucBjWi1n38juZpDDeX3U6YrsMeACdcNSTHkM8BQ * // xpub661MyMwAqRbcFuMLeHkSbTNwNHG9MQyrAZqV1Q4MEAsmj9MYa5sxg8WC2LKqW6EHviHVucBjWi1n38juZpDDeX3U6YrsMeACdcNSTHkM8BQ
*/ */
toXPub(hdNode) { toXPub (hdNode) {
return hdNode.neutered().toBase58() return hdNode.neutered().toBase58()
} }
@@ -226,7 +226,7 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // to extended private key * // to extended private key
@@ -238,14 +238,14 @@ class HDNode {
* // create mnemonic from entropy * // create mnemonic from entropy
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy); * let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // to extended private key * // to extended private key
* bchjs.HDNode.toXPriv(hdNode); * bchjs.HDNode.toXPriv(hdNode);
* // xprv9s21ZrQH143K2b5GPP6zHz22E6LeCgQXJtwNbC3MA3Kz7Se7tveKo96EhqwFtSkYWkyenVcMqM7uq35PcUNG8cUdpsJEgwKG3dvfP7TmL3v * // xprv9s21ZrQH143K2b5GPP6zHz22E6LeCgQXJtwNbC3MA3Kz7Se7tveKo96EhqwFtSkYWkyenVcMqM7uq35PcUNG8cUdpsJEgwKG3dvfP7TmL3v
*/ */
toXPriv(hdNode) { toXPriv (hdNode) {
return hdNode.toBase58() return hdNode.toBase58()
} }
@@ -260,7 +260,7 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create root seed buffer from mnemonic * // create root seed buffer from mnemonic
* let rootSeed= bchjs.Mnemonic.toSeed(mnemonic); * let rootSeed= await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from root seed * // create HDNode from root seed
* let hdNode = bchjs.HDNode.fromSeed(rootSeed); * let hdNode = bchjs.HDNode.fromSeed(rootSeed);
* // create public key buffer from HDNode * // create public key buffer from HDNode
@@ -271,13 +271,13 @@ class HDNode {
* // create mnemonic from entropy * // create mnemonic from entropy
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy); * let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // create public key buffer from HDNode * // create public key buffer from HDNode
* bchjs.HDNode.toKeyPair(hdNode); * bchjs.HDNode.toKeyPair(hdNode);
*/ */
toKeyPair(hdNode) { toKeyPair (hdNode) {
return hdNode.keyPair return hdNode.keyPair
} }
@@ -292,7 +292,7 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create root seed buffer from mnemonic * // create root seed buffer from mnemonic
* let rootSeed= bchjs.Mnemonic.toSeed(mnemonic); * let rootSeed= await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from root seed * // create HDNode from root seed
* let hdNode = bchjs.HDNode.fromSeed(rootSeed); * let hdNode = bchjs.HDNode.fromSeed(rootSeed);
* // create public key buffer from HDNode * // create public key buffer from HDNode
@@ -304,14 +304,14 @@ class HDNode {
* // create mnemonic from entropy * // create mnemonic from entropy
* let mnemonic = bchjs.Mnemonic.fromEntropy(entropy); * let mnemonic = bchjs.Mnemonic.fromEntropy(entropy);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // create public key buffer from HDNode * // create public key buffer from HDNode
* bchjs.HDNode.toPublicKey(hdNode); * bchjs.HDNode.toPublicKey(hdNode);
* // <Buffer 02 d2 26 74 6e 78 03 ac 11 e0 96 c6 24 de e8 dd 62 52 e7 8e 51 56 8a c1 18 62 aa 2a 72 50 1d ea 7d> * // <Buffer 02 d2 26 74 6e 78 03 ac 11 e0 96 c6 24 de e8 dd 62 52 e7 8e 51 56 8a c1 18 62 aa 2a 72 50 1d ea 7d>
*/ */
toPublicKey(hdNode) { toPublicKey (hdNode) {
return hdNode.getPublicKeyBuffer() return hdNode.getPublicKeyBuffer()
} }
@@ -329,10 +329,10 @@ class HDNode {
* // testnet xpriv * // testnet xpriv
* bchjs.HDNode.fromXPriv('tprv8gQ3zr1F5pRHMebqqhorrorYNvUG3XkcZjSWVs2cEtRwwJy1TRhgRx4XcF8dYHM2eyTbTCcdKYNhqgyBQphxwRoVyVKr9zuyoA8WxNDRvom'); * bchjs.HDNode.fromXPriv('tprv8gQ3zr1F5pRHMebqqhorrorYNvUG3XkcZjSWVs2cEtRwwJy1TRhgRx4XcF8dYHM2eyTbTCcdKYNhqgyBQphxwRoVyVKr9zuyoA8WxNDRvom');
*/ */
fromXPriv(xpriv) { fromXPriv (xpriv) {
let bitcoincash let bitcoincash
if (xpriv[0] === "x") bitcoincash = coininfo.bitcoincash.main if (xpriv[0] === 'x') bitcoincash = coininfo.bitcoincash.main
else if (xpriv[0] === "t") bitcoincash = coininfo.bitcoincash.test else if (xpriv[0] === 't') bitcoincash = coininfo.bitcoincash.test
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS() const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
return Bitcoin.HDNode.fromBase58(xpriv, bitcoincashBitcoinJSLib) return Bitcoin.HDNode.fromBase58(xpriv, bitcoincashBitcoinJSLib)
@@ -352,10 +352,10 @@ class HDNode {
* // testnet xpub * // testnet xpub
* bchjs.HDNode.fromXPub('tpubDD669G3VEC6xF7ddjMUTGDWewwzCCrwX933HnP4ufAELmoDn5pXGcSgPnLodjFvWQwRXkG94f77BatEDA8dfQ99yy97kRYynUpNLENEqTBo'); * bchjs.HDNode.fromXPub('tpubDD669G3VEC6xF7ddjMUTGDWewwzCCrwX933HnP4ufAELmoDn5pXGcSgPnLodjFvWQwRXkG94f77BatEDA8dfQ99yy97kRYynUpNLENEqTBo');
*/ */
fromXPub(xpub) { fromXPub (xpub) {
let bitcoincash let bitcoincash
if (xpub[0] === "x") bitcoincash = coininfo.bitcoincash.main if (xpub[0] === 'x') bitcoincash = coininfo.bitcoincash.main
else if (xpub[0] === "t") bitcoincash = coininfo.bitcoincash.test else if (xpub[0] === 't') bitcoincash = coininfo.bitcoincash.test
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS() const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
return Bitcoin.HDNode.fromBase58(xpub, bitcoincashBitcoinJSLib) return Bitcoin.HDNode.fromBase58(xpub, bitcoincashBitcoinJSLib)
@@ -372,13 +372,13 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // derive hardened child HDNode * // derive hardened child HDNode
* bchjs.HDNode.derivePath(hdNode, "m/44'/145'/0'"); * bchjs.HDNode.derivePath(hdNode, "m/44'/145'/0'");
*/ */
derivePath(hdnode, path) { derivePath (hdnode, path) {
return hdnode.derivePath(path) return hdnode.derivePath(path)
} }
@@ -393,13 +393,13 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // derive unhardened child HDNode * // derive unhardened child HDNode
* bchjs.HDNode.derive(hdNode, 0); * bchjs.HDNode.derive(hdNode, 0);
*/ */
derive(hdnode, path) { derive (hdnode, path) {
return hdnode.derive(path) return hdnode.derive(path)
} }
@@ -414,13 +414,13 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create seed buffer from mnemonic * // create seed buffer from mnemonic
* let seedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let seedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create HDNode from seed buffer * // create HDNode from seed buffer
* let hdNode = bchjs.HDNode.fromSeed(seedBuffer); * let hdNode = bchjs.HDNode.fromSeed(seedBuffer);
* // derive hardened child HDNode * // derive hardened child HDNode
* bchjs.HDNode.deriveHardened(hdNode, 0); * bchjs.HDNode.deriveHardened(hdNode, 0);
*/ */
deriveHardened(hdnode, path) { deriveHardened (hdnode, path) {
return hdnode.deriveHardened(path) return hdnode.deriveHardened(path)
} }
@@ -450,7 +450,7 @@ class HDNode {
* // sign * // sign
* bchjs.HDNode.sign(hdnode, buf); * bchjs.HDNode.sign(hdnode, buf);
*/ */
sign(hdnode, buffer) { sign (hdnode, buffer) {
return hdnode.sign(buffer) return hdnode.sign(buffer)
} }
@@ -494,7 +494,7 @@ class HDNode {
* bchjs.HDNode.verify(hdnode2, buf, signature); * bchjs.HDNode.verify(hdnode2, buf, signature);
* // false * // false
*/ */
verify(hdnode, buffer, signature) { verify (hdnode, buffer, signature) {
return hdnode.verify(buffer, signature) return hdnode.verify(buffer, signature)
} }
@@ -530,7 +530,7 @@ class HDNode {
* bchjs.HDNode.isPublic(node); * bchjs.HDNode.isPublic(node);
* // false * // false
*/ */
isPublic(hdnode) { isPublic (hdnode) {
return hdnode.isNeutered() return hdnode.isNeutered()
} }
@@ -566,7 +566,7 @@ class HDNode {
* bchjs.HDNode.isPrivate(node); * bchjs.HDNode.isPrivate(node);
* // true * // true
*/ */
isPrivate(hdnode) { isPrivate (hdnode) {
return !hdnode.isNeutered() return !hdnode.isNeutered()
} }
@@ -598,11 +598,11 @@ class HDNode {
* bchjs.Crypto.hash160(publicKeyBuffer); * bchjs.Crypto.hash160(publicKeyBuffer);
* // <Buffer e1 8e 20 e3 f8 f1 c0 53 e6 1f 9e 3a 58 8e 71 f5 0b 8d 2d c4> * // <Buffer e1 8e 20 e3 f8 f1 c0 53 e6 1f 9e 3a 58 8e 71 f5 0b 8d 2d c4>
*/ */
toIdentifier(hdnode) { toIdentifier (hdnode) {
return hdnode.getIdentifier() return hdnode.getIdentifier()
} }
fromBase58(base58, network) { fromBase58 (base58, network) {
return Bitcoin.HDNode.fromBase58(base58, network) return Bitcoin.HDNode.fromBase58(base58, network)
} }
@@ -617,7 +617,7 @@ class HDNode {
* // create mnemonic * // create mnemonic
* let mnemonic = bchjs.Mnemonic.generate(128); * let mnemonic = bchjs.Mnemonic.generate(128);
* // create root seed buffer * // create root seed buffer
* let rootSeedBuffer = bchjs.Mnemonic.toSeed(mnemonic); * let rootSeedBuffer = await bchjs.Mnemonic.toSeed(mnemonic);
* // create master hd node * // create master hd node
* let masterHDNode = bchjs.HDNode.fromSeed(rootSeedBuffer); * let masterHDNode = bchjs.HDNode.fromSeed(rootSeedBuffer);
* // derive child node * // derive child node
@@ -625,14 +625,14 @@ class HDNode {
* // create account * // create account
* let account = bchjs.HDNode.createAccount([childNode]); * let account = bchjs.HDNode.createAccount([childNode]);
*/ */
createAccount(hdNodes) { createAccount (hdNodes) {
const arr = hdNodes.map( const arr = hdNodes.map(
(item, index) => new bip32utils.Chain(item.neutered()) (item, index) => new bip32utils.Chain(item.neutered())
) )
return new bip32utils.Account(arr) return new bip32utils.Account(arr)
} }
createChain(hdNode) { createChain (hdNode) {
return new bip32utils.Chain(hdNode) return new bip32utils.Chain(hdNode)
} }
} }
+57 -47
View File
@@ -3,19 +3,19 @@
and downloading data from the IPFS network. and downloading data from the IPFS network.
*/ */
const axios = require("axios") const axios = require('axios')
const Uppy = require("@uppy/core") const Uppy = require('@uppy/core')
const Tus = require("@uppy/tus") const Tus = require('@uppy/tus')
const fs = require("fs") const fs = require('fs')
let _this let _this
class IPFS { class IPFS {
constructor(config) { constructor (config) {
this.IPFS_API = process.env.IPFS_API this.IPFS_API = process.env.IPFS_API
? process.env.IPFS_API ? process.env.IPFS_API
: // : `http://localhost:5001` : 'https://ipfs-file-upload.fullstackcash.nl'
`https://ipfs-api.fullstack.cash` // : `http://localhost:5001`
// Default options when calling axios. // Default options when calling axios.
this.axiosOptions = { this.axiosOptions = {
@@ -35,16 +35,16 @@ class IPFS {
} }
// Initializes Uppy, which is used for file uploads. // Initializes Uppy, which is used for file uploads.
initUppy() { initUppy () {
const uppy = Uppy({ const uppy = Uppy({
allowMultipleUploads: false, allowMultipleUploads: false,
meta: { test: "avatar" }, meta: { test: 'avatar' },
debug: false, debug: false,
restrictions: { restrictions: {
maxFileSize: null, maxFileSize: null,
maxNumberOfFiles: 1, maxNumberOfFiles: 1,
minNumberOfFiles: 1, minNumberOfFiles: 1,
allowedFileTypes: null //type of files allowed to load allowedFileTypes: null // type of files allowed to load
} }
}) })
uppy.use(Tus, { endpoint: `${_this.IPFS_API}/uppy-files` }) uppy.use(Tus, { endpoint: `${_this.IPFS_API}/uppy-files` })
@@ -90,11 +90,12 @@ class IPFS {
* "__v": 0 * "__v": 0
* } * }
*/ */
async createFileModelServer(path) { async createFileModelServer (path) {
try { try {
// Ensure the file exists. // Ensure the file exists.
if (!_this.fs.existsSync(path)) if (!_this.fs.existsSync(path)) {
throw new Error(`Could not find this file: ${path}`) throw new Error(`Could not find this file: ${path}`)
}
// Read in the file. // Read in the file.
const fileBuf = _this.fs.readFileSync(path) const fileBuf = _this.fs.readFileSync(path)
@@ -103,11 +104,11 @@ class IPFS {
// console.log(`Buffer length: ${fileBuf.length}`) // console.log(`Buffer length: ${fileBuf.length}`)
// Get the file name from the path. // Get the file name from the path.
const splitPath = path.split("/") const splitPath = path.split('/')
const fileName = splitPath[splitPath.length - 1] const fileName = splitPath[splitPath.length - 1]
// Get the file extension. // Get the file extension.
const splitExt = fileName.split(".") const splitExt = fileName.split('.')
const fileExt = splitExt[splitExt.length - 1] const fileExt = splitExt[splitExt.length - 1]
const fileObj = { const fileObj = {
@@ -125,7 +126,7 @@ class IPFS {
return fileData.data return fileData.data
} catch (err) { } catch (err) {
console.error(`Error in createFileModel()`) console.error('Error in createFileModel()')
throw err throw err
} }
} }
@@ -157,15 +158,16 @@ class IPFS {
* "fileExtension": "js" * "fileExtension": "js"
* } * }
*/ */
async uploadFileServer(path, modelId) { async uploadFileServer (path, modelId) {
try { try {
// Ensure the file exists. // Ensure the file exists.
if (!_this.fs.existsSync(path)) if (!_this.fs.existsSync(path)) {
throw new Error(`Could not find this file: ${path}`) throw new Error(`Could not find this file: ${path}`)
}
if (!modelId) { if (!modelId) {
throw new Error( throw new Error(
`Must include a file model ID in order to upload a file.` 'Must include a file model ID in order to upload a file.'
) )
} }
@@ -173,14 +175,14 @@ class IPFS {
const fileBuf = _this.fs.readFileSync(path) const fileBuf = _this.fs.readFileSync(path)
// Get the file name from the path. // Get the file name from the path.
const splitPath = path.split("/") const splitPath = path.split('/')
const fileName = splitPath[splitPath.length - 1] const fileName = splitPath[splitPath.length - 1]
// Prepare the upload object. Get a file ID from Uppy. // Prepare the upload object. Get a file ID from Uppy.
const id = _this.uppy.addFile({ const id = _this.uppy.addFile({
name: fileName, name: fileName,
data: fileBuf, data: fileBuf,
source: "Local", source: 'Local',
isRemote: false isRemote: false
}) })
// console.log(`id: ${JSON.stringify(id, null, 2)}`) // console.log(`id: ${JSON.stringify(id, null, 2)}`)
@@ -191,8 +193,9 @@ class IPFS {
// Upload the file to the server. // Upload the file to the server.
const upData = await _this.uppy.upload() const upData = await _this.uppy.upload()
if (upData.failed.length) if (upData.failed.length) {
throw new Error("The file could not be uploaded") throw new Error('The file could not be uploaded')
}
if (upData.successful.length) { if (upData.successful.length) {
delete upData.successful[0].data delete upData.successful[0].data
@@ -213,7 +216,7 @@ class IPFS {
return false return false
} catch (err) { } catch (err) {
console.error(`Error in bch-js/src/ipfs.js/upload(): `) console.error('Error in bch-js/src/ipfs.js/upload(): ')
throw err throw err
} }
} }
@@ -246,9 +249,9 @@ class IPFS {
* "fileName": "ipfs-e2e.js" * "fileName": "ipfs-e2e.js"
* } * }
*/ */
async getStatus(modelId) { async getStatus (modelId) {
try { try {
if (!modelId) throw new Error(`Must include a file model ID.`) if (!modelId) throw new Error('Must include a file model ID.')
const fileData = await _this.axios.get( const fileData = await _this.axios.get(
`${this.IPFS_API}/files/${modelId}` `${this.IPFS_API}/files/${modelId}`
@@ -266,10 +269,11 @@ class IPFS {
return fileObj return fileObj
} catch (err) { } catch (err) {
console.error(`Error in getStatus()`) console.error('Error in getStatus()')
throw err throw err
} }
} }
/** /**
* @api IPFS.createFileModelWeb() createFileModelWeb() * @api IPFS.createFileModelWeb() createFileModelWeb()
* @apiName createFileModelWeb() * @apiName createFileModelWeb()
@@ -311,20 +315,22 @@ class IPFS {
* "__v": 0 * "__v": 0
* } * }
*/ */
async createFileModelWeb(file) { async createFileModelWeb (file) {
try { try {
if (!file) throw new Error(`File is required`) if (!file) throw new Error('File is required')
const { name, size } = file const { name, size } = file
if (!name || typeof name !== "string") if (!name || typeof name !== 'string') {
throw new Error(`File should have the property 'name' of string type`) throw new Error("File should have the property 'name' of string type")
}
if (!size || typeof size !== "number") if (!size || typeof size !== 'number') {
throw new Error(`File should have the property 'size' of number type`) throw new Error("File should have the property 'size' of number type")
}
// Get the file extension. // Get the file extension.
const splitExt = name.split(".") const splitExt = name.split('.')
const fileExt = splitExt[splitExt.length - 1] const fileExt = splitExt[splitExt.length - 1]
const fileObj = { const fileObj = {
@@ -342,7 +348,7 @@ class IPFS {
return fileData.data return fileData.data
} catch (err) { } catch (err) {
console.error(`Error in createFileModelWeb()`) console.error('Error in createFileModelWeb()')
throw err throw err
} }
} }
@@ -377,24 +383,27 @@ class IPFS {
* "fileExtension": "js" * "fileExtension": "js"
* } * }
*/ */
async uploadFileWeb(file, modelId) { async uploadFileWeb (file, modelId) {
try { try {
if (!file) throw new Error(`File is required`) if (!file) throw new Error('File is required')
const { name, type, size } = file const { name, type, size } = file
if (!name || typeof name !== "string") if (!name || typeof name !== 'string') {
throw new Error(`File should have the property 'name' of string type`) throw new Error("File should have the property 'name' of string type")
}
if (!type || typeof type !== "string") if (!type || typeof type !== 'string') {
throw new Error(`File should have the property 'type' of string type`) throw new Error("File should have the property 'type' of string type")
}
if (!size || typeof size !== "number") if (!size || typeof size !== 'number') {
throw new Error(`File should have the property 'size' of number type`) throw new Error("File should have the property 'size' of number type")
}
if (!modelId) { if (!modelId) {
throw new Error( throw new Error(
`Must include a file model ID in order to upload a file.` 'Must include a file model ID in order to upload a file.'
) )
} }
@@ -402,7 +411,7 @@ class IPFS {
const id = _this.uppy.addFile({ const id = _this.uppy.addFile({
name: name, name: name,
data: file, data: file,
source: "Local", source: 'Local',
isRemote: false isRemote: false
}) })
// console.log(`id: ${JSON.stringify(id, null, 2)}`) // console.log(`id: ${JSON.stringify(id, null, 2)}`)
@@ -413,8 +422,9 @@ class IPFS {
// Upload the file to the server. // Upload the file to the server.
const upData = await _this.uppy.upload() const upData = await _this.uppy.upload()
if (upData.failed.length) if (upData.failed.length) {
throw new Error("The file could not be uploaded") throw new Error('The file could not be uploaded')
}
if (upData.successful.length) { if (upData.successful.length) {
delete upData.successful[0].data delete upData.successful[0].data
@@ -435,7 +445,7 @@ class IPFS {
return false return false
} catch (err) { } catch (err) {
console.error(`Error in bch-js/src/ipfs.js/uploadFileWeb(): `) console.error('Error in bch-js/src/ipfs.js/uploadFileWeb(): ')
throw err throw err
} }
} }
+13 -13
View File
@@ -1,9 +1,9 @@
const axios = require("axios") const axios = require('axios')
let _this // let _this
class Mining { class Mining {
constructor(config) { constructor (config) {
this.restURL = config.restURL this.restURL = config.restURL
this.apiToken = config.apiToken this.apiToken = config.apiToken
this.authToken = config.authToken this.authToken = config.authToken
@@ -24,14 +24,14 @@ class Mining {
} }
} }
_this = this // _this = this
} }
async getBlockTemplate(template_request) { async getBlockTemplate (templateRequest) {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}mining/getBlockTemplate/${template_request}`, `${this.restURL}mining/getBlockTemplate/${templateRequest}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -40,11 +40,11 @@ class Mining {
} }
} }
async getMiningInfo() { async getMiningInfo () {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}mining/getMiningInfo`, `${this.restURL}mining/getMiningInfo`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -53,11 +53,11 @@ class Mining {
} }
} }
async getNetworkHashps(nblocks = 120, height = 1) { async getNetworkHashps (nblocks = 120, height = 1) {
try { try {
const response = await axios.get( const response = await axios.get(
`${this.restURL}mining/getNetworkHashps?nblocks=${nblocks}&height=${height}`, `${this.restURL}mining/getNetworkHashps?nblocks=${nblocks}&height=${height}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} catch (error) { } catch (error) {
@@ -66,12 +66,12 @@ class Mining {
} }
} }
async submitBlock(hex, parameters) { async submitBlock (hex, parameters) {
let path = `${this.restURL}mining/submitBlock/${hex}` let path = `${this.restURL}mining/submitBlock/${hex}`
if (parameters) path = `${path}?parameters=${parameters}` if (parameters) path = `${path}?parameters=${parameters}`
try { try {
const response = await axios.post(path, _this.axiosOptions) const response = await axios.post(path, this.axiosOptions)
return response.data return response.data
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
+33 -33
View File
@@ -1,11 +1,14 @@
const BIP39 = require("bip39") /* eslint no-prototype-builtins: "off" */
const randomBytes = require("randombytes") /* eslint node/no-callback-literal: "off" */
const Bitcoin = require("bitcoincashjs-lib")
const Buffer = require("safe-buffer").Buffer const BIP39 = require('bip39')
const wif = require("wif") const randomBytes = require('randombytes')
const Bitcoin = require('@psf/bitcoincashjs-lib')
const Buffer = require('safe-buffer').Buffer
const wif = require('wif')
class Mnemonic { class Mnemonic {
constructor(address) { constructor (address) {
this._address = address this._address = address
} }
@@ -45,7 +48,7 @@ class Mnemonic {
* bchjs.Mnemonic.generate(256, bitbox.Mnemonic.wordLists().korean) * bchjs.Mnemonic.generate(256, bitbox.Mnemonic.wordLists().korean)
* // 기능 단추 교육 비난 시집 근육 운동 코미디 숟가락 과목 한동안 유적 시리즈 삼월 앞날 유난히 흰색 사실 논문 장사 어른 논문 의논 장차 * // 기능 단추 교육 비난 시집 근육 운동 코미디 숟가락 과목 한동안 유적 시리즈 삼월 앞날 유난히 흰색 사실 논문 장사 어른 논문 의논 장차
*/ */
generate(bits = 128, wordlist) { generate (bits = 128, wordlist) {
return BIP39.generateMnemonic(bits, randomBytes, wordlist) return BIP39.generateMnemonic(bits, randomBytes, wordlist)
} }
@@ -95,11 +98,8 @@ class Mnemonic {
* // generate 16 bytes of entropy * // generate 16 bytes of entropy
* let entropy = bchjs.Crypto.randomBytes(16); * let entropy = bchjs.Crypto.randomBytes(16);
* // * //
* // turn entropy to 12 japanese word mnemonic
* bchjs.Mnemonic.fromEntropy(entropy.toString('hex'), bchjs.Mnemonic.wordLists().japanese)
* // ぱそこん にあう にんめい きどく ちそう せんきょ かいが きおく いれる いねむり しいく きかんしゃ
*/ */
fromEntropy(bytes, wordlist) { fromEntropy (bytes, wordlist) {
return BIP39.entropyToMnemonic(bytes, wordlist) return BIP39.entropyToMnemonic(bytes, wordlist)
} }
@@ -136,8 +136,8 @@ class Mnemonic {
* bchjs.Mnemonic.toEntropy(mnemonic) * bchjs.Mnemonic.toEntropy(mnemonic)
* // <Buffer f3 79 da 02 cc 42 6e 6e 26 43 0d 25 e6 cc 37 2d fd 0a 1a 2e 4a 33 ac 4d c6 ae 6d 56 01 7f 64 2d> * // <Buffer f3 79 da 02 cc 42 6e 6e 26 43 0d 25 e6 cc 37 2d fd 0a 1a 2e 4a 33 ac 4d c6 ae 6d 56 01 7f 64 2d>
*/ */
toEntropy(mnemonic, wordlist) { toEntropy (mnemonic, wordlist) {
return Buffer.from(BIP39.mnemonicToEntropy(mnemonic, wordlist), "hex") return Buffer.from(BIP39.mnemonicToEntropy(mnemonic, wordlist), 'hex')
} }
/** /**
@@ -157,11 +157,11 @@ class Mnemonic {
* bchjs.Mnemonic.validate('boil lonely casino manage habit where total glory muffin name limit mansion boil lonely casino manage habit where total glory muffin name limit mansion', bitbox.Mnemonic.wordLists().english) * bchjs.Mnemonic.validate('boil lonely casino manage habit where total glory muffin name limit mansion boil lonely casino manage habit where total glory muffin name limit mansion', bitbox.Mnemonic.wordLists().english)
* // Invalid mnemonic * // Invalid mnemonic
*/ */
validate(mnemonic, wordlist) { validate (mnemonic, wordlist) {
// Preprocess the words // Preprocess the words
const words = mnemonic.split(" ") const words = mnemonic.split(' ')
// Detect blank phrase // Detect blank phrase
if (words.length === 0) return "Blank mnemonic" if (words.length === 0) return 'Blank mnemonic'
// Check each word // Check each word
for (let i = 0; i < words.length; i++) { for (let i = 0; i < words.length; i++) {
@@ -173,11 +173,11 @@ class Mnemonic {
} }
} }
// Check the words are valid // Check the words are valid
//const properPhrase = words.join() // const properPhrase = words.join()
const isValid = BIP39.validateMnemonic(mnemonic, wordlist) const isValid = BIP39.validateMnemonic(mnemonic, wordlist)
if (!isValid) return "Invalid mnemonic" if (!isValid) return 'Invalid mnemonic'
return "Valid mnemonic" return 'Valid mnemonic'
} }
/** /**
@@ -203,7 +203,7 @@ class Mnemonic {
* await bchjs.Mnemonic.toSeed('frost deliver coin clutch upon round scene wonder various wise luggage country', 'yayayayay'); * await bchjs.Mnemonic.toSeed('frost deliver coin clutch upon round scene wonder various wise luggage country', 'yayayayay');
* // <Buffer 1d 00 9f a3 a8 86 51 a4 04 d5 03 3d eb 6d b1 01 e2 f1 3b c3 c8 6d 1f b9 93 b4 d1 33 dc 84 21 12 2c 9b 52 10 ba d8 96 15 e0 b0 9a 34 33 52 f8 07 c8 c4 ... > * // <Buffer 1d 00 9f a3 a8 86 51 a4 04 d5 03 3d eb 6d b1 01 e2 f1 3b c3 c8 6d 1f b9 93 b4 d1 33 dc 84 21 12 2c 9b 52 10 ba d8 96 15 e0 b0 9a 34 33 52 f8 07 c8 c4 ... >
*/ */
toSeed(mnemonic, password = "") { toSeed (mnemonic, password = '') {
return BIP39.mnemonicToSeed(mnemonic, password) return BIP39.mnemonicToSeed(mnemonic, password)
} }
@@ -229,7 +229,7 @@ class Mnemonic {
* // spanish: [] * // spanish: []
* // } * // }
*/ */
wordLists() { wordLists () {
return BIP39.wordlists return BIP39.wordlists
} }
@@ -260,10 +260,10 @@ class Mnemonic {
* // { privateKeyWIF: 'L5gB66JqhfouEtZG5aRMQ9JaVS2ggkK3YozGfzZegBupaPXqdfaz', * // { privateKeyWIF: 'L5gB66JqhfouEtZG5aRMQ9JaVS2ggkK3YozGfzZegBupaPXqdfaz',
* // address: 'bitcoincash:qphwlpu2wzjxrjts94pn4wh778fwsu2afg2aj5her9' } ] * // address: 'bitcoincash:qphwlpu2wzjxrjts94pn4wh778fwsu2afg2aj5her9' } ]
*/ */
async toKeypairs(mnemonic, numberOfKeypairs = 1, regtest = false) { async toKeypairs (mnemonic, numberOfKeypairs = 1, regtest = false) {
const rootSeedBuffer = await this.toSeed(mnemonic, "") const rootSeedBuffer = await this.toSeed(mnemonic, '')
const hdNode = Bitcoin.HDNode.fromSeedBuffer(rootSeedBuffer) const hdNode = Bitcoin.HDNode.fromSeedBuffer(rootSeedBuffer)
const HDPath = `44'/145'/0'/0/` const HDPath = "44'/145'/0'/0/"
const accounts = [] const accounts = []
@@ -321,7 +321,7 @@ class Mnemonic {
* bchjs.Mnemonic.findNearestWord(word, wordlist); * bchjs.Mnemonic.findNearestWord(word, wordlist);
* // neve * // neve
*/ */
findNearestWord(word, wordlist) { findNearestWord (word, wordlist) {
let minDistance = 99 let minDistance = 99
let closestWord = wordlist[0] let closestWord = wordlist[0]
for (let i = 0; i < wordlist.length; i++) { for (let i = 0; i < wordlist.length; i++) {
@@ -352,7 +352,7 @@ module.exports = Mnemonic
* @return Object the final object. * @return Object the final object.
*/ */
const _extend = function(dst) { const _extend = function (dst) {
const sources = Array.prototype.slice.call(arguments, 1) const sources = Array.prototype.slice.call(arguments, 1)
for (let i = 0; i < sources.length; ++i) { for (let i = 0; i < sources.length; ++i) {
const src = sources[i] const src = sources[i]
@@ -365,8 +365,8 @@ const _extend = function(dst) {
* Defer execution of given function. * Defer execution of given function.
* @param {Function} func * @param {Function} func
*/ */
const _defer = function(func) { const _defer = function (func) {
if (typeof setImmediate === "function") return setImmediate(func) if (typeof setImmediate === 'function') return setImmediate(func)
return setTimeout(func, 0) return setTimeout(func, 0)
} }
@@ -374,7 +374,7 @@ const _defer = function(func) {
/** /**
* Based on the algorithm at http://en.wikipedia.org/wiki/Levenshtein_distance. * Based on the algorithm at http://en.wikipedia.org/wiki/Levenshtein_distance.
*/ */
var Levenshtein = { const Levenshtein = {
/** /**
* Calculate levenshtein distance of the two strings. * Calculate levenshtein distance of the two strings.
* *
@@ -382,7 +382,7 @@ var Levenshtein = {
* @param str2 String the second string. * @param str2 String the second string.
* @return Integer the levenshtein distance (0 and above). * @return Integer the levenshtein distance (0 and above).
*/ */
get: function(str1, str2) { get: function (str1, str2) {
// base cases // base cases
if (str1 === str2) return 0 if (str1 === str2) return 0
if (str1.length === 0) return str2.length if (str1.length === 0) return str2.length
@@ -432,7 +432,7 @@ var Levenshtein = {
* @param [options] Object additional options. * @param [options] Object additional options.
* @param [options.progress] Function progress callback with signature: function(percentComplete) * @param [options.progress] Function progress callback with signature: function(percentComplete)
*/ */
getAsync: function(str1, str2, cb, options) { getAsync: function (str1, str2, cb, options) {
options = _extend( options = _extend(
{}, {},
{ {
@@ -457,7 +457,7 @@ var Levenshtein = {
i = 0 i = 0
j = -1 j = -1
var __calculate = function() { const __calculate = function () {
// reset timer // reset timer
startTime = new Date().valueOf() startTime = new Date().valueOf()
currentTime = startTime currentTime = startTime
@@ -499,7 +499,7 @@ var Levenshtein = {
} }
// send a progress update? // send a progress update?
if (null !== options.progress) { if (options.progress !== null) {
try { try {
options.progress.call(null, (i * 100.0) / str1.length) options.progress.call(null, (i * 100.0) / str1.length)
} catch (err) { } catch (err) {
+25 -25
View File
@@ -3,12 +3,12 @@
Bitcoin.com Bitcoin.com
*/ */
const axios = require("axios") const axios = require('axios')
let _this // let _this
class Ninsight { class Ninsight {
constructor(config) { constructor (config) {
// this.restURL = config.restURL // this.restURL = config.restURL
// this.apiToken = config.apiToken // this.apiToken = config.apiToken
@@ -16,7 +16,7 @@ class Ninsight {
if (config) { if (config) {
this.ninsightURL = config.ninsightURL this.ninsightURL = config.ninsightURL
? config.ninsightURL ? config.ninsightURL
: `https://rest.bitcoin.com/v2` : 'https://rest.bitcoin.com/v2'
} }
// Add JWT token to the authorization header. // Add JWT token to the authorization header.
@@ -27,7 +27,7 @@ class Ninsight {
} }
} }
_this = this // _this = this
} }
/** /**
@@ -66,16 +66,16 @@ class Ninsight {
* // ] * // ]
* *
*/ */
async utxo(address) { async utxo (address) {
try { try {
// Handle single address. // Handle single address.
if (typeof address === "string") { if (typeof address === 'string') {
const response = await axios.post( const response = await axios.post(
`${this.ninsightURL}/address/utxo`, `${this.ninsightURL}/address/utxo`,
{ {
addresses: [address] addresses: [address]
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -86,13 +86,13 @@ class Ninsight {
{ {
addresses: address addresses: address
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input address must be a string or array of strings.`) throw new Error('Input address must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
@@ -135,15 +135,15 @@ class Ninsight {
* // ] * // ]
* *
*/ */
async unconfirmed(address) { async unconfirmed (address) {
try { try {
if (typeof address === "string") { if (typeof address === 'string') {
const response = await axios.post( const response = await axios.post(
`${this.ninsightURL}/address/unconfirmed`, `${this.ninsightURL}/address/unconfirmed`,
{ {
addresses: [address] addresses: [address]
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -153,13 +153,13 @@ class Ninsight {
{ {
addresses: address addresses: address
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input address must be a string or array of strings.`) throw new Error('Input address must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
@@ -211,15 +211,15 @@ class Ninsight {
* // * //
* *
*/ */
async transactions(address) { async transactions (address) {
try { try {
if (typeof address === "string") { if (typeof address === 'string') {
const response = await axios.post( const response = await axios.post(
`${this.ninsightURL}/address/transactions`, `${this.ninsightURL}/address/transactions`,
{ {
addresses: [address] addresses: [address]
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -229,13 +229,13 @@ class Ninsight {
{ {
addresses: address addresses: address
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Input address must be a string or array of strings.`) throw new Error('Input address must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
@@ -284,15 +284,15 @@ class Ninsight {
* // ] * // ]
* *
*/ */
async txDetails(txid) { async txDetails (txid) {
try { try {
if (typeof txid === "string") { if (typeof txid === 'string') {
const response = await axios.post( const response = await axios.post(
`${this.ninsightURL}/transaction/details`, `${this.ninsightURL}/transaction/details`,
{ {
txids: [txid] txids: [txid]
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -302,13 +302,13 @@ class Ninsight {
{ {
txids: txid txids: txid
}, },
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} }
throw new Error(`Transaction ID must be a string or array of strings.`) throw new Error('Transaction ID must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
-229
View File
@@ -1,229 +0,0 @@
/*
This library interacts with the public REST API endpoints that OpenBazaar
provides for their application.
*/
const axios = require("axios")
let _this
class OpenBazaar {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
}
_this = this
}
/**
* @api OpenBazaar.balance() balance()
* @apiName OpenBazaar Balance
* @apiGroup OpenBazaar
* @apiDescription Return Balance about an address.
*
* @apiExample Example usage:
* (async () => {
* try {
* let balance = await bchjs.OpenBazaar.balance('bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9');
* console.log(balance)
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* page: 1,
* totalPages: 1,
* itemsOnPage: 1000,
* addrStr: "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9",
* balance: "0.00001",
* totalReceived: "0.00001",
* totalSent: "0",
* unconfirmedBalance: "0",
* unconfirmedTxApperances: 0,
* txApperances: 1,
* transactions: [
* "2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7"
* ]
* }
*
*/
async balance(address) {
try {
// Handle single address.
if (typeof address === "string") {
let response
if (process.env.NETWORK === "testnet") {
response = await axios.get(
`https://tbch.blockbook.api.openbazaar.org/api/address/${address}`,
_this.axiosOptions
)
} else {
response = await axios.get(
`https://bch.blockbook.api.openbazaar.org/api/address/${address}`,
_this.axiosOptions
)
}
return response.data
}
throw new Error(`Input address must be a string.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
/**
* @api OpenBazaar.utxo() utxo()
* @apiName OpenBazaar Utxo
* @apiGroup OpenBazaar
* @apiDescription Return list of uxto for address.
*
* @apiExample Example usage:
* (async () => {
* try {
* let utxo = await bchjs.OpenBazaar.utxo('bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9');
* console.log(utxo);
* } catch(error) {
* console.error(error)
* }
* })()
*
* [
* {
* txid: "2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7",
* vout: 0,
* amount: "0.00001",
* satoshis: 1000,
* height: 602405,
* confirmations: 11
* }
* ]
*/
async utxo(address) {
try {
// Handle single address.
if (typeof address === "string") {
let response
if (process.env.NETWORK === "testnet") {
response = await axios.get(
`https://tbch.blockbook.api.openbazaar.org/api/utxo/${address}`,
_this.axiosOptions
)
} else {
response = await axios.get(
`https://bch.blockbook.api.openbazaar.org/api/utxo/${address}`,
_this.axiosOptions
)
}
return response.data
}
throw new Error(`Input address must be a string.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
/**
* @api OpenBazaar.tx() tx()
* @apiName OpenBazaar Tx
* @apiGroup OpenBazaar
* @apiDescription Get details on a transaction.
*
* @apiExample Example usage:
* (async () => {
* try {
* let txDetails = await bchjs.OpenBazaar.tx(`2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7`);
* console.log(txDetails);
* } catch(error) {
* console.error(error)
* }
* })()
*
*{
* txid: "2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7",
* version: 2,
* vin: [
* {
* txid: "5f09d317e24c5d376f737a2711f3bd1d381abdb41743fff3819b4f76382e1eac",
* vout: 1,
* sequence: 4294967295,
* n: 0,
* scriptSig: {
* hex:
* "473044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c4121038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9"
* },
* addresses: ["bitcoincash:qqxy8hycqe89j7wa79gnggq6z3gaqu2uvqy26xehfe"],
* value: "0.00047504"
* }
* ],
* vout: [
* {
* value: "0.00001",
* n: 0,
* scriptPubKey: {
* hex: "76a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788ac",
* addresses: ["bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"]
* },
* spent: false
* },
* {
* value: "0.00046256",
* n: 1,
* scriptPubKey: {
* hex: "76a9142dbf5e1804c39a497b908c876097d63210c8490288ac",
* addresses: ["bitcoincash:qqkm7hscqnpe5jtmjzxgwcyh6ceppjzfqg3jdn422e"]
* },
* spent: false
* }
* ],
* blockhash: "0000000000000000010903a1fc4274499037c9339be9ec7338ee980331c20ce5",
* blockheight: 602405,
* confirmations: 11,
* blocktime: 1569792892,
* valueOut: "0.00047256",
* valueIn: "0.00047504",
* fees: "0.00000248",
* hex:
* "0200000001ac1e2e38764f9b81f3ff4317b4bd1a381dbdf311277a736f375d4ce217d3095f010000006a473044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c4121038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9ffffffff02e8030000000000001976a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788acb0b40000000000001976a9142dbf5e1804c39a497b908c876097d63210c8490288ac00000000"
* }
*
*/
async tx(txid) {
try {
// Handle single txid.
if (typeof txid === "string") {
let response
if (process.env.NETWORK === "testnet") {
response = await axios.get(
`https://tbch.blockbook.api.openbazaar.org/api/tx/${txid}`,
_this.axiosOptions
)
} else {
response = await axios.get(
`https://bch.blockbook.api.openbazaar.org/api/tx/${txid}`,
_this.axiosOptions
)
}
return response.data
}
throw new Error(`Input txid must be a string.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = OpenBazaar
+91 -17
View File
@@ -1,10 +1,10 @@
const axios = require("axios") const axios = require('axios')
let _this // let _this
class Price { class Price {
constructor(config) { constructor (config) {
_this = this // _this = this
this.restURL = config.restURL this.restURL = config.restURL
this.apiToken = config.apiToken this.apiToken = config.apiToken
@@ -30,7 +30,7 @@ class Price {
} }
// This endpoint is deprecated. Documentation removed. // This endpoint is deprecated. Documentation removed.
async current(currency = "usd") { async current (currency = 'usd') {
try { try {
const response = await this.axios.get( const response = await this.axios.get(
`https://index-api.bitcoin.com/api/v0/cash/price/${currency.toLowerCase()}` `https://index-api.bitcoin.com/api/v0/cash/price/${currency.toLowerCase()}`
@@ -58,17 +58,17 @@ class Price {
* let current = await bchjs.Price.getUsd(); * let current = await bchjs.Price.getUsd();
* console.log(current); * console.log(current);
* } catch(err) { * } catch(err) {
* console.err(err) * console.error(err)
* } * }
*})() *})()
* *
* // 266.81 * // 266.81
*/ */
async getUsd() { async getUsd () {
try { try {
const response = await this.axios.get( const response = await this.axios.get(
`${this.restURL}price/usd`, `${this.restURL}price/usd`,
_this.axiosOptions this.axiosOptions
) )
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`) // console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
@@ -94,7 +94,7 @@ class Price {
* let current = await bchjs.Price.rates(); * let current = await bchjs.Price.rates();
* console.log(current); * console.log(current);
* } catch(err) { * } catch(err) {
* console.err(err) * console.error(err)
* } * }
*})() *})()
* *
@@ -107,11 +107,11 @@ class Price {
* ZWL: "80215.03" * ZWL: "80215.03"
* } * }
*/ */
async rates() { async rates () {
try { try {
const response = await this.axios.get( const response = await this.axios.get(
`${this.restURL}price/rates`, `${this.restURL}price/rates`,
_this.axiosOptions this.axiosOptions
) )
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`) // console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
@@ -127,8 +127,9 @@ class Price {
* @apiName Price getBchaUsd() * @apiName Price getBchaUsd()
* @apiGroup Price * @apiGroup Price
* @apiDescription Return current price of BCHA in USD. * @apiDescription Return current price of BCHA in USD.
* This endpoint gets the USD price of BCHA from the Coinex API. The price * This endpoint gets the USD price of XEC from the Coinex API. The price
* comes from bch-api, so it has a better chance of working in Tor. * denominated in BCHA comes from bch-api, so it has a better chance of
* working in Tor.
* *
* @apiExample Example usage: * @apiExample Example usage:
*(async () => { *(async () => {
@@ -136,17 +137,90 @@ class Price {
* let current = await bchjs.Price.getBchaUsd(); * let current = await bchjs.Price.getBchaUsd();
* console.log(current); * console.log(current);
* } catch(err) { * } catch(err) {
* console.err(err) * console.error(err)
* } * }
*})() *})()
* *
* // 18.81 * // 212.34
*/ */
async getBchaUsd() { async getBchaUsd () {
try { try {
const response = await this.axios.get( const response = await this.axios.get(
`${this.restURL}price/bchausd`, `${this.restURL}price/bchausd`,
_this.axiosOptions this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
const bchaPrice = response.data.usd * 1000000
// Convert XEC denomination to BCHA denomination
return bchaPrice
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
/**
* @api price.getXecUsd() getXecUsd()
* @apiName Price getXecUsd()
* @apiGroup Price
* @apiDescription Return current price of XEC in USD.
* This endpoint gets the USD price of XEC from the Coinex API. The price
* comes from bch-api, so it has a better chance of working in Tor.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.getXecUsd();
* console.log(current);
* } catch(err) {
* console.error(err)
* }
*})()
*
* // 0.00021234
*/
async getXecUsd () {
try {
const response = await this.axios.get(
`${this.restURL}price/bchausd`,
this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data.usd
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
/**
* @api price.getBchUsd() getBchUsd()
* @apiName Price getBchUsd()
* @apiGroup Price
* @apiDescription Return current price of BCH in USD.
* This endpoint gets the USD price of BCH from the Coinex API. The price
* comes from bch-api, so it has a better chance of working in Tor.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.getBchUsd();
* console.log(current);
* } catch(err) {
* console.error(err)
* }
*})()
*
* // 512.81
*/
async getBchUsd () {
try {
const response = await this.axios.get(
`${this.restURL}price/bchusd`,
this.axiosOptions
) )
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`) // console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
+166 -41
View File
@@ -1,9 +1,9 @@
const axios = require("axios") const axios = require('axios')
let _this // let _this
class RawTransactions { class RawTransactions {
constructor(config) { constructor (config) {
this.restURL = config.restURL this.restURL = config.restURL
this.apiToken = config.apiToken this.apiToken = config.apiToken
this.authToken = config.authToken this.authToken = config.authToken
@@ -27,7 +27,7 @@ class RawTransactions {
// Encapsulate dependencies // Encapsulate dependencies
this.axios = axios this.axios = axios
_this = this // this = this
} }
/** /**
@@ -92,13 +92,13 @@ class RawTransactions {
* // vin: [ [Object] ], * // vin: [ [Object] ],
* // vout: [ [Object] ] } ] * // vout: [ [Object] ] } ]
*/ */
async decodeRawTransaction(hex) { async decodeRawTransaction (hex) {
try { try {
// Single hex // Single hex
if (typeof hex === "string") { if (typeof hex === 'string') {
const response = await axios.get( const response = await axios.get(
`${this.restURL}rawtransactions/decodeRawTransaction/${hex}`, `${this.restURL}rawtransactions/decodeRawTransaction/${hex}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
@@ -106,23 +106,22 @@ class RawTransactions {
// Array of hexes // Array of hexes
} else if (Array.isArray(hex)) { } else if (Array.isArray(hex)) {
const options = { const options = {
method: "POST", method: 'POST',
url: `${this.restURL}rawtransactions/decodeRawTransaction`, url: `${this.restURL}rawtransactions/decodeRawTransaction`,
data: { data: {
hexes: hex hexes: hex
}, },
headers: { headers: this.axiosOptions.headers
authorization: `Token ${_this.apiToken}`
}
} }
const response = await axios(options) const response = await axios(options)
return response.data return response.data
} }
throw new Error(`Input must be a string or array of strings.`) throw new Error('Input must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.error) throw new Error(error.error)
else if (error.response && error.response.data) throw error.response.data
else throw error else throw error
} }
} }
@@ -160,36 +159,35 @@ class RawTransactions {
* // type: 'nonstandard', * // type: 'nonstandard',
* // p2sh: 'bitcoincash:pqwndulzwft8dlmqrteqyc9hf823xr3lcc7ypt74ts' }] * // p2sh: 'bitcoincash:pqwndulzwft8dlmqrteqyc9hf823xr3lcc7ypt74ts' }]
*/ */
async decodeScript(script) { async decodeScript (script) {
//if (typeof script !== "string") script = JSON.stringify(script) // if (typeof script !== "string") script = JSON.stringify(script)
try { try {
if (typeof script === "string") { if (typeof script === 'string') {
const response = await axios.get( const response = await axios.get(
`${this.restURL}rawtransactions/decodeScript/${script}`, `${this.restURL}rawtransactions/decodeScript/${script}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} else if (Array.isArray(script)) { } else if (Array.isArray(script)) {
const options = { const options = {
method: "POST", method: 'POST',
url: `${this.restURL}rawtransactions/decodeScript`, url: `${this.restURL}rawtransactions/decodeScript`,
data: { data: {
hexes: script hexes: script
}, },
headers: { headers: this.axiosOptions.headers
authorization: `Token ${this.apiToken}`
}
} }
const response = await axios(options) const response = await axios(options)
return response.data return response.data
} }
throw new Error(`Input must be a string or array of strings.`) throw new Error('Input must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.error) throw new Error(error.error)
else if (error.response && error.response.data) throw error.response.data
else throw error else throw error
} }
} }
@@ -258,34 +256,160 @@ class RawTransactions {
* // time: 1547752564, * // time: 1547752564,
* // blocktime: 1547752564 } ] * // blocktime: 1547752564 } ]
*/ */
async getRawTransaction(txid, verbose = false) { async getRawTransaction (txid, verbose = false, usrObj = null) {
try { try {
if (typeof txid === "string") { if (typeof txid === 'string') {
// console.log(
// 'getRawTransaction() this.axiosOptions: ',
// this.axiosOptions
// )
const response = await axios.get( const response = await axios.get(
`${this.restURL}rawtransactions/getRawTransaction/${txid}?verbose=${verbose}`, `${this.restURL}rawtransactions/getRawTransaction/${txid}?verbose=${verbose}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
} else if (Array.isArray(txid)) { } else if (Array.isArray(txid)) {
const options = { const options = {
method: "POST", method: 'POST',
url: `${this.restURL}rawtransactions/getRawTransaction`, url: `${this.restURL}rawtransactions/getRawTransaction`,
data: { data: {
txids: txid, txids: txid,
verbose: verbose verbose: verbose,
usrObj // pass user data when making an internal call.
}, },
headers: _this.axiosOptions.headers headers: this.axiosOptions.headers
} }
const response = await axios(options) const response = await axios(options)
return response.data return response.data
} }
throw new Error(`Input must be a string or array of strings.`) throw new Error('Input must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.error) throw new Error(error.error)
else throw error
// This case handles rate limit errors.
if (error.response && error.response.data && error.response.data.error) {
throw new Error(error.response.data.error)
} else if (error.response && error.response.data) {
throw error.response.data
} else throw error
}
}
// Given verbose transaction details, this function retrieves the transaction
// data for the inputs (the parent transactions). It returns an array of
// objects. Each object corresponds to a transaction input, and contains
// the address that generated that input UTXO.
//
// Assumes a single TX. Does not yet work with an array of TXs.
// This function returns an array of objects, each object if formated as follows:
// {
// vin: 0, // The position of the input for the given txid
// address: bitcoincash:qzhrpmu7nruyfcemeanqh5leuqcnf6zkjq4qm9nqh0
// }
async _getInputAddrs (txDetails) {
try {
const retArray = [] // Return array
for (let i = 0; i < txDetails.vin.length; i++) {
// The first input represents the sender of the BCH or tokens.
const vin = txDetails.vin[i]
const inputTxid = vin.txid
const inputVout = vin.vout
// TODO: Coinbase TXs have no input transaction. Figure out how to
// handle this corner case.
// Get the TX details for the input, in order to retrieve the address of
// the sender.
const txDetailsParent = await this.getRawTransaction(inputTxid, true)
// console.log(
// `txDetailsParent: ${JSON.stringify(txDetailsParent, null, 2)}`
// )
// The vout from the previous tx that represents the sender.
const voutSender = txDetailsParent.vout[inputVout]
retArray.push({
vin: i,
address: voutSender.scriptPubKey.addresses[0],
value: voutSender.value
})
}
return retArray
} catch (error) {
if (error.error) throw new Error(error.error)
// This case handles rate limit errors.
if (error.response && error.response.data && error.response.data.error) {
throw new Error(error.response.data.error)
} else if (error.response && error.response.data) {
throw error.response.data
} else throw error
}
}
/**
* @api RawTransactions.getTxData() getTxData()
* @apiName getTxData
* @apiGroup RawTransactions
* @apiDescription
* Returns an object of transaction data, including addresses for input UTXOs.
*
* This function is equivalent to running `getRawTransaction (txid, true)`,
* execept the `vin` array will be populated with an `address` property that
* contains the `bitcoincash:` address of the sender for each input.
*
* This function will only work with a single txid. It does not yet support an
* array of TXIDs.
*
* @apiExample Example usage:
* (async () => {
* try {
* let txData = await bchjs.RawTransactions.getTxData("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
* console.log(txData);
* } catch(error) {
* console.error(error)
* }
* })()
*/
// Equivalent to running: async getRawTransaction (txid, verbose = true)
// Only handles a single TXID (not arrays).
// Appends the BCH address to the inputs of the transaction.
async getTxData (txid) {
try {
if (typeof txid !== 'string') {
throw new Error(
'Input to raw-transaction.js/getTxData() must be a string containg a TXID.'
)
}
// Get the TX details for the transaction under consideration.
const txDetails = await this.getRawTransaction(txid, true)
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
const inAddrs = await this._getInputAddrs(txDetails)
// console.log(`inAddrs: ${JSON.stringify(inAddrs, null, 2)}`)
// Add the input address to the transaction data.
for (let i = 0; i < inAddrs.length; i++) {
txDetails.vin[i].address = inAddrs[i].address
txDetails.vin[i].value = inAddrs[i].value
}
return txDetails
} catch (error) {
if (error.error) throw new Error(error.error)
// This case handles rate limit errors.
if (error.response && error.response.data && error.response.data.error) {
throw new Error(error.response.data.error)
} else if (error.response && error.response.data) {
throw error.response.data
} else throw error
} }
} }
@@ -321,16 +445,16 @@ class RawTransactions {
* })() * })()
* // ['0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098'] * // ['0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098']
*/ */
async sendRawTransaction(hex, allowhighfees = false) { async sendRawTransaction (hex, allowhighfees = false) {
try { try {
// Single tx hex. // Single tx hex.
if (typeof hex === "string") { if (typeof hex === 'string') {
const response = await this.axios.get( const response = await this.axios.get(
`${this.restURL}rawtransactions/sendRawTransaction/${hex}`, `${this.restURL}rawtransactions/sendRawTransaction/${hex}`,
_this.axiosOptions this.axiosOptions
) )
if (response.data === "66: insufficient priority") { if (response.data === '66: insufficient priority') {
console.warn( console.warn(
`WARN: Insufficient Priority! This is likely due to a fee that is too low, or insufficient funds. `WARN: Insufficient Priority! This is likely due to a fee that is too low, or insufficient funds.
Please ensure that there is BCH in the given wallet. If you are running on the testnet, get some Please ensure that there is BCH in the given wallet. If you are running on the testnet, get some
@@ -343,21 +467,22 @@ class RawTransactions {
// Array input // Array input
} else if (Array.isArray(hex)) { } else if (Array.isArray(hex)) {
const options = { const options = {
method: "POST", method: 'POST',
url: `${this.restURL}rawtransactions/sendRawTransaction`, url: `${this.restURL}rawtransactions/sendRawTransaction`,
data: { data: {
hexes: hex hexes: hex
}, },
headers: _this.axiosOptions.headers headers: this.axiosOptions.headers
} }
const response = await _this.axios(options) const response = await this.axios(options)
return response.data return response.data
} }
throw new Error(`Input hex must be a string or array of strings.`) throw new Error('Input hex must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.error) throw new Error(error.error)
else if (error.response && error.response.data) throw error.response.data
else throw error else throw error
} }
} }
+18 -18
View File
@@ -1,7 +1,7 @@
const schnorr = require("bip-schnorr") const schnorr = require('bip-schnorr')
class Schnorr { class Schnorr {
constructor(config) { constructor (config) {
this.restURL = config.restURL this.restURL = config.restURL
this.apiToken = config.apiToken this.apiToken = config.apiToken
this.authToken = config.authToken this.authToken = config.authToken
@@ -46,7 +46,7 @@ class Schnorr {
* console.log("The signature is: " + createdSignature.toString("hex")) * console.log("The signature is: " + createdSignature.toString("hex"))
* // The signature is: 2a298dacae57395a15d0795ddbfd1dcb564da82b0f269bc70a74f8220429ba1d1e51a22ccec35599b8f266912281f8365ffc2d035a230434a1a64dc59f7013fd * // The signature is: 2a298dacae57395a15d0795ddbfd1dcb564da82b0f269bc70a74f8220429ba1d1e51a22ccec35599b8f266912281f8365ffc2d035a230434a1a64dc59f7013fd
*/ */
sign(privateKey, message) { sign (privateKey, message) {
return schnorr.sign(privateKey, message) return schnorr.sign(privateKey, message)
} }
@@ -78,7 +78,7 @@ class Schnorr {
* console.error("The signature verification failed: " + e) * console.error("The signature verification failed: " + e)
* } * }
*/ */
verify(publicKey, message, signatureToVerify) { verify (publicKey, message, signatureToVerify) {
return schnorr.verify(publicKey, message, signatureToVerify) return schnorr.verify(publicKey, message, signatureToVerify)
} }
@@ -140,7 +140,7 @@ class Schnorr {
* console.error("The signature verification failed: " + e) * console.error("The signature verification failed: " + e)
* } * }
*/ */
batchVerify(publicKeys, messages, signaturesToVerify) { batchVerify (publicKeys, messages, signaturesToVerify) {
return schnorr.batchVerify(publicKeys, messages, signaturesToVerify) return schnorr.batchVerify(publicKeys, messages, signaturesToVerify)
} }
@@ -189,7 +189,7 @@ class Schnorr {
* console.error("The signature verification failed: " + e) * console.error("The signature verification failed: " + e)
* } * }
*/ */
nonInteractive(privateKeys, message) { nonInteractive (privateKeys, message) {
return schnorr.muSig.nonInteractive(privateKeys, message) return schnorr.muSig.nonInteractive(privateKeys, message)
} }
@@ -263,7 +263,7 @@ class Schnorr {
* // ----------------------------------------------------------------------- * // -----------------------------------------------------------------------
* publicData.pubKeyHash = bchjs.Schnorr.computeEll(publicData.pubKeys) * publicData.pubKeyHash = bchjs.Schnorr.computeEll(publicData.pubKeys)
*/ */
computeEll(publicKeys) { computeEll (publicKeys) {
return schnorr.muSig.computeEll(publicKeys) return schnorr.muSig.computeEll(publicKeys)
} }
@@ -281,7 +281,7 @@ class Schnorr {
* publicData.pubKeyHash * publicData.pubKeyHash
* ) * )
*/ */
publicKeyCombine(publicKeys, publicKeyHash) { publicKeyCombine (publicKeys, publicKeyHash) {
return schnorr.muSig.pubKeyCombine(publicKeys, publicKeyHash) return schnorr.muSig.pubKeyCombine(publicKeys, publicKeyHash)
} }
@@ -313,7 +313,7 @@ class Schnorr {
* }) * })
* const signerSession = signerPrivateData[0].session * const signerSession = signerPrivateData[0].session
*/ */
sessionInitialize(sessionId, privateKey, message, pubKeyCombined, ell, idx) { sessionInitialize (sessionId, privateKey, message, pubKeyCombined, ell, idx) {
return schnorr.muSig.sessionInitialize( return schnorr.muSig.sessionInitialize(
sessionId, sessionId,
privateKey, privateKey,
@@ -365,7 +365,7 @@ class Schnorr {
* data => (data.session.nonceIsNegated = signerSession.nonceIsNegated) * data => (data.session.nonceIsNegated = signerSession.nonceIsNegated)
* ) * )
*/ */
sessionNonceCombine(session, nonces) { sessionNonceCombine (session, nonces) {
return schnorr.muSig.sessionNonceCombine(session, nonces) return schnorr.muSig.sessionNonceCombine(session, nonces)
} }
@@ -392,7 +392,7 @@ class Schnorr {
* ) * )
* }) * })
*/ */
partialSign(session, message, nonceCombined, pubKeyCombined) { partialSign (session, message, nonceCombined, pubKeyCombined) {
return schnorr.muSig.partialSign( return schnorr.muSig.partialSign(
session, session,
message, message,
@@ -436,7 +436,7 @@ class Schnorr {
* ) * )
* } * }
*/ */
partialSignatureVerify( partialSignatureVerify (
session, session,
partialSignature, partialSignature,
nonceCombined, nonceCombined,
@@ -484,27 +484,27 @@ class Schnorr {
* publicData.signature * publicData.signature
* ) * )
*/ */
partialSignaturesCombine(nonceCombined, partialSignatures) { partialSignaturesCombine (nonceCombined, partialSignatures) {
return schnorr.muSig.partialSigCombine(nonceCombined, partialSignatures) return schnorr.muSig.partialSigCombine(nonceCombined, partialSignatures)
} }
bufferToInt(buffer) { bufferToInt (buffer) {
return schnorr.convert.bufferToInt(buffer) return schnorr.convert.bufferToInt(buffer)
} }
intToBuffer(bigInteger) { intToBuffer (bigInteger) {
return schnorr.convert.intToBuffer(bigInteger) return schnorr.convert.intToBuffer(bigInteger)
} }
hash(buffer) { hash (buffer) {
return schnorr.convert.hash(buffer) return schnorr.convert.hash(buffer)
} }
pointToBuffer(point) { pointToBuffer (point) {
return schnorr.convert.pointToBuffer(point) return schnorr.convert.pointToBuffer(point)
} }
pubKeyToPoint(publicKey) { pubKeyToPoint (publicKey) {
return schnorr.convert.pubKeyToPoint(publicKey) return schnorr.convert.pubKeyToPoint(publicKey)
} }
} }
+10 -10
View File
@@ -1,8 +1,8 @@
const Bitcoin = require("bitcoincashjs-lib") const Bitcoin = require('@psf/bitcoincashjs-lib')
const opcodes = require("bitcoincash-ops") const opcodes = require('@psf/bitcoincash-ops')
class Script { class Script {
constructor() { constructor () {
this.opcodes = opcodes this.opcodes = opcodes
this.nullData = Bitcoin.script.nullData this.nullData = Bitcoin.script.nullData
this.multisig = { this.multisig = {
@@ -58,7 +58,7 @@ class Script {
* bchjs.Script.classifyInput(bchjs.Script.fromASM(scripthashInput)); * bchjs.Script.classifyInput(bchjs.Script.fromASM(scripthashInput));
* // scripthash * // scripthash
*/ */
classifyInput(script) { classifyInput (script) {
return Bitcoin.script.classifyInput(script) return Bitcoin.script.classifyInput(script)
} }
@@ -90,7 +90,7 @@ class Script {
* bchjs.Script.classifyOutput(bchjs.Script.fromASM(scripthashOutput)); * bchjs.Script.classifyOutput(bchjs.Script.fromASM(scripthashOutput));
* // scripthash * // scripthash
*/ */
classifyOutput(script) { classifyOutput (script) {
return Bitcoin.script.classifyOutput(script) return Bitcoin.script.classifyOutput(script)
} }
@@ -116,7 +116,7 @@ class Script {
* // 136, * // 136,
* // 172 ] * // 172 ]
*/ */
decode(scriptBuffer) { decode (scriptBuffer) {
return Bitcoin.script.decompile(scriptBuffer) return Bitcoin.script.decompile(scriptBuffer)
} }
@@ -150,7 +150,7 @@ class Script {
* bchjs.Script.encode(scriptPubKey); * bchjs.Script.encode(scriptPubKey);
* // <Buffer 76 a9 14 24 e9 c0 78 04 d0 ee 7e 5b da 93 4e 0a 3a e8 71 0f c0 07 dd 88 ac> * // <Buffer 76 a9 14 24 e9 c0 78 04 d0 ee 7e 5b da 93 4e 0a 3a e8 71 0f c0 07 dd 88 ac>
*/ */
encode(scriptChunks) { encode (scriptChunks) {
const arr = [] const arr = []
scriptChunks.forEach(chunk => { scriptChunks.forEach(chunk => {
arr.push(chunk) arr.push(chunk)
@@ -187,7 +187,7 @@ class Script {
* bchjs.Script.encode2(scriptPubKey); * bchjs.Script.encode2(scriptPubKey);
* // <Buffer 76 a9 14 24 e9 c0 78 04 d0 ee 7e 5b da 93 4e 0a 3a e8 71 0f c0 07 dd 88 ac> * // <Buffer 76 a9 14 24 e9 c0 78 04 d0 ee 7e 5b da 93 4e 0a 3a e8 71 0f c0 07 dd 88 ac>
*/ */
encode2(scriptChunks) { encode2 (scriptChunks) {
const arr = [] const arr = []
scriptChunks.forEach(chunk => { scriptChunks.forEach(chunk => {
arr.push(chunk) arr.push(chunk)
@@ -213,7 +213,7 @@ class Script {
* bchjs.Script.toASM(scriptBuffer); * bchjs.Script.toASM(scriptBuffer);
* // OP_DUP OP_HASH160 bee4182d9fbc8931a728410a0cd3e0f340f2995a OP_EQUALVERIFY OP_CHECKSIG * // OP_DUP OP_HASH160 bee4182d9fbc8931a728410a0cd3e0f340f2995a OP_EQUALVERIFY OP_CHECKSIG
*/ */
toASM(buffer) { toASM (buffer) {
return Bitcoin.script.toASM(buffer) return Bitcoin.script.toASM(buffer)
} }
@@ -235,7 +235,7 @@ class Script {
* bchjs.Script.fromASM(scriptPubKeyASM); * bchjs.Script.fromASM(scriptPubKeyASM);
* // <Buffer 76 a9 14 be e4 18 2d 9f bc 89 31 a7 28 41 0a 0c d3 e0 f3 40 f2 99 5a 88 ac> * // <Buffer 76 a9 14 be e4 18 2d 9f bc 89 31 a7 28 41 0a 0c d3 e0 f3 40 f2 99 5a 88 ac>
*/ */
fromASM(asm) { fromASM (asm) {
return Bitcoin.script.fromASM(asm) return Bitcoin.script.fromASM(asm)
} }
} }
+26 -20
View File
@@ -1,11 +1,11 @@
const BCHJSAddress = require("../address") const BCHJSAddress = require('../address')
// const bchAddress = new BCHJSAddress() // const bchAddress = new BCHJSAddress()
let bchAddress let bchAddress
const bchaddrjs = require("bchaddrjs-slp") const bchaddrjs = require('bchaddrjs-slp')
class Address extends BCHJSAddress { class Address extends BCHJSAddress {
constructor(config) { constructor (config) {
super(config) super(config)
this.restURL = config.restURL this.restURL = config.restURL
@@ -52,11 +52,11 @@ class Address extends BCHJSAddress {
* bchjs.SLP.Address.toSLPAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx', false) * bchjs.SLP.Address.toSLPAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx', false)
* // qzq9je6pntpva3wf6scr7mlnycr54sjgeqauyclpwv * // qzq9je6pntpva3wf6scr7mlnycr54sjgeqauyclpwv
*/ */
toSLPAddress(address, prefix = true, regtest = false) { toSLPAddress (address, prefix = true, regtest = false) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
const slpAddress = bchaddrjs.toSlpAddress(address) const slpAddress = bchaddrjs.toSlpAddress(address)
if (prefix) return slpAddress if (prefix) return slpAddress
return slpAddress.split(":")[1] return slpAddress.split(':')[1]
} }
/** /**
@@ -98,11 +98,11 @@ class Address extends BCHJSAddress {
* bchjs.SLP.Address.toCashAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx', false) * bchjs.SLP.Address.toCashAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx', false)
* // qzq9je6pntpva3wf6scr7mlnycr54sjgeqxgrr9ku3 * // qzq9je6pntpva3wf6scr7mlnycr54sjgeqxgrr9ku3
*/ */
toCashAddress(address, prefix = true, regtest = false) { toCashAddress (address, prefix = true, regtest = false) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
const cashAddress = bchaddrjs.toCashAddress(address) const cashAddress = bchaddrjs.toCashAddress(address)
if (prefix) return cashAddress if (prefix) return cashAddress
return cashAddress.split(":")[1] return cashAddress.split(':')[1]
} }
/** /**
@@ -145,18 +145,18 @@ class Address extends BCHJSAddress {
* bchjs.SLP.Address.toLegacyAddress('qph2v4mkxjgdqgmlyjx6njmey0ftrxlnggs3v58dse') * bchjs.SLP.Address.toLegacyAddress('qph2v4mkxjgdqgmlyjx6njmey0ftrxlnggs3v58dse')
* // mqc1tmwY2368LLGktnePzEyPAsgADxbksi * // mqc1tmwY2368LLGktnePzEyPAsgADxbksi
*/ */
toLegacyAddress(address) { toLegacyAddress (address) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
const cashAddr = bchaddrjs.toCashAddress(address) const cashAddr = bchaddrjs.toCashAddress(address)
return bchAddress.toLegacyAddress(cashAddr) return bchAddress.toLegacyAddress(cashAddr)
} }
isLegacyAddress(address) { isLegacyAddress (address) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
return bchAddress.isLegacyAddress(address) return bchAddress.isLegacyAddress(address)
} }
isCashAddress(address) { isCashAddress (address) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
if (bchaddrjs.isSlpAddress(address)) return false if (bchaddrjs.isSlpAddress(address)) return false
@@ -195,7 +195,7 @@ class Address extends BCHJSAddress {
* bchjs.SLP.Address.isSLPAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi') * bchjs.SLP.Address.isSLPAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
* // false * // false
*/ */
isSLPAddress(address) { isSLPAddress (address) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
return bchaddrjs.isSlpAddress(address) return bchaddrjs.isSlpAddress(address)
} }
@@ -248,7 +248,7 @@ class Address extends BCHJSAddress {
* bchjs.SLP.Address.isMainnetAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi') * bchjs.SLP.Address.isMainnetAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
* // false * // false
*/ */
isMainnetAddress(address) { isMainnetAddress (address) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
const cashaddr = bchaddrjs.toCashAddress(address) const cashaddr = bchaddrjs.toCashAddress(address)
return bchAddress.isMainnetAddress(cashaddr) return bchAddress.isMainnetAddress(cashaddr)
@@ -301,11 +301,12 @@ class Address extends BCHJSAddress {
* bchjs.SLP.Address.isTestnetAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi') * bchjs.SLP.Address.isTestnetAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
* // true * // true
*/ */
isTestnetAddress(address) { isTestnetAddress (address) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
const cashAddr = bchaddrjs.toCashAddress(address) const cashAddr = bchaddrjs.toCashAddress(address)
return bchAddress.isTestnetAddress(cashAddr) return bchAddress.isTestnetAddress(cashAddr)
} }
/** /**
* @api SLP.Address.isP2PKHAddress() isP2PKHAddress() * @api SLP.Address.isP2PKHAddress() isP2PKHAddress()
* @apiName isP2PKHAddress * @apiName isP2PKHAddress
@@ -353,11 +354,12 @@ class Address extends BCHJSAddress {
* bchjs.SLP.Address.isP2PKHAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi') * bchjs.SLP.Address.isP2PKHAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
* // true * // true
*/ */
isP2PKHAddress(address) { isP2PKHAddress (address) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
const cashAddr = bchaddrjs.toCashAddress(address) const cashAddr = bchaddrjs.toCashAddress(address)
return bchAddress.isP2PKHAddress(cashAddr) return bchAddress.isP2PKHAddress(cashAddr)
} }
/** /**
* @api SLP.Address.isP2SHAddress() isP2SHAddress() * @api SLP.Address.isP2SHAddress() isP2SHAddress()
* @apiName isP2SHAddress * @apiName isP2SHAddress
@@ -405,11 +407,12 @@ class Address extends BCHJSAddress {
* bchjs.SLP.Address.isP2SHAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi') * bchjs.SLP.Address.isP2SHAddress('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
* // false * // false
*/ */
isP2SHAddress(address) { isP2SHAddress (address) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
const cashAddr = bchaddrjs.toCashAddress(address) const cashAddr = bchaddrjs.toCashAddress(address)
return bchAddress.isP2SHAddress(cashAddr) return bchAddress.isP2SHAddress(cashAddr)
} }
/** /**
* @api SLP.Address.detectAddressFormat() detectAddressFormat() * @api SLP.Address.detectAddressFormat() detectAddressFormat()
* @apiName detectAddressFormat * @apiName detectAddressFormat
@@ -457,12 +460,13 @@ class Address extends BCHJSAddress {
* bchjs.SLP.Address.detectAddressFormat('mqc1tmwY2368LLGktnePzEyPAsgADxbksi') * bchjs.SLP.Address.detectAddressFormat('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
* // legacy * // legacy
*/ */
detectAddressFormat(address) { detectAddressFormat (address) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
if (bchaddrjs.isSlpAddress(address)) return "slpaddr" if (bchaddrjs.isSlpAddress(address)) return 'slpaddr'
return bchAddress.detectAddressFormat(address) return bchAddress.detectAddressFormat(address)
} }
/** /**
* @api SLP.Address.detectAddressNetwork() detectAddressNetwork() * @api SLP.Address.detectAddressNetwork() detectAddressNetwork()
* @apiName detectAddressNetwork * @apiName detectAddressNetwork
@@ -510,11 +514,12 @@ class Address extends BCHJSAddress {
* bchjs.SLP.Address.detectAddressNetwork('mqc1tmwY2368LLGktnePzEyPAsgADxbksi') * bchjs.SLP.Address.detectAddressNetwork('mqc1tmwY2368LLGktnePzEyPAsgADxbksi')
* // testnet * // testnet
*/ */
detectAddressNetwork(address) { detectAddressNetwork (address) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
const cashAddr = bchaddrjs.toCashAddress(address) const cashAddr = bchaddrjs.toCashAddress(address)
return bchAddress.detectAddressNetwork(cashAddr) return bchAddress.detectAddressNetwork(cashAddr)
} }
/** /**
* @api SLP.Address.detectAddressType() detectAddressType() * @api SLP.Address.detectAddressType() detectAddressType()
* @apiName detectAddressType * @apiName detectAddressType
@@ -562,11 +567,12 @@ class Address extends BCHJSAddress {
* bchjs.SLP.Address.detectAddressType('mqc1tmwY2368LLGktnePzEyPAsgADxbksi'); * bchjs.SLP.Address.detectAddressType('mqc1tmwY2368LLGktnePzEyPAsgADxbksi');
* // p2pkh * // p2pkh
*/ */
detectAddressType(address) { detectAddressType (address) {
this._ensureValidAddress(address) this._ensureValidAddress(address)
const cashAddr = bchaddrjs.toCashAddress(address) const cashAddr = bchaddrjs.toCashAddress(address)
return bchAddress.detectAddressType(cashAddr) return bchAddress.detectAddressType(cashAddr)
} }
/* /*
async details(address) { async details(address) {
let tmpBITBOX let tmpBITBOX
@@ -644,7 +650,7 @@ class Address extends BCHJSAddress {
return tmpBITBOX.Address.transactions(address) return tmpBITBOX.Address.transactions(address)
} }
*/ */
_ensureValidAddress(address) { _ensureValidAddress (address) {
try { try {
bchaddrjs.toCashAddress(address) bchaddrjs.toCashAddress(address)
} catch (err) { } catch (err) {
+5 -5
View File
@@ -1,9 +1,9 @@
//const BCHJS = require("../bch-js") // const BCHJS = require("../bch-js")
//const bchjs = new BCHJS() // const bchjs = new BCHJS()
const BCHJSECPair = require("../ecpair") const BCHJSECPair = require('../ecpair')
const bchaddrjs = require("bchaddrjs-slp") const bchaddrjs = require('bchaddrjs-slp')
class ECPair extends BCHJSECPair { class ECPair extends BCHJSECPair {
/* /*
@@ -25,7 +25,7 @@ class ECPair extends BCHJSECPair {
* bchjs.SLP.ECPair.toSLPAddress(ecpair); * bchjs.SLP.ECPair.toSLPAddress(ecpair);
* // slptest:qq835u5srlcqwrtwt6xm4efwan30fxg9hcqag6fk03 * // slptest:qq835u5srlcqwrtwt6xm4efwan30fxg9hcqag6fk03
*/ */
static toSLPAddress(ecpair) { static toSLPAddress (ecpair) {
const slpAddress = bchaddrjs.toSlpAddress(this.toCashAddress(ecpair)) const slpAddress = bchaddrjs.toSlpAddress(this.toCashAddress(ecpair))
return slpAddress return slpAddress
} }
+160 -56
View File
@@ -8,23 +8,30 @@
(Parent) token. (Parent) token.
*/ */
const Address = require("./address") // Public npm libraries
const axios = require('axios')
const BigNumber = require("bignumber.js") // Local libraries.
const slpMdm = require("slp-mdm") const Address = require('./address')
// const BigNumber = require('bignumber.js')
const slpMdm = require('slp-mdm')
// let _this
// const addy = new Address() // const addy = new Address()
let addy let addy
const TransactionBuilder = require("../transaction-builder") const TransactionBuilder = require('../transaction-builder')
class TokenType1 { class Nft1 {
constructor(config) { constructor (config) {
this.restURL = config.restURL this.restURL = config.restURL
addy = new Address(config) addy = new Address(config)
// Instantiate the transaction builder. // Instantiate the transaction builder.
TransactionBuilder.setAddress(addy) TransactionBuilder.setAddress(addy)
// _this = this
} }
/** /**
@@ -59,12 +66,12 @@ class TokenType1 {
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft * // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft
* *
*/ */
newNFTGroupOpReturn(configObj) { newNFTGroupOpReturn (configObj) {
try { try {
// TODO: Add input validation. // TODO: Add input validation.
// Prevent error if user fails to add the document hash. // Prevent error if user fails to add the document hash.
if (!configObj.documentHash) configObj.documentHash = "" if (!configObj.documentHash) configObj.documentHash = ''
// If mint baton is not specified, then replace it with null. // If mint baton is not specified, then replace it with null.
if (!configObj.mintBatonVout) configObj.mintBatonVout = null if (!configObj.mintBatonVout) configObj.mintBatonVout = null
@@ -81,7 +88,7 @@ class TokenType1 {
return script return script
} catch (err) { } catch (err) {
console.log(`Error in generateNFTParentOpReturn()`) console.log('Error in generateNFTParentOpReturn()')
throw err throw err
} }
} }
@@ -130,43 +137,47 @@ class TokenType1 {
* // See additional code here: * // See additional code here:
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft * // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft
*/ */
mintNFTGroupOpReturn(tokenUtxos, mintQty, destroyBaton = false) { mintNFTGroupOpReturn (tokenUtxos, mintQty, destroyBaton = false) {
try { // try {
// Throw error if input is not an array. // Throw error if input is not an array.
if (!Array.isArray(tokenUtxos)) if (!Array.isArray(tokenUtxos)) {
throw new Error(`tokenUtxos must be an array.`) throw new Error('tokenUtxos must be an array.')
// Loop through the tokenUtxos array and find the minting baton.
let mintBatonUtxo
for (let i = 0; i < tokenUtxos.length; i++) {
if (tokenUtxos[i].utxoType === "minting-baton")
mintBatonUtxo = tokenUtxos[i]
}
// Throw an error if the minting baton could not be found.
if (!mintBatonUtxo)
throw new Error(`Minting baton could not be found in tokenUtxos array.`)
const tokenId = mintBatonUtxo.tokenId
if (!tokenId)
throw new Error(`tokenId property not found in mint-baton UTXO.`)
// Signal that the baton should be passed or detroyed.
let batonVout = 2
if (destroyBaton) batonVout = null
const script = slpMdm.NFT1.Group.mint(
tokenId,
batonVout,
new slpMdm.BN(mintQty)
)
return script
} catch (err) {
// console.log(`Error in generateMintOpReturn()`)
throw err
} }
// Loop through the tokenUtxos array and find the minting baton.
let mintBatonUtxo
for (let i = 0; i < tokenUtxos.length; i++) {
if (tokenUtxos[i].utxoType === 'minting-baton') {
mintBatonUtxo = tokenUtxos[i]
}
}
// Throw an error if the minting baton could not be found.
if (!mintBatonUtxo) {
throw new Error('Minting baton could not be found in tokenUtxos array.')
}
const tokenId = mintBatonUtxo.tokenId
if (!tokenId) {
throw new Error('tokenId property not found in mint-baton UTXO.')
}
// Signal that the baton should be passed or detroyed.
let batonVout = 2
if (destroyBaton) batonVout = null
const script = slpMdm.NFT1.Group.mint(
tokenId,
batonVout,
new slpMdm.BN(mintQty)
)
return script
// } catch (err) {
// // console.log(`Error in generateMintOpReturn()`)
// throw err
// }
} }
/** /**
@@ -200,12 +211,12 @@ class TokenType1 {
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft * // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft
* *
*/ */
generateNFTChildGenesisOpReturn(configObj) { generateNFTChildGenesisOpReturn (configObj) {
try { try {
// TODO: Add input validation. // TODO: Add input validation.
// Prevent error if user fails to add the document hash. // Prevent error if user fails to add the document hash.
if (!configObj.documentHash) configObj.documentHash = "" if (!configObj.documentHash) configObj.documentHash = ''
// If mint baton is not specified, then replace it with null. // If mint baton is not specified, then replace it with null.
if (!configObj.mintBatonVout) configObj.mintBatonVout = null if (!configObj.mintBatonVout) configObj.mintBatonVout = null
@@ -217,12 +228,12 @@ class TokenType1 {
configObj.documentHash, configObj.documentHash,
0, 0,
configObj.mintBatonVout, configObj.mintBatonVout,
new slpMdm.BN("1") new slpMdm.BN('1')
) )
return script return script
} catch (err) { } catch (err) {
console.log(`Error in generateNFTChildGenesisOpReturn()`) console.log('Error in generateNFTChildGenesisOpReturn()')
throw err throw err
} }
} }
@@ -270,7 +281,7 @@ class TokenType1 {
* // See additional code here: * // See additional code here:
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft * // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft
*/ */
generateNFTChildSendOpReturn(tokenUtxos, sendQty) { generateNFTChildSendOpReturn (tokenUtxos, sendQty) {
try { try {
// TODO: Add input validation. // TODO: Add input validation.
@@ -278,8 +289,9 @@ class TokenType1 {
// Calculate the total amount of tokens owned by the wallet. // Calculate the total amount of tokens owned by the wallet.
let totalTokens = 0 let totalTokens = 0
for (let i = 0; i < tokenUtxos.length; i++) for (let i = 0; i < tokenUtxos.length; i++) {
totalTokens += tokenUtxos[i].tokenQty totalTokens += tokenUtxos[i].tokenQty
}
const change = totalTokens - sendQty const change = totalTokens - sendQty
@@ -312,7 +324,7 @@ class TokenType1 {
return { script, outputs } return { script, outputs }
} catch (err) { } catch (err) {
console.log(`Error in generateNFTChildSendOpReturn()`) console.log('Error in generateNFTChildSendOpReturn()')
throw err throw err
} }
} }
@@ -360,7 +372,7 @@ class TokenType1 {
* // See additional code here: * // See additional code here:
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft * // https://github.com/Permissionless-Software-Foundation/bch-js-examples/tree/master/applications/slp/nft
*/ */
generateNFTGroupSendOpReturn(tokenUtxos, sendQty) { generateNFTGroupSendOpReturn (tokenUtxos, sendQty) {
try { try {
// TODO: Add input validation. // TODO: Add input validation.
@@ -368,8 +380,9 @@ class TokenType1 {
// Calculate the total amount of tokens owned by the wallet. // Calculate the total amount of tokens owned by the wallet.
let totalTokens = 0 let totalTokens = 0
for (let i = 0; i < tokenUtxos.length; i++) for (let i = 0; i < tokenUtxos.length; i++) {
totalTokens += tokenUtxos[i].tokenQty totalTokens += tokenUtxos[i].tokenQty
}
const change = totalTokens - sendQty const change = totalTokens - sendQty
@@ -402,10 +415,101 @@ class TokenType1 {
return { script, outputs } return { script, outputs }
} catch (err) { } catch (err) {
console.log(`Error in generateNFTGroupSendOpReturn()`) console.log('Error in generateNFTGroupSendOpReturn()')
throw err throw err
} }
} }
/**
* @api SLP.NFT1.listNFTGroupChildren() listNFTGroupChildren()
* @apiName listNFTGroupChildren
* @apiGroup SLP NFT1
* @apiDescription Return list of NFT children tokens in a NFT Group.
* It's assumed provided groupId parameter is for an NFT Group token (type=129)
*
* Returns an Array with GENESIS transaction IDs of the children tokens.
*
* @apiExample Example usage:
*
* const groupId = '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
* const children = await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
*
* children = {
* "nftChildren": [
* "45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9",
* "928ce61fe1006b1325a0ba0dce700bf83986a6f0691ba26e121c9ac035d12a55"
* ]
* }
*/
async listNFTGroupChildren (groupId) {
try {
if (typeof groupId === 'string') {
const response = await axios.get(
`${this.restURL}slp/nftChildren/${groupId}`,
this.axiosOptions
)
return response.data
}
throw new Error('groupId must be a string.')
} catch (error) {
// console.log('Error in listNFTGroupChildren()')
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
/**
* @api SLP.NFT1.parentNFTGroup() parentNFTGroup()
* @apiName parentNFTGroup
* @apiGroup SLP NFT1
* @apiDescription Return parent NFT Group information for a given NFT child token.
* It's assumed provided groupId parameter is for an NFT Child token (type=65)
*
* Returns a JSON with NFT group information.
*
* @apiExample Example usage:
*
* const tokenId = '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
* const group = await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
*
* group = {
* "nftGroup": {
* "decimals": 0,
* "timestamp": "2021-05-03 10:36:01",
* "timestamp_unix": 1620038161,
* "versionType": 129,
* "documentUri": "psfoundation.cash",
* "symbol": "PSF.TEST.GROUP",
* "name": "PSF Test NFT Group",
* "containsBaton": true,
* "id": "68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a",
* "documentHash": null,
* "initialTokenQty": 1000000,
* "blockCreated": 686117,
* "totalMinted": null,
* "totalBurned": null,
* "circulatingSupply": null
* }
* }
*/
async parentNFTGroup (tokenId) {
try {
if (typeof tokenId === 'string') {
const response = await axios.get(
`${this.restURL}slp/nftGroup/${tokenId}`,
this.axiosOptions
)
return response.data
}
throw new Error('tokenId must be a string.')
} catch (error) {
// console.log('Error in parentNFTGroup()')
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
} }
module.exports = TokenType1 module.exports = Nft1
+8 -8
View File
@@ -7,17 +7,17 @@
// imports // imports
// require deps // require deps
//const BCHJS = require("../bch-js") // const BCHJS = require("../bch-js")
const Address = require("./address") const Address = require('./address')
const ECPair = require("./ecpair") const ECPair = require('./ecpair')
// const HDNode = require("./hdnode") // const HDNode = require("./hdnode")
const TokenType1 = require("./tokentype1") const TokenType1 = require('./tokentype1')
const NFT1 = require("./nft1") const NFT1 = require('./nft1')
const Utils = require("./utils") const Utils = require('./utils')
// SLP is a superset of BITBOX // SLP is a superset of BITBOX
class SLP { class SLP {
constructor(config) { constructor (config) {
this.restURL = config.restURL this.restURL = config.restURL
this.apiToken = config.apiToken this.apiToken = config.apiToken
this.authToken = config.authToken this.authToken = config.authToken
@@ -41,7 +41,7 @@ class SLP {
this.Address = new Address(config) this.Address = new Address(config)
this.ECPair = ECPair this.ECPair = ECPair
this.TokenType1 = new TokenType1(config) this.TokenType1 = new TokenType1(config)
this.NFT1 = new NFT1(this.restURL) this.NFT1 = new NFT1(config)
this.Utils = new Utils(config) this.Utils = new Utils(config)
} }
} }
+90 -55
View File
@@ -1,21 +1,25 @@
//const BCHJS = require("../bch-js") /*
//const bchjs = new BCHJS() This library handles the OP_RETURN of SLP TokenType1 transactions.
*/
const Address = require("./address") // const BCHJS = require("../bch-js")
const Script = require("../script") // const bchjs = new BCHJS()
const BigNumber = require("bignumber.js") const Address = require('./address')
const slpMdm = require("slp-mdm") const Script = require('../script')
const axios = require("axios")
const BigNumber = require('bignumber.js')
const slpMdm = require('slp-mdm')
const axios = require('axios')
// const addy = new Address() // const addy = new Address()
let addy let addy
const TransactionBuilder = require("../transaction-builder") const TransactionBuilder = require('../transaction-builder')
let _this // local global let _this // local global
class TokenType1 { class TokenType1 {
constructor(config) { constructor (config) {
this.restURL = config.restURL this.restURL = config.restURL
this.apiToken = config.apiToken this.apiToken = config.apiToken
this.authToken = config.authToken this.authToken = config.authToken
@@ -88,17 +92,21 @@ class TokenType1 {
* // See additional code here: * // See additional code here:
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/applications/slp/send-token/send-token.js * // https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/applications/slp/send-token/send-token.js
*/ */
generateSendOpReturn(tokenUtxos, sendQty) { generateSendOpReturn (tokenUtxos, sendQty) {
try { try {
const tokenId = tokenUtxos[0].tokenId const tokenId = tokenUtxos[0].tokenId
const decimals = tokenUtxos[0].decimals const decimals = tokenUtxos[0].decimals
// Calculate the total amount of tokens owned by the wallet. const sendQtyBig = new BigNumber(sendQty).times(10 ** decimals)
let totalTokens = 0
for (let i = 0; i < tokenUtxos.length; i++)
totalTokens += tokenUtxos[i].tokenQty
const change = totalTokens - sendQty // Calculate the total amount of tokens owned by the wallet.
const totalTokens = tokenUtxos.reduce(
(tot, txo) =>
tot.plus(new BigNumber(txo.tokenQty).times(10 ** decimals)),
new BigNumber(0)
)
const change = totalTokens.minus(sendQtyBig)
// console.log(`change: ${change}`) // console.log(`change: ${change}`)
let script let script
@@ -109,18 +117,22 @@ class TokenType1 {
outputs = 2 outputs = 2
// Convert the send quantity to the format expected by slp-mdm. // Convert the send quantity to the format expected by slp-mdm.
let baseQty = new BigNumber(sendQty).times(10 ** decimals) const baseQty = sendQtyBig.toString()
baseQty = baseQty.absoluteValue() // console.log('baseQty: ', baseQty)
baseQty = Math.floor(baseQty)
baseQty = baseQty.toString()
// console.log(`baseQty: `, baseQty)
// Convert the change quantity to the format expected by slp-mdm. // Convert the change quantity to the format expected by slp-mdm.
let baseChange = new BigNumber(change).times(10 ** decimals) const baseChange = change.toString()
baseChange = baseChange.absoluteValue() // console.log('baseChange: ', baseChange)
baseChange = Math.floor(baseChange)
baseChange = baseChange.toString() // Check for potential burns
// console.log(`baseChange: `, baseChange) const outputQty = new BigNumber(baseChange).plus(new BigNumber(baseQty))
const inputQty = new BigNumber(totalTokens)
const tokenOutputDelta = outputQty.minus(inputQty).toString() !== '0'
if (tokenOutputDelta) {
throw new Error(
'Token transaction inputs do not match outputs, cannot send transaction'
)
}
// Generate the OP_RETURN as a Buffer. // Generate the OP_RETURN as a Buffer.
script = slpMdm.TokenType1.send(tokenId, [ script = slpMdm.TokenType1.send(tokenId, [
@@ -131,13 +143,19 @@ class TokenType1 {
// Corner case, when there is no token change to send back. // Corner case, when there is no token change to send back.
} else { } else {
let baseQty = new BigNumber(sendQty).times(10 ** decimals) const baseQty = sendQtyBig.toString()
baseQty = baseQty.absoluteValue()
baseQty = Math.floor(baseQty)
baseQty = baseQty.toString()
// console.log(`baseQty: `, baseQty) // console.log(`baseQty: `, baseQty)
// console.log(`baseQty: ${baseQty.toString()}`) // Check for potential burns
const noChangeOutputQty = new BigNumber(baseQty)
const noChangeInputQty = new BigNumber(totalTokens)
const tokenSingleOutputError =
noChangeOutputQty.minus(noChangeInputQty).toString() !== '0'
if (tokenSingleOutputError) {
throw new Error(
'Token transaction inputs do not match outputs, cannot send transaction'
)
}
// Generate the OP_RETURN as a Buffer. // Generate the OP_RETURN as a Buffer.
script = slpMdm.TokenType1.send(tokenId, [new slpMdm.BN(baseQty)]) script = slpMdm.TokenType1.send(tokenId, [new slpMdm.BN(baseQty)])
@@ -145,7 +163,7 @@ class TokenType1 {
return { script, outputs } return { script, outputs }
} catch (err) { } catch (err) {
console.log(`Error in generateSendOpReturn()`) console.log('Error in generateSendOpReturn()')
throw err throw err
} }
} }
@@ -196,15 +214,21 @@ class TokenType1 {
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/applications/slp/burn-tokens/burn-tokens.js * // https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/applications/slp/burn-tokens/burn-tokens.js
* *
*/ */
generateBurnOpReturn(tokenUtxos, burnQty) { generateBurnOpReturn (tokenUtxos, burnQty) {
try { try {
const tokenId = tokenUtxos[0].tokenId const tokenId = tokenUtxos[0].tokenId
const decimals = tokenUtxos[0].decimals const decimals = tokenUtxos[0].decimals
// Calculate the total amount of tokens owned by the wallet. // Calculate the total amount of tokens owned by the wallet.
let totalTokens = 0 let totalTokens = 0
for (let i = 0; i < tokenUtxos.length; i++) for (let i = 0; i < tokenUtxos.length; i++) {
totalTokens += tokenUtxos[i].tokenQty totalTokens += parseFloat(tokenUtxos[i].tokenQty)
}
// Make sure burn quantity isn't bigger than the total amount in tokens
if (burnQty > totalTokens) {
burnQty = totalTokens
}
const remainder = totalTokens - burnQty const remainder = totalTokens - burnQty
@@ -220,7 +244,7 @@ class TokenType1 {
return script return script
} catch (err) { } catch (err) {
console.log(`Error in generateBurnOpReturn()`) console.log('Error in generateBurnOpReturn()')
throw err throw err
} }
} }
@@ -259,19 +283,25 @@ class TokenType1 {
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/applications/slp/create-token/create-token.js * // https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/applications/slp/create-token/create-token.js
* *
*/ */
generateGenesisOpReturn(configObj) { generateGenesisOpReturn (configObj) {
try { try {
// TODO: Add input validation. // TODO: Add input validation.
let baseQty = new BigNumber(configObj.initialQty).times( let baseQty
10 ** configObj.decimals if (configObj.decimals !== 0) {
) baseQty = new BigNumber(configObj.initialQty).times(
10 ** configObj.decimals
)
} else {
baseQty = new BigNumber(configObj.initialQty)
}
baseQty = baseQty.absoluteValue() baseQty = baseQty.absoluteValue()
baseQty = Math.floor(baseQty) baseQty = Math.floor(baseQty)
baseQty = baseQty.toString() baseQty = baseQty.toString()
// Prevent error if user fails to add the document hash. // Prevent error if user fails to add the document hash.
if (!configObj.documentHash) configObj.documentHash = "" if (!configObj.documentHash) configObj.documentHash = ''
// If mint baton is not specified, then replace it with null. // If mint baton is not specified, then replace it with null.
if (!configObj.mintBatonVout) configObj.mintBatonVout = null if (!configObj.mintBatonVout) configObj.mintBatonVout = null
@@ -288,7 +318,7 @@ class TokenType1 {
return script return script
} catch (err) { } catch (err) {
console.log(`Error in generateGenesisOpReturn()`) console.log('Error in generateGenesisOpReturn()')
throw err throw err
} }
} }
@@ -339,30 +369,35 @@ class TokenType1 {
* // See additional code here: * // See additional code here:
* // https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/applications/slp/mint-token/mint-token.js * // https://github.com/Permissionless-Software-Foundation/bch-js-examples/blob/master/applications/slp/mint-token/mint-token.js
*/ */
generateMintOpReturn(tokenUtxos, mintQty, destroyBaton = false) { generateMintOpReturn (tokenUtxos, mintQty, destroyBaton = false) {
try { try {
// Throw error if input is not an array. // Throw error if input is not an array.
if (!Array.isArray(tokenUtxos)) if (!Array.isArray(tokenUtxos)) {
throw new Error(`tokenUtxos must be an array.`) throw new Error('tokenUtxos must be an array.')
}
// Loop through the tokenUtxos array and find the minting baton. // Loop through the tokenUtxos array and find the minting baton.
let mintBatonUtxo let mintBatonUtxo
for (let i = 0; i < tokenUtxos.length; i++) { for (let i = 0; i < tokenUtxos.length; i++) {
if (tokenUtxos[i].utxoType === "minting-baton") if (tokenUtxos[i].utxoType === 'minting-baton') {
mintBatonUtxo = tokenUtxos[i] mintBatonUtxo = tokenUtxos[i]
}
} }
// Throw an error if the minting baton could not be found. // Throw an error if the minting baton could not be found.
if (!mintBatonUtxo) if (!mintBatonUtxo) {
throw new Error(`Minting baton could not be found in tokenUtxos array.`) throw new Error('Minting baton could not be found in tokenUtxos array.')
}
const tokenId = mintBatonUtxo.tokenId const tokenId = mintBatonUtxo.tokenId
const decimals = mintBatonUtxo.decimals const decimals = mintBatonUtxo.decimals
if (!tokenId) if (!tokenId) {
throw new Error(`tokenId property not found in mint-baton UTXO.`) throw new Error('tokenId property not found in mint-baton UTXO.')
if (!decimals) }
throw new Error(`decimals property not found in mint-baton UTXO.`) if (!decimals) {
throw new Error('decimals property not found in mint-baton UTXO.')
}
let baseQty = new BigNumber(mintQty).times(10 ** decimals) let baseQty = new BigNumber(mintQty).times(10 ** decimals)
baseQty = baseQty.absoluteValue() baseQty = baseQty.absoluteValue()
@@ -381,7 +416,7 @@ class TokenType1 {
return script return script
} catch (err) { } catch (err) {
// console.log(`Error in generateMintOpReturn()`) console.log('Error in generateMintOpReturn()')
throw err throw err
} }
} }
@@ -413,7 +448,7 @@ class TokenType1 {
* "outputs": 2 * "outputs": 2
* } * }
*/ */
async getHexOpReturn(tokenUtxos, sendQty) { async getHexOpReturn (tokenUtxos, sendQty) {
try { try {
// TODO: Add input filtering. // TODO: Add input filtering.
@@ -425,7 +460,7 @@ class TokenType1 {
const result = await _this.axios.post( const result = await _this.axios.post(
`${this.restURL}slp/generatesendopreturn`, `${this.restURL}slp/generatesendopreturn`,
data, data,
_this.axiosOptions this.axiosOptions
) )
const slpSendObj = result.data const slpSendObj = result.data
@@ -437,7 +472,7 @@ class TokenType1 {
return slpSendObj return slpSendObj
} catch (err) { } catch (err) {
// console.log(err) console.log('Error in getHexOpReturn()')
throw err throw err
} }
} }
+643 -297
View File
File diff suppressed because it is too large Load Diff
-29
View File
@@ -1,29 +0,0 @@
const io = require("socket.io-client")
class Socket {
constructor(config = {}) {
if (typeof config === "string") {
// TODO remove this check in v2.0
this.socket = io(`${config}`)
} else {
if (config.restURL) {
this.socket = io(`${config.restURL}`)
} else {
const restURL = "https://rest.bitcoin.com"
this.socket = io(`${restURL}`)
}
if (config.callback) config.callback()
}
}
listen(endpoint, cb) {
this.socket.emit(endpoint)
if (endpoint === "blocks") this.socket.on("blocks", msg => cb(msg))
else if (endpoint === "transactions")
this.socket.on("transactions", msg => cb(msg))
}
}
module.exports = Socket
+19 -17
View File
@@ -1,18 +1,16 @@
const Bitcoin = require("bitcoincashjs-lib") const Bitcoin = require('@psf/bitcoincashjs-lib')
const coininfo = require("coininfo") const coininfo = require('@psf/coininfo')
const bip66 = require("bip66") const bip66 = require('bip66')
const bip68 = require("bc-bip68") const bip68 = require('bc-bip68')
class TransactionBuilder { class TransactionBuilder {
static setAddress(address) { static setAddress (address) {
TransactionBuilder._address = address TransactionBuilder._address = address
} }
constructor(network = "mainnet") { constructor (network = 'mainnet') {
let bitcoincash let bitcoincash
if (network === "bitcoincash" || network === "mainnet") if (network === 'bitcoincash' || network === 'mainnet') { bitcoincash = coininfo.bitcoincash.main } else bitcoincash = coininfo.bitcoincash.test
bitcoincash = coininfo.bitcoincash.main
else bitcoincash = coininfo.bitcoincash.test
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS() const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
this.transaction = new Bitcoin.TransactionBuilder(bitcoincashBitcoinJSLib) this.transaction = new Bitcoin.TransactionBuilder(bitcoincashBitcoinJSLib)
@@ -33,7 +31,7 @@ class TransactionBuilder {
this.bip66 = bip66 this.bip66 = bip66
this.bip68 = bip68 this.bip68 = bip68
this.p2shInput = false this.p2shInput = false
this.tx // this.tx
} }
/** /**
@@ -48,23 +46,24 @@ class TransactionBuilder {
* // add input with txid and index of vout * // add input with txid and index of vout
* transactionBuilder.addInput(txid, 0); * transactionBuilder.addInput(txid, 0);
*/ */
addInput(txHash, vout, sequence = this.DEFAULT_SEQUENCE, prevOutScript) { addInput (txHash, vout, sequence = this.DEFAULT_SEQUENCE, prevOutScript) {
this.transaction.addInput(txHash, vout, sequence, prevOutScript) this.transaction.addInput(txHash, vout, sequence, prevOutScript)
} }
addInputScript(vout, script) { addInputScript (vout, script) {
this.tx = this.transaction.buildIncomplete() this.tx = this.transaction.buildIncomplete()
this.tx.setInputScript(vout, script) this.tx.setInputScript(vout, script)
this.p2shInput = true this.p2shInput = true
} }
addInputScripts(scripts) { addInputScripts (scripts) {
this.tx = this.transaction.buildIncomplete() this.tx = this.transaction.buildIncomplete()
scripts.forEach(script => { scripts.forEach(script => {
this.tx.setInputScript(script.vout, script.script) this.tx.setInputScript(script.vout, script.script)
}) })
this.p2shInput = true this.p2shInput = true
} }
/** /**
* @api Transaction-Builder.addOutput() addOutput() * @api Transaction-Builder.addOutput() addOutput()
* @apiName AddOutput * @apiName AddOutput
@@ -79,7 +78,7 @@ class TransactionBuilder {
* // add output w/ address and amount to send * // add output w/ address and amount to send
* transactionBuilder.addOutput('bitcoincash:qpuax2tarq33f86wccwlx8ge7tad2wgvqgjqlwshpw', sendAmount); * transactionBuilder.addOutput('bitcoincash:qpuax2tarq33f86wccwlx8ge7tad2wgvqgjqlwshpw', sendAmount);
*/ */
addOutput(scriptPubKey, amount) { addOutput (scriptPubKey, amount) {
try { try {
this.transaction.addOutput( this.transaction.addOutput(
TransactionBuilder._address.toLegacyAddress(scriptPubKey), TransactionBuilder._address.toLegacyAddress(scriptPubKey),
@@ -89,6 +88,7 @@ class TransactionBuilder {
this.transaction.addOutput(scriptPubKey, amount) this.transaction.addOutput(scriptPubKey, amount)
} }
} }
/** /**
* @api Transaction-Builder.setLockTime() setLockTime() * @api Transaction-Builder.setLockTime() setLockTime()
* @apiName SetLockTime * @apiName SetLockTime
@@ -104,9 +104,10 @@ class TransactionBuilder {
* transactionBuilder.addOutput('bitcoincash:qpuax2tarq33f86wccwlx8ge7tad2wgvqgjqlwshpw', sendAmount); * transactionBuilder.addOutput('bitcoincash:qpuax2tarq33f86wccwlx8ge7tad2wgvqgjqlwshpw', sendAmount);
* transactionBuilder.setLockTime(50000) * transactionBuilder.setLockTime(50000)
*/ */
setLockTime(locktime) { setLockTime (locktime) {
this.transaction.setLockTime(locktime) this.transaction.setLockTime(locktime)
} }
/** /**
* @api Transaction-Builder.sign() sign() * @api Transaction-Builder.sign() sign()
* @apiName Sign. * @apiName Sign.
@@ -124,7 +125,7 @@ class TransactionBuilder {
* // sign w/ keyPair * // sign w/ keyPair
* transactionBuilder.sign(0, keyPair, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, originalAmount, transactionBuilder.signatureAlgorithms.SCHNORR); * transactionBuilder.sign(0, keyPair, redeemScript, transactionBuilder.hashTypes.SIGHASH_ALL, originalAmount, transactionBuilder.signatureAlgorithms.SCHNORR);
*/ */
sign( sign (
vin, vin,
keyPair, keyPair,
redeemScript, redeemScript,
@@ -144,6 +145,7 @@ class TransactionBuilder {
signatureAlgorithm signatureAlgorithm
) )
} }
/** /**
* @api Transaction-Builder.build() build() * @api Transaction-Builder.build() build()
* @apiName Build. * @apiName Build.
@@ -154,7 +156,7 @@ class TransactionBuilder {
* // build tx * // build tx
* let tx = bchjs.transactionBuilder.build(); * let tx = bchjs.transactionBuilder.build();
*/ */
build() { build () {
if (this.p2shInput === true) return this.tx if (this.p2shInput === true) return this.tx
return this.transaction.build() return this.transaction.build()
+528
View File
@@ -0,0 +1,528 @@
/*
High-level functions for working with Transactions
*/
const BigNumber = require('bignumber.js')
const RawTransaction = require('./raw-transactions')
const SlpUtils = require('./slp/utils')
const Blockchain = require('./blockchain')
class Transaction {
constructor (config) {
// Encapsulate dependencies
this.slpUtils = new SlpUtils(config)
this.rawTransaction = new RawTransaction(config)
this.blockchain = new Blockchain(config)
}
/**
* @api Transaction.get() get()
* @apiName get
* @apiGroup Transaction
* @apiDescription
* Returns an object of transaction data, including addresses for input UTXOs.
* If it is a SLP token transaction, the token information for inputs and
* outputs will also be included.
*
* This is an API heavy call. This function will only work with a single txid.
* It does not yet support an array of TXIDs.
*
* @apiExample Example usage:
* (async () => {
* try {
* let txData = await bchjs.Transaction.get("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
* console.log(txData);
* } catch(error) {
* console.error(error)
* }
* })()
*/
// CT 10/31/21: TODO: this function should be refactored to use get2(), but
// add waterfall validation of the TX and its inputs.
async get (txid) {
try {
if (typeof txid !== 'string') {
throw new Error(
'Input to Transaction.get() must be a string containing a TXID.'
)
}
const txDetails = await this.rawTransaction.getTxData(txid)
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
// Setup default SLP properties.
txDetails.isValidSLPTx = false
// First get the token information for the output. If that fails, then
// this is not an SLP transaction, and this method can return false.
let outTokenData
try {
outTokenData = await this.slpUtils.decodeOpReturn(txid)
// console.log(`outTokenData: ${JSON.stringify(outTokenData, null, 2)}`)
// Get Genesis data for this token.
const genesisData = await this.slpUtils.decodeOpReturn(
outTokenData.tokenId
// decodeOpReturnCache
// usrObj // pass user data when making an internal call.
)
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
// Add token information to the tx details object.
txDetails.tokenTxType = outTokenData.txType
txDetails.tokenId = outTokenData.tokenId
txDetails.tokenTicker = genesisData.ticker
txDetails.tokenName = genesisData.name
txDetails.tokenDecimals = genesisData.decimals
txDetails.tokenUri = genesisData.documentUri
txDetails.tokenDocHash = genesisData.documentHash
// Add the token quantity to each output.
for (let i = 0; i < outTokenData.amounts.length; i++) {
const rawQty = outTokenData.amounts[i]
// const realQty = Number(rawQty) / Math.pow(10, txDetails.tokenDecimals)
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(rawQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
txDetails.vout[i + 1].tokenQtyStr = realQty
txDetails.vout[i + 1].tokenQty = parseFloat(realQty)
}
// Add tokenQty = null to any outputs that don't have a value.
for (let i = 0; i < txDetails.vout.length; i++) {
const thisVout = txDetails.vout[i]
if (!thisVout.tokenQty && thisVout.tokenQty !== 0) {
thisVout.tokenQty = null
}
}
// Loop through each input and retrieve the token data.
for (let i = 0; i < txDetails.vin.length; i++) {
const thisVin = txDetails.vin[i]
// console.log(`thisVin: ${JSON.stringify(thisVin, null, 2)}`)
try {
// If decodeOpReturn() throws an error, then this input is not
// from an SLP transaction and can be ignored.
const inTokenData = await this.slpUtils.decodeOpReturn(thisVin.txid)
// console.log(
// `vin[${i}] tokenData: ${JSON.stringify(inTokenData, null, 2)}`
// )
let tokenQty = 0
// Validate the input. Mark qty as null if not valid.
const vinIsValid = await this.slpUtils.waterfallValidateTxid(
thisVin.txid
)
if (!vinIsValid) {
// If the input is not a valid, then set qty as null.
tokenQty = null
} else if (inTokenData.txType === 'SEND') {
// Get the appropriate vout token amount. This may throw an error,
// which means this Vin is not actually a token UTXO, it was just
// associated with a previous token TX.
tokenQty = inTokenData.amounts[thisVin.vout - 1]
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
//
} else if (inTokenData.txType === 'GENESIS') {
// Only vout[1] of a Genesis transaction represents the tokens.
// Any other outputs in that transaction are normal BCH UTXOs.
if (thisVin.vout === 1) {
tokenQty = inTokenData.qty
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
}
} else if (inTokenData.txType === 'MINT') {
// vout=1 (second output) recieves the newly minted tokens.
if (thisVin.vout === 1) {
tokenQty = inTokenData.qty
} else {
tokenQty = null
}
//
} else {
console.log(
'Unexpected code path in Transaction.get(). What is the txType?'
)
console.log(inTokenData)
throw new Error('Unexpected code path')
}
if (tokenQty) {
// const realQty =
// Number(tokenQty) / Math.pow(10, txDetails.tokenDecimals)
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(tokenQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
thisVin.tokenQtyStr = realQty
thisVin.tokenQty = parseFloat(realQty)
// txDetails.vin[i].tokenQty = tokenQty
// Add token ID to input
thisVin.tokenId = inTokenData.tokenId
} else {
thisVin.tokenQty = null
}
} catch (err) {
// If decodeOpReturn() throws an error, then this input is not
// from an SLP transaction and can be ignored.
// thisVin.tokenQty = null
thisVin.tokenQty = null
continue
}
}
// Finally, validate the SLP TX.
// this.slpUtils.waterfallValidateTxid(txid, usrObj)
txDetails.isValidSLPTx = await this.slpUtils.waterfallValidateTxid(txid)
// TODO: Convert the block hash to a block height. Add block height
// value to the transaction.
} catch (err) {
// console.log('Error: ', err)
// This case handles rate limit errors.
if (err.response && err.response.data && err.response.data.error) {
throw new Error(err.response.data.error)
}
// If decoding the op_return fails, then it's not an SLP transaction,
// and the non-hyrated TX details can be returned.
return txDetails
}
return txDetails
} catch (err) {
// console.error('Error in transactions.js/get(): ', err)
// This case handles rate limit errors.
if (err.response && err.response.data && err.response.data.error) {
throw new Error(err.response.data.error)
}
if (err.error) throw new Error(err.error)
throw err
}
}
/**
* @api Transaction.get3() get3()
* @apiName get3
* @apiGroup Transaction
* @apiDescription
* Returns an object of transaction data, including addresses for input UTXOs.
* If it is a SLP token transaction, the token information for inputs and
* outputs will also be included.
*
* This is an API heavy call. This function will only work with a single txid.
* It does not yet support an array of TXIDs.
*
* This is the same as get(), except it omits DAG validation of the TXID.
*
* @apiExample Example usage:
* (async () => {
* try {
* let txData = await bchjs.Transaction.get2("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
* console.log(txData);
* } catch(error) {
* console.error(error)
* }
* })()
*/
async get3 (txid) {
try {
if (typeof txid !== 'string') {
throw new Error(
'Input to Transaction.get() must be a string containing a TXID.'
)
}
// Get TX data
const txDetails = await this.rawTransaction.getTxData(txid)
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
// Get the block height the transaction was mined in.
const blockHeader = await this.blockchain.getBlockHeader(
txDetails.blockhash
)
txDetails.blockheight = blockHeader.height
// console.log(`blockHeader: ${JSON.stringify(blockHeader, null, 2)}`)
// Set default as not an SLP tx
txDetails.isSlpTx = false
// Get Token Data
const txTokenData = await this.getTokenInfo(txid)
// console.log(`txTokenData: ${JSON.stringify(txTokenData, null, 2)}`)
// If not a token, return the tx data. Processing is complete.
if (!txTokenData) return txDetails
// Mark TX as an SLP tx. This does not mean it's valid, it just means
// the OP_RETURN passes a basic check.
txDetails.isSlpTx = true
// Get Genesis data
const genesisData = await this.getTokenInfo(txTokenData.tokenId)
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
// Add token information to the tx details object.
txDetails.tokenTxType = txTokenData.txType
txDetails.tokenId = txTokenData.tokenId
txDetails.tokenTicker = genesisData.ticker
txDetails.tokenName = genesisData.name
txDetails.tokenDecimals = genesisData.decimals
txDetails.tokenUri = genesisData.documentUri
txDetails.tokenDocHash = genesisData.documentHash
// console.log(`txDetails before processing input and outputs: ${JSON.stringify(txDetails, null, 2)}`)
// Process TX Outputs
// Add the token quantity to each output.
// 'i' starts at 1, because vout[0] is the OP_RETURN
for (let i = 0; i < txDetails.vout.length; i++) {
const thisVout = txDetails.vout[i]
if (txTokenData.txType === 'SEND') {
// console.log(
// `output txTokenData: ${JSON.stringify(txTokenData, null, 2)}`
// )
// First output is OP_RETURN, so tokenQty is null.
if (i === 0) {
thisVout.tokenQty = null
thisVout.tokenQtyStr = null
continue
}
// Non SLP outputs.
if (i > txTokenData.amounts.length) {
thisVout.tokenQty = null
thisVout.tokenQtyStr = null
continue
}
const rawQty = txTokenData.amounts[i - 1]
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(rawQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
txDetails.vout[i].tokenQtyStr = realQty
txDetails.vout[i].tokenQty = parseFloat(realQty)
// console.log(
// `thisVout ${i}: ${JSON.stringify(txDetails.vout[i], null, 2)}`
// )
} else if (
txTokenData.txType === 'GENESIS' ||
txTokenData.txType === 'MINT'
) {
// console.log(
// `output txTokenData: ${JSON.stringify(txTokenData, null, 2)}`
// )
let tokenQty = 0 // Default value
// Only vout[1] of a Genesis or Mint transaction represents the tokens.
// Any other outputs in that transaction are normal BCH UTXOs.
if (i === 1) {
tokenQty = txTokenData.qty
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(tokenQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
thisVout.tokenQtyStr = realQty
thisVout.tokenQty = parseFloat(realQty)
// console.log(`thisVout[${i}]: ${JSON.stringify(thisVout, null, 2)}`)
} else if (i === txTokenData.mintBatonVout) {
// Optional Mint baton
thisVout.tokenQtyStr = '0'
thisVout.tokenQty = 0
thisVout.isMintBaton = true
} else {
thisVout.tokenQtyStr = '0'
thisVout.tokenQty = 0
}
} else {
throw new Error('Unknown SLP TX type for TX')
}
}
// Process TX inputs
for (let i = 0; i < txDetails.vin.length; i++) {
const thisVin = txDetails.vin[i]
// console.log(`thisVin[${i}]: ${JSON.stringify(thisVin, null, 2)}`)
const vinTokenData = await this.getTokenInfo(thisVin.txid)
// console.log(
// `vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
// )
// Corner case: Ensure the token ID is the same.
const vinTokenIdIsTheSame = vinTokenData.tokenId === txDetails.tokenId
// If the input is not a token input, or if the tokenID is not the same,
// then mark the token output as null.
if (!vinTokenData || !vinTokenIdIsTheSame) {
thisVin.tokenQty = 0
thisVin.tokenQtyStr = '0'
thisVin.tokenId = null
continue
}
if (vinTokenData.txType === 'SEND') {
// console.log(
// `SEND vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
// )
const tokenQty = vinTokenData.amounts[thisVin.vout - 1]
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(tokenQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
thisVin.tokenQtyStr = realQty
thisVin.tokenQty = parseFloat(realQty)
thisVin.tokenId = vinTokenData.tokenId
} else if (vinTokenData.txType === 'MINT') {
// console.log(
// `MINT vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
// )
let tokenQty = 0 // Default value
// Only vout[1] of a Genesis transaction represents the tokens.
// Any other outputs in that transaction are normal BCH UTXOs.
if (thisVin.vout === 1) {
tokenQty = vinTokenData.qty
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(tokenQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
thisVin.tokenQtyStr = realQty
thisVin.tokenQty = parseFloat(realQty)
thisVin.tokenId = vinTokenData.tokenId
} else if (thisVin.vout === vinTokenData.mintBatonVout) {
// Optional Mint baton
thisVin.tokenQtyStr = '0'
thisVin.tokenQty = 0
thisVin.tokenId = vinTokenData.tokenId
thisVin.isMintBaton = true
} else {
thisVin.tokenQtyStr = '0'
thisVin.tokenQty = 0
thisVin.tokenId = null
}
} else if (vinTokenData.txType === 'GENESIS') {
// console.log(
// `GENESIS vinTokenData ${i}: ${JSON.stringify(
// vinTokenData,
// null,
// 2
// )}`
// )
let tokenQty = 0 // Default value
// Only vout[1] of a Genesis transaction represents the tokens.
// Any other outputs in that transaction are normal BCH UTXOs.
if (thisVin.vout === 1) {
tokenQty = vinTokenData.qty
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(tokenQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
thisVin.tokenQtyStr = realQty
thisVin.tokenQty = parseFloat(realQty)
thisVin.tokenId = vinTokenData.tokenId
} else if (thisVin.vout === vinTokenData.mintBatonVout) {
// Optional Mint baton
thisVin.tokenQtyStr = '0'
thisVin.tokenQty = 0
thisVin.tokenId = vinTokenData.tokenId
thisVin.isMintBaton = true
} else {
thisVin.tokenQtyStr = '0'
thisVin.tokenQty = 0
thisVin.tokenId = null
}
} else {
console.log(
`Unknown vinTokenData: ${JSON.stringify(vinTokenData, null, 2)}`
)
throw new Error('Unknown token type in input')
}
}
return txDetails
} catch (err) {
console.error('Error in get3()')
// This case handles rate limit errors.
if (err.response && err.response.data && err.response.data.error) {
throw new Error(err.response.data.error)
}
if (err.error) throw new Error(err.error)
throw err
}
}
// A wrapper for decodeOpReturn(). Returns false if txid is not an SLP tx.
// Returns the token data if the txid is an SLP tx.
async getTokenInfo (txid) {
try {
const tokenData = await this.slpUtils.decodeOpReturn(txid)
return tokenData
} catch (err) {
return false
}
}
}
module.exports = Transaction
+157 -10
View File
@@ -1,9 +1,9 @@
const axios = require("axios") const axios = require('axios')
let _this // let _this
class Util { class Util {
constructor(config) { constructor (config) {
this.restURL = config.restURL this.restURL = config.restURL
this.apiToken = config.apiToken this.apiToken = config.apiToken
this.authToken = config.authToken this.authToken = config.authToken
@@ -24,11 +24,158 @@ class Util {
} }
} }
_this = this // _this = this
} }
/** /**
* @api util.validateAddress() validateAddress() * @api Util.floor8() floor8()
* @apiName floor8
* @apiGroup Util
* @apiDescription Round a number down to 8 decimal places.
*
*
* @apiExample Example usage:
* (async () => {
* try {
* const num = 1.234567891111
* const result = bchjs.Util.floor8(num)
* console.log(result)
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* 1.23456789
*/
// floor8 - round to 8 decimal places
// Takes a number and returns it, rounded to the nearest 8 decimal place.
floor8 (num) {
const thisNum = Number(num)
if (isNaN(thisNum)) throw new Error('input must be a number')
let tempNum = thisNum * 100000000
tempNum = Math.floor(tempNum)
tempNum = tempNum / 100000000
return tempNum
}
/**
* @api Util.floor2() floor2()
* @apiName floor2
* @apiGroup Util
* @apiDescription Round a number down to 2 decimal places.
*
*
* @apiExample Example usage:
* (async () => {
* try {
* const num = 1.234567891111
* const result = bchjs.Util.floor2(num)
* console.log(result)
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* 1.23
*/
// floor2 - round down to 2 decimal places
// Takes a number and returns it, rounded to the nearest 2 decimal place.
floor2 (num) {
const thisNum = Number(num)
if (isNaN(thisNum)) throw new Error('input must be a number')
let tempNum = thisNum * 100
tempNum = Math.floor(tempNum)
tempNum = tempNum / 100
return tempNum
}
/**
* @api Util.chunk20() chunk20()
* @apiName chunk20
* @apiGroup Util
* @apiDescription chunk up an array into multiple arrays of 20 elements each.
* Input: arrayToSlice - a one-dimensional array of elements.
* Returns a two-dimensional array. An array of 20-element arrays.
*
* @apiExample Example usage:
* (async () => {
* try {
* const bigArray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]
*
* const chunked = bchjs.Util.chunk20(bigArray)
* console.log(chunked)
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* [
* [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19],
* [20,21,22,23,24,25,26]
* ]
*/
// chunk20 - chunk up an array into multiple arrays of 20 elements each.
// Input: arrayToSlice - a one-dimensional array of elements.
// Returns a two-dimensional array. An array of 20-element arrays.
chunk20 (arrayToSlice) {
try {
// Validate inputs
if (!Array.isArray(arrayToSlice)) {
throw new Error('input must be an array')
}
let offset = 0
const result = []
// Loop over the array and slice off chunks of 20 elements.
while (offset < arrayToSlice.length) {
const chunk = arrayToSlice.slice(offset, offset + 20)
result.push(chunk)
offset = offset + 20
}
return result
} catch (err) {
console.error('Error in chunk20()')
throw err
}
}
/**
* @api Util.sleep() sleep()
* @apiName sleep
* @apiGroup Util
* @apiDescription Promise-based delay.
* Expects an integer as input, which represents milliseconds. This function
* will return a Promise that resolves that many milliseconds later.
*
*
* @apiExample Example usage:
* (async () => {
* try {
* const tenSeconds = 10000
* await bchjs.Util.sleep(tenSeconds)
* } catch(error) {
* console.error(error)
* }
* })()
*
*/
sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
/**
* @api Util.validateAddress() validateAddress()
* @apiName Validate Address. * @apiName Validate Address.
* @apiGroup Util * @apiGroup Util
* @apiDescription Return information about the given bitcoin address. * @apiDescription Return information about the given bitcoin address.
@@ -72,20 +219,20 @@ class Util {
* // iscompressed: true, * // iscompressed: true,
* // account: 'Test' }] * // account: 'Test' }]
*/ */
async validateAddress(address) { async validateAddress (address) {
try { try {
// Single block // Single block
if (typeof address === "string") { if (typeof address === 'string') {
const response = await axios.get( const response = await axios.get(
`${this.restURL}util/validateAddress/${address}`, `${this.restURL}util/validateAddress/${address}`,
_this.axiosOptions this.axiosOptions
) )
return response.data return response.data
// Array of blocks. // Array of blocks.
} else if (Array.isArray(address)) { } else if (Array.isArray(address)) {
const options = { const options = {
method: "POST", method: 'POST',
url: `${this.restURL}util/validateAddress`, url: `${this.restURL}util/validateAddress`,
data: { data: {
addresses: address addresses: address
@@ -99,7 +246,7 @@ class Util {
return response.data return response.data
} }
throw new Error(`Input must be a string or array of strings.`) throw new Error('Input must be a string or array of strings.')
} catch (error) { } catch (error) {
if (error.response && error.response.data) throw error.response.data if (error.response && error.response.data) throw error.response.data
else throw error else throw error
+350
View File
@@ -0,0 +1,350 @@
/*
High-level functions for working with UTXOs
TODO:
- Make a getWL() clone of get(), but uses hydrateUtxosWL()
*/
// Local libraries
const Electrumx = require('./electrumx')
const Slp = require('./slp/slp')
class UTXO {
constructor (config) {
// Encapsulate dependencies for easier mocking.
this.electrumx = new Electrumx(config)
this.slp = new Slp(config)
}
/**
* @api Utxo.get() get()
* @apiName get
* @apiGroup UTXO
* @apiDescription Get UTXOs for an address
*
* Given an address, this function will return an object with thre following
* properties:
* - address: "" - the address these UTXOs are associated with
* - bchUtxos: [] - UTXOs confirmed to be spendable as normal BCH
* - nullUtxo: [] - UTXOs that did not pass SLP validation. Should be ignored and
* not spent, to be safe.
* - slpUtxos: {} - UTXOs confirmed to be colored as valid SLP tokens
* - type1: {}
* - tokens: [] - SLP token Type 1 tokens.
* - mintBatons: [] - SLP token Type 1 mint batons.
* - nft: {}
* - tokens: [] - NFT tokens
* - groupTokens: [] - NFT Group tokens, used to create NFT tokens.
* - groupMintBatons: [] - Minting baton to create more NFT Group tokens.
*
* Note: You can pass in an optional second Boolean argument. The default
* `false` will use the normal waterfall validation method. Set to `true`,
* SLP UTXOs will be validated with the whitelist filtered SLPDB. This will
* result is many more UTXOs in the `nullUtxos` array.
*
* @apiExample Example usage:
* (async () => {
* try {
* let utxos = await bchjs.Utxo.get('simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj');
* console.log(utxos);
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* [
* {
* "address": "bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv",
* "bchUtxos": [
* {
* "height": 674513,
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "tx_pos": 3,
* "value": 38134,
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "vout": 3,
* "isValid": false
* }
* ],
* "nullUtxos": [],
* "slpUtxos": {
* "type1": {
* "mintBatons": [
* {
* "height": 674512,
* "tx_hash": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "tx_pos": 2,
* "value": 546,
* "txid": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "vout": 2,
* "utxoType": "minting-baton",
* "tokenId": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "tokenTicker": "SLPTEST",
* "tokenName": "SLP Test Token",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 8,
* "tokenType": 1,
* "isValid": true
* }
* ],
* "tokens": [
* {
* "height": 674512,
* "tx_hash": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "tx_pos": 1,
* "value": 546,
* "txid": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "vout": 1,
* "utxoType": "token",
* "tokenQty": "100",
* "tokenId": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "tokenTicker": "SLPTEST",
* "tokenName": "SLP Test Token",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 8,
* "tokenType": 1,
* "isValid": true
* }
* ]
* },
* "nft": {
* "groupMintBatons": [
* {
* "height": 674513,
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "tx_pos": 2,
* "value": 546,
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "vout": 2,
* "utxoType": "minting-baton",
* "transactionType": "mint",
* "tokenId": "a9a2458a0f9f0761d5b8725c256f2e7fa35b9de4dec6f47b46e9f20d92d0e395",
* "tokenType": 129,
* "tokenTicker": "NFTGT",
* "tokenName": "NFT Test Group Token",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 0,
* "mintBatonVout": 2,
* "isValid": true
* }
* ],
* "groupTokens": [
* {
* "height": 674513,
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "tx_pos": 1,
* "value": 546,
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "vout": 1,
* "utxoType": "token",
* "tokenQty": "10",
* "transactionType": "mint",
* "tokenId": "a9a2458a0f9f0761d5b8725c256f2e7fa35b9de4dec6f47b46e9f20d92d0e395",
* "tokenType": 129,
* "tokenTicker": "NFTGT",
* "tokenName": "NFT Test Group Token",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 0,
* "mintBatonVout": 2,
* "isValid": true
* }
* ],
* "tokens": [
* {
* "height": 674512,
* "tx_hash": "eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b",
* "tx_pos": 1,
* "value": 546,
* "txid": "eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b",
* "vout": 1,
* "utxoType": "token",
* "tokenQty": "1",
* "tokenId": "eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b",
* "tokenTicker": "NFT004",
* "tokenName": "NFT Child",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 0,
* "tokenType": 65,
* "isValid": true
* }
* ]
* }
* }
* }
* ]
*
*
*/
async get (address, useWhitelist = false) {
try {
// Convert address to an array if it is a string.
if (typeof address === 'string') address = [address]
// Throw an error if there are more than 20 addresses passed in at a time.
if (address.length > 20) throw new Error('Too many elements, 20 max.')
// Covert each address to a BCH address.
const addr = address.map(elem => this.slp.Address.toCashAddress(elem))
// Get the UTXOs associated with the address.
const utxoData = await this.electrumx.utxo(addr)
// console.log(`utxoData: ${JSON.stringify(utxoData, null, 2)}`)
// Hydate the utxos with token information.
let hydratedUtxos
if (useWhitelist) {
hydratedUtxos = await this.slp.Utils.hydrateUtxosWL(utxoData.utxos)
} else {
hydratedUtxos = await this.slp.Utils.hydrateUtxos(utxoData.utxos)
}
// console.log(`hydratedUtxos: ${JSON.stringify(hydratedUtxos, null, 2)}`)
const retAry = [] // Return array
// Loop through each address.
for (let i = 0; i < hydratedUtxos.slpUtxos.length; i++) {
const thisAddr = hydratedUtxos.slpUtxos[i]
const addrObj = {
address: thisAddr.address
}
// Filter out the different types of UTXOs.
addrObj.bchUtxos = thisAddr.utxos.filter(elem => elem.isValid === false)
addrObj.nullUtxos = thisAddr.utxos.filter(elem => elem.isValid === null)
// Break down the SLP UTXOs.
addrObj.slpUtxos = {
type1: {},
nft: {}
}
// Token Type 1 Minting Batons.
addrObj.slpUtxos.type1.mintBatons = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs1 = elem.tokenType === 1
const isMintingBaton = elem.utxoType === 'minting-baton'
return isValid && tokenTypeIs1 && isMintingBaton
})
// Token Type 1 tokens.
addrObj.slpUtxos.type1.tokens = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs1 = elem.tokenType === 1
const isToken = elem.utxoType === 'token'
return isValid && tokenTypeIs1 && isToken
})
// NFT Group Minting Batons
addrObj.slpUtxos.nft.groupMintBatons = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs129 = elem.tokenType === 129
const isMintingBaton = elem.utxoType === 'minting-baton'
return isValid && tokenTypeIs129 && isMintingBaton
})
// NFT Group tokens
addrObj.slpUtxos.nft.groupTokens = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs129 = elem.tokenType === 129
const isToken = elem.utxoType === 'token'
return isValid && tokenTypeIs129 && isToken
})
// NFT (Child) tokens
addrObj.slpUtxos.nft.tokens = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs65 = elem.tokenType === 65
const isToken = elem.utxoType === 'token'
return isValid && tokenTypeIs65 && isToken
})
retAry.push(addrObj)
}
return retAry
} catch (err) {
console.error('Error in bchjs.utxo.get()')
if (err.error) throw new Error(err.error)
throw err
}
}
/**
* @api Utxo.findBiggestUtxo() findBiggestUtxo()
* @apiName findBiggestUtxo
* @apiGroup UTXO
* @apiDescription Get the biggest UTXO in an array.
*
* Given an array of BCH UTXOs, this method will return the biggest UTXO.
* This is often the simplest way to pick a UTXO for generating a transaction.
*
* @apiExample Example usage:
* (async () => {
* try {
* const utxos = await bchjs.Utxo.get('bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z');
* const utxo = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
* console.log(utxo);
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* {
* "height": 655431,
* "tx_hash": "7a091716f8137e94f87e7760648cd34a17e32754ef95f7c7bda38a635c9b2b1b",
* "tx_pos": 0,
* "value": 800,
* "txid": "7a091716f8137e94f87e7760648cd34a17e32754ef95f7c7bda38a635c9b2b1b",
* "vout": 0,
* "isValid": false,
* "satoshis": 800
* }
*/
// Returns the utxo with the biggest balance from an array of utxos.
findBiggestUtxo (utxos) {
let largestAmount = 0
let largestIndex = 0
if (!Array.isArray(utxos)) {
throw new Error('utxos input to findBiggestUtxo() must be an array')
}
for (let i = 0; i < utxos.length; i++) {
const thisUtxo = utxos[i]
// Give Elecrumx utxos a satoshis property.
if (thisUtxo.value) {
if (!thisUtxo.satoshis) thisUtxo.satoshis = Number(thisUtxo.value)
}
if (!thisUtxo.satoshis) {
throw new Error(
'UTXOs require a satoshis or value property for findBiggestUtxo()'
)
}
if (thisUtxo.satoshis > largestAmount) {
largestAmount = thisUtxo.satoshis
largestIndex = i
}
}
return utxos[largestIndex]
}
}
module.exports = UTXO
-77
View File
@@ -1,77 +0,0 @@
// import BCHWalletBridge from "bch-wallet-bridge.js"
class Wallet {
constructor(walletProvider) {
// this.bchWalletBridge = new BCHWalletBridge(walletProvider)
}
setWalletProvider(walletProvider) {
this.bchWalletBridge.walletProvider = walletProvider
}
getAddress(changeType, index, dAppId) {
return this.bchWalletBridge.getAddress(changeType, index, dAppId)
}
getAddressIndex(changeType, dAppId) {
return this.bchWalletBridge.getAddressIndex(changeType, dAppId)
}
getAddresses(changeType, startIndex, size, dAppId) {
return this.bchWalletBridge.getAddresses(
changeType,
startIndex,
size,
dAppId
)
}
getRedeemScript(p2shAddress, dAppId) {
return this.bchWalletBridge.getRedeemScript(p2shAddress, dAppId)
}
getRedeemScripts(dAppId) {
return this.bchWalletBridge.getRedeemScripts(dAppId)
}
addRedeemScript(redeemScript, dAppId) {
return this.bchWalletBridge.addRedeemScript(redeemScript, dAppId)
}
getUtxos(dAppId) {
return this.bchWalletBridge.getUtxos(dAppId)
}
getBalance(dAppId) {
return this.bchWalletBridge.getBalance(dAppId)
}
sign(address, dataToSign) {
return this.bchWalletBridge.sign(address, dataToSign)
}
buildTransaction(outputs, dAppId) {
return this.bchWalletBridge.buildTransaction(outputs, dAppId)
}
getProtocolVersion() {
return this.bchWalletBridge.getProtocolVersion()
}
getNetwork() {
return this.bchWalletBridge.getNetwork()
}
getFeePerByte() {
return this.bchWalletBridge.getFeePerByte()
}
getDefaultDAppId() {
return this.bchWalletBridge.getDefaultDAppId()
}
setDefaultDAppId(dAppId) {
return this.bchWalletBridge.setDefaultDAppId(dAppId)
}
}
module.exports = Wallet
+7 -7
View File
@@ -2,17 +2,17 @@
A Mocha test file for running end-to-end (e2e) tests. A Mocha test file for running end-to-end (e2e) tests.
*/ */
//const mocha = require("mocha") // const mocha = require("mocha")
const assert = require("chai").assert const assert = require('chai').assert
const sendToken = require("./send-token/send-token") const sendToken = require('./send-token/send-token')
describe("#end-to-end tests", () => { describe('#end-to-end tests', () => {
describe("#send-tokens", () => { describe('#send-tokens', () => {
it("SLPDB should update balances in less than 10 seconds", async () => { it('SLPDB should update balances in less than 10 seconds', async () => {
const result = await sendToken.sendTokenTest() const result = await sendToken.sendTokenTest()
assert(result, true, "True expected if test passed successfully.") assert(result, true, 'True expected if test passed successfully.')
}) })
}) })
}) })
+5 -5
View File
@@ -3,15 +3,15 @@
IPFS is working. IPFS is working.
*/ */
process.env.IPFS_API = `http://localhost:5001` process.env.IPFS_API = 'http://localhost:5001'
// process.env.IPFS_API = `https://ipfs-api.fullstack.cash` // process.env.IPFS_API = `https://ipfs-api.fullstack.cash`
const BCHJS = require("../../../src/bch-js") const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
describe(`#IPFS`, () => { describe('#IPFS', () => {
it("should upload a file to the server", async () => { it('should upload a file to the server', async () => {
const path = `${__dirname}/ipfs-e2e.js` const path = `${__dirname.toString()}/ipfs-e2e.js`
const fileModel = await bchjs.IPFS.createFileModel(path) const fileModel = await bchjs.IPFS.createFileModel(path)
console.log(`fileModel: ${JSON.stringify(fileModel, null, 2)}`) console.log(`fileModel: ${JSON.stringify(fileModel, null, 2)}`)
+14 -14
View File
@@ -12,37 +12,37 @@
mocha --timeout=30000 anonymous-rate-limits.js mocha --timeout=30000 anonymous-rate-limits.js
*/ */
const assert = require("chai").assert const assert = require('chai').assert
// const RESTURL = `https://abc.fullstack.cash/v4/` // const RESTURL = `https://abc.fullstack.cash/v5/`
const RESTURL = `https://bchn.fullstack.cash/v4/` const RESTURL = 'https://bchn.fullstack.cash/v5/'
// const RESTURL = `http://localhost:3000/v4/` // const RESTURL = `http://localhost:3000/v5/`
const BCHJS = require("../../../src/bch-js") const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({ restURL: RESTURL }) const bchjs = new BCHJS({ restURL: RESTURL })
describe("#anonymous rate limits", () => { describe('#anonymous rate limits', () => {
it("should allow an anonymous call to a full node endpoint", async () => { it('should allow an anonymous call to a full node endpoint', async () => {
const result = await bchjs.Control.getNetworkInfo() const result = await bchjs.Control.getNetworkInfo()
assert.property(result, "version") assert.property(result, 'version')
}).timeout(5000) }).timeout(5000)
it("should allow an anonymous call to an indexer", async () => { it('should allow an anonymous call to an indexer', async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" const addr = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
const result = await bchjs.Electrumx.balance(addr) const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "balance") assert.property(result, 'balance')
}).timeout(5000) }).timeout(5000)
it("should throw error when rate limit exceeded", async () => { it('should throw error when rate limit exceeded', async () => {
try { try {
for (let i = 0; i < 35; i++) await bchjs.Control.getNetworkInfo() for (let i = 0; i < 35; i++) await bchjs.Control.getNetworkInfo()
assert.fail("Unexpected result") assert.fail('Unexpected result')
} catch (err) { } catch (err) {
assert.include(err.error, "Too many requests") assert.include(err.error, 'Too many requests')
} }
}).timeout(20000) }).timeout(20000)
}) })
+14 -14
View File
@@ -7,49 +7,49 @@
- Update the PRO_PASS value with a current PRO_PASSES string used by bch-api. - Update the PRO_PASS value with a current PRO_PASSES string used by bch-api.
*/ */
const assert = require("chai").assert const assert = require('chai').assert
const PRO_PASS = "testpassword" const PRO_PASS = 'testpassword'
const BCHJS = require("../../../src/bch-js") const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({ const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v4/`, // restURL: `https://bchn.fullstack.cash/v5/`,
restURL: `https://abc.fullstack.cash/v4/`, restURL: 'https://abc.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v4/`, // restURL: `http://localhost:3000/v5/`,
authPass: PRO_PASS authPass: PRO_PASS
}) })
describe("#Basic Authentication rate limits", () => { describe('#Basic Authentication rate limits', () => {
it("should allow more than 20 RPM to full node", async () => { it('should allow more than 20 RPM to full node', async () => {
for (let i = 0; i < 22; i++) { for (let i = 0; i < 22; i++) {
const result = await bchjs.Control.getNetworkInfo() const result = await bchjs.Control.getNetworkInfo()
if (i === 5) { if (i === 5) {
// console.log(`validating 5th call: ${i}`) // console.log(`validating 5th call: ${i}`)
assert.property(result, "version", "more than 3 calls allowed") assert.property(result, 'version', 'more than 3 calls allowed')
} }
if (i === 15) { if (i === 15) {
// console.log(`validating 5th call: ${i}`) // console.log(`validating 5th call: ${i}`)
assert.property(result, "version", "more than 10 calls allowed") assert.property(result, 'version', 'more than 10 calls allowed')
} }
} }
}).timeout(45000) }).timeout(45000)
it("should allow more than 20 RPM to an indexer", async () => { it('should allow more than 20 RPM to an indexer', async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" const addr = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
for (let i = 0; i < 22; i++) { for (let i = 0; i < 22; i++) {
const result = await bchjs.Blockbook.balance(addr) const result = await bchjs.Blockbook.balance(addr)
if (i === 5) { if (i === 5) {
// console.log(`validating 5th call: ${i}`) // console.log(`validating 5th call: ${i}`)
assert.property(result, "balance", "more than 3 calls allowed") assert.property(result, 'balance', 'more than 3 calls allowed')
} }
if (i === 15) { if (i === 15) {
// console.log(`validating 5th call: ${i}`) // console.log(`validating 5th call: ${i}`)
assert.property(result, "balance", "more than 10 calls allowed") assert.property(result, 'balance', 'more than 10 calls allowed')
} }
} }
}).timeout(45000) }).timeout(45000)
+21 -21
View File
@@ -11,59 +11,59 @@
- Update the JWT_TOKEN value with a current free-level JWT token. - Update the JWT_TOKEN value with a current free-level JWT token.
*/ */
const assert = require("chai").assert const assert = require('chai').assert
const JWT_TOKEN = const JWT_TOKEN =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2YyMDIyMWMxMDAxMmFkOTQwMSIsImVtYWlsIjoiZGVtb0BkZW1vLmNvbSIsImFwaUxldmVsIjoxMCwicmF0ZUxpbWl0IjozLCJpYXQiOjE2MDQ4NTQ2OTEsImV4cCI6MTYwNzQ0NjY5MX0.iwse0z0KDKHx9graCxcOwj6lSlfKQAb1zLhmjvygvts" 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2YyMDIyMWMxMDAxMmFkOTQwMSIsImVtYWlsIjoiZGVtb0BkZW1vLmNvbSIsImFwaUxldmVsIjoxMCwicmF0ZUxpbWl0IjozLCJpYXQiOjE2MDQ4NTQ2OTEsImV4cCI6MTYwNzQ0NjY5MX0.iwse0z0KDKHx9graCxcOwj6lSlfKQAb1zLhmjvygvts'
const BCHJS = require("../../../src/bch-js") const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({ const bchjs = new BCHJS({
restURL: `https://api.fullstack.cash/v4/`, restURL: 'https://api.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v4/`, // restURL: `http://localhost:3000/v5/`,
apiToken: JWT_TOKEN apiToken: JWT_TOKEN
}) })
describe("#free rate limits", () => { describe('#free rate limits', () => {
it("should allow an free call to an indexer", async () => { it('should allow an free call to an indexer', async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" const addr = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
const result = await bchjs.Blockbook.balance(addr) const result = await bchjs.Blockbook.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "balance") assert.property(result, 'balance')
}).timeout(5000) }).timeout(5000)
it("should allow up to 10 RPM to indexer, then throw error", async () => { it('should allow up to 10 RPM to indexer, then throw error', async () => {
try { try {
const addr = "bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0" const addr = 'bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0'
for (let i = 0; i < 15; i++) { for (let i = 0; i < 15; i++) {
const result = await await bchjs.Blockbook.balance(addr) const result = await await bchjs.Blockbook.balance(addr)
if (i === 5) { if (i === 5) {
// console.log(`validating 5th call: ${i}`) // console.log(`validating 5th call: ${i}`)
assert.property(result, "address", "more than 3 calls allowed") assert.property(result, 'address', 'more than 3 calls allowed')
} }
} }
} catch (err) { } catch (err) {
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include( assert.include(
err.error, err.error,
"currently 10 requests", 'currently 10 requests',
"more than 10 not allowed" 'more than 10 not allowed'
) )
assert.include(err.error, 10) assert.include(err.error, 10)
assert.notInclude(err.error, 3) assert.notInclude(err.error, 3)
} }
}).timeout(20000) }).timeout(20000)
it("should allow up to 10 RPM to full node, then throw error", async () => { it('should allow up to 10 RPM to full node, then throw error', async () => {
try { try {
for (let i = 0; i < 15; i++) { for (let i = 0; i < 15; i++) {
const result = await bchjs.Control.getNetworkInfo() const result = await bchjs.Control.getNetworkInfo()
if (i === 5) { if (i === 5) {
// console.log(`validating 5th call: ${i}`) // console.log(`validating 5th call: ${i}`)
assert.property(result, "version", "more than 3 calls allowed") assert.property(result, 'version', 'more than 3 calls allowed')
} }
} }
} catch (err) { } catch (err) {
@@ -71,22 +71,22 @@ describe("#free rate limits", () => {
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include( assert.include(
err.error, err.error,
"currently 10 requests", 'currently 10 requests',
"more than 10 not allowed" 'more than 10 not allowed'
) )
assert.include(err.error, 10) assert.include(err.error, 10)
assert.notInclude(err.error, 3) assert.notInclude(err.error, 3)
} }
}).timeout(20000) }).timeout(20000)
it("should throw error when rate limit exceeded 20 RPM for indexer endpoints", async () => { it('should throw error when rate limit exceeded 20 RPM for indexer endpoints', async () => {
try { try {
for (let i = 0; i < 22; i++) await bchjs.Control.getNetworkInfo() for (let i = 0; i < 22; i++) await bchjs.Control.getNetworkInfo()
assert.fail("unexpected result") assert.fail('unexpected result')
} catch (err) { } catch (err) {
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include(err.error, "Too many requests") assert.include(err.error, 'Too many requests')
} }
}).timeout(20000) }).timeout(20000)
}) })
+16 -16
View File
@@ -11,26 +11,26 @@
- Update the JWT_TOKEN value with a paid tier JWT token. - Update the JWT_TOKEN value with a paid tier JWT token.
*/ */
const assert = require("chai").assert const assert = require('chai').assert
const JWT_TOKEN = const JWT_TOKEN =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMzIyNzEwNCwiZXhwIjoxNjA1ODE5MTA0fQ.CV36grzdD36Ht3BwZGHG4XU40CVDzMRw9Ars1x1r27M" 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMzIyNzEwNCwiZXhwIjoxNjA1ODE5MTA0fQ.CV36grzdD36Ht3BwZGHG4XU40CVDzMRw9Ars1x1r27M'
const BCHJS = require("../../../src/bch-js") const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({ const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v4/`, // restURL: `https://bchn.fullstack.cash/v5/`,
restURL: `https:/abc.fullstack.cash/v4/`, restURL: 'https:/abc.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v4/`, // restURL: `http://localhost:3000/v5/`,
apiToken: JWT_TOKEN apiToken: JWT_TOKEN
}) })
describe("#full node rate limits", () => { describe('#full node rate limits', () => {
it("should allow an free call to an indexer", async () => { it('should allow an free call to an indexer', async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" const addr = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
const result = await bchjs.Electrumx.balance(addr) const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "balance") assert.property(result, 'balance')
}).timeout(5000) }).timeout(5000)
// CT 11/8/20: New rate limits make this test invalid. // CT 11/8/20: New rate limits make this test invalid.
@@ -47,30 +47,30 @@ describe("#full node rate limits", () => {
// } // }
// }).timeout(10000) // }).timeout(10000)
it("should allow more than 20 RPM to full node", async () => { it('should allow more than 20 RPM to full node', async () => {
for (let i = 0; i < 22; i++) { for (let i = 0; i < 22; i++) {
const result = await bchjs.Control.getNetworkInfo() const result = await bchjs.Control.getNetworkInfo()
if (i === 5) { if (i === 5) {
// console.log(`validating 5th call: ${i}`) // console.log(`validating 5th call: ${i}`)
assert.property(result, "version", "more than 3 calls allowed") assert.property(result, 'version', 'more than 3 calls allowed')
} }
if (i === 15) { if (i === 15) {
// console.log(`validating 5th call: ${i}`) // console.log(`validating 5th call: ${i}`)
assert.property(result, "version", "more than 10 calls allowed") assert.property(result, 'version', 'more than 10 calls allowed')
} }
} }
}).timeout(20000) }).timeout(20000)
it("should throw error for more than 100 RPM to fullnode", async () => { it('should throw error for more than 100 RPM to fullnode', async () => {
try { try {
console.log(`This test usually doesn't pass, because of latency.`) console.log("This test usually doesn't pass, because of latency.")
for (let i = 0; i < 100; i++) await bchjs.Control.getNetworkInfo() for (let i = 0; i < 100; i++) await bchjs.Control.getNetworkInfo()
} catch (err) { } catch (err) {
// console.log(`validating after 10th call`) // console.log(`validating after 10th call`)
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include(err.error, "Too many requests") assert.include(err.error, 'Too many requests')
assert.include(err.error, 100) assert.include(err.error, 100)
} }
}).timeout(45000) }).timeout(45000)
+14 -14
View File
@@ -7,50 +7,50 @@
- Update the JWT_TOKEN value with a current indexer-level JWT token. - Update the JWT_TOKEN value with a current indexer-level JWT token.
*/ */
const assert = require("chai").assert const assert = require('chai').assert
const JWT_TOKEN = const JWT_TOKEN =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMzIyNzEwNCwiZXhwIjoxNjA1ODE5MTA0fQ.CV36grzdD36Ht3BwZGHG4XU40CVDzMRw9Ars1x1r27M" 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMzIyNzEwNCwiZXhwIjoxNjA1ODE5MTA0fQ.CV36grzdD36Ht3BwZGHG4XU40CVDzMRw9Ars1x1r27M'
const BCHJS = require("../../../src/bch-js") const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({ const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v4/`, // restURL: `https://bchn.fullstack.cash/v5/`,
restURL: `https://abc.fullstack.cash/v4/`, restURL: 'https://abc.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v4/`, // restURL: `http://localhost:3000/v5/`,
apiToken: JWT_TOKEN apiToken: JWT_TOKEN
}) })
describe("#Indexer rate limits", () => { describe('#Indexer rate limits', () => {
it("should allow more than 20 RPM to full node", async () => { it('should allow more than 20 RPM to full node', async () => {
for (let i = 0; i < 22; i++) { for (let i = 0; i < 22; i++) {
const result = await bchjs.Control.getNetworkInfo() const result = await bchjs.Control.getNetworkInfo()
if (i === 5) { if (i === 5) {
// console.log(`validating 5th call: ${i}`) // console.log(`validating 5th call: ${i}`)
assert.property(result, "version", "more than 3 calls allowed") assert.property(result, 'version', 'more than 3 calls allowed')
} }
if (i === 15) { if (i === 15) {
// console.log(`validating 5th call: ${i}`) // console.log(`validating 5th call: ${i}`)
assert.property(result, "version", "more than 10 calls allowed") assert.property(result, 'version', 'more than 10 calls allowed')
} }
} }
}).timeout(45000) }).timeout(45000)
it("should allow more than 20 RPM to an indexer", async () => { it('should allow more than 20 RPM to an indexer', async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" const addr = 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
for (let i = 0; i < 22; i++) { for (let i = 0; i < 22; i++) {
const result = await bchjs.Electrumx.balance(addr) const result = await bchjs.Electrumx.balance(addr)
if (i === 5) { if (i === 5) {
// console.log(`validating 5th call: ${i}`) // console.log(`validating 5th call: ${i}`)
assert.property(result, "balance", "more than 3 calls allowed") assert.property(result, 'balance', 'more than 3 calls allowed')
} }
if (i === 15) { if (i === 15) {
// console.log(`validating 5th call: ${i}`) // console.log(`validating 5th call: ${i}`)
assert.property(result, "balance", "more than 10 calls allowed") assert.property(result, 'balance', 'more than 10 calls allowed')
} }
} }
}).timeout(45000) }).timeout(45000)
@@ -13,24 +13,25 @@
*/ */
// Replace the address below with the address you want to send the BCH to. // Replace the address below with the address you want to send the BCH to.
const RECV_ADDR1 = `bchtest:qzfn2mly05t6fjsh5kjj0dqq0jjtct27ng089dgg05` const RECV_ADDR1 = 'bchtest:qzfn2mly05t6fjsh5kjj0dqq0jjtct27ng089dgg05'
const RECV_ADDR2 = `bchtest:qz6yw0kqgfkknfy6jh2jvlfnkzmre3lt2u0pgcckdk` const RECV_ADDR2 = 'bchtest:qz6yw0kqgfkknfy6jh2jvlfnkzmre3lt2u0pgcckdk'
const SATOSHIS_TO_SEND = 1000 const SATOSHIS_TO_SEND = 1000
// Instantiate BITBOX. // Instantiate BITBOX.
const bitboxLib = "../../../lib/BITBOX" const bitboxLib = '../../../lib/BITBOX'
const BITBOXSDK = require(bitboxLib) const BITBOXSDK = require(bitboxLib)
const BITBOX = new BITBOXSDK({ restURL: "https://trest.bitcoin.com/v2/" }) const BITBOX = new BITBOXSDK({ restURL: 'https://trest.bitcoin.com/v2/' })
//const BITBOX = new BITBOXSDK({ restURL: "http://localhost:3000/v2/" }) // const BITBOX = new BITBOXSDK({ restURL: "http://localhost:3000/v2/" })
const util = require("util") // const util = require('util')
// Open the wallet generated with create-wallet. // Open the wallet generated with create-wallet.
let walletInfo = {}
try { try {
var walletInfo = require(`./wallet.json`) walletInfo = require('./wallet.json')
} catch (err) { } catch (err) {
console.log( console.log(
`Could not open wallet.json. Generate a wallet with create-wallet first.` 'Could not open wallet.json. Generate a wallet with create-wallet first.'
) )
process.exit(0) process.exit(0)
} }
@@ -38,7 +39,7 @@ try {
const SEND_ADDR = walletInfo.cashAddress const SEND_ADDR = walletInfo.cashAddress
const SEND_MNEMONIC = walletInfo.mnemonic const SEND_MNEMONIC = walletInfo.mnemonic
async function testSend() { async function testSend () {
try { try {
const hex1 = await buildTx1(RECV_ADDR1) const hex1 = await buildTx1(RECV_ADDR1)
const hex2 = await buildTx2(RECV_ADDR2) const hex2 = await buildTx2(RECV_ADDR2)
@@ -51,15 +52,15 @@ async function testSend() {
hex2 hex2
]) ])
console.log(`Transaction IDs: ${JSON.stringify(broadcast, null, 2)}`) console.log(`Transaction IDs: ${JSON.stringify(broadcast, null, 2)}`)
console.log(`Should return an array of TXID strings.`) console.log('Should return an array of TXID strings.')
} catch (err) { } catch (err) {
console.log(`Error in testSend: `, err) console.log('Error in testSend: ', err)
} }
} }
testSend() testSend()
// Build a TX hex with the largest UTXO. // Build a TX hex with the largest UTXO.
async function buildTx1(recAddr) { async function buildTx1 (recAddr) {
try { try {
// Get the balance of the sending address. // Get the balance of the sending address.
const balance = await getBCHBalance(SEND_ADDR, false) const balance = await getBCHBalance(SEND_ADDR, false)
@@ -68,7 +69,7 @@ async function buildTx1(recAddr) {
// Exit if the balance is zero. // Exit if the balance is zero.
if (balance <= 0.0) { if (balance <= 0.0) {
console.log(`Balance of sending address is zero. Exiting.`) console.log('Balance of sending address is zero. Exiting.')
process.exit(0) process.exit(0)
} }
@@ -77,16 +78,17 @@ async function buildTx1(recAddr) {
console.log(`Sender Legacy Address: ${SEND_ADDR_LEGACY}`) console.log(`Sender Legacy Address: ${SEND_ADDR_LEGACY}`)
console.log(`Receiver Legacy Address: ${RECV_ADDR_LEGACY}`) console.log(`Receiver Legacy Address: ${RECV_ADDR_LEGACY}`)
const balance2 = await getBCHBalance(recAddr, false) // const balance2 = await getBCHBalance(recAddr, false)
//console.log(`Balance of recieving address ${recAddr} is ${balance2} BCH.`) // console.log(`Balance of recieving address ${recAddr} is ${balance2} BCH.`)
await getBCHBalance(recAddr, false)
const u = await BITBOX.Address.utxo(SEND_ADDR) const u = await BITBOX.Address.utxo(SEND_ADDR)
//console.log(`u: ${JSON.stringify(u, null, 2)}`) // console.log(`u: ${JSON.stringify(u, null, 2)}`)
const utxo = findBiggestUtxo(u.utxos) const utxo = findBiggestUtxo(u.utxos)
console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`) console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
// instance of transaction builder // instance of transaction builder
const transactionBuilder = new BITBOX.TransactionBuilder("testnet") const transactionBuilder = new BITBOX.TransactionBuilder('testnet')
const satoshisToSend = SATOSHIS_TO_SEND const satoshisToSend = SATOSHIS_TO_SEND
const originalAmount = utxo.satoshis const originalAmount = utxo.satoshis
@@ -137,13 +139,13 @@ async function buildTx1(recAddr) {
return hex return hex
} catch (err) { } catch (err) {
console.log(`Error in buildTx().`) console.log('Error in buildTx().')
throw err throw err
} }
} }
// Build a TX hex with the SECOND largest UTXO. // Build a TX hex with the SECOND largest UTXO.
async function buildTx2(recAddr) { async function buildTx2 (recAddr) {
try { try {
// Get the balance of the sending address. // Get the balance of the sending address.
const balance = await getBCHBalance(SEND_ADDR, false) const balance = await getBCHBalance(SEND_ADDR, false)
@@ -152,7 +154,7 @@ async function buildTx2(recAddr) {
// Exit if the balance is zero. // Exit if the balance is zero.
if (balance <= 0.0) { if (balance <= 0.0) {
console.log(`Balance of sending address is zero. Exiting.`) console.log('Balance of sending address is zero. Exiting.')
process.exit(0) process.exit(0)
} }
@@ -161,16 +163,17 @@ async function buildTx2(recAddr) {
console.log(`Sender Legacy Address: ${SEND_ADDR_LEGACY}`) console.log(`Sender Legacy Address: ${SEND_ADDR_LEGACY}`)
console.log(`Receiver Legacy Address: ${RECV_ADDR_LEGACY}`) console.log(`Receiver Legacy Address: ${RECV_ADDR_LEGACY}`)
const balance2 = await getBCHBalance(recAddr, false) // const balance2 = await getBCHBalance(recAddr, false)
//console.log(`Balance of recieving address ${recAddr} is ${balance2} BCH.`) // console.log(`Balance of recieving address ${recAddr} is ${balance2} BCH.`)
await getBCHBalance(recAddr, false)
const u = await BITBOX.Address.utxo(SEND_ADDR) const u = await BITBOX.Address.utxo(SEND_ADDR)
//console.log(`u: ${JSON.stringify(u, null, 2)}`) // console.log(`u: ${JSON.stringify(u, null, 2)}`)
const utxo = findNextBiggestUtxo(u.utxos) const utxo = findNextBiggestUtxo(u.utxos)
console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`) console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
// instance of transaction builder // instance of transaction builder
const transactionBuilder = new BITBOX.TransactionBuilder("testnet") const transactionBuilder = new BITBOX.TransactionBuilder('testnet')
const satoshisToSend = SATOSHIS_TO_SEND const satoshisToSend = SATOSHIS_TO_SEND
const originalAmount = utxo.satoshis const originalAmount = utxo.satoshis
@@ -221,30 +224,30 @@ async function buildTx2(recAddr) {
return hex return hex
} catch (err) { } catch (err) {
console.log(`Error in buildTx().`) console.log('Error in buildTx().')
throw err throw err
} }
} }
// Generate a change address from a Mnemonic of a private key. // Generate a change address from a Mnemonic of a private key.
function changeAddrFromMnemonic(mnemonic) { function changeAddrFromMnemonic (mnemonic) {
// root seed buffer // root seed buffer
const rootSeed = BITBOX.Mnemonic.toSeed(mnemonic) const rootSeed = BITBOX.Mnemonic.toSeed(mnemonic)
// master HDNode // master HDNode
const masterHDNode = BITBOX.HDNode.fromSeed(rootSeed, "testnet") const masterHDNode = BITBOX.HDNode.fromSeed(rootSeed, 'testnet')
// HDNode of BIP44 account // HDNode of BIP44 account
const account = BITBOX.HDNode.derivePath(masterHDNode, "m/44'/145'/0'") const account = BITBOX.HDNode.derivePath(masterHDNode, "m/44'/145'/0'")
// derive the first external change address HDNode which is going to spend utxo // derive the first external change address HDNode which is going to spend utxo
const change = BITBOX.HDNode.derivePath(account, "0/0") const change = BITBOX.HDNode.derivePath(account, '0/0')
return change return change
} }
// Get the balance in BCH of a BCH address. // Get the balance in BCH of a BCH address.
async function getBCHBalance(addr, verbose) { async function getBCHBalance (addr, verbose) {
try { try {
const result = await BITBOX.Address.details(addr) const result = await BITBOX.Address.details(addr)
@@ -254,16 +257,16 @@ async function getBCHBalance(addr, verbose) {
return bchBalance.balance return bchBalance.balance
} catch (err) { } catch (err) {
console.error(`Error in getBCHBalance: `, err) console.error('Error in getBCHBalance: ', err)
console.log(`addr: ${addr}`) console.log(`addr: ${addr}`)
throw err throw err
} }
} }
// Returns the utxo with the biggest balance from an array of utxos. // Returns the utxo with the biggest balance from an array of utxos.
function findBiggestUtxo(utxos) { function findBiggestUtxo (utxos) {
// Sort the utxos by the amount of satoshis, largest first. // Sort the utxos by the amount of satoshis, largest first.
utxos.sort(function(a, b) { utxos.sort(function (a, b) {
return b.satoshis - a.satoshis return b.satoshis - a.satoshis
}) })
@@ -271,9 +274,9 @@ function findBiggestUtxo(utxos) {
} }
// Returns the utxo with the 2nd biggest balance from an array of utxos. // Returns the utxo with the 2nd biggest balance from an array of utxos.
function findNextBiggestUtxo(utxos) { function findNextBiggestUtxo (utxos) {
// Sort the utxos by the amount of satoshis, largest first. // Sort the utxos by the amount of satoshis, largest first.
utxos.sort(function(a, b) { utxos.sort(function (a, b) {
return b.satoshis - a.satoshis return b.satoshis - a.satoshis
}) })
@@ -13,23 +13,24 @@
*/ */
// Replace the address below with the address you want to send the BCH to. // Replace the address below with the address you want to send the BCH to.
const RECV_ADDR1 = `bchtest:qzfn2mly05t6fjsh5kjj0dqq0jjtct27ng089dgg05` const RECV_ADDR1 = 'bchtest:qzfn2mly05t6fjsh5kjj0dqq0jjtct27ng089dgg05'
const SATOSHIS_TO_SEND = 1000 const SATOSHIS_TO_SEND = 1000
// Instantiate BITBOX. // Instantiate BITBOX.
const bitboxLib = "../../../lib/BITBOX" const bitboxLib = '../../../lib/BITBOX'
const BITBOXSDK = require(bitboxLib) const BITBOXSDK = require(bitboxLib)
const BITBOX = new BITBOXSDK({ restURL: "https://trest.bitcoin.com/v2/" }) const BITBOX = new BITBOXSDK({ restURL: 'https://trest.bitcoin.com/v2/' })
//const BITBOX = new BITBOXSDK({ restURL: "http://localhost:3000/v2/" }) // const BITBOX = new BITBOXSDK({ restURL: "http://localhost:3000/v2/" })
const util = require("util") // const util = require('util')
// Open the wallet generated with create-wallet. // Open the wallet generated with create-wallet.
let walletInfo = {}
try { try {
var walletInfo = require(`./wallet.json`) walletInfo = require('./wallet.json')
} catch (err) { } catch (err) {
console.log( console.log(
`Could not open wallet.json. Generate a wallet with create-wallet first.` 'Could not open wallet.json. Generate a wallet with create-wallet first.'
) )
process.exit(0) process.exit(0)
} }
@@ -37,26 +38,26 @@ try {
const SEND_ADDR = walletInfo.cashAddress const SEND_ADDR = walletInfo.cashAddress
const SEND_MNEMONIC = walletInfo.mnemonic const SEND_MNEMONIC = walletInfo.mnemonic
async function testSend() { async function testSend () {
try { try {
const hex1 = await buildTx1(RECV_ADDR1) const hex1 = await buildTx1(RECV_ADDR1)
//const hex2 = await buildTx2(RECV_ADDR2) // const hex2 = await buildTx2(RECV_ADDR2)
console.log(`hex1: ${hex1}\n\n`) console.log(`hex1: ${hex1}\n\n`)
//console.log(`hex2: ${hex2}\n\n`) // console.log(`hex2: ${hex2}\n\n`)
const broadcast = await BITBOX.RawTransactions.sendRawTransaction(hex1) const broadcast = await BITBOX.RawTransactions.sendRawTransaction(hex1)
console.log(`Transaction IDs: ${JSON.stringify(broadcast, null, 2)}`) console.log(`Transaction IDs: ${JSON.stringify(broadcast, null, 2)}`)
console.log(`Should return a TXID string.`) console.log('Should return a TXID string.')
} catch (err) { } catch (err) {
console.log(`Error in testSend: `, err) console.log('Error in testSend: ', err)
} }
} }
testSend() testSend()
// Build a TX hex with the largest UTXO. // Build a TX hex with the largest UTXO.
async function buildTx1(recAddr) { async function buildTx1 (recAddr) {
try { try {
// Get the balance of the sending address. // Get the balance of the sending address.
const balance = await getBCHBalance(SEND_ADDR, false) const balance = await getBCHBalance(SEND_ADDR, false)
@@ -65,7 +66,7 @@ async function buildTx1(recAddr) {
// Exit if the balance is zero. // Exit if the balance is zero.
if (balance <= 0.0) { if (balance <= 0.0) {
console.log(`Balance of sending address is zero. Exiting.`) console.log('Balance of sending address is zero. Exiting.')
process.exit(0) process.exit(0)
} }
@@ -74,16 +75,17 @@ async function buildTx1(recAddr) {
console.log(`Sender Legacy Address: ${SEND_ADDR_LEGACY}`) console.log(`Sender Legacy Address: ${SEND_ADDR_LEGACY}`)
console.log(`Receiver Legacy Address: ${RECV_ADDR_LEGACY}`) console.log(`Receiver Legacy Address: ${RECV_ADDR_LEGACY}`)
const balance2 = await getBCHBalance(recAddr, false) // const balance2 = await getBCHBalance(recAddr, false)
//console.log(`Balance of recieving address ${recAddr} is ${balance2} BCH.`) // console.log(`Balance of recieving address ${recAddr} is ${balance2} BCH.`)
await getBCHBalance(recAddr, false)
const u = await BITBOX.Address.utxo(SEND_ADDR) const u = await BITBOX.Address.utxo(SEND_ADDR)
//console.log(`u: ${JSON.stringify(u, null, 2)}`) // console.log(`u: ${JSON.stringify(u, null, 2)}`)
const utxo = findBiggestUtxo(u.utxos) const utxo = findBiggestUtxo(u.utxos)
console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`) console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
// instance of transaction builder // instance of transaction builder
const transactionBuilder = new BITBOX.TransactionBuilder("testnet") const transactionBuilder = new BITBOX.TransactionBuilder('testnet')
const satoshisToSend = SATOSHIS_TO_SEND const satoshisToSend = SATOSHIS_TO_SEND
const originalAmount = utxo.satoshis const originalAmount = utxo.satoshis
@@ -134,30 +136,30 @@ async function buildTx1(recAddr) {
return hex return hex
} catch (err) { } catch (err) {
console.log(`Error in buildTx().`) console.log('Error in buildTx().')
throw err throw err
} }
} }
// Generate a change address from a Mnemonic of a private key. // Generate a change address from a Mnemonic of a private key.
function changeAddrFromMnemonic(mnemonic) { function changeAddrFromMnemonic (mnemonic) {
// root seed buffer // root seed buffer
const rootSeed = BITBOX.Mnemonic.toSeed(mnemonic) const rootSeed = BITBOX.Mnemonic.toSeed(mnemonic)
// master HDNode // master HDNode
const masterHDNode = BITBOX.HDNode.fromSeed(rootSeed, "testnet") const masterHDNode = BITBOX.HDNode.fromSeed(rootSeed, 'testnet')
// HDNode of BIP44 account // HDNode of BIP44 account
const account = BITBOX.HDNode.derivePath(masterHDNode, "m/44'/145'/0'") const account = BITBOX.HDNode.derivePath(masterHDNode, "m/44'/145'/0'")
// derive the first external change address HDNode which is going to spend utxo // derive the first external change address HDNode which is going to spend utxo
const change = BITBOX.HDNode.derivePath(account, "0/0") const change = BITBOX.HDNode.derivePath(account, '0/0')
return change return change
} }
// Get the balance in BCH of a BCH address. // Get the balance in BCH of a BCH address.
async function getBCHBalance(addr, verbose) { async function getBCHBalance (addr, verbose) {
try { try {
const result = await BITBOX.Address.details(addr) const result = await BITBOX.Address.details(addr)
@@ -167,16 +169,16 @@ async function getBCHBalance(addr, verbose) {
return bchBalance.balance return bchBalance.balance
} catch (err) { } catch (err) {
console.error(`Error in getBCHBalance: `, err) console.error('Error in getBCHBalance: ', err)
console.log(`addr: ${addr}`) console.log(`addr: ${addr}`)
throw err throw err
} }
} }
// Returns the utxo with the biggest balance from an array of utxos. // Returns the utxo with the biggest balance from an array of utxos.
function findBiggestUtxo(utxos) { function findBiggestUtxo (utxos) {
// Sort the utxos by the amount of satoshis, largest first. // Sort the utxos by the amount of satoshis, largest first.
utxos.sort(function(a, b) { utxos.sort(function (a, b) {
return b.satoshis - a.satoshis return b.satoshis - a.satoshis
}) })
+14 -14
View File
@@ -8,7 +8,7 @@
*/ */
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
@@ -18,22 +18,22 @@ util.inspect.defaultOptions = {
// const SLPSDK = require("../../../lib/SLP") // const SLPSDK = require("../../../lib/SLP")
// const slpsdk = new SLPSDK() // const slpsdk = new SLPSDK()
const WALLET1 = `../wallet1.json` const WALLET1 = '../wallet1.json'
const WALLET2 = `../wallet2.json` const WALLET2 = '../wallet2.json'
const lib = require("../util/e2e-util") const lib = require('../util/e2e-util')
// The main test function. // The main test function.
// Sends a token and reports on how long it takes to show up in SLPDB production. // Sends a token and reports on how long it takes to show up in SLPDB production.
async function sendTokenTest() { async function sendTokenTest () {
try { try {
// Open the sending wallet. // Open the sending wallet.
const sendWallet = await lib.openWallet(WALLET1) const sendWallet = await lib.openWallet(WALLET1)
//console.log(`sendWallet: ${JSON.stringify(walletInfo, null, 2)}`) // console.log(`sendWallet: ${JSON.stringify(walletInfo, null, 2)}`)
// Open the recieving wallet. // Open the recieving wallet.
const recvWallet = await lib.openWallet(WALLET2) const recvWallet = await lib.openWallet(WALLET2)
//console.log(`recvWallet: ${JSON.stringify(walletInfo, null, 2)}`) // console.log(`recvWallet: ${JSON.stringify(walletInfo, null, 2)}`)
// Get the balance of the recieving wallet. // Get the balance of the recieving wallet.
// const testTokens = recvWallet.tokenBalance.filter( // const testTokens = recvWallet.tokenBalance.filter(
@@ -46,7 +46,7 @@ async function sendTokenTest() {
// Send a token to the recieving wallet. // Send a token to the recieving wallet.
await lib.sendToken(sendWallet, recvWallet) await lib.sendToken(sendWallet, recvWallet)
console.log(`Sent test token.`) console.log('Sent test token.')
// Track the time until the balance for the recieving wallet has been updated. // Track the time until the balance for the recieving wallet has been updated.
const startTime = new Date() const startTime = new Date()
@@ -56,7 +56,7 @@ async function sendTokenTest() {
for (let i = 0; i < 50; i++) { for (let i = 0; i < 50; i++) {
await sleep(waitTime) // Wait for a while before checking await sleep(waitTime) // Wait for a while before checking
console.log(`Checking token balance...`) console.log('Checking token balance...')
newBalance = await lib.getTestTokenBalance(recvWallet) newBalance = await lib.getTestTokenBalance(recvWallet)
// Break out of the loop once a new balance is detected. // Break out of the loop once a new balance is detected.
@@ -65,12 +65,12 @@ async function sendTokenTest() {
// Provide high-level warnings. // Provide high-level warnings.
const secondsPassed = (i * waitTime) / 1000 const secondsPassed = (i * waitTime) / 1000
if (secondsPassed > 60 * 10) { if (secondsPassed > 60 * 10) {
console.log(`More than 10 minutes passed.`) console.log('More than 10 minutes passed.')
return false // Fail the test. return false // Fail the test.
} else if (secondsPassed > 60 * 5) { } else if (secondsPassed > 60 * 5) {
console.log(`More than 5 minutes passed.`) console.log('More than 5 minutes passed.')
} else if (secondsPassed > 60) { } else if (secondsPassed > 60) {
console.log(`More than 1 minute passed.`) console.log('More than 1 minute passed.')
} }
} }
@@ -85,13 +85,13 @@ async function sendTokenTest() {
return deltaTime // Return the time in minutes it took for SLPDB to update. return deltaTime // Return the time in minutes it took for SLPDB to update.
} catch (err) { } catch (err) {
console.log(`Error in e2e/send-token.js/sendTokenTest(): `, err) console.log('Error in e2e/send-token.js/sendTokenTest(): ', err)
return false return false
} }
} }
// Promise based sleep function. // Promise based sleep function.
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+19 -18
View File
@@ -10,19 +10,19 @@ module.exports = {
threeDecimals threeDecimals
} }
const SLPSDK = require("../../../lib/SLP") const SLPSDK = require('../../../lib/SLP')
const slpsdk = new SLPSDK() const slpsdk = new SLPSDK()
const testTokenId = const testTokenId =
"cc1b2084a9c43bb5a633df7f38201adde5c5f5cef2fed945d12f8dcd4e505c67" 'cc1b2084a9c43bb5a633df7f38201adde5c5f5cef2fed945d12f8dcd4e505c67'
// Open a wallet and return an object with its address, BCH balance, and SLP // Open a wallet and return an object with its address, BCH balance, and SLP
// token balance. // token balance.
async function openWallet(filename) { async function openWallet (filename) {
try { try {
walletInfo = require(filename) const walletInfo = require(filename)
//const walletBalance = await getBalance(walletInfo) // const walletBalance = await getBalance(walletInfo)
// const walletBalance = await slpsdk.Utils.balancesForAddress( // const walletBalance = await slpsdk.Utils.balancesForAddress(
// walletInfo.slpAddress // walletInfo.slpAddress
// ) // )
@@ -40,7 +40,7 @@ async function openWallet(filename) {
} }
// Send a token from wallet1 to wallet2. // Send a token from wallet1 to wallet2.
async function sendToken(wallet1, wallet2) { async function sendToken (wallet1, wallet2) {
try { try {
const mnemonic = wallet1.mnemonic const mnemonic = wallet1.mnemonic
@@ -53,11 +53,11 @@ async function sendToken(wallet1, wallet2) {
// HDNode of BIP44 account // HDNode of BIP44 account
const account = slpsdk.HDNode.derivePath(masterHDNode, "m/44'/145'/0'") const account = slpsdk.HDNode.derivePath(masterHDNode, "m/44'/145'/0'")
const change = slpsdk.HDNode.derivePath(account, "0/0") const change = slpsdk.HDNode.derivePath(account, '0/0')
// get the cash address // get the cash address
//const cashAddress = slpsdk.HDNode.toCashAddress(change) // const cashAddress = slpsdk.HDNode.toCashAddress(change)
//const slpAddress = slpsdk.HDNode.toSLPAddress(change) // const slpAddress = slpsdk.HDNode.toSLPAddress(change)
const fundingAddress = wallet1.slpAddress const fundingAddress = wallet1.slpAddress
const fundingWif = slpsdk.HDNode.toWIF(change) // <-- compressed WIF format const fundingWif = slpsdk.HDNode.toWIF(change) // <-- compressed WIF format
@@ -71,25 +71,26 @@ async function sendToken(wallet1, wallet2) {
tokenReceiverAddress, tokenReceiverAddress,
bchChangeReceiverAddress, bchChangeReceiverAddress,
tokenId: tokenId:
"cc1b2084a9c43bb5a633df7f38201adde5c5f5cef2fed945d12f8dcd4e505c67", 'cc1b2084a9c43bb5a633df7f38201adde5c5f5cef2fed945d12f8dcd4e505c67',
amount: 1 amount: 1
} }
//console.log(`createConfig: ${util.inspect(createConfig)}`) // console.log(`createConfig: ${util.inspect(createConfig)}`)
// Generate, sign, and broadcast a hex-encoded transaction for sending // Generate, sign, and broadcast a hex-encoded transaction for sending
// the tokens. // the tokens.
const sendTxId = await slpsdk.TokenType1.send(sendConfig) // const sendTxId = await slpsdk.TokenType1.send(sendConfig)
await slpsdk.TokenType1.send(sendConfig)
//console.log(`sendTxId: ${sendTxId}`) // console.log(`sendTxId: ${sendTxId}`)
} catch (err) { } catch (err) {
console.log(`Error in e2e-util.js/sendToken()`) console.log('Error in e2e-util.js/sendToken()')
throw err throw err
} }
} }
// Returns just the test token balance for a wallet. // Returns just the test token balance for a wallet.
async function getTestTokenBalance(walletData) { async function getTestTokenBalance (walletData) {
try { try {
const tokenBalance = await slpsdk.Util.balancesForAddress( const tokenBalance = await slpsdk.Util.balancesForAddress(
walletData.slpAddress walletData.slpAddress
@@ -100,20 +101,20 @@ async function getTestTokenBalance(walletData) {
return testTokens[0].balance return testTokens[0].balance
} catch (err) { } catch (err) {
console.log(`Error in e2e-util.js/getTestTokenBalance()`) console.log('Error in e2e-util.js/getTestTokenBalance()')
throw err throw err
} }
} }
// Round a number to three decimal places. // Round a number to three decimal places.
function threeDecimals(inNum) { function threeDecimals (inNum) {
try { try {
let tempNum = inNum * 1000 let tempNum = inNum * 1000
tempNum = Math.round(tempNum) tempNum = Math.round(tempNum)
tempNum = tempNum / 1000 tempNum = tempNum / 1000
return tempNum return tempNum
} catch (err) { } catch (err) {
console.log(`Error in e2e-util.js/threeDecimals()`) console.log('Error in e2e-util.js/threeDecimals()')
throw err throw err
} }
} }
-148
View File
@@ -1,148 +0,0 @@
/*
Integration tests for the Blockbook library.
*/
const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
//const axios = require("axios")
// Inspect utility used for debugging.
const util = require("util")
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe(`#Blockbook`, () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe(`#Balance`, () => {
it(`should GET balance for a single address`, async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
const result = await bchjs.Blockbook.balance(addr)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAnyKeys(result, [
"page",
"totalPages",
"itemsOnPage",
"address",
"balance",
"totalReceived",
"totalSent",
"unconfirmedBalance",
"unconfirmedTxs",
"txs",
"txids"
])
assert.isArray(result.txids)
})
it(`should POST request balances for an array of addresses`, async () => {
const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
]
const result = await bchjs.Blockbook.balance(addr)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"page",
"totalPages",
"itemsOnPage",
"address",
"balance",
"totalReceived",
"totalSent",
"unconfirmedBalance",
"unconfirmedTxs",
"txs",
"txids"
])
assert.isArray(result[0].txids)
})
it(`should throw error on array size rate limit`, async () => {
try {
const addr = []
for (let i = 0; i < 25; i++)
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
const result = await bchjs.Blockbook.balance(addr)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.hasAnyKeys(err, ["error"])
assert.include(err.error, "Array too large")
}
})
})
describe(`#utxo`, () => {
it(`should GET utxos for a single address`, async () => {
const addr = "bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7"
const result = await bchjs.Blockbook.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"txid",
"vout",
"value",
"height",
"confirmations"
])
})
it(`should POST utxo details for an array of addresses`, async () => {
const addr = [
"bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7",
"bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr"
]
const result = await bchjs.Blockbook.utxo(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isArray(result[0])
assert.hasAnyKeys(result[0][0], [
"txid",
"vout",
"value",
"height",
"confirmations"
])
})
it(`should throw error on array size rate limit`, async () => {
try {
const addr = []
for (let i = 0; i < 25; i++)
addr.push("bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr")
const result = await bchjs.Blockbook.utxo(addr)
//console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.hasAnyKeys(err, ["error"])
assert.include(err.error, "Array too large")
}
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+125 -88
View File
@@ -7,109 +7,109 @@
of an e2e test to be properly tested. of an e2e test to be properly tested.
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
depth: 3 depth: 3
} }
describe(`#blockchain`, () => { describe('#blockchain', () => {
beforeEach(async () => { beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
describe(`#getBestBlockHash`, () => { describe('#getBestBlockHash', () => {
it(`should GET best block hash`, async () => { it('should GET best block hash', async () => {
const result = await bchjs.Blockchain.getBestBlockHash() const result = await bchjs.Blockchain.getBestBlockHash()
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.isString(result) assert.isString(result)
assert.equal(result.length, 64, "Specific hash length") assert.equal(result.length, 64, 'Specific hash length')
}) })
}) })
describe("#getBlockHeader", () => { describe('#getBlockHeader', () => {
it(`should GET block header for a single hash`, async () => { it('should GET block header for a single hash', async () => {
const hash = const hash =
"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201" '000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201'
const result = await bchjs.Blockchain.getBlockHeader(hash) const result = await bchjs.Blockchain.getBlockHeader(hash)
assert.hasAllKeys(result, [ assert.hasAllKeys(result, [
"hash", 'hash',
"confirmations", 'confirmations',
"height", 'height',
"version", 'version',
"versionHex", 'versionHex',
"merkleroot", 'merkleroot',
"time", 'time',
"mediantime", 'mediantime',
"nonce", 'nonce',
"bits", 'bits',
"difficulty", 'difficulty',
"chainwork", 'chainwork',
"previousblockhash", 'previousblockhash',
"nextblockhash", 'nextblockhash',
"nTx" 'nTx'
]) ])
}) })
it(`should GET block headers for an array of hashes`, async () => { it('should GET block headers for an array of hashes', async () => {
const hash = [ const hash = [
"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201", '000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201',
"00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3" '00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3'
] ]
const result = await bchjs.Blockchain.getBlockHeader(hash) const result = await bchjs.Blockchain.getBlockHeader(hash)
assert.isArray(result) assert.isArray(result)
assert.hasAllKeys(result[0], [ assert.hasAllKeys(result[0], [
"hash", 'hash',
"confirmations", 'confirmations',
"height", 'height',
"version", 'version',
"versionHex", 'versionHex',
"merkleroot", 'merkleroot',
"time", 'time',
"mediantime", 'mediantime',
"nonce", 'nonce',
"bits", 'bits',
"difficulty", 'difficulty',
"chainwork", 'chainwork',
"previousblockhash", 'previousblockhash',
"nextblockhash", 'nextblockhash',
"nTx" 'nTx'
]) ])
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const data = [] const data = []
for (let i = 0; i < 25; i++) { for (let i = 0; i < 25; i++) {
data.push( data.push(
"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201" '000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201'
) )
} }
const result = await bchjs.Blockchain.getBlockHeader(data) const result = await bchjs.Blockchain.getBlockHeader(data)
console.log(`result: ${util.inspect(result)}`) console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe("#getMempoolEntry", () => { describe('#getMempoolEntry', () => {
/* /*
// To run this test, the txid must be unconfirmed. // To run this test, the txid must be unconfirmed.
const txid = const txid =
@@ -161,77 +161,77 @@ describe(`#blockchain`, () => {
}) })
*/ */
it(`should throw an error if txid is not in mempool`, async () => { it('should throw an error if txid is not in mempool', async () => {
try { try {
const txid = const txid =
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7" '03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7'
await bchjs.Blockchain.getMempoolEntry(txid) await bchjs.Blockchain.getMempoolEntry(txid)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
//console.log(`err: ${util.inspect(err)}`) // console.log(`err: ${util.inspect(err)}`)
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, `Transaction not in mempool`) assert.include(err.error, 'Transaction not in mempool')
} }
}) })
}) })
describe(`#getTxOutProof`, () => { describe('#getTxOutProof', () => {
it(`should get single tx out proof`, async () => { it('should get single tx out proof', async () => {
const txid = const txid =
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7" '03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7'
const result = await bchjs.Blockchain.getTxOutProof(txid) const result = await bchjs.Blockchain.getTxOutProof(txid)
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isString(result) assert.isString(result)
}) })
it(`should get an array of tx out proofs`, async () => { it('should get an array of tx out proofs', async () => {
const txid = [ const txid = [
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7", '03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7',
"fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33'
] ]
const result = await bchjs.Blockchain.getTxOutProof(txid) const result = await bchjs.Blockchain.getTxOutProof(txid)
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.isString(result[0]) assert.isString(result[0])
}) })
}) })
describe(`#verifyTxOutProof`, () => { describe('#verifyTxOutProof', () => {
const mockTxOutProof = const mockTxOutProof =
"0000002086a4a3161f9ba2174883ec0b93acceac3b2f37b36ed1f90000000000000000009cb02406d1094ecf3e0b4c0ca7c585125e721147c39daf6b48c90b512741e13a12333e5cb38705180f441d8c7100000008fee9b60f1edb57e5712839186277ed39e0a004a32be9096ee47472efde8eae62f789f9d7a9f59d0ea7093dea1e0c65ff0b953f1d8cf3d47f92e732ca0295f603c272d5f4a63509f7a887f2549d78af7444aa0ecbb4f66d9cbe13bc6a89f59e05a199df8325d490818ffefe6b6321d32d7496a68580459836c0183f89082fc1b491cc91b23ecdcaa4c347bf599a62904d61f1c15b400ebbd5c90149010c139d9c1e31b774b796977393a238080ab477e1d240d0c4f155d36f519668f49bae6bd8cd5b8e40522edf76faa09cca6188d83ff13af6967cc6a569d1a5e9aeb1fdb7f531ddd2d0cbb81879741d5f38166ac1932136264366a4065cc96a42e41f96294f02df01" '0000002086a4a3161f9ba2174883ec0b93acceac3b2f37b36ed1f90000000000000000009cb02406d1094ecf3e0b4c0ca7c585125e721147c39daf6b48c90b512741e13a12333e5cb38705180f441d8c7100000008fee9b60f1edb57e5712839186277ed39e0a004a32be9096ee47472efde8eae62f789f9d7a9f59d0ea7093dea1e0c65ff0b953f1d8cf3d47f92e732ca0295f603c272d5f4a63509f7a887f2549d78af7444aa0ecbb4f66d9cbe13bc6a89f59e05a199df8325d490818ffefe6b6321d32d7496a68580459836c0183f89082fc1b491cc91b23ecdcaa4c347bf599a62904d61f1c15b400ebbd5c90149010c139d9c1e31b774b796977393a238080ab477e1d240d0c4f155d36f519668f49bae6bd8cd5b8e40522edf76faa09cca6188d83ff13af6967cc6a569d1a5e9aeb1fdb7f531ddd2d0cbb81879741d5f38166ac1932136264366a4065cc96a42e41f96294f02df01'
it(`should verify a single proof`, async () => { it('should verify a single proof', async () => {
const result = await bchjs.Blockchain.verifyTxOutProof(mockTxOutProof) const result = await bchjs.Blockchain.verifyTxOutProof(mockTxOutProof)
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.isString(result[0]) assert.isString(result[0])
assert.equal( assert.equal(
result[0], result[0],
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7" '03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7'
) )
}) })
it(`should verify an array of proofs`, async () => { it('should verify an array of proofs', async () => {
const proofs = [mockTxOutProof, mockTxOutProof] const proofs = [mockTxOutProof, mockTxOutProof]
const result = await bchjs.Blockchain.verifyTxOutProof(proofs) const result = await bchjs.Blockchain.verifyTxOutProof(proofs)
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.isString(result[0]) assert.isString(result[0])
assert.equal( assert.equal(
result[0], result[0],
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7" '03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7'
) )
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const data = [] const data = []
for (let i = 0; i < 25; i++) data.push(mockTxOutProof) for (let i = 0; i < 25; i++) data.push(mockTxOutProof)
@@ -239,35 +239,35 @@ describe(`#blockchain`, () => {
const result = await bchjs.Blockchain.verifyTxOutProof(data) const result = await bchjs.Blockchain.verifyTxOutProof(data)
console.log(`result: ${util.inspect(result)}`) console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe("#getTxOut", () => { describe('#getTxOut', () => {
it("should get information on an unspent tx", async () => { it('should get information on an unspent tx', async () => {
const result = await bchjs.Blockchain.getTxOut( const result = await bchjs.Blockchain.getTxOut(
"62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8", '62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8',
0, 0,
true true
) )
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, [ assert.hasAllKeys(result, [
"bestblock", 'bestblock',
"confirmations", 'confirmations',
"value", 'value',
"scriptPubKey", 'scriptPubKey',
"coinbase" 'coinbase'
]) ])
}) })
it("should get information on a spent tx", async () => { it('should get information on a spent tx', async () => {
const result = await bchjs.Blockchain.getTxOut( const result = await bchjs.Blockchain.getTxOut(
"87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871", '87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871',
0, 0,
true true
) )
@@ -276,8 +276,45 @@ describe(`#blockchain`, () => {
assert.equal(result, null) assert.equal(result, null)
}) })
}) })
describe('#getBlock', () => {
it('should get block information with default verbosity', async () => {
const blockhash =
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
const result = await bchjs.Blockchain.getBlock(blockhash)
assert.hasAllKeys(result, [
'hash',
'confirmations',
'size',
'height',
'version',
'versionHex',
'merkleroot',
'tx',
'time',
'mediantime',
'nonce',
'bits',
'difficulty',
'chainwork',
'nTx',
'previousblockhash',
'nextblockhash'
])
assert.isArray(result.tx)
})
it('should get block information with verbosity 0', async () => {
const blockhash =
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
const verbosity = 0
const result = await bchjs.Blockchain.getBlock(blockhash, verbosity)
assert.isString(result)
})
})
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+22 -20
View File
@@ -5,22 +5,22 @@
TODO TODO
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const BCHJS = require("../../../../src/bch-js") const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS({ restURL: process.env.RESTURL })
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
depth: 3 depth: 3
} }
describe("#rawtransaction", () => { describe('#rawtransaction', () => {
beforeEach(async () => { beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
/* /*
@@ -29,29 +29,30 @@ describe("#rawtransaction", () => {
below expect error messages returned from the server, but at least test below expect error messages returned from the server, but at least test
that the server is responding on those endpoints, and responds consistently. that the server is responding on those endpoints, and responds consistently.
*/ */
describe("sendRawTransaction", () => { describe('sendRawTransaction', () => {
it("should send a single transaction hex", async () => { it('should send a single transaction hex', async () => {
try { try {
const hex = const hex =
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
await bchjs.RawTransactions.sendRawTransaction(hex) await bchjs.RawTransactions.sendRawTransaction(hex)
//console.log(`result ${JSON.stringify(result, null, 2)}`) // console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
//console.log(`err: ${util.inspect(err)}`) // console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"]) assert.hasAllKeys(err, ['error'])
assert.include(err.error, "bad-txns-inputs-missingorspent") assert.include(err.error, 'bad-txns-inputs-missingorspent')
// assert.include(err.error, 'Missing inputs')
} }
}) })
it("should send an array of tx hexes", async () => { it('should send an array of tx hexes', async () => {
try { try {
const hexes = [ const hexes = [
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000", '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000',
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
] ]
const result = await bchjs.RawTransactions.sendRawTransaction(hexes) const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
@@ -59,13 +60,14 @@ describe("#rawtransaction", () => {
} catch (err) { } catch (err) {
// console.log(`err: ${util.inspect(err)}`) // console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"]) assert.hasAllKeys(err, ['error'])
assert.include(err.error, "bad-txns-inputs-missingorspent") assert.include(err.error, 'bad-txns-inputs-missingorspent')
// assert.include(err.error, 'Missing inputs')
} }
}) })
}) })
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+50 -47
View File
@@ -3,21 +3,21 @@
These tests are specific to the ABC chain. These tests are specific to the ABC chain.
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const BCHJS = require("../../../../src/bch-js") const BCHJS = require('../../../../src/bch-js')
let bchjs let bchjs
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
depth: 1 depth: 1
} }
describe(`#SLP`, () => { describe('#SLP', () => {
// before(() => { // before(() => {
// console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`) // console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`)
// console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`) // console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`)
@@ -25,68 +25,68 @@ describe(`#SLP`, () => {
beforeEach(async () => { beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits. // Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
bchjs = new BCHJS() bchjs = new BCHJS({ restURL: process.env.RESTURL })
}) })
describe("#util", () => { describe('#util', () => {
describe("#tokenUtxoDetails", () => { describe('#tokenUtxoDetails', () => {
it("should handle a range of UTXO types", async () => { it('should handle a range of UTXO types', async () => {
const utxos = [ const utxos = [
// Malformed SLP tx // Malformed SLP tx
{ {
note: "Malformed SLP tx", note: 'Malformed SLP tx',
tx_hash: tx_hash:
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Normal TX (non-SLP) // Normal TX (non-SLP)
{ {
note: "Normal TX (non-SLP)", note: 'Normal TX (non-SLP)',
tx_hash: tx_hash:
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0", '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
tx_pos: 0, tx_pos: 0,
value: 400000 value: 400000
}, },
// Valid PSF SLP tx // Valid PSF SLP tx
{ {
note: "Valid PSF SLP tx", note: 'Valid PSF SLP tx',
tx_hash: tx_hash:
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd", 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Valid SLP token not in whitelist // Valid SLP token not in whitelist
{ {
note: "Valid SLP token not in whitelist", note: 'Valid SLP token not in whitelist',
tx_hash: tx_hash:
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488", '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Token send on BCHN network. // Token send on BCHN network.
{ {
note: "Token send on BCHN network", note: 'Token send on BCHN network',
tx_hash: tx_hash:
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019", '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Token send on ABC network. // Token send on ABC network.
{ {
note: "Token send on ABC network", note: 'Token send on ABC network',
tx_hash: tx_hash:
"336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d", '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Known invalid SLP token send of PSF tokens. // Known invalid SLP token send of PSF tokens.
{ {
note: "Known invalid SLP token send of PSF tokens", note: 'Known invalid SLP token send of PSF tokens',
tx_hash: tx_hash:
"2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74", '2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
} }
@@ -113,6 +113,7 @@ describe(`#SLP`, () => {
// Token send on BCHN network // Token send on BCHN network
assert.equal(data[4].tx_hash, utxos[4].tx_hash) assert.equal(data[4].tx_hash, utxos[4].tx_hash)
// assert.equal(data[4].isValid, true)
assert.equal(data[4].isValid, null) assert.equal(data[4].isValid, null)
// Token send on ABC network // Token send on ABC network
@@ -125,52 +126,52 @@ describe(`#SLP`, () => {
}) })
}) })
describe("#validateTxid3", () => { describe('#validateTxid3', () => {
it("should invalidate a known invalid TXID", async () => { it('should invalidate a known invalid TXID', async () => {
const txid = const txid =
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
const result = await bchjs.SLP.Utils.validateTxid3(txid) const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.property(result[0], "txid") assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid) assert.equal(result[0].txid, txid)
assert.property(result[0], "valid") assert.property(result[0], 'valid')
assert.equal(result[0].valid, null) assert.equal(result[0].valid, null)
}) })
it("should validate a known valid TXID", async () => { it('should validate a known valid TXID', async () => {
const txid = const txid =
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd" 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
const result = await bchjs.SLP.Utils.validateTxid3(txid) const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.property(result[0], "txid") assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid) assert.equal(result[0].txid, txid)
assert.property(result[0], "valid") assert.property(result[0], 'valid')
assert.equal(result[0].valid, true) assert.equal(result[0].valid, true)
}) })
it("should handle a mix of valid, invalid, and non-SLP txs", async () => { it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [ const txids = [
// Malformed SLP tx // Malformed SLP tx
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP) // Normal TX (non-SLP)
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0", '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx // Valid PSF SLP tx
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd", 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist // Valid SLP token not in whitelist
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488", '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Unprocessed SLP TX // Unprocessed SLP TX
// "a0d6406eecfd8634158efa9314ff15b4cbf451938e9dc7b5678c46b41eabc6ed" // Mint baton // "a0d6406eecfd8634158efa9314ff15b4cbf451938e9dc7b5678c46b41eabc6ed" // Mint baton
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019" '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019'
] ]
const result = await bchjs.SLP.Utils.validateTxid3(txids) const result = await bchjs.SLP.Utils.validateTxid3(txids)
@@ -180,26 +181,27 @@ describe(`#SLP`, () => {
assert.equal(result[0].valid, null) assert.equal(result[0].valid, null)
assert.equal(result[1].valid, null) assert.equal(result[1].valid, null)
assert.equal(result[2].valid, true) assert.equal(result[2].valid, true)
// assert.equal(result[2].valid, null)
assert.equal(result[3].valid, true) assert.equal(result[3].valid, true)
assert.equal(result[4].valid, null) assert.equal(result[4].valid, null)
}) })
}) })
describe("#validateTxid", () => { describe('#validateTxid', () => {
it("should handle a mix of valid, invalid, and non-SLP txs", async () => { it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [ const txids = [
// Malformed SLP tx // Malformed SLP tx
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP) // Normal TX (non-SLP)
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0", '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx // Valid PSF SLP tx
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd", 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist // Valid SLP token not in whitelist
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488", '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Token send on BCHN network. // Token send on BCHN network.
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019", '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
// Token send on ABC network. // Token send on ABC network.
"336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d" '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d'
] ]
const result = await bchjs.SLP.Utils.validateTxid(txids) const result = await bchjs.SLP.Utils.validateTxid(txids)
@@ -209,6 +211,7 @@ describe(`#SLP`, () => {
assert.equal(result[0].valid, null) assert.equal(result[0].valid, null)
assert.equal(result[1].valid, null) assert.equal(result[1].valid, null)
assert.equal(result[2].valid, true) assert.equal(result[2].valid, true)
// assert.equal(result[2].valid, null)
assert.equal(result[3].valid, true) assert.equal(result[3].valid, true)
assert.equal(result[4].valid, null) assert.equal(result[4].valid, null)
assert.equal(result[5].valid, true) assert.equal(result[5].valid, true)
@@ -218,6 +221,6 @@ describe(`#SLP`, () => {
}) })
// Promise-based sleep function // Promise-based sleep function
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
@@ -0,0 +1,38 @@
/*
Integration tests for the utxo.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
describe('#UTXO', () => {
beforeEach(async () => {
// sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#get', () => {
it('should get hydrated and filtered UTXOs for an address', async () => {
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -5,22 +5,22 @@
TODO TODO
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const BCHJS = require("../../../../src/bch-js") const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
depth: 3 depth: 3
} }
describe("#rawtransaction", () => { describe('#rawtransaction', () => {
beforeEach(async () => { beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(3000)
}) })
/* /*
@@ -29,29 +29,29 @@ describe("#rawtransaction", () => {
below expect error messages returned from the server, but at least test below expect error messages returned from the server, but at least test
that the server is responding on those endpoints, and responds consistently. that the server is responding on those endpoints, and responds consistently.
*/ */
describe("sendRawTransaction", () => { describe('sendRawTransaction', () => {
it("should send a single transaction hex", async () => { it('should send a single transaction hex', async () => {
try { try {
const hex = const hex =
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
await bchjs.RawTransactions.sendRawTransaction(hex) await bchjs.RawTransactions.sendRawTransaction(hex)
//console.log(`result ${JSON.stringify(result, null, 2)}`) // console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
//console.log(`err: ${util.inspect(err)}`) // console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"]) assert.hasAllKeys(err, ['error'])
assert.include(err.error, "Missing inputs") assert.include(err.error, 'Missing inputs')
} }
}) })
it("should send an array of tx hexes", async () => { it('should send an array of tx hexes', async () => {
try { try {
const hexes = [ const hexes = [
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000", '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000',
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
] ]
const result = await bchjs.RawTransactions.sendRawTransaction(hexes) const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
@@ -59,13 +59,13 @@ describe("#rawtransaction", () => {
} catch (err) { } catch (err) {
// console.log(`err: ${util.inspect(err)}`) // console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"]) assert.hasAllKeys(err, ['error'])
assert.include(err.error, "Missing inputs") assert.include(err.error, 'Missing inputs')
} }
}) })
}) })
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
@@ -3,21 +3,21 @@
These tests are specific to the ABC chain. These tests are specific to the ABC chain.
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const BCHJS = require("../../../../src/bch-js") const BCHJS = require('../../../../src/bch-js')
let bchjs let bchjs
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
depth: 1 depth: 1
} }
describe(`#SLP`, () => { describe('#SLP', () => {
// before(() => { // before(() => {
// console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`) // console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`)
// console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`) // console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`)
@@ -25,68 +25,93 @@ describe(`#SLP`, () => {
beforeEach(async () => { beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits. // Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(3000)
bchjs = new BCHJS() bchjs = new BCHJS()
}) })
describe("#util", () => { describe('#util', () => {
describe("#tokenUtxoDetails", () => { describe('#decodeOpReturn', () => {
it("should handle a range of UTXO types", async () => { it('should decode a NFT Child transaction', async () => {
const txid =
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data, 'tokenType')
assert.property(data, 'txType')
assert.property(data, 'ticker')
assert.property(data, 'name')
assert.property(data, 'tokenId')
assert.property(data, 'documentUri')
assert.property(data, 'documentHash')
assert.property(data, 'decimals')
assert.property(data, 'mintBatonVout')
assert.property(data, 'qty')
assert.equal(data.tokenType, 65)
assert.equal(data.mintBatonVout, 0)
assert.equal(data.qty, '1')
})
})
describe('#tokenUtxoDetails', () => {
it('should handle a range of UTXO types', async () => {
const utxos = [ const utxos = [
// Malformed SLP tx // Malformed SLP tx
{ {
note: "Malformed SLP tx", note: 'Malformed SLP tx',
tx_hash: tx_hash:
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Normal TX (non-SLP) // Normal TX (non-SLP)
{ {
note: "Normal TX (non-SLP)", note: 'Normal TX (non-SLP)',
tx_hash: tx_hash:
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0", '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
tx_pos: 0, tx_pos: 0,
value: 400000 value: 400000
}, },
// Valid PSF SLP tx // Valid PSF SLP tx
{ {
note: "Valid PSF SLP tx", note: 'Valid PSF SLP tx',
tx_hash: tx_hash:
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd", 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Valid SLP token not in whitelist // Valid SLP token not in whitelist
{ {
note: "Valid SLP token not in whitelist", note: 'Valid SLP token not in whitelist',
tx_hash: tx_hash:
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488", '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Token send on BCHN network. // Token send on BCHN network.
{ {
note: "Token send on BCHN network", note: 'Token send on BCHN network',
tx_hash: tx_hash:
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019", '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Token send on ABC network. // Token send on ABC network.
{ {
note: "Token send on ABC network", note: 'Token send on ABC network',
tx_hash: tx_hash:
"336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d", '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Known invalid SLP token send of PSF tokens. // Known invalid SLP token send of PSF tokens.
{ {
note: "Known invalid SLP token send of PSF tokens", note: 'Known invalid SLP token send of PSF tokens',
tx_hash: tx_hash:
"2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74", '2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
} }
@@ -125,51 +150,51 @@ describe(`#SLP`, () => {
}) })
}) })
describe("#validateTxid3", () => { describe('#validateTxid3', () => {
it("should invalidate a known invalid TXID", async () => { it('should invalidate a known invalid TXID', async () => {
const txid = const txid =
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
const result = await bchjs.SLP.Utils.validateTxid3(txid) const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.property(result[0], "txid") assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid) assert.equal(result[0].txid, txid)
assert.property(result[0], "valid") assert.property(result[0], 'valid')
assert.equal(result[0].valid, null) assert.equal(result[0].valid, null)
}) })
it("should validate a known valid TXID", async () => { it('should validate a known valid TXID', async () => {
const txid = const txid =
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd" 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
const result = await bchjs.SLP.Utils.validateTxid3(txid) const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.property(result[0], "txid") assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid) assert.equal(result[0].txid, txid)
assert.property(result[0], "valid") assert.property(result[0], 'valid')
assert.equal(result[0].valid, true) assert.equal(result[0].valid, true)
}) })
it("should handle a mix of valid, invalid, and non-SLP txs", async () => { it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [ const txids = [
// Malformed SLP tx // Malformed SLP tx
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP) // Normal TX (non-SLP)
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0", '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx // Valid PSF SLP tx
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd", 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist // Valid SLP token not in whitelist
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488", '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Unprocessed SLP TX // Unprocessed SLP TX
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019" '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019'
] ]
const result = await bchjs.SLP.Utils.validateTxid3(txids) const result = await bchjs.SLP.Utils.validateTxid3(txids)
@@ -186,30 +211,32 @@ describe(`#SLP`, () => {
assert.equal(result[2].txid, txids[2]) assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true) assert.equal(result[2].valid, true)
// True in validateTxid() // True in validateTxid() (no whitelist SLPDB) but null in validateTxid3()
assert.equal(result[3].txid, txids[3]) assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, true) assert.equal(result[3].valid, true)
// Note: This should change from null to true once SLPDB finishes indexing.
assert.equal(result[4].txid, txids[4]) assert.equal(result[4].txid, txids[4])
// True when no whitelist SLPDB is used.
assert.equal(result[4].valid, true) assert.equal(result[4].valid, true)
}) })
}) })
describe("#validateTxid", () => { describe('#validateTxid', () => {
it("should handle a mix of valid, invalid, and non-SLP txs", async () => { it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [ const txids = [
// Malformed SLP tx // Malformed SLP tx
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP) // Normal TX (non-SLP)
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0", '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx // Valid PSF SLP tx
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd", 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist // Valid SLP token not in whitelist
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488", '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Token send on BCHN network. // Token send on BCHN network.
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019", '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
// Token send on ABC network. // Token send on ABC network.
"336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d" '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d'
] ]
const result = await bchjs.SLP.Utils.validateTxid(txids) const result = await bchjs.SLP.Utils.validateTxid(txids)
@@ -241,6 +268,6 @@ describe(`#SLP`, () => {
}) })
// Promise-based sleep function // Promise-based sleep function
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
@@ -0,0 +1,105 @@
/*
Integration tests for the utxo.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
describe('#UTXO', () => {
beforeEach(async () => {
// sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
})
describe('#get', () => {
it('should get hydrated and filtered UTXOs for an address', async () => {
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should handle an array of addresses', async () => {
const addr = [
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
]
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should handle NFTs and minting batons', async () => {
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
assert.isArray(result[0].slpUtxos.type1.mintBatons)
assert.isArray(result[0].slpUtxos.type1.tokens)
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
assert.isArray(result[0].slpUtxos.nft.groupTokens)
assert.isArray(result[0].slpUtxos.nft.tokens)
})
})
describe('#findBiggestUtxo', () => {
it('should sort UTXOs from Electrumx', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
it('should sort UTXOs from Utxos.get()', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
const utxos = await bchjs.Utxo.get(addr)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+25
View File
@@ -0,0 +1,25 @@
/*
Integration tests for bchjs dsproof library.
*/
const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
describe('#DSProof', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
})
describe('#getDSProof', () => {
it('should get TX info from the full node', async () => {
const txid =
'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
const result = await bchjs.DSProof.getDSProof(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, null)
})
})
})
+19 -19
View File
@@ -5,22 +5,22 @@
TODO TODO
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const BCHJS = require("../../../../src/bch-js") const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
depth: 3 depth: 3
} }
describe("#rawtransaction", () => { describe('#rawtransaction', () => {
beforeEach(async () => { beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(3000)
}) })
/* /*
@@ -29,29 +29,29 @@ describe("#rawtransaction", () => {
below expect error messages returned from the server, but at least test below expect error messages returned from the server, but at least test
that the server is responding on those endpoints, and responds consistently. that the server is responding on those endpoints, and responds consistently.
*/ */
describe("sendRawTransaction", () => { describe('sendRawTransaction', () => {
it("should send a single transaction hex", async () => { it('should send a single transaction hex', async () => {
try { try {
const hex = const hex =
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
await bchjs.RawTransactions.sendRawTransaction(hex) await bchjs.RawTransactions.sendRawTransaction(hex)
//console.log(`result ${JSON.stringify(result, null, 2)}`) // console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
//console.log(`err: ${util.inspect(err)}`) // console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"]) assert.hasAllKeys(err, ['error'])
assert.include(err.error, "Missing inputs") assert.include(err.error, 'Missing inputs')
} }
}) })
it("should send an array of tx hexes", async () => { it('should send an array of tx hexes', async () => {
try { try {
const hexes = [ const hexes = [
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000", '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000',
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
] ]
const result = await bchjs.RawTransactions.sendRawTransaction(hexes) const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
@@ -59,13 +59,13 @@ describe("#rawtransaction", () => {
} catch (err) { } catch (err) {
// console.log(`err: ${util.inspect(err)}`) // console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"]) assert.hasAllKeys(err, ['error'])
assert.include(err.error, "Missing inputs") assert.include(err.error, 'Missing inputs')
} }
}) })
}) })
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+96 -46
View File
@@ -3,21 +3,21 @@
These tests are specific to the ABC chain. These tests are specific to the ABC chain.
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const BCHJS = require("../../../../src/bch-js") const BCHJS = require('../../../../src/bch-js')
let bchjs let bchjs
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
depth: 1 depth: 1
} }
describe(`#SLP`, () => { describe('#SLP', () => {
// before(() => { // before(() => {
// console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`) // console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`)
// console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`) // console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`)
@@ -25,68 +25,93 @@ describe(`#SLP`, () => {
beforeEach(async () => { beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits. // Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(3000)
bchjs = new BCHJS() bchjs = new BCHJS()
}) })
describe("#util", () => { describe('#util', () => {
describe("#tokenUtxoDetails", () => { describe('#decodeOpReturn', () => {
it("should handle a range of UTXO types", async () => { it('should decode a NFT Child transaction', async () => {
const txid =
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data, 'tokenType')
assert.property(data, 'txType')
assert.property(data, 'ticker')
assert.property(data, 'name')
assert.property(data, 'tokenId')
assert.property(data, 'documentUri')
assert.property(data, 'documentHash')
assert.property(data, 'decimals')
assert.property(data, 'mintBatonVout')
assert.property(data, 'qty')
assert.equal(data.tokenType, 65)
assert.equal(data.mintBatonVout, 0)
assert.equal(data.qty, '1')
})
})
describe('#tokenUtxoDetails', () => {
it('should handle a range of UTXO types', async () => {
const utxos = [ const utxos = [
// Malformed SLP tx // Malformed SLP tx
{ {
note: "Malformed SLP tx", note: 'Malformed SLP tx',
tx_hash: tx_hash:
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Normal TX (non-SLP) // Normal TX (non-SLP)
{ {
note: "Normal TX (non-SLP)", note: 'Normal TX (non-SLP)',
tx_hash: tx_hash:
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0", '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
tx_pos: 0, tx_pos: 0,
value: 400000 value: 400000
}, },
// Valid PSF SLP tx // Valid PSF SLP tx
{ {
note: "Valid PSF SLP tx", note: 'Valid PSF SLP tx',
tx_hash: tx_hash:
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd", 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Valid SLP token not in whitelist // Valid SLP token not in whitelist
{ {
note: "Valid SLP token not in whitelist", note: 'Valid SLP token not in whitelist',
tx_hash: tx_hash:
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488", '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Token send on BCHN network. // Token send on BCHN network.
{ {
note: "Token send on BCHN network", note: 'Token send on BCHN network',
tx_hash: tx_hash:
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019", '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Token send on ABC network. // Token send on ABC network.
{ {
note: "Token send on ABC network", note: 'Token send on ABC network',
tx_hash: tx_hash:
"336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d", '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
}, },
// Known invalid SLP token send of PSF tokens. // Known invalid SLP token send of PSF tokens.
{ {
note: "Known invalid SLP token send of PSF tokens", note: 'Known invalid SLP token send of PSF tokens',
tx_hash: tx_hash:
"2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74", '2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74',
tx_pos: 1, tx_pos: 1,
value: 546 value: 546
} }
@@ -125,51 +150,51 @@ describe(`#SLP`, () => {
}) })
}) })
describe("#validateTxid3", () => { describe('#validateTxid3', () => {
it("should invalidate a known invalid TXID", async () => { it('should invalidate a known invalid TXID', async () => {
const txid = const txid =
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
const result = await bchjs.SLP.Utils.validateTxid3(txid) const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.property(result[0], "txid") assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid) assert.equal(result[0].txid, txid)
assert.property(result[0], "valid") assert.property(result[0], 'valid')
assert.equal(result[0].valid, null) assert.equal(result[0].valid, null)
}) })
it("should validate a known valid TXID", async () => { it('should validate a known valid TXID', async () => {
const txid = const txid =
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd" 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
const result = await bchjs.SLP.Utils.validateTxid3(txid) const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.property(result[0], "txid") assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid) assert.equal(result[0].txid, txid)
assert.property(result[0], "valid") assert.property(result[0], 'valid')
assert.equal(result[0].valid, true) assert.equal(result[0].valid, true)
}) })
it("should handle a mix of valid, invalid, and non-SLP txs", async () => { it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [ const txids = [
// Malformed SLP tx // Malformed SLP tx
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP) // Normal TX (non-SLP)
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0", '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx // Valid PSF SLP tx
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd", 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist // Valid SLP token not in whitelist
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488", '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Unprocessed SLP TX // Unprocessed SLP TX
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019" '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019'
] ]
const result = await bchjs.SLP.Utils.validateTxid3(txids) const result = await bchjs.SLP.Utils.validateTxid3(txids)
@@ -196,21 +221,21 @@ describe(`#SLP`, () => {
}) })
}) })
describe("#validateTxid", () => { describe('#validateTxid', () => {
it("should handle a mix of valid, invalid, and non-SLP txs", async () => { it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [ const txids = [
// Malformed SLP tx // Malformed SLP tx
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a", 'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP) // Normal TX (non-SLP)
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0", '01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx // Valid PSF SLP tx
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd", 'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist // Valid SLP token not in whitelist
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488", '3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Token send on BCHN network. // Token send on BCHN network.
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019", '402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
// Token send on ABC network. // Token send on ABC network.
"336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d" '336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d'
] ]
const result = await bchjs.SLP.Utils.validateTxid(txids) const result = await bchjs.SLP.Utils.validateTxid(txids)
@@ -239,9 +264,34 @@ describe(`#SLP`, () => {
}) })
}) })
}) })
describe('#nft1', () => {
describe('#listNFTGroupChildren', () => {
it('should return array of children GENESIS transactions IDs', async () => {
const groupId = '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
const result = await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftChildren')
assert.isArray(result.nftChildren)
})
})
describe('#parentNFTGroup', () => {
it('should return parent NFT group information', async () => {
const tokenId = '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
const result = await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftGroup')
assert.property(result.nftGroup, 'id')
assert.property(result.nftGroup, 'name')
assert.property(result.nftGroup, 'symbol')
assert.property(result.nftGroup, 'documentUri')
assert.property(result.nftGroup, 'versionType')
assert.property(result.nftGroup, 'initialTokenQty')
})
})
})
}) })
// Promise-based sleep function // Promise-based sleep function
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+103
View File
@@ -0,0 +1,103 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
*/
const chai = require('chai')
const assert = chai.assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
// Inspect utility used for debugging.
const util = require('util')
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe('#util', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#validateAddress', () => {
it('should return false for testnet addr on mainnet', async () => {
const address = 'bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y'
const result = await bchjs.Util.validateAddress(address)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ['isvalid'])
assert.equal(result.isvalid, false)
})
it('should return false for bad address', async () => {
const address = 'bitcoincash:qp4k8fjtgunhdr7yq30ha4peu'
const result = await bchjs.Util.validateAddress(address)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ['isvalid'])
assert.equal(result.isvalid, false)
})
it('should validate valid address', async () => {
const address = 'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z'
const result = await bchjs.Util.validateAddress(address)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
'isvalid',
'address',
'scriptPubKey',
// "ismine",
// "iswatchonly",
'isscript'
])
assert.equal(result.isvalid, true)
})
it('should validate an array of addresses', async () => {
const address = [
'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z',
'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z'
]
const result = await bchjs.Util.validateAddress(address)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
'isvalid',
'address',
'scriptPubKey',
// "ismine",
// "iswatchonly",
'isscript'
])
})
it('should throw error on array size rate limit', async () => {
try {
const dataMock =
'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z'
const data = []
for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.Util.validateAddress(data)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -0,0 +1,125 @@
/*
Integration tests for the utxo.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
describe('#UTXO', () => {
beforeEach(async () => {
// sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
})
describe('#get', () => {
it('should get hydrated and filtered UTXOs for an address', async () => {
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should handle an array of addresses', async () => {
const addr = [
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
]
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should handle NFTs and minting batons', async () => {
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
assert.isArray(result[0].slpUtxos.type1.mintBatons)
assert.isArray(result[0].slpUtxos.type1.tokens)
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
assert.isArray(result[0].slpUtxos.nft.groupTokens)
assert.isArray(result[0].slpUtxos.nft.tokens)
})
it('should use the whitelist when flag is set', async () => {
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const useWhitelist = true
const result = await bchjs.Utxo.get(addr, useWhitelist)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
// Most token UTXOs should end up in the nullUtxos array.
assert.isAbove(result[0].bchUtxos.length, 0)
assert.isAbove(result[0].nullUtxos.length, 1)
})
})
describe('#findBiggestUtxo', () => {
it('should sort UTXOs from Electrumx', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
it('should sort UTXOs from Utxos.get()', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
const utxos = await bchjs.Utxo.get(addr)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+74 -74
View File
@@ -7,116 +7,116 @@
of an e2e test to be properly tested. of an e2e test to be properly tested.
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const RESTURL = process.env.RESTURL const RESTURL = process.env.RESTURL
? process.env.RESTURL ? process.env.RESTURL
: "https://testnet3.fullstack.cash/v4/" : 'https://testnet3.fullstack.cash/v5/'
// if (process.env.RESTURL) RESTURL = process.env.RESTURL // if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../../src/bch-js") const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` }) // const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN }) const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
depth: 3 depth: 3
} }
describe("#blockchain", () => { describe('#blockchain', () => {
beforeEach(async () => { beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
describe("#getBestBlockHash", () => { describe('#getBestBlockHash', () => {
it("should GET best block hash", async () => { it('should GET best block hash', async () => {
const result = await bchjs.Blockchain.getBestBlockHash() const result = await bchjs.Blockchain.getBestBlockHash()
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.isString(result) assert.isString(result)
assert.equal(result.length, 64, "Specific hash length") assert.equal(result.length, 64, 'Specific hash length')
}) })
}) })
describe("#getBlockHeader", () => { describe('#getBlockHeader', () => {
it("should GET block header for a single hash", async () => { it('should GET block header for a single hash', async () => {
const hash = const hash =
"000000000000c57178ace90210289e6b5383134c5b5306e1cdd8395176e10aaf" '000000000000c57178ace90210289e6b5383134c5b5306e1cdd8395176e10aaf'
const result = await bchjs.Blockchain.getBlockHeader(hash) const result = await bchjs.Blockchain.getBlockHeader(hash)
assert.hasAllKeys(result, [ assert.hasAllKeys(result, [
"hash", 'hash',
"confirmations", 'confirmations',
"height", 'height',
"version", 'version',
"versionHex", 'versionHex',
"merkleroot", 'merkleroot',
"time", 'time',
"mediantime", 'mediantime',
"nonce", 'nonce',
"bits", 'bits',
"difficulty", 'difficulty',
"chainwork", 'chainwork',
"previousblockhash", 'previousblockhash',
"nextblockhash", 'nextblockhash',
"nTx" 'nTx'
]) ])
}) })
it("should GET block headers for an array of hashes", async () => { it('should GET block headers for an array of hashes', async () => {
const hash = [ const hash = [
"000000000000c57178ace90210289e6b5383134c5b5306e1cdd8395176e10aaf", '000000000000c57178ace90210289e6b5383134c5b5306e1cdd8395176e10aaf',
"00000000000b7db4cbae48d852fcbef32f728014582094ad613fe12af6600ff2" '00000000000b7db4cbae48d852fcbef32f728014582094ad613fe12af6600ff2'
] ]
const result = await bchjs.Blockchain.getBlockHeader(hash) const result = await bchjs.Blockchain.getBlockHeader(hash)
assert.isArray(result) assert.isArray(result)
assert.hasAllKeys(result[0], [ assert.hasAllKeys(result[0], [
"hash", 'hash',
"confirmations", 'confirmations',
"height", 'height',
"version", 'version',
"versionHex", 'versionHex',
"merkleroot", 'merkleroot',
"time", 'time',
"mediantime", 'mediantime',
"nonce", 'nonce',
"bits", 'bits',
"difficulty", 'difficulty',
"chainwork", 'chainwork',
"previousblockhash", 'previousblockhash',
"nextblockhash", 'nextblockhash',
"nTx" 'nTx'
]) ])
}) })
it("should throw error on array size rate limit", async () => { it('should throw error on array size rate limit', async () => {
try { try {
const data = [] const data = []
for (let i = 0; i < 25; i++) { for (let i = 0; i < 25; i++) {
data.push( data.push(
"00000000000b7db4cbae48d852fcbef32f728014582094ad613fe12af6600ff2" '00000000000b7db4cbae48d852fcbef32f728014582094ad613fe12af6600ff2'
) )
} }
await bchjs.Blockchain.getBlockHeader(data) await bchjs.Blockchain.getBlockHeader(data)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe("#getMempoolEntry", () => { describe('#getMempoolEntry', () => {
/* /*
// To run this test, the txid must be unconfirmed. // To run this test, the txid must be unconfirmed.
const txid = const txid =
@@ -168,26 +168,26 @@ describe("#blockchain", () => {
}) })
*/ */
it("should throw an error if txid is not in mempool", async () => { it('should throw an error if txid is not in mempool', async () => {
try { try {
const txid = const txid =
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04" '1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
await bchjs.Blockchain.getMempoolEntry(txid) await bchjs.Blockchain.getMempoolEntry(txid)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
// console.log(`err: ${util.inspect(err)}`) // console.log(`err: ${util.inspect(err)}`)
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Transaction not in mempool") assert.include(err.error, 'Transaction not in mempool')
} }
}) })
}) })
describe("#getTxOutProof", () => { describe('#getTxOutProof', () => {
it("should get single tx out proof", async () => { it('should get single tx out proof', async () => {
const txid = const txid =
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04" '1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
const result = await bchjs.Blockchain.getTxOutProof(txid) const result = await bchjs.Blockchain.getTxOutProof(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
@@ -195,10 +195,10 @@ describe("#blockchain", () => {
assert.isString(result) assert.isString(result)
}) })
it("should get an array of tx out proofs", async () => { it('should get an array of tx out proofs', async () => {
const txid = [ const txid = [
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04", '1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04',
"fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752" 'fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752'
] ]
const result = await bchjs.Blockchain.getTxOutProof(txid) const result = await bchjs.Blockchain.getTxOutProof(txid)
@@ -209,11 +209,11 @@ describe("#blockchain", () => {
}) })
}) })
describe("#verifyTxOutProof", () => { describe('#verifyTxOutProof', () => {
const mockTxOutProof = const mockTxOutProof =
"00000020ac86ce8f2bda235c0dc135d18f6a777c44b121e8f41db8a51ca65000000000000cd4de3c49337712f3a1092c47c3bf73ec2f9b1cfd289ee991ede0b6eab4df229d5b8c5dffff001d82ce005b0700000003ec55d9142eac2c1d2229e5af24898b2590c111b62e2787fa1998d3d713fd432fd557124ed758fa156a6cdc6d317d5575aec2b86dfb159c62ecb4743f28bef1cb52e7a32fbec31e367ec152fdb952f75ee75bd8575f97b394093dbb0e6c694ffc0135" '00000020ac86ce8f2bda235c0dc135d18f6a777c44b121e8f41db8a51ca65000000000000cd4de3c49337712f3a1092c47c3bf73ec2f9b1cfd289ee991ede0b6eab4df229d5b8c5dffff001d82ce005b0700000003ec55d9142eac2c1d2229e5af24898b2590c111b62e2787fa1998d3d713fd432fd557124ed758fa156a6cdc6d317d5575aec2b86dfb159c62ecb4743f28bef1cb52e7a32fbec31e367ec152fdb952f75ee75bd8575f97b394093dbb0e6c694ffc0135'
it("should verify a single proof", async () => { it('should verify a single proof', async () => {
const result = await bchjs.Blockchain.verifyTxOutProof(mockTxOutProof) const result = await bchjs.Blockchain.verifyTxOutProof(mockTxOutProof)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
@@ -221,11 +221,11 @@ describe("#blockchain", () => {
assert.isString(result[0]) assert.isString(result[0])
assert.equal( assert.equal(
result[0], result[0],
"fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752" 'fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752'
) )
}) })
it("should verify an array of proofs", async () => { it('should verify an array of proofs', async () => {
const proofs = [mockTxOutProof, mockTxOutProof] const proofs = [mockTxOutProof, mockTxOutProof]
const result = await bchjs.Blockchain.verifyTxOutProof(proofs) const result = await bchjs.Blockchain.verifyTxOutProof(proofs)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
@@ -234,11 +234,11 @@ describe("#blockchain", () => {
assert.isString(result[0]) assert.isString(result[0])
assert.equal( assert.equal(
result[0], result[0],
"fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752" 'fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752'
) )
}) })
it("should throw error on array size rate limit", async () => { it('should throw error on array size rate limit', async () => {
try { try {
const data = [] const data = []
for (let i = 0; i < 25; i++) data.push(mockTxOutProof) for (let i = 0; i < 25; i++) data.push(mockTxOutProof)
@@ -246,15 +246,15 @@ describe("#blockchain", () => {
const result = await bchjs.Blockchain.verifyTxOutProof(data) const result = await bchjs.Blockchain.verifyTxOutProof(data)
console.log(`result: ${util.inspect(result)}`) console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+9 -9
View File
@@ -2,31 +2,31 @@
Integration tests for bchjs control library. Integration tests for bchjs control library.
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const RESTURL = process.env.RESTURL const RESTURL = process.env.RESTURL
? process.env.RESTURL ? process.env.RESTURL
: `https://testnet3.fullstack.cash/v4/` : 'https://testnet3.fullstack.cash/v5/'
const BCHJS = require("../../../../src/bch-js") const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN }) const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
describe(`#control`, () => { describe('#control', () => {
beforeEach(async () => { beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
describe(`#getNetworkInfo`, () => { describe('#getNetworkInfo', () => {
it("should get info on the full node", async () => { it('should get info on the full node', async () => {
const result = await bchjs.Control.getNetworkInfo() const result = await bchjs.Control.getNetworkInfo()
console.log(`result: ${JSON.stringify(result, null, 2)}`) console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "version") assert.property(result, 'version')
}) })
}) })
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+135 -129
View File
@@ -1,195 +1,200 @@
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const sinon = require("sinon") const sinon = require('sinon')
const util = require('util')
const RESTURL = process.env.RESTURL const RESTURL = process.env.RESTURL
? process.env.RESTURL ? process.env.RESTURL
: `https://testnet3.fullstack.cash/v4/` : 'https://testnet3.fullstack.cash/v5/'
const BCHJS = require("../../../../src/bch-js") const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN }) const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
describe(`#ElectrumX`, () => { describe('#ElectrumX', () => {
let sandbox let sandbox
beforeEach(async () => { beforeEach(async () => {
sandbox = sinon.createSandbox() sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
describe(`#utxo`, () => { describe('#utxo', () => {
it(`should GET utxos for a single address`, async () => { it('should GET utxos for a single address', async () => {
const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2" const addr = 'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2'
const result = await bchjs.Electrumx.utxo(addr) const result = await bchjs.Electrumx.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "utxos") assert.property(result, 'utxos')
assert.isArray(result.utxos) assert.isArray(result.utxos)
assert.property(result.utxos[0], "height") assert.property(result.utxos[0], 'height')
assert.property(result.utxos[0], "tx_hash") assert.property(result.utxos[0], 'tx_hash')
assert.property(result.utxos[0], "tx_pos") assert.property(result.utxos[0], 'tx_pos')
assert.property(result.utxos[0], "value") assert.property(result.utxos[0], 'value')
}) })
it(`should POST utxo details for an array of addresses`, async () => { it('should POST utxo details for an array of addresses', async () => {
const addr = [ const addr = [
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2", 'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2',
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2" 'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2'
] ]
const result = await bchjs.Electrumx.utxo(addr) const result = await bchjs.Electrumx.utxo(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "utxos") assert.property(result, 'utxos')
assert.isArray(result.utxos) assert.isArray(result.utxos)
assert.property(result.utxos[0], "utxos") assert.property(result.utxos[0], 'utxos')
assert.isArray(result.utxos[0].utxos) assert.isArray(result.utxos[0].utxos)
assert.property(result.utxos[0], "address") assert.property(result.utxos[0], 'address')
assert.property(result.utxos[0].utxos[0], "height") assert.property(result.utxos[0].utxos[0], 'height')
assert.property(result.utxos[0].utxos[0], "tx_hash") assert.property(result.utxos[0].utxos[0], 'tx_hash')
assert.property(result.utxos[0].utxos[0], "tx_pos") assert.property(result.utxos[0].utxos[0], 'tx_pos')
assert.property(result.utxos[0].utxos[0], "value") assert.property(result.utxos[0].utxos[0], 'value')
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const addr = [] const addr = []
for (let i = 0; i < 25; i++) for (let i = 0; i < 25; i++) {
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2") addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
}
const result = await bchjs.Electrumx.utxo(addr) // const result = await bchjs.Electrumx.utxo(addr)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
await bchjs.Electrumx.utxo(addr)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe(`#balance`, () => { describe('#balance', () => {
it(`should GET balance for a single address`, async () => { it('should GET balance for a single address', async () => {
const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2" const addr = 'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2'
const result = await bchjs.Electrumx.balance(addr) const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "balance") assert.property(result, 'balance')
assert.property(result.balance, "confirmed") assert.property(result.balance, 'confirmed')
assert.property(result.balance, "unconfirmed") assert.property(result.balance, 'unconfirmed')
}) })
it(`should POST request for balances for an array of addresses`, async () => { it('should POST request for balances for an array of addresses', async () => {
const addr = [ const addr = [
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2", 'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2',
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2" 'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2'
] ]
const result = await bchjs.Electrumx.balance(addr) const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "balances") assert.property(result, 'balances')
assert.isArray(result.balances) assert.isArray(result.balances)
assert.property(result.balances[0], "address") assert.property(result.balances[0], 'address')
assert.property(result.balances[0], "balance") assert.property(result.balances[0], 'balance')
assert.property(result.balances[0].balance, "confirmed") assert.property(result.balances[0].balance, 'confirmed')
assert.property(result.balances[0].balance, "unconfirmed") assert.property(result.balances[0].balance, 'unconfirmed')
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const addr = [] const addr = []
for (let i = 0; i < 25; i++) for (let i = 0; i < 25; i++) {
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2") addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
}
const result = await bchjs.Electrumx.balance(addr) const result = await bchjs.Electrumx.balance(addr)
console.log(`result: ${util.inspect(result)}`) console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe(`#transactions`, () => { describe('#transactions', () => {
it(`should GET transaction history for a single address`, async () => { it('should GET transaction history for a single address', async () => {
const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2" const addr = 'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2'
const result = await bchjs.Electrumx.transactions(addr) const result = await bchjs.Electrumx.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "transactions") assert.property(result, 'transactions')
assert.isArray(result.transactions) assert.isArray(result.transactions)
assert.property(result.transactions[0], "height") assert.property(result.transactions[0], 'height')
assert.property(result.transactions[0], "tx_hash") assert.property(result.transactions[0], 'tx_hash')
}) })
it(`should POST request for transaction history for an array of addresses`, async () => { it('should POST request for transaction history for an array of addresses', async () => {
const addr = [ const addr = [
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2", 'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2',
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2" 'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2'
] ]
const result = await bchjs.Electrumx.transactions(addr) const result = await bchjs.Electrumx.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "transactions") assert.property(result, 'transactions')
assert.isArray(result.transactions) assert.isArray(result.transactions)
assert.property(result.transactions[0], "address") assert.property(result.transactions[0], 'address')
assert.property(result.transactions[0], "transactions") assert.property(result.transactions[0], 'transactions')
assert.isArray(result.transactions[0].transactions) assert.isArray(result.transactions[0].transactions)
assert.property(result.transactions[0].transactions[0], "height") assert.property(result.transactions[0].transactions[0], 'height')
assert.property(result.transactions[0].transactions[0], "tx_hash") assert.property(result.transactions[0].transactions[0], 'tx_hash')
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const addr = [] const addr = []
for (let i = 0; i < 25; i++) for (let i = 0; i < 25; i++) {
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2") addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
}
const result = await bchjs.Electrumx.transactions(addr) const result = await bchjs.Electrumx.transactions(addr)
console.log(`result: ${util.inspect(result)}`) console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe(`#unconfirmed`, () => { describe('#unconfirmed', () => {
// These tests won't work because unconfirmed transactions are transient in nature. // These tests won't work because unconfirmed transactions are transient in nature.
/* /*
it(`should GET unconfirmed UTXOs (mempool) for a single address`, async () => { it(`should GET unconfirmed UTXOs (mempool) for a single address`, async () => {
@@ -234,137 +239,138 @@ describe(`#ElectrumX`, () => {
}) })
*/ */
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const addr = [] const addr = []
for (let i = 0; i < 25; i++) for (let i = 0; i < 25; i++) {
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2") addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
}
await bchjs.Electrumx.unconfirmed(addr) await bchjs.Electrumx.unconfirmed(addr)
//console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe(`#blockHeader`, () => { describe('#blockHeader', () => {
it(`should GET block headers for given height and count`, async () => { it('should GET block headers for given height and count', async () => {
const height = 42 const height = 42
const count = 2 const count = 2
const result = await bchjs.Electrumx.blockHeader(height, count) const result = await bchjs.Electrumx.blockHeader(height, count)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "headers") assert.property(result, 'headers')
assert.isArray(result.headers) assert.isArray(result.headers)
assert.equal(result.headers.length, 2) assert.equal(result.headers.length, 2)
}) })
it(`should GET block headers for given height with default count = 1`, async () => { it('should GET block headers for given height with default count = 1', async () => {
const height = 42 const height = 42
const result = await bchjs.Electrumx.blockHeader(height) const result = await bchjs.Electrumx.blockHeader(height)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "headers") assert.property(result, 'headers')
assert.isArray(result.headers) assert.isArray(result.headers)
assert.equal(result.headers.length, 1) assert.equal(result.headers.length, 1)
}) })
}) })
describe(`#txData`, () => { describe('#txData', () => {
it(`should GET details for a single transaction`, async () => { it('should GET details for a single transaction', async () => {
const txid = const txid =
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee" '76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee'
const result = await bchjs.Electrumx.txData(txid) const result = await bchjs.Electrumx.txData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "details") assert.property(result, 'details')
assert.isObject(result.details) assert.isObject(result.details)
assert.property(result.details, "blockhash") assert.property(result.details, 'blockhash')
assert.property(result.details, "hash") assert.property(result.details, 'hash')
assert.property(result.details, "hex") assert.property(result.details, 'hex')
assert.property(result.details, "vin") assert.property(result.details, 'vin')
assert.property(result.details, "vout") assert.property(result.details, 'vout')
assert.equal(result.details.hash, txid) assert.equal(result.details.hash, txid)
}) })
it(`should POST details for an array of transactions`, async () => { it('should POST details for an array of transactions', async () => {
const txids = [ const txids = [
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee", '76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee',
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee" '76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee'
] ]
const result = await bchjs.Electrumx.txData(txids) const result = await bchjs.Electrumx.txData(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "transactions") assert.property(result, 'transactions')
assert.isArray(result.transactions) assert.isArray(result.transactions)
assert.property(result.transactions[0], "txid") assert.property(result.transactions[0], 'txid')
assert.property(result.transactions[0], "details") assert.property(result.transactions[0], 'details')
assert.property(result.transactions[0].details, "blockhash") assert.property(result.transactions[0].details, 'blockhash')
assert.property(result.transactions[0].details, "hash") assert.property(result.transactions[0].details, 'hash')
assert.property(result.transactions[0].details, "hex") assert.property(result.transactions[0].details, 'hex')
assert.property(result.transactions[0].details, "vin") assert.property(result.transactions[0].details, 'vin')
assert.property(result.transactions[0].details, "vout") assert.property(result.transactions[0].details, 'vout')
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const txids = [] const txids = []
for (let i = 0; i < 25; i++) { for (let i = 0; i < 25; i++) {
txids.push( txids.push(
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee" '76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee'
) )
} }
await bchjs.Electrumx.txData(txids) await bchjs.Electrumx.txData(txids)
console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe(`#broadcast`, () => { describe('#broadcast', () => {
it(`should broadcast a single transaction`, async () => { it('should broadcast a single transaction', async () => {
const txHex = const txHex =
"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000" '020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000'
try { try {
await bchjs.Electrumx.broadcast(txHex) await bchjs.Electrumx.broadcast(txHex)
} catch (err) { } catch (err) {
assert.property(err, "success") assert.property(err, 'success')
assert.equal(err.success, false) assert.equal(err.success, false)
assert.include( assert.include(
err.error, err.error.error,
"the transaction was rejected by network rules" 'the transaction was rejected by network rules'
) )
} }
}) })
}) })
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
-147
View File
@@ -1,147 +0,0 @@
const chai = require("chai")
const assert = chai.assert
const sinon = require("sinon")
const BCHJS = require("../../../../src/bch-js")
const bchjs = new BCHJS({ ninsightURL: "https://trest.bitcoin.com/v2" })
describe(`#Ninsight`, () => {
let sandbox
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
sandbox = sinon.createSandbox()
})
afterEach(() => sandbox.restore())
describe(`#utxo`, () => {
it(`should GET utxos for a single address`, async () => {
const addr = "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr"
const result = await bchjs.Ninsight.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result[0], "utxos")
assert.property(result[0], "legacyAddress")
assert.property(result[0], "cashAddress")
assert.property(result[0], "slpAddress")
assert.property(result[0], "scriptPubKey")
assert.property(result[0], "asm")
assert.isArray(result[0].utxos)
assert.property(result[0].utxos[0], "txid")
assert.property(result[0].utxos[0], "vout")
assert.property(result[0].utxos[0], "amount")
assert.property(result[0].utxos[0], "satoshis")
assert.property(result[0].utxos[0], "height")
assert.property(result[0].utxos[0], "confirmations")
})
it(`should POST utxo details for an array of addresses`, async () => {
const addr = [
"bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr",
"bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"
]
const result = await bchjs.Ninsight.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isArray(result[0].utxos)
assert.property(result[0], "utxos")
assert.property(result[0], "legacyAddress")
assert.property(result[0], "cashAddress")
assert.property(result[0], "slpAddress")
assert.property(result[0], "scriptPubKey")
assert.property(result[0], "asm")
// assert.hasAnyKeys(result[0][0], [
// "txid",
// "vout",
// "value",
// "height",
// "confirmations"
// ])
})
})
describe(`#transactions`, () => {
it(`should POST transactions history for a single address`, async () => {
const addr = "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr"
const result = await bchjs.Ninsight.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "cashAddress")
assert.property(result[0], "legacyAddress")
assert.property(result[0], "txs")
assert.isArray(result[0].txs)
assert.property(result[0].txs[0], "txid")
assert.property(result[0].txs[0], "vin")
assert.property(result[0].txs[0], "vout")
})
it(`should POST transactions history for an array of addresses`, async () => {
const addr = [
"bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr",
"bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"
]
const result = await bchjs.Ninsight.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "cashAddress")
assert.property(result[0], "legacyAddress")
assert.property(result[0], "txs")
assert.isArray(result[0].txs)
assert.property(result[0].txs[0], "txid")
assert.property(result[0].txs[0], "vin")
assert.property(result[0].txs[0], "vout")
})
})
describe(`#txDetails`, () => {
it(`should POST transactions details for a single address`, async () => {
const txid =
"76856d82e00b2696acd8d989e1fa6c46b431005046a285ce905814cac0ff8fea"
const result = await bchjs.Ninsight.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "txid")
assert.property(result[0], "version")
assert.property(result[0], "locktime")
assert.property(result[0], "vin")
assert.property(result[0], "vout")
assert.property(result[0], "blockhash")
assert.property(result[0], "blockheight")
assert.property(result[0], "confirmations")
assert.property(result[0], "time")
assert.property(result[0], "blocktime")
assert.property(result[0], "valueOut")
assert.property(result[0], "size")
})
it(`should POST transactions details for an array of addresses`, async () => {
const txid = [
"f191d1062789d7071da6f2168ba306869a829294f6b1c09d03492db4ca3a8e77",
"76856d82e00b2696acd8d989e1fa6c46b431005046a285ce905814cac0ff8fea"
]
const result = await bchjs.Ninsight.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "txid")
assert.property(result[0], "vin")
assert.property(result[0], "vout")
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+102 -102
View File
@@ -5,143 +5,143 @@
TODO TODO
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const RESTURL = process.env.RESTURL const RESTURL = process.env.RESTURL
? process.env.RESTURL ? process.env.RESTURL
: `https://testnet3.fullstack.cash/v4/` : 'https://testnet3.fullstack.cash/v5/'
// if (process.env.RESTURL) RESTURL = process.env.RESTURL // if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../../src/bch-js") const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` }) // const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN }) const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
depth: 3 depth: 3
} }
describe("#rawtransaction", () => { describe('#rawtransaction', () => {
beforeEach(async () => { beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
describe("#decodeRawTransaction", () => { describe('#decodeRawTransaction', () => {
it("should decode tx for a single hex", async () => { it('should decode tx for a single hex', async () => {
const hex = const hex =
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" '0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000'
const result = await bchjs.RawTransactions.decodeRawTransaction(hex) const result = await bchjs.RawTransactions.decodeRawTransaction(hex)
//console.log(`result ${JSON.stringify(result, null, 2)}`) // console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [ assert.hasAnyKeys(result, [
"txid", 'txid',
"hash", 'hash',
"size", 'size',
"version", 'version',
"locktime", 'locktime',
"vin", 'vin',
"vout" 'vout'
]) ])
assert.isArray(result.vin) assert.isArray(result.vin)
assert.isArray(result.vout) assert.isArray(result.vout)
}) })
it("should decode an array of tx hexes", async () => { it('should decode an array of tx hexes', async () => {
const hexes = [ const hexes = [
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000", '0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000',
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" '0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000'
] ]
const result = await bchjs.RawTransactions.decodeRawTransaction(hexes) const result = await bchjs.RawTransactions.decodeRawTransaction(hexes)
//console.log(`result ${JSON.stringify(result, null, 2)}`) // console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.hasAnyKeys(result[0], [ assert.hasAnyKeys(result[0], [
"txid", 'txid',
"hash", 'hash',
"size", 'size',
"version", 'version',
"locktime", 'locktime',
"vin", 'vin',
"vout" 'vout'
]) ])
assert.isArray(result[0].vin) assert.isArray(result[0].vin)
assert.isArray(result[0].vout) assert.isArray(result[0].vout)
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const data = [] const data = []
for (let i = 0; i < 25; i++) { for (let i = 0; i < 25; i++) {
data.push( data.push(
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" '0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000'
) )
} }
const result = await bchjs.RawTransactions.decodeRawTransaction(data) const result = await bchjs.RawTransactions.decodeRawTransaction(data)
console.log(`result: ${util.inspect(result)}`) console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe("#getRawTransaction", () => { describe('#getRawTransaction', () => {
it("should decode a single txid, with concise output", async () => { it('should decode a single txid, with concise output', async () => {
const txid = const txid =
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04" '1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
const verbose = false const verbose = false
const result = await bchjs.RawTransactions.getRawTransaction( const result = await bchjs.RawTransactions.getRawTransaction(
txid, txid,
verbose verbose
) )
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isString(result) assert.isString(result)
}) })
it("should decode a single txid, with verbose output", async () => { it('should decode a single txid, with verbose output', async () => {
const txid = const txid =
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04" '1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
const verbose = true const verbose = true
const result = await bchjs.RawTransactions.getRawTransaction( const result = await bchjs.RawTransactions.getRawTransaction(
txid, txid,
verbose verbose
) )
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [ assert.hasAnyKeys(result, [
"hex", 'hex',
"txid", 'txid',
"hash", 'hash',
"size", 'size',
"version", 'version',
"locktime", 'locktime',
"vin", 'vin',
"vout", 'vout',
"blockhash", 'blockhash',
"confirmations", 'confirmations',
"time", 'time',
"blocktime" 'blocktime'
]) ])
assert.isArray(result.vin) assert.isArray(result.vin)
assert.isArray(result.vout) assert.isArray(result.vout)
}) })
it("should decode an array of txids, with a concise output", async () => { it('should decode an array of txids, with a concise output', async () => {
const txid = [ const txid = [
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04", '1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04',
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04" '1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
] ]
const verbose = false const verbose = false
@@ -149,16 +149,16 @@ describe("#rawtransaction", () => {
txid, txid,
verbose verbose
) )
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.isString(result[0]) assert.isString(result[0])
}) })
it("should decode an array of txids, with a verbose output", async () => { it('should decode an array of txids, with a verbose output', async () => {
const txid = [ const txid = [
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04", '1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04',
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04" '1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
] ]
const verbose = true const verbose = true
@@ -166,54 +166,54 @@ describe("#rawtransaction", () => {
txid, txid,
verbose verbose
) )
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.hasAnyKeys(result[0], [ assert.hasAnyKeys(result[0], [
"hex", 'hex',
"txid", 'txid',
"hash", 'hash',
"size", 'size',
"version", 'version',
"locktime", 'locktime',
"vin", 'vin',
"vout", 'vout',
"blockhash", 'blockhash',
"confirmations", 'confirmations',
"time", 'time',
"blocktime" 'blocktime'
]) ])
assert.isArray(result[0].vin) assert.isArray(result[0].vin)
assert.isArray(result[0].vout) assert.isArray(result[0].vout)
}) })
it(`should throw error on array size limit`, async () => { it('should throw error on array size limit', async () => {
try { try {
const dataMock = const dataMock =
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04" '1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
const data = [] const data = []
for (let i = 0; i < 25; i++) data.push(dataMock) for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.RawTransactions.getRawTransaction(data) const result = await bchjs.RawTransactions.getRawTransaction(data)
console.log(`result: ${util.inspect(result)}`) console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe("#decodeScript", () => { describe('#decodeScript', () => {
it("should decode script for a single hex", async () => { it('should decode script for a single hex', async () => {
const hex = const hex =
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16" '4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16'
const result = await bchjs.RawTransactions.decodeScript(hex) const result = await bchjs.RawTransactions.decodeScript(hex)
//console.log(`result ${JSON.stringify(result, null, 2)}`) // console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ["asm", "type", "p2sh"]) assert.hasAllKeys(result, ['asm', 'type', 'p2sh'])
}) })
// CT 2/20/19 - Waiting for this PR to be merged complete the test: // CT 2/20/19 - Waiting for this PR to be merged complete the test:
@@ -237,29 +237,29 @@ describe("#rawtransaction", () => {
below expect error messages returned from the server, but at least test below expect error messages returned from the server, but at least test
that the server is responding on those endpoints, and responds consistently. that the server is responding on those endpoints, and responds consistently.
*/ */
describe("sendRawTransaction", () => { describe('sendRawTransaction', () => {
it("should send a single transaction hex", async () => { it('should send a single transaction hex', async () => {
try { try {
const hex = const hex =
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
await bchjs.RawTransactions.sendRawTransaction(hex) await bchjs.RawTransactions.sendRawTransaction(hex)
//console.log(`result ${JSON.stringify(result, null, 2)}`) // console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
//console.log(`err: ${util.inspect(err)}`) // console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"]) assert.hasAllKeys(err, ['error'])
assert.include(err.error, "Missing inputs") assert.include(err.error, 'Missing inputs')
} }
}) })
it("should send an array of tx hexes", async () => { it('should send an array of tx hexes', async () => {
try { try {
const hexes = [ const hexes = [
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000", '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000',
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
] ]
const result = await bchjs.RawTransactions.sendRawTransaction(hexes) const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
@@ -267,30 +267,30 @@ describe("#rawtransaction", () => {
} catch (err) { } catch (err) {
// console.log(`err: ${util.inspect(err)}`) // console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"]) assert.hasAllKeys(err, ['error'])
assert.include(err.error, "Missing inputs") assert.include(err.error, 'Missing inputs')
} }
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const dataMock = const dataMock =
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
const data = [] const data = []
for (let i = 0; i < 25; i++) data.push(dataMock) for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.RawTransactions.sendRawTransaction(data) const result = await bchjs.RawTransactions.sendRawTransaction(data)
console.log(`result: ${util.inspect(result)}`) console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+62 -75
View File
@@ -2,132 +2,119 @@
Integration tests for the bchjs covering SLP tokens. Integration tests for the bchjs covering SLP tokens.
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const RESTURL = process.env.RESTURL const RESTURL = process.env.RESTURL
? process.env.RESTURL ? process.env.RESTURL
: `https://testnet3.fullstack.cash/v4/` : 'https://testnet3.fullstack.cash/v5/'
// if (process.env.RESTURL) RESTURL = process.env.RESTURL // if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../../src/bch-js") const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` }) // const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN }) const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
depth: 1 depth: 1
} }
describe(`#SLP`, () => { describe('#SLP', () => {
beforeEach(async () => { beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits. // Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
describe("#util", () => { describe('#util', () => {
it(`should get information on the Oasis token`, async () => { it('should get information on the Oasis token', async () => {
const tokenId = `a371e9934c7695d08a5eb7f31d3bceb4f3644860cc67520cda1e149423b9ec39` const tokenId =
'a371e9934c7695d08a5eb7f31d3bceb4f3644860cc67520cda1e149423b9ec39'
const result = await bchjs.SLP.Utils.list(tokenId) const result = await bchjs.SLP.Utils.list(tokenId)
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [ assert.hasAnyKeys(result, [
"decimals", 'decimals',
"timestamp", 'timestamp',
"timestamp_unix", 'timestamp_unix',
"versionType", 'versionType',
"documentUri", 'documentUri',
"symbol", 'symbol',
"name", 'name',
"containsBaton", 'containsBaton',
"id", 'id',
"documentHash", 'documentHash',
"initialTokenQty", 'initialTokenQty',
"blockCreated", 'blockCreated',
"blockLastActiveSend", 'blockLastActiveSend',
"blockLastActiveMint", 'blockLastActiveMint',
"txnsSinceGenesis", 'txnsSinceGenesis',
"validAddress", 'validAddress',
"totalMinted", 'totalMinted',
"totalBurned", 'totalBurned',
"circulatingSupply", 'circulatingSupply',
"mintingBatonStatus" 'mintingBatonStatus'
]) ])
}) })
}) })
describe("#decodeOpReturn", () => { describe('#decodeOpReturn', () => {
it("should decode the OP_RETURN for a SEND txid", async () => { it('should decode the OP_RETURN for a SEND txid', async () => {
const txid = const txid =
"ad28116e0818339342dddfc5f58ca8a5379ceb9679b4e4cbd72f4de905415ec1" 'ad28116e0818339342dddfc5f58ca8a5379ceb9679b4e4cbd72f4de905415ec1'
const result = await bchjs.SLP.Utils.decodeOpReturn(txid) const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ["amounts", "tokenType", "tokenId", "txType"]) assert.hasAllKeys(result, ['amounts', 'tokenType', 'tokenId', 'txType'])
}) })
}) })
describe("#balancesForAddress", () => { describe('#balancesForAddress', () => {
it(`should fetch all balances for address: slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9`, async () => { it('should fetch all balances for address: slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9', async () => {
// Mock the call to rest.bitcoin.com
if (process.env.TEST === "unit") {
sandbox
.stub(axios, "get")
.resolves({ data: mockData.balancesForAddress })
}
const balances = await bchjs.SLP.Utils.balancesForAddress( const balances = await bchjs.SLP.Utils.balancesForAddress(
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9" 'slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9'
) )
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`) // console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
assert.isArray(balances) assert.isArray(balances)
assert.hasAllKeys(balances[0], [ assert.hasAllKeys(balances[0], [
"tokenId", 'tokenId',
"balanceString", 'balanceString',
"balance", 'balance',
"decimalCount", 'decimalCount',
"slpAddress" 'slpAddress'
]) ])
}) })
it(`should fetch balances for multiple addresses`, async () => { it('should fetch balances for multiple addresses', async () => {
const addresses = [ const addresses = [
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9", 'slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9',
"slptest:qr5uy4h8ysyhwkp9s245scckdnc7teyjqc5ft2z43h" 'slptest:qr5uy4h8ysyhwkp9s245scckdnc7teyjqc5ft2z43h'
] ]
// Mock the call to rest.bitcoin.com
if (process.env.TEST === "unit") {
sandbox
.stub(axios, "post")
.resolves({ data: mockData.balancesForAddresses })
}
const balances = await bchjs.SLP.Utils.balancesForAddress(addresses) const balances = await bchjs.SLP.Utils.balancesForAddress(addresses)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`) // console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
assert.isArray(balances) assert.isArray(balances)
assert.isArray(balances[0]) assert.isArray(balances[0])
assert.hasAllKeys(balances[0][0], [ assert.hasAllKeys(balances[0][0], [
"tokenId", 'tokenId',
"balanceString", 'balanceString',
"balance", 'balance',
"decimalCount", 'decimalCount',
"slpAddress" 'slpAddress'
]) ])
}) })
}) })
describe("#tokenUtxoDetails", () => { describe('#tokenUtxoDetails', () => {
it("should hydrate UTXOs", async () => { it('should hydrate UTXOs', async () => {
const bchAddr = bchjs.SLP.Address.toCashAddress( const bchAddr = bchjs.SLP.Address.toCashAddress(
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9" 'slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9'
) )
const utxos = await bchjs.Electrumx.utxo([bchAddr]) const utxos = await bchjs.Electrumx.utxo([bchAddr])
@@ -145,12 +132,12 @@ describe(`#SLP`, () => {
}) })
}) })
describe("#hydrateUtxos", () => { describe('#hydrateUtxos', () => {
// This test will error out if the LOCAL_RESTURL settings is not set properly // This test will error out if the LOCAL_RESTURL settings is not set properly
// in bch-api. // in bch-api.
it("should hydrate UTXOs", async () => { it('should hydrate UTXOs', async () => {
const bchAddr = bchjs.SLP.Address.toCashAddress( const bchAddr = bchjs.SLP.Address.toCashAddress(
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9" 'slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9'
) )
const utxos = await bchjs.Electrumx.utxo([bchAddr]) const utxos = await bchjs.Electrumx.utxo([bchAddr])
@@ -166,10 +153,10 @@ describe(`#SLP`, () => {
}) })
}) })
describe("#validateTxid2", () => { describe('#validateTxid2', () => {
it("should validate a token txid", async () => { it('should validate a token txid', async () => {
const txid = const txid =
"ad28116e0818339342dddfc5f58ca8a5379ceb9679b4e4cbd72f4de905415ec1" 'ad28116e0818339342dddfc5f58ca8a5379ceb9679b4e4cbd72f4de905415ec1'
const validated = await bchjs.SLP.Utils.validateTxid(txid) const validated = await bchjs.SLP.Utils.validateTxid(txid)
// console.log(validated) // console.log(validated)
@@ -180,6 +167,6 @@ describe(`#SLP`, () => {
}) })
// Promise-based sleep function // Promise-based sleep function
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
export RESTURL=https://free-test.fullstack.cash/v4/ export RESTURL=https://free-test.fullstack.cash/v5/
export NETWORK=testnet export NETWORK=testnet
export IS_USING_FREE_TIER=true export IS_USING_FREE_TIER=true
+41 -41
View File
@@ -3,107 +3,107 @@
rest.bitcoin.com. rest.bitcoin.com.
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const RESTURL = process.env.RESTURL const RESTURL = process.env.RESTURL
? process.env.RESTURL ? process.env.RESTURL
: `https://testnet3.fullstack.cash/v4/` : 'https://testnet3.fullstack.cash/v5/'
// if (process.env.RESTURL) RESTURL = process.env.RESTURL // if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../../src/bch-js") const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` }) // const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN }) const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
depth: 3 depth: 3
} }
describe(`#util`, () => { describe('#util', () => {
beforeEach(async () => { beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
describe(`#validateAddress`, () => { describe('#validateAddress', () => {
it(`should return false for testnet addr on mainnet`, async () => { it('should return false for testnet addr on mainnet', async () => {
const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peu` const address = 'bitcoincash:qp4k8fjtgunhdr7yq30ha4peu'
const result = await bchjs.Util.validateAddress(address) const result = await bchjs.Util.validateAddress(address)
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ["isvalid"]) assert.hasAllKeys(result, ['isvalid'])
assert.equal(result.isvalid, false) assert.equal(result.isvalid, false)
}) })
it(`should return false for bad address`, async () => { it('should return false for bad address', async () => {
const address = `bchtest:qqqk4y6lsl5da64sg53xezmplyu5kmpyz2ysaa5y` const address = 'bchtest:qqqk4y6lsl5da64sg53xezmplyu5kmpyz2ysaa5y'
const result = await bchjs.Util.validateAddress(address) const result = await bchjs.Util.validateAddress(address)
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ["isvalid"]) assert.hasAllKeys(result, ['isvalid'])
assert.equal(result.isvalid, false) assert.equal(result.isvalid, false)
}) })
it(`should validate valid address`, async () => { it('should validate valid address', async () => {
const address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y` const address = 'bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y'
const result = await bchjs.Util.validateAddress(address) const result = await bchjs.Util.validateAddress(address)
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [ assert.hasAnyKeys(result, [
"isvalid", 'isvalid',
"address", 'address',
"scriptPubKey", 'scriptPubKey',
//"ismine", // "ismine",
//"iswatchonly", // "iswatchonly",
"isscript" 'isscript'
]) ])
assert.equal(result.isvalid, true) assert.equal(result.isvalid, true)
}) })
it(`should validate an array of addresses`, async () => { it('should validate an array of addresses', async () => {
const address = [ const address = [
`bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`, 'bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y',
`bchtest:pq6k9969f6v6sg7a75jkru4n7wn9sknv5cztcp0dnh` 'bchtest:pq6k9969f6v6sg7a75jkru4n7wn9sknv5cztcp0dnh'
] ]
const result = await bchjs.Util.validateAddress(address) const result = await bchjs.Util.validateAddress(address)
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.hasAnyKeys(result[0], [ assert.hasAnyKeys(result[0], [
"isvalid", 'isvalid',
"address", 'address',
"scriptPubKey", 'scriptPubKey',
//"ismine", // "ismine",
//"iswatchonly", // "iswatchonly",
"isscript" 'isscript'
]) ])
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const dataMock = `bchtest:pq6k9969f6v6sg7a75jkru4n7wn9sknv5cztcp0dnh` const dataMock = 'bchtest:pq6k9969f6v6sg7a75jkru4n7wn9sknv5cztcp0dnh'
const data = [] const data = []
for (let i = 0; i < 25; i++) data.push(dataMock) for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.Util.validateAddress(data) const result = await bchjs.Util.validateAddress(data)
console.log(`result: ${util.inspect(result)}`) console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+8 -8
View File
@@ -2,26 +2,26 @@
Integration tests for bchjs control library. Integration tests for bchjs control library.
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
describe(`#control`, () => { describe('#control', () => {
beforeEach(async () => { beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
describe(`#getNetworkInfo`, () => { describe('#getNetworkInfo', () => {
it("should get info on the full node", async () => { it('should get info on the full node', async () => {
const result = await bchjs.Control.getNetworkInfo() const result = await bchjs.Control.getNetworkInfo()
console.log(`result: ${JSON.stringify(result, null, 2)}`) console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "version") assert.property(result, 'version')
}) })
}) })
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+157 -130
View File
@@ -1,191 +1,199 @@
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const sinon = require("sinon") const sinon = require('sinon')
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
describe(`#ElectrumX`, () => { describe('#ElectrumX', () => {
let sandbox let sandbox
beforeEach(async () => { beforeEach(async () => {
sandbox = sinon.createSandbox() sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
describe(`#utxo`, () => { describe('#utxo', () => {
it(`should GET utxos for a single address`, async () => { it('should GET utxos for a single address', async () => {
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const result = await bchjs.Electrumx.utxo(addr) const result = await bchjs.Electrumx.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "utxos") assert.property(result, 'utxos')
assert.isArray(result.utxos) assert.isArray(result.utxos)
assert.property(result.utxos[0], "height") assert.property(result.utxos[0], 'height')
assert.property(result.utxos[0], "tx_hash") assert.property(result.utxos[0], 'tx_hash')
assert.property(result.utxos[0], "tx_pos") assert.property(result.utxos[0], 'tx_pos')
assert.property(result.utxos[0], "value") assert.property(result.utxos[0], 'value')
}) })
it(`should POST request for UTXOs for an array of addresses`, async () => { it('should POST request for UTXOs for an array of addresses', async () => {
const addr = [ const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
] ]
const result = await bchjs.Electrumx.utxo(addr) const result = await bchjs.Electrumx.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "utxos") assert.property(result, 'utxos')
assert.isArray(result.utxos) assert.isArray(result.utxos)
assert.property(result.utxos[0], "utxos") assert.property(result.utxos[0], 'utxos')
assert.isArray(result.utxos[0].utxos) assert.isArray(result.utxos[0].utxos)
assert.property(result.utxos[0], "address") assert.property(result.utxos[0], 'address')
assert.property(result.utxos[0].utxos[0], "height") assert.property(result.utxos[0].utxos[0], 'height')
assert.property(result.utxos[0].utxos[0], "tx_hash") assert.property(result.utxos[0].utxos[0], 'tx_hash')
assert.property(result.utxos[0].utxos[0], "tx_pos") assert.property(result.utxos[0].utxos[0], 'tx_pos')
assert.property(result.utxos[0].utxos[0], "value") assert.property(result.utxos[0].utxos[0], 'value')
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const addr = [] const addr = []
for (let i = 0; i < 25; i++) for (let i = 0; i < 25; i++) {
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") addr.push('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf')
}
const result = await bchjs.Electrumx.utxo(addr) // const result = await bchjs.Electrumx.utxo(addr)
await bchjs.Electrumx.utxo(addr)
console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe(`#balance`, () => { describe('#balance', () => {
it(`should GET balance for a single address`, async () => { it('should GET balance for a single address', async () => {
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const result = await bchjs.Electrumx.balance(addr) const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "balance") assert.property(result, 'balance')
assert.property(result.balance, "confirmed") assert.property(result.balance, 'confirmed')
assert.property(result.balance, "unconfirmed") assert.property(result.balance, 'unconfirmed')
}) })
it(`should POST request for balances for an array of addresses`, async () => { it('should POST request for balances for an array of addresses', async () => {
const addr = [ const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
] ]
const result = await bchjs.Electrumx.balance(addr) const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "balances") assert.property(result, 'balances')
assert.isArray(result.balances) assert.isArray(result.balances)
assert.property(result.balances[0], "address") assert.property(result.balances[0], 'address')
assert.property(result.balances[0], "balance") assert.property(result.balances[0], 'balance')
assert.property(result.balances[0].balance, "confirmed") assert.property(result.balances[0].balance, 'confirmed')
assert.property(result.balances[0].balance, "unconfirmed") assert.property(result.balances[0].balance, 'unconfirmed')
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const addr = [] const addr = []
for (let i = 0; i < 25; i++) for (let i = 0; i < 25; i++) {
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") addr.push('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf')
}
const result = await bchjs.Electrumx.balance(addr) // const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${util.inspect(result)}`)
console.log(`result: ${util.inspect(result)}`) await bchjs.Electrumx.balance(addr)
assert.equal(true, false, "Unexpected result!")
assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe(`#transactions`, () => { describe('#transactions', () => {
it(`should GET transaction history for a single address`, async () => { it('should GET transaction history for a single address', async () => {
const addr = "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" const addr = 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
const result = await bchjs.Electrumx.transactions(addr) const result = await bchjs.Electrumx.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "transactions") assert.property(result, 'transactions')
assert.isArray(result.transactions) assert.isArray(result.transactions)
assert.property(result.transactions[0], "height") assert.property(result.transactions[0], 'height')
assert.property(result.transactions[0], "tx_hash") assert.property(result.transactions[0], 'tx_hash')
}) })
it(`should POST request for transaction history for an array of addresses`, async () => { it('should POST request for transaction history for an array of addresses', async () => {
const addr = [ const addr = [
"bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj", 'bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj',
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
] ]
const result = await bchjs.Electrumx.transactions(addr) const result = await bchjs.Electrumx.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "transactions") assert.property(result, 'transactions')
assert.isArray(result.transactions) assert.isArray(result.transactions)
assert.property(result.transactions[0], "address") assert.property(result.transactions[0], 'address')
assert.property(result.transactions[0], "transactions") assert.property(result.transactions[0], 'transactions')
assert.isArray(result.transactions[0].transactions) assert.isArray(result.transactions[0].transactions)
assert.property(result.transactions[0].transactions[0], "height") assert.property(result.transactions[0].transactions[0], 'height')
assert.property(result.transactions[0].transactions[0], "tx_hash") assert.property(result.transactions[0].transactions[0], 'tx_hash')
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const addr = [] const addr = []
for (let i = 0; i < 25; i++) for (let i = 0; i < 25; i++) {
addr.push("bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0") addr.push('bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0')
}
const result = await bchjs.Electrumx.transactions(addr) // const result = await bchjs.Electrumx.transactions(addr)
// console.log(`result: ${util.inspect(result)}`)
console.log(`result: ${util.inspect(result)}`) await bchjs.Electrumx.transactions(addr)
assert.equal(true, false, "Unexpected result!")
assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe(`#unconfirmed`, () => { describe('#unconfirmed', () => {
// These tests won't work because unconfirmed transactions are transient in nature. // These tests won't work because unconfirmed transactions are transient in nature.
/* /*
it(`should GET unconfirmed UTXOs (mempool) for a single address`, async () => { it(`should GET unconfirmed UTXOs (mempool) for a single address`, async () => {
@@ -230,137 +238,156 @@ describe(`#ElectrumX`, () => {
}) })
*/ */
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const addr = [] const addr = []
for (let i = 0; i < 25; i++) for (let i = 0; i < 25; i++) {
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") addr.push('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf')
}
await bchjs.Electrumx.unconfirmed(addr) await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe(`#blockHeader`, () => { describe('#blockHeader', () => {
it(`should GET block headers for given height and count`, async () => { it('should GET block headers for given height and count', async () => {
const height = 42 const height = 42
const count = 2 const count = 2
const result = await bchjs.Electrumx.blockHeader(height, count) const result = await bchjs.Electrumx.blockHeader(height, count)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "headers") assert.property(result, 'headers')
assert.isArray(result.headers) assert.isArray(result.headers)
assert.equal(result.headers.length, 2) assert.equal(result.headers.length, 2)
}) })
it(`should GET block headers for given height with default count = 1`, async () => { it('should GET block headers for given height with default count = 1', async () => {
const height = 42 const height = 42
const result = await bchjs.Electrumx.blockHeader(height) const result = await bchjs.Electrumx.blockHeader(height)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "headers") assert.property(result, 'headers')
assert.isArray(result.headers) assert.isArray(result.headers)
assert.equal(result.headers.length, 1) assert.equal(result.headers.length, 1)
}) })
}) })
describe(`#txData`, () => { describe('#txData', () => {
it(`should GET details for a single transaction`, async () => { it('should GET details for a single transaction', async () => {
const txid = const txid =
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'
const result = await bchjs.Electrumx.txData(txid) const result = await bchjs.Electrumx.txData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "details") assert.property(result, 'details')
assert.isObject(result.details) assert.isObject(result.details)
assert.property(result.details, "blockhash") assert.property(result.details, 'blockhash')
assert.property(result.details, "hash") assert.property(result.details, 'hash')
assert.property(result.details, "hex") assert.property(result.details, 'hex')
assert.property(result.details, "vin") assert.property(result.details, 'vin')
assert.property(result.details, "vout") assert.property(result.details, 'vout')
assert.equal(result.details.hash, txid) assert.equal(result.details.hash, txid)
}) })
it(`should POST details for an array of transactions`, async () => { it('should POST details for an array of transactions', async () => {
const txids = [ const txids = [
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251", '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251',
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'
] ]
const result = await bchjs.Electrumx.txData(txids) const result = await bchjs.Electrumx.txData(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "transactions") assert.property(result, 'transactions')
assert.isArray(result.transactions) assert.isArray(result.transactions)
assert.property(result.transactions[0], "txid") assert.property(result.transactions[0], 'txid')
assert.property(result.transactions[0], "details") assert.property(result.transactions[0], 'details')
assert.property(result.transactions[0].details, "blockhash") assert.property(result.transactions[0].details, 'blockhash')
assert.property(result.transactions[0].details, "hash") assert.property(result.transactions[0].details, 'hash')
assert.property(result.transactions[0].details, "hex") assert.property(result.transactions[0].details, 'hex')
assert.property(result.transactions[0].details, "vin") assert.property(result.transactions[0].details, 'vin')
assert.property(result.transactions[0].details, "vout") assert.property(result.transactions[0].details, 'vout')
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const txids = [] const txids = []
for (let i = 0; i < 25; i++) { for (let i = 0; i < 25; i++) {
txids.push( txids.push(
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'
) )
} }
await bchjs.Electrumx.txData(txids) await bchjs.Electrumx.txData(txids)
console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe(`#broadcast`, () => { describe('#broadcast', () => {
it(`should broadcast a single transaction`, async () => { it('should broadcast a single transaction', async () => {
const txHex = const txHex =
"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000" '020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000'
try { try {
await bchjs.Electrumx.broadcast(txHex) await bchjs.Electrumx.broadcast(txHex)
} catch (err) { } catch (err) {
assert.property(err, "success") // console.log('err: ', err)
assert.property(err, 'success')
assert.equal(err.success, false) assert.equal(err.success, false)
assert.include( assert.include(
err.error, err.error.error,
"the transaction was rejected by network rules" 'the transaction was rejected by network rules'
) )
} }
}) })
}) })
describe('#sortAllTxs', () => {
it('should GET transaction history for a single address', async () => {
const addr = 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
const txs = await bchjs.Electrumx.transactions(addr)
const sortedTransactions = await bchjs.Electrumx.sortAllTxs(
txs.transactions
)
// console.log(
// `sortedTransactions: ${JSON.stringify(sortedTransactions, null, 2)}`
// )
assert.isAbove(sortedTransactions[0].height, sortedTransactions[1].height)
})
})
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+15 -15
View File
@@ -1,39 +1,39 @@
const assert = require("chai").assert const assert = require('chai').assert
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
describe("#Encryption", () => { describe('#Encryption', () => {
beforeEach(async () => { beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
describe("#getPubKey", () => { describe('#getPubKey', () => {
it("should get a public key", async () => { it('should get a public key', async () => {
const addr = "bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq" const addr = 'bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq'
const result = await bchjs.encryption.getPubKey(addr) const result = await bchjs.encryption.getPubKey(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "publicKey") assert.property(result, 'publicKey')
}) })
it("should report when public key can not be found", async () => { it('should report when public key can not be found', async () => {
const addr = "bitcoincash:qrgqqkky28jdkv3w0ctrah0mz3jcsnsklc34gtukrh" const addr = 'bitcoincash:qrgqqkky28jdkv3w0ctrah0mz3jcsnsklc34gtukrh'
const result = await bchjs.encryption.getPubKey(addr) const result = await bchjs.encryption.getPubKey(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, false) assert.equal(result.success, false)
assert.property(result, "publicKey") assert.property(result, 'publicKey')
assert.equal(result.publicKey, "not found") assert.equal(result.publicKey, 'not found')
}) })
}) })
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
-138
View File
@@ -1,138 +0,0 @@
const chai = require("chai")
const assert = chai.assert
const sinon = require("sinon")
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS({ ninsightURL: "https://rest.bitcoin.com/v2" })
describe(`#Ninsight`, () => {
let sandbox
beforeEach(async () => {
sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
afterEach(() => sandbox.restore())
describe(`#utxo`, () => {
it(`should GET utxos for a single address`, async () => {
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
const result = await bchjs.Ninsight.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result[0], "utxos")
assert.property(result[0], "legacyAddress")
assert.property(result[0], "cashAddress")
assert.property(result[0], "slpAddress")
assert.property(result[0], "scriptPubKey")
assert.property(result[0], "asm")
assert.isArray(result[0].utxos)
assert.property(result[0].utxos[0], "txid")
assert.property(result[0].utxos[0], "vout")
assert.property(result[0].utxos[0], "amount")
assert.property(result[0].utxos[0], "satoshis")
assert.property(result[0].utxos[0], "height")
assert.property(result[0].utxos[0], "confirmations")
})
it(`should POST utxo details for an array of addresses`, async () => {
const addr = [
"bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7",
"bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr"
]
const result = await bchjs.Ninsight.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isArray(result[0].utxos)
assert.property(result[0], "utxos")
assert.property(result[0], "legacyAddress")
assert.property(result[0], "cashAddress")
assert.property(result[0], "slpAddress")
assert.property(result[0], "scriptPubKey")
assert.property(result[0], "asm")
})
})
describe(`#transactions`, () => {
it(`should POST transactions history for a single address`, async () => {
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
const result = await bchjs.Ninsight.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "cashAddress")
assert.property(result[0], "legacyAddress")
assert.property(result[0], "txs")
assert.isArray(result[0].txs)
assert.property(result[0].txs[0], "txid")
assert.property(result[0].txs[0], "vin")
assert.property(result[0].txs[0], "vout")
})
it(`should POST transactions history for an array of addresses`, async () => {
const addr = [
"bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7",
"bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr"
]
const result = await bchjs.Ninsight.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "cashAddress")
assert.property(result[0], "legacyAddress")
assert.property(result[0], "txs")
assert.isArray(result[0].txs)
assert.property(result[0].txs[0], "txid")
assert.property(result[0].txs[0], "vin")
assert.property(result[0].txs[0], "vout")
})
})
describe(`#txDetails`, () => {
it(`should POST transactions details for a single address`, async () => {
const txid = "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33"
const result = await bchjs.Ninsight.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "txid")
assert.property(result[0], "version")
assert.property(result[0], "locktime")
assert.property(result[0], "vin")
assert.property(result[0], "vout")
assert.property(result[0], "blockhash")
assert.property(result[0], "blockheight")
assert.property(result[0], "confirmations")
assert.property(result[0], "time")
assert.property(result[0], "blocktime")
assert.property(result[0], "valueOut")
assert.property(result[0], "size")
})
it(`should POST transactions details for an array of addresses`, async () => {
const txid = [
"fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33",
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
]
const result = await bchjs.Ninsight.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "txid")
assert.property(result[0], "vin")
assert.property(result[0], "vout")
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
-89
View File
@@ -1,89 +0,0 @@
/*
tests for OpenBazaar library.
*/
const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
describe(`#OpenBazaar`, () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe(`#Balance`, () => {
it(`should GET balance for a single address`, async () => {
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
const result = await bchjs.OpenBazaar.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, [
"page",
"totalPages",
"itemsOnPage",
"addrStr",
"balance",
"totalReceived",
"totalSent",
"unconfirmedBalance",
"unconfirmedTxApperances",
"txApperances",
"transactions"
])
assert.isArray(result.transactions)
})
})
describe(`#utxo`, () => {
it(`should GET utxos for a single address`, async () => {
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
const result = await bchjs.OpenBazaar.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], [
"txid",
"vout",
"amount",
"height",
"confirmations",
"satoshis"
])
})
})
describe(`#tx`, () => {
it(`should GET transactions for a single address`, async () => {
const addr =
"2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7"
const result = await bchjs.OpenBazaar.tx(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, [
"txid",
"version",
"vin",
"vout",
"blockhash",
"blockheight",
"confirmations",
"blocktime",
"valueOut",
"valueIn",
"fees",
"hex",
"time"
])
assert.isArray(result.vin)
assert.isArray(result.vout)
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+25 -17
View File
@@ -1,23 +1,23 @@
const assert = require("chai").assert const assert = require('chai').assert
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
describe("#price", () => { describe('#price', () => {
beforeEach(async () => { beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
describe("#current", () => { describe('#current', () => {
describe("#single currency", () => { describe('#single currency', () => {
it("should get current price for single currency", async () => { it('should get current price for single currency', async () => {
const result = await bchjs.Price.current("usd") const result = await bchjs.Price.current('usd')
assert.notEqual(0, result) assert.notEqual(0, result)
}) })
}) })
}) })
describe("#getUsd", () => { describe('#getUsd', () => {
it("should get the USD price of BCH", async () => { it('should get the USD price of BCH', async () => {
const result = await bchjs.Price.getUsd() const result = await bchjs.Price.getUsd()
// console.log(result) // console.log(result)
@@ -25,26 +25,34 @@ describe("#price", () => {
}) })
}) })
describe("#getBchaUsd", () => { describe('#getBchaUsd', () => {
it("should get the USD price of BCHA", async () => { it('should get the USD price of BCHA', async () => {
const result = await bchjs.Price.getBchaUsd() const result = await bchjs.Price.getBchaUsd()
console.log(result) console.log(result)
assert.isNumber(result) assert.isNumber(result)
}) })
}) })
describe('#getBchUsd', () => {
it('should get the USD price of BCH', async () => {
const result = await bchjs.Price.getBchUsd()
console.log(result)
describe("#rates", () => { assert.isNumber(result)
it("should get the price of BCH in several currencies", async () => { })
})
describe('#rates', () => {
it('should get the price of BCH in several currencies', async () => {
const result = await bchjs.Price.rates() const result = await bchjs.Price.rates()
// console.log(result) // console.log(result)
assert.property(result, "USD") assert.property(result, 'USD')
assert.property(result, "CAD") assert.property(result, 'CAD')
}) })
}) })
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+149 -92
View File
@@ -1,140 +1,139 @@
/* /*
Integration tests for the bchjs. Only covers calls made to Integration tests for the bchjs.
rest.bitcoin.com.
TODO TODO
*/ */
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
// Inspect utility used for debugging. // Inspect utility used for debugging.
const util = require("util") const util = require('util')
util.inspect.defaultOptions = { util.inspect.defaultOptions = {
showHidden: true, showHidden: true,
colors: true, colors: true,
depth: 3 depth: 3
} }
describe("#rawtransaction", () => { describe('#rawtransaction', () => {
beforeEach(async () => { beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000) if (process.env.IS_USING_FREE_TIER) await sleep(1500)
}) })
describe("#decodeRawTransaction", () => { describe('#decodeRawTransaction', () => {
it("should decode tx for a single hex", async () => { it('should decode tx for a single hex', async () => {
const hex = const hex =
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" '0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000'
const result = await bchjs.RawTransactions.decodeRawTransaction(hex) const result = await bchjs.RawTransactions.decodeRawTransaction(hex)
//console.log(`result ${JSON.stringify(result, null, 2)}`) // console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [ assert.hasAnyKeys(result, [
"txid", 'txid',
"hash", 'hash',
"size", 'size',
"version", 'version',
"locktime", 'locktime',
"vin", 'vin',
"vout" 'vout'
]) ])
assert.isArray(result.vin) assert.isArray(result.vin)
assert.isArray(result.vout) assert.isArray(result.vout)
}) })
it("should decode an array of tx hexes", async () => { it('should decode an array of tx hexes', async () => {
const hexes = [ const hexes = [
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000", '0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000',
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" '0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000'
] ]
const result = await bchjs.RawTransactions.decodeRawTransaction(hexes) const result = await bchjs.RawTransactions.decodeRawTransaction(hexes)
//console.log(`result ${JSON.stringify(result, null, 2)}`) // console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.hasAnyKeys(result[0], [ assert.hasAnyKeys(result[0], [
"txid", 'txid',
"hash", 'hash',
"size", 'size',
"version", 'version',
"locktime", 'locktime',
"vin", 'vin',
"vout" 'vout'
]) ])
assert.isArray(result[0].vin) assert.isArray(result[0].vin)
assert.isArray(result[0].vout) assert.isArray(result[0].vout)
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const data = [] const data = []
for (let i = 0; i < 25; i++) { for (let i = 0; i < 25; i++) {
data.push( data.push(
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" '0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000'
) )
} }
const result = await bchjs.RawTransactions.decodeRawTransaction(data) const result = await bchjs.RawTransactions.decodeRawTransaction(data)
console.log(`result: ${util.inspect(result)}`) console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe("#getRawTransaction", () => { describe('#getRawTransaction', () => {
it("should decode a single txid, with concise output", async () => { it('should decode a single txid, with concise output', async () => {
const txid = const txid =
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1" '23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1'
const verbose = false const verbose = false
const result = await bchjs.RawTransactions.getRawTransaction( const result = await bchjs.RawTransactions.getRawTransaction(
txid, txid,
verbose verbose
) )
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isString(result) assert.isString(result)
}) })
it("should decode a single txid, with verbose output", async () => { it('should decode a single txid, with verbose output', async () => {
const txid = const txid =
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1" '23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1'
const verbose = true const verbose = true
const result = await bchjs.RawTransactions.getRawTransaction( const result = await bchjs.RawTransactions.getRawTransaction(
txid, txid,
verbose verbose
) )
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [ assert.hasAnyKeys(result, [
"hex", 'hex',
"txid", 'txid',
"hash", 'hash',
"size", 'size',
"version", 'version',
"locktime", 'locktime',
"vin", 'vin',
"vout", 'vout',
"blockhash", 'blockhash',
"confirmations", 'confirmations',
"time", 'time',
"blocktime" 'blocktime'
]) ])
assert.isArray(result.vin) assert.isArray(result.vin)
assert.isArray(result.vout) assert.isArray(result.vout)
}) })
it("should decode an array of txids, with a concise output", async () => { it('should decode an array of txids, with a concise output', async () => {
const txid = [ const txid = [
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1", '23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1',
"b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f" 'b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f'
] ]
const verbose = false const verbose = false
@@ -142,16 +141,16 @@ describe("#rawtransaction", () => {
txid, txid,
verbose verbose
) )
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.isString(result[0]) assert.isString(result[0])
}) })
it("should decode an array of txids, with a verbose output", async () => { it('should decode an array of txids, with a verbose output', async () => {
const txid = [ const txid = [
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1", '23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1',
"b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f" 'b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f'
] ]
const verbose = true const verbose = true
@@ -159,54 +158,53 @@ describe("#rawtransaction", () => {
txid, txid,
verbose verbose
) )
//console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result) assert.isArray(result)
assert.hasAnyKeys(result[0], [ assert.hasAnyKeys(result[0], [
"hex", 'hex',
"txid", 'txid',
"hash", 'hash',
"size", 'size',
"version", 'version',
"locktime", 'locktime',
"vin", 'vin',
"vout", 'vout',
"blockhash", 'blockhash',
"confirmations", 'confirmations',
"time", 'time',
"blocktime" 'blocktime'
]) ])
assert.isArray(result[0].vin) assert.isArray(result[0].vin)
assert.isArray(result[0].vout) assert.isArray(result[0].vout)
}) })
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const dataMock = const dataMock =
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1" '23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1'
const data = [] const data = []
for (let i = 0; i < 25; i++) data.push(dataMock) for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.RawTransactions.getRawTransaction(data) const result = await bchjs.RawTransactions.getRawTransaction(data)
console.log(`result: ${util.inspect(result)}`) console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.include(err.message, 'Array too large')
assert.include(err.error, "Array too large")
} }
}) })
}) })
describe("#decodeScript", () => { describe('#decodeScript', () => {
it("should decode script for a single hex", async () => { it('should decode script for a single hex', async () => {
const hex = const hex =
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16" '4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16'
const result = await bchjs.RawTransactions.decodeScript(hex) const result = await bchjs.RawTransactions.decodeScript(hex)
//console.log(`result ${JSON.stringify(result, null, 2)}`) // console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ["asm", "type", "p2sh"]) assert.hasAllKeys(result, ['asm', 'type', 'p2sh'])
}) })
// CT 2/20/19 - Waiting for this PR to be merged complete the test: // CT 2/20/19 - Waiting for this PR to be merged complete the test:
@@ -230,26 +228,85 @@ describe("#rawtransaction", () => {
below expect error messages returned from the server, but at least test below expect error messages returned from the server, but at least test
that the server is responding on those endpoints, and responds consistently. that the server is responding on those endpoints, and responds consistently.
*/ */
describe("sendRawTransaction", () => { describe('#sendRawTransaction', () => {
it(`should throw error on array size rate limit`, async () => { it('should throw error on array size rate limit', async () => {
try { try {
const dataMock = const dataMock =
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" '01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
const data = [] const data = []
for (let i = 0; i < 25; i++) data.push(dataMock) for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.RawTransactions.sendRawTransaction(data) await bchjs.RawTransactions.sendRawTransaction(data)
console.log(`result: ${util.inspect(result)}`) // console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
assert.hasAnyKeys(err, ["error"]) assert.hasAnyKeys(err, ['error'])
assert.include(err.error, "Array too large") assert.include(err.error, 'Array too large')
} }
}) })
}) })
describe('#_getInputAddrs', () => {
it('should return an array of input addresses', async () => {
// const txid = '32233db13f2ae6d82b6262f335643dccf09fc0bcfcef4bc3fbe023355f02e112'
const txid =
'05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
const txDetails = await bchjs.RawTransactions.getRawTransaction(
txid,
true
)
const result = await bchjs.RawTransactions._getInputAddrs(txDetails)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result.length, 1)
assert.property(result[0], 'vin')
assert.property(result[0], 'address')
assert.property(result[0], 'value')
})
})
describe('#getTxData', () => {
it('should return tx data with input addresses', async () => {
const txid =
'05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
const result = await bchjs.RawTransactions.getTxData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
})
})
// describe('#getTxDataSlp', () => {
// it('should return tx data with SLP information', async () => {
// const txid = 'b438855cfcab64516b44097d7212df9cdb99226c8d7c7ab504d35fcfd834cb5b'
//
// const result = await bchjs.RawTransactions.getTxDataSlp(txid)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.property(result.vin[0], 'address')
// assert.property(result.vin[0], 'tokenQty')
//
// assert.equal(result.vin[0].tokenQty, null)
// assert.equal(result.vin[1].tokenQty, '100000000000')
// })
//
// it('should handle non-slp tx', async () => {
// const txid = '04a6aca328af2445327015895b8da9766093b1989b52e477559759eb8072fc0a'
//
// const result = await bchjs.RawTransactions.getTxDataSlp(txid)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.equal(result, false)
// })
// })
}) })
function sleep(ms) { function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)) return new Promise(resolve => setTimeout(resolve, ms))
} }
+465 -324
View File
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,81 @@
/*
Integration tests for the transaction.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
describe('#Transaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
})
describe('#get', () => {
it('should get details about a non-SLP transaction', async () => {
const txid =
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[0].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, false)
})
it('should get details about a SLP transaction', async () => {
const txid =
'266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vout[0], 'tokenQty')
assert.equal(result.vout[0].tokenQty, null)
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result.vin[0], 'tokenQty')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, true)
})
// it('should get problematic transaction', async () => {
// const txid = 'a55515de32577e296c512840bcaabed5823bb773fb4f8fd8e5197cc96cbc54d1'
//
// const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// })
// TX a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe has
// an input TXID (bd84bc1dd5ecd976165892306992401272f6bedeb37d7b2cdbf74fc4a55967a6)
// that is also a valid SLP tx, but is unrelated. Both TXs pass DAG validation,
// but for separate tokens.
it('should get problematic transaction', async () => {
const txid = 'a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// The token ID should equal the txid for this Vin.
assert.equal(result.vin[2].txid, result.vin[2].tokenId)
})
})
})
-102
View File
@@ -1,102 +0,0 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
*/
const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
// Inspect utility used for debugging.
const util = require("util")
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe(`#util`, () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe(`#validateAddress`, () => {
it(`should return false for testnet addr on mainnet`, async () => {
const address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
const result = await bchjs.Util.validateAddress(address)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ["isvalid"])
assert.equal(result.isvalid, false)
})
it(`should return false for bad address`, async () => {
const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peu`
const result = await bchjs.Util.validateAddress(address)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ["isvalid"])
assert.equal(result.isvalid, false)
})
it(`should validate valid address`, async () => {
const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
const result = await bchjs.Util.validateAddress(address)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
"isvalid",
"address",
"scriptPubKey",
//"ismine",
//"iswatchonly",
"isscript"
])
assert.equal(result.isvalid, true)
})
it(`should validate an array of addresses`, async () => {
const address = [
`bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`,
`bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
]
const result = await bchjs.Util.validateAddress(address)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"isvalid",
"address",
"scriptPubKey",
//"ismine",
//"iswatchonly",
"isscript"
])
})
it(`should throw error on array size rate limit`, async () => {
try {
const dataMock = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
const data = []
for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.Util.validateAddress(data)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.hasAnyKeys(err, ["error"])
assert.include(err.error, "Array too large")
}
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+211 -208
View File
File diff suppressed because it is too large Load Diff
+26 -26
View File
@@ -1,11 +1,11 @@
// Public npm libraries // Public npm libraries
const assert = require("assert") const assert = require('assert')
// Mocks // Mocks
const fixtures = require("./fixtures/bitcoincash.json") const fixtures = require('./fixtures/bitcoincash.json')
// Unit under test (uut) // Unit under test (uut)
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
// const bchjs = new BCHJS() // const bchjs = new BCHJS()
let bchjs let bchjs
@@ -25,13 +25,13 @@ let bchjs
// * confirm xpriv generates WIF // * confirm xpriv generates WIF
// 6. More error test cases. // 6. More error test cases.
describe("#BitcoinCash", () => { describe('#BitcoinCash', () => {
beforeEach(() => { beforeEach(() => {
bchjs = new BCHJS() bchjs = new BCHJS()
}) })
describe("price conversion", () => { describe('price conversion', () => {
it("should exercise toBitcoinCash", () => { it('should exercise toBitcoinCash', () => {
fixtures.conversion.toBCH.satoshis.forEach(satoshi => { fixtures.conversion.toBCH.satoshis.forEach(satoshi => {
it(`should convert ${satoshi[0]} Satoshis to ${satoshi[1]} $BCH`, () => { it(`should convert ${satoshi[0]} Satoshis to ${satoshi[1]} $BCH`, () => {
assert.equal(bchjs.BitcoinCash.toBitcoinCash(satoshi[0]), satoshi[1]) assert.equal(bchjs.BitcoinCash.toBitcoinCash(satoshi[0]), satoshi[1])
@@ -57,7 +57,7 @@ describe("#BitcoinCash", () => {
}) })
}) })
it("should exercise toSatoshi", () => { it('should exercise toSatoshi', () => {
fixtures.conversion.toSatoshi.bch.forEach(bch => { fixtures.conversion.toSatoshi.bch.forEach(bch => {
it(`should convert ${bch[0]} $BCH to ${bch[1]} Satoshis`, () => { it(`should convert ${bch[0]} $BCH to ${bch[1]} Satoshis`, () => {
assert.equal(bchjs.BitcoinCash.toSatoshi(bch[0]), bch[1]) assert.equal(bchjs.BitcoinCash.toSatoshi(bch[0]), bch[1])
@@ -83,7 +83,7 @@ describe("#BitcoinCash", () => {
}) })
}) })
it("should exercise satsToBits", () => { it('should exercise satsToBits', () => {
fixtures.conversion.satsToBits.bch.forEach(bch => { fixtures.conversion.satsToBits.bch.forEach(bch => {
it(`should convert ${bch[0]} BCH to ${bch[1]} bits`, () => { it(`should convert ${bch[0]} BCH to ${bch[1]} bits`, () => {
assert.equal( assert.equal(
@@ -118,8 +118,8 @@ describe("#BitcoinCash", () => {
// }); // });
}) })
describe("sign and verify messages", () => { describe('sign and verify messages', () => {
it("should exercise signMessageWithPrivKey", () => { it('should exercise signMessageWithPrivKey', () => {
fixtures.signatures.sign.forEach(sign => { fixtures.signatures.sign.forEach(sign => {
it(`should sign a message w/ ${sign.network} ${sign.privateKeyWIF}`, () => { it(`should sign a message w/ ${sign.network} ${sign.privateKeyWIF}`, () => {
const privateKeyWIF = sign.privateKeyWIF const privateKeyWIF = sign.privateKeyWIF
@@ -133,7 +133,7 @@ describe("#BitcoinCash", () => {
}) })
}) })
it("shoudl exercise verifyMessage", () => { it('shoudl exercise verifyMessage', () => {
fixtures.signatures.verify.forEach(sign => { fixtures.signatures.verify.forEach(sign => {
it(`should verify a valid signed message from ${sign.network} cashaddr address ${sign.address}`, () => { it(`should verify a valid signed message from ${sign.network} cashaddr address ${sign.address}`, () => {
assert.equal( assert.equal(
@@ -147,7 +147,7 @@ describe("#BitcoinCash", () => {
}) })
}) })
it("should verify legacy addresses", () => { it('should verify legacy addresses', () => {
fixtures.signatures.verify.forEach(sign => { fixtures.signatures.verify.forEach(sign => {
const legacyAddress = bchjs.Address.toLegacyAddress(sign.address) const legacyAddress = bchjs.Address.toLegacyAddress(sign.address)
it(`should verify a valid signed message from ${sign.network} legacy address ${legacyAddress}`, () => { it(`should verify a valid signed message from ${sign.network} legacy address ${legacyAddress}`, () => {
@@ -170,7 +170,7 @@ describe("#BitcoinCash", () => {
bchjs.BitcoinCash.verifyMessage( bchjs.BitcoinCash.verifyMessage(
sign.address, sign.address,
sign.signature, sign.signature,
"nope" 'nope'
), ),
false false
) )
@@ -179,8 +179,8 @@ describe("#BitcoinCash", () => {
}) })
}) })
describe("encode and decode to base58Check", () => { describe('encode and decode to base58Check', () => {
describe("#encodeBase58Check", () => { describe('#encodeBase58Check', () => {
fixtures.encodeBase58Check.forEach((base58Check, i) => { fixtures.encodeBase58Check.forEach((base58Check, i) => {
it(`encode ${base58Check.hex} as base58Check ${base58Check.base58Check}`, () => { it(`encode ${base58Check.hex} as base58Check ${base58Check.base58Check}`, () => {
assert.equal( assert.equal(
@@ -191,7 +191,7 @@ describe("#BitcoinCash", () => {
}) })
}) })
describe("#decodeBase58Check", () => { describe('#decodeBase58Check', () => {
fixtures.encodeBase58Check.forEach((base58Check, i) => { fixtures.encodeBase58Check.forEach((base58Check, i) => {
it(`decode ${base58Check.base58Check} as ${base58Check.hex}`, () => { it(`decode ${base58Check.base58Check} as ${base58Check.hex}`, () => {
assert.equal( assert.equal(
@@ -203,8 +203,8 @@ describe("#BitcoinCash", () => {
}) })
}) })
describe("encode and decode BIP21 urls", () => { describe('encode and decode BIP21 urls', () => {
describe("#encodeBIP21", () => { describe('#encodeBIP21', () => {
fixtures.bip21.valid.forEach((bip21, i) => { fixtures.bip21.valid.forEach((bip21, i) => {
it(`encode ${bip21.address} as url`, () => { it(`encode ${bip21.address} as url`, () => {
const url = bchjs.BitcoinCash.encodeBIP21( const url = bchjs.BitcoinCash.encodeBIP21(
@@ -226,7 +226,7 @@ describe("#BitcoinCash", () => {
}) })
}) })
describe("#decodeBIP21", () => { describe('#decodeBIP21', () => {
fixtures.bip21.valid.forEach((bip21, i) => { fixtures.bip21.valid.forEach((bip21, i) => {
it(`decodes ${bip21.url}`, () => { it(`decodes ${bip21.url}`, () => {
const decoded = bchjs.BitcoinCash.decodeBIP21(bip21.url) const decoded = bchjs.BitcoinCash.decodeBIP21(bip21.url)
@@ -252,9 +252,9 @@ describe("#BitcoinCash", () => {
}) })
}) })
describe("#getByteCount", () => { describe('#getByteCount', () => {
fixtures.getByteCount.forEach(fixture => { fixtures.getByteCount.forEach(fixture => {
it(`get byte count`, () => { it('get byte count', () => {
const byteCount = bchjs.BitcoinCash.getByteCount( const byteCount = bchjs.BitcoinCash.getByteCount(
fixture.inputs, fixture.inputs,
fixture.outputs fixture.outputs
@@ -264,8 +264,8 @@ describe("#BitcoinCash", () => {
}) })
}) })
describe("#bip38", () => { describe('#bip38', () => {
describe("#encryptBIP38", () => { describe('#encryptBIP38', () => {
fixtures.bip38.encrypt.mainnet.forEach(fixture => { fixtures.bip38.encrypt.mainnet.forEach(fixture => {
it(`BIP 38 encrypt wif ${fixture.wif} with password ${fixture.password} on mainnet`, () => { it(`BIP 38 encrypt wif ${fixture.wif} with password ${fixture.password} on mainnet`, () => {
const encryptedKey = bchjs.BitcoinCash.encryptBIP38( const encryptedKey = bchjs.BitcoinCash.encryptBIP38(
@@ -287,13 +287,13 @@ describe("#BitcoinCash", () => {
}) })
}) })
describe("#decryptBIP38", () => { describe('#decryptBIP38', () => {
fixtures.bip38.decrypt.mainnet.forEach(fixture => { fixtures.bip38.decrypt.mainnet.forEach(fixture => {
it(`BIP 38 decrypt encrypted key ${fixture.encryptedKey} on mainnet`, () => { it(`BIP 38 decrypt encrypted key ${fixture.encryptedKey} on mainnet`, () => {
const wif = bchjs.BitcoinCash.decryptBIP38( const wif = bchjs.BitcoinCash.decryptBIP38(
fixture.encryptedKey, fixture.encryptedKey,
fixture.password, fixture.password,
"mainnet" 'mainnet'
) )
assert.equal(wif, fixture.wif) assert.equal(wif, fixture.wif)
}) })
@@ -304,7 +304,7 @@ describe("#BitcoinCash", () => {
const wif = bchjs.BitcoinCash.decryptBIP38( const wif = bchjs.BitcoinCash.decryptBIP38(
fixture.encryptedKey, fixture.encryptedKey,
fixture.password, fixture.password,
"testnet" 'testnet'
) )
assert.equal(wif, fixture.wif) assert.equal(wif, fixture.wif)
}) })
-275
View File
@@ -1,275 +0,0 @@
const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
const axios = require("axios")
const sinon = require("sinon")
const mockData = require("./fixtures/blockbook-mock")
describe(`#Blockbook`, () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
describe(`#Balance`, () => {
it(`should GET balance for a single address`, async () => {
// Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.balance })
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
const result = await bchjs.Blockbook.balance(addr)
//console.log(`result: ${util.inspect(result)}`)
assert.hasAnyKeys(result, [
"page",
"totalPages",
"itemsOnPage",
"address",
"balance",
"totalReceived",
"totalSent",
"unconfirmedBalance",
"unconfirmedTxs",
"txs",
"txids"
])
assert.isArray(result.txids)
})
it(`should POST request balances for an array of addresses`, async () => {
// Stub the network call.
sandbox
.stub(axios, "post")
.resolves({ data: [mockData.balance, mockData.balance] })
const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
]
const result = await bchjs.Blockbook.balance(addr)
//console.log(`result: ${util.inspect(result)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"page",
"totalPages",
"itemsOnPage",
"address",
"balance",
"totalReceived",
"totalSent",
"unconfirmedBalance",
"unconfirmedTxs",
"txs",
"txids"
])
assert.isArray(result[0].txids)
})
it(`should throw an error for improper input`, async () => {
try {
// Stub the network call.
sandbox
.stub(axios, "post")
.throws(`Input address must be a string or array of strings`)
const addr = 12345
await bchjs.Blockbook.balance(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input address must be a string or array of strings`
)
}
})
/*
it(`should throw error on array size rate limit`, async () => {
try {
// Stub the network call.
sandbox.stub(axios, "post").throws(`Array too large`)
const addr = []
for (let i = 0; i < 25; i++)
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
const result = await bchjs.Blockbook.balance(addr)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.hasAnyKeys(err, ["error"])
assert.include(err.error, "Array too large")
}
})
*/
})
describe(`#utxo`, () => {
it(`should GET utxos for a single address`, async () => {
// Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.utxo })
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
const result = await bchjs.Blockbook.utxo(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"txid",
"vout",
"value",
"height",
"confirmations"
])
})
it(`should POST utxo details for an array of addresses`, async () => {
// Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.utxos })
const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
]
const result = await bchjs.Blockbook.utxo(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isArray(result[0])
assert.hasAnyKeys(result[0][0], [
"txid",
"vout",
"value",
"height",
"confirmations"
])
})
it(`should throw an error for improper input`, async () => {
try {
// Stub the network call.
sandbox
.stub(axios, "post")
.throws(`Input address must be a string or array of strings`)
const addr = 12345
await bchjs.Blockbook.utxo(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input address must be a string or array of strings`
)
}
})
/*
it(`should throw error on array size rate limit`, async () => {
try {
const addr = []
for (let i = 0; i < 25; i++)
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
const result = await bchjs.Blockbook.utxo(addr)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.hasAnyKeys(err, ["error"])
assert.include(err.error, "Array too large")
}
})
*/
})
describe(`#tx`, () => {
it(`should GET tx details for a single txid`, async () => {
// Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.utxo })
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
const result = await bchjs.Blockbook.utxo(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"txid",
"vout",
"value",
"height",
"confirmations"
])
})
// it(`should POST utxo details for an array of addresses`, async () => {
// // Stub the network call.
// sandbox.stub(axios, "post").resolves({ data: mockData.utxos })
//
// const addr = [
// "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
// "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
// ]
//
// const result = await bchjs.Blockbook.utxo(addr)
// //console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.isArray(result)
// assert.isArray(result[0])
// assert.hasAnyKeys(result[0][0], [
// "txid",
// "vout",
// "value",
// "height",
// "confirmations"
// ])
// })
// it(`should throw an error for improper input`, async () => {
// try {
// // Stub the network call.
// sandbox
// .stub(axios, "post")
// .throws(`Input address must be a string or array of strings`)
//
// const addr = 12345
//
// await bchjs.Blockbook.utxo(addr)
// assert.equal(true, false, "Unexpected result!")
// } catch (err) {
// //console.log(`err: `, err)
// assert.include(
// err.message,
// `Input address must be a string or array of strings`
// )
// }
// })
/*
it(`should throw error on array size rate limit`, async () => {
try {
const addr = []
for (let i = 0; i < 25; i++)
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
const result = await bchjs.Blockbook.utxo(addr)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.hasAnyKeys(err, ["error"])
assert.include(err.error, "Array too large")
}
})
*/
})
})
+172 -148
View File
@@ -1,102 +1,126 @@
const assert = require("assert") const assert = require('assert')
const assert2 = require("chai").assert const assert2 = require('chai').assert
const axios = require("axios") const axios = require('axios')
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
const sinon = require("sinon") const sinon = require('sinon')
const mockData = require("./fixtures/blockchain-mock") const mockData = require('./fixtures/blockchain-mock')
describe("#Blockchain", () => { describe('#Blockchain', () => {
describe("#getBestBlockHash", () => { describe('#getBestBlockHash', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
it("should get best block hash", done => { it('should get best block hash', done => {
const resolved = new Promise(r => const resolved = new Promise(resolve =>
r({ resolve({
data: data:
"0000000000000000005f1f550d3d8b142b684277016ebd00fa29c668606ae52d" '0000000000000000005f1f550d3d8b142b684277016ebd00fa29c668606ae52d'
}) })
) )
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getBestBlockHash() bchjs.Blockchain.getBestBlockHash()
.then(result => { .then(result => {
const hash = const hash =
"0000000000000000005f1f550d3d8b142b684277016ebd00fa29c668606ae52d" '0000000000000000005f1f550d3d8b142b684277016ebd00fa29c668606ae52d'
assert.equal(hash, result) assert.strictEqual(hash, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#getBlock", () => { describe('#getBlock', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
const data = { const data = {
hash: "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09", hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
confirmations: 526807, confirmations: 526807,
size: 216, size: 216,
height: 1000, height: 1000,
version: 1, version: 1,
versionHex: "00000001", versionHex: '00000001',
merkleroot: merkleroot:
"fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33", 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
tx: ["fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33"], tx: ['fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33'],
time: 1232346882, time: 1232346882,
mediantime: 1232344831, mediantime: 1232344831,
nonce: 2595206198, nonce: 2595206198,
bits: "1d00ffff", bits: '1d00ffff',
difficulty: 1, difficulty: 1,
chainwork: chainwork:
"000000000000000000000000000000000000000000000000000003e903e903e9", '000000000000000000000000000000000000000000000000000003e903e903e9',
previousblockhash: previousblockhash:
"0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d", '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
nextblockhash: nextblockhash:
"00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6" '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6'
} }
it("should get block by hash", done => { it('should get block by hash', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'post').returns(resolved)
bchjs.Blockchain.getBlock( const blockhash =
"00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09" '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09'
) bchjs.Blockchain.getBlock(blockhash)
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
it('should throw error if blockhash is not provided', async () => {
try {
await bchjs.Blockchain.getBlock()
assert2.fail('Unexpected result')
} catch (err) {
assert2.include(err.message, 'blockhash must be a string')
}
})
it('should handle response error', async () => {
try {
const error = new Error()
error.response = {
data: 'Test Error'
}
sandbox.stub(axios, 'post').throws(error)
const blockhash =
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09'
await bchjs.Blockchain.getBlock(blockhash)
assert2.fail('Unexpected result')
} catch (err) {
assert2.include(err, 'Test Error')
}
})
}) })
describe("#getBlockchainInfo", () => { describe('#getBlockchainInfo', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
const data = { const data = {
chain: "main", chain: 'main',
blocks: 527810, blocks: 527810,
headers: 527810, headers: 527810,
bestblockhash: bestblockhash:
"000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0", '000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0',
difficulty: 576023394804.6666, difficulty: 576023394804.6666,
mediantime: 1524878499, mediantime: 1524878499,
verificationprogress: 0.9999990106793685, verificationprogress: 0.9999990106793685,
chainwork: chainwork:
"00000000000000000000000000000000000000000096da5b040913fa09249b4e", '00000000000000000000000000000000000000000096da5b040913fa09249b4e',
pruned: false, pruned: false,
softforks: [ softforks: [
{ id: "bip34", version: 2, reject: [Object] }, { id: 'bip34', version: 2, reject: [Object] },
{ id: "bip66", version: 3, reject: [Object] }, { id: 'bip66', version: 3, reject: [Object] },
{ id: "bip65", version: 4, reject: [Object] } { id: 'bip65', version: 4, reject: [Object] }
], ],
bip9_softforks: { bip9_softforks: {
csv: { csv: {
status: "active", status: 'active',
startTime: 1462060800, startTime: 1462060800,
timeout: 1493596800, timeout: 1493596800,
since: 419328 since: 419328
@@ -104,178 +128,178 @@ describe("#Blockchain", () => {
} }
} }
it("should get blockchain info", done => { it('should get blockchain info', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getBlockchainInfo() bchjs.Blockchain.getBlockchainInfo()
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#getBlockCount", () => { describe('#getBlockCount', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
const data = 527810 const data = 527810
it("should get block count", done => { it('should get block count', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getBlockCount() bchjs.Blockchain.getBlockCount()
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#getBlockHash", () => { describe('#getBlockHash', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
const data = const data =
"000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0" '000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0'
it("should get block hash by height", done => { it('should get block hash by height', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getBlockHash(527810) bchjs.Blockchain.getBlockHash(527810)
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#getBlockHeader", () => { describe('#getBlockHeader', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
const data = { const data = {
hash: "000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0", hash: '000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0',
confirmations: 1, confirmations: 1,
height: 527810, height: 527810,
version: 536870912, version: 536870912,
versionHex: "20000000", versionHex: '20000000',
merkleroot: merkleroot:
"9298432bbebe4638456aa19cb7ef91639da87668a285d88d0ecd6080424d223b", '9298432bbebe4638456aa19cb7ef91639da87668a285d88d0ecd6080424d223b',
time: 1524881438, time: 1524881438,
mediantime: 1524878499, mediantime: 1524878499,
nonce: 3326843941, nonce: 3326843941,
bits: "1801e8a5", bits: '1801e8a5',
difficulty: 576023394804.6666, difficulty: 576023394804.6666,
chainwork: chainwork:
"00000000000000000000000000000000000000000096da5b040913fa09249b4e", '00000000000000000000000000000000000000000096da5b040913fa09249b4e',
previousblockhash: previousblockhash:
"000000000000000000b33251708bc7a7b4540e61880d8c376e8e2db6a19a4789" '000000000000000000b33251708bc7a7b4540e61880d8c376e8e2db6a19a4789'
} }
it("should get block header by hash", done => { it('should get block header by hash', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getBlockHeader( bchjs.Blockchain.getBlockHeader(
"000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0", '000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0',
true true
) )
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#getDifficulty", () => { describe('#getDifficulty', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
const data = "577528469277.1339" const data = '577528469277.1339'
it("should get difficulty", done => { it('should get difficulty', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getDifficulty() bchjs.Blockchain.getDifficulty()
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#getMempoolAncestors", () => { describe('#getMempoolAncestors', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
const data = "Transaction not in mempool" const data = 'Transaction not in mempool'
it("should get mempool ancestors", done => { it('should get mempool ancestors', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getMempoolAncestors( bchjs.Blockchain.getMempoolAncestors(
"daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88", 'daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88',
true true
) )
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#getMempoolDescendants", () => { describe('#getMempoolDescendants', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
const data = { const data = {
result: "Transaction not in mempool" result: 'Transaction not in mempool'
} }
it("should get mempool descendants", done => { it('should get mempool descendants', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getMempoolDescendants( bchjs.Blockchain.getMempoolDescendants(
"daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88", 'daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88',
true true
) )
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#getMempoolEntry", () => { describe('#getMempoolEntry', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
const data = { const data = {
result: "Transaction not in mempool" result: 'Transaction not in mempool'
} }
it("should get mempool entry", done => { it('should get mempool entry', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getMempoolEntry( bchjs.Blockchain.getMempoolEntry(
"daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88" 'daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88'
) )
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#getMempoolInfo", () => { describe('#getMempoolInfo', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
@@ -289,19 +313,19 @@ describe("#Blockchain", () => {
} }
} }
it("should get mempool info", done => { it('should get mempool info', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getMempoolInfo() bchjs.Blockchain.getMempoolInfo()
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#getRawMempool", () => { describe('#getRawMempool', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
@@ -310,7 +334,7 @@ describe("#Blockchain", () => {
transactions: [ transactions: [
{ {
txid: txid:
"ab36d68dd0a618592fe34e4a898e8beeeb4049133547dbb16f9338384084af96", 'ab36d68dd0a618592fe34e4a898e8beeeb4049133547dbb16f9338384084af96',
size: 191, size: 191,
fee: 0.00047703, fee: 0.00047703,
modifiedfee: 0.00047703, modifiedfee: 0.00047703,
@@ -330,19 +354,19 @@ describe("#Blockchain", () => {
} }
} }
it("should get mempool info", done => { it('should get mempool info', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getRawMempool() bchjs.Blockchain.getRawMempool()
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#getTxOut", () => { describe('#getTxOut', () => {
// TODO finish this test // TODO finish this test
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
@@ -351,63 +375,63 @@ describe("#Blockchain", () => {
// result: {} // result: {}
// } // }
it("should throw an error for improper txid.", async () => { it('should throw an error for improper txid.', async () => {
try { try {
await bchjs.Blockchain.getTxOut("badtxid") await bchjs.Blockchain.getTxOut('badtxid')
} catch (err) { } catch (err) {
assert2.include(err.message, "txid needs to be a proper transaction ID") assert2.include(err.message, 'txid needs to be a proper transaction ID')
} }
}) })
it("should throw an error if no vout value is provided.", async () => { it('should throw an error if no vout value is provided.', async () => {
try { try {
await bchjs.Blockchain.getTxOut( await bchjs.Blockchain.getTxOut(
"daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88" 'daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88'
) )
} catch (err) { } catch (err) {
assert2.include(err.message, "n must be an integer") assert2.include(err.message, 'n must be an integer')
} }
}) })
it("should throw an error if include_mempool is not a boolean", async () => { it('should throw an error if include_mempool is not a boolean', async () => {
try { try {
await bchjs.Blockchain.getTxOut( await bchjs.Blockchain.getTxOut(
"daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88", 'daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88',
0, 0,
"bad value" 'bad value'
) )
} catch (err) { } catch (err) {
assert2.include( assert2.include(
err.message, err.message,
"include_mempool input must be of type boolean" 'includeMempool input must be of type boolean'
) )
} }
}) })
it("should get information on an unspent tx", async () => { it('should get information on an unspent tx', async () => {
sandbox.stub(axios, "post").resolves({ data: mockData.txOutUnspent }) sandbox.stub(axios, 'post').resolves({ data: mockData.txOutUnspent })
const result = await bchjs.Blockchain.getTxOut( const result = await bchjs.Blockchain.getTxOut(
"62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8", '62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8',
0, 0,
true true
) )
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert2.hasAllKeys(result, [ assert2.hasAllKeys(result, [
"bestblock", 'bestblock',
"confirmations", 'confirmations',
"value", 'value',
"scriptPubKey", 'scriptPubKey',
"coinbase" 'coinbase'
]) ])
}) })
it("should get information on a spent tx", async () => { it('should get information on a spent tx', async () => {
sandbox.stub(axios, "post").resolves({ data: null }) sandbox.stub(axios, 'post').resolves({ data: null })
const result = await bchjs.Blockchain.getTxOut( const result = await bchjs.Blockchain.getTxOut(
"87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871", '87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871',
0, 0,
true true
) )
@@ -417,7 +441,7 @@ describe("#Blockchain", () => {
}) })
}) })
describe("#preciousBlock", () => { describe('#preciousBlock', () => {
// TODO finish this test // TODO finish this test
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
@@ -426,67 +450,67 @@ describe("#Blockchain", () => {
result: {} result: {}
} }
it("should get TODO", done => { it('should get TODO', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.preciousBlock() bchjs.Blockchain.preciousBlock()
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#pruneBlockchain", () => { describe('#pruneBlockchain', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
const data = "Cannot prune blocks because node is not in prune mode." const data = 'Cannot prune blocks because node is not in prune mode.'
it("should prune blockchain", done => { it('should prune blockchain', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "post").returns(resolved) sandbox.stub(axios, 'post').returns(resolved)
bchjs.Blockchain.pruneBlockchain(507) bchjs.Blockchain.pruneBlockchain(507)
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#verifyChain", () => { describe('#verifyChain', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
const data = true const data = true
it("should verify blockchain", done => { it('should verify blockchain', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.verifyChain(3, 6) bchjs.Blockchain.verifyChain(3, 6)
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#verifyTxOutProof", () => { describe('#verifyTxOutProof', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
const data = "proof must be hexadecimal string (not '')" const data = "proof must be hexadecimal string (not '')"
it("should verify utxo proof", done => { it('should verify utxo proof', done => {
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.verifyTxOutProof("3") bchjs.Blockchain.verifyTxOutProof('3')
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
+20 -24
View File
@@ -1,46 +1,42 @@
const assert = require("assert") const assert = require('assert')
const axios = require("axios") const axios = require('axios')
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
const sinon = require("sinon") const sinon = require('sinon')
describe("#Control", () => { describe('#Control', () => {
describe("#getNetworkInfo", () => { let sandbox
let sandbox beforeEach(() => (sandbox = sinon.createSandbox()))
beforeEach(() => (sandbox = sinon.createSandbox())) afterEach(() => sandbox.restore())
afterEach(() => sandbox.restore())
it("should get info", done => { describe('#getNetworkInfo', () => {
it('should get info', done => {
const data = { const data = {
version: 170000, version: 170000,
protocolversion: 70015, protocolversion: 70015,
blocks: 527813, blocks: 527813,
timeoffset: 0, timeoffset: 0,
connections: 21, connections: 21,
proxy: "", proxy: '',
difficulty: 581086703759.5878, difficulty: 581086703759.5878,
testnet: false, testnet: false,
paytxfee: 0, paytxfee: 0,
relayfee: 0.00001, relayfee: 0.00001,
errors: "" errors: ''
} }
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Control.getNetworkInfo() bchjs.Control.getNetworkInfo()
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
}) })
describe("#getMemoryInfo", () => { describe('#getMemoryInfo', () => {
let sandbox it('should get memory info', done => {
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
it("should get memory info", done => {
const data = { const data = {
locked: { locked: {
used: 0, used: 0,
@@ -51,12 +47,12 @@ describe("#Control", () => {
chunks_free: 1 chunks_free: 1
} }
} }
const resolved = new Promise(r => r({ data: data })) const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, "get").returns(resolved) sandbox.stub(axios, 'get').returns(resolved)
bchjs.Control.getMemoryInfo() bchjs.Control.getMemoryInfo()
.then(result => { .then(result => {
assert.deepEqual(data, result) assert.deepStrictEqual(data, result)
}) })
.then(done, done) .then(done, done)
}) })
+35 -35
View File
@@ -1,97 +1,97 @@
const fixtures = require("./fixtures/crypto.json") const fixtures = require('./fixtures/crypto.json')
const assert = require("assert") const assert = require('assert')
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
const Buffer = require("safe-buffer").Buffer const Buffer = require('safe-buffer').Buffer
describe("#Crypto", () => { describe('#Crypto', () => {
describe("#sha256", () => { describe('#sha256', () => {
fixtures.sha256.forEach(fixture => { fixtures.sha256.forEach(fixture => {
it(`should create SHA256Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => { it(`should create SHA256Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => {
const data = Buffer.from(fixture.hex, "hex") const data = Buffer.from(fixture.hex, 'hex')
const sha256Hash = bchjs.Crypto.sha256(data).toString("hex") const sha256Hash = bchjs.Crypto.sha256(data).toString('hex')
assert.equal(sha256Hash, fixture.hash) assert.equal(sha256Hash, fixture.hash)
}) })
it(`should create 64 character SHA256Hash hex encoded`, () => { it('should create 64 character SHA256Hash hex encoded', () => {
const data = Buffer.from(fixture.hex, "hex") const data = Buffer.from(fixture.hex, 'hex')
const sha256Hash = bchjs.Crypto.sha256(data).toString("hex") const sha256Hash = bchjs.Crypto.sha256(data).toString('hex')
assert.equal(sha256Hash.length, 64) assert.equal(sha256Hash.length, 64)
}) })
}) })
}) })
describe("#ripemd160", () => { describe('#ripemd160', () => {
fixtures.ripemd160.forEach(fixture => { fixtures.ripemd160.forEach(fixture => {
it(`should create RIPEMD160Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => { it(`should create RIPEMD160Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => {
const data = Buffer.from(fixture.hex, "hex") const data = Buffer.from(fixture.hex, 'hex')
const ripemd160 = bchjs.Crypto.ripemd160(data).toString("hex") const ripemd160 = bchjs.Crypto.ripemd160(data).toString('hex')
assert.equal(ripemd160, fixture.hash) assert.equal(ripemd160, fixture.hash)
}) })
it(`should create 64 character RIPEMD160Hash hex encoded`, () => { it('should create 64 character RIPEMD160Hash hex encoded', () => {
const data = Buffer.from(fixture.hex, "hex") const data = Buffer.from(fixture.hex, 'hex')
const ripemd160 = bchjs.Crypto.ripemd160(data).toString("hex") const ripemd160 = bchjs.Crypto.ripemd160(data).toString('hex')
assert.equal(ripemd160.length, 40) assert.equal(ripemd160.length, 40)
}) })
}) })
}) })
describe("#hash256", () => { describe('#hash256', () => {
fixtures.hash256.forEach(fixture => { fixtures.hash256.forEach(fixture => {
it(`should create double SHA256 Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => { it(`should create double SHA256 Hash hex encoded ${fixture.hash} from ${fixture.hex}`, () => {
const data = Buffer.from(fixture.hex, "hex") const data = Buffer.from(fixture.hex, 'hex')
const hash256 = bchjs.Crypto.hash256(data).toString("hex") const hash256 = bchjs.Crypto.hash256(data).toString('hex')
assert.equal(hash256, fixture.hash) assert.equal(hash256, fixture.hash)
}) })
it(`should create 64 character SHA256 Hash hex encoded`, () => { it('should create 64 character SHA256 Hash hex encoded', () => {
const data = Buffer.from(fixture.hex, "hex") const data = Buffer.from(fixture.hex, 'hex')
const hash256 = bchjs.Crypto.hash256(data).toString("hex") const hash256 = bchjs.Crypto.hash256(data).toString('hex')
assert.equal(hash256.length, 64) assert.equal(hash256.length, 64)
}) })
}) })
}) })
describe("#hash160", () => { describe('#hash160', () => {
fixtures.hash160.forEach(fixture => { fixtures.hash160.forEach(fixture => {
it(`should create RIPEMD160(SHA256()) hex encoded ${fixture.hash} from ${fixture.hex}`, () => { it(`should create RIPEMD160(SHA256()) hex encoded ${fixture.hash} from ${fixture.hex}`, () => {
const data = Buffer.from(fixture.hex, "hex") const data = Buffer.from(fixture.hex, 'hex')
const hash160 = bchjs.Crypto.hash160(data).toString("hex") const hash160 = bchjs.Crypto.hash160(data).toString('hex')
assert.equal(hash160, fixture.hash) assert.equal(hash160, fixture.hash)
}) })
it(`should create 64 character SHA256Hash hex encoded`, () => { it('should create 64 character SHA256Hash hex encoded', () => {
const data = Buffer.from(fixture.hex, "hex") const data = Buffer.from(fixture.hex, 'hex')
const hash160 = bchjs.Crypto.hash160(data).toString("hex") const hash160 = bchjs.Crypto.hash160(data).toString('hex')
assert.equal(hash160.length, 40) assert.equal(hash160.length, 40)
}) })
}) })
}) })
describe("#randomBytes", () => { describe('#randomBytes', () => {
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
it("should return 16 bytes of entropy hex encoded", () => { it('should return 16 bytes of entropy hex encoded', () => {
const entropy = bchjs.Crypto.randomBytes(16) const entropy = bchjs.Crypto.randomBytes(16)
assert.equal(Buffer.byteLength(entropy), 16) assert.equal(Buffer.byteLength(entropy), 16)
}) })
it("should return 20 bytes of entropy hex encoded", () => { it('should return 20 bytes of entropy hex encoded', () => {
const entropy = bchjs.Crypto.randomBytes(20) const entropy = bchjs.Crypto.randomBytes(20)
assert.equal(Buffer.byteLength(entropy), 20) assert.equal(Buffer.byteLength(entropy), 20)
}) })
it("should return 24 bytes of entropy hex encoded", () => { it('should return 24 bytes of entropy hex encoded', () => {
const entropy = bchjs.Crypto.randomBytes(24) const entropy = bchjs.Crypto.randomBytes(24)
assert.equal(Buffer.byteLength(entropy), 24) assert.equal(Buffer.byteLength(entropy), 24)
}) })
it("should return 28 bytes of entropy hex encoded", () => { it('should return 28 bytes of entropy hex encoded', () => {
const entropy = bchjs.Crypto.randomBytes(28) const entropy = bchjs.Crypto.randomBytes(28)
assert.equal(Buffer.byteLength(entropy), 28) assert.equal(Buffer.byteLength(entropy), 28)
}) })
it("should return 32 bytes of entropy hex encoded", () => { it('should return 32 bytes of entropy hex encoded', () => {
const entropy = bchjs.Crypto.randomBytes(32) const entropy = bchjs.Crypto.randomBytes(32)
assert.equal(Buffer.byteLength(entropy), 32) assert.equal(Buffer.byteLength(entropy), 32)
}) })
+66
View File
@@ -0,0 +1,66 @@
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Unit under test (uut)
const BCHJS = require('../../src/bch-js')
const mockData = require('./fixtures/dsproof-mock')
let bchjs
const txid = 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
describe('#DSProof', () => {
let sandbox
beforeEach(() => {
bchjs = new BCHJS()
sandbox = sinon.createSandbox()
})
afterEach(() => sandbox.restore())
describe('#getDSProof', () => {
it('should throw error if input is not provided', async () => {
try {
await bchjs.DSProof.getDSProof()
assert.equal(false, true, 'unexpected error')
} catch (err) {
assert.include(err.message, 'txid is required')
}
})
it('should throw error txid is invalid', async () => {
try {
await bchjs.DSProof.getDSProof('txid')
assert.equal(false, true, 'unexpected error')
} catch (err) {
assert.include(err.message, 'txid must be of length 64')
}
})
it('should handle error', async () => {
try {
sandbox.stub(bchjs.DSProof.axios, 'get').throws(new Error('test error'))
await bchjs.DSProof.getDSProof(txid)
assert.equal(false, true, 'unexpected error')
} catch (err) {
assert.include(err.message, 'test error')
}
})
it('should get double spend proof', async () => {
try {
sandbox
.stub(bchjs.DSProof.axios, 'get')
.resolves({ data: mockData.dsproof })
const result = await bchjs.DSProof.getDSProof(txid)
assert.property(result, 'dspid')
assert.property(result, 'txid')
assert.property(result, 'outpoint')
assert.property(result, 'path')
assert.property(result, 'descendants')
assert.property(result.outpoint, 'txid')
assert.property(result.outpoint, 'vout')
} catch (error) {
assert.equal(false, true, 'unexpected error')
}
})
})
})
+29 -29
View File
@@ -1,25 +1,25 @@
// Public npm libraries. // Public npm libraries.
const assert = require("assert") const assert = require('assert')
const Buffer = require("safe-buffer").Buffer const Buffer = require('safe-buffer').Buffer
// Mocks // Mocks
const fixtures = require("./fixtures/ecpair.json") const fixtures = require('./fixtures/ecpair.json')
// Unit under test (uut) // Unit under test (uut)
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
// const bchjs = new BCHJS() // const bchjs = new BCHJS()
let bchjs let bchjs
describe("#ECPair", () => { describe('#ECPair', () => {
beforeEach(() => { beforeEach(() => {
bchjs = new BCHJS() bchjs = new BCHJS()
}) })
describe("#fromWIF", () => { describe('#fromWIF', () => {
fixtures.fromWIF.forEach(fixture => { fixtures.fromWIF.forEach(fixture => {
it(`should create ECPair from WIF ${fixture.privateKeyWIF}`, () => { it(`should create ECPair from WIF ${fixture.privateKeyWIF}`, () => {
const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF)
assert.equal(typeof ecpair, "object") assert.equal(typeof ecpair, 'object')
}) })
it(`should get ${fixture.legacy} legacy address`, () => { it(`should get ${fixture.legacy} legacy address`, () => {
@@ -42,7 +42,7 @@ describe("#ECPair", () => {
}) })
}) })
describe("#toWIF", () => { describe('#toWIF', () => {
fixtures.toWIF.forEach(fixture => { fixtures.toWIF.forEach(fixture => {
it(`should get WIF ${fixture.privateKeyWIF} from ECPair`, () => { it(`should get WIF ${fixture.privateKeyWIF} from ECPair`, () => {
const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF)
@@ -52,32 +52,32 @@ describe("#ECPair", () => {
}) })
}) })
describe("#fromPublicKey", () => { describe('#fromPublicKey', () => {
fixtures.fromPublicKey.forEach(fixture => { fixtures.fromPublicKey.forEach(fixture => {
it(`should create ECPair from public key buffer`, () => { it('should create ECPair from public key buffer', () => {
const ecpair = bchjs.ECPair.fromPublicKey( const ecpair = bchjs.ECPair.fromPublicKey(
Buffer.from(fixture.pubkeyHex, "hex") Buffer.from(fixture.pubkeyHex, 'hex')
) )
assert.equal(typeof ecpair, "object") assert.equal(typeof ecpair, 'object')
}) })
it(`should get ${fixture.legacy} legacy address`, () => { it(`should get ${fixture.legacy} legacy address`, () => {
const ecpair = bchjs.ECPair.fromPublicKey( const ecpair = bchjs.ECPair.fromPublicKey(
Buffer.from(fixture.pubkeyHex, "hex") Buffer.from(fixture.pubkeyHex, 'hex')
) )
assert.equal(bchjs.HDNode.toLegacyAddress(ecpair), fixture.legacy) assert.equal(bchjs.HDNode.toLegacyAddress(ecpair), fixture.legacy)
}) })
it(`should get ${fixture.cashAddr} cash address`, () => { it(`should get ${fixture.cashAddr} cash address`, () => {
const ecpair = bchjs.ECPair.fromPublicKey( const ecpair = bchjs.ECPair.fromPublicKey(
Buffer.from(fixture.pubkeyHex, "hex") Buffer.from(fixture.pubkeyHex, 'hex')
) )
assert.equal(bchjs.HDNode.toCashAddress(ecpair), fixture.cashAddr) assert.equal(bchjs.HDNode.toCashAddress(ecpair), fixture.cashAddr)
}) })
it(`should get ${fixture.regtestAddr} cash address`, () => { it(`should get ${fixture.regtestAddr} cash address`, () => {
const ecpair = bchjs.ECPair.fromPublicKey( const ecpair = bchjs.ECPair.fromPublicKey(
Buffer.from(fixture.pubkeyHex, "hex") Buffer.from(fixture.pubkeyHex, 'hex')
) )
assert.equal( assert.equal(
bchjs.HDNode.toCashAddress(ecpair, true), bchjs.HDNode.toCashAddress(ecpair, true),
@@ -87,19 +87,19 @@ describe("#ECPair", () => {
}) })
}) })
describe("#toPublicKey", () => { describe('#toPublicKey', () => {
fixtures.toPublicKey.forEach(fixture => { fixtures.toPublicKey.forEach(fixture => {
it(`should create a public key buffer from an ECPair`, () => { it('should create a public key buffer from an ECPair', () => {
const ecpair = bchjs.ECPair.fromPublicKey( const ecpair = bchjs.ECPair.fromPublicKey(
Buffer.from(fixture.pubkeyHex, "hex") Buffer.from(fixture.pubkeyHex, 'hex')
) )
const pubkeyBuffer = bchjs.ECPair.toPublicKey(ecpair) const pubkeyBuffer = bchjs.ECPair.toPublicKey(ecpair)
assert.equal(typeof pubkeyBuffer, "object") assert.equal(typeof pubkeyBuffer, 'object')
}) })
}) })
}) })
describe("#toLegacyAddress", () => { describe('#toLegacyAddress', () => {
fixtures.toLegacyAddress.forEach(fixture => { fixtures.toLegacyAddress.forEach(fixture => {
it(`should create legacy address ${fixture.legacy} from an ECPair`, () => { it(`should create legacy address ${fixture.legacy} from an ECPair`, () => {
const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF)
@@ -109,7 +109,7 @@ describe("#ECPair", () => {
}) })
}) })
describe("#toCashAddress", () => { describe('#toCashAddress', () => {
fixtures.toCashAddress.forEach(fixture => { fixtures.toCashAddress.forEach(fixture => {
it(`should create cash address ${fixture.cashAddr} from an ECPair`, () => { it(`should create cash address ${fixture.cashAddr} from an ECPair`, () => {
const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF)
@@ -127,23 +127,23 @@ describe("#ECPair", () => {
}) })
}) })
describe("#sign", () => { describe('#sign', () => {
fixtures.sign.forEach(fixture => { fixtures.sign.forEach(fixture => {
it(`should sign 32 byte hash buffer`, () => { it('should sign 32 byte hash buffer', () => {
const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF) const ecpair = bchjs.ECPair.fromWIF(fixture.privateKeyWIF)
const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), "hex") const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), 'hex')
const signatureBuf = bchjs.ECPair.sign(ecpair, buf) const signatureBuf = bchjs.ECPair.sign(ecpair, buf)
assert.equal(typeof signatureBuf, "object") assert.equal(typeof signatureBuf, 'object')
}) })
}) })
}) })
describe("#verify", () => { describe('#verify', () => {
fixtures.verify.forEach(fixture => { fixtures.verify.forEach(fixture => {
it(`should verify signed 32 byte hash buffer`, () => { it('should verify signed 32 byte hash buffer', () => {
const ecpair1 = bchjs.ECPair.fromWIF(fixture.privateKeyWIF1) const ecpair1 = bchjs.ECPair.fromWIF(fixture.privateKeyWIF1)
//const ecpair2 = bchjs.ECPair.fromWIF(fixture.privateKeyWIF2) // const ecpair2 = bchjs.ECPair.fromWIF(fixture.privateKeyWIF2)
const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), "hex") const buf = Buffer.from(bchjs.Crypto.sha256(fixture.data), 'hex')
const signature = bchjs.ECPair.sign(ecpair1, buf) const signature = bchjs.ECPair.sign(ecpair1, buf)
const verify = bchjs.ECPair.verify(ecpair1, buf, signature) const verify = bchjs.ECPair.verify(ecpair1, buf, signature)
assert.equal(verify, true) assert.equal(verify, true)
+218 -145
View File
@@ -1,275 +1,275 @@
const chai = require("chai") const chai = require('chai')
const assert = chai.assert const assert = chai.assert
const axios = require("axios") const axios = require('axios')
const sinon = require("sinon") const sinon = require('sinon')
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS() const bchjs = new BCHJS()
const mockData = require("./fixtures/electrumx-mock") const mockData = require('./fixtures/electrumx-mock')
describe(`#ElectrumX`, () => { describe('#ElectrumX', () => {
let sandbox let sandbox
beforeEach(() => (sandbox = sinon.createSandbox())) beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
describe(`#utxo`, () => { describe('#utxo', () => {
it(`should throw an error for improper input`, async () => { it('should throw an error for improper input', async () => {
try { try {
const addr = 12345 const addr = 12345
await bchjs.Electrumx.utxo(addr) await bchjs.Electrumx.utxo(addr)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include( assert.include(
err.message, err.message,
`Input address must be a string or array of strings` 'Input address must be a string or array of strings'
) )
} }
}) })
it(`should GET utxos for a single address`, async () => { it('should GET utxos for a single address', async () => {
// Stub the network call. // Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.utxo }) sandbox.stub(axios, 'get').resolves({ data: mockData.utxo })
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const result = await bchjs.Electrumx.utxo(addr) const result = await bchjs.Electrumx.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "utxos") assert.property(result, 'utxos')
assert.isArray(result.utxos) assert.isArray(result.utxos)
assert.property(result.utxos[0], "height") assert.property(result.utxos[0], 'height')
assert.property(result.utxos[0], "tx_hash") assert.property(result.utxos[0], 'tx_hash')
assert.property(result.utxos[0], "tx_pos") assert.property(result.utxos[0], 'tx_pos')
assert.property(result.utxos[0], "value") assert.property(result.utxos[0], 'value')
}) })
it(`should POST utxo details for an array of addresses`, async () => { it('should POST utxo details for an array of addresses', async () => {
// Stub the network call. // Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.utxos }) sandbox.stub(axios, 'post').resolves({ data: mockData.utxos })
const addr = [ const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
] ]
const result = await bchjs.Electrumx.utxo(addr) const result = await bchjs.Electrumx.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "utxos") assert.property(result, 'utxos')
assert.isArray(result.utxos) assert.isArray(result.utxos)
assert.property(result.utxos[0], "utxos") assert.property(result.utxos[0], 'utxos')
assert.isArray(result.utxos[0].utxos) assert.isArray(result.utxos[0].utxos)
assert.property(result.utxos[0], "address") assert.property(result.utxos[0], 'address')
assert.property(result.utxos[0].utxos[0], "height") assert.property(result.utxos[0].utxos[0], 'height')
assert.property(result.utxos[0].utxos[0], "tx_hash") assert.property(result.utxos[0].utxos[0], 'tx_hash')
assert.property(result.utxos[0].utxos[0], "tx_pos") assert.property(result.utxos[0].utxos[0], 'tx_pos')
assert.property(result.utxos[0].utxos[0], "value") assert.property(result.utxos[0].utxos[0], 'value')
}) })
}) })
describe(`#balance`, () => { describe('#balance', () => {
it(`should throw an error for improper input`, async () => { it('should throw an error for improper input', async () => {
try { try {
const addr = 12345 const addr = 12345
await bchjs.Electrumx.balance(addr) await bchjs.Electrumx.balance(addr)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include( assert.include(
err.message, err.message,
`Input address must be a string or array of strings` 'Input address must be a string or array of strings'
) )
} }
}) })
it(`should GET balance for a single address`, async () => { it('should GET balance for a single address', async () => {
// Stub the network call. // Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.balance }) sandbox.stub(axios, 'get').resolves({ data: mockData.balance })
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const result = await bchjs.Electrumx.balance(addr) const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "balance") assert.property(result, 'balance')
assert.property(result.balance, "confirmed") assert.property(result.balance, 'confirmed')
assert.property(result.balance, "unconfirmed") assert.property(result.balance, 'unconfirmed')
}) })
it(`should POST balance for an array of addresses`, async () => { it('should POST balance for an array of addresses', async () => {
// Stub the network call. // Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.balances }) sandbox.stub(axios, 'post').resolves({ data: mockData.balances })
const addr = [ const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
] ]
const result = await bchjs.Electrumx.balance(addr) const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "balances") assert.property(result, 'balances')
assert.isArray(result.balances) assert.isArray(result.balances)
assert.property(result.balances[0], "address") assert.property(result.balances[0], 'address')
assert.property(result.balances[0], "balance") assert.property(result.balances[0], 'balance')
assert.property(result.balances[0].balance, "confirmed") assert.property(result.balances[0].balance, 'confirmed')
assert.property(result.balances[0].balance, "unconfirmed") assert.property(result.balances[0].balance, 'unconfirmed')
}) })
}) })
describe(`#transactions`, () => { describe('#transactions', () => {
it(`should throw an error for improper input`, async () => { it('should throw an error for improper input', async () => {
try { try {
const addr = 12345 const addr = 12345
await bchjs.Electrumx.transactions(addr) await bchjs.Electrumx.transactions(addr)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include( assert.include(
err.message, err.message,
`Input address must be a string or array of strings` 'Input address must be a string or array of strings'
) )
} }
}) })
it(`should GET transactions for a single address`, async () => { it('should GET transactions for a single address', async () => {
// Stub the network call. // Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.transaction }) sandbox.stub(axios, 'get').resolves({ data: mockData.transaction })
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const result = await bchjs.Electrumx.transactions(addr) const result = await bchjs.Electrumx.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "transactions") assert.property(result, 'transactions')
assert.isArray(result.transactions) assert.isArray(result.transactions)
assert.property(result.transactions[0], "height") assert.property(result.transactions[0], 'height')
assert.property(result.transactions[0], "tx_hash") assert.property(result.transactions[0], 'tx_hash')
}) })
it(`should POST transaction history for an array of addresses`, async () => { it('should POST transaction history for an array of addresses', async () => {
// Stub the network call. // Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.transactions }) sandbox.stub(axios, 'post').resolves({ data: mockData.transactions })
const addr = [ const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
] ]
const result = await bchjs.Electrumx.transactions(addr) const result = await bchjs.Electrumx.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "transactions") assert.property(result, 'transactions')
assert.isArray(result.transactions) assert.isArray(result.transactions)
assert.property(result.transactions[0], "address") assert.property(result.transactions[0], 'address')
assert.property(result.transactions[0], "transactions") assert.property(result.transactions[0], 'transactions')
assert.isArray(result.transactions[0].transactions) assert.isArray(result.transactions[0].transactions)
assert.property(result.transactions[0].transactions[0], "height") assert.property(result.transactions[0].transactions[0], 'height')
assert.property(result.transactions[0].transactions[0], "tx_hash") assert.property(result.transactions[0].transactions[0], 'tx_hash')
}) })
}) })
describe(`#unconfirmed`, () => { describe('#unconfirmed', () => {
it(`should throw an error for improper input`, async () => { it('should throw an error for improper input', async () => {
try { try {
const addr = 12345 const addr = 12345
await bchjs.Electrumx.unconfirmed(addr) await bchjs.Electrumx.unconfirmed(addr)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include( assert.include(
err.message, err.message,
`Input address must be a string or array of strings` 'Input address must be a string or array of strings'
) )
} }
}) })
it(`should GET unconfirmed utxos for a single address`, async () => { it('should GET unconfirmed utxos for a single address', async () => {
// Stub the network call. // Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.unconfirmed }) sandbox.stub(axios, 'get').resolves({ data: mockData.unconfirmed })
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const result = await bchjs.Electrumx.unconfirmed(addr) const result = await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "utxos") assert.property(result, 'utxos')
assert.isArray(result.utxos) assert.isArray(result.utxos)
assert.property(result.utxos[0], "height") assert.property(result.utxos[0], 'height')
assert.property(result.utxos[0], "tx_hash") assert.property(result.utxos[0], 'tx_hash')
assert.property(result.utxos[0], "fee") assert.property(result.utxos[0], 'fee')
}) })
it(`should POST unconfirmed utxo details for an array of addresses`, async () => { it('should POST unconfirmed utxo details for an array of addresses', async () => {
// Stub the network call. // Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.unconfirmedArray }) sandbox.stub(axios, 'post').resolves({ data: mockData.unconfirmedArray })
const addr = [ const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
] ]
const result = await bchjs.Electrumx.unconfirmed(addr) const result = await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "utxos") assert.property(result, 'utxos')
assert.isArray(result.utxos) assert.isArray(result.utxos)
assert.property(result.utxos[0], "utxos") assert.property(result.utxos[0], 'utxos')
assert.isArray(result.utxos[0].utxos) assert.isArray(result.utxos[0].utxos)
assert.property(result.utxos[0], "address") assert.property(result.utxos[0], 'address')
assert.property(result.utxos[0].utxos[0], "height") assert.property(result.utxos[0].utxos[0], 'height')
assert.property(result.utxos[0].utxos[0], "tx_hash") assert.property(result.utxos[0].utxos[0], 'tx_hash')
assert.property(result.utxos[0].utxos[0], "fee") assert.property(result.utxos[0].utxos[0], 'fee')
}) })
}) })
describe(`#blockHeader`, () => { describe('#blockHeader', () => {
it(`should throw an error for improper height input`, async () => { it('should throw an error for improper height input', async () => {
try { try {
// Mock network calls. // Mock network calls.
sandbox.stub(axios, "get").rejects({ sandbox.stub(axios, 'get').rejects({
response: { response: {
data: { data: {
success: false, success: false,
error: "height must be a positive number" error: 'height must be a positive number'
} }
} }
}) })
@@ -277,21 +277,21 @@ describe(`#ElectrumX`, () => {
const height = -10 const height = -10
await bchjs.Electrumx.blockHeader(height) await bchjs.Electrumx.blockHeader(height)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include(err.message, `height must be a positive number`) assert.include(err.message, 'height must be a positive number')
} }
}) })
it(`should throw an error for improper count input`, async () => { it('should throw an error for improper count input', async () => {
try { try {
// Mock network calls. // Mock network calls.
sandbox.stub(axios, "get").rejects({ sandbox.stub(axios, 'get').rejects({
response: { response: {
data: { data: {
success: false, success: false,
error: "count must be a positive number" error: 'count must be a positive number'
} }
} }
}) })
@@ -300,16 +300,16 @@ describe(`#ElectrumX`, () => {
const count = -10 const count = -10
await bchjs.Electrumx.blockHeader(height, count) await bchjs.Electrumx.blockHeader(height, count)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include(err.message, `count must be a positive number`) assert.include(err.message, 'count must be a positive number')
} }
}) })
it(`should GET block headers for a given height`, async () => { it('should GET block headers for a given height', async () => {
// Stub the network call. // Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.blockHeaders }) sandbox.stub(axios, 'get').resolves({ data: mockData.blockHeaders })
const height = 42 const height = 42
@@ -320,102 +320,175 @@ describe(`#ElectrumX`, () => {
}) })
}) })
describe(`#txData`, () => { describe('#txData', () => {
it(`should throw an error for improper input`, async () => { it('should throw an error for improper input', async () => {
try { try {
const txid = 12345 const txid = 12345
await bchjs.Electrumx.txData(txid) await bchjs.Electrumx.txData(txid)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include( assert.include(
err.message, err.message,
`Input txId must be a string or array of strings` 'Input txId must be a string or array of strings'
) )
} }
}) })
it(`should GET details data for a single transaction`, async () => { it('should GET details data for a single transaction', async () => {
// Stub the network call. // Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.details }) sandbox.stub(axios, 'get').resolves({ data: mockData.details })
const txid = const txid =
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'
const result = await bchjs.Electrumx.txData(txid) const result = await bchjs.Electrumx.txData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "details") assert.property(result, 'details')
assert.isObject(result.details) assert.isObject(result.details)
assert.property(result.details, "blockhash") assert.property(result.details, 'blockhash')
assert.property(result.details, "hash") assert.property(result.details, 'hash')
assert.property(result.details, "hex") assert.property(result.details, 'hex')
assert.property(result.details, "vin") assert.property(result.details, 'vin')
assert.property(result.details, "vout") assert.property(result.details, 'vout')
assert.equal(result.details.hash, txid) assert.equal(result.details.hash, txid)
}) })
it(`should POST details for an array of transactions`, async () => { it('should POST details for an array of transactions', async () => {
// Stub the network call. // Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.detailsArray }) sandbox.stub(axios, 'post').resolves({ data: mockData.detailsArray })
const txids = [ const txids = [
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251", '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251',
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'
] ]
const result = await bchjs.Electrumx.txData(txids) const result = await bchjs.Electrumx.txData(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "transactions") assert.property(result, 'transactions')
assert.isArray(result.transactions) assert.isArray(result.transactions)
assert.property(result.transactions[0], "txid") assert.property(result.transactions[0], 'txid')
assert.property(result.transactions[0], "details") assert.property(result.transactions[0], 'details')
assert.property(result.transactions[0].details, "blockhash") assert.property(result.transactions[0].details, 'blockhash')
assert.property(result.transactions[0].details, "hash") assert.property(result.transactions[0].details, 'hash')
assert.property(result.transactions[0].details, "hex") assert.property(result.transactions[0].details, 'hex')
assert.property(result.transactions[0].details, "vin") assert.property(result.transactions[0].details, 'vin')
assert.property(result.transactions[0].details, "vout") assert.property(result.transactions[0].details, 'vout')
assert.equal(result.transactions.length, 2, "2 outputs for 2 inputs") assert.equal(result.transactions.length, 2, '2 outputs for 2 inputs')
}) })
}) })
describe(`#broadcast`, () => {
it(`should throw an error for improper input`, async () => { describe('#broadcast', () => {
it('should throw an error for improper input', async () => {
try { try {
const txHex = 12345 const txHex = 12345
await bchjs.Electrumx.broadcast(txHex) await bchjs.Electrumx.broadcast(txHex)
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include(err.message, `Input txHex must be a string.`) assert.include(err.message, 'Input txHex must be a string.')
} }
}) })
it(`should broadcast a single transaction`, async () => {
it('should broadcast a single transaction', async () => {
// Stub the network call. // Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.broadcast }) sandbox.stub(axios, 'post').resolves({ data: mockData.broadcast })
const tx = mockData.details const tx = mockData.details
const txid = tx.details.txid const txid = tx.details.txid
const result = await bchjs.Electrumx.broadcast(tx.details.hex) const result = await bchjs.Electrumx.broadcast(tx.details.hex)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "txid") assert.property(result, 'txid')
assert.equal(result.txid, txid) assert.equal(result.txid, txid)
}) })
}) })
describe('#sortConfTxs', () => {
it('should sort in ascending order', () => {
const result = bchjs.Electrumx.sortConfTxs(mockData.transaction.transactions, 'ASCENDING')
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isBelow(result[0].height, result[1].height)
})
it('should sort in descending order by default', () => {
const result = bchjs.Electrumx.sortConfTxs(mockData.transaction.transactions)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(result[0].height, result[1].height)
})
it('should ignore unconfirmed txs', () => {
const result = bchjs.Electrumx.sortConfTxs(mockData.txHistoryWithUnconfirmed.transactions)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(result[0].height, 0)
})
it('should handle errors', () => {
try {
bchjs.Electrumx.sortConfTxs('abc')
assert.fail('Unexpected result')
} catch (err) {
// console.log(err.message)
assert.equal(err.message, 'txs.filter is not a function')
}
})
})
// These tests use mocked data that contains unconfirmed transactions.
describe('#sortAllTxs', () => {
it('should sort in ascending', async () => {
// Stub network calls
sandbox.stub(bchjs.Electrumx.blockchain, 'getBlockCount').resolves(672141)
const result = await bchjs.Electrumx.sortAllTxs(mockData.txHistoryWithUnconfirmed.transactions, 'ASCENDING')
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isBelow(result[0].height, result[1].height)
})
it('should sort in descending order by default', async () => {
// Stub network calls
sandbox.stub(bchjs.Electrumx.blockchain, 'getBlockCount').resolves(672141)
const result = await bchjs.Electrumx.sortAllTxs(mockData.txHistoryWithUnconfirmed.transactions)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(result[1].height, result[2].height)
})
it('should handle errors', async () => {
try {
// Stub network calls
sandbox.stub(bchjs.Electrumx.blockchain, 'getBlockCount').resolves(672141)
await bchjs.Electrumx.sortAllTxs('abc')
assert.fail('Unexpected result')
} catch (err) {
// console.log(err.message)
assert.equal(err.message, 'txs.map is not a function')
}
})
})
}) })
+20 -20
View File
@@ -1,13 +1,13 @@
const assert = require("chai").assert const assert = require('chai').assert
const sinon = require("sinon") const sinon = require('sinon')
const BCHJS = require("../../src/bch-js") const BCHJS = require('../../src/bch-js')
// const bchjs = new BCHJS() // const bchjs = new BCHJS()
let bchjs let bchjs
const mockData = require("./fixtures/encryption-mock") const mockData = require('./fixtures/encryption-mock')
describe("#Encryption", () => { describe('#Encryption', () => {
let sandbox let sandbox
beforeEach(() => { beforeEach(() => {
@@ -17,52 +17,52 @@ describe("#Encryption", () => {
afterEach(() => sandbox.restore()) afterEach(() => sandbox.restore())
describe("#getPubKey", () => { describe('#getPubKey', () => {
it("should throw error if BCH address is not provided.", async () => { it('should throw error if BCH address is not provided.', async () => {
try { try {
await bchjs.encryption.getPubKey() await bchjs.encryption.getPubKey()
assert.equal(true, false, "Unexpected result!") assert.equal(true, false, 'Unexpected result!')
} catch (err) { } catch (err) {
// console.log(`err: `, err) // console.log(`err: `, err)
assert.include( assert.include(
err.message, err.message,
"Input must be a valid Bitcoin Cash address" 'Input must be a valid Bitcoin Cash address'
) )
} }
}) })
it("should report when public key can not be found", async () => { it('should report when public key can not be found', async () => {
// Stub the network call. // Stub the network call.
sandbox sandbox
.stub(bchjs.encryption.axios, "get") .stub(bchjs.encryption.axios, 'get')
.resolves({ data: mockData.failureMock }) .resolves({ data: mockData.failureMock })
const addr = "bitcoincash:qpxqr2pmcverj4vukgjqssvk2zju8tp9xsgz2nqagx" const addr = 'bitcoincash:qpxqr2pmcverj4vukgjqssvk2zju8tp9xsgz2nqagx'
const result = await bchjs.encryption.getPubKey(addr) const result = await bchjs.encryption.getPubKey(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, false) assert.equal(result.success, false)
assert.property(result, "publicKey") assert.property(result, 'publicKey')
assert.equal(result.publicKey, "not found") assert.equal(result.publicKey, 'not found')
}) })
it("should get a public key", async () => { it('should get a public key', async () => {
// Stub the network call. // Stub the network call.
sandbox sandbox
.stub(bchjs.encryption.axios, "get") .stub(bchjs.encryption.axios, 'get')
.resolves({ data: mockData.successMock }) .resolves({ data: mockData.successMock })
const addr = "bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq" const addr = 'bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq'
const result = await bchjs.encryption.getPubKey(addr) const result = await bchjs.encryption.getPubKey(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`) // console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success") assert.property(result, 'success')
assert.equal(result.success, true) assert.equal(result.success, true)
assert.property(result, "publicKey") assert.property(result, 'publicKey')
}) })
}) })
}) })
+14 -14
View File
@@ -6,34 +6,34 @@ const balance = { confirmed: 230000, unconfirmed: 0, balance: 230000 }
const utxo = [ const utxo = [
{ {
_id: "5cecdd39a9f1235e2a3d409a", _id: '5cecdd39a9f1235e2a3d409a',
chain: "BCH", chain: 'BCH',
network: "mainnet", network: 'mainnet',
coinbase: false, coinbase: false,
mintIndex: 0, mintIndex: 0,
spentTxid: "", spentTxid: '',
mintTxid: mintTxid:
"27ec8512c1a9ee9e9ae9b98eb60375f1d2bd60e2e76a1eff5a45afdbc517cf9c", '27ec8512c1a9ee9e9ae9b98eb60375f1d2bd60e2e76a1eff5a45afdbc517cf9c',
mintHeight: 560430, mintHeight: 560430,
spentHeight: -2, spentHeight: -2,
address: "qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", address: 'qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
script: "76a914db6ea94fa26b7272dc5e1487c35f258391e0f38788ac", script: '76a914db6ea94fa26b7272dc5e1487c35f258391e0f38788ac',
value: 100000, value: 100000,
confirmations: -1 confirmations: -1
}, },
{ {
_id: "5cecdd1ca9f1235e2a3b6349", _id: '5cecdd1ca9f1235e2a3b6349',
chain: "BCH", chain: 'BCH',
network: "mainnet", network: 'mainnet',
coinbase: false, coinbase: false,
mintIndex: 0, mintIndex: 0,
spentTxid: "", spentTxid: '',
mintTxid: mintTxid:
"6e1ae1bf7db6de799ec1c05ab2816ac65549bd80141567af088e6f291385b07d", '6e1ae1bf7db6de799ec1c05ab2816ac65549bd80141567af088e6f291385b07d',
mintHeight: 560039, mintHeight: 560039,
spentHeight: -2, spentHeight: -2,
address: "qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", address: 'qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf',
script: "76a914db6ea94fa26b7272dc5e1487c35f258391e0f38788ac", script: '76a914db6ea94fa26b7272dc5e1487c35f258391e0f38788ac',
value: 130000, value: 130000,
confirmations: -1 confirmations: -1
} }
+7 -7
View File
@@ -4,24 +4,24 @@
module.exports = { module.exports = {
details: { details: {
hash: "000000001c6aeec19265e9cc3ded8ba5ef5e63fae7747f30bf9c02c7bc8883f0", hash: '000000001c6aeec19265e9cc3ded8ba5ef5e63fae7747f30bf9c02c7bc8883f0',
size: 216, size: 216,
height: 507, height: 507,
version: 1, version: 1,
merkleroot: merkleroot:
"a85fa3d831ab6b0305e7ff88d2d4941e25a810d4461635df51490653822071a8", 'a85fa3d831ab6b0305e7ff88d2d4941e25a810d4461635df51490653822071a8',
tx: ["a85fa3d831ab6b0305e7ff88d2d4941e25a810d4461635df51490653822071a8"], tx: ['a85fa3d831ab6b0305e7ff88d2d4941e25a810d4461635df51490653822071a8'],
time: 1231973656, time: 1231973656,
nonce: 330467862, nonce: 330467862,
bits: "1d00ffff", bits: '1d00ffff',
difficulty: 1, difficulty: 1,
chainwork: chainwork:
"000000000000000000000000000000000000000000000000000001fc01fc01fc", '000000000000000000000000000000000000000000000000000001fc01fc01fc',
confirmations: 585104, confirmations: 585104,
previousblockhash: previousblockhash:
"00000000a99525c043fd7e323414b60add43c254c44860094048f9c01e9a5fdd", '00000000a99525c043fd7e323414b60add43c254c44860094048f9c01e9a5fdd',
nextblockhash: nextblockhash:
"000000000d550f4161f2702165fdd782ec72ff9c541f864ebb8256b662b7e51a", '000000000d550f4161f2702165fdd782ec72ff9c541f864ebb8256b662b7e51a',
reward: 50, reward: 50,
isMainChain: true, isMainChain: true,
poolInfo: {} poolInfo: {}
-64
View File
@@ -1,64 +0,0 @@
/*
Unit test mocks for Blockbook endpoints.
*/
const balance = {
page: 1,
totalPages: 1,
itemsOnPage: 1000,
address: "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
balance: "230000",
totalReceived: "6194528752",
totalSent: "6194298752",
unconfirmedBalance: "0",
unconfirmedTxs: 0,
txs: 3,
txids: [
"27ec8512c1a9ee9e9ae9b98eb60375f1d2bd60e2e76a1eff5a45afdbc517cf9c",
"6e1ae1bf7db6de799ec1c05ab2816ac65549bd80141567af088e6f291385b07d",
"5d95f4e6047901fc1502a9758bbdb14ce73f24662b1784d13e0217b3d37bb7a2"
]
}
const utxo = [
{
txid: "27ec8512c1a9ee9e9ae9b98eb60375f1d2bd60e2e76a1eff5a45afdbc517cf9c",
vout: 0,
value: "100000",
height: 560430,
confirmations: 29321
},
{
txid: "6e1ae1bf7db6de799ec1c05ab2816ac65549bd80141567af088e6f291385b07d",
vout: 0,
value: "130000",
height: 560039,
confirmations: 29712
}
]
const utxos = [
[
{
txid: "27ec8512c1a9ee9e9ae9b98eb60375f1d2bd60e2e76a1eff5a45afdbc517cf9c",
vout: 0,
value: "100000",
height: 560430,
confirmations: 29868
},
{
txid: "6e1ae1bf7db6de799ec1c05ab2816ac65549bd80141567af088e6f291385b07d",
vout: 0,
value: "130000",
height: 560039,
confirmations: 30259
}
],
[]
]
module.exports = {
balance,
utxo,
utxos
}
+15 -15
View File
@@ -4,47 +4,47 @@
module.exports = { module.exports = {
bestBlockHash: bestBlockHash:
"0000000000000000008e1f65f875703872544aa888c7ca6587f055f8f5fbd4bf", '0000000000000000008e1f65f875703872544aa888c7ca6587f055f8f5fbd4bf',
blockHeader: { blockHeader: {
hash: "000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201", hash: '000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201',
confirmations: 85727, confirmations: 85727,
height: 500000, height: 500000,
version: 536870912, version: 536870912,
versionHex: "20000000", versionHex: '20000000',
merkleroot: merkleroot:
"4af279645e1b337e655ae3286fc2ca09f58eb01efa6ab27adedd1e9e6ec19091", '4af279645e1b337e655ae3286fc2ca09f58eb01efa6ab27adedd1e9e6ec19091',
time: 1509343584, time: 1509343584,
mediantime: 1509336533, mediantime: 1509336533,
nonce: 3604508752, nonce: 3604508752,
bits: "1809b91a", bits: '1809b91a',
difficulty: 113081236211.4533, difficulty: 113081236211.4533,
chainwork: chainwork:
"0000000000000000000000000000000000000000007ae48aca46e3b449ad9714", '0000000000000000000000000000000000000000007ae48aca46e3b449ad9714',
previousblockhash: previousblockhash:
"0000000000000000043831d6ebb013716f0580287ee5e5687e27d0ed72e6e523", '0000000000000000043831d6ebb013716f0580287ee5e5687e27d0ed72e6e523',
nextblockhash: nextblockhash:
"00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3" '00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3'
}, },
txOutProof: txOutProof:
"0000002086a4a3161f9ba2174883ec0b93acceac3b2f37b36ed1f90000000000000000009cb02406d1094ecf3e0b4c0ca7c585125e721147c39daf6b48c90b512741e13a12333e5cb38705180f441d8c7100000008fee9b60f1edb57e5712839186277ed39e0a004a32be9096ee47472efde8eae62f789f9d7a9f59d0ea7093dea1e0c65ff0b953f1d8cf3d47f92e732ca0295f603c272d5f4a63509f7a887f2549d78af7444aa0ecbb4f66d9cbe13bc6a89f59e05a199df8325d490818ffefe6b6321d32d7496a68580459836c0183f89082fc1b491cc91b23ecdcaa4c347bf599a62904d61f1c15b400ebbd5c90149010c139d9c1e31b774b796977393a238080ab477e1d240d0c4f155d36f519668f49bae6bd8cd5b8e40522edf76faa09cca6188d83ff13af6967cc6a569d1a5e9aeb1fdb7f531ddd2d0cbb81879741d5f38166ac1932136264366a4065cc96a42e41f96294f02df01", '0000002086a4a3161f9ba2174883ec0b93acceac3b2f37b36ed1f90000000000000000009cb02406d1094ecf3e0b4c0ca7c585125e721147c39daf6b48c90b512741e13a12333e5cb38705180f441d8c7100000008fee9b60f1edb57e5712839186277ed39e0a004a32be9096ee47472efde8eae62f789f9d7a9f59d0ea7093dea1e0c65ff0b953f1d8cf3d47f92e732ca0295f603c272d5f4a63509f7a887f2549d78af7444aa0ecbb4f66d9cbe13bc6a89f59e05a199df8325d490818ffefe6b6321d32d7496a68580459836c0183f89082fc1b491cc91b23ecdcaa4c347bf599a62904d61f1c15b400ebbd5c90149010c139d9c1e31b774b796977393a238080ab477e1d240d0c4f155d36f519668f49bae6bd8cd5b8e40522edf76faa09cca6188d83ff13af6967cc6a569d1a5e9aeb1fdb7f531ddd2d0cbb81879741d5f38166ac1932136264366a4065cc96a42e41f96294f02df01',
verifiedProof: verifiedProof:
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7", '03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7',
txOutUnspent: { txOutUnspent: {
bestblock: bestblock:
"000000000000000000b441e02f5b1b9f5b3def961047afcc6f2f5636c952705e", '000000000000000000b441e02f5b1b9f5b3def961047afcc6f2f5636c952705e',
confirmations: 2, confirmations: 2,
value: 0.00006, value: 0.00006,
scriptPubKey: { scriptPubKey: {
asm: asm:
"OP_DUP OP_HASH160 d19fae66b685f5c3633c0db0600313918347225f OP_EQUALVERIFY OP_CHECKSIG", 'OP_DUP OP_HASH160 d19fae66b685f5c3633c0db0600313918347225f OP_EQUALVERIFY OP_CHECKSIG',
hex: "76a914d19fae66b685f5c3633c0db0600313918347225f88ac", hex: '76a914d19fae66b685f5c3633c0db0600313918347225f88ac',
reqSigs: 1, reqSigs: 1,
type: "pubkeyhash", type: 'pubkeyhash',
addresses: ["bitcoincash:qrgeltnxk6zltsmr8sxmqcqrzwgcx3eztusrwgf0x3"] addresses: ['bitcoincash:qrgeltnxk6zltsmr8sxmqcqrzwgcx3eztusrwgf0x3']
}, },
coinbase: false coinbase: false
} }
+19
View File
@@ -0,0 +1,19 @@
/*
Mock data used for unit testing.
*/
const dsproof = {
dspid: '6234fff2a9dbcaa50e2c50d74e1d725a2bed1757d7a05a2db7a82df659554397',
txid: 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e',
outpoint: {
txid: '17ebef34f7e9a0692317dd6fbb43ffccff45b7d688a2a754dbccfc5e6d93ce3e',
vout: 1
},
path: ['ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'],
descendants: [
'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
]
}
module.exports = {
dsproof
}
+63 -27
View File
@@ -8,7 +8,7 @@ const utxo = {
{ {
height: 602405, height: 602405,
tx_hash: tx_hash:
"2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7", '2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7',
tx_pos: 0, tx_pos: 0,
value: 1000 value: 1000
} }
@@ -23,23 +23,23 @@ const utxos = {
{ {
height: 604392, height: 604392,
tx_hash: tx_hash:
"7774e449c5a3065144cefbc4c0c21e6b69c987f095856778ef9f45ddd8ae1a41", '7774e449c5a3065144cefbc4c0c21e6b69c987f095856778ef9f45ddd8ae1a41',
tx_pos: 0, tx_pos: 0,
value: 1000 value: 1000
}, },
{ {
height: 630834, height: 630834,
tx_hash: tx_hash:
"4fe60a51e0d8f5134bfd8e5f872d6e502d7f01b28a6afebb27f4438a4f638d53", '4fe60a51e0d8f5134bfd8e5f872d6e502d7f01b28a6afebb27f4438a4f638d53',
tx_pos: 0, tx_pos: 0,
value: 6000 value: 6000
} }
], ],
address: "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" address: 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
}, },
{ {
utxos: [], utxos: [],
address: "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" address: 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
} }
] ]
} }
@@ -60,14 +60,14 @@ const balances = {
confirmed: 7000, confirmed: 7000,
unconfirmed: 0 unconfirmed: 0
}, },
address: "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" address: 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
}, },
{ {
balance: { balance: {
confirmed: 0, confirmed: 0,
unconfirmed: 0 unconfirmed: 0
}, },
address: "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" address: 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
} }
] ]
} }
@@ -78,12 +78,12 @@ const transaction = {
{ {
height: 560430, height: 560430,
tx_hash: tx_hash:
"3e1f3e882be9c03897eeb197224bf87f312be556a89f4308fabeeeabcf9bc851" '3e1f3e882be9c03897eeb197224bf87f312be556a89f4308fabeeeabcf9bc851'
}, },
{ {
height: 560534, height: 560534,
tx_hash: tx_hash:
"4ebbeaac51ce141e262964e3a0ce11b96ca72c0dffe9b4127ce80135f503a280" '4ebbeaac51ce141e262964e3a0ce11b96ca72c0dffe9b4127ce80135f503a280'
} }
] ]
} }
@@ -96,25 +96,25 @@ const transactions = {
{ {
height: 631219, height: 631219,
tx_hash: tx_hash:
"ae2daa01c8172545b5edd205ea438706bcb74e63d4084a26b9ff2a46d46dc97f" 'ae2daa01c8172545b5edd205ea438706bcb74e63d4084a26b9ff2a46d46dc97f'
} }
], ],
address: "bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj" address: 'bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj'
}, },
{ {
transactions: [ transactions: [
{ {
height: 560430, height: 560430,
tx_hash: tx_hash:
"3e1f3e882be9c03897eeb197224bf87f312be556a89f4308fabeeeabcf9bc851" '3e1f3e882be9c03897eeb197224bf87f312be556a89f4308fabeeeabcf9bc851'
}, },
{ {
height: 560534, height: 560534,
tx_hash: tx_hash:
"4ebbeaac51ce141e262964e3a0ce11b96ca72c0dffe9b4127ce80135f503a280" '4ebbeaac51ce141e262964e3a0ce11b96ca72c0dffe9b4127ce80135f503a280'
} }
], ],
address: "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" address: 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
} }
] ]
} }
@@ -125,7 +125,7 @@ const unconfirmed = {
{ {
height: 602405, height: 602405,
tx_hash: tx_hash:
"2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7", '2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7',
fee: 34100 fee: 34100
} }
] ]
@@ -139,21 +139,21 @@ const unconfirmedArray = {
{ {
height: 604392, height: 604392,
tx_hash: tx_hash:
"7774e449c5a3065144cefbc4c0c21e6b69c987f095856778ef9f45ddd8ae1a41", '7774e449c5a3065144cefbc4c0c21e6b69c987f095856778ef9f45ddd8ae1a41',
fee: 34210 fee: 34210
}, },
{ {
height: 630834, height: 630834,
tx_hash: tx_hash:
"4fe60a51e0d8f5134bfd8e5f872d6e502d7f01b28a6afebb27f4438a4f638d53", '4fe60a51e0d8f5134bfd8e5f872d6e502d7f01b28a6afebb27f4438a4f638d53',
value: 3000 value: 3000
} }
], ],
address: "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" address: 'bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf'
}, },
{ {
utxos: [], utxos: [],
address: "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" address: 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
} }
] ]
} }
@@ -168,7 +168,8 @@ const txDetails = {
blocktime: 1578327094, blocktime: 1578327094,
confirmations: 31861, confirmations: 31861,
hash: '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251', hash: '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251',
hex: '020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000', hex:
'020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000',
locktime: 0, locktime: 0,
size: 392, size: 392,
time: 1578327094, time: 1578327094,
@@ -177,8 +178,10 @@ const txDetails = {
vin: [ vin: [
{ {
scriptSig: { scriptSig: {
asm: 'dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e[ALL|FORKID] 020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309', asm:
hex: '41dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309' 'dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e[ALL|FORKID] 020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309',
hex:
'41dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309'
}, },
sequence: 4294967295, sequence: 4294967295,
txid: '6796672c8f0342770e3d4ac2a3b0e4494daeb7af7997f3518a0c8402f43ed165', txid: '6796672c8f0342770e3d4ac2a3b0e4494daeb7af7997f3518a0c8402f43ed165',
@@ -186,8 +189,10 @@ const txDetails = {
}, },
{ {
scriptSig: { scriptSig: {
asm: '347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f77[ALL|FORKID] 028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954', asm:
hex: '41347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954' '347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f77[ALL|FORKID] 028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954',
hex:
'41347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954'
}, },
sequence: 4294967295, sequence: 4294967295,
txid: '6796672c8f0342770e3d4ac2a3b0e4494daeb7af7997f3518a0c8402f43ed165', txid: '6796672c8f0342770e3d4ac2a3b0e4494daeb7af7997f3518a0c8402f43ed165',
@@ -210,7 +215,8 @@ const txDetails = {
n: 1, n: 1,
scriptPubKey: { scriptPubKey: {
addresses: ['bitcoincash: qq59hv6s3qdjrtyfwfxxldkuj9xsjmx48vrz882knz'], addresses: ['bitcoincash: qq59hv6s3qdjrtyfwfxxldkuj9xsjmx48vrz882knz'],
asm: 'OP_DUP OP_HASH160 285bb350881b21ac89724c6fb6dc914d096cd53b OP_EQUALVERIFY OP_CHECKSIG', asm:
'OP_DUP OP_HASH160 285bb350881b21ac89724c6fb6dc914d096cd53b OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914285bb350881b21ac89724c6fb6dc914d096cd53b88ac', hex: '76a914285bb350881b21ac89724c6fb6dc914d096cd53b88ac',
reqSigs: 1, reqSigs: 1,
type: 'pubkeyhash' type: 'pubkeyhash'
@@ -221,7 +227,8 @@ const txDetails = {
n: 2, n: 2,
scriptPubKey: { scriptPubKey: {
addresses: ['bitcoincash: qpzlruwy4xu5rxjs3z37nsj29y7h59gwvsu4ddp0u4'], addresses: ['bitcoincash: qpzlruwy4xu5rxjs3z37nsj29y7h59gwvsu4ddp0u4'],
asm: 'OP_DUP OP_HASH160 45f1f1c4a9b9419a5088a3e9c24a293d7a150e64 OP_EQUALVERIFY OP_CHECKSIG', asm:
'OP_DUP OP_HASH160 45f1f1c4a9b9419a5088a3e9c24a293d7a150e64 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac', hex: '76a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac',
reqSigs: 1, reqSigs: 1,
type: 'pubkeyhash' type: 'pubkeyhash'
@@ -255,6 +262,34 @@ const broadcast = {
txid: txDetails.txid txid: txDetails.txid
} }
const txHistoryWithUnconfirmed = {
success: true,
transactions: [
{
height: 668539,
tx_hash:
'fd9bffca3dd7a2d801c87cb36e8b43949d2e171bb8e25e3d92cf39ac5e2ba179'
},
{
height: 668544,
tx_hash:
'9d1b438aa55d75da8ebe9dcdfa4fa96714042ce0a19c8f3c10d86e1e3dcbccf5'
},
{
fee: 440,
height: 0,
tx_hash:
'30d3fa9bf78eab2162d4f1894223e417e7275fdf0d590fe5085381dd475ff077'
},
{
fee: 468,
height: -1,
tx_hash:
'4b93c26a4612b6819fde2bf5d1f6387bf92f49a3375c53a472001e7d76a80e59'
}
]
}
module.exports = { module.exports = {
utxo, utxo,
utxos, utxos,
@@ -267,5 +302,6 @@ module.exports = {
blockHeaders, blockHeaders,
details, details,
detailsArray, detailsArray,
broadcast broadcast,
txHistoryWithUnconfirmed
} }

Some files were not shown because too many files have changed in this diff Show More