Compare commits

...

272 Commits

Author SHA1 Message Date
Simon 53a3a2845b Fix package.json 2018-09-18 09:41:35 -07:00
Simon 1803cb983e Add Sapling transaction support. 2018-09-17 22:45:57 -07:00
Ian Munoz 1ab4e2fd55
Merge pull request #1 from ianamunoz/master
change source in scaffold/lib/create.js
2018-06-18 12:12:10 -04:00
Ian Munoz d85bd7404f Merge branch 'master' of https://github.com/zcash-hackworks/bitcore-node-zcash 2018-06-18 10:32:13 -04:00
Ian Munoz f2e287d901 change url to zcash-hackworks in lib/scaffold/create.js 2018-06-18 10:22:22 -04:00
Simon 34a0a90e1d Update url to zcash-hackworks. 2018-05-09 14:26:14 -07:00
Simon 4f0edd4bd6 Update contributors. 2018-05-07 23:37:20 -07:00
Simon 0fe30a61f8 Add support for Overwinter transactions. 2018-05-07 23:35:57 -07:00
Ian Munoz 3405d2edeb update dependencies to my repo 2018-05-01 12:17:26 -06:00
Jack Grigg 9212ef3c9e Create nodes using GitHub links to bitcore-*-zcash packages 2016-08-30 17:15:45 +12:00
Jack Grigg f5b864f29e Remove hosted download 2016-08-30 15:42:10 +12:00
Jack Grigg 433a08d070 Add JoinSplit public values to getDetailedTransaction() 2016-08-29 21:01:16 +12:00
Jack Grigg 3ecbe21697 Zcash-ify console output 2016-08-28 14:53:22 +12:00
Jack Grigg 112c6d896d Zcash-ify daemon, config files etc. 2016-08-28 14:53:22 +12:00
Jack Grigg 10b44b6392 Use bitcore-lib-zcash 2016-08-28 14:53:21 +12:00
Jack Grigg 0a9d654ca7 Zcash-ify package 2016-08-28 14:52:41 +12:00
Braydon Fuller 639fdc941e Bump package version to 3.1.2 2016-07-28 10:59:12 -04:00
Chris Kleeschulte b5401b0269 Merge pull request #472 from braydonf/0.12.1-bitcore-2
Bump bitcoind tag to v0.12.1-bitcore-2
2016-07-28 10:53:01 -04:00
Braydon Fuller 75d41acdd6 Bump bitcoind tag to v0.12.1-bitcore-2 2016-07-26 15:48:15 -04:00
Braydon Fuller f913784421 Bump package version to 3.1.1 2016-07-06 14:44:29 -04:00
Braydon Fuller a44df8c336 Merge branch 'docs-links' 2016-07-06 14:39:44 -04:00
Matias Alejo Garcia 5351284289 Merge pull request #458 from braydonf/config-payload
web: configure payload size
2016-07-06 15:36:12 -03:00
Braydon Fuller f6e0783f55 Update links to bitcoin bitcore branch 2016-07-05 10:12:44 -04:00
Braydon Fuller 716cee68ee Link directly to repository without redirect 2016-07-05 10:11:52 -04:00
Braydon Fuller fa79e694cf Fix second link to bitcoin service docs 2016-07-05 10:07:23 -04:00
Karel Bílek e602eb9a48 Fixing link to bitcoin service docs 2016-07-04 18:20:29 +02:00
Braydon Fuller 4dc664200b Update links in readme 2016-07-01 09:53:08 -04:00
Braydon Fuller cc4d8d4c5e Bump package version to 3.1.0 2016-06-27 13:04:10 -04:00
Braydon Fuller cfe12eb17e Merge pull request #457 from kleetus/master
Updated the download script for the latest bitcoind tag.
2016-06-27 11:03:18 -04:00
Chris Kleeschulte f15460a3d9 Adjusted tags. 2016-06-27 10:57:16 -04:00
Braydon Fuller f72fe82c60 Bump package version to 3.0.2 2016-06-20 10:25:48 -04:00
Braydon Fuller b7f888fc3e web: configure payload size 2016-06-16 13:36:30 -04:00
Chris Kleeschulte e5e9d60081 Updated the download script for the latest bitcoind tag. 2016-06-16 13:34:33 -04:00
Chris Kleeschulte 5e5551afbf Merge pull request #456 from braydonf/utxo-tests
test: add additional mempool related utxo tests
2016-06-13 11:09:54 -04:00
Chris Kleeschulte 4ade31ff11 Merge pull request #455 from braydonf/rpcoption
bitcoind: connect option for strict ssl
2016-06-13 11:09:33 -04:00
Chris Kleeschulte 3713a18b2b Merge pull request #454 from braydonf/docs-fix
docs: update config in services doc
2016-06-13 11:08:51 -04:00
Chris Kleeschulte 3a1034757c Merge pull request #453 from braydonf/try-all
bitcoind: fix timing issue with tryAll
2016-06-13 11:08:16 -04:00
Chris Kleeschulte 14f21f9f68 Merge pull request #449 from braydonf/address-history-concurrency
bitcoind: get detailed transactions with concurrency
2016-06-13 11:07:08 -04:00
Chris Kleeschulte ae7359cf93 Merge pull request #443 from braydonf/opt-service-methods
node: optional getAPIMethods and getPublishEvents
2016-06-13 11:06:03 -04:00
Chris Kleeschulte 4a220d8e69 Merge pull request #442 from braydonf/export-lib
index: export bitcore-lib as lib
2016-06-13 11:05:38 -04:00
Braydon Fuller a2a30b81d8 bitcoind: start tryAllClients with the current round-robin index 2016-06-10 19:05:22 -04:00
Chris Kleeschulte be8a5c7db1 Merge pull request #441 from braydonf/undef-config
node: handle undefined service config
2016-06-10 18:06:37 -04:00
Chris Kleeschulte 4e78500662 Merge pull request #440 from braydonf/cli-json
cli: parse json params
2016-06-10 18:05:06 -04:00
Braydon Fuller b528c851ab test: add additional mempool related utxo tests 2016-06-10 10:43:01 -04:00
Braydon Fuller 3dc6860cb3 bitcoind: connect option for strict ssl
This is to be able to configure the RPC client to handle self-signed
certificates for development purposes.
2016-06-09 16:39:03 -04:00
Braydon Fuller ec760dc44e docs: update config in services doc 2016-06-09 15:17:20 -04:00
Braydon Fuller 8f9af8241a Bump package version to 3.0.1 2016-06-09 11:35:34 -04:00
Braydon Fuller 6ac912545b bitcoind: _tryAll -> _tryAllClients
Fixes a timing bug with not all clients being tried
2016-06-09 11:18:56 -04:00
Braydon Fuller 3715f07c84 bitcoind: get detailed transactions with concurrency
increase performance of querying address history by executing multiple
rpc calls concurrently with a configurable limit
2016-06-07 09:01:42 -04:00
Braydon Fuller 70fae5335c node: optional getAPIMethods and getPublishEvents 2016-06-03 16:31:54 -04:00
Braydon Fuller d31438b22f index: export bitcore-lib as lib 2016-06-03 16:16:11 -04:00
Braydon Fuller 3043263e3b node: handle undefined service config 2016-06-03 15:54:01 -04:00
Braydon Fuller 61caf6974a cli: parse json params 2016-06-03 15:41:14 -04:00
Chris Kleeschulte e87f628e7a Merge pull request #439 from braydonf/zmqcheck
bitcoind: added zmq precondition
2016-06-02 14:11:59 -04:00
Chris Kleeschulte a63fac32de Merge pull request #438 from braydonf/docs/clarify-summary
docs: clarify getAddressSummary results
2016-06-02 14:11:31 -04:00
Chris Kleeschulte 3a0ba64a43 Merge pull request #437 from braydonf/relative-datadir
bitcoind: relative spawn.datadir handling
2016-06-02 14:10:58 -04:00
Chris Kleeschulte 7efe271bdb Merge pull request #434 from braydonf/docs/memory
docs: bump recommended memory
2016-06-02 13:04:30 -04:00
Braydon Fuller cf16a23408 bitcoind: added zmq precondition
Adds a state check that transaction and block events are over the same host
and port. This is to make sure that block events can be subscribed to and
that the tip of the chain stays up to date for correct confirmation counts.
2016-06-01 19:50:45 -04:00
Braydon Fuller 32a6b25a91 docs: clarify getAddressSummary results 2016-06-01 13:11:51 -04:00
Braydon Fuller 4d780a9d2d bitcoind: separate function for relative datadir expanding 2016-06-01 11:41:41 -04:00
Braydon Fuller 814576953c bitcoind: relative spawn.datadir handling
Will expand the datadir into an absolute path based on the location
of the configuration file. This is to avoid unexpected behavior in regards
to the location of configuration files.
2016-06-01 11:33:06 -04:00
Braydon Fuller b7560933ba docs: bump recommended memory 2016-05-31 12:58:31 -04:00
Chris Kleeschulte c897f62d02 Update release.md 2016-05-27 14:21:33 -04:00
Chris Kleeschulte fd00be7e8c Bump package version to 3.0 2016-05-27 14:09:51 -04:00
Chris Kleeschulte 2b2b4d821c Merge pull request #422 from braydonf/bitcoind
bitcoind address index
2016-05-27 13:58:56 -04:00
Braydon Fuller 6df9387715 docs: update release process 2016-05-27 13:21:15 -04:00
Braydon Fuller 29b59c6f7d build: update bitcoind-rpc to version 0.6.0 2016-05-27 11:56:41 -04:00
Braydon Fuller 47e3cf7fc8 build: update download of bitcoind to tag v0.12-bitcore 2016-05-27 11:43:50 -04:00
Braydon Fuller aa7f0d7c60 test: remove duplicated test 2016-05-26 10:23:42 -04:00
Braydon Fuller e8a35e2bb5 bitcoind: bug with getting block hash from address
Fixes an issue where passing an address as the blockArg would get the blockhash
for the parsed integer of the address. `parseInt` would parse the address as
an integer and then get the block hash for 1. A regular expression now checks
that the string is numeric with only 0-9 and the length is less than 40, the
size of a ripemd160, and also less than the length of a sha256 hash.
2016-05-26 10:15:42 -04:00
Braydon Fuller c7ec2dcc89 test: bitcoind test for undefined scriptPubKey.addresses 2016-05-26 09:16:08 -04:00
Braydon Fuller f38fa1324f bitcoind: account for scriptPubKey.addresses not always being set 2016-05-25 17:14:22 -04:00
Braydon Fuller 88c15f6844 scaffold: remove no longer needed '-dev' version handling 2016-05-24 17:15:28 -04:00
Braydon Fuller 2dddf01bb0 test: coverage for spawnChildProcess 2016-05-24 17:11:02 -04:00
Braydon Fuller f76b206178 test: coverage for getBlockHeader 2016-05-24 16:39:22 -04:00
Braydon Fuller 1d9b89f187 test: coverage for getAddressHistory 2016-05-24 16:29:36 -04:00
Braydon Fuller 0cb795d980 test: add bitcoind test for early shutdown while connecting 2016-05-24 16:22:41 -04:00
Braydon Fuller 52cf300858 test: coverage for bitcoind getAddressSummary 2016-05-24 15:13:41 -04:00
Braydon Fuller 8d7d78a89e build: run coveralls for unit test coverage 2016-05-24 15:02:49 -04:00
Braydon Fuller 0c820c5987 test: unit tests for bitcoind address details 2016-05-24 14:30:54 -04:00
Braydon Fuller 86b1acd0be test: coverage for bitcoind getAddressUnspentOutputs 2016-05-24 14:06:03 -04:00
Braydon Fuller 4d1b853fd4 test: increase timeout for before all in node address regtest 2016-05-24 13:36:04 -04:00
Braydon Fuller 9c90f05c73 test: more coverage for bitcoind 2016-05-24 13:35:08 -04:00
Braydon Fuller 3fef6f5ffc test: increase test coverage of bitcoind service 2016-05-24 13:07:51 -04:00
Braydon Fuller 35a1b6dd04 test: more coverage for bitcoind service
tests for catching errors in #_initChain
2016-05-24 10:21:28 -04:00
Braydon Fuller 0a95765e51 bitcoind: fix indentation 2016-05-23 16:48:17 -04:00
Braydon Fuller ea3c813d51 test: check that caching is working 2016-05-23 16:47:57 -04:00
Braydon Fuller 2a53aad34a test: add test for respawn bitcoind 2016-05-23 16:21:03 -04:00
Braydon Fuller f1a9f6d066 test: stub logging in bitcoind tests 2016-05-23 15:58:54 -04:00
Braydon Fuller 85a302ee9d test: unit tests for zmq socket setup 2016-05-23 15:57:38 -04:00
Braydon Fuller a4888e5354 test: increase test coverage for lib/node.js 2016-05-23 15:45:13 -04:00
Braydon Fuller 202971ec0c test: increase test coverage for bitcoind
adds tests for subscribing with addresses
2016-05-23 15:44:33 -04:00
Braydon Fuller 584dd2cb98 test: add unit test for node getNetworkName 2016-05-23 13:46:03 -04:00
Braydon Fuller bce64d86e3 scaffold: upgrade message formatting 2016-05-23 13:06:32 -04:00
Braydon Fuller 4001a41d2d docs: add additional node about reindexing 2016-05-23 12:53:17 -04:00
Braydon Fuller cd9bbc8661 scaffold: expanded v2 config checks 2016-05-23 11:30:57 -04:00
Braydon Fuller 73197fdc75 build: update url to download bitcoin-0.12-bitcore-rc3 2016-05-18 20:04:23 -04:00
Braydon Fuller 83880910dc docs: add documentation for addresstxid event 2016-05-18 11:22:36 -04:00
Braydon Fuller 28ff52ece6 tests: add tests for addresstxid event 2016-05-18 11:20:23 -04:00
Braydon Fuller 1800294dfe bitcoind: change dataformat of addresstxid event
Adds the address to the message to quickly determine the address
associated with the event.
2016-05-18 10:20:18 -04:00
Braydon Fuller 6fbadb6c42 test: stub logging in unit tests 2016-05-18 00:33:57 -04:00
Braydon Fuller 522c822304 test: use callback instead of ready event 2016-05-18 00:24:08 -04:00
Braydon Fuller bf080422ed web: get remoteAddress for socket with cloudflare header 2016-05-18 00:17:45 -04:00
Braydon Fuller 57cb146ce0 build: fix jshint unused variable warnings 2016-05-17 23:03:04 -04:00
Braydon Fuller 4df9b5f6cf bitcoind: add addresstxid event 2016-05-17 22:46:38 -04:00
Braydon Fuller a48bcaf900 web: added logging for web socket events 2016-05-17 18:16:56 -04:00
Braydon Fuller fa6474e85f bitcoind: handle block height number as string 2016-05-16 18:01:12 -04:00
Braydon Fuller 484b707589 bitcoind: update jsdocs for getDetailedTransaction 2016-05-16 17:39:54 -04:00
Braydon Fuller 8f11a33834 test: add getBlockOverview unit tests and refactor 2016-05-16 17:34:40 -04:00
Braydon Fuller 64ed440729 docs: update docs to reflect api changes 2016-05-16 17:07:26 -04:00
Braydon Fuller 2cc06cc34b build: update bitcoind release to include mempool spentindex 2016-05-16 15:39:29 -04:00
Braydon Fuller cd4432652d main: remove transaction with populate methods
The methods populateInputs and populateSpentInfo are nolonger necessary or used
now that there is is getDetailedTransaction.
2016-05-13 18:52:12 -04:00
Braydon Fuller 8bddf4f0d6 bitcoind: add getDetailedTransaction method
Adds a new method getDetailedTransaction with a standard JavaScript object
with block information, address, amounts and fees. And removes the
getTransactionWithBlockInfo method since this new method is equivalent,
and will serialize over an API correctly.

Also includes a new method getBlockOverview to get the txids for a block,
that can be combined with getDetailedTransaction for viewing block
transactions with additional information.
2016-05-13 16:50:49 -04:00
Braydon Fuller 950a9d521c docs: make note about sorting of blockhashes 2016-05-11 13:33:21 -04:00
Braydon Fuller 17e7a7eedb build: bitcoind-rpc with explicit work limit exceeded handling
fixes an issues were 500 error codes are used for both block not found
as well as work limit exceeded errors
2016-05-11 13:13:33 -04:00
Braydon Fuller 98bfd358d3 build: update bitcoind-rpc with work limit exceeded handling 2016-05-11 12:51:24 -04:00
Braydon Fuller ae91ff2420 bitcoind: update jsdocs for getBlockHeader 2016-05-11 12:15:33 -04:00
Braydon Fuller b597a05cb4 bitcoind: camelCase result from getBlockHeader
for consistency with other methods
2016-05-11 11:39:14 -04:00
Braydon Fuller d399e9acea bitcoind: camelCase getInfo results
for consistency with other bitcoind api responses
2016-05-11 11:16:04 -04:00
Braydon Fuller 4757edc570 test: add missing property checks 2016-05-10 19:40:29 -04:00
Braydon Fuller cceb4186d4 test: bump timeout in bitcoind after/before 2016-05-10 19:35:37 -04:00
Braydon Fuller 791047c10d bitcoind: bump max tx history default to 50 2016-05-10 19:30:36 -04:00
Braydon Fuller 8b0d16d5a3 test: check callcount for retry in bitcoind spawn child method 2016-05-10 18:54:02 -04:00
Braydon Fuller f6bbe54293 test: bitcoind chainwork test
modified comparison to show how the values differ
2016-05-10 18:33:37 -04:00
Braydon Fuller 26c87ea32a test: check height from tip event in cluster regtest 2016-05-10 18:19:58 -04:00
Braydon Fuller 85a0c16eef test: fixes for bitcoind regtest 2016-05-10 18:17:53 -04:00
Braydon Fuller 75c43559d4 bitcoind: paginate txids in address summary
so that one request doesn't yield a 80MB response
2016-05-10 13:48:56 -04:00
Braydon Fuller 0387c1a6e4 test: increase timeout for cluster test and decrease keypool
resolves issues when the keypool takes time to fill
2016-05-10 11:47:31 -04:00
Braydon Fuller e24a9c96ae build: update bitcoind links to bitpay/bitcoin bitcore-rc1 release 2016-05-09 16:46:45 -04:00
Braydon Fuller d9d50c1f0c docs: update prereqs in readme 2016-05-09 16:46:40 -04:00
Braydon Fuller 27112fc1d7 docs: make note about libzmq-dev 2016-05-09 16:37:28 -04:00
Braydon Fuller 36f337afb3 web: update jsdoc with enableSocketRPC option 2016-05-09 14:30:07 -04:00
Braydon Fuller 7be7a7dce5 scaffold: update error message to be more accurate 2016-05-09 14:28:17 -04:00
Braydon Fuller abfb07f5f8 build: update bitcoind-rpc commit 2016-05-09 14:27:13 -04:00
Braydon Fuller c9154d4e0e docs: bump disk prereq to 200GB 2016-05-09 14:23:53 -04:00
Braydon Fuller b0290899ce bitcoind: handle empty input from pid file 2016-04-28 16:19:33 -04:00
Braydon Fuller 2e912af9b4 bitcoind: subscribe to zmq event closer to 100% sync
Instead of subscribing at >= 0.995 subscribe at >= 0.9999 progress
2016-04-28 13:13:51 -04:00
Braydon Fuller c22f6505eb bitcoind: reduce duplicate tx messages
remember a larger number of tx zmq messages to not emit a transaction twice
once from the block and another from the mempool
2016-04-28 12:10:05 -04:00
Braydon Fuller d969ad7fb6 build: include bitcoind in package.json bin 2016-04-27 14:38:58 -04:00
Braydon Fuller 24d1bc82e9 logger: added option to disable formatting
- systemd journalctl includes timestamps in log messages already
- updated logger to use console.error, console.warn, console.info, and etc.
2016-04-27 13:46:50 -04:00
Braydon Fuller ea792b692f scaffold: remove outdated logging of db service sync status 2016-04-27 12:00:47 -04:00
Braydon Fuller 271dcd8902 build: add jshint to scripts and ci build 2016-04-27 11:36:46 -04:00
Braydon Fuller 92bae5f09a general: code cleanup, refactoring and formatting 2016-04-27 11:36:26 -04:00
Braydon Fuller c1e9d5a3d9 test: added tests for stopSpawnedBitcoin 2016-04-26 17:29:40 -04:00
Braydon Fuller d28f8567f1 bitcoind: handle unexpected process exits 2016-04-26 14:32:51 -04:00
Braydon Fuller d958e83f1d build: add support for nodejs 0.10
For Ubuntu 14.04 Node.js compatibility: http://packages.ubuntu.com/trusty/nodejs
2016-04-25 17:23:48 -04:00
Braydon Fuller 9e0e9a2c89 build: include environment variables for downloading bitcoin
for parent modules to specify npm rebuild and install behavior
2016-04-25 16:35:18 -04:00
Braydon Fuller 76eeba5999 build: verify bitcoin download 2016-04-25 16:08:19 -04:00
Braydon Fuller 944c44ed74 bitcoind: return selected set of info for getinfo 2016-04-25 11:16:06 -04:00
Braydon Fuller 3f34fb6ea0 bitcoind: always log errors emitted instead of being uncaught exceptions 2016-04-25 11:02:37 -04:00
Braydon Fuller a61f43a584 build: upgrade socket.io
nodejs binary addons have been removed as optional dependencies from ws, however
they will still be used if available:
49b11093e9
2016-04-23 17:09:46 -04:00
Braydon Fuller c63e98f061 bitcoind: limit tx history range 2016-04-22 16:51:56 -04:00
Braydon Fuller c6e543c2a1 bitcoind: fix noTxList caching issue 2016-04-22 16:13:57 -04:00
Braydon Fuller 5e6600162a test: add unit test for getaddressunspentoutputs with mempool 2016-04-22 12:48:16 -04:00
Braydon Fuller 7f17dd4a4c bitcoind: fixed issue with cache mempool updates 2016-04-22 12:09:57 -04:00
Braydon Fuller 0272b17f0e test: fix regtest amount check 2016-04-21 17:34:25 -04:00
Braydon Fuller b901e10c9d bitcoind: update unspentoutputs with mempool 2016-04-21 17:13:24 -04:00
Braydon Fuller d1cf9deef0 bitcoind: parse ints for pagination 2016-04-20 15:35:43 -04:00
Braydon Fuller 587602d080 bitcoind: stop failsafe timeout 2016-04-20 13:03:18 -04:00
Braydon Fuller 2015514e78 test: increase timeout for check synced test 2016-04-20 12:05:15 -04:00
Braydon Fuller 3e2492e6d4 scaffold: detect incompatible config 2016-04-20 11:55:45 -04:00
Braydon Fuller 019bc2a58c bitcoind: load network bitcoin.conf and set defaults 2016-04-20 11:41:02 -04:00
Braydon Fuller c3dab07b30 bitcoind: fix clearInterval issue with Node.js 0.12 2016-04-19 20:27:52 -04:00
Braydon Fuller 40e7b24ea9 test: fix unstubbed uncaughException 2016-04-19 20:10:32 -04:00
Braydon Fuller 7d878adcf0 bitcoind: immediately subscribe with connect option 2016-04-19 17:23:27 -04:00
Braydon Fuller 458fe2f2b6 bitcoind: emit block events while polling before subscribing to zmq events 2016-04-19 16:53:00 -04:00
Braydon Fuller b092adcc21 bitcoind: subscribe to zmq events without interval if already synced 2016-04-19 16:40:54 -04:00
Braydon Fuller 2b38f08175 bitcoind: subscribe to zmq events once synced
prevents flooding tx and and block events that can cause issues
2016-04-19 16:00:17 -04:00
Braydon Fuller 7dabd8c4ab docs: correct development environment docs 2016-04-19 14:00:22 -04:00
Braydon Fuller 2975f27a8d bitcoind: add uacomment option to default config 2016-04-18 18:34:54 -04:00
Braydon Fuller 033a62387f docs: include upgrade notes for bitcore 3 -> 4 2016-04-18 17:22:49 -04:00
Braydon Fuller feb8038da6 bitcoind: fix check reindex interval 2016-04-18 16:05:09 -04:00
Braydon Fuller bf67b932de bitcoind: fix check reindex method not found 2016-04-18 15:52:35 -04:00
Braydon Fuller 3fed348cf7 docs: update development guide 2016-04-18 15:40:59 -04:00
Braydon Fuller c8ba4eaa8f test: remove outdated regtest 2016-04-18 13:45:06 -04:00
Braydon Fuller afda35962b test: mempool helper method unit tests 2016-04-18 13:37:37 -04:00
Braydon Fuller 7c37eba91e test: unit tests for connect and spawn processes 2016-04-18 13:24:47 -04:00
Braydon Fuller e09cc3d1fc test: bitcoind start unit tests 2016-04-18 11:11:17 -04:00
Braydon Fuller 317fdbbdd8 test: bitcoind _getAddressStrings unit tests 2016-04-18 10:54:50 -04:00
Braydon Fuller 8fd405eedf test: getBlockHeader unit tests 2016-04-18 10:48:32 -04:00
Braydon Fuller a4f5a6fa82 test: getblock unit tests 2016-04-18 10:37:33 -04:00
Braydon Fuller 552abf77cf docs: symlink docs/index.md -> README.md 2016-04-15 17:00:40 -04:00
Braydon Fuller dab49aef39 docs: various updates
- remove build and update bitcoind
- remove outdated error documentation
- update bus docs
2016-04-15 16:55:59 -04:00
Braydon Fuller bb726bac8b test: bitcoind getaddressbalance unit test 2016-04-15 16:35:31 -04:00
Braydon Fuller 24ca5ce053 web: option to enable/disable socket rpc handling 2016-04-15 11:20:38 -04:00
Braydon Fuller 69ff5423c2 bitcoind: rename exported events to rawtransaction and hashblock 2016-04-15 11:20:38 -04:00
Braydon Fuller 890b38744d test: update unit tests, refactoring and cleanup 2016-04-15 11:20:33 -04:00
Braydon Fuller 848dc29777 docs: update get balance method 2016-04-13 09:17:28 -04:00
Braydon Fuller c2eda9b3c2 bitcoin: address history by height range 2016-04-12 20:51:58 -04:00
Braydon Fuller 1d358a6994 test: update pagination test 2016-04-12 17:22:37 -04:00
Braydon Fuller 042576474f build: bump bitcoin build tag to v0.12.0-bitcore-beta2 2016-04-12 16:46:14 -04:00
Braydon Fuller c36b0777d4 bitcoind: add checkstate for spentindex 2016-04-12 15:36:59 -04:00
Braydon Fuller cdfe572344 bitcoind: include height in spentinfo 2016-04-12 15:30:17 -04:00
Braydon Fuller 37f31fdb19 bitcoind: added getspentinfo method 2016-04-12 15:07:16 -04:00
Braydon Fuller b757bd3148 docs: update docs for bitcoind with address indexes 2016-04-11 15:45:28 -04:00
Braydon Fuller 52f05f3027 bitcoind: emit block events 2016-04-11 10:47:52 -04:00
Braydon Fuller d0937fea55 bitcoind: try to get transaction from all bitcoind nodes 2016-04-11 10:47:44 -04:00
Braydon Fuller 019626ba15 bitcoind: prevent rapid tip updates for all networks 2016-04-11 10:47:37 -04:00
Braydon Fuller 5bea36edc6 bitcoind: try querying all bitcoind nodes 2016-04-11 10:47:30 -04:00
Braydon Fuller d7f49cc192 test: add regtest for multiple bitcoind connections 2016-04-08 22:17:45 -04:00
Braydon Fuller dbcb70f839 transaction: update getTransaction arguments 2016-04-08 16:16:41 -04:00
Braydon Fuller 8102761b55 bitcoind: normalize address arguments 2016-04-08 16:04:27 -04:00
Braydon Fuller f3f2f59615 node: add getNetworkName method 2016-04-08 16:00:33 -04:00
Braydon Fuller 90e354093c bitcoind: increase reindex interval to 10s 2016-04-08 15:54:04 -04:00
Braydon Fuller 3713c6ac1e bitcoind: sendTransaction second arg as object 2016-04-08 15:52:57 -04:00
Braydon Fuller b4b560aa45 bitcoind: get blocks and transactions as buffers 2016-04-08 15:49:18 -04:00
Braydon Fuller d11d0300de bitcoind: spawn in default configs 2016-04-08 15:29:57 -04:00
Braydon Fuller 1013ad3c56 build: upgrade chai and mocha 2016-04-08 14:58:45 -04:00
Braydon Fuller fd427fa128 test: increase timeout and remove new lines 2016-04-08 14:49:58 -04:00
Braydon Fuller 9bf6941fdf test: update node regtest 2016-04-08 14:44:24 -04:00
Braydon Fuller 4662ca0850 bitcoind: update jsdocs and cleanup 2016-04-08 12:00:10 -04:00
Braydon Fuller 88872734de bitcoind: add missing api methods to export 2016-04-08 12:00:10 -04:00
Braydon Fuller 82232c0f55 bitcoind: wrap rpc errors as instances of error 2016-04-08 12:00:10 -04:00
Braydon Fuller 3ead5928a7 test: update titles and docs for regtests 2016-04-08 12:00:10 -04:00
Braydon Fuller 88a82719ca test: moved integration -> regtest 2016-04-08 12:00:10 -04:00
Braydon Fuller 962e7b87f8 test: update regtest 2016-04-08 12:00:10 -04:00
Braydon Fuller 67b8ec2152 build: update travis with zmq 2016-04-08 12:00:10 -04:00
Braydon Fuller 7c344b5f24 build: updates for npm install 2016-04-08 12:00:10 -04:00
Braydon Fuller 1fb552a972 build: download bitcoin binary distribution 2016-04-08 12:00:10 -04:00
Braydon Fuller c4649c9b13 test: mark last zmq block before rpc calls 2016-04-08 12:00:10 -04:00
Braydon Fuller 31da32ecfd test: updated p2p integration test 2016-04-08 12:00:09 -04:00
Braydon Fuller c116353b8d build: start of install script 2016-04-08 12:00:09 -04:00
Braydon Fuller 7c6e5cf7b1 bitcoind: only cache transaction with height if confirmations >= 6 2016-04-08 11:59:26 -04:00
Braydon Fuller 18310268a5 node: log intro with config path 2016-04-08 11:59:26 -04:00
Braydon Fuller 0f24dd5f49 config: update configuration options for exec path
- config options for bitcoind to specify exec path of bitcoind
- config options to connect to multiple bitcoind processes
- systemd and upstart preferred methods to daemonize
2016-04-08 11:59:26 -04:00
Braydon Fuller 5932b34a1f bitcoind: set height when starting 2016-04-08 11:59:26 -04:00
Braydon Fuller 9409374fbe bitcoind: fix multiple addresses for address history 2016-04-08 11:59:26 -04:00
Braydon Fuller b473b65207 bitcoind: fix tx event to include tx buffer 2016-04-08 11:59:26 -04:00
Braydon Fuller 60333bcb0e bitcoind: add mempool to address txid results 2016-04-08 11:59:26 -04:00
Braydon Fuller ab70aa666e bitcoind: add address utxos 2016-04-08 11:59:26 -04:00
Braydon Fuller 7d7dfe329d bitcoind: variable name fixes 2016-04-08 11:59:26 -04:00
Braydon Fuller af573b765b bitcoind: fix txid pagination 2016-04-08 11:59:26 -04:00
Braydon Fuller b69d848352 bitcoind: add lru caching for results 2016-04-08 11:59:26 -04:00
Braydon Fuller 7e70bbfa7d bitcoind: bitcoind service using rpc and zmq with address index 2016-04-08 11:59:26 -04:00
Chris Kleeschulte 07c317df80 Bump development version to v2.1.1-dev 2016-04-08 11:31:46 -04:00
Chris Kleeschulte 6147be5c49 Bump package version to v2.1.1 2016-04-08 10:33:57 -04:00
Braydon Fuller 3863f5ce9b Merge pull request #425 from jpochyla/fix-tx-pagination
Fix pagination of transaction history
2016-04-07 10:10:45 -04:00
Jan Pochyla b55ecf3044 clamp tx pagination to 0 2016-04-01 18:11:49 +02:00
Matias Alejo Garcia 766d87192a Merge pull request #416 from braydonf/bug/txblock
bindings: fixes confirmation issue with orphaned block transactions
2016-03-08 08:24:59 -03:00
Braydon Fuller 8c10221480 bindings: fixes confirmation issue with orphaned block transactions 2016-03-04 13:52:09 -05:00
Braydon Fuller 462e4e3cdd Merge pull request #407 from bitpay/opt/multiple-address-history
Opt/multiple address history
2016-02-11 12:15:15 -05:00
Braydon Fuller c988fdc64d Merge pull request #408 from kleetus/feature/cross_compiling_support
Enable Cross-Compiling support
2016-02-11 11:12:18 -05:00
Matias Alejo Garcia e36cdb717a rm empty keys 2016-02-11 10:42:30 -05:00
Chris Kleeschulte 610b9ea269 Added a doc fragment in build.md about cross compilation and clarified
the error message.
2016-02-11 10:25:22 -05:00
Chris Kleeschulte afce33e5ff Fixed test to refer to variables.sh for the architecture. 2016-02-11 10:25:22 -05:00
Chris Kleeschulte 4894f1abec Enable Cross-Compiling support
1. To use this feature, set CC and CXX env variables to the appropriate cross compiler
2. Example, for cross compiling to ARM, use: CC=arm-linux-gnueabihf-gcc-4.9 CXX=arm-linux-gnueabihf-g++-4.9 npm install
3. You can still compile without setting CC and CXX, you can still just run npm install
2016-02-11 10:25:22 -05:00
Matias Alejo Garcia 4d03aaa73f use mocknode 2016-02-10 16:18:27 -05:00
Matias Alejo Garcia 6e600b5def refactor test 2016-02-10 16:08:27 -05:00
Matias Alejo Garcia 1a68ca4fae add tests to _updateAddressIndex 2016-02-10 15:38:02 -05:00
Matias Alejo Garcia 02f2234004 rm extra commas 2016-02-10 15:05:05 -05:00
Matias Alejo Garcia 3bb3d82aac add counter for address mempool index 2016-02-10 15:03:34 -05:00
Matias Alejo Garcia e7895b4b34 use key as binary 2016-02-09 15:30:40 -05:00
Matias Alejo Garcia d0c2fa61d8 fix tests 2016-02-09 10:57:40 -05:00
Matias Alejo Garcia 9f87156adc fix format 2016-02-09 10:26:09 -05:00
Chris Kleeschulte 53735025dd Bump development version to v2.1.0-dev 2016-02-08 15:37:30 -05:00
Matias Alejo Garcia dae5c9d3d5 fix regtests 2016-02-08 15:07:32 -05:00
Chris Kleeschulte 6dfb354230 Bump package version to v2.1.0 2016-02-08 14:37:29 -05:00
Matias Alejo Garcia c65c2bad20 add mempoolADdressIndex test 2016-02-08 13:40:27 -05:00
Matias Alejo Garcia c1d3f351f2 add address index to mempool + noBalance options 2016-02-08 13:21:53 -05:00
Matias Alejo Garcia e7e33313cf add noBalance options + mempoolAddressIndex 2016-02-08 12:50:32 -05:00
Gabe Gattis ae8221ffb3 Merge pull request #405 from braydonf/add-regtest
Switch to use enableRegtest from bitcore-lib
2016-02-08 12:21:33 -05:00
Chris Kleeschulte 4529c3013d Merge pull request #406 from braydonf/getinfo-network
Added network name to bitcoind.getInfo
2016-02-05 12:26:45 -05:00
Braydon Fuller e56fdf457f Added network name to bitcoind.getInfo 2016-02-05 10:33:11 -05:00
Braydon Fuller 0d6bc98333 Dependencies: Updated bitcore-lib to version ^0.13.13
This release includes new API for regtest with `enableRegtest()` and `disableRegtest()`
2016-02-05 10:14:09 -05:00
Braydon Fuller 17e8173d14 Dependencies: Temporarily switch to development version of bitcore-lib 2016-02-04 17:28:10 -05:00
Braydon Fuller 83eba52657 Tests: Fix tests to use enable/disableRegtest 2016-02-04 17:26:17 -05:00
Braydon Fuller 6e8f3ee917 Add regtest from bitcore-lib 2016-02-03 18:29:33 -05:00
Chris Kleeschulte 8afb2b8669 Merge pull request #404 from braydonf/lower-limits
Lower and include new concurrency limits
2016-02-03 13:17:48 -05:00
Braydon Fuller f473ddeddd Lower and include new concurrency limits 2016-02-03 12:28:32 -05:00
Chris Kleeschulte 6a899e4b9c Merge pull request #403 from braydonf/bug/limit-addresses
Address Service: Limit the number of simultaneous requests
2016-02-02 14:25:13 -05:00
Braydon Fuller 93e5dbfc34 Address Service: Limit the number of simultaneous requests 2016-02-02 13:27:45 -05:00
Chris Kleeschulte ca19994326 Bump development version to v2.0.1-dev 2016-02-01 15:49:20 -05:00
103 changed files with 9439 additions and 13055 deletions

1
.coveralls.yml Normal file
View File

@ -0,0 +1 @@
repo_token: DvrDb09a8vhPlVf6DT4cGBjcFOi6DfZN1

14
.gitignore vendored
View File

@ -1,12 +1,7 @@
node_modules/
node_modules/*
coverage/*
out/
out/*
build/
build/*
.lock-wscript
Makefile.gyp
*.swp
*.Makefile
*.target.gyp.mk
@ -19,15 +14,16 @@ Makefile.gyp
*.filters
*.user
*.project
test.js
**/*.dylib
**/*.so
**/*.old
**/*.files
**/*.config
**/*.creator
libbitcoind
libbitcoind*
libbitcoind.includes
*.log
.DS_Store
bin/bitcoin*
bin/SHA256SUMS.asc
regtest/data/node1/regtest
regtest/data/node2/regtest
regtest/data/node3/regtest

View File

@ -1,7 +1,7 @@
sudo: false
language: node_js
env:
- BITCORENODE_ENV=test BITCORENODE_ASSUME_YES=true CXX=g++-4.8 CC=gcc-4.8
- CXX=g++-4.8 CC=gcc-4.8
addons:
apt:
sources:
@ -9,14 +9,14 @@ addons:
packages:
- g++-4.8
- gcc-4.8
- libzmq3-dev
node_js:
- "v0.10.25"
- "v0.12.7"
- "v4"
script:
- _mocha -R spec integration/regtest.js
- _mocha -R spec integration/regtest-node.js
- _mocha -R spec integration/p2p.js
- _mocha -R spec --recursive
cache:
directories:
- cache
- npm run regtest
- npm run test
- npm run jshint
after_success:
- npm run coveralls

View File

@ -1 +0,0 @@
v0.11.2

View File

@ -1,7 +1,7 @@
Bitcore Node
============
A Bitcoin full node for building applications and services with Node.js. A node is extensible and can be configured to run additional services. At the minimum a node has native bindings to Bitcoin Core with the [Bitcoin Service](docs/services/bitcoind.md). Additional services can be enabled to make a node more useful such as exposing new APIs, adding new indexes for addresses with the [Address Service](docs/services/address.md), running a block explorer, wallet service, and other customizations.
A Bitcoin full node for building applications and services with Node.js. A node is extensible and can be configured to run additional services. At the minimum a node has an interface to [Bitcoin Core with additional indexing](https://github.com/bitpay/bitcoin/tree/0.12.1-bitcore) for more advanced address queries. Additional services can be enabled to make a node more useful such as exposing new APIs, running a block explorer and wallet service.
## Install
@ -10,14 +10,15 @@ npm install -g bitcore-node
bitcore-node start
```
Note: For your convenience, we distribute binaries for x86_64 Linux and x86_64 Mac OS X. Upon npm install, the binaries for your platform will be downloaded. For more detailed installation instructions, or if you want to compile the project yourself, then please see the [Build & Install](docs/build.md) documentation to build the project from source.
Note: For your convenience, we distribute bitcoind binaries for x86_64 Linux and x86_64 Mac OS X. Upon npm install, the binaries for your platform will be downloaded. For more detailed installation instructions, or if you want to compile the project yourself, then please see the Bitcore branch of [Bitcoin Core with additional indexing](https://github.com/bitpay/bitcoin/tree/0.12.1-bitcore).
## Prerequisites
- Node.js v0.12 or v4.2
- ~100GB of disk storage
- ~4GB of RAM
- Mac OS X >= 10.9, Ubuntu >= 12.04 (libc >= 2.15 and libstdc++ >= 6.0.16)
- GNU/Linux x86_32/x86_64, or OSX 64bit *(for bitcoind distributed binaries)*
- Node.js v0.10, v0.12 or v4
- ZeroMQ *(libzmq3-dev for Ubuntu/Debian or zeromq on OSX)*
- ~200GB of disk storage
- ~8GB of RAM
## Configuration
@ -32,33 +33,23 @@ bitcore-node install https://github.com/yourname/helloworld
This will create a directory with configuration files for your node and install the necessary dependencies. For more information about (and developing) services, please see the [Service Documentation](docs/services.md).
To start bitcore-node as a daemon:
```bash
bitcore-node start --daemon
```
## Add-on Services
There are several add-on services available to extend the functionality of Bitcore:
- [Insight API](https://github.com/bitpay/insight-api/tree/v0.3.0)
- [Insight UI](https://github.com/bitpay/insight/tree/v0.3.0)
- [Insight API](https://github.com/bitpay/insight-api)
- [Insight UI](https://github.com/bitpay/insight-ui)
- [Bitcore Wallet Service](https://github.com/bitpay/bitcore-wallet-service)
## Documentation
- [Upgrade Notes](docs/upgrade.md)
- [Services](docs/services.md)
- [Bitcoind](docs/services/bitcoind.md) - Native bindings to Bitcoin Core
- [Database](docs/services/db.md) - The foundation API methods for getting information about blocks and transactions.
- [Address](docs/services/address.md) - Adds additional API methods for querying and subscribing to events with bitcoin addresses.
- [Bitcoind](docs/services/bitcoind.md) - Interface to Bitcoin Core
- [Web](docs/services/web.md) - Creates an express application over which services can expose their web/API content
- [Build & Install](docs/build.md) - How to build and install from source
- [Testing & Development](docs/testing.md) - Developer guide for testing
- [Development Environment](docs/development.md) - Guide for setting up a development environment
- [Node](docs/node.md) - Details on the node constructor
- [Bus](docs/bus.md) - Overview of the event bus constructor
- [Errors](docs/errors.md) - Reference for error handling and types
- [Patch](docs/patch.md) - Information about the patch applied to Bitcoin Core
- [Release Process](docs/release.md) - Information about verifying a release and the release process.
## Contributing

View File

@ -1,75 +0,0 @@
'use strict';
var benchmark = require('benchmark');
var async = require('async');
var sinon = require('sinon');
var bitcore = require('bitcore-lib');
var Block = bitcore.Block;
var AddressService = require('../lib/services/address');
var maxTime = 20;
var blockData1 = require('./data/block-367238.json');
var blockData2 = require('./data/block-367239.json');
var blockData3 = require('./data/block-367240.json');
console.log('Address Service Block Handler');
console.log('-----------------------------');
async.series([
function(next) {
var c = 0;
var blocks = [
Block.fromBuffer(new Buffer(blockData1, 'hex')),
Block.fromBuffer(new Buffer(blockData2, 'hex')),
Block.fromBuffer(new Buffer(blockData3, 'hex'))
];
var blocksLength = 3;
var node = {
services: {
bitcoind : {
on: sinon.stub()
}
}
};
var addressService = new AddressService({node: node});
function blockHandler(deffered) {
if (c >= blocksLength) {
c = 0;
}
var block = blocks[c];
addressService.blockHandler(block, true, function(err, operations) {
if (err) {
throw err;
}
deffered.resolve();
});
c++;
}
var suite = new benchmark.Suite();
suite.add('blockHandler', blockHandler, {
defer: true,
maxTime: maxTime
});
suite
.on('cycle', function(event) {
console.log(String(event.target));
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').pluck('name'));
console.log('----------------------------------------------------------------------');
next();
})
.run();
}
], function(err) {
if (err) {
throw err;
}
console.log('Finished');
process.exit();
});

View File

@ -5,7 +5,7 @@ var bitcoin = require('bitcoin');
var async = require('async');
var maxTime = 20;
console.log('Bitcoin Service native interface vs. Bitcoin JSON RPC interface');
console.log('Zcash Service native interface vs. Zcash JSON RPC interface');
console.log('----------------------------------------------------------------------');
// To run the benchmarks a fully synced Bitcore Core directory is needed. The RPC comands
@ -28,7 +28,7 @@ var fixtureData = {
var bitcoind = require('../').services.Bitcoin({
node: {
datadir: process.env.HOME + '/.bitcoin',
datadir: process.env.HOME + '/.zcash',
network: {
name: 'testnet'
}
@ -43,12 +43,12 @@ bitcoind.start(function(err) {
if (err) {
throw err;
}
console.log('Bitcoin Core started');
console.log('Zcash started');
});
bitcoind.on('ready', function() {
console.log('Bitcoin Core ready');
console.log('Zcash ready');
var client = new bitcoin.Client({
host: 'localhost',

184
bin/build
View File

@ -1,184 +0,0 @@
#!/bin/bash
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.."
options=`cat ${root_dir}/bin/config_options.sh`
depends_dir=$($root_dir/bin/variables.sh depends_dir)
host=$(${root_dir}/bin/variables.sh host)
btc_dir="${root_dir}/libbitcoind"
patch_sha=$($root_dir/bin/variables.sh patch_sha)
config_lib_dir=$($root_dir/bin/variables.sh config_lib_dir)
export CPPFLAGS="-I${depends_dir}/${host}/include/boost -I${depends_dir}/${host}/include -L${depends_dir}/${host}/lib"
echo "Using BTC directory: ${btc_dir}"
cd "${root_dir}" || exit -1
build_dependencies () {
if [ -d "${btc_dir}" ]; then
pushd "${depends_dir}" || exit -1
echo "using host for dependencies: ${host}"
if [ "${test}" = true ]; then
make HOST=${host} NO_QT=1 NO_UPNP=1
else
make HOST=${host} NO_QT=1 NO_WALLET=1 NO_UPNP=1
fi
if test $? -eq 0; then
popd || exit -1
else
echo "Bitcoin's dependency building failed, please check the previous output for details."
exit -1
fi
fi
}
get_patch_file () {
if test -e "${root_dir/PATCH_VERSION}"; then
tag=`cat "${root_dir}/PATCH_VERSION" | xargs` || exit -1
else
echo "no tag file found, please create it in the root of the project as so: 'echo \"v0.10.2\" > PATCH_VERSION'"
exit 1
fi
}
compare_patch () {
cd "${btc_dir}" || exit -1
get_patch_file
echo "running the diff command from HEAD to ${tag}"
last_commit=$(git rev-parse HEAD)
diff=$(git show ${last_commit})
stripped_diff=$( echo -n "${diff}" | tail -n $( expr `echo -n "${diff}" | wc -l` - 5 ) )
matching_patch=`echo -n "${stripped_diff}" | diff -w "${root_dir}/etc/bitcoin.patch" -`
}
cache_files () {
cache_file="${root_dir}"/cache/cache.tar
pushd "${btc_dir}" || exit -1
find . -type f \( -name "*.h" -or -name "*.hpp" -or -name \
"*.ipp" -or -name "*.a" \) | tar -cf "${cache_file}" -T -
if test $? -ne 0; then
echo "We were trying to copy over your cached artifacts, but there was an issue."
exit -1
fi
tar xf "${cache_file}" -C "${root_dir}"/cache
if test $? -ne 0; then
echo "We were trying to untar your cache, but there was an issue."
exit -1
fi
rm -fr "${cache_file}" >/dev/null 2>&1
popd || exit -1
}
debug=
if [ "${BITCORENODE_ENV}" == "debug" ]; then
options=`cat ${root_dir}/bin/config_options_debug.sh` || exit -1
fi
test=false
if [ "${BITCORENODE_ENV}" == "test" ]; then
test=true
options=`cat ${root_dir}/bin/config_options_test.sh` || exit -1
fi
if hash shasum 2>/dev/null; then
shasum_cmd="shasum -a 256"
else
shasum_cmd="sha256sum"
fi
patch_file_sha=$(${shasum_cmd} "${root_dir}/etc/bitcoin.patch" | awk '{print $1}')
last_patch_file_sha=
if [ -e "${patch_sha}" ]; then
echo "Patch file sha exists, let's see if the patch has changed since last build..."
last_patch_file_sha=$(cat "${patch_sha}")
fi
shared_file_built=false
if [ "${last_patch_file_sha}" == "${patch_file_sha}" ]; then
echo "Patch file contents matches the sha from the patch file itself, so no reason to rebuild the bindings unless there are no prebuilt bindings."
shared_file_built=true
fi
if [ "${shared_file_built}" = false ]; then
echo "Looks like the patch to bitcoin changed since last build -or- this is the first build, so rebuilding libbitcoind itself..."
mac_response=$($root_dir/bin/variables.sh mac_dependencies)
if [ "${mac_response}" != "" ]; then
echo "${mac_response}"
exit -1
fi
only_make=false
if [ -d "${btc_dir}" ]; then
echo "running compare patch..."
compare_patch
repatch=false
if [[ "${matching_patch}" =~ [^\s\\] ]]; then
echo "Warning! libbitcoind is not patched with:\
${root_dir}/etc/bitcoin.patch."
echo -n "Would you like to remove the current patch, checkout the tag: ${tag} and \
apply the current patch from "${root_dir}"/etc/bitcoin.patch? (y/N): "
if [ "${BITCORENODE_ASSUME_YES}" = true ]; then
input=y
echo ""
else
read input
fi
if [[ "${input}" =~ ^y|^Y ]]; then
repatch=true
echo "Removing directory: \"${btc_dir}\" and starting over!"
rm -fr "${btc_dir}" >/dev/null 2>&1
fi
fi
if [ "${repatch}" = false ]; then
echo "Running make inside libbitcoind (assuming you've previously patched and configured libbitcoind)..."
cd "${btc_dir}" || exit -1
only_make=true
fi
fi
if [ "${only_make}" = false ]; then
echo "Cloning, patching, and building libbitcoind..."
get_patch_file
echo "attempting to checkout tag: ${tag} of bitcoin from github..."
cd "${root_dir}" || exit -1
#versions of git prior to 2.x will not clone correctly with --branch
git clone --depth 1 https://github.com/bitcoin/bitcoin.git libbitcoind
cd "${btc_dir}" || exit -1
git fetch --tags
git checkout "${tag}"
echo '../patch-bitcoin.sh' "${btc_dir}"
../bin/patch-bitcoin "${btc_dir}"
if ! test -d .git; then
echo 'Please point this script to an upstream bitcoin git repo.'
exit -1
fi
fi
build_dependencies
echo './autogen.sh'
./autogen.sh || exit -1
full_options="${options} ${config_lib_dir}"
echo "running the configure script with the following options:\n :::[\"${full_options}\"]:::"
${full_options}
echo 'make V=1'
make V=1 || exit -1
echo "Creating the sha marker for the patching in libbitcoind..."
echo "Writing patch sha file to: \"${patch_sha}\""
echo -n `${shasum_cmd} "${root_dir}"/etc/bitcoin.patch | awk '{print $1}'` > "${patch_sha}"
cache_files
echo 'Build finished successfully.'
else
echo 'Using existing static library.'
fi
# Building the Bindings
set -e
cd "${root_dir}"
debug=--debug=false
if test x"$1" = x'debug'; then
debug=--debug
fi
node-gyp ${debug} rebuild

View File

@ -1,9 +0,0 @@
#!/bin/bash
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.."
cd "${root_dir}"
pushd "${root_dir}"/libbitcoind
make clean
popd
node-gyp clean
rm -fr cache/*

View File

@ -1,2 +0,0 @@
./configure --enable-tests=no --enable-daemonlib --with-gui=no --without-qt --without-miniupnpc --without-bdb --disable-wallet --without-utils

View File

@ -1,2 +0,0 @@
./configure --enable-debug --enable-tests=no --enable-daemonlib --with-gui=no --without-qt --without-miniupnpc --without-bdb --disable-wallet --without-utils

View File

@ -1,2 +0,0 @@
./configure --enable-debug --enable-tests=no --enable-daemonlib --with-gui=no --without-qt --without-miniupnpc

View File

@ -1,17 +0,0 @@
'use strict';
function getTarballName() {
var packageRoot = __dirname + '/..';
var version = require(packageRoot + '/package.json').version;
var platform = process.platform;
var arch = process.arch;
var abi = process.versions.modules;
var tarballName = 'libbitcoind-' + version + '-node' + abi + '-' + platform + '-' + arch + '.tgz';
return tarballName;
}
if (require.main === module) {
process.stdout.write(getTarballName());
}
module.exports = getTarballName;

View File

@ -1,36 +0,0 @@
#!/bin/bash
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.."
cd "${root_dir}"
tarball_name=`node bin/get-tarball-name.js`
bucket_name="bitcore-node"
binary_url="https://${bucket_name}.s3.amazonaws.com/${tarball_name}"
echo "Downloading binary: ${binary_url}"
is_curl=true
if hash curl 2>/dev/null; then
curl --fail -I $binary_url >/dev/null 2>&1
else
is_curl=false
wget --server-response --spider $binary_url >/dev/null 2>&1
fi
if test $? -eq 0; then
if [ "${is_curl}" = true ]; then
curl $binary_url > $tarball_name
else
wget $binary_url
fi
if test -e "${tarball_name}"; then
echo "Unpacking binary distribution"
tar -xvzf $tarball_name
if test $? -eq 0; then
exit 0
fi
fi
fi
echo "Prebuild binary could not be downloaded, building from source..."
./bin/build

View File

@ -1,58 +0,0 @@
'use strict';
var exec = require('child_process').exec;
var bindings = require('bindings');
var index = require('../');
var log = index.log;
var packageRoot = bindings.getRoot(bindings.getFileName());
var binaryPath = bindings({
path: true,
bindings: 'bitcoind.node'
});
var relativeBinaryPath = binaryPath.replace(packageRoot + '/', '');
var tarballName = require('./get-tarball-name')();
log.info('Signing binding binary: "' + binaryPath + '"');
var signCommand = 'gpg --yes --out ' + binaryPath + '.sig --detach-sig ' + binaryPath;
var signchild = exec(signCommand, function(error, stdout, stderr) {
if (error) {
throw error;
}
if (stdout) {
log.info('GPG:', stdout);
}
if (stderr) {
log.error(stderr);
}
log.info('Packaging tarball: "' + tarballName + '"');
// Create a tarball of both the binding and the signature
var tarCommand = 'tar -C ' +
packageRoot + ' -cvzf ' +
tarballName + ' ' +
relativeBinaryPath + ' ' +
relativeBinaryPath + '.sig';
var tarchild = exec(tarCommand, function (error, stdout, stderr) {
if (error) {
throw error;
}
if (stdout) {
log.info('Tar:', stdout);
}
if (stderr) {
log.error(stderr);
}
});
});

View File

@ -1,36 +0,0 @@
#!/bin/bash
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.."
#root_dir="$(readlink -f "$(dirname "$0")")/.."
cd "$root_dir"
dir=$(test -n "$1" && echo "$1" || echo "${HOME}/bitcoin")
patch_file="$(pwd)/etc/bitcoin.patch"
cd "$dir" || exit 1
if ! test -d .git; then
echo 'Please point this script to an upstream bitcoin git repo.'
exit 1
fi
if test $? -ne 0; then
echo 'Unable to checkout necessary commit.'
echo 'Please pull the latest HEAD from the upstream bitcoin repo.'
exit 1
fi
git checkout -b "libbitcoind-$(date '+%Y.%m.%d')" || exit 1
patch -p1 < "$patch_file" || exit 1
git add --all || exit 1
[ -n "$( git config user.name )" ] \
|| git config user.name 'Bitcore Build'
[ -n "$( git config user.email )" ] \
|| git config user.email "$( id -n -u )@$( hostname -f )"
git commit -a -m 'allow compiling of libbitcoind.so.' || exit 1
echo 'Patch completed successfully.'
exit 0

View File

@ -1,61 +0,0 @@
#!/usr/bin/env node
'use strict';
var index = require('..');
var log = index.log;
process.title = 'libbitcoind';
/**
* daemon
*/
var daemon = require('../').services.Bitcoin({
node: {
datadir: process.env.BITCORENODE_DIR || process.env.HOME + '/.bitcoin',
network: {
name: process.env.BITCORENODE_NETWORK || 'livenet'
}
}
});
daemon.start(function() {
log.info('ready');
});
daemon.on('error', function(err) {
log.info('error="%s"', err.message);
});
daemon.on('open', function(status) {
log.info('status="%s"', status);
});
function exitHandler(options, err) {
log.info('Stopping daemon');
if (err) {
log.error('uncaught exception:', err);
if(err.stack) {
console.log(err.stack);
}
process.exit(-1);
}
if (options.sigint) {
daemon.stop(function(err) {
if(err) {
log.error('Failed to stop services: ' + err);
return process.exit(1);
}
log.info('Halted');
process.exit(0);
});
}
}
//catches uncaught exceptions
process.on('uncaughtException', exitHandler.bind(null, {exit:true}));
//catches ctrl+c event
process.on('SIGINT', exitHandler.bind(null, {sigint:true}));

View File

@ -1,8 +0,0 @@
#!/usr/bin/env node
'use strict';
var start = require('../lib/scaffold/start');
var defaultConfig = require('../lib/scaffold/default-config');
start(defaultConfig());

View File

@ -1,52 +0,0 @@
'use strict';
var fs = require('fs');
var AWS = require('aws-sdk');
var bindings = require('bindings');
var index = require('../');
var log = index.log;
var config = require(process.env.HOME + '/.bitcore-node-upload.json');
AWS.config.region = config.region;
AWS.config.update({
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey
});
var packageRoot = bindings.getRoot(bindings.getFileName());
var tarballName = require('./get-tarball-name')();
var bucketName = 'bitcore-node';
var url = 'https://' + bucketName + '.s3.amazonaws.com/' + tarballName;
var localPath = packageRoot + '/' + tarballName;
log.info('Uploading package: ' + localPath);
var fileStream = fs.createReadStream(localPath);
fileStream.on('error', function(err) {
if (err) {
throw err;
}
});
fileStream.on('open', function() {
var s3 = new AWS.S3();
var params = {
ACL: 'public-read',
Key: tarballName,
Body: fileStream,
Bucket: bucketName
};
s3.putObject(params, function(err, data) {
if (err) {
throw err;
} else {
log.info('Successfully uploaded to: ' + url);
}
});
});

View File

@ -1,153 +0,0 @@
#!/bin/bash
exec 2> /dev/null
root_dir="$(cd "$(dirname $0)" && pwd)/.."
if [ "${root_dir}" == "" ]; then
root_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/.."
fi
bitcoin_dir="${root_dir}"/libbitcoind
cache_dir="${root_dir}"/cache
host=
compute_host () {
platform=`uname -a | awk '{print tolower($1)}'`
arch=`uname -m`
if [ "${arch:0:3}" == "arm" ]; then
host="arm-linux-gnueabihf"
else
host="${arch}"-"${platform}"
fi
}
compute_host
mac_response=
check_mac_build_system () {
if [ "${platform}" == "darwin" ]; then
if [ ! -e "/usr/include/stdlib.h" ]; then
if hash xcode-select 2>/dev/null; then
mac_response="Please run 'xcode-select --install' from the command line because it seems that you've got Xcode, but not the Xcode command line tools that are required for compiling this project from source..."
else
mac_response="please use the App Store to install Xcode and Xcode command line tools. After Xcode is installed, please run: 'xcode-select --install' from the command line"
fi
fi
fi
}
depends_dir="${bitcoin_dir}"/depends
thread="${cache_dir}"/depends/"${host}"/lib/libboost_thread-mt.a
filesystem="${cache_dir}"/depends/"${host}"/lib/libboost_filesystem-mt.a
chrono="${cache_dir}"/depends/"${host}"/lib/libboost_chrono-mt.a
program_options="${cache_dir}"/depends/"${host}"/lib/libboost_program_options-mt.a
system="${cache_dir}"/depends/"${host}"/lib/libboost_system-mt.a
leveldb="${cache_dir}"/src/leveldb/libleveldb.a
memenv="${cache_dir}"/src/leveldb/libmemenv.a
libsecp256k1="${cache_dir}"/src/secp256k1/.libs/libsecp256k1.a
ssl="${cache_dir}"/depends/"${host}"/lib/libssl.a
crypto="${cache_dir}"/depends/"${host}"/lib/libcrypto.a
config_lib_dir=
if [ "${platform}" == "darwin" ]; then
config_lib_dir="--with-boost-libdir=${depends_dir}/${host}/lib"
else
config_lib_dir="--prefix=${depends_dir}/${host}"
fi
if test x"$1" = x'anl'; then
if [ "${platform}" != "darwin" ]; then
echo -n "-lanl"
fi
fi
if test x"$1" = x'cache_dir'; then
echo -n "${cache_dir}"
fi
if test x"$1" = x'btcdir'; then
echo -n "${bitcoin_dir}"
fi
if test -z "$1" -o x"$1" = x'thread'; then
echo -n "${thread}"
fi
if test -z "$1" -o x"$1" = x'filesystem'; then
echo -n "${filesystem}"
fi
if test -z "$1" -o x"$1" = x'program_options'; then
echo -n "${program_options}"
fi
if test -z "$1" -o x"$1" = x'system'; then
echo -n "${system}"
fi
if test -z "$1" -o x"$1" = x'ssl'; then
echo -n "${ssl}"
fi
if test -z "$1" -o x"$1" = x'crypto'; then
echo -n "${crypto}"
fi
if test -z "$1" -o x"$1" = x'chrono'; then
echo -n "${chrono}"
fi
if test -z "$1" -o x"$1" = x'depends_dir'; then
echo -n "${depends_dir}"
fi
if test -z "$1" -o x"$1" = x'leveldb'; then
echo -n "${leveldb}"
fi
if test -z "$1" -o x"$1" = x'memenv'; then
echo -n "${memenv}"
fi
if test -z "$1" -o x"$1" = x'libsecp256k1'; then
echo -n "${libsecp256k1}"
fi
if test -z "$1" -o x"$1" = x'host'; then
echo -n "${host}"
fi
if test -z "$1" -o x"$1" = x'bdb'; then
if [ "${BITCORENODE_ENV}" == "test" ]; then
echo -n "${cache_dir}"/depends/"${host}"/lib/libdb_cxx.a
fi
fi
if test -z "$1" -o x"$1" = x'patch_sha'; then
echo -n "${root_dir}"/cache/patch_sha.txt
fi
if test -z "$1" -o x"$1" = x'load_archive'; then
if [ "${os}" == "osx" ]; then
echo -n "-Wl,-all_load -Wl,--no-undefined"
else
echo -n "-Wl,--whole-archive ${filesystem} ${thread} "${cache_dir}"/src/.libs/libbitcoind.a -Wl,--no-whole-archive"
fi
fi
if test -z "$1" -o x"$1" = x'mac_dependencies'; then
check_mac_build_system
echo -n "${mac_response}"
fi
if test -z "$1" -o x"$1" = x'wallet_enabled'; then
if [ "${BITCORENODE_ENV}" == "test" ]; then
echo -n "-DENABLE_WALLET"
fi
fi
if test -z "$1" -o x"$1" = x'bitcoind'; then
echo -n "${cache_dir}"/src/.libs/libbitcoind.a
fi
if test -z "$1" -o x"$1" = x'config_lib_dir'; then
echo -n "${config_lib_dir}"
fi

View File

@ -1,59 +0,0 @@
{
"targets": [
{
"target_name": "libbitcoind",
"include_dirs" : [
"<!(node -e \"require('nan')\")",
"<!(./bin/variables.sh cache_dir)/src",
"<!(./bin/variables.sh cache_dir)/depends/<!(./bin/variables.sh host)/include",
"<!(./bin/variables.sh cache_dir)/src/leveldb/include"
],
"sources": [
"./src/libbitcoind.cc",
],
"conditions": [
[
"OS==\"mac\"",
{
"xcode_settings": {
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
"GCC_ENABLE_CPP_RTTI": "YES",
"MACOSX_DEPLOYMENT_TARGET": "10.9",
'OTHER_CFLAGS': [
"-fexceptions",
"-frtti",
"-fpermissive",
"<!(./bin/variables.sh wallet_enabled)",
]
}
}
]
],
"cflags_cc": [
"-fexceptions",
"-frtti",
"-fpermissive",
"<!(./bin/variables.sh wallet_enabled)",
],
"link_settings": {
"libraries": [
"<!(./bin/variables.sh bitcoind)",
"<!(./bin/variables.sh filesystem)",
"<!(./bin/variables.sh thread)",
"<!(./bin/variables.sh program_options)",
"<!(./bin/variables.sh system)",
"<!(./bin/variables.sh chrono)",
"<!(./bin/variables.sh libsecp256k1)",
"<!(./bin/variables.sh leveldb)",
"<!(./bin/variables.sh memenv)",
"<!(./bin/variables.sh bdb)",
"<!(./bin/variables.sh anl)",
"<!(./bin/variables.sh ssl)"
],
"ldflags": [
"<!(./bin/variables.sh load_archive)"
]
}
}
]
}

3
cache/.gitignore vendored
View File

@ -1,3 +0,0 @@
patch_sha.txt
src/*
depends/*

View File

@ -1,89 +0,0 @@
# Build & Install
This includes a detailed instructions for compiling. There are two main parts of the build, compiling Bitcoin Core as a static library and the Node.js bindings.
## Ubuntu 14.04 (Unix/Linux)
If git is not already installed, it can be installed by running:
```bash
sudo apt-get install git
```
If Node.js v0.12 isn't installed, it can be installed using "nvm", it can be done by following the installation script at [https://github.com/creationix/nvm#install-script](https://github.com/creationix/nvm#install-script) and then install version v0.12
```bash
nvm install v0.12
```
To build Bitcoin Core and bindings development packages are needed:
```bash
sudo apt-get install build-essential libtool autotools-dev automake autoconf pkg-config libssl-dev
```
Clone the bitcore-node repository locally:
```bash
git clone https://github.com/bitpay/bitcore-node.git
cd bitcore-node
```
And finally run the build which will take several minutes. A script in the "bin" directory will download Bitcoin Core v0.11, apply a patch (see more info below), and compile the static library and Node.js bindings. You can start this by running:
```bash
npm install
```
Once everything is built, you can run bitcore-node via:
```bash
npm start
```
This will then start the syncing process for Bitcoin Core and the extended capabilities as provided by the built-in Address Module (details below).
## Fedora
Later versions of Fedora (>= 22) should also work with this project. The directions for Ubuntu should generally work except the installation of system utilities and libraries is a bit different. Git is already installed and ready for use without installation.
```bash
yum install libtool automake autoconf pkgconfig openssl make gcc gcc-c++ kernel-devel openssl-devel.x86_64 patch
```
## Mac OS X Yosemite
If Xcode is not already installed, it can be installed via the Mac App Store (will take several minutes). XCode includes "Clang", "git" and other build tools. Once Xcode is installed, you'll then need to install "xcode-select" via running in a terminal and following the prompts:
```bash
xcode-select --install
```
If "Homebrew" is not yet installed, it's needed to install "autoconf" and others. You can install it using the script at [http://brew.sh](http://brew.sh) and following the directions at [https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/Installation.md](https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/Installation.md) And then run in a terminal:
```bash
brew install autoconf automake libtool openssl pkg-config
```
If Node.js v0.12 and associated commands "node", "npm" and "nvm" are not already installed, you can use "nvm" by running the script at [https://github.com/creationix/nvm#install-script](https://github.com/creationix/nvm#install-script) And then run this command to install Node.js v0.12
```bash
nvm install v0.12
```
Clone the bitcore-node repository locally:
```bash
git clone https://github.com/bitpay/bitcore-node.git
cd bitcore-node
```
And finally run the build which will take several minutes. A script in the "bin" directory will download Bitcoin Core v0.11, apply a patch (see more info below), and compile the static library and Node.js bindings. You can start this by running:
```bash
npm install
```
Once everything is built, you can run bitcore-node via:
```bash
npm start
```
This will then start the syncing process for Bitcoin Core and the extended capabilities as provided by the built-in Address Module (details below).

View File

@ -20,11 +20,11 @@ bus.close();
```javascript
// subscribe to all transaction events
bus.subscribe('db/transaction');
bus.subscribe('bitcoind/rawtransaction');
// only subscribe to events relevant to a bitcoin address
bus.subscribe('address/transaction', ['13FMwCYz3hUhwPcaWuD2M1U2KzfTtvLM89']);
// to subscribe to new block hashes
bus.subscribe('bitcoind/hashblock');
// unsubscribe
bus.unsubscribe('db/transaction');
bus.unsubscribe('bitcoind/rawtransaction');
```

162
docs/development.md Normal file
View File

@ -0,0 +1,162 @@
# Setting up Development Environment
## Install Node.js
Install Node.js by your favorite method, or use Node Version Manager by following directions at https://github.com/creationix/nvm
```bash
nvm install v4
```
## Fork and Download Repositories
To develop bitcore-node:
```bash
cd ~
git clone git@github.com:<yourusername>/bitcore-node.git
git clone git@github.com:<yourusername>/bitcore-lib.git
```
To develop bitcoin or to compile from source:
```bash
git clone git@github.com:<yourusername>/bitcoin.git
git fetch origin <branchname>:<branchname>
git checkout <branchname>
```
**Note**: See bitcoin documentation for building bitcoin on your platform.
## Install Development Dependencies
For Ubuntu:
```bash
sudo apt-get install libzmq3-dev
sudo apt-get install build-essential
```
**Note**: Make sure that libzmq-dev is not installed, it should be removed when installing libzmq3-dev.
For Mac OS X:
```bash
brew install zeromq
```
## Install and Symlink
```bash
cd bitcore-lib
npm install
cd ../bitcore-node
npm install
```
**Note**: If you get a message about not being able to download bitcoin distribution, you'll need to compile bitcoind from source, and setup your configuration to use that version.
We now will setup symlinks in `bitcore-node` *(repeat this for any other modules you're planning on developing)*:
```bash
cd node_modules
rm -rf bitcore-lib
ln -s ~/bitcore-lib
rm -rf bitcoind-rpc
ln -s ~/bitcoind-rpc
```
And if you're compiling or developing bitcoin:
```bash
cd ../bin
ln -sf ~/bitcoin/src/bitcoind
```
## Run Tests
If you do not already have mocha installed:
```bash
npm install mocha -g
```
To run all test suites:
```bash
cd bitcore-node
npm run regtest
npm run test
```
To run a specific unit test in watch mode:
```bash
mocha -w -R spec test/services/bitcoind.unit.js
```
To run a specific regtest:
```bash
mocha -R spec regtest/bitcoind.js
```
## Running a Development Node
To test running the node, you can setup a configuration that will specify development versions of all of the services:
```bash
cd ~
mkdir devnode
cd devnode
mkdir node_modules
touch bitcore-node.json
touch package.json
```
Edit `bitcore-node.json` with something similar to:
```json
{
"network": "livenet",
"port": 3001,
"services": [
"bitcoind",
"web",
"insight-api",
"insight-ui",
"<additional_service>"
],
"servicesConfig": {
"bitcoind": {
"spawn": {
"datadir": "/home/<youruser>/.bitcoin",
"exec": "/home/<youruser>/bitcoin/src/bitcoind"
}
}
}
}
```
**Note**: To install services [insight-api](https://github.com/bitpay/insight-api) and [insight-ui](https://github.com/bitpay/insight-ui) you'll need to clone the repositories locally.
Setup symlinks for all of the services and dependencies:
```bash
cd node_modules
ln -s ~/bitcore-lib
ln -s ~/bitcore-node
ln -s ~/insight-api
ln -s ~/insight-ui
```
Make sure that the `<datadir>/bitcoin.conf` has the necessary settings, for example:
```
server=1
whitelist=127.0.0.1
txindex=1
addressindex=1
timestampindex=1
spentindex=1
zmqpubrawtx=tcp://127.0.0.1:28332
zmqpubhashblock=tcp://127.0.0.1:28332
rpcallowip=127.0.0.1
rpcuser=bitcoin
rpcpassword=local321
```
From within the `devnode` directory with the configuration file, start the node:
```bash
../bitcore-node/bin/bitcore-node start
```

View File

@ -1,16 +0,0 @@
# Errors
Many times there are cases where an error condition can be gracefully handled depending on a particular use. To assist in better error handling, errors will have different types so that it's possible to determine the type of error and handle appropriately.
```js
node.services.address.getUnspentOutputs('00000000839a8...', function(err, outputs) {
if (err instanceof errors.NoOutputs) {
// the address hasn't received any transactions
}
// otherwise the address has outputs (which may be unspent/spent)
});
```
For more information about different types of errors, please see `lib/errors.js`.

View File

@ -1,54 +0,0 @@
A Bitcoin full node for building applications and services with Node.js. A node is extensible and can be configured to run additional services. At the minimum a node has native bindings to Bitcoin Core with the [Bitcoin Service](services/bitcoind.md). Additional services can be enabled to make a node more useful such as exposing new APIs, adding new indexes for addresses with the [Address Service](services/address.md), running a block explorer, wallet service, and other customizations.
# Install
```bash
npm install -g bitcore-node
bitcore-node start
```
Note: For your convenience, we distribute binaries for x86_64 Linux and x86_64 Mac OS X. Upon npm install, the binaries for your platform will be downloaded. For more detailed installation instructions, or if you want to compile the project yourself, then please see the [Build & Install](build.md) documentation to build the project from source.
# Prerequisites
- Node.js v0.12 or v4.2
- ~100GB of disk storage
- ~4GB of RAM
- Mac OS X >= 10.9, Ubuntu >= 12.04 (libc >= 2.15 and libstdc++ >= 6.0.16)
# Configuration
Bitcore includes a Command Line Interface (CLI) for managing, configuring and interfacing with your Bitcore Node.
```bash
bitcore-node create -d <bitcoin-data-dir> mynode
cd mynode
bitcore-node install <service>
bitcore-node install https://github.com/yourname/helloworld
```
This will create a directory with configuration files for your node and install the necessary dependencies. For more information about (and developing) services, please see the [Service Documentation](services.md).
To start bitcore as a daemon:
```bash
bitcore start --daemon
```
# Add-on Services
There are several add-on services available to extend the functionality of Bitcore Node:
- [Insight API](https://github.com/bitpay/insight-api/tree/v0.3.0)
- [Insight UI](https://github.com/bitpay/insight/tree/v0.3.0)
# Documentation
- [Services](services.md)
- [Bitcoind](services/bitcoind.md) - Native bindings to Bitcoin Core
- [Database](services/db.md) - The foundation API methods for getting information about blocks and transactions.
- [Address](services/address.md) - Adds additional API methods for querying and subscribing to events with bitcoin addresses.
- [Web](services/web.md) - Creates an express application over which services can expose their web/API content
- [Build & Install](build.md) - How to build and install from source
- [Testing & Development](testing.md) - Developer guide for testing
- [Node](node.md) - Details on the node constructor
- [Bus](bus.md) - Overview of the event bus constructor
- [Errors](errors.md) - Reference for error handling and types
- [Patch](patch.md) - Information about the patch applied to Bitcoin Core
- [Release Process](release.md) - Information about verifying a release and the release process.

1
docs/index.md Symbolic link
View File

@ -0,0 +1 @@
../README.md

View File

@ -1,6 +0,0 @@
# Static Library Patch
To provide native bindings to JavaScript _(or any other language for that matter)_, Bitcoin code, itself, must be linkable. Currently, Bitcoin Core provides a JSON RPC interface to bitcoind as well as a shared library for script validation _(and hopefully more)_ called libbitcoinconsensus. There is a node module, [node-libbitcoinconsensus](https://github.com/bitpay/node-libbitcoinconsensus), that exposes these methods. While these interfaces are useful for several use cases, there are additional use cases that are not fulfilled, and being able to implement customized interfaces is necessary. To be able to do this a few simple changes need to be made to Bitcoin Core to compile as a static library.
The patch is located at `etc/bitcoin.patch` and adds a configure option `--enable-daemonlib` to compile all object files with `-fPIC` (Position Independent Code - needed to create a shared object), exposes leveldb variables and objects, exposes the threadpool to the bindings, and conditionally includes the main function.
Every effort will be made to ensure that this patch stays up-to-date with the latest release of Bitcoin. At the very least, this project began supporting Bitcoin Core v0.11.

View File

@ -1,55 +1,28 @@
# Release Process
Binaries for the C++ binding file (which includes libbitcoind statically linked in) are distributed for convenience. The binary binding file `bitcoind.node` is signed and published to S3 for later download and installation. Source files can also be built if binaries are not desired.
## How to Verify Signatures
```
cd build/Release
gpg --verify bitcoind.node.sig bitcoind.node
```
To verify signatures, use the following PGP keys:
- @braydonf: [https://pgp.mit.edu/pks/lookup?op=get&search=0x9BBF07CAC07A276D](https://pgp.mit.edu/pks/lookup?op=get&search=0x9BBF07CAC07A276D)
- @kleetus: [https://pgp.mit.edu/pks/lookup?op=get&search=0x33195D27EF6BDB7F](https://pgp.mit.edu/pks/lookup?op=get&search=0x33195D27EF6BDB7F)
- @pnagurny: [https://pgp.mit.edu/pks/lookup?op=get&search=0x0909B33F0AA53013](https://pgp.mit.edu/pks/lookup?op=get&search=0x0909B33F0AA53013)
Binaries for bitcoind are distributed for convenience and built deterministically with Gitian, signatures for bitcoind are located at the [gitian.sigs](https://github.com/bitpay/gitian.sigs) respository.
## How to Release
Ensure you've followed the instructions in the README.md for building the project from source. When building for any platform, be sure to keep in mind the minimum supported C and C++ system libraries and build from source using this library. Example, Ubuntu 12.04 has the earliest system library for Linux that we support, so it would be easiest to build the Linux artifact using this version. You will be using node-gyp to build the C++ bindings. A script will then upload the bindings to S3 for later use. You will also need credentials for BitPay's bitcore-node S3 bucket and be listed as an author for the bitcore-node's npm module.
- Create a file `.bitcore-node-upload.json` in your home directory
- The format of this file should be:
```json
{
"region": "us-west-2",
"accessKeyId": "xxx",
"secretAccessKey": "yyy"
}
```
When publishing to npm, the .gitignore file is used to exclude files from the npm publishing process. Be sure that the bitcore-node directory has only the directories and files that you would like to publish to npm. You might need to run the commands below on each platform that you intend to publish (e.g. Mac and Linux).
To make a release, bump the `version` and `lastBuild` of the `package.json`:
To make a release, bump the `version` of the `package.json`:
```bash
git checkout master
git pull upstream master
git commit -a -m "Bump package version to <version>"
npm install
npm run package
npm run upload
npm publish
```
And then update the `version` of the `package.json` for development (e.g. "0.3.2-dev"):
```bash
git commit -a -m "Bump development version to <version>"
npm run test
npm run regtest
npm run jshint
git commit -a -m "Bump package version to <version>"
git push upstream master
npm publish
```
Create a release tag and push it to the BitPay Github repo:
```bash
git tag <version>
git push upstream <version>
git tag -s v<version> -m 'v<version>'
git push upstream v<version>
```

View File

@ -10,7 +10,7 @@ The `bitcore-node.json` file describes which services will load for a node:
```json
{
"services": [
"bitcoind", "db", "address", "insight-api"
"bitcoind", "web"
]
}
```
@ -37,31 +37,21 @@ If, instead, you would like to run a custom node, you can include services by in
var bitcore = require('bitcore-node');
//Services
var Address = bitcore.services.Address;
var Bitcoin = bitcore.services.Bitcoin;
var DB = bitcore.services.DB;
var Web = bitcore.services.Web;
var myNode = new bitcore.Node({
datadir: '/home/user/.bitcore',
network: {
name: 'livenet'
},
"services": [
{
name: "address",
module: Address,
config: {}
},
network: 'regtest'
services: [
{
name: 'bitcoind',
module: Bitcoin,
config: {}
},
{
name: 'db',
module: DB,
config: {}
config: {
spawn: {
datadir: '/home/<username>/.bitcoin',
exec: '/home/<username>/bitcore-node/bin/zcashd'
}
}
},
{
name: 'web',
@ -77,8 +67,8 @@ var myNode = new bitcore.Node({
Now that you've loaded your services you can access them via `myNode.services.<service-name>.<method-name>`. For example if you wanted to check the balance of an address, you could access the address service like so.
```js
myNode.services.address.getBalance('1HB5XMLmzFVj8ALj6mfBsbifRoD4miY36v', false, function(err, total) {
console.log(total); //Satoshi amount of this address
myNode.services.bitcoind.getAddressBalance('1HB5XMLmzFVj8ALj6mfBsbifRoD4miY36v', false, function(err, total) {
console.log(total.balance); //Satoshi amount of this address
});
```
@ -96,140 +86,3 @@ The `package.json` for the service module can either export the `Node.Service` d
Please take a look at some of the existing services for implementation specifics.
### Adding an index
One quite useful feature exposed to services is the ability to index arbitrary data in the blockchain. To do so we make use of leveldb, a simple key-value store. As a service we can expose a 'blockHandler' function which is called each time a new block is added or removed from the blockchain. This gives us access to every new transaction received, allowing us to index them. Let's take a look at an example where we will index the time that a transaction was confirmed.
```js
//Index prefix, so that we can determine the difference between our index
//and the indexes provided by other services
MyService.datePrefix = new Buffer('10', 'hex');
MyService.minPosition = new Buffer('00000', 'hex');
MyService.maxPosition = new Buffer('99999', 'hex');
//This function is automatically called when a block is added or receieved
MyService.prototype.prototype.blockHandler = function(block, addOutput, callback) {
//Determine if the block is added or removed, and therefore whether we are adding
//or deleting indexes
var databaseAction = 'put';
if (!addOutput) {
databaseAction = 'del';
}
//An array of all leveldb operations we will be committing
var operations = [];
//Timestamp of the current block
var blocktime = new Buffer(4);
blocktime.writeUInt32BE(block.header.time);
for (var i = 0; i < block.transactions.length; i++) {
var transaction = block.transactions[i];
var txid = new Buffer(transaction.id, 'hex');
var position = new Buffer(('0000' + i).slice(-5), 'hex');
//To be able to query this txid by the block date we create an index, leading with the prefix we
//defined earlier, the the current blocktime, and finally a differentiator, in this case the index
//of this transaction in the block's transaction list
var indexOperation = {
type: databaseAction,
key: Buffer.concat([this.datePrefix, blockTime, position]),
value: txid
};
//Now we push this index into our list of operations that should be performed
operations.push(indexOperation);
}
//Send the list of db operations back so they can be performed
setImmediate(function() {
callback(null, operations);
});
};
```
### Retrieving data using an index
With our block handler code every transaction in the blockchain will now be indexed. However, if we want to query this data we need to add a method to our service to expose it.
```js
MyService.prototype.getTransactionIdsByDate = function(startDateBuffer, endDateBuffer, callback) {
var error;
var transactions = [];
//Read data from leveldb which is between our startDate and endDate
var stream = this.node.services.db.store.createReadStream({
gte: Buffer.concat([
MyService.datePrefix,
startDateBuffer,
MyService.minPosition
]),
lte: Buffer.concat([
MyService.datePrefix,
endDateBuffer,
MyService.maxPosition
]),
valueEncoding: 'binary',
keyEncoding: 'binary'
});
stream.on('data', function(data) {
transactions.push(data.value.toString('hex'));
});
stream.on('error', function(streamError) {
if (streamError) {
error = streamError;
}
});
stream.on('close', function() {
if (error) {
return callback(error);
}
callback(null, transactions);
});
};
```
If you're new to leveldb and would like to better understand how createReadStream works you can find [more information here](https://github.com/Level/levelup#dbcreatereadstreamoptions).
### Understanding indexes
You may notice there are several pieces to the index itself. Let's take a look at each piece to make them easier to understand.
#### Prefixes
Since leveldb is just a simple key-value store we need something to differentiate which keys are part of which index. If we had two services trying to index on the same key, say a txid, they would overwrite each other and their queries would return results from the other index. By introducing a unique prefix per index type that we can prepend our indexes with prevents these collisions.
```js
//A simple example of indexing the number of inputs and ouputs given a transaction id
/** Wrong way **/
var index1key = new Buffer(transaction.id, 'hex');
var index1value = transaction.inputs.length;
//Since this key has the same value it would just overwrite index1 when we write to the db
var index2key = new Buffer(transaction.id, 'hex');
var index2value = transaction.outputs.length;
/** Right way **/
var index1prefix = new Buffer('11', 'hex');
var index2prefix = new Buffer('12', 'hex');
var index1key = Buffer.concat([index1prefix, new Buffer(transaction.id, 'hex')]);
var index1value = transaction.inputs.length;
//Now that the keys are different, this won't overwrite the index
var index2key = Buffer.concat([index2prefix, new Buffer(transaction.id, 'hex')]);
var index2value = transaction.outputs.length;
```
Remember that all indexes are global, so check to make sure no other services you are using make use of the same prefix you plan to use in your service. We recommend documenting which prefixes you use and that you check for collisions with popular services if you plan to release your service for others to use.
#### Index Key
The index key is the value you want to query by. This value should be deterministic so that it can be removed in the case of a [re-org](https://en.bitcoin.it/wiki/Chain_Reorganization) resulting in a block removal. The value should be unique, as no two indexes can be the same value. If you need two indexes with the same key value, consider adding a deterministic differentiator, such as a position in an array, or instead storing multiple values within the same index data.
#### Index Data
This is the data which is returned when you search by the index's key. This can be whatever you would like to retrieve. Try to be efficient by not storing data that is already available elsewhere, such as storing a transaction ID instead of an entire transaction.

View File

@ -1,136 +0,0 @@
# Address Service
The address service builds on the [Bitcoin Service](bitcoind.md) and the [Database Service](db.md) to add additional functionality for querying and subscribing to information based on bitcoin addresses. This will typically represent the core functionality for wallet applications.
## API Documentation
These methods are exposed over the JSON-RPC interface and can be called directly from a node via:
```js
node.services.address.<methodName>
```
**Get Unspent Outputs**
One of the most common uses will be to retrieve unspent outputs necessary to create a transaction, here is how to get the unspent outputs for an address:
```js
var address = 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW';
var includeMempool = true;
node.services.address.getUnspentOutputs(address, includeMempool, function(err, unspentOutputs) {
// see below
});
```
The `unspentOutputs` will have the format:
```js
[
{
address: 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW',
txid: '9d956c5d324a1c2b12133f3242deff264a9b9f61be701311373998681b8c1769',
outputIndex: 1,
height: 150,
satoshis: 1000000000,
script: '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac',
confirmations: 3
}
]
```
**View Balances**
```js
var address = 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW';
var includeMempool = true;
node.services.address.getBalance(address, includeMempool, function(err, balance) {
// balance will be in satoshis
});
```
**View Address History**
This method will give history of an address limited by a range of block heights by using the "start" and "end" arguments. The "start" value is the more recent, and greater, block height. The "end" value is the older, and lesser, block height. This feature is most useful for synchronization as previous history can be omitted. Furthermore for large ranges of block heights, results can be paginated by using the "from" and "to" arguments.
```js
var addresses = ['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW'];
var options = {
start: 345000,
end: 344000,
queryMempool: true
};
node.services.address.getAddressHistory(addresses, options, function(err, history) {
// see below
});
```
The history format will be:
```js
{
totalCount: 1, // The total number of items within "start" and "end"
items: [
{
addresses: {
'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW': {
inputIndexes: [],
outputIndexes: [0]
}
},
satoshis: 1000000000,
height: 150, // the block height of the transaction
confirmations: 3,
timestamp: 1442948127, // in seconds
fees: 191,
tx: <Transaction> // the populated transaction
}
]
}
```
**View Address Summary**
```js
var address = 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW';
var options = {
noTxList: false
};
node.services.address.getAddressSummary(address, options, function(err, summary) {
// see below
});
```
The `summary` will have the format (values are in satoshis):
```js
{
totalReceived: 1000000000,
totalSpent: 0,
balance: 1000000000,
unconfirmedBalance: 1000000000,
appearances: 1, // number of transactions
unconfirmedAppearances: 0,
txids: [
'3f7d13efe12e82f873f4d41f7e63bb64708fc4c942eb8c6822fa5bd7606adb00'
]
}
```
## Events
For details on instantiating a bus for a node, see the [Bus Documentation](../bus.md).
- Name: `address/transaction`, Arguments: `[address, address...]`
- Name: `address/balance`, Arguments: `[address, address...]`
**Examples:**
```js
bus.subscribe('address/transaction', ['13FMwCYz3hUhwPcaWuD2M1U2KzfTtvLM89']);
bus.subscribe('address/balance', ['13FMwCYz3hUhwPcaWuD2M1U2KzfTtvLM89']);
bus.on('address/transaction', function(transaction) {
});
bus.on('address/balance', function(balance) {
});
```

View File

@ -1,20 +1,120 @@
# Bitcoin Service
The Bitcoin Service adds a native [Node.js](https://nodejs.org) interface to [Bitcoin Core](https://github.com/bitcoin/bitcoin) for querying information about the Bitcoin blockchain. Bindings are linked to Bitcoin Core compiled as a static library.
The Bitcoin Service is a Node.js interface to [Bitcoin Core](https://github.com/bitcoin/bitcoin) for querying information about the bitcoin block chain. It will manage starting and stopping `bitcoind` or connect to several running `bitcoind` processes. It uses a branch of a [branch of Bitcoin Core](https://github.com/bitpay/bitcoin/tree/0.12.1-bitcore) with additional indexes for querying information about addresses and blocks. Results are cached for performance and there are several additional API methods added for common queries.
## Configuration
The default configuration will include a "spawn" configuration in "bitcoind". This defines the location of the block chain database and the location of the `bitcoind` daemon executable. The below configuration points to a local clone of `bitcoin`, and will start `bitcoind` automatically with your Node.js application.
```json
"servicesConfig": {
"bitcoind": {
"spawn": {
"datadir": "/home/bitcore/.bitcoin",
"exec": "/home/bitcore/bitcoin/src/bitcoind"
}
}
}
```
It's also possible to connect to separately managed `bitcoind` processes with round-robin quering, for example:
```json
"servicesConfig": {
"bitcoind": {
"connect": [
{
"rpchost": "127.0.0.1",
"rpcport": 30521,
"rpcuser": "bitcoin",
"rpcpassword": "local321",
"zmqpubrawtx": "tcp://127.0.0.1:30611"
},
{
"rpchost": "127.0.0.1",
"rpcport": 30522,
"rpcuser": "bitcoin",
"rpcpassword": "local321",
"zmqpubrawtx": "tcp://127.0.0.1:30622"
},
{
"rpchost": "127.0.0.1",
"rpcport": 30523,
"rpcuser": "bitcoin",
"rpcpassword": "local321",
"zmqpubrawtx": "tcp://127.0.0.1:30633"
}
]
}
}
```
**Note**: For detailed example configuration see [`regtest/cluster.js`](regtest/cluster.js)
## API Documentation
These methods are currently only available via directly interfacing with a node:
Methods are available by directly interfacing with the service:
```js
node.services.bitcoind.<methodName>
```
### Chain
**Getting Latest Blocks**
```js
// gives the block hashes sorted from low to high within a range of timestamps
var high = 1460393372; // Mon Apr 11 2016 12:49:25 GMT-0400 (EDT)
var low = 1460306965; // Mon Apr 10 2016 12:49:25 GMT-0400 (EDT)
node.services.bitcoind.getBlockHashesByTimestamp(high, low, function(err, blockHashes) {
//...
});
// get the current tip of the chain
node.services.bitcoind.getBestBlockHash(function(err, blockHash) {
//...
})
```
**Getting Synchronization and Node Status**
```js
// gives a boolean if the daemon is fully synced (not the initial block download)
node.services.bitcoind.isSynced(function(err, synced) {
//...
})
// gives the current estimate of blockchain download as a percentage
node.services.bitcoind.syncPercentage(function(err, percent) {
//...
});
// gives information about the chain including total number of blocks
node.services.bitcoind.getInfo(function(err, info) {
//...
});
```
**Generate Blocks**
```js
// will generate a block for the "regtest" network (development purposes)
var numberOfBlocks = 10;
node.services.bitcoind.generateBlock(numberOfBlocks, function(err, blockHashes) {
//...
});
```
### Blocks and Transactions
**Getting Block Information**
It's possible to query blocks by both block hash and by height. Blocks are given as Node.js buffers and can be parsed via Bitcore:
It's possible to query blocks by both block hash and by height. Blocks are given as Node.js Buffers and can be parsed via Bitcore:
```js
var blockHeight = 0;
node.services.bitcoind.getBlock(blockHeight, function(err, blockBuffer) {
node.services.bitcoind.getRawBlock(blockHeight, function(err, blockBuffer) {
if (err) {
throw err;
}
@ -22,36 +122,43 @@ node.services.bitcoind.getBlock(blockHeight, function(err, blockBuffer) {
console.log(block);
};
// check if the block is part of the main chain
var mainChain = node.services.bitcoind.isMainChain(block.hash);
console.log(mainChain);
// get a bitcore object of the block (as above)
node.services.bitcoind.getBlock(blockHash, function(err, block) {
//...
};
// get only the block index (including chain work and previous hash)
var blockIndex = node.services.bitcoind.getBlockIndex(blockHeight);
console.log(blockIndex);
// get only the block header and index (including chain work, height, and previous hash)
node.services.bitcoind.getBlockHeader(blockHeight, function(err, blockHeader) {
//...
});
// get the block with a list of txids
node.services.bitcoind.getBlockOverview(blockHash, function(err, blockOverview) {
//...
};
```
**Retrieving and Sending Transactions**
Get a transaction asynchronously by reading it from disk, with an argument to optionally not include the mempool:
Get a transaction asynchronously by reading it from disk:
```js
var txid = '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623';
var queryMempool = true;
node.services.bitcoind.getTransaction(txid, queryMempool, function(err, transactionBuffer) {
node.services.bitcoind.getRawTransaction(txid, function(err, transactionBuffer) {
if (err) {
throw err;
}
var transaction = bitcore.Transaction().fromBuffer(transactionBuffer);
});
// get a bitcore object of the transaction (as above)
node.services.bitcoind.getTransaction(txid, function(err, transaction) {
//...
});
// also retrieve the block timestamp and height
node.services.bitcoind.getTransactionWithBlockInfo(txid, queryMempool, function(err, info) {
console.log(info.blockHash);
console.log(info.height);
console.log(info.timestamp); // in seconds
var transaction = bitcore.Transaction().fromBuffer(transactionBuffer);
// retrieve the transaction with input values, fees, spent and block info
node.services.bitcoind.getDetailedTransaction(txid, function(err, transaction) {
//...
});
```
@ -59,71 +166,164 @@ Send a transaction to the network:
```js
var numberOfBlocks = 3;
var feesPerKilobyte = node.services.bitcoind.estimateFee(numberOfBlocks); // in satoshis
node.services.bitcoind.estimateFee(numberOfBlocks, function(err, feesPerKilobyte) {
//...
});
try {
node.services.bitcoind.sendTransaction(transaction.serialize());
} catch(err) {
// handle error
node.services.bitcoind.sendTransaction(transaction.serialize(), function(err, hash) {
//...
});
```
### Addresses
**Get Unspent Outputs**
One of the most common uses will be to retrieve unspent outputs necessary to create a transaction, here is how to get the unspent outputs for an address:
```js
var address = 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW';
node.services.bitcoind.getAddressUnspentOutputs(address, options, function(err, unspentOutputs) {
// see below
});
```
The `unspentOutputs` will have the format:
```js
[
{
address: 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW',
txid: '9d956c5d324a1c2b12133f3242deff264a9b9f61be701311373998681b8c1769',
outputIndex: 1,
height: 150,
satoshis: 1000000000,
script: '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac',
confirmations: 3
}
]
```
**View Balances**
```js
var address = 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW';
node.services.bitcoind.getAddressBalance(address, options, function(err, balance) {
// balance will be in satoshis with "received" and "balance"
});
```
**View Address History**
This method will give history of an address limited by a range of block heights by using the "start" and "end" arguments. The "start" value is the more recent, and greater, block height. The "end" value is the older, and lesser, block height. This feature is most useful for synchronization as previous history can be omitted. Furthermore for large ranges of block heights, results can be paginated by using the "from" and "to" arguments.
```js
var addresses = ['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW'];
var options = {
start: 345000,
end: 344000,
queryMempool: true
};
node.services.bitcoind.getAddressHistory(addresses, options, function(err, history) {
// see below
});
```
The history format will be:
```js
{
totalCount: 1, // The total number of items within "start" and "end"
items: [
{
addresses: {
'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW': {
inputIndexes: [],
outputIndexes: [0]
}
},
satoshis: 1000000000,
tx: <detailed_transaction> // the same format as getDetailedTransaction
}
]
}
```
Get all of the transactions in the mempool:
**View Address Summary**
```js
var mempool = node.services.bitcoind.getMempoolTransactions();
var transactions = [];
for (var i = 0; i < mempool.length; i++) {
transactions.push(bitcore.Transaction().fromBuffer(transactions[i]));
var address = 'mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW';
var options = {
noTxList: false
};
node.services.bitcoind.getAddressSummary(address, options, function(err, summary) {
// see below
});
```
The `summary` will have the format (values are in satoshis):
```js
{
totalReceived: 1000000000,
totalSpent: 0,
balance: 1000000000,
unconfirmedBalance: 1000000000,
appearances: 1,
unconfirmedAppearances: 0,
txids: [
'3f7d13efe12e82f873f4d41f7e63bb64708fc4c942eb8c6822fa5bd7606adb00'
]
}
```
**Notes**:
- `totalReceived` does not exclude change *(the amount of satoshis originating from the same address)*
- `unconfirmedBalance` is the delta that the unconfirmed transactions have on the total balance *(can be both positive and negative)*
- `unconfirmedAppearances` is the total number of unconfirmed transactions
- `appearances` is the total confirmed transactions
- `txids` Are sorted in block order with the most recent at the beginning. A maximum of 1000 *(default)* will be returned, the `from` and `to` options can be used to get further values.
Determine if an output is spent (excluding the mempool):
```js
var spent = node.services.bitcoind.isSpent(txid, outputIndex);
console.log(spent);
```
**Miscellaneous**
- `bitcoind.start(callback)` - Start the JavaScript Bitcoin node, the callback is called when the daemon is ready.
- `bitcoind.getInfo()` - Basic information about the chain including total number of blocks.
- `bitcoind.isSynced()` - Returns a boolean if the daemon is fully synced (not the initial block download)
- `bitcoind.syncPercentage()` - Returns the current estimate of blockchain download as a percentage.
- `bitcoind.stop(callback)` - Stop the JavaScript bitcoin node safely, the callback will be called when bitcoind is closed. This will also be done automatically on `process.exit`. It also takes the bitcoind node off the libuv event loop. If the daemon object is the only thing on the event loop. Node will simply close.
## Events
The Bitcoin Service doesn't expose any events via the Bus, however there are a few events that can be directly registered:
The Bitcoin Service exposes two events via the Bus, and there are a few events that can be directly registered:
```js
node.services.bitcoind.on('tip', function(blockHash) {
// a new block tip has been added
// a new block tip has been added, if there is a rapid update (with a second) this will not emit every tip update
});
node.services.bitcoind.on('tx', function(txInfo) {
node.services.bitcoind.on('tx', function(transactionBuffer) {
// a new transaction has entered the mempool
});
node.services.bitcoind.on('txleave', function(txLeaveInfo) {
// a new transaction has left the mempool
node.services.bitcoind.on('block', function(blockHash) {
// a new block has been added
});
```
The `txInfo` object will have the format:
For details on instantiating a bus for a node, see the [Bus Documentation](../bus.md).
- Name: `bitcoind/rawtransaction`
- Name: `bitcoind/hashblock`
- Name: `bitcoind/addresstxid`, Arguments: [address, address...]
**Examples:**
```js
{
buffer: <Buffer...>,
mempool: true, // will currently always be true
hash: '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623'
}
```
bus.subscribe('bitcoind/rawtransaction');
bus.subscribe('bitcoind/hashblock');
bus.subscribe('bitcoind/addresstxid', ['13FMwCYz3hUhwPcaWuD2M1U2KzfTtvLM89']);
The `txLeaveInfo` object will have the format:
bus.on('bitcoind/rawtransaction', function(transactionHex) {
//...
});
```js
{
buffer: <Buffer...>,
hash: '7426c707d0e9705bdd8158e60983e37d0f5d63529086d6672b07d9238d5aa623'
}
bus.on('bitcoind/hashblock', function(blockhashHex) {
//...
});
bus.on('bitcoind/addresstxid', function(data) {
// data.address;
// data.txid;
});
```

View File

@ -1,113 +0,0 @@
# Database Service
This service synchronizes a leveldb database with the [Bitcoin Service](bitcoind.md) block chain by connecting and disconnecting blocks to build new indexes that can be queried. Other services can extend the data that is indexed by implementing a `blockHandler` method, similar to the built-in [Address Service](address.md).
## How to Reindex
If you need to be able to recreate the database from historical transactions in blocks:
- Shutdown your node
- Remove the `bitcore-node.db` directory in the data directory (e.g. `~/.bitcore/bitcore-node.db`)
- Start your node again
The database will then ask bitcoind for all the blocks again and recreate the database. This is sometimes required during upgrading as the format of the keys and values has changed. For "livenet" this can take half a day or more, for "testnet" this can take around an hour.
## Adding Indexes
For a service to include additional block data, it can implement a `blockHandler` method that will be run to when there are new blocks added or removed.
```js
CustomService.prototype.blockHandler = function(block, add, callback) {
var transactions = block.transactions;
var operations = [];
operations.push({
type: add ? 'put' : 'del',
key: 'key',
value: 'value'
});
callback(null, operations);
};
```
Take a look at the Address Service implementation for more details about how to encode the key, value for the best efficiency and ways to format the keys for streaming reads.
## API Documentation
These methods are exposed over the JSON-RPC interface and can be called directly from a node via:
```js
node.services.db.<methodName>
```
**Query Blocks by Date**
One of the additional indexes created by the Database Service is querying for blocks by ranges of dates:
```js
var newest = 1441914000; // Notice time is in seconds not milliseconds
var oldest = 1441911000;
node.services.db.getBlockHashesByTimestamp(newest, oldest, function(err, hashes) {
// hashes will be an array of block hashes
});
```
**Working with Blocks and Transactions as Bitcore Instances**
```js
var txid = 'c349b124b820fe6e32136c30e99f6c4f115fce4d750838edf0c46d3cb4d7281e';
var includeMempool = true;
node.services.db.getTransaction(txid, includeMempool, function(err, transaction) {
console.log(transaction.toObject());
});
var txid = 'c349b124b820fe6e32136c30e99f6c4f115fce4d750838edf0c46d3cb4d7281e';
var includeMempool = true;
node.services.db.getTransactionWithBlockInfo(txid, includeMempool, function(err, transaction) {
console.log(transaction.toObject());
console.log(transaction.__blockHash);
console.log(transaction.__height);
console.log(transaction.__timestamp);
});
var blockHash = '00000000d17332a156a807b25bc5a2e041d2c730628ceb77e75841056082a2c2';
node.services.db.getBlock(blockHash, function(err, block) {
console.log(block.toObject());
});
// contruct a transaction
var transaction = bitcore.Transaction(<serializedString>);
node.services.db.sendTransaction(transaction, function(err) {
if (err) {
throw err;
}
// otherwise the transaction has been sent
});
```
## Events
For details on instantiating a bus for a node, see the [Bus Documentation](../bus.md).
- Name: `db/transaction`
- Name: `db/block`
**Examples:**
```js
bus.subscribe('db/transaction');
bus.subscribe('db/block');
bus.on('db/block', function(blockHash) {
// blockHash will be a hex string of the block hash
});
bus.on('db/transaction', function(txInfo) {
// see below
});
```
The `txInfo` object will have the format:
```js
{
rejected: true, // If the transaction was rejected into the mempool
tx: <Transaction> // a Bitcore Transaction instance
}
```

View File

@ -1,52 +0,0 @@
# Development & Testing
To run all of the JavaScript tests:
```bash
npm run test
```
To run tests against the bindings, as defined in `bindings.gyp` the regtest feature of Bitcoin Core is used, and to enable this feature we currently need to build with the wallet enabled _(not a part of the regular build)_. To do this, export an environment variable and recompile:
```bash
export BITCORENODE_ENV=test
npm run build
```
If you do not already have mocha installed:
```bash
npm install mocha -g
```
To run the integration tests:
```bash
mocha -R spec integration/regtest.js
```
If any changes have been made to the bindings in the "src" directory, manually compile the Node.js bindings, as defined in `bindings.gyp`, you can run (-d for debug):
```bash
$ node-gyp -d rebuild
```
Note: `node-gyp` can be installed with `npm install node-gyp -g`
To be able to debug you'll need to have `gdb` and `node` compiled for debugging with gdb using `--gdb` (sometimes called node_g), and you can then run:
```bash
$ gdb --args node examples/node.js
```
To run mocha from within gdb (notice `_mocha` and not `mocha` so that the tests run in the same process):
```bash
$ gdb --args node /path/to/_mocha -R spec integration/regtest.js
```
To run the benchmarks:
```bash
$ cd benchmarks
$ node index.js
```

77
docs/upgrade.md Normal file
View File

@ -0,0 +1,77 @@
# Upgrade Notes
## From Bitcore 3.0.0 to 4.0.0
`bitcore-node@2.1.1` to `bitcore-node@3.0.0`
This major upgrade includes changes to indexes, API methods and services. Please review below details before upgrading.
### Indexes
Indexes include *more information* and are now also *faster*. Because of this a **reindex will be necessary** when upgrading as the address and database indexes are now a part of bitcoind with three new `bitcoin.conf` options:
- `-addressindex`
- `-timestampindex`
- `-spentindex`
To start reindexing add `reindex=1` during the **first startup only**.
### Configuration Options
- The `bitcoin.conf` file in will need to be updated to include additional indexes *(see below)*.
- The `datadir` option is now a part of `bitcoind` spawn configuration, and there is a new option to connect to multiple bitcoind processes (Please see [Bitcoin Service Docs](services/bitcoind.md) for more details). The services `db` and `address` are now a part of the `bitcoind` service. Here is how to update `bitcore-node.json` configuration options:
**Before**:
```json
{
"datadir": "/home/<username>/.bitcoin",
"network": "livenet",
"port": 3001,
"services": [
"address",
"bitcoind",
"db",
"web"
]
}
```
**After**:
```json
{
"network": "livenet",
"port": 3001,
"services": [
"bitcoind",
"web"
],
"servicesConfig": {
"bitcoind": {
"spawn": {
"datadir": "/home/<username>/.bitcoin",
"exec": "/home/<username>/bitcore-node/bin/zcashd"
}
}
}
}
```
It will also be necessary to update `bitcoin.conf` settings, to include these fields:
```
server=1
whitelist=127.0.0.1
txindex=1
addressindex=1
timestampindex=1
spentindex=1
zmqpubrawtx=tcp://127.0.0.1:<port>
zmqpubhashblock=tcp://127.0.0.1:<port>
rpcallowip=127.0.0.1
rpcuser=<user>
rpcpassword=<password>
```
**Important**: Once changes have been made you'll also need to add the `reindex=1` option **only for the first startup** to regenerate the indexes. Once this is complete you should be able to remove the `bitcore-node.db` directory with the old indexes.
### API and Service Changes
- Many API methods that were a part of the `db` and `address` services are now a part of the `bitcoind` service. Please see [Bitcoin Service Docs](services/bitcoind.md) for more details.
- The `db` and `address` services are deprecated, most of the functionality still exists. Any services that were extending indexes with the `db` service, will need to manage chain state itself, or build the indexes within `bitcoind`.

View File

@ -1,393 +0,0 @@
diff --git a/config_me.sh b/config_me.sh
new file mode 100644
index 0000000..19e9a1b
--- /dev/null
+++ b/config_me.sh
@@ -0,0 +1 @@
+./configure --enable-tests=no --enable-daemonlib --with-gui=no --without-qt --without-miniupnpc --without-bdb --enable-debug --disable-wallet --without-utils
diff --git a/configure.ac b/configure.ac
index 5debd21..3bfc59e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -119,6 +119,12 @@ AC_ARG_ENABLE([reduce-exports],
[use_reduce_exports=$enableval],
[use_reduce_exports=no])
+AC_ARG_ENABLE([daemonlib],
+ [AS_HELP_STRING([--enable-daemonlib],
+ [compile all of bitcoind as a library (default is no)])],
+ [use_daemonlib=$enableval],
+ [use_daemonlib=no])
+
AC_ARG_ENABLE([ccache],
[AS_HELP_STRING([--enable-ccache],
[use ccache for building (default is yes if ccache is found)])],
@@ -402,6 +408,9 @@ fi
if test x$use_hardening != xno; then
AX_CHECK_COMPILE_FLAG([-Wstack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"])
AX_CHECK_COMPILE_FLAG([-fstack-protector-all],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"])
+ if test x$use_daemonlib = xno; then
+ AX_CHECK_COMPILE_FLAG([-fPIE],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fPIE"])
+ fi
AX_CHECK_PREPROC_FLAG([-D_FORTIFY_SOURCE=2],[
AX_CHECK_PREPROC_FLAG([-U_FORTIFY_SOURCE],[
@@ -415,7 +424,7 @@ if test x$use_hardening != xno; then
AX_CHECK_LINK_FLAG([[-Wl,-z,relro]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,relro"])
AX_CHECK_LINK_FLAG([[-Wl,-z,now]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-z,now"])
- if test x$TARGET_OS != xwindows; then
+ if test x$TARGET_OS != xwindows -a x$use_daemonlib = xno; then
# All windows code is PIC, forcing it on just adds useless compile warnings
AX_CHECK_COMPILE_FLAG([-fPIE],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fPIE"])
AX_CHECK_LINK_FLAG([[-pie]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -pie"])
@@ -433,6 +442,17 @@ if test x$use_hardening != xno; then
OBJCXXFLAGS="$CXXFLAGS"
fi
+AC_DEFINE([ENABLE_DAEMONLIB],[0],[Enable daemonlib.])
+AM_CONDITIONAL([ENABLE_DAEMONLIB],[false])
+if test x$use_daemonlib != xno; then
+ AX_CHECK_COMPILE_FLAG([-fPIC],[DAEMONLIB_CXXFLAGS="$DAEMONLIB_CXXFLAGS -fPIC"])
+ AC_DEFINE([ENABLE_DAEMONLIB],[1],[Enable daemonlib.])
+ AM_CONDITIONAL([ENABLE_DAEMONLIB],[true])
+ CXXFLAGS="$CXXFLAGS $DAEMONLIB_CXXFLAGS"
+ CPPFLAGS="$CPPFLAGS $DAEMONLIB_CPPFLAGS"
+ OBJCXXFLAGS="$CXXFLAGS"
+fi
+
dnl this flag screws up non-darwin gcc even when the check fails. special-case it.
if test x$TARGET_OS = xdarwin; then
AX_CHECK_LINK_FLAG([[-Wl,-dead_strip]], [LDFLAGS="$LDFLAGS -Wl,-dead_strip"])
@@ -483,11 +503,18 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([
]
)
-if test x$use_reduce_exports = xyes; then
+if test x$use_reduce_exports = xyes -a x$use_daemonlib = xno; then
AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"],
[AC_MSG_ERROR([Cannot set default symbol visibility. Use --disable-reduce-exports.])])
fi
+AC_MSG_CHECKING([whether to compile as daemonlib])
+if test x$use_daemonlib != xno; then
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
LEVELDB_CPPFLAGS=
LIBLEVELDB=
LIBMEMENV=
diff --git a/depends/hosts/linux.mk b/depends/hosts/linux.mk
index b13a0f1..0513394 100644
--- a/depends/hosts/linux.mk
+++ b/depends/hosts/linux.mk
@@ -10,15 +10,15 @@ linux_debug_CXXFLAGS=$(linux_debug_CFLAGS)
linux_debug_CPPFLAGS=-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC
ifeq (86,$(findstring 86,$(build_arch)))
-i686_linux_CC=gcc -m32
-i686_linux_CXX=g++ -m32
+i686_linux_CC=${CC} -m32
+i686_linux_CXX=${CXX} -m32
i686_linux_AR=ar
i686_linux_RANLIB=ranlib
i686_linux_NM=nm
i686_linux_STRIP=strip
-x86_64_linux_CC=gcc -m64
-x86_64_linux_CXX=g++ -m64
+x86_64_linux_CC=${CC} -m64
+x86_64_linux_CXX=${CXX} -m64
x86_64_linux_AR=ar
x86_64_linux_RANLIB=ranlib
x86_64_linux_NM=nm
diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk
index 68841af..65a105b 100644
--- a/depends/packages/bdb.mk
+++ b/depends/packages/bdb.mk
@@ -9,6 +9,7 @@ define $(package)_set_vars
$(package)_config_opts=--disable-shared --enable-cxx --disable-replication
$(package)_config_opts_mingw32=--enable-mingw
$(package)_config_opts_linux=--with-pic
+$(package)_cxxflags_darwin=-stdlib=libc++
endef
define $(package)_preprocess_cmds
diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk
index e7aa48d..df0f7ae 100644
--- a/depends/packages/boost.mk
+++ b/depends/packages/boost.mk
@@ -1,9 +1,8 @@
package=boost
-$(package)_version=1_55_0
-$(package)_download_path=http://sourceforge.net/projects/boost/files/boost/1.55.0
+$(package)_version=1_57_0
+$(package)_download_path=http://sourceforge.net/projects/boost/files/boost/1.57.0
$(package)_file_name=$(package)_$($(package)_version).tar.bz2
-$(package)_sha256_hash=fff00023dd79486d444c8e29922f4072e1d451fc5a4d2b6075852ead7f2b7b52
-$(package)_patches=darwin_boost_atomic-1.patch darwin_boost_atomic-2.patch gcc_5_no_cxx11.patch
+$(package)_sha256_hash=910c8c022a33ccec7f088bd65d4f14b466588dda94ba2124e78b8c57db264967
define $(package)_set_vars
$(package)_config_opts_release=variant=release
@@ -11,7 +10,7 @@ $(package)_config_opts_debug=variant=debug
$(package)_config_opts=--layout=tagged --build-type=complete --user-config=user-config.jam
$(package)_config_opts+=threading=multi link=static -sNO_BZIP2=1 -sNO_ZLIB=1
$(package)_config_opts_linux=threadapi=pthread runtime-link=shared
-$(package)_config_opts_darwin=--toolset=darwin-4.2.1 runtime-link=shared
+$(package)_config_opts_darwin=--toolset=clang runtime-link=shared
$(package)_config_opts_mingw32=binary-format=pe target-os=windows threadapi=win32 runtime-link=static
$(package)_config_opts_x86_64_mingw32=address-model=64
$(package)_config_opts_i686_mingw32=address-model=32
@@ -20,15 +19,14 @@ $(package)_toolset_$(host_os)=gcc
$(package)_archiver_$(host_os)=$($(package)_ar)
$(package)_toolset_darwin=darwin
$(package)_archiver_darwin=$($(package)_libtool)
-$(package)_config_libraries=chrono,filesystem,program_options,system,thread,test
-$(package)_cxxflags=-fvisibility=hidden
-$(package)_cxxflags_linux=-fPIC
+$(package)_config_libraries=chrono,filesystem,program_options,system,thread
+$(package)_cxxflags=-fvisibility=default -fPIC
+$(package)_cxxflags_darwin=-std=c++11 -stdlib=libc++
+$(package)_linkflags=-stdlib=libc++
endef
+
define $(package)_preprocess_cmds
- patch -p2 < $($(package)_patch_dir)/darwin_boost_atomic-1.patch && \
- patch -p2 < $($(package)_patch_dir)/darwin_boost_atomic-2.patch && \
- patch -p2 < $($(package)_patch_dir)/gcc_5_no_cxx11.patch && \
echo "using $(boost_toolset_$(host_os)) : : $($(package)_cxx) : <cxxflags>\"$($(package)_cxxflags) $($(package)_cppflags)\" <linkflags>\"$($(package)_ldflags)\" <archiver>\"$(boost_archiver_$(host_os))\" <striper>\"$(host_STRIP)\" <ranlib>\"$(host_RANLIB)\" <rc>\"$(host_WINDRES)\" : ;" > user-config.jam
endef
diff --git a/src/Makefile.am b/src/Makefile.am
index 2461f82..7be6d6e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,12 @@
DIST_SUBDIRS = secp256k1
AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS)
+noinst_LTLIBRARIES =
+libbitcoind_la_LIBADD =
+libbitcoind_la_LDFLAGS = -no-undefined
+STATIC_BOOST_LIBS =
+STATIC_BDB_LIBS =
+STATIC_EXTRA_LIBS = $(STATIC_BOOST_LIBS) $(LIBLEVELDB) $(LIBMEMENV)
if EMBEDDED_LEVELDB
LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include
@@ -49,16 +55,16 @@ BITCOIN_INCLUDES += $(BDB_CPPFLAGS)
EXTRA_LIBRARIES += libbitcoin_wallet.a
endif
-if BUILD_BITCOIN_LIBS
-lib_LTLIBRARIES = libbitcoinconsensus.la
-LIBBITCOIN_CONSENSUS=libbitcoinconsensus.la
-else
-LIBBITCOIN_CONSENSUS=
-endif
-
+LIBBITCOIN_CONSENSUS =
bin_PROGRAMS =
TESTS =
+if BUILD_BITCOIN_LIBS
+noinst_LTLIBRARIES += libbitcoinconsensus.la
+LIBBITCOIN_CONSENSUS += libbitcoinconsensus.la
+endif
+
+if !ENABLE_DAEMONLIB
if BUILD_BITCOIND
bin_PROGRAMS += bitcoind
endif
@@ -66,6 +72,9 @@ endif
if BUILD_BITCOIN_UTILS
bin_PROGRAMS += bitcoin-cli bitcoin-tx
endif
+else
+noinst_LTLIBRARIES += libbitcoind.la
+endif
.PHONY: FORCE
# bitcoin core #
@@ -170,8 +179,11 @@ obj/build.h: FORCE
@$(MKDIR_P) $(builddir)/obj
@$(top_srcdir)/share/genbuild.sh $(abs_top_builddir)/src/obj/build.h \
$(abs_top_srcdir)
-libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
+ARCH_PLATFORM = $(shell ../../bin/variables.sh host)
+
+libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
+clientversion.cpp: obj/build.h
# server: shared between bitcoind and bitcoin-qt
libbitcoin_server_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS)
libbitcoin_server_a_SOURCES = \
@@ -310,9 +322,18 @@ nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h
bitcoind_SOURCES = bitcoind.cpp
bitcoind_CPPFLAGS = $(BITCOIN_INCLUDES)
bitcoind_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+libbitcoind_la_SOURCES = bitcoind.cpp
+libbitcoind_la_SOURCES += $(libbitcoin_util_a_SOURCES)
+libbitcoind_la_SOURCES += $(libbitcoin_univalue_a_SOURCES)
+libbitcoind_la_SOURCES += $(libbitcoin_crypto_a_SOURCES)
+libbitcoind_la_SOURCES += $(libbitcoin_common_a_SOURCES)
+libbitcoind_la_SOURCES += $(libbitcoin_server_a_SOURCES)
+libbitcoind_la_SOURCES += $(crypto_libbitcoin_crypto_a_SOURCES)
+libbitcoind_la_SOURCES += $(univalue_libbitcoin_univalue_a_SOURCES)
if TARGET_WINDOWS
bitcoind_SOURCES += bitcoind-res.rc
+libbitcoind_la_SOURCES += bitcoind-res.rc
endif
bitcoind_LDADD = \
@@ -327,10 +348,21 @@ bitcoind_LDADD = \
if ENABLE_WALLET
bitcoind_LDADD += libbitcoin_wallet.a
+STATIC_EXTRA_LIBS += $(STATIC_BDB_LIBS)
+libbitcoind_la_SOURCES += $(libbitcoin_wallet_a_SOURCES)
endif
+STATIC_BOOST_LIBS += ../depends/$(ARCH_PLATFORM)/lib/libboost_filesystem-mt.a ../depends/$(ARCH_PLATFORM)/lib/libboost_system-mt.a ../depends/$(ARCH_PLATFORM)/lib/libboost_chrono-mt.a ../depends/$(ARCH_PLATFORM)/lib/libboost_thread-mt.a ../depends/$(ARCH_PLATFORM)/lib/libboost_program_options-mt.a
+STATIC_BDB_LIBS += ../depends/$(ARCH_PLATFORM)/lib/libdb_cxx.a
+
bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS)
-#
+libbitcoind_la_LIBADD += $(SSL_LIBS) $(LIBSECP256K1) $(CRYPTO_LIBS) $(STATIC_EXTRA_LIBS)
+libbitcoind_la_CPPFLAGS = $(BITCOIN_INCLUDES)
+if TARGET_DARWIN
+libbitcoind_la_LDFLAGS += -Wl,-all_load
+else
+libbitcoind_la_LDFLAGS += -Wl,--whole-archive $(STATIC_EXTRA_LIBS) -Wl,--no-whole-archive
+endif
# bitcoin-cli binary #
bitcoin_cli_SOURCES = bitcoin-cli.cpp
diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp
index 6e2758a..0352a9d 100644
--- a/src/bitcoind.cpp
+++ b/src/bitcoind.cpp
@@ -33,6 +33,10 @@
static bool fDaemon;
+#if ENABLE_DAEMONLIB
+extern void WaitForShutdown(boost::thread_group* threadGroup);
+#endif
+
void WaitForShutdown(boost::thread_group* threadGroup)
{
bool fShutdown = ShutdownRequested();
@@ -166,6 +170,7 @@ bool AppInit(int argc, char* argv[])
return fRet;
}
+#if !ENABLE_DAEMONLIB
int main(int argc, char* argv[])
{
SetupEnvironment();
@@ -175,3 +180,4 @@ int main(int argc, char* argv[])
return (AppInit(argc, argv) ? 0 : 1);
}
+#endif
diff --git a/src/init.cpp b/src/init.cpp
index a04e4e0..33d0bc7 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -638,21 +638,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
umask(077);
}
- // Clean shutdown on SIGTERM
- struct sigaction sa;
- sa.sa_handler = HandleSIGTERM;
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
- sigaction(SIGTERM, &sa, NULL);
- sigaction(SIGINT, &sa, NULL);
-
- // Reopen debug.log on SIGHUP
- struct sigaction sa_hup;
- sa_hup.sa_handler = HandleSIGHUP;
- sigemptyset(&sa_hup.sa_mask);
- sa_hup.sa_flags = 0;
- sigaction(SIGHUP, &sa_hup, NULL);
-
#if defined (__SVR4) && defined (__sun)
// ignore SIGPIPE on Solaris
signal(SIGPIPE, SIG_IGN);
diff --git a/src/init.h b/src/init.h
index dcb2b29..5ce68ba 100644
--- a/src/init.h
+++ b/src/init.h
@@ -18,6 +18,11 @@ class thread_group;
extern CWallet* pwalletMain;
+#if ENABLE_DAEMONLIB
+#include <boost/filesystem/path.hpp>
+#include <boost/thread/mutex.hpp>
+#endif
+
void StartShutdown();
bool ShutdownRequested();
void Shutdown();
diff --git a/src/main.cpp b/src/main.cpp
index fe072ec..9f677cf 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1105,6 +1105,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Store transaction in memory
pool.addUnchecked(hash, entry, !IsInitialBlockDownload());
+ GetNodeSignals().TxToMemPool(tx);
}
SyncWithWallets(tx, NULL);
diff --git a/src/net.cpp b/src/net.cpp
index e4b22f9..33fe6f9 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -432,8 +432,10 @@ void CNode::PushVersion()
LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id);
else
LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id);
+ std::vector<std::string> bitcore;
+ bitcore.push_back("bitcore"); //the dash character is removed from the comments section
PushMessage("version", PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe,
- nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<string>()), nBestHeight, true);
+ nLocalHostNonce, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, bitcore), nBestHeight, true);
}
diff --git a/src/net.h b/src/net.h
index 17502b9..c9ae1b2 100644
--- a/src/net.h
+++ b/src/net.h
@@ -99,6 +99,8 @@ struct CNodeSignals
{
boost::signals2::signal<int ()> GetHeight;
boost::signals2::signal<bool (CNode*), CombinerAll> ProcessMessages;
+ boost::signals2::signal<bool (const CTransaction&)> TxToMemPool;
+ boost::signals2::signal<bool (const CTransaction&)> TxLeaveMemPool;
boost::signals2::signal<bool (CNode*, bool), CombinerAll> SendMessages;
boost::signals2::signal<void (NodeId, const CNode*)> InitializeNode;
boost::signals2::signal<void (NodeId)> FinalizeNode;
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index c3d1b60..03e265d 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -133,6 +133,7 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
if (!mapTx.count(hash))
continue;
const CTransaction& tx = mapTx[hash].GetTx();
+ GetNodeSignals().TxLeaveMemPool(tx);
if (fRecursive) {
for (unsigned int i = 0; i < tx.vout.size(); i++) {
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i));

View File

@ -1,49 +0,0 @@
'use strict';
var socket = require('socket.io-client')('http://localhost:3000');
socket.on('connect', function(){
console.log('connected');
});
socket.on('disconnect', function(){
console.log('disconnected');
});
var message = {
method: 'getOutputs',
params: ['2NChMRHVCxTPq9KeyvHQUSbfLaQY55Zzzp8', true]
};
socket.send(message, function(response) {
if(response.error) {
console.log('Error', response.error);
return;
}
console.log(response.result);
});
var message2 = {
method: 'getTransaction',
params: ['4f793f67fc7465f14fa3a8d3727fa7d133cdb2f298234548b94a5f08b6f4103e', true]
};
socket.send(message2, function(response) {
if(response.error) {
console.log('Error', response.error);
return;
}
console.log(response.result);
});
socket.on('transaction', function(obj) {
console.log(JSON.stringify(obj, null, 2));
});
socket.on('address/transaction', function(obj) {
console.log(JSON.stringify(obj, null, 2));
});
socket.emit('subscribe', 'transaction');
socket.emit('subscribe', 'address/transaction', ['13FMwCYz3hUhwPcaWuD2M1U2KzfTtvLM89']);

View File

@ -1,26 +1,12 @@
'use strict';
var semver = require('semver');
var packageData = require('./package.json');
function nodeVersionCheck(version, expected) {
if (!semver.satisfies(version, expected)) {
throw new Error('Node.js version ' + version + ' is expected to be ' + expected);
}
}
nodeVersionCheck(process.versions.node, packageData.engines.node);
module.exports = require('./lib');
module.exports.nodeVersionCheck = nodeVersionCheck;
module.exports.Node = require('./lib/node');
module.exports.Transaction = require('./lib/transaction');
module.exports.Service = require('./lib/service');
module.exports.errors = require('./lib/errors');
module.exports.services = {};
module.exports.services.Address = require('./lib/services/address');
module.exports.services.Bitcoin = require('./lib/services/bitcoind');
module.exports.services.DB = require('./lib/services/db');
module.exports.services.Web = require('./lib/services/web');
module.exports.scaffold = {};
@ -37,3 +23,5 @@ module.exports.cli.main = require('./lib/cli/main');
module.exports.cli.daemon = require('./lib/cli/daemon');
module.exports.cli.bitcore = require('./lib/cli/bitcore');
module.exports.cli.bitcored = require('./lib/cli/bitcored');
module.exports.lib = require('bitcore-lib-zcash');

View File

@ -1,9 +0,0 @@
server=1
whitelist=127.0.0.1
rpcssl=1
rpcsslcertificatechainfile=../bitcoind.crt
rpcsslprivatekeyfile=../bitcoind_no_pass.key
txindex=1
rpcallowip=127.0.0.1
rpcuser=bitcoin
rpcpassword=local321

View File

@ -1,492 +0,0 @@
'use strict';
// These tests require bitcore-node Bitcoin Core bindings to be compiled with
// the environment variable BITCORENODE_ENV=test. This enables the use of regtest
// functionality by including the wallet in the build.
// To run the tests: $ mocha -R spec integration/regtest.js
var index = require('..');
var log = index.log;
if (process.env.BITCORENODE_ENV !== 'test') {
log.info('Please set the environment variable BITCORENODE_ENV=test and make sure bindings are compiled for testing');
process.exit();
}
var chai = require('chai');
var bitcore = require('bitcore-lib');
var BN = bitcore.crypto.BN;
var async = require('async');
var rimraf = require('rimraf');
var bitcoind;
/* jshint unused: false */
var should = chai.should();
var assert = chai.assert;
var sinon = require('sinon');
var BitcoinRPC = require('bitcoind-rpc');
var transactionData = [];
var blockHashes = [];
var utxos;
var client;
var coinbasePrivateKey;
var privateKey = bitcore.PrivateKey();
var destKey = bitcore.PrivateKey();
describe('Daemon Binding Functionality', function() {
before(function(done) {
this.timeout(30000);
// Add the regtest network
bitcore.Networks.remove(bitcore.Networks.testnet);
bitcore.Networks.add({
name: 'regtest',
alias: 'regtest',
pubkeyhash: 0x6f,
privatekey: 0xef,
scripthash: 0xc4,
xpubkey: 0x043587cf,
xprivkey: 0x04358394,
networkMagic: 0xfabfb5da,
port: 18444,
dnsSeeds: [ ]
});
var datadir = __dirname + '/data';
rimraf(datadir + '/regtest', function(err) {
if (err) {
throw err;
}
bitcoind = require('../').services.Bitcoin({
node: {
datadir: datadir,
network: {
name: 'regtest'
}
}
});
bitcoind.on('error', function(err) {
log.error('error="%s"', err.message);
});
log.info('Waiting for Bitcoin Core to initialize...');
bitcoind.start(function() {
log.info('Bitcoind started');
client = new BitcoinRPC({
protocol: 'https',
host: '127.0.0.1',
port: 18332,
user: 'bitcoin',
pass: 'local321',
rejectUnauthorized: false
});
log.info('Generating 100 blocks...');
// Generate enough blocks so that the initial coinbase transactions
// can be spent.
setImmediate(function() {
client.generate(150, function(err, response) {
if (err) {
throw err;
}
blockHashes = response.result;
log.info('Preparing test data...');
// Get all of the unspent outputs
client.listUnspent(0, 150, function(err, response) {
utxos = response.result;
async.mapSeries(utxos, function(utxo, next) {
async.series([
function(finished) {
// Load all of the transactions for later testing
client.getTransaction(utxo.txid, function(err, txresponse) {
if (err) {
throw err;
}
// add to the list of transactions for testing later
transactionData.push(txresponse.result.hex);
finished();
});
},
function(finished) {
// Get the private key for each utxo
client.dumpPrivKey(utxo.address, function(err, privresponse) {
if (err) {
throw err;
}
utxo.privateKeyWIF = privresponse.result;
finished();
});
}
], next);
}, function(err) {
if (err) {
throw err;
}
done();
});
});
});
});
});
});
});
after(function(done) {
this.timeout(20000);
bitcoind.stop(function(err, result) {
done();
});
});
describe('get blocks by hash', function() {
[0,1,2,3,5,6,7,8,9].forEach(function(i) {
it('generated block ' + i, function(done) {
bitcoind.getBlock(blockHashes[i], function(err, response) {
if (err) {
throw err;
}
should.exist(response);
var block = bitcore.Block.fromBuffer(response);
block.hash.should.equal(blockHashes[i]);
done();
});
});
});
});
describe('get blocks by height', function() {
[0,1,2,3,4,5,6,7,8,9].forEach(function(i) {
it('generated block ' + i, function(done) {
// add the genesis block
var height = i + 1;
bitcoind.getBlock(i + 1, function(err, response) {
if (err) {
throw err;
}
should.exist(response);
var block = bitcore.Block.fromBuffer(response);
block.hash.should.equal(blockHashes[i]);
done();
});
});
});
it('will get error with number greater than tip', function(done) {
bitcoind.getBlock(1000000000, function(err, response) {
should.exist(err);
done();
});
});
});
describe('get transactions by hash', function() {
[0,1,2,3,4,5,6,7,8,9].forEach(function(i) {
it('for tx ' + i, function(done) {
var txhex = transactionData[i];
var tx = new bitcore.Transaction();
tx.fromString(txhex);
bitcoind.getTransaction(tx.hash, true, function(err, response) {
if (err) {
throw err;
}
assert(response.toString('hex') === txhex, 'incorrect tx data result');
done();
});
});
});
it('will return null if the transaction does not exist', function(done) {
var txid = '6226c407d0e9705bdd7158e60983e37d0f5d23529086d6672b07d9238d5aa618';
bitcoind.getTransaction(txid, true, function(err, response) {
if (err) {
throw err;
}
should.not.exist(response);
done();
});
});
});
describe('get block index', function() {
var expectedWork = new BN(6);
[1,2,3,4,5,6,7,8,9].forEach(function(i) {
it('generate block ' + i, function() {
var blockIndex = bitcoind.getBlockIndex(blockHashes[i]);
should.exist(blockIndex);
should.exist(blockIndex.chainWork);
var work = new BN(blockIndex.chainWork, 'hex');
work.cmp(expectedWork).should.equal(0);
expectedWork = expectedWork.add(new BN(2));
should.exist(blockIndex.prevHash);
blockIndex.hash.should.equal(blockHashes[i]);
blockIndex.prevHash.should.equal(blockHashes[i - 1]);
blockIndex.height.should.equal(i + 1);
});
});
it('will get null prevHash for the genesis block', function() {
var blockIndex = bitcoind.getBlockIndex(0);
should.exist(blockIndex);
should.equal(blockIndex.prevHash, null);
});
});
describe('get block index by height', function() {
var expectedWork = new BN(6);
[2,3,4,5,6,7,8,9].forEach(function(i) {
it('generate block ' + i, function() {
var blockIndex = bitcoind.getBlockIndex(i);
should.exist(blockIndex);
should.exist(blockIndex.chainWork);
var work = new BN(blockIndex.chainWork, 'hex');
work.cmp(expectedWork).should.equal(0);
expectedWork = expectedWork.add(new BN(2));
should.exist(blockIndex.prevHash);
blockIndex.hash.should.equal(blockHashes[i - 1]);
blockIndex.prevHash.should.equal(blockHashes[i - 2]);
blockIndex.height.should.equal(i);
});
});
it('will get null with number greater than tip', function(done) {
var index = bitcoind.getBlockIndex(100000);
should.equal(index, null);
done();
});
});
describe('isMainChain', function() {
[1,2,3,4,5,6,7,8,9].forEach(function(i) {
it('block ' + i + ' is on the main chain', function() {
bitcoind.isMainChain(blockHashes[i]).should.equal(true);
});
});
});
describe('send transaction functionality', function() {
it('will not error and return the transaction hash', function() {
// create and sign the transaction
var tx = bitcore.Transaction();
tx.from(utxos[0]);
tx.change(privateKey.toAddress());
tx.to(destKey.toAddress(), utxos[0].amount * 1e8 - 1000);
tx.sign(bitcore.PrivateKey.fromWIF(utxos[0].privateKeyWIF));
// test sending the transaction
var hash = bitcoind.sendTransaction(tx.serialize());
hash.should.equal(tx.hash);
});
it('will throw an error if an unsigned transaction is sent', function() {
var tx = bitcore.Transaction();
tx.from(utxos[1]);
tx.change(privateKey.toAddress());
tx.to(destKey.toAddress(), utxos[1].amount * 1e8 - 1000);
(function() {
bitcoind.sendTransaction(tx.uncheckedSerialize());
}).should.throw('\x10: mandatory-script-verify-flag-failed (Operation not valid with the current stack size)');
});
it('will throw an error for unexpected types', function() {
var garbage = new Buffer('abcdef', 'hex');
(function() {
bitcoind.sendTransaction(garbage);
}).should.throw('TX decode failed');
var num = 23;
(function() {
bitcoind.sendTransaction(num);
}).should.throw('TX decode failed');
});
it('will emit "tx" events', function(done) {
var tx = bitcore.Transaction();
tx.from(utxos[2]);
tx.change(privateKey.toAddress());
tx.to(destKey.toAddress(), utxos[2].amount * 1e8 - 1000);
tx.sign(bitcore.PrivateKey.fromWIF(utxos[2].privateKeyWIF));
var serialized = tx.serialize();
bitcoind.once('tx', function(result) {
result.buffer.toString('hex').should.equal(serialized);
result.hash.should.equal(tx.hash);
result.mempool.should.equal(true);
done();
});
bitcoind.sendTransaction(serialized);
});
});
describe('fee estimation', function() {
it('will estimate fees', function() {
var fees = bitcoind.estimateFee();
fees.should.equal(-1);
});
});
describe('tip updates', function() {
it('will get an event when the tip is new', function(done) {
this.timeout(4000);
bitcoind.on('tip', function(height) {
if (height == 151) {
height.should.equal(151);
done();
}
});
client.generate(1, function(err, response) {
if (err) {
throw err;
}
});
});
});
describe('transactions leaving the mempool', function() {
it('receive event when transaction leaves', function(done) {
// add transaction to build a new block
var tx = bitcore.Transaction();
tx.from(utxos[4]);
tx.change(privateKey.toAddress());
tx.to(destKey.toAddress(), utxos[4].amount * 1e8 - 1000);
tx.sign(bitcore.PrivateKey.fromWIF(utxos[4].privateKeyWIF));
bitcoind.sendTransaction(tx.serialize());
bitcoind.once('txleave', function(txInfo) {
txInfo.hash.should.equal(tx.hash);
done();
});
client.generate(1, function(err, response) {
if (err) {
throw err;
}
});
});
});
describe('mempool functionality', function() {
var fromAddress = 'mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1';
var utxo1 = {
address: fromAddress,
txId: 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458',
outputIndex: 0,
script: bitcore.Script.buildPublicKeyHashOut(fromAddress).toString(),
satoshis: 100000
};
var toAddress = 'mrU9pEmAx26HcbKVrABvgL7AwA5fjNFoDc';
var changeAddress = 'mgBCJAsvzgT2qNNeXsoECg2uPKrUsZ76up';
var changeAddressP2SH = '2N7T3TAetJrSCruQ39aNrJvYLhG1LJosujf';
var privateKey1 = 'cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY';
var private1 = '6ce7e97e317d2af16c33db0b9270ec047a91bff3eff8558afb5014afb2bb5976';
var private2 = 'c9b26b0f771a0d2dad88a44de90f05f416b3b385ff1d989343005546a0032890';
var tx = new bitcore.Transaction();
tx.from(utxo1);
tx.to(toAddress, 50000);
tx.change(changeAddress);
tx.sign(privateKey1);
var tx2;
var tx2Key;
before(function() {
tx2 = bitcore.Transaction();
tx2.from(utxos[3]);
tx2.change(privateKey.toAddress());
tx2.to(destKey.toAddress(), utxos[3].amount * 1e8 - 1000);
tx2Key = bitcore.PrivateKey.fromWIF(utxos[3].privateKeyWIF);
tx2.sign(tx2Key);
});
it('will add an unchecked transaction', function() {
var added = bitcoind.addMempoolUncheckedTransaction(tx.serialize());
added.should.equal(true);
bitcoind.getTransaction(tx.hash, true, function(err, txBuffer) {
if(err) {
throw err;
}
var expected = tx.toBuffer().toString('hex');
txBuffer.toString('hex').should.equal(expected);
});
});
it('get one transaction', function() {
var transactions = bitcoind.getMempoolTransactions();
transactions[0].toString('hex').should.equal(tx.serialize());
});
it('get multiple transactions', function() {
bitcoind.sendTransaction(tx2.serialize());
var transactions = bitcoind.getMempoolTransactions();
var expected = [tx.serialize(), tx2.serialize()];
expected.should.contain(transactions[0].toString('hex'));
expected.should.contain(transactions[1].toString('hex'));
});
});
describe('get transaction with block info', function() {
it('should include tx buffer, height and timestamp', function(done) {
bitcoind.getTransactionWithBlockInfo(utxos[0].txid, true, function(err, data) {
should.not.exist(err);
should.exist(data.height);
data.height.should.be.a('number');
should.exist(data.timestamp);
should.exist(data.buffer);
done();
});
});
});
describe('get next block hash', function() {
it('will get next block hash', function() {
var nextBlockHash = bitcoind.getNextBlockHash(blockHashes[0]);
nextBlockHash.should.equal(blockHashes[1]);
var nextnextBlockHash = bitcoind.getNextBlockHash(nextBlockHash);
nextnextBlockHash.should.equal(blockHashes[2]);
});
it('will get a null response if the tip hash is provided', function() {
var bestBlockHash = bitcoind.getBestBlockHash();
var nextBlockHash = bitcoind.getNextBlockHash(bestBlockHash);
should.not.exist(nextBlockHash);
});
});
describe('#getInfo', function() {
it('will get information', function() {
var info = bitcoind.getInfo();
should.exist(info);
should.exist(info.version);
should.exist(info.blocks);
should.exist(info.timeoffset);
should.exist(info.connections);
should.exist(info.difficulty);
should.exist(info.testnet);
should.exist(info.relayfee);
should.exist(info.errors);
});
});
});

View File

@ -13,6 +13,7 @@ var util = require('util');
function Bus(params) {
events.EventEmitter.call(this);
this.node = params.node;
this.remoteAddress = params.remoteAddress;
}
util.inherits(Bus, events.EventEmitter);

View File

@ -9,7 +9,7 @@ function main(parentServicesPath, additionalServices) {
moduleName: 'bitcore-node',
configName: 'bitcore-node',
processTitle: 'bitcore'
}).on('require', function (name, module) {
}).on('require', function (name) {
console.log('Loading:', name);
}).on('requireFail', function (name, err) {
console.log('Unable to load:', name, err);

View File

@ -9,7 +9,7 @@ function main(parentServicesPath, additionalServices) {
moduleName: 'bitcore-node',
configName: 'bitcore-node',
processTitle: 'bitcored'
}).on('require', function (name, module) {
}).on('require', function (name) {
console.log('Loading:', name);
}).on('requireFail', function (name, err) {
console.log('Unable to load:', name, err);

View File

@ -3,6 +3,7 @@
var program = require('commander');
var path = require('path');
var bitcorenode = require('..');
var utils = require('../utils');
function main(servicesPath, additionalServices) {
/* jshint maxstatements: 100 */
@ -124,7 +125,8 @@ function main(servicesPath, additionalServices) {
program
.command('call <method> [params...]')
.description('Call an API method')
.action(function(method, params) {
.action(function(method, paramsArg) {
var params = utils.parseParamsWithJSON(paramsArg);
var configInfo = findConfig(process.cwd());
if (!configInfo) {
configInfo = defaultConfig();

View File

@ -3,23 +3,10 @@
var createError = require('errno').create;
var BitcoreNodeError = createError('BitcoreNodeError');
var NoOutputs = createError('NoOutputs', BitcoreNodeError);
var NoOutput = createError('NoOutput', BitcoreNodeError);
var Wallet = createError('WalletError', BitcoreNodeError);
Wallet.InsufficientFunds = createError('InsufficientFunds', Wallet);
var Consensus = createError('Consensus', BitcoreNodeError);
Consensus.BlockExists = createError('BlockExists', Consensus);
var Transaction = createError('Transaction', BitcoreNodeError);
Transaction.NotFound = createError('NotFound', Transaction);
var RPCError = createError('RPCError', BitcoreNodeError);
module.exports = {
Error: BitcoreNodeError,
NoOutputs: NoOutputs,
NoOutput: NoOutput,
Wallet: Wallet,
Consensus: Consensus,
Transaction: Transaction
RPCError: RPCError
};

View File

@ -1,14 +1,22 @@
'use strict';
var bitcore = require('bitcore-lib-zcash');
var _ = bitcore.deps._;
var colors = require('colors/safe');
/**
* Wraps console.log with some special magic
* @constructor
*/
function Logger() {
function Logger(options) {
if (!options) {
options = {};
}
this.formatting = _.isUndefined(options.formatting) ? Logger.DEFAULT_FORMATTING : options.formatting;
}
Logger.DEFAULT_FORMATTING = true;
/**
* Prints an info message
* #info
@ -45,17 +53,18 @@ Logger.prototype.warn = function() {
* Proxies console.log with color and arg parsing magic
* #_log
*/
Logger.prototype._log = function(color, type) {
if (process.env.NODE_ENV === 'test') {
return;
}
Logger.prototype._log = function(color) {
var args = Array.prototype.slice.call(arguments);
args = args.slice(1);
var date = new Date();
var typeString = colors[color].italic(args.shift() + ':');
args[0] = '[' + date.toISOString() + ']' + ' ' + typeString + ' ' + args[0];
console.log.apply(console, args);
var level = args.shift();
if (this.formatting) {
var date = new Date();
var typeString = colors[color].italic(level + ':');
args[0] = '[' + date.toISOString() + ']' + ' ' + typeString + ' ' + args[0];
}
var fn = console[level] || console.log;
fn.apply(console, args);
};
module.exports = Logger;

View File

@ -3,7 +3,7 @@
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var async = require('async');
var bitcore = require('bitcore-lib');
var bitcore = require('bitcore-lib-zcash');
var Networks = bitcore.Networks;
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
@ -27,8 +27,8 @@ var errors = require('./errors');
* ```
*
* @param {Object} config - The configuration of the node
* @param {Array} config.formatLogs - Option to disable formatting of logs
* @param {Array} config.services - The array of services
* @param {String} config.datadir - The directory for data (e.g. bitcoind datadir)
* @param {Number} config.port - The HTTP port for services
* @param {Boolean} config.https - Enable https
* @param {Object} config.httpsOptions - Options for https
@ -41,8 +41,14 @@ function Node(config) {
if(!(this instanceof Node)) {
return new Node(config);
}
this.configPath = config.path;
this.errors = errors;
this.log = log;
if (!_.isUndefined(config.formatLogs)) {
this.log.formatting = config.formatLogs ? true : false;
}
this.network = null;
this.services = {};
this._unloadedServices = [];
@ -52,8 +58,6 @@ function Node(config) {
$.checkArgument(Array.isArray(config.services));
this._unloadedServices = config.services;
}
$.checkState(config.datadir, 'Node config expects "datadir"');
this.datadir = config.datadir;
this.port = config.port;
this.https = config.https;
this.httpsOptions = config.httpsOptions;
@ -71,19 +75,7 @@ Node.prototype._setNetwork = function(config) {
if (config.network === 'testnet') {
this.network = Networks.get('testnet');
} else if (config.network === 'regtest') {
Networks.remove(Networks.testnet);
Networks.add({
name: 'regtest',
alias: 'regtest',
pubkeyhash: 0x6f,
privatekey: 0xef,
scripthash: 0xc4,
xpubkey: 0x043587cf,
xprivkey: 0x04358394,
networkMagic: 0xfabfb5da,
port: 18444,
dnsSeeds: [ ]
});
Networks.enableRegtest();
this.network = Networks.get('regtest');
} else {
this.network = Networks.defaultNetwork;
@ -95,8 +87,11 @@ Node.prototype._setNetwork = function(config) {
* Will instantiate a new Bus for this node.
* @returns {Bus}
*/
Node.prototype.openBus = function() {
return new Bus({node: this});
Node.prototype.openBus = function(options) {
if (!options) {
options = {};
}
return new Bus({node: this, remoteAddress: options.remoteAddress});
};
/**
@ -107,7 +102,9 @@ Node.prototype.getAllAPIMethods = function() {
var methods = [];
for(var i in this.services) {
var mod = this.services[i];
methods = methods.concat(mod.getAPIMethods());
if (mod.getAPIMethods) {
methods = methods.concat(mod.getAPIMethods());
}
}
return methods;
};
@ -120,7 +117,9 @@ Node.prototype.getAllPublishEvents = function() {
var events = [];
for (var i in this.services) {
var mod = this.services[i];
events = events.concat(mod.getPublishEvents());
if (mod.getPublishEvents) {
events = events.concat(mod.getPublishEvents());
}
}
return events;
};
@ -184,13 +183,18 @@ Node.prototype.getServiceOrder = function() {
Node.prototype._startService = function(serviceInfo, callback) {
var self = this;
$.checkState(_.isObject(serviceInfo.config));
$.checkState(!serviceInfo.config.node);
$.checkState(!serviceInfo.config.name);
log.info('Starting ' + serviceInfo.name);
var config = serviceInfo.config;
var config;
if (serviceInfo.config) {
$.checkState(_.isObject(serviceInfo.config));
$.checkState(!serviceInfo.config.node);
$.checkState(!serviceInfo.config.name);
config = serviceInfo.config;
} else {
config = {};
}
config.node = this;
config.name = serviceInfo.name;
var service = new serviceInfo.module(config);
@ -204,24 +208,26 @@ Node.prototype._startService = function(serviceInfo, callback) {
}
// add API methods
var methodData = service.getAPIMethods();
var methodNameConflicts = [];
methodData.forEach(function(data) {
var name = data[0];
var instance = data[1];
var method = data[2];
if (service.getAPIMethods) {
var methodData = service.getAPIMethods();
var methodNameConflicts = [];
methodData.forEach(function(data) {
var name = data[0];
var instance = data[1];
var method = data[2];
if (self[name]) {
methodNameConflicts.push(name);
} else {
self[name] = function() {
return method.apply(instance, arguments);
};
if (self[name]) {
methodNameConflicts.push(name);
} else {
self[name] = function() {
return method.apply(instance, arguments);
};
}
});
if (methodNameConflicts.length > 0) {
return callback(new Error('Existing API method(s) exists: ' + methodNameConflicts.join(', ')));
}
});
if (methodNameConflicts.length > 0) {
return callback(new Error('Existing API method(s) exists: ' + methodNameConflicts.join(', ')));
}
callback();
@ -230,6 +236,14 @@ Node.prototype._startService = function(serviceInfo, callback) {
};
Node.prototype._logTitle = function() {
if (this.configPath) {
log.info('Using config:', this.configPath);
log.info('Using network:', this.getNetworkName());
}
};
/**
* Will start all running services in the order based on the dependency chain.
* @param {Function} callback - Called when all services are started
@ -238,6 +252,8 @@ Node.prototype.start = function(callback) {
var self = this;
var servicesOrder = this.getServiceOrder();
self._logTitle();
async.eachSeries(
servicesOrder,
function(service, next) {
@ -253,6 +269,14 @@ Node.prototype.start = function(callback) {
);
};
Node.prototype.getNetworkName = function() {
var network = this.network.name;
if (this.network.regtestEnabled) {
network = 'regtest';
}
return network;
};
/**
* Will stop all running services in the reverse order that they
* were initially started.

View File

@ -4,7 +4,8 @@ var async = require('async');
var fs = require('fs');
var path = require('path');
var spawn = require('child_process').spawn;
var bitcore = require('bitcore-lib');
var bitcore = require('bitcore-lib-zcash');
var utils = require('../utils');
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
@ -14,7 +15,7 @@ var _ = bitcore.deps._;
* @param {Function} done
*/
function addConfig(configFilePath, service, done) {
$.checkState(path.isAbsolute(configFilePath), 'An absolute path is expected');
$.checkState(utils.isAbsolutePath(configFilePath), 'An absolute path is expected');
fs.readFile(configFilePath, function(err, data) {
if (err) {
return done(err);
@ -39,7 +40,7 @@ function addConfig(configFilePath, service, done) {
* @param {Function} done
*/
function addService(configDir, service, done) {
$.checkState(path.isAbsolute(configDir), 'An absolute path is expected');
$.checkState(utils.isAbsolutePath(configDir), 'An absolute path is expected');
var npm = spawn('npm', ['install', service, '--save'], {cwd: configDir});
npm.stdout.on('data', function(data) {
@ -69,7 +70,7 @@ function add(options, done) {
$.checkArgument(_.isObject(options));
$.checkArgument(_.isFunction(done));
$.checkArgument(
_.isString(options.path) && path.isAbsolute(options.path),
_.isString(options.path) && utils.isAbsolutePath(options.path),
'An absolute path is expected'
);
$.checkArgument(Array.isArray(options.services));

View File

@ -1,7 +1,7 @@
'use strict';
var spawn = require('child_process').spawn;
var bitcore = require('bitcore-lib');
var bitcore = require('bitcore-lib-zcash');
var async = require('async');
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
@ -11,21 +11,16 @@ var mkdirp = require('mkdirp');
var fs = require('fs');
var defaultBaseConfig = require('./default-base-config');
var version;
if (packageFile.version.match('-dev')) {
version = '^' + packageFile.lastBuild;
} else {
version = '^' + packageFile.version;
}
var version = '^' + packageFile.version;
var BASE_PACKAGE = {
description: 'A full Bitcoin node build with Bitcore',
description: 'A full Zcash node build with Bitcore',
repository: 'https://github.com/user/project',
license: 'MIT',
readme: 'README.md',
dependencies: {
'bitcore-lib': '^' + bitcore.version,
'bitcore-node': version
'bitcore-lib-zcash': 'zcash-hackworks/bitcore-lib-zcash',
'bitcore-node-zcash': 'zcash-hackworks/bitcore-node-zcash'
}
};

View File

@ -7,6 +7,7 @@ var path = require('path');
* or default locations.
* @param {Object} options
* @param {String} options.network - "testnet" or "livenet"
* @param {String} options.datadir - Absolute path to bitcoin database directory
*/
function getDefaultBaseConfig(options) {
if (!options) {
@ -15,10 +16,17 @@ function getDefaultBaseConfig(options) {
return {
path: process.cwd(),
config: {
datadir: options.datadir || path.resolve(process.env.HOME, '.bitcoin'),
network: options.network || 'livenet',
port: 3001,
services: ['bitcoind', 'db', 'address', 'web']
services: ['bitcoind', 'web'],
servicesConfig: {
bitcoind: {
spawn: {
datadir: options.datadir || path.resolve(process.env.HOME, '.zcash'),
exec: path.resolve(__dirname, '../../bin/zcashd')
}
}
}
}
};
}

View File

@ -24,17 +24,24 @@ function getDefaultConfig(options) {
mkdirp.sync(defaultPath);
}
var defaultServices = ['bitcoind', 'db', 'address', 'web'];
var defaultServices = ['bitcoind', 'web'];
if (options.additionalServices) {
defaultServices = defaultServices.concat(options.additionalServices);
}
if (!fs.existsSync(defaultConfigFile)) {
var defaultConfig = {
datadir: path.resolve(defaultPath, './data'),
network: 'livenet',
port: 3001,
services: defaultServices
services: defaultServices,
servicesConfig: {
bitcoind: {
spawn: {
datadir: path.resolve(defaultPath, './data'),
exec: path.resolve(__dirname, '../../bin/zcashd')
}
}
}
};
fs.writeFileSync(defaultConfigFile, JSON.stringify(defaultConfig, null, 2));
}

View File

@ -1,10 +1,11 @@
'use strict';
var bitcore = require('bitcore-lib');
var bitcore = require('bitcore-lib-zcash');
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
var path = require('path');
var fs = require('fs');
var utils = require('../utils');
/**
* Will return the path and bitcore-node configuration
@ -12,7 +13,7 @@ var fs = require('fs');
*/
function findConfig(cwd) {
$.checkArgument(_.isString(cwd), 'Argument should be a string');
$.checkArgument(path.isAbsolute(cwd), 'Argument should be an absolute path');
$.checkArgument(utils.isAbsolutePath(cwd), 'Argument should be an absolute path');
var directory = String(cwd);
while (!fs.existsSync(path.resolve(directory, 'bitcore-node.json'))) {
directory = path.resolve(directory, '../');

View File

@ -5,9 +5,10 @@ var fs = require('fs');
var npm = require('npm');
var path = require('path');
var spawn = require('child_process').spawn;
var bitcore = require('bitcore-lib');
var bitcore = require('bitcore-lib-zcash');
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
var utils = require('../utils');
/**
* Will remove a service from bitcore-node.json
@ -16,7 +17,7 @@ var _ = bitcore.deps._;
* @param {Function} done
*/
function removeConfig(configFilePath, service, done) {
$.checkArgument(path.isAbsolute(configFilePath), 'An absolute path is expected');
$.checkArgument(utils.isAbsolutePath(configFilePath), 'An absolute path is expected');
fs.readFile(configFilePath, function(err, data) {
if (err) {
return done(err);
@ -47,7 +48,7 @@ function removeConfig(configFilePath, service, done) {
* @param {Function} done
*/
function uninstallService(configDir, service, done) {
$.checkArgument(path.isAbsolute(configDir), 'An absolute path is expected');
$.checkArgument(utils.isAbsolutePath(configDir), 'An absolute path is expected');
$.checkArgument(_.isString(service), 'A string is expected for the service argument');
var child = spawn('npm', ['uninstall', service, '--save'], {cwd: configDir});
@ -76,7 +77,7 @@ function uninstallService(configDir, service, done) {
* @param {Function} done
*/
function removeService(configDir, service, done) {
$.checkArgument(path.isAbsolute(configDir), 'An absolute path is expected');
$.checkArgument(utils.isAbsolutePath(configDir), 'An absolute path is expected');
$.checkArgument(_.isString(service), 'A string is expected for the service argument');
// check if the service is installed
@ -109,7 +110,7 @@ function remove(options, done) {
$.checkArgument(_.isObject(options));
$.checkArgument(_.isFunction(done));
$.checkArgument(
_.isString(options.path) && path.isAbsolute(options.path),
_.isString(options.path) && utils.isAbsolutePath(options.path),
'An absolute path is expected'
);
$.checkArgument(Array.isArray(options.services));

View File

@ -3,16 +3,152 @@
var path = require('path');
var BitcoreNode = require('../node');
var index = require('../');
var bitcore = require('bitcore-lib');
var bitcore = require('bitcore-lib-zcash');
var _ = bitcore.deps._;
var $ = bitcore.util.preconditions;
var log = index.log;
var child_process = require('child_process');
var fs = require('fs');
var shuttingDown = false;
log.debug = function() {};
/**
* Checks for configuration options from version 2. This includes an "address" and
* "db" service, or having "datadir" at the root of the config.
*/
function checkConfigVersion2(fullConfig) {
var datadirUndefined = _.isUndefined(fullConfig.datadir);
var addressDefined = (fullConfig.services.indexOf('address') >= 0);
var dbDefined = (fullConfig.services.indexOf('db') >= 0);
if (!datadirUndefined || addressDefined || dbDefined) {
console.warn('\nConfiguration file is not compatible with this version. \n' +
'A reindex for zcashd is necessary for this upgrade with the "reindex=1" zcash.conf option. \n' +
'There are changes necessary in both zcash.conf and bitcore-node.json. \n\n' +
'To upgrade please see the details below and documentation at: \n' +
'https://github.com/bitpay/bitcore-node/blob/bitcoind/docs/upgrade.md \n');
if (!datadirUndefined) {
console.warn('Please remove "datadir" and add it to the config at ' + fullConfig.path + ' with:');
var missingConfig = {
servicesConfig: {
bitcoind: {
spawn: {
datadir: fullConfig.datadir,
exec: path.resolve(__dirname, '../../bin/zcashd')
}
}
}
};
console.warn(JSON.stringify(missingConfig, null, 2) + '\n');
}
if (addressDefined || dbDefined) {
console.warn('Please remove "address" and/or "db" from "services" in: ' + fullConfig.path + '\n');
}
return true;
}
return false;
}
/**
* This function will instantiate and start a Node, requiring the necessary service
* modules, and registering event handlers.
* @param {Object} options
* @param {Object} options.servicesPath - The path to the location of service modules
* @param {String} options.path - The absolute path of the configuration file
* @param {Object} options.config - The parsed bitcore-node.json configuration file
* @param {Array} options.config.services - An array of services names.
* @param {Object} options.config.servicesConfig - Parameters to pass to each service
* @param {String} options.config.network - 'livenet', 'testnet' or 'regtest
* @param {Number} options.config.port - The port to use for the web service
*/
function start(options) {
/* jshint maxstatements: 20 */
var fullConfig = _.clone(options.config);
var servicesPath;
if (options.servicesPath) {
servicesPath = options.servicesPath; // services are in a different directory than the config
} else {
servicesPath = options.path; // defaults to the same directory
}
fullConfig.path = path.resolve(options.path, './bitcore-node.json');
if (checkConfigVersion2(fullConfig)) {
process.exit(1);
}
fullConfig.services = start.setupServices(require, servicesPath, options.config);
var node = new BitcoreNode(fullConfig);
// setup handlers for uncaught exceptions and ctrl+c
start.registerExitHandlers(process, node);
node.on('ready', function() {
log.info('Bitcore Node ready');
});
node.on('error', function(err) {
log.error(err);
});
node.start(function(err) {
if(err) {
log.error('Failed to start services');
if (err.stack) {
log.error(err.stack);
}
start.cleanShutdown(process, node);
}
});
return node;
}
/**
* Checks a service for the expected methods
* @param {Object} service
*/
function checkService(service) {
// check that the service supports expected methods
if (!service.module.prototype ||
!service.module.dependencies ||
!service.module.prototype.start ||
!service.module.prototype.stop) {
throw new Error(
'Could not load service "' + service.name + '" as it does not support necessary methods and properties.'
);
}
}
/**
* Will require a module from local services directory first
* and then from available node_modules
* @param {Function} req
* @param {Object} service
*/
function loadModule(req, service) {
try {
// first try in the built-in bitcore-node services directory
service.module = req(path.resolve(__dirname, '../services/' + service.name));
} catch(e) {
// check if the package.json specifies a specific file to use
var servicePackage = req(service.name + '/package.json');
var serviceModule = service.name;
if (servicePackage.bitcoreNode) {
serviceModule = service.name + '/' + servicePackage.bitcoreNode;
}
service.module = req(serviceModule);
}
}
/**
* This function will loop over the configuration for services and require the
* specified modules, and assemble an array in this format:
@ -42,29 +178,8 @@ function setupServices(req, servicesPath, config) {
var hasConfig = config.servicesConfig && config.servicesConfig[service.name];
service.config = hasConfig ? config.servicesConfig[service.name] : {};
try {
// first try in the built-in bitcore-node services directory
service.module = req(path.resolve(__dirname, '../services/' + service.name));
} catch(e) {
// check if the package.json specifies a specific file to use
var servicePackage = req(service.name + '/package.json');
var serviceModule = service.name;
if (servicePackage.bitcoreNode) {
serviceModule = service.name + '/' + servicePackage.bitcoreNode;
}
service.module = req(serviceModule);
}
// check that the service supports expected methods
if (!service.module.prototype ||
!service.module.dependencies ||
!service.module.prototype.start ||
!service.module.prototype.stop) {
throw new Error(
'Could not load service "' + service.name + '" as it does not support necessary methods.'
);
}
loadModule(req, service);
checkService(service);
services.push(service);
}
@ -72,50 +187,6 @@ function setupServices(req, servicesPath, config) {
return services;
}
/**
* Will register event handlers to log the current db sync status.
* @param {Node} node
*/
function registerSyncHandlers(node, delay) {
delay = delay || 10000;
var interval = false;
var count = 0;
function logSyncStatus() {
log.info(
'Database Sync Status: Tip:', node.services.db.tip.hash,
'Height:', node.services.db.tip.__height,
'Rate:', count/10, 'blocks per second'
);
}
node.on('ready', function() {
if (node.services.db) {
node.on('synced', function() {
clearInterval(interval);
logSyncStatus();
});
node.services.db.on('addblock', function(block) {
count++;
// Initialize logging if not already instantiated
if (!interval) {
interval = setInterval(function() {
logSyncStatus();
count = 0;
}, delay);
}
});
}
});
node.on('stopping', function() {
clearInterval(interval);
});
}
/**
* Will shutdown a node and then the process
* @param {Object} _process - The Node.js process object
@ -132,20 +203,6 @@ function cleanShutdown(_process, node) {
});
}
/**
* Will register event handlers to stop the node for `process` events
* `uncaughtException` and `SIGINT`.
* @param {Object} _process - The Node.js process
* @param {Node} node
*/
function registerExitHandlers(_process, node) {
//catches uncaught exceptions
_process.on('uncaughtException', exitHandler.bind(null, {exit:true}, _process, node));
//catches ctrl+c event
_process.on('SIGINT', exitHandler.bind(null, {sigint:true}, _process, node));
}
/**
* Will handle all the shutdown tasks that need to take place to ensure a safe exit
* @param {Object} options
@ -177,106 +234,22 @@ function exitHandler(options, _process, node, err) {
}
/**
* This function will instantiate and start a Node, requiring the necessary service
* modules, and registering event handlers.
* @param {Object} options
* @param {Object} options.servicesPath - The path to the location of service modules
* @param {String} options.path - The absolute path of the configuration file
* @param {Object} options.config - The parsed bitcore-node.json configuration file
* @param {Array} options.config.services - An array of services names.
* @param {Object} options.config.servicesConfig - Parameters to pass to each service
* @param {String} options.config.datadir - A relative (to options.path) or absolute path to the datadir
* @param {String} options.config.network - 'livenet', 'testnet' or 'regtest
* @param {Number} options.config.port - The port to use for the web service
* Will register event handlers to stop the node for `process` events
* `uncaughtException` and `SIGINT`.
* @param {Object} _process - The Node.js process
* @param {Node} node
*/
function start(options) {
function registerExitHandlers(_process, node) {
//catches uncaught exceptions
_process.on('uncaughtException', exitHandler.bind(null, {exit:true}, _process, node));
var fullConfig = _.clone(options.config);
var servicesPath;
if (options.servicesPath) {
servicesPath = options.servicesPath; // services are in a different directory than the config
} else {
servicesPath = options.path; // defaults to the same directory
}
fullConfig.services = start.setupServices(require, servicesPath, options.config);
fullConfig.datadir = path.resolve(options.path, options.config.datadir);
if (fullConfig.daemon) {
start.spawnChildProcess(fullConfig.datadir, process);
}
var node = new BitcoreNode(fullConfig);
// set up the event handlers for logging sync information
start.registerSyncHandlers(node);
// setup handlers for uncaught exceptions and ctrl+c
start.registerExitHandlers(process, node);
node.on('ready', function() {
log.info('Bitcore Node ready');
});
node.on('error', function(err) {
log.error(err);
});
node.start(function(err) {
if(err) {
log.error('Failed to start services');
if (err.stack) {
log.error(err.stack);
}
start.cleanShutdown(process, node);
}
});
return node;
}
/**
* This function will fork the passed in process and exit the parent process
* in order to daemonize the process. If there is already a daemon for this pid (process),
* then the function just returns. Stdout and stderr both append to one file, 'bitcore-node.log'
* located in the datadir.
* @param {String} datadir - The data directory where the bitcoin blockchain and config live.
* @param {Object} _process - The process that needs to fork a child and then, itself, exit.
*/
function spawnChildProcess(datadir, _process) {
if (_process.env.__bitcore_node) {
return _process.pid;
}
var args = [].concat(_process.argv);
args.shift();
var script = args.shift();
var env = _process.env;
var cwd = _process.cwd();
env.__bitcore_node = true;
var stderr = fs.openSync(datadir + '/bitcore-node.log', 'a+');
var stdout = stderr;
var cp_opt = {
stdio: ['ignore', stdout, stderr],
env: env,
cwd: cwd,
detached: true
};
var child = child_process.spawn(_process.execPath, [script].concat(args), cp_opt);
child.unref();
return _process.exit();
//catches ctrl+c event
_process.on('SIGINT', exitHandler.bind(null, {sigint:true}, _process, node));
}
module.exports = start;
module.exports.registerExitHandlers = registerExitHandlers;
module.exports.exitHandler = exitHandler;
module.exports.registerSyncHandlers = registerSyncHandlers;
module.exports.setupServices = setupServices;
module.exports.spawnChildProcess = spawnChildProcess;
module.exports.cleanShutdown = cleanShutdown;
module.exports.checkConfigVersion2 = checkConfigVersion2;

View File

@ -78,7 +78,7 @@ Service.prototype.stop = function(done) {
* Setup express routes
* @param {Express} app
*/
Service.prototype.setupRoutes = function(app) {
Service.prototype.setupRoutes = function() {
// Setup express routes here
};
@ -86,6 +86,4 @@ Service.prototype.getRoutePrefix = function() {
return this.name;
};
module.exports = Service;

View File

@ -1,54 +0,0 @@
'use strict';
var exports = {};
exports.PREFIXES = {
OUTPUTS: new Buffer('02', 'hex'), // Query outputs by address and/or height
SPENTS: new Buffer('03', 'hex'), // Query inputs by address and/or height
SPENTSMAP: new Buffer('05', 'hex') // Get the input that spends an output
};
exports.MEMPREFIXES = {
OUTPUTS: new Buffer('01', 'hex'), // Query mempool outputs by address
SPENTS: new Buffer('02', 'hex'), // Query mempool inputs by address
SPENTSMAP: new Buffer('03', 'hex') // Query mempool for the input that spends an output
};
// To save space, we're only storing the PubKeyHash or ScriptHash in our index.
// To avoid intentional unspendable collisions, which have been seen on the blockchain,
// we must store the hash type (PK or Script) as well.
exports.HASH_TYPES = {
PUBKEY: new Buffer('01', 'hex'),
REDEEMSCRIPT: new Buffer('02', 'hex')
};
// Translates from our enum type back into the hash types returned by
// bitcore-lib/address.
exports.HASH_TYPES_READABLE = {
'01': 'pubkeyhash',
'02': 'scripthash'
};
exports.HASH_TYPES_MAP = {
'pubkeyhash': exports.HASH_TYPES.PUBKEY,
'scripthash': exports.HASH_TYPES.REDEEMSCRIPT
};
exports.SPACER_MIN = new Buffer('00', 'hex');
exports.SPACER_MAX = new Buffer('ff', 'hex');
exports.SPACER_HEIGHT_MIN = new Buffer('0000000000', 'hex');
exports.SPACER_HEIGHT_MAX = new Buffer('ffffffffff', 'hex');
exports.TIMESTAMP_MIN = new Buffer('0000000000000000', 'hex');
exports.TIMESTAMP_MAX = new Buffer('ffffffffffffffff', 'hex');
// The maximum number of inputs that can be queried at once
exports.MAX_INPUTS_QUERY_LENGTH = 50000;
// The maximum number of outputs that can be queried at once
exports.MAX_OUTPUTS_QUERY_LENGTH = 50000;
// The maximum number of transactions that can be queried at once
exports.MAX_HISTORY_QUERY_LENGTH = 100;
// The maximum number of addresses that can be queried at once
exports.MAX_ADDRESSES_QUERY = 10000;
module.exports = exports;

View File

@ -1,298 +0,0 @@
'use strict';
var bitcore = require('bitcore-lib');
var BufferReader = bitcore.encoding.BufferReader;
var Address = bitcore.Address;
var PublicKey = bitcore.PublicKey;
var constants = require('./constants');
var $ = bitcore.util.preconditions;
var exports = {};
exports.encodeSpentIndexSyncKey = function(txidBuffer, outputIndex) {
var outputIndexBuffer = new Buffer(4);
outputIndexBuffer.writeUInt32BE(outputIndex);
var key = Buffer.concat([
txidBuffer,
outputIndexBuffer
]);
return key.toString('binary');
};
exports.encodeOutputKey = function(hashBuffer, hashTypeBuffer, height, txidBuffer, outputIndex) {
var heightBuffer = new Buffer(4);
heightBuffer.writeUInt32BE(height);
var outputIndexBuffer = new Buffer(4);
outputIndexBuffer.writeUInt32BE(outputIndex);
var key = Buffer.concat([
constants.PREFIXES.OUTPUTS,
hashBuffer,
hashTypeBuffer,
constants.SPACER_MIN,
heightBuffer,
txidBuffer,
outputIndexBuffer
]);
return key;
};
exports.decodeOutputKey = function(buffer) {
var reader = new BufferReader(buffer);
var prefix = reader.read(1);
var hashBuffer = reader.read(20);
var hashTypeBuffer = reader.read(1);
var spacer = reader.read(1);
var height = reader.readUInt32BE();
var txid = reader.read(32);
var outputIndex = reader.readUInt32BE();
return {
prefix: prefix,
hashBuffer: hashBuffer,
hashTypeBuffer: hashTypeBuffer,
height: height,
txid: txid,
outputIndex: outputIndex
};
};
exports.encodeOutputValue = function(satoshis, scriptBuffer) {
var satoshisBuffer = new Buffer(8);
satoshisBuffer.writeDoubleBE(satoshis);
return Buffer.concat([satoshisBuffer, scriptBuffer]);
};
exports.encodeOutputMempoolValue = function(satoshis, timestampBuffer, scriptBuffer) {
var satoshisBuffer = new Buffer(8);
satoshisBuffer.writeDoubleBE(satoshis);
return Buffer.concat([satoshisBuffer, timestampBuffer, scriptBuffer]);
};
exports.decodeOutputValue = function(buffer) {
var satoshis = buffer.readDoubleBE(0);
var scriptBuffer = buffer.slice(8, buffer.length);
return {
satoshis: satoshis,
scriptBuffer: scriptBuffer
};
};
exports.decodeOutputMempoolValue = function(buffer) {
var satoshis = buffer.readDoubleBE(0);
var timestamp = buffer.readDoubleBE(8);
var scriptBuffer = buffer.slice(16, buffer.length);
return {
satoshis: satoshis,
timestamp: timestamp,
scriptBuffer: scriptBuffer
};
};
exports.encodeInputKey = function(hashBuffer, hashTypeBuffer, height, prevTxIdBuffer, outputIndex) {
var heightBuffer = new Buffer(4);
heightBuffer.writeUInt32BE(height);
var outputIndexBuffer = new Buffer(4);
outputIndexBuffer.writeUInt32BE(outputIndex);
return Buffer.concat([
constants.PREFIXES.SPENTS,
hashBuffer,
hashTypeBuffer,
constants.SPACER_MIN,
heightBuffer,
prevTxIdBuffer,
outputIndexBuffer
]);
};
exports.decodeInputKey = function(buffer) {
var reader = new BufferReader(buffer);
var prefix = reader.read(1);
var hashBuffer = reader.read(20);
var hashTypeBuffer = reader.read(1);
var spacer = reader.read(1);
var height = reader.readUInt32BE();
var prevTxId = reader.read(32);
var outputIndex = reader.readUInt32BE();
return {
prefix: prefix,
hashBuffer: hashBuffer,
hashTypeBuffer: hashTypeBuffer,
height: height,
prevTxId: prevTxId,
outputIndex: outputIndex
};
};
exports.encodeInputValue = function(txidBuffer, inputIndex) {
var inputIndexBuffer = new Buffer(4);
inputIndexBuffer.writeUInt32BE(inputIndex);
return Buffer.concat([
txidBuffer,
inputIndexBuffer
]);
};
exports.decodeInputValue = function(buffer) {
var txid = buffer.slice(0, 32);
var inputIndex = buffer.readUInt32BE(32);
return {
txid: txid,
inputIndex: inputIndex
};
};
exports.encodeInputKeyMap = function(outputTxIdBuffer, outputIndex) {
var outputIndexBuffer = new Buffer(4);
outputIndexBuffer.writeUInt32BE(outputIndex);
return Buffer.concat([
constants.PREFIXES.SPENTSMAP,
outputTxIdBuffer,
outputIndexBuffer
]);
};
exports.decodeInputKeyMap = function(buffer) {
var txid = buffer.slice(1, 33);
var outputIndex = buffer.readUInt32BE(33);
return {
outputTxId: txid,
outputIndex: outputIndex
};
};
exports.encodeInputValueMap = function(inputTxIdBuffer, inputIndex) {
var inputIndexBuffer = new Buffer(4);
inputIndexBuffer.writeUInt32BE(inputIndex);
return Buffer.concat([
inputTxIdBuffer,
inputIndexBuffer
]);
};
exports.decodeInputValueMap = function(buffer) {
var txid = buffer.slice(0, 32);
var inputIndex = buffer.readUInt32BE(32);
return {
inputTxId: txid,
inputIndex: inputIndex
};
};
exports.encodeSummaryCacheKey = function(address) {
return Buffer.concat([address.hashBuffer, constants.HASH_TYPES_MAP[address.type]]);
};
exports.decodeSummaryCacheKey = function(buffer, network) {
var hashBuffer = buffer.read(20);
var type = constants.HASH_TYPES_READABLE[buffer.read(20, 2).toString('hex')];
var address = new Address({
hashBuffer: hashBuffer,
type: type,
network: network
});
return address;
};
exports.encodeSummaryCacheValue = function(cache, tipHeight, tipHash) {
var tipHashBuffer = new Buffer(tipHash, 'hex');
var buffer = new Buffer(new Array(20));
buffer.writeUInt32BE(tipHeight);
buffer.writeDoubleBE(cache.result.totalReceived, 4);
buffer.writeDoubleBE(cache.result.balance, 12);
var txidBuffers = [];
for (var i = 0; i < cache.result.txids.length; i++) {
var buf = new Buffer(new Array(36));
var txid = cache.result.txids[i];
buf.write(txid, 'hex');
buf.writeUInt32BE(cache.result.appearanceIds[txid], 32);
txidBuffers.push(buf);
}
var txidsBuffer = Buffer.concat(txidBuffers);
var value = Buffer.concat([tipHashBuffer, buffer, txidsBuffer]);
return value;
};
exports.decodeSummaryCacheValue = function(buffer) {
var hash = buffer.slice(0, 32).toString('hex');
var height = buffer.readUInt32BE(32);
var totalReceived = buffer.readDoubleBE(36);
var balance = buffer.readDoubleBE(44);
// read 32 byte chunks until exhausted
var appearanceIds = {};
var txids = [];
var pos = 52;
while(pos < buffer.length) {
var txid = buffer.slice(pos, pos + 32).toString('hex');
var txidHeight = buffer.readUInt32BE(pos + 32);
txids.push(txid);
appearanceIds[txid] = txidHeight;
pos += 36;
}
var cache = {
height: height,
hash: hash,
result: {
appearanceIds: appearanceIds,
txids: txids,
totalReceived: totalReceived,
balance: balance,
unconfirmedAppearanceIds: {}, // unconfirmed values are never stored in cache
unconfirmedBalance: 0
}
};
return cache;
};
exports.getAddressInfo = function(addressStr) {
var addrObj = bitcore.Address(addressStr);
var hashTypeBuffer = constants.HASH_TYPES_MAP[addrObj.type];
return {
hashBuffer: addrObj.hashBuffer,
hashTypeBuffer: hashTypeBuffer,
hashTypeReadable: addrObj.type
};
};
/**
* This function is optimized to return address information about an output script
* without constructing a Bitcore Address instance.
* @param {Script} - An instance of a Bitcore Script
* @param {Network|String} - The network for the address
*/
exports.extractAddressInfoFromScript = function(script, network) {
$.checkArgument(network, 'Second argument is expected to be a network');
var hashBuffer;
var addressType;
var hashTypeBuffer;
if (script.isPublicKeyHashOut()) {
hashBuffer = script.chunks[2].buf;
hashTypeBuffer = constants.HASH_TYPES.PUBKEY;
addressType = Address.PayToPublicKeyHash;
} else if (script.isScriptHashOut()) {
hashBuffer = script.chunks[1].buf;
hashTypeBuffer = constants.HASH_TYPES.REDEEMSCRIPT;
addressType = Address.PayToScriptHash;
} else if (script.isPublicKeyOut()) {
var pubkey = script.chunks[0].buf;
var address = Address.fromPublicKey(new PublicKey(pubkey), network);
hashBuffer = address.hashBuffer;
hashTypeBuffer = constants.HASH_TYPES.PUBKEY;
// pay-to-publickey doesn't have an address, however for compatibility
// purposes, we can create an address
addressType = Address.PayToPublicKeyHash;
} else {
return false;
}
return {
hashBuffer: hashBuffer,
hashTypeBuffer: hashTypeBuffer,
addressType: addressType
};
};
module.exports = exports;

View File

@ -1,260 +0,0 @@
'use strict';
var bitcore = require('bitcore-lib');
var async = require('async');
var _ = bitcore.deps._;
var constants = require('./constants');
/**
* This represents an instance that keeps track of data over a series of
* asynchronous I/O calls to get the transaction history for a group of
* addresses. History can be queried by start and end block heights to limit large sets
* of results (uses leveldb key streaming).
*/
function AddressHistory(args) {
this.node = args.node;
this.options = args.options;
if(Array.isArray(args.addresses)) {
this.addresses = args.addresses;
} else {
this.addresses = [args.addresses];
}
this.maxHistoryQueryLength = args.options.maxHistoryQueryLength || constants.MAX_HISTORY_QUERY_LENGTH;
this.maxAddressesQuery = args.options.maxAddressesQuery || constants.MAX_ADDRESSES_QUERY;
this.addressStrings = [];
for (var i = 0; i < this.addresses.length; i++) {
var address = this.addresses[i];
if (address instanceof bitcore.Address) {
this.addressStrings.push(address.toString());
} else if (_.isString(address)) {
this.addressStrings.push(address);
} else {
throw new TypeError('Addresses are expected to be strings');
}
}
this.detailedArray = [];
}
AddressHistory.prototype._mergeAndSortTxids = function(summaries) {
var appearanceIds = {};
var unconfirmedAppearanceIds = {};
for (var i = 0; i < summaries.length; i++) {
var summary = summaries[i];
for (var key in summary.appearanceIds) {
appearanceIds[key] = summary.appearanceIds[key];
delete summary.appearanceIds[key];
}
for (var unconfirmedKey in summary.unconfirmedAppearanceIds) {
unconfirmedAppearanceIds[unconfirmedKey] = summary.unconfirmedAppearanceIds[unconfirmedKey];
delete summary.unconfirmedAppearanceIds[key];
}
}
var confirmedTxids = Object.keys(appearanceIds);
confirmedTxids.sort(function(a, b) {
// Confirmed are sorted by height
return appearanceIds[a] - appearanceIds[b];
});
var unconfirmedTxids = Object.keys(unconfirmedAppearanceIds);
unconfirmedTxids.sort(function(a, b) {
// Unconfirmed are sorted by timestamp
return unconfirmedAppearanceIds[a] - unconfirmedAppearanceIds[b];
});
return confirmedTxids.concat(unconfirmedTxids);
};
/**
* This function will give detailed history for the configured
* addresses. See AddressService.prototype.getAddressHistory
* for complete documentation about options and response format.
*/
AddressHistory.prototype.get = function(callback) {
var self = this;
if (this.addresses.length > this.maxAddressesQuery) {
return callback(new TypeError('Maximum number of addresses (' + this.maxAddressesQuery + ') exceeded'));
}
if (this.addresses.length === 1) {
var address = this.addresses[0];
self.node.services.address.getAddressSummary(address, this.options, function(err, summary) {
if (err) {
return callback(err);
}
return self._paginateWithDetails.call(self, summary.txids, callback);
});
} else {
var opts = _.clone(this.options);
opts.fullTxList = true;
async.map(
self.addresses,
function(address, next) {
self.node.services.address.getAddressSummary(address, opts, next);
},
function(err, summaries) {
if (err) {
return callback(err);
}
var txids = self._mergeAndSortTxids(summaries);
return self._paginateWithDetails.call(self, txids, callback);
}
);
}
};
AddressHistory.prototype._paginateWithDetails = function(allTxids, callback) {
var self = this;
var totalCount = allTxids.length;
// Slice the page starting with the most recent
var txids;
if (self.options.from >= 0 && self.options.to >= 0) {
var fromOffset = totalCount - self.options.from;
var toOffset = totalCount - self.options.to;
txids = allTxids.slice(toOffset, fromOffset);
} else {
txids = allTxids;
}
// Verify that this query isn't too long
if (txids.length > self.maxHistoryQueryLength) {
return callback(new Error(
'Maximum length query (' + self.maxHistoryQueryLength + ') exceeded for address(es): ' +
self.addresses.join(',')
));
}
// Reverse to include most recent at the top
txids.reverse();
async.eachSeries(
txids,
function(txid, next) {
self.getDetailedInfo(txid, next);
},
function(err) {
if (err) {
return callback(err);
}
callback(null, {
totalCount: totalCount,
items: self.detailedArray
});
}
);
};
/**
* This function will transform items from the combinedArray into
* the detailedArray with the full transaction, satoshis and confirmation.
* @param {Object} txInfo - An item from the `combinedArray`
* @param {Function} next
*/
AddressHistory.prototype.getDetailedInfo = function(txid, next) {
var self = this;
var queryMempool = _.isUndefined(self.options.queryMempool) ? true : self.options.queryMempool;
self.node.services.db.getTransactionWithBlockInfo(
txid,
queryMempool,
function(err, transaction) {
if (err) {
return next(err);
}
transaction.populateInputs(self.node.services.db, [], function(err) {
if (err) {
return next(err);
}
var addressDetails = self.getAddressDetailsForTransaction(transaction);
self.detailedArray.push({
addresses: addressDetails.addresses,
satoshis: addressDetails.satoshis,
height: transaction.__height,
confirmations: self.getConfirmationsDetail(transaction),
timestamp: transaction.__timestamp,
// TODO bitcore-lib should return null instead of throwing error on coinbase
fees: !transaction.isCoinbase() ? transaction.getFee() : null,
tx: transaction
});
next();
});
}
);
};
/**
* A helper function for `getDetailedInfo` for getting the confirmations.
* @param {Transaction} transaction - A transaction with a populated __height value.
*/
AddressHistory.prototype.getConfirmationsDetail = function(transaction) {
var confirmations = 0;
if (transaction.__height >= 0) {
confirmations = this.node.services.db.tip.__height - transaction.__height + 1;
}
return confirmations;
};
AddressHistory.prototype.getAddressDetailsForTransaction = function(transaction) {
var result = {
addresses: {},
satoshis: 0
};
for (var inputIndex = 0; inputIndex < transaction.inputs.length; inputIndex++) {
var input = transaction.inputs[inputIndex];
if (!input.script) {
continue;
}
var inputAddress = input.script.toAddress(this.node.network);
if (inputAddress) {
var inputAddressString = inputAddress.toString();
if (this.addressStrings.indexOf(inputAddressString) >= 0) {
if (!result.addresses[inputAddressString]) {
result.addresses[inputAddressString] = {
inputIndexes: [inputIndex],
outputIndexes: []
};
} else {
result.addresses[inputAddressString].inputIndexes.push(inputIndex);
}
result.satoshis -= input.output.satoshis;
}
}
}
for (var outputIndex = 0; outputIndex < transaction.outputs.length; outputIndex++) {
var output = transaction.outputs[outputIndex];
if (!output.script) {
continue;
}
var outputAddress = output.script.toAddress(this.node.network);
if (outputAddress) {
var outputAddressString = outputAddress.toString();
if (this.addressStrings.indexOf(outputAddressString) >= 0) {
if (!result.addresses[outputAddressString]) {
result.addresses[outputAddressString] = {
inputIndexes: [],
outputIndexes: [outputIndex]
};
} else {
result.addresses[outputAddressString].outputIndexes.push(outputIndex);
}
result.satoshis += output.satoshis;
}
}
}
return result;
};
module.exports = AddressHistory;

File diff suppressed because it is too large Load Diff

View File

@ -1,40 +0,0 @@
'use strict';
var Transform = require('stream').Transform;
var inherits = require('util').inherits;
var bitcore = require('bitcore-lib');
var encodingUtil = require('../encoding');
var $ = bitcore.util.preconditions;
function InputsTransformStream(options) {
$.checkArgument(options.address instanceof bitcore.Address);
Transform.call(this, {
objectMode: true
});
this._address = options.address;
this._addressStr = this._address.toString();
this._tipHeight = options.tipHeight;
}
inherits(InputsTransformStream, Transform);
InputsTransformStream.prototype._transform = function(chunk, encoding, callback) {
var self = this;
var key = encodingUtil.decodeInputKey(chunk.key);
var value = encodingUtil.decodeInputValue(chunk.value);
var input = {
address: this._addressStr,
hashType: this._address.type,
txid: value.txid.toString('hex'),
inputIndex: value.inputIndex,
height: key.height,
confirmations: this._tipHeight - key.height + 1
};
self.push(input);
callback();
};
module.exports = InputsTransformStream;

View File

@ -1,42 +0,0 @@
'use strict';
var Transform = require('stream').Transform;
var inherits = require('util').inherits;
var bitcore = require('bitcore-lib');
var encodingUtil = require('../encoding');
var $ = bitcore.util.preconditions;
function OutputsTransformStream(options) {
Transform.call(this, {
objectMode: true
});
$.checkArgument(options.address instanceof bitcore.Address);
this._address = options.address;
this._addressStr = this._address.toString();
this._tipHeight = options.tipHeight;
}
inherits(OutputsTransformStream, Transform);
OutputsTransformStream.prototype._transform = function(chunk, encoding, callback) {
var self = this;
var key = encodingUtil.decodeOutputKey(chunk.key);
var value = encodingUtil.decodeOutputValue(chunk.value);
var output = {
address: this._addressStr,
hashType: this._address.type,
txid: key.txid.toString('hex'), //TODO use a buffer
outputIndex: key.outputIndex,
height: key.height,
satoshis: value.satoshis,
script: value.scriptBuffer.toString('hex'), //TODO use a buffer
confirmations: this._tipHeight - key.height + 1
};
self.push(output);
callback();
};
module.exports = OutputsTransformStream;

File diff suppressed because it is too large Load Diff

View File

@ -1,805 +0,0 @@
'use strict';
var util = require('util');
var fs = require('fs');
var async = require('async');
var levelup = require('levelup');
var leveldown = require('leveldown');
var mkdirp = require('mkdirp');
var bitcore = require('bitcore-lib');
var BufferUtil = bitcore.util.buffer;
var Networks = bitcore.Networks;
var Block = bitcore.Block;
var $ = bitcore.util.preconditions;
var index = require('../');
var errors = index.errors;
var log = index.log;
var Transaction = require('../transaction');
var Service = require('../service');
/**
* This service synchronizes a leveldb database with bitcoin block chain by connecting and
* disconnecting blocks to build new indexes that can be queried. Other services can extend
* the data that is indexed by implementing a `blockHandler` method.
*
* @param {Object} options
* @param {Node} options.node - A reference to the node
* @param {Node} options.store - A levelup backend store
*/
function DB(options) {
/* jshint maxstatements: 20 */
if (!(this instanceof DB)) {
return new DB(options);
}
if (!options) {
options = {};
}
Service.call(this, options);
// Used to keep track of the version of the indexes
// to determine during an upgrade if a reindex is required
this.version = 2;
this.tip = null;
this.genesis = null;
$.checkState(this.node.network, 'Node is expected to have a "network" property');
this.network = this.node.network;
this._setDataPath();
this.maxOpenFiles = options.maxOpenFiles || DB.DEFAULT_MAX_OPEN_FILES;
this.levelupStore = leveldown;
if (options.store) {
this.levelupStore = options.store;
}
this.retryInterval = 60000;
this.subscriptions = {
transaction: [],
block: []
};
}
util.inherits(DB, Service);
DB.dependencies = ['bitcoind'];
DB.PREFIXES = {
VERSION: new Buffer('ff', 'hex'),
BLOCKS: new Buffer('01', 'hex'),
TIP: new Buffer('04', 'hex')
};
DB.DEFAULT_MAX_OPEN_FILES = 200;
/**
* This function will set `this.dataPath` based on `this.node.network`.
* @private
*/
DB.prototype._setDataPath = function() {
$.checkState(this.node.datadir, 'Node is expected to have a "datadir" property');
var regtest = Networks.get('regtest');
if (this.node.network === Networks.livenet) {
this.dataPath = this.node.datadir + '/bitcore-node.db';
} else if (this.node.network === Networks.testnet) {
this.dataPath = this.node.datadir + '/testnet3/bitcore-node.db';
} else if (this.node.network === regtest) {
this.dataPath = this.node.datadir + '/regtest/bitcore-node.db';
} else {
throw new Error('Unknown network: ' + this.network);
}
};
DB.prototype._checkVersion = function(callback) {
var self = this;
var options = {
keyEncoding: 'binary',
valueEncoding: 'binary'
};
self.store.get(DB.PREFIXES.TIP, options, function(err) {
if (err instanceof levelup.errors.NotFoundError) {
// The database is brand new and doesn't have a tip stored
// we can skip version checking
return callback();
} else if (err) {
return callback(err);
}
self.store.get(DB.PREFIXES.VERSION, options, function(err, buffer) {
var version;
if (err instanceof levelup.errors.NotFoundError) {
// The initial version (1) of the database didn't store the version number
version = 1;
} else if (err) {
return callback(err);
} else {
version = buffer.readUInt32BE();
}
if (self.version !== version) {
var helpUrl = 'https://github.com/bitpay/bitcore-node/blob/master/docs/services/db.md#how-to-reindex';
return callback(new Error(
'The version of the database "' + version + '" does not match the expected version "' +
self.version + '". A recreation of "' + self.dataPath + '" (can take several hours) is ' +
'required or to switch versions of software to match. Please see ' + helpUrl +
' for more information.'
));
}
callback();
});
});
};
DB.prototype._setVersion = function(callback) {
var versionBuffer = new Buffer(new Array(4));
versionBuffer.writeUInt32BE(this.version);
this.store.put(DB.PREFIXES.VERSION, versionBuffer, callback);
};
/**
* Called by Node to start the service.
* @param {Function} callback
*/
DB.prototype.start = function(callback) {
var self = this;
if (!fs.existsSync(this.dataPath)) {
mkdirp.sync(this.dataPath);
}
this.genesis = Block.fromBuffer(this.node.services.bitcoind.genesisBuffer);
this.store = levelup(this.dataPath, { db: this.levelupStore, maxOpenFiles: this.maxOpenFiles });
this.node.services.bitcoind.on('tx', this.transactionHandler.bind(this));
this.once('ready', function() {
log.info('Bitcoin Database Ready');
// Notify that there is a new tip
self.node.services.bitcoind.on('tip', function(height) {
if(!self.node.stopping) {
self.sync();
}
});
});
async.series([
function(next) {
self._checkVersion(next);
},
function(next) {
self._setVersion(next);
}
], function(err) {
if (err) {
return callback(err);
}
self.loadTip(function(err) {
if (err) {
return callback(err);
}
self.sync();
self.emit('ready');
setImmediate(callback);
});
});
};
/**
* Called by Node to stop the service
* @param {Function} callback
*/
DB.prototype.stop = function(callback) {
var self = this;
// Wait until syncing stops and all db operations are completed before closing leveldb
async.whilst(function() {
return self.bitcoindSyncing;
}, function(next) {
setTimeout(next, 10);
}, function() {
self.store.close(callback);
});
};
/**
* Will give information about the database from bitcoin.
* @param {Function} callback
*/
DB.prototype.getInfo = function(callback) {
var self = this;
setImmediate(function() {
var info = self.node.bitcoind.getInfo();
callback(null, info);
});
};
/**
* Closes the underlying store database
* @param {Function} callback
*/
DB.prototype.close = function(callback) {
this.store.close(callback);
};
/**
* This function is responsible for emitting `db/transaction` events.
* @param {Object} txInfo - The data from the bitcoind.on('tx') event
* @param {Buffer} txInfo.buffer - The transaction buffer
* @param {Boolean} txInfo.mempool - If the transaction was accepted in the mempool
* @param {String} txInfo.hash - The hash of the transaction
*/
DB.prototype.transactionHandler = function(txInfo) {
var tx = Transaction().fromBuffer(txInfo.buffer);
for (var i = 0; i < this.subscriptions.transaction.length; i++) {
this.subscriptions.transaction[i].emit('db/transaction', {
rejected: !txInfo.mempool,
tx: tx
});
}
};
/**
* Called by Node to determine the available API methods.
*/
DB.prototype.getAPIMethods = function() {
var methods = [
['getBlock', this, this.getBlock, 1],
['getBlockHashesByTimestamp', this, this.getBlockHashesByTimestamp, 2],
['getTransaction', this, this.getTransaction, 2],
['getTransactionWithBlockInfo', this, this.getTransactionWithBlockInfo, 2],
['sendTransaction', this, this.sendTransaction, 1],
['estimateFee', this, this.estimateFee, 1]
];
return methods;
};
DB.prototype.loadTip = function(callback) {
var self = this;
var options = {
keyEncoding: 'binary',
valueEncoding: 'binary'
};
self.store.get(DB.PREFIXES.TIP, options, function(err, tipData) {
if(err && err instanceof levelup.errors.NotFoundError) {
self.tip = self.genesis;
self.tip.__height = 0;
self.connectBlock(self.genesis, function(err) {
if(err) {
return callback(err);
}
self.emit('addblock', self.genesis);
callback();
});
return;
} else if(err) {
return callback(err);
}
var hash = tipData.toString('hex');
var times = 0;
async.retry({times: 3, interval: self.retryInterval}, function(done) {
self.getBlock(hash, function(err, tip) {
if(err) {
times++;
log.warn('Bitcoind does not have our tip (' + hash + '). Bitcoind may have crashed and needs to catch up.');
if(times < 3) {
log.warn('Retrying in ' + (self.retryInterval / 1000) + ' seconds.');
}
return done(err);
}
done(null, tip);
});
}, function(err, tip) {
if(err) {
log.warn('Giving up after 3 tries. Please report this bug to https://github.com/bitpay/bitcore-node/issues');
log.warn('Please reindex your database.');
return callback(err);
}
self.tip = tip;
var blockIndex = self.node.services.bitcoind.getBlockIndex(self.tip.hash);
if(!blockIndex) {
return callback(new Error('Could not get height for tip.'));
}
self.tip.__height = blockIndex.height;
callback();
});
});
};
/**
* Will get a block from bitcoind and give a Bitcore Block
* @param {String|Number} hash - A block hash or block height
*/
DB.prototype.getBlock = function(hash, callback) {
this.node.services.bitcoind.getBlock(hash, function(err, blockBuffer) {
if (err) {
return callback(err);
}
callback(null, Block.fromBuffer(blockBuffer));
});
};
/**
* Get block hashes between two timestamps
* @param {Number} high - high timestamp, in seconds, inclusive
* @param {Number} low - low timestamp, in seconds, inclusive
* @param {Function} callback
*/
DB.prototype.getBlockHashesByTimestamp = function(high, low, callback) {
var self = this;
var hashes = [];
var lowKey;
var highKey;
try {
lowKey = this._encodeBlockIndexKey(low);
highKey = this._encodeBlockIndexKey(high);
} catch(e) {
return callback(e);
}
var stream = this.store.createReadStream({
gte: lowKey,
lte: highKey,
reverse: true,
valueEncoding: 'binary',
keyEncoding: 'binary'
});
stream.on('data', function(data) {
hashes.push(self._decodeBlockIndexValue(data.value));
});
var error;
stream.on('error', function(streamError) {
if (streamError) {
error = streamError;
}
});
stream.on('close', function() {
if (error) {
return callback(error);
}
callback(null, hashes);
});
return stream;
};
/**
* Will give a Bitcore Transaction from bitcoind by txid
* @param {String} txid - A transaction hash
* @param {Boolean} queryMempool - Include the mempool
* @param {Function} callback
*/
DB.prototype.getTransaction = function(txid, queryMempool, callback) {
this.node.services.bitcoind.getTransaction(txid, queryMempool, function(err, txBuffer) {
if (err) {
return callback(err);
}
if (!txBuffer) {
return callback(new errors.Transaction.NotFound());
}
callback(null, Transaction().fromBuffer(txBuffer));
});
};
/**
* Will give a Bitcore Transaction and populated information about the block included.
* @param {String} txid - A transaction hash
* @param {Boolean} queryMempool - Include the mempool
* @param {Function} callback
*/
DB.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback) {
this.node.services.bitcoind.getTransactionWithBlockInfo(txid, queryMempool, function(err, obj) {
if (err) {
return callback(err);
}
var tx = Transaction().fromBuffer(obj.buffer);
tx.__blockHash = obj.blockHash;
tx.__height = obj.height;
tx.__timestamp = obj.timestamp;
callback(null, tx);
});
};
/**
* Will send a transaction to the Bitcoin network.
* @param {Transaction} tx - An instance of a Bitcore Transaction
* @param {Function} callback
*/
DB.prototype.sendTransaction = function(tx, callback) {
var txString;
if (tx instanceof Transaction) {
txString = tx.serialize();
} else {
txString = tx;
}
try {
var txid = this.node.services.bitcoind.sendTransaction(txString);
return callback(null, txid);
} catch(err) {
return callback(err);
}
};
/**
* Will estimate fees for a transaction and give a result in
* satoshis per kilobyte. Similar to the bitcoind estimateFee method.
* @param {Number} blocks - The number of blocks for the transaction to be included.
* @param {Function} callback
*/
DB.prototype.estimateFee = function(blocks, callback) {
var self = this;
setImmediate(function() {
callback(null, self.node.services.bitcoind.estimateFee(blocks));
});
};
/**
* Called by the Bus to determine the available events.
*/
DB.prototype.getPublishEvents = function() {
return [
{
name: 'db/transaction',
scope: this,
subscribe: this.subscribe.bind(this, 'transaction'),
unsubscribe: this.unsubscribe.bind(this, 'transaction')
},
{
name: 'db/block',
scope: this,
subscribe: this.subscribe.bind(this, 'block'),
unsubscribe: this.unsubscribe.bind(this, 'block')
}
];
};
DB.prototype.subscribe = function(name, emitter) {
this.subscriptions[name].push(emitter);
};
DB.prototype.unsubscribe = function(name, emitter) {
var index = this.subscriptions[name].indexOf(emitter);
if (index > -1) {
this.subscriptions[name].splice(index, 1);
}
};
/**
* Will give the previous hash for a block.
* @param {String} blockHash
* @param {Function} callback
*/
DB.prototype.getPrevHash = function(blockHash, callback) {
var blockIndex = this.node.services.bitcoind.getBlockIndex(blockHash);
setImmediate(function() {
if (blockIndex) {
callback(null, blockIndex.prevHash);
} else {
callback(new Error('Could not get prevHash, block not found'));
}
});
};
/**
* Connects a block to the database and add indexes
* @param {Block} block - The bitcore block
* @param {Function} callback
*/
DB.prototype.connectBlock = function(block, callback) {
log.debug('DB handling new chain block');
this.runAllBlockHandlers(block, true, callback);
};
/**
* Disconnects a block from the database and removes indexes
* @param {Block} block - The bitcore block
* @param {Function} callback
*/
DB.prototype.disconnectBlock = function(block, callback) {
log.debug('DB removing chain block');
this.runAllBlockHandlers(block, false, callback);
};
/**
* Will collect all database operations for a block from other services that implement
* `blockHandler` methods and then save operations to the database.
* @param {Block} block - The bitcore block
* @param {Boolean} add - If the block is being added/connected or removed/disconnected
* @param {Function} callback
*/
DB.prototype.runAllBlockHandlers = function(block, add, callback) {
var self = this;
var operations = [];
// Notify block subscribers
for (var i = 0; i < this.subscriptions.block.length; i++) {
this.subscriptions.block[i].emit('db/block', block.hash);
}
// Update tip
var tipHash = add ? new Buffer(block.hash, 'hex') : BufferUtil.reverse(block.header.prevHash);
operations.push({
type: 'put',
key: DB.PREFIXES.TIP,
value: tipHash
});
// Update block index
operations.push({
type: add ? 'put' : 'del',
key: this._encodeBlockIndexKey(block.header.timestamp),
value: this._encodeBlockIndexValue(block.hash)
});
async.eachSeries(
this.node.services,
function(mod, next) {
if(mod.blockHandler) {
$.checkArgument(typeof mod.blockHandler === 'function', 'blockHandler must be a function');
mod.blockHandler.call(mod, block, add, function(err, ops) {
if (err) {
return next(err);
}
if (ops) {
$.checkArgument(Array.isArray(ops), 'blockHandler for ' + mod.name + ' returned non-array');
operations = operations.concat(ops);
}
next();
});
} else {
setImmediate(next);
}
},
function(err) {
if (err) {
return callback(err);
}
log.debug('Updating the database with operations', operations);
self.store.batch(operations, callback);
}
);
};
DB.prototype._encodeBlockIndexKey = function(timestamp) {
$.checkArgument(timestamp >= 0 && timestamp <= 4294967295, 'timestamp out of bounds');
var timestampBuffer = new Buffer(4);
timestampBuffer.writeUInt32BE(timestamp);
return Buffer.concat([DB.PREFIXES.BLOCKS, timestampBuffer]);
};
DB.prototype._encodeBlockIndexValue = function(hash) {
return new Buffer(hash, 'hex');
};
DB.prototype._decodeBlockIndexValue = function(value) {
return value.toString('hex');
};
/**
* This function will find the common ancestor between the current chain and a forked block,
* by moving backwards on both chains until there is a meeting point.
* @param {Block} block - The new tip that forks the current chain.
* @param {Function} done - A callback function that is called when complete.
*/
DB.prototype.findCommonAncestor = function(block, done) {
var self = this;
var mainPosition = self.tip.hash;
var forkPosition = block.hash;
var mainHashesMap = {};
var forkHashesMap = {};
mainHashesMap[mainPosition] = true;
forkHashesMap[forkPosition] = true;
var commonAncestor = null;
async.whilst(
function() {
return !commonAncestor;
},
function(next) {
if(mainPosition) {
var mainBlockIndex = self.node.services.bitcoind.getBlockIndex(mainPosition);
if(mainBlockIndex && mainBlockIndex.prevHash) {
mainHashesMap[mainBlockIndex.prevHash] = true;
mainPosition = mainBlockIndex.prevHash;
} else {
mainPosition = null;
}
}
if(forkPosition) {
var forkBlockIndex = self.node.services.bitcoind.getBlockIndex(forkPosition);
if(forkBlockIndex && forkBlockIndex.prevHash) {
forkHashesMap[forkBlockIndex.prevHash] = true;
forkPosition = forkBlockIndex.prevHash;
} else {
forkPosition = null;
}
}
if(forkPosition && mainHashesMap[forkPosition]) {
commonAncestor = forkPosition;
}
if(mainPosition && forkHashesMap[mainPosition]) {
commonAncestor = mainPosition;
}
if(!mainPosition && !forkPosition) {
return next(new Error('Unknown common ancestor'));
}
setImmediate(next);
},
function(err) {
done(err, commonAncestor);
}
);
};
/**
* This function will attempt to rewind the chain to the common ancestor
* between the current chain and a forked block.
* @param {Block} block - The new tip that forks the current chain.
* @param {Function} done - A callback function that is called when complete.
*/
DB.prototype.syncRewind = function(block, done) {
var self = this;
self.findCommonAncestor(block, function(err, ancestorHash) {
if (err) {
return done(err);
}
log.warn('Reorg common ancestor found:', ancestorHash);
// Rewind the chain to the common ancestor
async.whilst(
function() {
// Wait until the tip equals the ancestor hash
return self.tip.hash !== ancestorHash;
},
function(removeDone) {
var tip = self.tip;
// TODO: expose prevHash as a string from bitcore
var prevHash = BufferUtil.reverse(tip.header.prevHash).toString('hex');
self.getBlock(prevHash, function(err, previousTip) {
if (err) {
removeDone(err);
}
// Undo the related indexes for this block
self.disconnectBlock(tip, function(err) {
if (err) {
return removeDone(err);
}
// Set the new tip
previousTip.__height = self.tip.__height - 1;
self.tip = previousTip;
self.emit('removeblock', tip);
removeDone();
});
});
}, done
);
});
};
/**
* This function will synchronize additional indexes for the chain based on
* the current active chain in the bitcoin daemon. In the event that there is
* a reorganization in the daemon, the chain will rewind to the last common
* ancestor and then resume syncing.
*/
DB.prototype.sync = function() {
var self = this;
if (self.bitcoindSyncing || self.node.stopping || !self.tip) {
return;
}
self.bitcoindSyncing = true;
var height;
async.whilst(function() {
height = self.tip.__height;
return height < self.node.services.bitcoind.height && !self.node.stopping;
}, function(done) {
self.node.services.bitcoind.getBlock(height + 1, function(err, blockBuffer) {
if (err) {
return done(err);
}
var block = Block.fromBuffer(blockBuffer);
// TODO: expose prevHash as a string from bitcore
var prevHash = BufferUtil.reverse(block.header.prevHash).toString('hex');
if (prevHash === self.tip.hash) {
// This block appends to the current chain tip and we can
// immediately add it to the chain and create indexes.
// Populate height
block.__height = self.tip.__height + 1;
// Create indexes
self.connectBlock(block, function(err) {
if (err) {
return done(err);
}
self.tip = block;
log.debug('Chain added block to main chain');
self.emit('addblock', block);
setImmediate(done);
});
} else {
// This block doesn't progress the current tip, so we'll attempt
// to rewind the chain to the common ancestor of the block and
// then we can resume syncing.
log.warn('Beginning reorg! Current tip: ' + self.tip.hash + '; New tip: ' + block.hash);
self.syncRewind(block, function(err) {
if(err) {
return done(err);
}
log.warn('Reorg complete. New tip is ' + self.tip.hash);
done();
});
}
});
}, function(err) {
if (err) {
Error.captureStackTrace(err);
return self.node.emit('error', err);
}
if(self.node.stopping) {
self.bitcoindSyncing = false;
return;
}
if (self.node.services.bitcoind.isSynced()) {
self.bitcoindSyncing = false;
self.node.emit('synced');
} else {
self.bitcoindSyncing = false;
}
});
};
module.exports = DB;

View File

@ -1,15 +1,19 @@
'use strict';
var fs = require('fs');
var http = require('http');
var https = require('https');
var express = require('express');
var bodyParser = require('body-parser');
var socketio = require('socket.io');
var BaseService = require('../service');
var inherits = require('util').inherits;
var BaseService = require('../service');
var bitcore = require('bitcore-lib-zcash');
var _ = bitcore.deps._;
var index = require('../');
var log = index.log;
var fs = require('fs');
/**
* This service represents a hub for combining several services over a single HTTP port. Services
@ -23,6 +27,7 @@ var fs = require('fs');
* @param {Object} options.httpsOptions - Options passed into https.createServer, defaults to node settings.
* @param {String} options.httpsOptions.key - Path to key file
* @param {String} options.httpsOptions.cert - Path to cert file
* @param {Boolean} options.enableSocketRPC - Option to enable/disable websocket RPC handling
* @param {Number} options.port - The port for the service, defaults to node settings.
*/
var WebService = function(options) {
@ -32,6 +37,13 @@ var WebService = function(options) {
this.httpsOptions = options.httpsOptions || this.node.httpsOptions;
this.port = options.port || this.node.port || 3456;
// set the maximum size of json payload, defaults to express default
// see: https://github.com/expressjs/body-parser#limit
this.jsonRequestLimit = options.jsonRequestLimit || '100kb';
this.enableSocketRPC = _.isUndefined(options.enableSocketRPC) ?
WebService.DEFAULT_SOCKET_RPC : options.enableSocketRPC;
this.node.on('ready', function() {
self.eventNames = self.getEventNames();
self.setupAllRoutes();
@ -43,6 +55,7 @@ var WebService = function(options) {
inherits(WebService, BaseService);
WebService.dependencies = [];
WebService.DEFAULT_SOCKET_RPC = true;
/**
* Called by Node to start the service
@ -50,7 +63,7 @@ WebService.dependencies = [];
*/
WebService.prototype.start = function(callback) {
this.app = express();
this.app.use(bodyParser.json());
this.app.use(bodyParser.json({limit: this.jsonRequestLimit}));
if(this.https) {
this.transformHttpsOptions();
@ -149,21 +162,31 @@ WebService.prototype.getEventNames = function() {
return eventNames;
};
WebService.prototype._getRemoteAddress = function(socket) {
return socket.client.request.headers['cf-connecting-ip'] || socket.conn.remoteAddress;
};
/**
* This function is responsible for managing a socket.io connection, including
* instantiating a new Bus, subscribing/unsubscribing and handling RPC commands.
* @param {Socket} socket - A socket.io socket instance
*/
WebService.prototype.socketHandler = function(socket) {
var bus = this.node.openBus();
var self = this;
var remoteAddress = self._getRemoteAddress(socket);
var bus = this.node.openBus({remoteAddress: remoteAddress});
socket.on('message', this.socketMessageHandler.bind(this));
if (this.enableSocketRPC) {
socket.on('message', this.socketMessageHandler.bind(this));
}
socket.on('subscribe', function(name, params) {
log.info(remoteAddress, 'web socket subscribe:', name);
bus.subscribe(name, params);
});
socket.on('unsubscribe', function(name, params) {
log.info(remoteAddress, 'web socket unsubscribe:', name);
bus.unsubscribe(name, params);
});
@ -183,6 +206,7 @@ WebService.prototype.socketHandler = function(socket) {
});
socket.on('disconnect', function() {
log.info(remoteAddress, 'web socket disconnect');
bus.close();
});
};

View File

@ -1,60 +0,0 @@
'use strict';
var async = require('async');
var levelup = require('levelup');
var bitcore = require('bitcore-lib');
var Transaction = bitcore.Transaction;
Transaction.prototype.populateInputs = function(db, poolTransactions, callback) {
var self = this;
if(this.isCoinbase()) {
return setImmediate(callback);
}
async.each(
this.inputs,
function(input, next) {
self._populateInput(db, input, poolTransactions, next);
},
callback
);
};
Transaction.prototype._populateInput = function(db, input, poolTransactions, callback) {
if (!input.prevTxId || !Buffer.isBuffer(input.prevTxId)) {
return callback(new Error('Input is expected to have prevTxId as a buffer'));
}
var txid = input.prevTxId.toString('hex');
db.getTransaction(txid, true, function(err, prevTx) {
if(err instanceof levelup.errors.NotFoundError) {
// Check the pool for transaction
for(var i = 0; i < poolTransactions.length; i++) {
if(txid === poolTransactions[i].hash) {
input.output = poolTransactions[i].outputs[input.outputIndex];
return callback();
}
}
return callback(new Error('Previous tx ' + input.prevTxId.toString('hex') + ' not found'));
} else if(err) {
callback(err);
} else {
input.output = prevTx.outputs[input.outputIndex];
callback();
}
});
};
Transaction.prototype._checkSpent = function(db, input, poolTransactions, callback) {
// TODO check and see if another transaction in the pool spent the output
db.isSpentDB(input, function(spent) {
if(spent) {
return callback(new Error('Input already spent'));
} else {
callback();
}
});
};
module.exports = Transaction;

View File

@ -21,4 +21,22 @@ utils.startAtZero = function startAtZero(obj, key) {
}
};
utils.isAbsolutePath = require('path').isAbsolute;
if (!utils.isAbsolutePath) {
utils.isAbsolutePath = require('path-is-absolute');
}
utils.parseParamsWithJSON = function parseParamsWithJSON(paramsArg) {
var params = paramsArg.map(function(paramArg) {
var param;
try {
param = JSON.parse(paramArg);
} catch(err) {
param = paramArg;
}
return param;
});
return params;
};
module.exports = utils;

View File

@ -1,14 +1,13 @@
{
"name": "bitcore-node",
"description": "Full node with extended capabilities using Bitcore and Bitcoin Core",
"name": "bitcore-node-zcash",
"description": "Full node with extended capabilities using Bitcore and Zcash",
"author": "BitPay <dev@bitpay.com>",
"version": "2.0.1",
"lastBuild": "2.0.0",
"version": "3.1.2",
"main": "./index.js",
"repository": "git://github.com/bitpay/bitcore-node.git",
"homepage": "https://github.com/bitpay/bitcore-node",
"repository": "git://github.com/zcash-hackworks/bitcore-node-zcash.git",
"homepage": "https://github.com/zcash-hackworks/bitcore-node-zcash",
"bugs": {
"url": "https://github.com/bitpay/bitcore-node/issues"
"url": "https://github.com/zcash-hackworks/bitcore-node-zcash/issues"
},
"contributors": [
{
@ -25,68 +24,69 @@
{
"name": "Patrick Nagurny",
"email": "patrick@bitpay.com"
},
{
"name": "Jack Grigg",
"email": "jack@z.cash"
},
{
"name": "Simon Liu",
"email": "simon@z.cash"
},
{
"name": "Ian Munoz",
"email": "ian.org@gmail.com"
}
],
"bin": {
"bitcore-node": "./bin/bitcore-node"
},
"scripts": {
"install": "./bin/install",
"build": "./bin/build",
"clean": "./bin/clean",
"package": "node bin/package.js",
"upload": "node bin/upload.js",
"start": "node bin/start.js",
"test": "NODE_ENV=test mocha -R spec --recursive",
"coverage": "NODE_ENV=test istanbul cover _mocha -- --recursive",
"libbitcoind": "node bin/start-libbitcoind.js"
"test": "mocha -R spec --recursive",
"regtest": "./scripts/regtest",
"jshint": "jshint --reporter=node_modules/jshint-stylish ./lib",
"coverage": "istanbul cover _mocha -- --recursive",
"coveralls": "./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- --recursive -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js"
},
"tags": [
"bitcoin",
"bitcoind"
"zcash",
"zcashd"
],
"dependencies": {
"async": "^1.3.0",
"bindings": "^1.2.1",
"bitcore-lib": "^0.13.7",
"bitcoind-rpc": "^0.6.0",
"bitcore-lib-zcash": "zcash-hackworks/bitcore-lib-zcash",
"body-parser": "^1.13.3",
"colors": "^1.1.2",
"commander": "^2.8.1",
"errno": "^0.1.4",
"express": "^4.13.3",
"leveldown": "bitpay/leveldown#bitpay-1.4.4",
"levelup": "^1.3.1",
"liftoff": "^2.2.0",
"memdown": "^1.0.0",
"lru-cache": "^4.0.1",
"mkdirp": "0.5.0",
"nan": "^2.0.9",
"npm": "^2.14.1",
"path-is-absolute": "^1.0.0",
"semver": "^5.0.1",
"socket.io": "bitpay/socket.io#bitpay-1.3.7",
"socket.io-client": "bitpay/socket.io-client#bitpay-1.3.7"
"socket.io": "^1.4.5",
"socket.io-client": "^1.4.5",
"zmq": "^2.14.0"
},
"optionalDependencies": {
"bufferutil": "~1.2.1",
"utf-8-validate": "~1.2.1"
},
"devDependencies": {
"aws-sdk": "~2.0.0-rc.15",
"benchmark": "1.0.0",
"bitcoin": "^2.3.2",
"bitcoind-rpc": "^0.3.0",
"chai": "^3.0.0",
"mocha": "~1.16.2",
"bitcore-p2p": "^1.1.0",
"chai": "^3.5.0",
"coveralls": "^2.11.9",
"istanbul": "^0.4.3",
"jshint": "^2.9.2",
"jshint-stylish": "^2.1.0",
"mocha": "^2.4.5",
"proxyquire": "^1.3.1",
"rimraf": "^2.4.2",
"sinon": "^1.15.4",
"bitcore-p2p": "~1.0.0"
"sinon": "^1.15.4"
},
"engines": {
"node": "^0.12 || ^4.2"
},
"os": [
"darwin",
"linux"
],
"cpu": [
"x64",
"arm"
],
"license": "MIT"
}

485
regtest/bitcoind.js Normal file
View File

@ -0,0 +1,485 @@
'use strict';
// To run the tests: $ mocha -R spec regtest/bitcoind.js
var path = require('path');
var index = require('..');
var log = index.log;
var chai = require('chai');
var bitcore = require('bitcore-lib-zcash');
var BN = bitcore.crypto.BN;
var async = require('async');
var rimraf = require('rimraf');
var bitcoind;
/* jshint unused: false */
var should = chai.should();
var assert = chai.assert;
var sinon = require('sinon');
var BitcoinRPC = require('bitcoind-rpc');
var transactionData = [];
var blockHashes = [];
var utxos;
var client;
var coinbasePrivateKey;
var privateKey = bitcore.PrivateKey();
var destKey = bitcore.PrivateKey();
describe('Zcashd Functionality', function() {
before(function(done) {
this.timeout(60000);
// Add the regtest network
bitcore.Networks.enableRegtest();
var regtestNetwork = bitcore.Networks.get('regtest');
var datadir = __dirname + '/data';
rimraf(datadir + '/regtest', function(err) {
if (err) {
throw err;
}
bitcoind = require('../').services.Bitcoin({
spawn: {
datadir: datadir,
exec: path.resolve(__dirname, '../bin/zcashd')
},
node: {
network: regtestNetwork,
getNetworkName: function() {
return 'regtest';
}
}
});
bitcoind.on('error', function(err) {
log.error('error="%s"', err.message);
});
log.info('Waiting for Zcash to initialize...');
bitcoind.start(function() {
log.info('Zcashd started');
client = new BitcoinRPC({
protocol: 'http',
host: '127.0.0.1',
port: 30331,
user: 'bitcoin',
pass: 'local321',
rejectUnauthorized: false
});
log.info('Generating 100 blocks...');
// Generate enough blocks so that the initial coinbase transactions
// can be spent.
setImmediate(function() {
client.generate(150, function(err, response) {
if (err) {
throw err;
}
blockHashes = response.result;
log.info('Preparing test data...');
// Get all of the unspent outputs
client.listUnspent(0, 150, function(err, response) {
utxos = response.result;
async.mapSeries(utxos, function(utxo, next) {
async.series([
function(finished) {
// Load all of the transactions for later testing
client.getTransaction(utxo.txid, function(err, txresponse) {
if (err) {
throw err;
}
// add to the list of transactions for testing later
transactionData.push(txresponse.result.hex);
finished();
});
},
function(finished) {
// Get the private key for each utxo
client.dumpPrivKey(utxo.address, function(err, privresponse) {
if (err) {
throw err;
}
utxo.privateKeyWIF = privresponse.result;
finished();
});
}
], next);
}, function(err) {
if (err) {
throw err;
}
done();
});
});
});
});
});
});
});
after(function(done) {
this.timeout(60000);
bitcoind.node.stopping = true;
bitcoind.stop(function(err, result) {
done();
});
});
describe('get blocks by hash', function() {
[0,1,2,3,5,6,7,8,9].forEach(function(i) {
it('generated block ' + i, function(done) {
bitcoind.getBlock(blockHashes[i], function(err, block) {
if (err) {
throw err;
}
should.exist(block);
block.hash.should.equal(blockHashes[i]);
done();
});
});
});
});
describe('get blocks as buffers', function() {
[0,1,2,3,5,6,7,8,9].forEach(function(i) {
it('generated block ' + i, function(done) {
bitcoind.getRawBlock(blockHashes[i], function(err, block) {
if (err) {
throw err;
}
should.exist(block);
(block instanceof Buffer).should.equal(true);
done();
});
});
});
});
describe('get errors as error instances', function() {
it('will wrap an rpc into a javascript error', function(done) {
bitcoind.client.getBlock(1000000000, function(err, response) {
var error = bitcoind._wrapRPCError(err);
(error instanceof Error).should.equal(true);
error.message.should.equal(err.message);
error.code.should.equal(err.code);
should.exist(error.stack);
done();
});
});
});
describe('get blocks by height', function() {
[0,1,2,3,4,5,6,7,8,9].forEach(function(i) {
it('generated block ' + i, function(done) {
// add the genesis block
var height = i + 1;
bitcoind.getBlock(i + 1, function(err, block) {
if (err) {
throw err;
}
should.exist(block);
block.hash.should.equal(blockHashes[i]);
done();
});
});
});
it('will get error with number greater than tip', function(done) {
bitcoind.getBlock(1000000000, function(err, response) {
should.exist(err);
err.code.should.equal(-8);
done();
});
});
});
describe('get transactions by hash', function() {
[0,1,2,3,4,5,6,7,8,9].forEach(function(i) {
it('for tx ' + i, function(done) {
var txhex = transactionData[i];
var tx = new bitcore.Transaction();
tx.fromString(txhex);
bitcoind.getTransaction(tx.hash, function(err, response) {
if (err) {
throw err;
}
assert(response.toString('hex') === txhex, 'incorrect tx data result');
done();
});
});
});
it('will return error if the transaction does not exist', function(done) {
var txid = '6226c407d0e9705bdd7158e60983e37d0f5d23529086d6672b07d9238d5aa618';
bitcoind.getTransaction(txid, function(err, response) {
should.exist(err);
done();
});
});
});
describe('get transactions as buffers', function() {
[0,1,2,3,4,5,6,7,8,9].forEach(function(i) {
it('for tx ' + i, function(done) {
var txhex = transactionData[i];
var tx = new bitcore.Transaction();
tx.fromString(txhex);
bitcoind.getRawTransaction(tx.hash, function(err, response) {
if (err) {
throw err;
}
response.should.be.instanceOf(Buffer);
assert(response.toString('hex') === txhex, 'incorrect tx data result');
done();
});
});
});
it('will return error if the transaction does not exist', function(done) {
var txid = '6226c407d0e9705bdd7158e60983e37d0f5d23529086d6672b07d9238d5aa618';
bitcoind.getRawTransaction(txid, function(err, response) {
should.exist(err);
done();
});
});
});
describe('get block header', function() {
var expectedWork = new BN(6);
[1,2,3,4,5,6,7,8,9].forEach(function(i) {
it('generate block ' + i, function(done) {
bitcoind.getBlockHeader(blockHashes[i], function(err, blockIndex) {
if (err) {
return done(err);
}
should.exist(blockIndex);
should.exist(blockIndex.chainWork);
var work = new BN(blockIndex.chainWork, 'hex');
work.toString(16).should.equal(expectedWork.toString(16));
expectedWork = expectedWork.add(new BN(2));
should.exist(blockIndex.prevHash);
blockIndex.hash.should.equal(blockHashes[i]);
blockIndex.prevHash.should.equal(blockHashes[i - 1]);
blockIndex.height.should.equal(i + 1);
done();
});
});
});
it('will get null prevHash for the genesis block', function(done) {
bitcoind.getBlockHeader(0, function(err, header) {
if (err) {
return done(err);
}
should.exist(header);
should.equal(header.prevHash, undefined);
done();
});
});
it('will get error for block not found', function(done) {
bitcoind.getBlockHeader('notahash', function(err, header) {
should.exist(err);
done();
});
});
});
describe('get block index by height', function() {
var expectedWork = new BN(6);
[2,3,4,5,6,7,8,9].forEach(function(i) {
it('generate block ' + i, function() {
bitcoind.getBlockHeader(i, function(err, header) {
should.exist(header);
should.exist(header.chainWork);
var work = new BN(header.chainWork, 'hex');
work.toString(16).should.equal(expectedWork.toString(16));
expectedWork = expectedWork.add(new BN(2));
should.exist(header.prevHash);
header.hash.should.equal(blockHashes[i - 1]);
header.prevHash.should.equal(blockHashes[i - 2]);
header.height.should.equal(i);
});
});
});
it('will get error with number greater than tip', function(done) {
bitcoind.getBlockHeader(100000, function(err, header) {
should.exist(err);
done();
});
});
});
describe('send transaction functionality', function() {
it('will not error and return the transaction hash', function(done) {
// create and sign the transaction
var tx = bitcore.Transaction();
tx.from(utxos[0]);
tx.change(privateKey.toAddress());
tx.to(destKey.toAddress(), utxos[0].amount * 1e8 - 1000);
tx.sign(bitcore.PrivateKey.fromWIF(utxos[0].privateKeyWIF));
// test sending the transaction
bitcoind.sendTransaction(tx.serialize(), function(err, hash) {
if (err) {
return done(err);
}
hash.should.equal(tx.hash);
done();
});
});
it('will throw an error if an unsigned transaction is sent', function(done) {
var tx = bitcore.Transaction();
tx.from(utxos[1]);
tx.change(privateKey.toAddress());
tx.to(destKey.toAddress(), utxos[1].amount * 1e8 - 1000);
bitcoind.sendTransaction(tx.uncheckedSerialize(), function(err, hash) {
should.exist(err);
(err instanceof Error).should.equal(true);
should.not.exist(hash);
done();
});
});
it('will throw an error for unexpected types (tx decode failed)', function(done) {
var garbage = new Buffer('abcdef', 'hex');
bitcoind.sendTransaction(garbage, function(err, hash) {
should.exist(err);
should.not.exist(hash);
var num = 23;
bitcoind.sendTransaction(num, function(err, hash) {
should.exist(err);
(err instanceof Error).should.equal(true);
should.not.exist(hash);
done();
});
});
});
it('will emit "tx" events', function(done) {
var tx = bitcore.Transaction();
tx.from(utxos[2]);
tx.change(privateKey.toAddress());
tx.to(destKey.toAddress(), utxos[2].amount * 1e8 - 1000);
tx.sign(bitcore.PrivateKey.fromWIF(utxos[2].privateKeyWIF));
var serialized = tx.serialize();
bitcoind.once('tx', function(buffer) {
buffer.toString('hex').should.equal(serialized);
done();
});
bitcoind.sendTransaction(serialized, function(err, hash) {
if (err) {
return done(err);
}
should.exist(hash);
});
});
});
describe('fee estimation', function() {
it('will estimate fees', function(done) {
bitcoind.estimateFee(1, function(err, fees) {
if (err) {
return done(err);
}
fees.should.equal(-1);
done();
});
});
});
describe('tip updates', function() {
it('will get an event when the tip is new', function(done) {
this.timeout(4000);
bitcoind.on('tip', function(height) {
if (height === 151) {
done();
}
});
client.generate(1, function(err, response) {
if (err) {
throw err;
}
});
});
});
describe('get detailed transaction', function() {
it('should include details for coinbase tx', function(done) {
bitcoind.getDetailedTransaction(utxos[0].txid, function(err, tx) {
if (err) {
return done(err);
}
should.exist(tx.height);
tx.height.should.be.a('number');
should.exist(tx.blockTimestamp);
should.exist(tx.blockHash);
tx.coinbase.should.equal(true);
tx.version.should.equal(1);
tx.hex.should.be.a('string');
tx.locktime.should.equal(0);
tx.feeSatoshis.should.equal(0);
tx.outputSatoshis.should.equal(50 * 1e8);
tx.inputSatoshis.should.equal(0);
tx.inputs.length.should.equal(1);
tx.outputs.length.should.equal(1);
should.equal(tx.inputs[0].prevTxId, null);
should.equal(tx.inputs[0].outputIndex, null);
tx.inputs[0].script.should.be.a('string');
should.equal(tx.inputs[0].scriptAsm, null);
should.equal(tx.inputs[0].address, null);
should.equal(tx.inputs[0].satoshis, null);
tx.outputs[0].satoshis.should.equal(50 * 1e8);
tx.outputs[0].script.should.be.a('string');
tx.outputs[0].scriptAsm.should.be.a('string');
tx.outputs[0].spentTxId.should.be.a('string');
tx.outputs[0].spentIndex.should.equal(0);
tx.outputs[0].spentHeight.should.be.a('number');
tx.outputs[0].address.should.be.a('string');
done();
});
});
});
describe('#getInfo', function() {
it('will get information', function(done) {
bitcoind.getInfo(function(err, info) {
if (err) {
return done(err);
}
info.network.should.equal('regtest');
should.exist(info);
should.exist(info.version);
should.exist(info.blocks);
should.exist(info.timeOffset);
should.exist(info.connections);
should.exist(info.difficulty);
should.exist(info.testnet);
should.exist(info.relayFee);
should.exist(info.errors);
done();
});
});
});
});

183
regtest/cluster.js Normal file
View File

@ -0,0 +1,183 @@
'use strict';
var path = require('path');
var async = require('async');
var spawn = require('child_process').spawn;
var BitcoinRPC = require('bitcoind-rpc');
var rimraf = require('rimraf');
var bitcore = require('bitcore-lib-zcash');
var chai = require('chai');
var should = chai.should();
var index = require('..');
var log = index.log;
log.debug = function() {};
var BitcoreNode = index.Node;
var BitcoinService = index.services.Bitcoin;
describe('Bitcoin Cluster', function() {
var node;
var daemons = [];
var execPath = path.resolve(__dirname, '../bin/zcashd');
var nodesConf = [
{
datadir: path.resolve(__dirname, './data/node1'),
conf: path.resolve(__dirname, './data/node1/zcash.conf'),
rpcuser: 'bitcoin',
rpcpassword: 'local321',
rpcport: 30521,
zmqpubrawtx: 'tcp://127.0.0.1:30611',
zmqpubhashblock: 'tcp://127.0.0.1:30611'
},
{
datadir: path.resolve(__dirname, './data/node2'),
conf: path.resolve(__dirname, './data/node2/zcash.conf'),
rpcuser: 'bitcoin',
rpcpassword: 'local321',
rpcport: 30522,
zmqpubrawtx: 'tcp://127.0.0.1:30622',
zmqpubhashblock: 'tcp://127.0.0.1:30622'
},
{
datadir: path.resolve(__dirname, './data/node3'),
conf: path.resolve(__dirname, './data/node3/zcash.conf'),
rpcuser: 'bitcoin',
rpcpassword: 'local321',
rpcport: 30523,
zmqpubrawtx: 'tcp://127.0.0.1:30633',
zmqpubhashblock: 'tcp://127.0.0.1:30633'
}
];
before(function(done) {
log.info('Starting 3 bitcoind daemons');
this.timeout(60000);
async.each(nodesConf, function(nodeConf, next) {
var opts = [
'--regtest',
'--datadir=' + nodeConf.datadir,
'--conf=' + nodeConf.conf
];
rimraf(path.resolve(nodeConf.datadir, './regtest'), function(err) {
if (err) {
return done(err);
}
var process = spawn(execPath, opts, {stdio: 'inherit'});
var client = new BitcoinRPC({
protocol: 'http',
host: '127.0.0.1',
port: nodeConf.rpcport,
user: nodeConf.rpcuser,
pass: nodeConf.rpcpassword
});
daemons.push(process);
async.retry({times: 10, interval: 5000}, function(ready) {
client.getInfo(ready);
}, next);
});
}, done);
});
after(function(done) {
this.timeout(10000);
setTimeout(function() {
async.each(daemons, function(process, next) {
process.once('exit', next);
process.kill('SIGINT');
}, done);
}, 1000);
});
it('step 1: will connect to three bitcoind daemons', function(done) {
this.timeout(20000);
var configuration = {
network: 'regtest',
services: [
{
name: 'bitcoind',
module: BitcoinService,
config: {
connect: [
{
rpchost: '127.0.0.1',
rpcport: 30521,
rpcuser: 'bitcoin',
rpcpassword: 'local321',
zmqpubrawtx: 'tcp://127.0.0.1:30611'
},
{
rpchost: '127.0.0.1',
rpcport: 30522,
rpcuser: 'bitcoin',
rpcpassword: 'local321',
zmqpubrawtx: 'tcp://127.0.0.1:30622'
},
{
rpchost: '127.0.0.1',
rpcport: 30523,
rpcuser: 'bitcoin',
rpcpassword: 'local321',
zmqpubrawtx: 'tcp://127.0.0.1:30633'
}
]
}
}
]
};
var regtest = bitcore.Networks.get('regtest');
should.exist(regtest);
node = new BitcoreNode(configuration);
node.on('error', function(err) {
log.error(err);
});
node.on('ready', function() {
done();
});
node.start(function(err) {
if (err) {
return done(err);
}
});
});
it('step 2: receive block events', function(done) {
this.timeout(10000);
node.services.bitcoind.once('tip', function(height) {
height.should.equal(1);
done();
});
node.generateBlock(1, function(err, hashes) {
if (err) {
return done(err);
}
should.exist(hashes);
});
});
it('step 3: get blocks', function(done) {
async.times(3, function(n, next) {
node.getBlock(1, function(err, block) {
if (err) {
return next(err);
}
should.exist(block);
next();
});
}, done);
});
});

12
regtest/data/bitcoin.conf Normal file
View File

@ -0,0 +1,12 @@
server=1
whitelist=127.0.0.1
txindex=1
addressindex=1
timestampindex=1
spentindex=1
zmqpubrawtx=tcp://127.0.0.1:30332
zmqpubhashblock=tcp://127.0.0.1:30332
rpcallowip=127.0.0.1
rpcport=30331
rpcuser=bitcoin
rpcpassword=local321

View File

@ -0,0 +1,16 @@
server=1
whitelist=127.0.0.1
txindex=1
addressindex=1
timestampindex=1
spentindex=1
addnode=127.0.0.1:30432
addnode=127.0.0.1:30433
port=30431
rpcport=30521
zmqpubrawtx=tcp://127.0.0.1:30611
zmqpubhashblock=tcp://127.0.0.1:30611
rpcallowip=127.0.0.1
rpcuser=bitcoin
rpcpassword=local321
keypool=3

View File

@ -0,0 +1,16 @@
server=1
whitelist=127.0.0.1
txindex=1
addressindex=1
timestampindex=1
spentindex=1
addnode=127.0.0.1:30431
addnode=127.0.0.1:30433
port=30432
rpcport=30522
zmqpubrawtx=tcp://127.0.0.1:30622
zmqpubhashblock=tcp://127.0.0.1:30622
rpcallowip=127.0.0.1
rpcuser=bitcoin
rpcpassword=local321
keypool=3

View File

@ -0,0 +1,16 @@
server=1
whitelist=127.0.0.1
txindex=1
addressindex=1
timestampindex=1
spentindex=1
addnode=127.0.0.1:30431
addnode=127.0.0.1:30432
port=30433
rpcport=30523
zmqpubrawtx=tcp://127.0.0.1:30633
zmqpubhashblock=tcp://127.0.0.1:30633
rpcallowip=127.0.0.1
rpcuser=bitcoin
rpcpassword=local321
keypool=3

View File

@ -1,22 +1,15 @@
'use strict';
// These tests require bitcore-node Bitcoin Core bindings to be compiled with
// the environment variable BITCORENODE_ENV=test. This enables the use of regtest
// functionality by including the wallet in the build.
// To run the tests: $ mocha -R spec integration/regtest-node.js
// To run the tests: $ mocha -R spec regtest/node.js
var path = require('path');
var index = require('..');
var async = require('async');
var log = index.log;
log.debug = function() {};
if (process.env.BITCORENODE_ENV !== 'test') {
log.info('Please set the environment variable BITCORENODE_ENV=test and make sure bindings are compiled for testing');
process.exit();
}
var chai = require('chai');
var bitcore = require('bitcore-lib');
var bitcore = require('bitcore-lib-zcash');
var rimraf = require('rimraf');
var node;
@ -24,12 +17,9 @@ var should = chai.should();
var BitcoinRPC = require('bitcoind-rpc');
var index = require('..');
var Transaction = index.Transaction;
var Transaction = bitcore.Transaction;
var BitcoreNode = index.Node;
var AddressService = index.services.Address;
var BitcoinService = index.services.Bitcoin;
var encoding = require('../lib/services/address/encoding');
var DBService = index.services.DB;
var testWIF = 'cSdkPxkAjA4HDr5VHgsebAPDEh9Gyub4HK8UJr2DFGGqKKy4K5sG';
var testKey;
var client;
@ -42,7 +32,7 @@ describe('Node Functionality', function() {
var regtest;
before(function(done) {
this.timeout(30000);
this.timeout(20000);
var datadir = __dirname + '/data';
@ -55,23 +45,17 @@ describe('Node Functionality', function() {
}
var configuration = {
datadir: datadir,
network: 'regtest',
services: [
{
name: 'db',
module: DBService,
config: {}
},
{
name: 'bitcoind',
module: BitcoinService,
config: {}
},
{
name: 'address',
module: AddressService,
config: {}
config: {
spawn: {
datadir: datadir,
exec: path.resolve(__dirname, '../bin/zcashd')
}
}
}
]
};
@ -85,37 +69,35 @@ describe('Node Functionality', function() {
log.error(err);
});
node.on('ready', function() {
node.start(function(err) {
if (err) {
return done(err);
}
client = new BitcoinRPC({
protocol: 'https',
protocol: 'http',
host: '127.0.0.1',
port: 18332,
port: 30331,
user: 'bitcoin',
pass: 'local321',
rejectUnauthorized: false
});
var syncedHandler = function() {
if (node.services.db.tip.__height === 150) {
node.removeListener('synced', syncedHandler);
if (node.services.bitcoind.height === 150) {
node.services.bitcoind.removeListener('synced', syncedHandler);
done();
}
};
node.on('synced', syncedHandler);
node.services.bitcoind.on('synced', syncedHandler);
client.generate(150, function(err, response) {
client.generate(150, function(err) {
if (err) {
throw err;
}
});
});
node.start(function(err) {
if (err) {
throw err;
}
});
@ -132,104 +114,31 @@ describe('Node Functionality', function() {
});
});
var invalidatedBlockHash;
it('will handle a reorganization', function(done) {
var count;
var blockHash;
async.series([
function(next) {
client.getBlockCount(function(err, response) {
if (err) {
return next(err);
}
count = response.result;
next();
});
},
function(next) {
client.getBlockHash(count, function(err, response) {
if (err) {
return next(err);
}
invalidatedBlockHash = response.result;
next();
});
},
function(next) {
client.invalidateBlock(invalidatedBlockHash, next);
},
function(next) {
client.getBlockCount(function(err, response) {
if (err) {
return next(err);
}
response.result.should.equal(count - 1);
next();
});
}
], function(err) {
if (err) {
throw err;
}
var blocksRemoved = 0;
var blocksAdded = 0;
var removeBlock = function() {
blocksRemoved++;
};
node.services.db.on('removeblock', removeBlock);
var addBlock = function() {
blocksAdded++;
if (blocksAdded === 2 && blocksRemoved === 1) {
node.services.db.removeListener('addblock', addBlock);
node.services.db.removeListener('removeblock', removeBlock);
done();
}
};
node.services.db.on('addblock', addBlock);
// We need to add a transaction to the mempool so that the next block will
// have a different hash as the hash has been invalidated.
client.sendToAddress(testKey.toAddress(regtest).toString(), 10, function(err) {
if (err) {
throw err;
}
client.generate(2, function(err, response) {
if (err) {
throw err;
}
});
});
});
});
it('isMainChain() will return false for stale/orphan block', function(done) {
node.services.bitcoind.isMainChain(invalidatedBlockHash).should.equal(false);
setImmediate(done);
});
describe('Bus Functionality', function() {
it('subscribes and unsubscribes to an event on the bus', function(done) {
var bus = node.openBus();
var block;
bus.subscribe('db/block');
bus.on('db/block', function(data) {
bus.unsubscribe('db/block');
data.should.be.equal(block);
done();
var blockExpected;
var blockReceived;
bus.subscribe('bitcoind/hashblock');
bus.on('bitcoind/hashblock', function(data) {
bus.unsubscribe('bitcoind/hashblock');
if (blockExpected) {
data.should.be.equal(blockExpected);
done();
} else {
blockReceived = data;
}
});
client.generate(1, function(err, response) {
if (err) {
throw err;
}
block = response.result[0];
if (blockReceived) {
blockReceived.should.be.equal(response.result[0]);
done();
} else {
blockExpected = response.result[0];
}
});
});
});
@ -237,20 +146,37 @@ describe('Node Functionality', function() {
describe('Address Functionality', function() {
var address;
var unspentOutput;
before(function() {
before(function(done) {
this.timeout(10000);
address = testKey.toAddress(regtest).toString();
});
it('should be able to get the balance of the test address', function(done) {
node.services.address.getBalance(address, false, function(err, balance) {
var startHeight = node.services.bitcoind.height;
node.services.bitcoind.on('tip', function(height) {
if (height === startHeight + 3) {
done();
}
});
client.sendToAddress(testKey.toAddress(regtest).toString(), 10, function(err) {
if (err) {
throw err;
}
balance.should.equal(10 * 1e8);
client.generate(3, function(err) {
if (err) {
throw err;
}
});
});
});
it('should be able to get the balance of the test address', function(done) {
node.getAddressBalance(address, false, function(err, data) {
if (err) {
throw err;
}
data.balance.should.equal(10 * 1e8);
done();
});
});
it('can get unspent outputs for address', function(done) {
node.services.address.getUnspentOutputs(address, false, function(err, results) {
node.getAddressUnspentOutputs(address, false, function(err, results) {
if (err) {
throw err;
}
@ -265,7 +191,7 @@ describe('Node Functionality', function() {
to: 10,
queryMempool: false
};
node.services.address.getAddressHistory(address, options, function(err, results) {
node.getAddressHistory(address, options, function(err, results) {
if (err) {
throw err;
}
@ -278,9 +204,8 @@ describe('Node Functionality', function() {
info.addresses[address].inputIndexes.should.deep.equal([]);
info.satoshis.should.equal(10 * 1e8);
info.confirmations.should.equal(3);
info.timestamp.should.be.a('number');
info.fees.should.be.within(950, 970);
info.tx.should.be.an.instanceof(Transaction);
info.tx.blockTimestamp.should.be.a('number');
info.tx.feeSatoshis.should.be.within(950, 4000);
done();
});
});
@ -288,16 +213,16 @@ describe('Node Functionality', function() {
var options = {
queryMempool: false
};
node.services.address.getAddressSummary(address, options, function(err, results) {
node.getAddressSummary(address, options, function(err, results) {
if (err) {
throw err;
}
results.totalReceived.should.equal(1000000000);
results.totalSpent.should.equal(0);
results.balance.should.equal(1000000000);
results.unconfirmedBalance.should.equal(0);
should.not.exist(results.unconfirmedBalance);
results.appearances.should.equal(1);
results.unconfirmedAppearances.should.equal(0);
should.not.exist(results.unconfirmedAppearances);
results.txids.length.should.equal(1);
done();
});
@ -316,10 +241,20 @@ describe('Node Functionality', function() {
var address5;
var testKey6;
var address6;
var tx2Amount;
var tx2Hash;
before(function(done) {
/* jshint maxstatements: 50 */
// Finished once all blocks have been mined
var startHeight = node.services.bitcoind.height;
node.services.bitcoind.on('tip', function(height) {
if (height === startHeight + 5) {
done();
}
});
testKey2 = bitcore.PrivateKey.fromWIF('cNfF4jXiLHQnFRsxaJyr2YSGcmtNYvxQYSakNhuDGxpkSzAwn95x');
address2 = testKey2.toAddress(regtest).toString();
@ -347,8 +282,6 @@ describe('Node Functionality', function() {
unspentOutputSpentTxId = tx.id;
node.services.bitcoind.sendTransaction(tx.serialize());
function mineBlock(next) {
client.generate(1, function(err, response) {
if (err) {
@ -359,13 +292,18 @@ describe('Node Functionality', function() {
});
}
client.generate(1, function(err, response) {
node.sendTransaction(tx.serialize(), function(err, hash) {
if (err) {
throw err;
return done(err);
}
should.exist(response);
node.once('synced', function() {
node.services.address.getUnspentOutputs(address, false, function(err, results) {
client.generate(1, function(err, response) {
if (err) {
throw err;
}
should.exist(response);
node.getAddressUnspentOutputs(address, false, function(err, results) {
/* jshint maxstatements: 50 */
if (err) {
throw err;
@ -375,28 +313,42 @@ describe('Node Functionality', function() {
async.series([
function(next) {
var tx2 = new Transaction();
tx2Amount = results[0].satoshis - 10000;
tx2.from(results[0]);
tx2.to(address2, results[0].satoshis - 10000);
tx2.to(address2, tx2Amount);
tx2.change(address);
tx2.sign(testKey);
node.services.bitcoind.sendTransaction(tx2.serialize());
mineBlock(next);
tx2Hash = tx2.hash;
node.sendTransaction(tx2.serialize(), function(err) {
if (err) {
return next(err);
}
mineBlock(next);
});
}, function(next) {
var tx3 = new Transaction();
tx3.from(results[1]);
tx3.to(address3, results[1].satoshis - 10000);
tx3.change(address);
tx3.sign(testKey);
node.services.bitcoind.sendTransaction(tx3.serialize());
mineBlock(next);
node.sendTransaction(tx3.serialize(), function(err) {
if (err) {
return next(err);
}
mineBlock(next);
});
}, function(next) {
var tx4 = new Transaction();
tx4.from(results[2]);
tx4.to(address4, results[2].satoshis - 10000);
tx4.change(address);
tx4.sign(testKey);
node.services.bitcoind.sendTransaction(tx4.serialize());
mineBlock(next);
node.sendTransaction(tx4.serialize(), function(err) {
if (err) {
return next(err);
}
mineBlock(next);
});
}, function(next) {
var tx5 = new Transaction();
tx5.from(results[3]);
@ -405,19 +357,22 @@ describe('Node Functionality', function() {
tx5.to(address6, results[4].satoshis - 10000);
tx5.change(address);
tx5.sign(testKey);
node.services.bitcoind.sendTransaction(tx5.serialize());
mineBlock(next);
node.sendTransaction(tx5.serialize(), function(err) {
if (err) {
return next(err);
}
mineBlock(next);
});
}
], function(err) {
if (err) {
throw err;
}
node.once('synced', function() {
done();
});
});
});
});
});
});
@ -431,22 +386,23 @@ describe('Node Functionality', function() {
address6
];
var options = {};
node.services.address.getAddressHistory(addresses, options, function(err, results) {
node.getAddressHistory(addresses, options, function(err, results) {
if (err) {
throw err;
}
results.totalCount.should.equal(4);
var history = results.items;
history.length.should.equal(4);
history[0].height.should.equal(157);
history[0].tx.height.should.equal(159);
history[0].confirmations.should.equal(1);
history[1].height.should.equal(156);
history[1].tx.height.should.equal(158);
should.exist(history[1].addresses[address4]);
history[2].height.should.equal(155);
history[2].tx.height.should.equal(157);
should.exist(history[2].addresses[address3]);
history[3].height.should.equal(154);
history[3].tx.height.should.equal(156);
should.exist(history[3].addresses[address2]);
history[3].satoshis.should.equal(99990000);
history[3].satoshis.should.equal(tx2Amount);
history[3].tx.hash.should.equal(tx2Hash);
history[3].confirmations.should.equal(4);
done();
});
@ -461,20 +417,20 @@ describe('Node Functionality', function() {
address6
];
var options = {
start: 157,
end: 156
start: 158,
end: 157
};
node.services.address.getAddressHistory(addresses, options, function(err, results) {
node.getAddressHistory(addresses, options, function(err, results) {
if (err) {
throw err;
}
results.totalCount.should.equal(2);
var history = results.items;
history.length.should.equal(2);
history[0].height.should.equal(157);
history[0].confirmations.should.equal(1);
history[1].height.should.equal(156);
should.exist(history[1].addresses[address4]);
history[0].tx.height.should.equal(158);
history[0].confirmations.should.equal(2);
history[1].tx.height.should.equal(157);
should.exist(history[1].addresses[address3]);
done();
});
});
@ -488,18 +444,18 @@ describe('Node Functionality', function() {
address6
];
var options = {
start: 155,
end: 154
start: 157,
end: 156
};
node.services.address.getAddressHistory(addresses, options, function(err, results) {
node.getAddressHistory(addresses, options, function(err, results) {
if (err) {
throw err;
}
results.totalCount.should.equal(2);
var history = results.items;
history.length.should.equal(2);
history[0].height.should.equal(155);
history[1].height.should.equal(154);
history[0].tx.height.should.equal(157);
history[1].tx.height.should.equal(156);
done();
});
});
@ -516,16 +472,16 @@ describe('Node Functionality', function() {
from: 0,
to: 3
};
node.services.address.getAddressHistory(addresses, options, function(err, results) {
node.getAddressHistory(addresses, options, function(err, results) {
if (err) {
throw err;
}
results.totalCount.should.equal(4);
var history = results.items;
history.length.should.equal(3);
history[0].height.should.equal(157);
history[0].tx.height.should.equal(159);
history[0].confirmations.should.equal(1);
history[1].height.should.equal(156);
history[1].tx.height.should.equal(158);
should.exist(history[1].addresses[address4]);
done();
});
@ -536,32 +492,32 @@ describe('Node Functionality', function() {
address
];
var options = {};
node.services.address.getAddressHistory(addresses, options, function(err, results) {
node.getAddressHistory(addresses, options, function(err, results) {
if (err) {
throw err;
}
results.totalCount.should.equal(6);
var history = results.items;
history.length.should.equal(6);
history[0].height.should.equal(157);
history[0].tx.height.should.equal(159);
history[0].addresses[address].inputIndexes.should.deep.equal([0, 1]);
history[0].addresses[address].outputIndexes.should.deep.equal([2]);
history[0].confirmations.should.equal(1);
history[1].height.should.equal(156);
history[2].height.should.equal(155);
history[3].height.should.equal(154);
history[4].height.should.equal(153);
history[1].tx.height.should.equal(158);
history[2].tx.height.should.equal(157);
history[3].tx.height.should.equal(156);
history[4].tx.height.should.equal(155);
history[4].satoshis.should.equal(-10000);
history[4].addresses[address].outputIndexes.should.deep.equal([0, 1, 2, 3, 4]);
history[4].addresses[address].inputIndexes.should.deep.equal([0]);
history[5].height.should.equal(150);
history[5].tx.height.should.equal(152);
history[5].satoshis.should.equal(10 * 1e8);
done();
});
});
it('summary for an address (sending and receiving)', function(done) {
node.services.address.getAddressSummary(address, {}, function(err, results) {
node.getAddressSummary(address, {}, function(err, results) {
if (err) {
throw err;
}
@ -582,7 +538,7 @@ describe('Node Functionality', function() {
address
];
var options = {};
node.services.address.getAddressHistory(addresses, options, function(err, results) {
node.getAddressHistory(addresses, options, function(err, results) {
if (err) {
throw err;
}
@ -597,13 +553,13 @@ describe('Node Functionality', function() {
from: 0,
to: 1
};
node.services.address.getAddressHistory(address, options, function(err, results) {
node.getAddressHistory(address, options, function(err, results) {
if (err) {
throw err;
}
var history = results.items;
history.length.should.equal(1);
history[0].height.should.equal(157);
history[0].tx.height.should.equal(159);
done();
});
});
@ -612,13 +568,13 @@ describe('Node Functionality', function() {
from: 1,
to: 2
};
node.services.address.getAddressHistory(address, options, function(err, results) {
node.getAddressHistory(address, options, function(err, results) {
if (err) {
throw err;
}
var history = results.items;
history.length.should.equal(1);
history[0].height.should.equal(156);
history[0].tx.height.should.equal(158);
done();
});
});
@ -627,13 +583,13 @@ describe('Node Functionality', function() {
from: 2,
to: 3
};
node.services.address.getAddressHistory(address, options, function(err, results) {
node.getAddressHistory(address, options, function(err, results) {
if (err) {
throw err;
}
var history = results.items;
history.length.should.equal(1);
history[0].height.should.equal(155);
history[0].tx.height.should.equal(157);
done();
});
});
@ -642,13 +598,13 @@ describe('Node Functionality', function() {
from: 3,
to: 4
};
node.services.address.getAddressHistory(address, options, function(err, results) {
node.getAddressHistory(address, options, function(err, results) {
if (err) {
throw err;
}
var history = results.items;
history.length.should.equal(1);
history[0].height.should.equal(154);
history[0].tx.height.should.equal(156);
done();
});
});
@ -657,13 +613,13 @@ describe('Node Functionality', function() {
from: 4,
to: 5
};
node.services.address.getAddressHistory(address, options, function(err, results) {
node.getAddressHistory(address, options, function(err, results) {
if (err) {
throw err;
}
var history = results.items;
history.length.should.equal(1);
history[0].height.should.equal(153);
history[0].tx.height.should.equal(155);
history[0].satoshis.should.equal(-10000);
history[0].addresses[address].outputIndexes.should.deep.equal([0, 1, 2, 3, 4]);
history[0].addresses[address].inputIndexes.should.deep.equal([0]);
@ -675,13 +631,13 @@ describe('Node Functionality', function() {
from: 5,
to: 6
};
node.services.address.getAddressHistory(address, options, function(err, results) {
node.getAddressHistory(address, options, function(err, results) {
if (err) {
throw err;
}
var history = results.items;
history.length.should.equal(1);
history[0].height.should.equal(150);
history[0].tx.height.should.equal(152);
history[0].satoshis.should.equal(10 * 1e8);
done();
});
@ -693,7 +649,7 @@ describe('Node Functionality', function() {
describe('Mempool Index', function() {
var unspentOutput;
before(function(done) {
node.services.address.getUnspentOutputs(address, false, function(err, results) {
node.getAddressUnspentOutputs(address, false, function(err, results) {
if (err) {
throw err;
}
@ -704,23 +660,20 @@ describe('Node Functionality', function() {
});
it('will update the mempool index after new tx', function(done) {
var memAddress = bitcore.PrivateKey().toAddress(node.network).toString();
var tx = new Transaction();
tx.from(unspentOutput);
tx.to(address, unspentOutput.satoshis - 1000);
tx.to(memAddress, unspentOutput.satoshis - 1000);
tx.fee(1000);
tx.sign(testKey);
node.services.bitcoind.sendTransaction(tx.serialize());
setImmediate(function() {
var addrObj = encoding.getAddressInfo(address);
node.services.address._getOutputsMempool(address, addrObj.hashBuffer,
addrObj.hashTypeBuffer, function(err, outs) {
node.services.bitcoind.sendTransaction(tx.serialize(), function(err, hash) {
node.getAddressTxids(memAddress, {}, function(err, txids) {
if (err) {
throw err;
return done(err);
}
outs.length.should.equal(1);
txids.length.should.equal(1);
txids[0].should.equal(hash);
done();
});
});
@ -728,72 +681,78 @@ describe('Node Functionality', function() {
});
describe('#getInputForOutput(db)', function() {
it('will get the input txid and input index', function(done) {
var txid = outputForIsSpentTest1.txid;
var outputIndex = outputForIsSpentTest1.outputIndex;
var options = {
queryMempool: true
};
node.services.address.getInputForOutput(txid, outputIndex, options, function(err, result) {
result.inputTxId.should.equal(unspentOutputSpentTxId);
result.inputIndex.should.equal(0);
done();
});
});
});
describe('#isSpent and #getInputForOutput(mempool)', function() {
var spentOutput;
var spentOutputInputTxId;
it('will return true if an input is spent in a confirmed transaction', function(done) {
var txid = outputForIsSpentTest1.txid;
var outputIndex = outputForIsSpentTest1.outputIndex;
var result = node.services.bitcoind.isSpent(txid, outputIndex);
result.should.equal(true);
done();
});
//CCoinsViewMemPool only checks for spent outputs that are not the mempool
it('will correctly return false for an input that is spent in an unconfirmed transaction', function(done) {
node.services.address.getUnspentOutputs(address, false, function(err, results) {
if (err) {
throw err;
}
var unspentOutput = results[0];
var tx = new Transaction();
tx.from(unspentOutput);
tx.to(address, unspentOutput.satoshis - 1000);
tx.fee(1000);
tx.sign(testKey);
node.services.bitcoind.sendTransaction(tx.serialize());
spentOutput = unspentOutput;
spentOutputInputTxId = tx.hash;
setImmediate(function() {
var result = node.services.bitcoind.isSpent(unspentOutput.txid, unspentOutput.outputIndex);
result.should.equal(false);
done();
});
});
});
it('will get the input txid and input index (mempool)', function(done) {
var txid = spentOutput.txid;
var outputIndex = spentOutput.outputIndex;
var options = {
queryMempool: true
};
node.services.address.getInputForOutput(txid, outputIndex, options, function(err, result) {
result.inputTxId.should.equal(spentOutputInputTxId);
result.inputIndex.should.equal(0);
done();
});
});
});
});
describe('Orphaned Transactions', function() {
this.timeout(8000);
var orphanedTransaction;
before(function(done) {
var count;
var invalidatedBlockHash;
async.series([
function(next) {
client.sendToAddress(testKey.toAddress(regtest).toString(), 10, function(err) {
if (err) {
return next(err);
}
client.generate(1, next);
});
},
function(next) {
client.getBlockCount(function(err, response) {
if (err) {
return next(err);
}
count = response.result;
next();
});
},
function(next) {
client.getBlockHash(count, function(err, response) {
if (err) {
return next(err);
}
invalidatedBlockHash = response.result;
next();
});
},
function(next) {
client.getBlock(invalidatedBlockHash, function(err, response) {
if (err) {
return next(err);
}
orphanedTransaction = response.result.tx[1];
should.exist(orphanedTransaction);
next();
});
},
function(next) {
client.invalidateBlock(invalidatedBlockHash, next);
}
], function(err) {
if (err) {
throw err;
}
done();
});
});
it('will not show confirmation count for orphaned transaction', function(done) {
// This test verifies that in the situation that the transaction is not in the mempool and
// is included in an orphaned block transaction index that the confirmation count will be unconfirmed.
node.getDetailedTransaction(orphanedTransaction, function(err, data) {
if (err) {
return done(err);
}
should.exist(data);
should.exist(data.height);
data.height.should.equal(-1);
done();
});
});
});
});

View File

@ -1,17 +1,16 @@
'use strict';
// To run the tests: $ mocha -R spec regtest/p2p.js
var path = require('path');
var index = require('..');
var log = index.log;
if (process.env.BITCORENODE_ENV !== 'test') {
log.info('Please set the environment variable BITCORENODE_ENV=test and make sure bindings are compiled for testing');
process.exit();
}
var p2p = require('bitcore-p2p');
var Peer = p2p.Peer;
var Messages = p2p.Messages;
var chai = require('chai');
var bitcore = require('bitcore-lib');
var bitcore = require('bitcore-lib-zcash');
var Transaction = bitcore.Transaction;
var BN = bitcore.crypto.BN;
var async = require('async');
@ -40,36 +39,23 @@ describe('P2P Functionality', function() {
before(function(done) {
this.timeout(100000);
// Add the regtest network
bitcore.Networks.remove(bitcore.Networks.testnet);
bitcore.Networks.add({
name: 'regtest',
alias: 'regtest',
pubkeyhash: 0x6f,
privatekey: 0xef,
scripthash: 0xc4,
xpubkey: 0x043587cf,
xprivkey: 0x04358394,
networkMagic: 0xfabfb5da,
port: 18444,
dnsSeeds: [ ]
});
// enable regtest
bitcore.Networks.enableRegtest();
var regtestNetwork = bitcore.Networks.get('regtest');
var datadir = __dirname + '/data';
rimraf(datadir + '/regtest', function(err) {;
rimraf(datadir + '/regtest', function(err) {
if (err) {
throw err;
}
bitcoind = require('../').services.Bitcoin({
node: {
spawn: {
datadir: datadir,
network: {
name: 'regtest'
}
exec: path.resolve(__dirname, '../bin/zcashd')
},
node: {
network: bitcore.Networks.testnet
}
});
@ -77,15 +63,18 @@ describe('P2P Functionality', function() {
log.error('error="%s"', err.message);
});
log.info('Waiting for Bitcoin Core to initialize...');
log.info('Waiting for Zcash to initialize...');
bitcoind.start(function() {
log.info('Bitcoind started');
bitcoind.start(function(err) {
if (err) {
throw err;
}
log.info('Zcashd started');
client = new BitcoinRPC({
protocol: 'https',
protocol: 'http',
host: '127.0.0.1',
port: 18332,
port: 30331,
user: 'bitcoin',
pass: 'local321',
rejectUnauthorized: false
@ -174,6 +163,7 @@ describe('P2P Functionality', function() {
this.timeout(20000);
peer.on('disconnect', function() {
log.info('Peer disconnected');
bitcoind.node.stopping = true;
bitcoind.stop(function(err, result) {
done();
});
@ -186,13 +176,11 @@ describe('P2P Functionality', function() {
var usedTxs = {};
bitcoind.on('tx', function(result) {
var txFromResult = new Transaction().fromBuffer(result.buffer);
bitcoind.on('tx', function(buffer) {
var txFromResult = new Transaction().fromBuffer(buffer);
var tx = usedTxs[txFromResult.id];
should.exist(tx);
result.buffer.toString('hex').should.equal(tx.serialize());
result.hash.should.equal(tx.hash);
result.mempool.should.equal(true);
buffer.toString('hex').should.equal(tx.serialize());
delete usedTxs[tx.id];
if (Object.keys(usedTxs).length === 0) {
done();

119
scripts/download Executable file
View File

@ -0,0 +1,119 @@
#!/bin/bash
set -e
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.."
platform=`uname -a | awk '{print tolower($1)}'`
arch=`uname -m`
version="0.12.1"
url="https://github.com/bitpay/bitcoin/releases/download"
tag="v0.12.1-bitcore-2"
if [ "${platform}" == "linux" ]; then
if [ "${arch}" == "x86_64" ]; then
tarball_name="bitcoin-${version}-linux64.tar.gz"
elif [ "${arch}" == "x86_32" ]; then
tarball_name="bitcoin-${version}-linux32.tar.gz"
fi
elif [ "${platform}" == "darwin" ]; then
tarball_name="bitcoin-${version}-osx64.tar.gz"
else
echo "Bitcoin binary distribution not available for platform and architecture"
exit -1
fi
binary_url="${url}/${tag}/${tarball_name}"
shasums_url="${url}/${tag}/SHA256SUMS.asc"
download_bitcoind() {
cd "${root_dir}/bin"
echo "Downloading bitcoin: ${binary_url}"
is_curl=true
if hash curl 2>/dev/null; then
curl --fail -I $binary_url >/dev/null 2>&1
else
is_curl=false
wget --server-response --spider $binary_url >/dev/null 2>&1
fi
if test $? -eq 0; then
if [ "${is_curl}" = true ]; then
curl -L $binary_url > $tarball_name
curl -L $shasums_url > SHA256SUMS.asc
else
wget $binary_url
wget $shasums_url
fi
if test -e "${tarball_name}"; then
echo "Unpacking bitcoin distribution"
tar -xvzf $tarball_name
if test $? -eq 0; then
ln -sf "bitcoin-${version}/bin/bitcoind"
return;
fi
fi
fi
echo "Bitcoin binary distribution could not be downloaded"
exit -1
}
verify_download() {
echo "Verifying signatures of bitcoin download"
gpg --verify "${root_dir}/bin/SHA256SUMS.asc"
if hash shasum 2>/dev/null; then
shasum_cmd="shasum -a 256"
else
shasum_cmd="sha256sum"
fi
download_sha=$(${shasum_cmd} "${root_dir}/bin/${tarball_name}" | awk '{print $1}')
expected_sha=$(cat "${root_dir}/bin/SHA256SUMS.asc" | grep "${tarball_name}" | awk '{print $1}')
echo "Checksum (download): ${download_sha}"
echo "Checksum (verified): ${expected_sha}"
if [ "${download_sha}" != "${expected_sha}" ]; then
echo -e "\033[1;31mChecksums did NOT match!\033[0m\n"
exit 1
else
echo -e "\033[1;32mChecksums matched!\033[0m\n"
fi
}
download=1
verify=0
if [ "${SKIP_BITCOIN_DOWNLOAD}" = 1 ]; then
download=0;
fi
if [ "${VERIFY_BITCOIN_DOWNLOAD}" = 1 ]; then
verify=1;
fi
while [ -n "$1" ]; do
param="$1"
value="$2"
case $param in
--skip-bitcoin-download)
download=0
;;
--verify-bitcoin-download)
verify=1
;;
esac
shift
done
if [ "${download}" = 1 ]; then
download_bitcoind
fi
if [ "${verify}" = 1 ]; then
verify_download
fi
exit 0

8
scripts/regtest Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
set -e
_mocha -R spec regtest/p2p.js
_mocha -R spec regtest/bitcoind.js
_mocha -R spec regtest/cluster.js
_mocha -R spec regtest/node.js

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +0,0 @@
#include "main.h"
#include "addrman.h"
#include "alert.h"
#include "base58.h"
#include "init.h"
#include "noui.h"
#include "rpcserver.h"
#include "txdb.h"
#include <boost/thread.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include "nan.h"
#include "scheduler.h"
#include "core_io.h"
#include "script/bitcoinconsensus.h"
#include "consensus/validation.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#endif

View File

@ -1,17 +0,0 @@
'use strict';
var should = require('chai').should();
var path = require('path');
var getTarballName = require('../../bin/get-tarball-name');
describe('#getTarballName', function() {
it('will return the expected tarball name', function() {
var name = getTarballName();
var version = require(path.resolve(__dirname + '../../../package.json')).version;
var platform = process.platform;
var arch = process.arch;
var abi = process.versions.modules;
var expected = 'libbitcoind-' + version + '-node' + abi + '-' + platform + '-' + arch + '.tgz';
name.should.equal(expected);
});
});

View File

@ -1,12 +1,16 @@
'use strict';
var sinon = require('sinon');
var Service = require('../lib/service');
var BitcoreNode = require('../lib/node');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var should = require('chai').should();
var index = require('../lib');
var log = index.log;
var TestService = function(options) {
this.node = options.node;
}
};
util.inherits(TestService, Service);
TestService.dependencies = [];
@ -40,6 +44,14 @@ TestService.prototype.unsubscribe = function(name, emitter) {
describe('Bus Functionality', function() {
var sandbox = sinon.sandbox.create();
beforeEach(function() {
sandbox.stub(log, 'info');
});
afterEach(function() {
sandbox.restore();
});
it('should subscribe to testEvent', function(done) {
var node = new BitcoreNode({
datadir: './',

View File

@ -1,17 +1,23 @@
#testnet=1
#irc=0
#upnp=0
upnp=0
server=1
whitelist=127.0.0.1
txindex=1
addressindex=1
timestampindex=1
spentindex=1
dbcache=8192
checkblocks=144
maxuploadtarget=1024
zmqpubrawtx=tcp://127.0.0.1:28332
zmqpubhashblock=tcp://127.0.0.1:28332
# listen on different ports
port=20000
rpcport=50001
rpcallowip=127.0.0.1
rpcuser=bitcoin
rpcpassword=local321

View File

@ -0,0 +1,12 @@
server=1
whitelist=127.0.0.1
txindex=1
addressindex=1
timestampindex=1
spentindex=1
zmqpubrawtx=tcp://127.0.0.1:28332
zmqpubhashblock=tcp://127.0.0.1:28332
rpcallowip=127.0.0.1
rpcuser=bitcoin
rpcpassword=local321
uacomment=bitcore

View File

@ -1,19 +1,12 @@
'use strict';
var should = require('chai').should();
var index = require('..');
describe('Index', function() {
describe('#nodeVersionCheck', function() {
it('will throw informative error message with incompatible Node.js version 4.1.2', function() {
(function() {
index.nodeVersionCheck('4.1.2', '>=0.12.0 <1');
}).should.throw('Node.js version');
});
it('will throw informative error message with incompatible Node.js version 0.10.40', function() {
(function() {
index.nodeVersionCheck('4.1.2', '>=0.12.0 <1');
}).should.throw('Node.js version');
});
describe('Index Exports', function() {
it('will export bitcore-lib', function() {
var bitcore = require('../');
should.exist(bitcore.lib);
should.exist(bitcore.lib.Transaction);
should.exist(bitcore.lib.Block);
});
});

83
test/logger.unit.js Normal file
View File

@ -0,0 +1,83 @@
'use strict';
var sinon = require('sinon');
var chai = require('chai');
var should = chai.should();
var Logger = require('../lib/logger');
describe('Logger', function() {
var sandbox = sinon.sandbox.create();
afterEach(function() {
sandbox.restore();
});
it('will instatiate without options', function() {
var logger = new Logger();
should.exist(logger);
logger.formatting.should.equal(true);
});
it('will instatiate with formatting option', function() {
var logger = new Logger({
formatting: false
});
logger.formatting.should.equal(false);
var logger2 = new Logger({
formatting: true
});
logger2.formatting.should.equal(true);
});
it('will log with formatting', function() {
var logger = new Logger({formatting: true});
sandbox.stub(console, 'info');
logger.info('Test info log');
console.info.callCount.should.equal(1);
console.info.restore();
sandbox.stub(console, 'error');
logger.error(new Error('Test error log'));
console.error.callCount.should.equal(1);
console.error.restore();
sandbox.stub(console, 'log');
logger.debug('Test debug log');
console.log.callCount.should.equal(1);
console.log.restore();
sandbox.stub(console, 'warn');
logger.warn('Test warn log');
console.warn.callCount.should.equal(1);
console.warn.restore();
});
it('will log without formatting', function() {
var logger = new Logger({formatting: false});
sandbox.stub(console, 'info');
logger.info('Test info log');
console.info.callCount.should.equal(1);
should.not.exist(console.info.args[0][0].match(/^\[/));
console.info.restore();
sandbox.stub(console, 'error');
logger.error(new Error('Test error log'));
console.error.callCount.should.equal(1);
console.error.args[0][0].should.be.instanceof(Error);
console.error.restore();
sandbox.stub(console, 'log');
logger.debug('Test debug log');
console.log.callCount.should.equal(1);
should.equal(console.log.args[0][0].match(/^\[/), null);
console.log.restore();
sandbox.stub(console, 'warn');
logger.warn('Test warn log');
console.warn.callCount.should.equal(1);
should.equal(console.warn.args[0][0].match(/^\[/), null);
console.warn.restore();
});
});

View File

@ -2,17 +2,17 @@
var should = require('chai').should();
var sinon = require('sinon');
var bitcore = require('bitcore-lib');
var bitcore = require('bitcore-lib-zcash');
var Networks = bitcore.Networks;
var proxyquire = require('proxyquire');
var util = require('util');
var BaseService = require('../lib/service');
var index = require('../lib');
var log = index.log;
describe('Bitcore Node', function() {
var baseConfig = {
datadir: 'testdir'
};
var baseConfig = {};
var Node;
@ -21,29 +21,9 @@ describe('Bitcore Node', function() {
Node.prototype._loadConfiguration = sinon.spy();
Node.prototype._initialize = sinon.spy();
});
after(function() {
var regtest = Networks.get('regtest');
if (regtest) {
Networks.remove(regtest);
}
// restore testnet
Networks.add({
name: 'testnet',
alias: 'testnet',
pubkeyhash: 0x6f,
privatekey: 0xef,
scripthash: 0xc4,
xpubkey: 0x043587cf,
xprivkey: 0x04358394,
networkMagic: 0x0b110907,
port: 18333,
dnsSeeds: [
'testnet-seed.bitcoin.petertodd.org',
'testnet-seed.bluematt.me',
'testnet-seed.alexykot.me',
'testnet-seed.bitcoin.schildbach.de'
],
});
Networks.disableRegtest();
});
describe('@constructor', function() {
@ -54,7 +34,6 @@ describe('Bitcore Node', function() {
});
it('will set properties', function() {
var config = {
datadir: 'testdir',
services: [
{
name: 'test1',
@ -69,11 +48,15 @@ describe('Bitcore Node', function() {
node._unloadedServices[0].name.should.equal('test1');
node._unloadedServices[0].module.should.equal(TestService);
node.network.should.equal(Networks.defaultNetwork);
var node2 = TestNode(config);
node2._unloadedServices.length.should.equal(1);
node2._unloadedServices[0].name.should.equal('test1');
node2._unloadedServices[0].module.should.equal(TestService);
node2.network.should.equal(Networks.defaultNetwork);
});
it('will set network to testnet', function() {
var config = {
network: 'testnet',
datadir: 'testdir',
services: [
{
name: 'test1',
@ -89,7 +72,6 @@ describe('Bitcore Node', function() {
it('will set network to regtest', function() {
var config = {
network: 'regtest',
datadir: 'testdir',
services: [
{
name: 'test1',
@ -104,6 +86,26 @@ describe('Bitcore Node', function() {
should.exist(regtest);
node.network.should.equal(regtest);
});
it('will be able to disable log formatting', function() {
var config = {
network: 'regtest',
services: [
{
name: 'test1',
module: TestService
}
],
formatLogs: false
};
var TestNode = proxyquire('../lib/node', {});
var node = new TestNode(config);
node.log.formatting.should.equal(false);
var TestNode = proxyquire('../lib/node', {});
config.formatLogs = true;
var node2 = new TestNode(config);
node2.log.formatting.should.equal(true);
});
});
describe('#openBus', function() {
@ -112,6 +114,11 @@ describe('Bitcore Node', function() {
var bus = node.openBus();
bus.node.should.equal(node);
});
it('will use remoteAddress config option', function() {
var node = new Node(baseConfig);
var bus = node.openBus({remoteAddress: '127.0.0.1'});
bus.remoteAddress.should.equal('127.0.0.1');
});
});
describe('#getAllAPIMethods', function() {
@ -132,6 +139,21 @@ describe('Bitcore Node', function() {
var methods = node.getAllAPIMethods();
methods.should.deep.equal(['db1', 'db2', 'mda1', 'mda2', 'mdb1', 'mdb2']);
});
it('will handle service without getAPIMethods defined', function() {
var node = new Node(baseConfig);
node.services = {
db: {
getAPIMethods: sinon.stub().returns(['db1', 'db2']),
},
service1: {},
service2: {
getAPIMethods: sinon.stub().returns(['mdb1', 'mdb2'])
}
};
var methods = node.getAllAPIMethods();
methods.should.deep.equal(['db1', 'db2', 'mdb1', 'mdb2']);
});
});
describe('#getAllPublishEvents', function() {
@ -151,6 +173,20 @@ describe('Bitcore Node', function() {
var events = node.getAllPublishEvents();
events.should.deep.equal(['db1', 'db2', 'mda1', 'mda2', 'mdb1', 'mdb2']);
});
it('will handle service without getPublishEvents defined', function() {
var node = new Node(baseConfig);
node.services = {
db: {
getPublishEvents: sinon.stub().returns(['db1', 'db2']),
},
service1: {},
service2: {
getPublishEvents: sinon.stub().returns(['mdb1', 'mdb2'])
}
};
var events = node.getAllPublishEvents();
events.should.deep.equal(['db1', 'db2', 'mdb1', 'mdb2']);
});
});
describe('#getServiceOrder', function() {
@ -191,12 +227,20 @@ describe('Bitcore Node', function() {
});
describe('#_startService', function() {
var sandbox = sinon.sandbox.create();
beforeEach(function() {
sandbox.stub(log, 'info');
});
afterEach(function() {
sandbox.restore();
});
it('will instantiate an instance and load api methods', function() {
var node = new Node(baseConfig);
function TestService() {}
util.inherits(TestService, BaseService);
TestService.prototype.start = sinon.stub().callsArg(0);
TestService.prototype.getData = function() {};
var getData = sinon.stub();
TestService.prototype.getData = getData;
TestService.prototype.getAPIMethods = function() {
return [
['getData', this, this.getData, 1]
@ -214,6 +258,35 @@ describe('Bitcore Node', function() {
TestService.prototype.start.callCount.should.equal(1);
should.exist(node.services.testservice);
should.exist(node.getData);
node.getData();
getData.callCount.should.equal(1);
});
});
it('will handle config not being set', function() {
var node = new Node(baseConfig);
function TestService() {}
util.inherits(TestService, BaseService);
TestService.prototype.start = sinon.stub().callsArg(0);
var getData = sinon.stub();
TestService.prototype.getData = getData;
TestService.prototype.getAPIMethods = function() {
return [
['getData', this, this.getData, 1]
];
};
var service = {
name: 'testservice',
module: TestService,
};
node._startService(service, function(err) {
if (err) {
throw err;
}
TestService.prototype.start.callCount.should.equal(1);
should.exist(node.services.testservice);
should.exist(node.getData);
node.getData();
getData.callCount.should.equal(1);
});
});
it('will give an error from start', function() {
@ -233,6 +306,13 @@ describe('Bitcore Node', function() {
});
describe('#start', function() {
var sandbox = sinon.sandbox.create();
beforeEach(function() {
sandbox.stub(log, 'info');
});
afterEach(function() {
sandbox.restore();
});
it('will call start for each service', function(done) {
var node = new Node(baseConfig);
@ -319,9 +399,62 @@ describe('Bitcore Node', function() {
});
});
it('will handle service with getAPIMethods undefined', function(done) {
var node = new Node(baseConfig);
function TestService() {}
util.inherits(TestService, BaseService);
TestService.prototype.start = sinon.stub().callsArg(0);
TestService.prototype.getData = function() {};
node.getServiceOrder = sinon.stub().returns([
{
name: 'test',
module: TestService,
config: {}
},
]);
node.start(function() {
TestService.prototype.start.callCount.should.equal(1);
done();
});
});
});
describe('#getNetworkName', function() {
afterEach(function() {
bitcore.Networks.disableRegtest();
});
it('it will return the network name for livenet', function() {
var node = new Node(baseConfig);
node.getNetworkName().should.equal('livenet');
});
it('it will return the network name for testnet', function() {
var baseConfig = {
network: 'testnet'
};
var node = new Node(baseConfig);
node.getNetworkName().should.equal('testnet');
});
it('it will return the network for regtest', function() {
var baseConfig = {
network: 'regtest'
};
var node = new Node(baseConfig);
node.getNetworkName().should.equal('regtest');
});
});
describe('#stop', function() {
var sandbox = sinon.sandbox.create();
beforeEach(function() {
sandbox.stub(log, 'info');
});
afterEach(function() {
sandbox.restore();
});
it('will call stop for each service', function(done) {
var node = new Node(baseConfig);
function TestService() {}

View File

@ -94,7 +94,7 @@ describe('#add', function() {
var callCount = 0;
var oldPackage = {
dependencies: {
'bitcore-lib': '^v0.13.7',
'bitcore-lib-zcash': '^v0.13.7',
'bitcore-node': '^v0.2.0'
}
};

View File

@ -33,7 +33,7 @@ describe('#create', function() {
if (err) {
throw err;
}
mkdirp(testDir + '/.bitcoin', function(err) {
mkdirp(testDir + '/.zcash', function(err) {
if (err) {
throw err;
}
@ -104,7 +104,7 @@ describe('#create', function() {
dirname: 'mynode3',
name: 'My Node 3',
isGlobal: true,
datadir: '../.bitcoin'
datadir: '../.zcash'
}, function(err) {
if (err) {
throw err;
@ -139,7 +139,7 @@ describe('#create', function() {
dirname: 'mynode4',
name: 'My Node 4',
isGlobal: false,
datadir: '../.bitcoin'
datadir: '../.zcash'
}, function(err) {
should.exist(err);
err.message.should.equal('There was an error installing dependencies.');

View File

@ -1,6 +1,7 @@
'use strict';
var should = require('chai').should();
var path = require('path');
var defaultBaseConfig = require('../../lib/scaffold/default-base-config');
describe('#defaultBaseConfig', function() {
@ -9,29 +10,19 @@ describe('#defaultBaseConfig', function() {
var home = process.env.HOME;
var info = defaultBaseConfig();
info.path.should.equal(cwd);
info.config.datadir.should.equal(home + '/.bitcoin');
info.config.network.should.equal('livenet');
info.config.port.should.equal(3001);
info.config.services.should.deep.equal(['bitcoind', 'db', 'address', 'web']);
info.config.services.should.deep.equal(['bitcoind', 'web']);
var bitcoind = info.config.servicesConfig.bitcoind;
bitcoind.spawn.datadir.should.equal(home + '/.zcash');
bitcoind.spawn.exec.should.equal(path.resolve(__dirname, '../../bin/zcashd'));
});
it('be able to specify a network', function() {
var cwd = process.cwd();
var home = process.env.HOME;
var info = defaultBaseConfig({network: 'testnet'});
info.path.should.equal(cwd);
info.config.datadir.should.equal(home + '/.bitcoin');
info.config.network.should.equal('testnet');
info.config.port.should.equal(3001);
info.config.services.should.deep.equal(['bitcoind', 'db', 'address', 'web']);
});
it('be able to specify a datadir', function() {
var cwd = process.cwd();
var home = process.env.HOME;
var info = defaultBaseConfig({datadir: './data2', network: 'testnet'});
info.path.should.equal(cwd);
info.config.datadir.should.equal('./data2');
info.config.network.should.equal('testnet');
info.config.port.should.equal(3001);
info.config.services.should.deep.equal(['bitcoind', 'db', 'address', 'web']);
info.config.servicesConfig.bitcoind.spawn.datadir.should.equal('./data2');
});
});

View File

@ -1,21 +1,29 @@
'use strict';
var path = require('path');
var should = require('chai').should();
var sinon = require('sinon');
var proxyquire = require('proxyquire');
describe('#defaultConfig', function() {
var expectedExecPath = path.resolve(__dirname, '../../bin/zcashd');
it('will return expected configuration', function() {
var config = JSON.stringify({
datadir: process.env.HOME + '/.bitcore/data',
network: 'livenet',
port: 3001,
services: [
'bitcoind',
'db',
'address',
'web'
]
],
servicesConfig: {
bitcoind: {
spawn: {
datadir: process.env.HOME + '/.bitcore/data',
exec: expectedExecPath
}
}
}
}, null, 2);
var defaultConfig = proxyquire('../../lib/scaffold/default-config', {
fs: {
@ -32,28 +40,35 @@ describe('#defaultConfig', function() {
sync: sinon.stub()
}
});
var cwd = process.cwd();
var home = process.env.HOME;
var info = defaultConfig();
info.path.should.equal(home + '/.bitcore');
info.config.datadir.should.equal(home + '/.bitcore/data');
info.config.network.should.equal('livenet');
info.config.port.should.equal(3001);
info.config.services.should.deep.equal(['bitcoind', 'db', 'address', 'web']);
info.config.services.should.deep.equal(['bitcoind', 'web']);
var bitcoind = info.config.servicesConfig.bitcoind;
should.exist(bitcoind);
bitcoind.spawn.datadir.should.equal(home + '/.bitcore/data');
bitcoind.spawn.exec.should.equal(expectedExecPath);
});
it('will include additional services', function() {
var config = JSON.stringify({
datadir: process.env.HOME + '/.bitcore/data',
network: 'livenet',
port: 3001,
services: [
'bitcoind',
'db',
'address',
'web',
'insight-api',
'insight-ui'
]
],
servicesConfig: {
bitcoind: {
spawn: {
datadir: process.env.HOME + '/.bitcore/data',
exec: expectedExecPath
}
}
}
}, null, 2);
var defaultConfig = proxyquire('../../lib/scaffold/default-config', {
fs: {
@ -75,16 +90,17 @@ describe('#defaultConfig', function() {
additionalServices: ['insight-api', 'insight-ui']
});
info.path.should.equal(home + '/.bitcore');
info.config.datadir.should.equal(home + '/.bitcore/data');
info.config.network.should.equal('livenet');
info.config.port.should.equal(3001);
info.config.services.should.deep.equal([
'bitcoind',
'db',
'address',
'web',
'insight-api',
'insight-ui'
]);
var bitcoind = info.config.servicesConfig.bitcoind;
should.exist(bitcoind);
bitcoind.spawn.datadir.should.equal(home + '/.bitcore/data');
bitcoind.spawn.exec.should.equal(expectedExecPath);
});
});

View File

@ -3,19 +3,33 @@
var should = require('chai').should();
var sinon = require('sinon');
var proxyquire = require('proxyquire');
var AddressService = require('../../lib/services/address');
var BitcoinService = require('../../lib/services/bitcoind');
var index = require('../../lib');
var log = index.log;
describe('#start', function() {
var sandbox = sinon.sandbox.create();
beforeEach(function() {
sandbox.stub(log, 'error');
});
afterEach(function() {
sandbox.restore();
});
describe('will dynamically create a node from a configuration', function() {
it('require each bitcore-node service with default config', function(done) {
var node;
var TestNode = function(options) {
options.services[0].should.deep.equal({
name: 'address',
module: AddressService,
config: {}
name: 'bitcoind',
module: BitcoinService,
config: {
spawn: {
datadir: './data'
}
}
});
};
TestNode.prototype.start = sinon.stub().callsArg(0);
@ -28,13 +42,21 @@ describe('#start', function() {
'../node': TestNode
});
starttest.registerExitHandlers = sinon.stub();
node = starttest({
path: __dirname,
config: {
services: [
'address'
'bitcoind'
],
datadir: './data'
servicesConfig: {
bitcoind: {
spawn: {
datadir: './data'
}
}
}
}
});
node.should.be.instanceof(TestNode);
@ -51,11 +73,13 @@ describe('#start', function() {
'../node': TestNode
});
starttest.cleanShutdown = sinon.stub();
starttest.registerExitHandlers = sinon.stub();
starttest({
path: __dirname,
config: {
services: [],
datadir: './testdir'
servicesConfig: {}
}
});
setImmediate(function() {
@ -67,10 +91,13 @@ describe('#start', function() {
var node;
var TestNode = function(options) {
options.services[0].should.deep.equal({
name: 'address',
module: AddressService,
name: 'bitcoind',
module: BitcoinService,
config: {
param: 'test'
param: 'test',
spawn: {
datadir: './data'
}
}
});
};
@ -83,19 +110,23 @@ describe('#start', function() {
var starttest = proxyquire('../../lib/scaffold/start', {
'../node': TestNode
});
starttest.registerExitHandlers = sinon.stub();
node = starttest({
path: __dirname,
config: {
services: [
'address'
'bitcoind'
],
servicesConfig: {
'address': {
param: 'test'
'bitcoind': {
param: 'test',
spawn: {
datadir: './data'
}
}
},
datadir: './data'
}
});
node.should.be.instanceof(TestNode);

View File

@ -8,6 +8,35 @@ var proxyquire = require('proxyquire');
var start = require('../../lib/scaffold/start');
describe('#start', function() {
describe('#checkConfigVersion2', function() {
var sandbox = sinon.sandbox.create();
beforeEach(function() {
sandbox.stub(console, 'warn');
});
afterEach(function() {
sandbox.restore();
});
it('will give true with "datadir" at root', function() {
var checkConfigVersion2 = proxyquire('../../lib/scaffold/start', {}).checkConfigVersion2;
var v2 = checkConfigVersion2({datadir: '/home/user/.bitcore/data', services: []});
v2.should.equal(true);
});
it('will give true with "address" service enabled', function() {
var checkConfigVersion2 = proxyquire('../../lib/scaffold/start', {}).checkConfigVersion2;
var v2 = checkConfigVersion2({services: ['address']});
v2.should.equal(true);
});
it('will give true with "db" service enabled', function() {
var checkConfigVersion2 = proxyquire('../../lib/scaffold/start', {}).checkConfigVersion2;
var v2 = checkConfigVersion2({services: ['db']});
v2.should.equal(true);
});
it('will give false without "datadir" at root and "address", "db" services disabled', function() {
var checkConfigVersion2 = proxyquire('../../lib/scaffold/start', {}).checkConfigVersion2;
var v2 = checkConfigVersion2({services: []});
v2.should.equal(false);
});
});
describe('#setupServices', function() {
var cwd = process.cwd();
var setupServices = proxyquire('../../lib/scaffold/start', {}).setupServices;
@ -96,34 +125,6 @@ describe('#start', function() {
}).should.throw('Could not load service');
});
});
describe('#registerSyncHandlers', function() {
it('will log the sync status at an interval', function(done) {
var log = {
info: sinon.stub()
};
var registerSyncHandlers = proxyquire('../../lib/scaffold/start', {
'../': {
log: log
}
}).registerSyncHandlers;
var node = new EventEmitter();
node.services = {
db: new EventEmitter()
};
node.services.db.tip = {
hash: 'hash',
__height: 10
};
registerSyncHandlers(node, 10);
node.emit('ready');
node.services.db.emit('addblock');
setTimeout(function() {
node.emit('synced');
log.info.callCount.should.be.within(3, 4);
done();
}, 35);
});
});
describe('#cleanShutdown', function() {
it('will call node stop and process exit', function() {
var log = {
@ -212,110 +213,6 @@ describe('#start', function() {
});
});
});
describe('#spawnChildProcess', function() {
it('should build the appropriate arguments to spawn a child process', function() {
var child = {
unref: function() {}
};
var _process = {
exit: function() {},
env: {
__bitcore_node: false
},
argv: [
'node',
'bitcore-node'
],
cwd: function(){return ''},
pid: 999,
execPath: '/tmp'
};
var fd = {};
var spawn = sinon.stub().returns(child);
var openSync = sinon.stub().returns(fd);
var spawnChildProcess = proxyquire('../../lib/scaffold/start', {
fs: {
openSync: openSync
},
child_process: {
spawn: spawn
}
}).spawnChildProcess;
spawnChildProcess('/tmp', _process);
spawn.callCount.should.equal(1);
spawn.args[0][0].should.equal(_process.execPath);
var expected = [].concat(_process.argv);
expected.shift();
spawn.args[0][1].should.deep.equal(expected);
var cp_opt = {
stdio: ['ignore', fd, fd],
env: _process.env,
cwd: '',
detached: true
};
spawn.args[0][2].should.deep.equal(cp_opt);
openSync.callCount.should.equal(1);
openSync.args[0][0].should.equal('/tmp/bitcore-node.log');
openSync.args[0][1].should.equal('a+');
});
it('should not spawn a new child process if there is already a daemon running', function() {
var _process = {
exit: function() {},
env: {
__bitcore_node: true
},
argv: [
'node',
'bitcore-node'
],
cwd: 'cwd',
pid: 999,
execPath: '/tmp'
};
var spawnChildProcess = proxyquire('../../lib/scaffold/start', {}).spawnChildProcess;
spawnChildProcess('/tmp', _process).should.equal(999);
});
});
describe('daemon', function() {
var sandbox;
var spawn;
var setup;
var registerSync;
var registerExit;
var start = require('../../lib/scaffold/start');
var options = {
config: {
datadir: '/tmp',
daemon: true
}
}
beforeEach(function() {
sandbox = sinon.sandbox.create();
spawn = sandbox.stub(start, 'spawnChildProcess', function() {});
setup = sandbox.stub(start, 'setupServices', function() {});
registerSync = sandbox.stub(start, 'registerSyncHandlers', function() {});
registerExit = sandbox.stub(start, 'registerExitHandlers', function() {});
});
afterEach(function() {
sandbox.restore();
});
it('call spawnChildProcess if there is a config option to do so', function() {
start(options);
registerSync.callCount.should.equal(1);
registerExit.callCount.should.equal(1);
spawn.callCount.should.equal(1);
});
it('not call spawnChildProcess if there is not an option to do so', function() {
options.config.daemon = false;
start(options);
registerSync.callCount.should.equal(1);
registerExit.callCount.should.equal(1);
spawn.callCount.should.equal(0);
});
});
describe('#registerExitHandlers', function() {
var stub;
var registerExitHandlers = require('../../lib/scaffold/start').registerExitHandlers;

View File

@ -1,103 +0,0 @@
'use strict';
var chai = require('chai');
var should = chai.should();
var sinon = require('sinon');
var bitcorenode = require('../../../');
var bitcore = require('bitcore-lib');
var Address = bitcore.Address;
var Script = bitcore.Script;
var AddressService = bitcorenode.services.Address;
var Networks = bitcore.Networks;
var encoding = require('../../../lib/services/address/encoding');
var mockdb = {
};
var mocknode = {
network: Networks.testnet,
datadir: 'testdir',
db: mockdb,
services: {
bitcoind: {
on: sinon.stub()
}
}
};
describe('Address Service Encoding', function() {
describe('#encodeSpentIndexSyncKey', function() {
it('will encode to 36 bytes (string)', function() {
var txidBuffer = new Buffer('3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7', 'hex');
var key = encoding.encodeSpentIndexSyncKey(txidBuffer, 12);
key.length.should.equal(36);
});
it('will be able to decode encoded value', function() {
var txid = '3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7';
var txidBuffer = new Buffer(txid, 'hex');
var key = encoding.encodeSpentIndexSyncKey(txidBuffer, 12);
var keyBuffer = new Buffer(key, 'binary');
keyBuffer.slice(0, 32).toString('hex').should.equal(txid);
var outputIndex = keyBuffer.readUInt32BE(32);
outputIndex.should.equal(12);
});
});
describe('#_encodeInputKeyMap/#_decodeInputKeyMap roundtrip', function() {
var encoded;
var outputTxIdBuffer = new Buffer('3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7', 'hex');
it('encode key', function() {
encoded = encoding.encodeInputKeyMap(outputTxIdBuffer, 13);
});
it('decode key', function() {
var key = encoding.decodeInputKeyMap(encoded);
key.outputTxId.toString('hex').should.equal(outputTxIdBuffer.toString('hex'));
key.outputIndex.should.equal(13);
});
});
describe('#_encodeInputValueMap/#_decodeInputValueMap roundtrip', function() {
var encoded;
var inputTxIdBuffer = new Buffer('3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7', 'hex');
it('encode key', function() {
encoded = encoding.encodeInputValueMap(inputTxIdBuffer, 7);
});
it('decode key', function() {
var key = encoding.decodeInputValueMap(encoded);
key.inputTxId.toString('hex').should.equal(inputTxIdBuffer.toString('hex'));
key.inputIndex.should.equal(7);
});
});
describe('#extractAddressInfoFromScript', function() {
it('pay-to-publickey', function() {
var pubkey = new bitcore.PublicKey('022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da');
var script = Script.buildPublicKeyOut(pubkey);
var info = encoding.extractAddressInfoFromScript(script, Networks.livenet);
info.addressType.should.equal(Address.PayToPublicKeyHash);
info.hashBuffer.toString('hex').should.equal('9674af7395592ec5d91573aa8d6557de55f60147');
});
it('pay-to-publickeyhash', function() {
var script = Script('OP_DUP OP_HASH160 20 0x0000000000000000000000000000000000000000 OP_EQUALVERIFY OP_CHECKSIG');
var info = encoding.extractAddressInfoFromScript(script, Networks.livenet);
info.addressType.should.equal(Address.PayToPublicKeyHash);
info.hashBuffer.toString('hex').should.equal('0000000000000000000000000000000000000000');
});
it('pay-to-scripthash', function() {
var script = Script('OP_HASH160 20 0x0000000000000000000000000000000000000000 OP_EQUAL');
var info = encoding.extractAddressInfoFromScript(script, Networks.livenet);
info.addressType.should.equal(Address.PayToScriptHash);
info.hashBuffer.toString('hex').should.equal('0000000000000000000000000000000000000000');
});
it('non-address script type', function() {
var buf = new Buffer(40);
buf.fill(0);
var script = Script('OP_RETURN 40 0x' + buf.toString('hex'));
var info = encoding.extractAddressInfoFromScript(script, Networks.livenet);
info.should.equal(false);
});
});
});

View File

@ -1,541 +0,0 @@
'use strict';
var should = require('chai').should();
var sinon = require('sinon');
var bitcore = require('bitcore-lib');
var Transaction = require('../../../lib/transaction');
var AddressHistory = require('../../../lib/services/address/history');
describe('Address Service History', function() {
var address = '12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX';
describe('@constructor', function() {
it('will construct a new instance', function() {
var node = {};
var options = {};
var addresses = [address];
var history = new AddressHistory({
node: node,
options: options,
addresses: addresses
});
history.should.be.instanceof(AddressHistory);
history.node.should.equal(node);
history.options.should.equal(options);
history.addresses.should.equal(addresses);
history.detailedArray.should.deep.equal([]);
});
it('will set addresses an array if only sent a string', function() {
var history = new AddressHistory({
node: {},
options: {},
addresses: address
});
history.addresses.should.deep.equal([address]);
});
});
describe('#get', function() {
it('will give an error if length of addresses is too long', function(done) {
var node = {};
var options = {};
var addresses = [];
for (var i = 0; i < 101; i++) {
addresses.push(address);
}
var history = new AddressHistory({
node: node,
options: options,
addresses: addresses
});
history.maxAddressesQuery = 100;
history.get(function(err) {
should.exist(err);
err.message.match(/Maximum/);
done();
});
});
it('give error from getAddressSummary with one address', function(done) {
var node = {
services: {
address: {
getAddressSummary: sinon.stub().callsArgWith(2, new Error('test'))
}
}
};
var options = {};
var addresses = [address];
var history = new AddressHistory({
node: node,
options: options,
addresses: addresses
});
history.get(function(err) {
should.exist(err);
err.message.should.equal('test');
done();
});
});
it('give error from getAddressSummary with multiple addresses', function(done) {
var node = {
services: {
address: {
getAddressSummary: sinon.stub().callsArgWith(2, new Error('test2'))
}
}
};
var options = {};
var addresses = [address, address];
var history = new AddressHistory({
node: node,
options: options,
addresses: addresses
});
history.get(function(err) {
should.exist(err);
err.message.should.equal('test2');
done();
});
});
it('will query get address summary directly with one address', function(done) {
var txids = [];
var summary = {
txids: txids
};
var node = {
services: {
address: {
getAddressSummary: sinon.stub().callsArgWith(2, null, summary)
}
}
};
var options = {};
var addresses = [address];
var history = new AddressHistory({
node: node,
options: options,
addresses: addresses
});
history._mergeAndSortTxids = sinon.stub();
history._paginateWithDetails = sinon.stub().callsArg(1);
history.get(function() {
history.node.services.address.getAddressSummary.callCount.should.equal(1);
history.node.services.address.getAddressSummary.args[0][0].should.equal(address);
history.node.services.address.getAddressSummary.args[0][1].should.equal(options);
history._paginateWithDetails.callCount.should.equal(1);
history._paginateWithDetails.args[0][0].should.equal(txids);
history._mergeAndSortTxids.callCount.should.equal(0);
done();
});
});
it('will merge multiple summaries with multiple addresses', function(done) {
var txids = [];
var summary = {
txids: txids
};
var node = {
services: {
address: {
getAddressSummary: sinon.stub().callsArgWith(2, null, summary)
}
}
};
var options = {};
var addresses = [address, address];
var history = new AddressHistory({
node: node,
options: options,
addresses: addresses
});
history._mergeAndSortTxids = sinon.stub().returns(txids);
history._paginateWithDetails = sinon.stub().callsArg(1);
history.get(function() {
history.node.services.address.getAddressSummary.callCount.should.equal(2);
history.node.services.address.getAddressSummary.args[0][0].should.equal(address);
history.node.services.address.getAddressSummary.args[0][1].should.deep.equal({
fullTxList: true
});
history._paginateWithDetails.callCount.should.equal(1);
history._paginateWithDetails.args[0][0].should.equal(txids);
history._mergeAndSortTxids.callCount.should.equal(1);
done();
});
});
});
describe('#_paginateWithDetails', function() {
it('slice txids based on "from" and "to" (3 to 30)', function() {
var node = {};
var options = {
from: 3,
to: 30
};
var addresses = [address];
var history = new AddressHistory({
node: node,
options: options,
addresses: addresses
});
var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
sinon.stub(history, 'getDetailedInfo', function(txid, next) {
this.detailedArray.push(txid);
next();
});
history._paginateWithDetails(txids, function(err, result) {
result.totalCount.should.equal(11);
result.items.should.deep.equal([7, 6, 5, 4, 3, 2, 1, 0]);
});
});
it('slice txids based on "from" and "to" (0 to 3)', function() {
var node = {};
var options = {
from: 0,
to: 3
};
var addresses = [address];
var history = new AddressHistory({
node: node,
options: options,
addresses: addresses
});
var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
sinon.stub(history, 'getDetailedInfo', function(txid, next) {
this.detailedArray.push(txid);
next();
});
history._paginateWithDetails(txids, function(err, result) {
result.totalCount.should.equal(11);
result.items.should.deep.equal([10, 9, 8]);
});
});
it('will given an error if the full details is too long', function() {
var node = {};
var options = {
from: 0,
to: 3
};
var addresses = [address];
var history = new AddressHistory({
node: node,
options: options,
addresses: addresses
});
var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
sinon.stub(history, 'getDetailedInfo', function(txid, next) {
this.detailedArray.push(txid);
next();
});
history.maxHistoryQueryLength = 1;
history._paginateWithDetails(txids, function(err) {
should.exist(err);
err.message.match(/Maximum/);
});
});
it('will give full result without pagination options', function() {
var node = {};
var options = {};
var addresses = [address];
var history = new AddressHistory({
node: node,
options: options,
addresses: addresses
});
var txids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
sinon.stub(history, 'getDetailedInfo', function(txid, next) {
this.detailedArray.push(txid);
next();
});
history._paginateWithDetails(txids, function(err, result) {
result.totalCount.should.equal(11);
result.items.should.deep.equal([10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]);
});
});
});
describe('#_mergeAndSortTxids', function() {
it('will merge and sort multiple summaries', function() {
var summaries = [
{
totalReceived: 10000000,
totalSpent: 0,
balance: 10000000,
appearances: 2,
unconfirmedBalance: 20000000,
unconfirmedAppearances: 2,
appearanceIds: {
'56fafeb01961831b926558d040c246b97709fd700adcaa916541270583e8e579': 154,
'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce': 120
},
unconfirmedAppearanceIds: {
'ec94d845c603f292a93b7c829811ac624b76e52b351617ca5a758e9d61a11681': 1452898347406,
'ed11a08e3102f9610bda44c80c46781d97936a4290691d87244b1b345b39a693': 1452898331964
}
},
{
totalReceived: 59990000,
totalSpent: 0,
balance: 49990000,
appearances: 3,
unconfirmedBalance: 1000000,
unconfirmedAppearances: 3,
appearanceIds: {
'bc992ad772eb02864db07ef248d31fb3c6826d25f1153ebf8c79df9b7f70fcf2': 156,
'f3c1ba3ef86a0420d6102e40e2cfc8682632ab95d09d86a27f5d466b9fa9da47': 152,
'f637384e9f81f18767ea50e00bce58fc9848b6588a1130529eebba22a410155f': 151
},
unconfirmedAppearanceIds: {
'f71bccef3a8f5609c7f016154922adbfe0194a96fb17a798c24077c18d0a9345': 1452897902377,
'edc080f2084eed362aa488ccc873a24c378dc0979aa29b05767517b70569414a': 1452897971363,
'f35e7e2a2334e845946f3eaca76890d9a68f4393ccc9fe37a0c2fb035f66d2e9': 1452897923107
}
}
];
var node = {};
var options = {};
var addresses = [address];
var history = new AddressHistory({
node: node,
options: options,
addresses: addresses
});
var txids = history._mergeAndSortTxids(summaries);
txids.should.deep.equal([
'e9dcf22807db77ac0276b03cc2d3a8b03c4837db8ac6650501ef45af1c807cce',
'f637384e9f81f18767ea50e00bce58fc9848b6588a1130529eebba22a410155f',
'f3c1ba3ef86a0420d6102e40e2cfc8682632ab95d09d86a27f5d466b9fa9da47',
'56fafeb01961831b926558d040c246b97709fd700adcaa916541270583e8e579',
'bc992ad772eb02864db07ef248d31fb3c6826d25f1153ebf8c79df9b7f70fcf2',
'f71bccef3a8f5609c7f016154922adbfe0194a96fb17a798c24077c18d0a9345',
'f35e7e2a2334e845946f3eaca76890d9a68f4393ccc9fe37a0c2fb035f66d2e9',
'edc080f2084eed362aa488ccc873a24c378dc0979aa29b05767517b70569414a',
'ed11a08e3102f9610bda44c80c46781d97936a4290691d87244b1b345b39a693',
'ec94d845c603f292a93b7c829811ac624b76e52b351617ca5a758e9d61a11681'
]);
});
});
describe('#getDetailedInfo', function() {
it('will add additional information to existing this.transactions', function(done) {
var txid = '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0';
var tx = {
populateInputs: sinon.stub().callsArg(2),
__height: 20,
__timestamp: 1453134151,
isCoinbase: sinon.stub().returns(false),
getFee: sinon.stub().returns(1000)
};
var history = new AddressHistory({
node: {
services: {
db: {
getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, null, tx),
tip: {
__height: 300
}
}
}
},
options: {},
addresses: []
});
history.getAddressDetailsForTransaction = sinon.stub().returns({
addresses: {},
satoshis: 1000,
});
history.getDetailedInfo(txid, function(err) {
if (err) {
throw err;
}
history.node.services.db.getTransactionWithBlockInfo.callCount.should.equal(1);
done();
});
});
it('will handle error from getTransactionFromBlock', function(done) {
var txid = '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0';
var history = new AddressHistory({
node: {
services: {
db: {
getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, new Error('test')),
}
}
},
options: {},
addresses: []
});
history.getDetailedInfo(txid, function(err) {
err.message.should.equal('test');
done();
});
});
it('will handle error from populateInputs', function(done) {
var txid = '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0';
var history = new AddressHistory({
node: {
services: {
db: {
getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, null, {
populateInputs: sinon.stub().callsArgWith(2, new Error('test'))
}),
}
}
},
options: {},
addresses: []
});
history.getDetailedInfo(txid, function(err) {
err.message.should.equal('test');
done();
});
});
it('will set this.transactions with correct information', function(done) {
// block #314159
// txid 30169e8bf78bc27c4014a7aba3862c60e2e3cce19e52f1909c8255e4b7b3174e
// outputIndex 1
var txAddress = '1Cj4UZWnGWAJH1CweTMgPLQMn26WRMfXmo';
var txString = '0100000001a08ee59fcd5d86fa170abb6d925d62d5c5c476359681b70877c04f270c4ef246000000008a47304402203fb9b476bb0c37c9b9ed5784ebd67ae589492be11d4ae1612be29887e3e4ce750220741ef83781d1b3a5df8c66fa1957ad0398c733005310d7d9b1d8c2310ef4f74c0141046516ad02713e51ecf23ac9378f1069f9ae98e7de2f2edbf46b7836096e5dce95a05455cc87eaa1db64f39b0c63c0a23a3b8df1453dbd1c8317f967c65223cdf8ffffffff02b0a75fac000000001976a91484b45b9bf3add8f7a0f3daad305fdaf6b73441ea88ac20badc02000000001976a914809dc14496f99b6deb722cf46d89d22f4beb8efd88ac00000000';
var previousTxString = '010000000155532fad2869bb951b0bd646a546887f6ee668c4c0ee13bf3f1c4bce6d6e3ed9000000008c4930460221008540795f4ef79b1d2549c400c61155ca5abbf3089c84ad280e1ba6db2a31abce022100d7d162175483d51174d40bba722e721542c924202a0c2970b07e680b51f3a0670141046516ad02713e51ecf23ac9378f1069f9ae98e7de2f2edbf46b7836096e5dce95a05455cc87eaa1db64f39b0c63c0a23a3b8df1453dbd1c8317f967c65223cdf8ffffffff02f0af3caf000000001976a91484b45b9bf3add8f7a0f3daad305fdaf6b73441ea88ac80969800000000001976a91421277e65777760d1f3c7c982ba14ed8f934f005888ac00000000';
var transaction = new Transaction();
var previousTransaction = new Transaction();
previousTransaction.fromString(previousTxString);
var previousTransactionTxid = '46f24e0c274fc07708b781963576c4c5d5625d926dbb0a17fa865dcd9fe58ea0';
transaction.fromString(txString);
var txid = transaction.hash;
transaction.__blockHash = '00000000000000001bb82a7f5973618cfd3185ba1ded04dd852a653f92a27c45';
transaction.__height = 314159;
transaction.__timestamp = 1407292005;
var history = new AddressHistory({
node: {
services: {
db: {
tip: {
__height: 314159
},
getTransactionWithBlockInfo: sinon.stub().callsArgWith(2, null, transaction),
getTransaction: function(prevTxid, queryMempool, callback) {
prevTxid.should.equal(previousTransactionTxid);
setImmediate(function() {
callback(null, previousTransaction);
});
}
}
}
},
options: {},
addresses: [txAddress]
});
var transactionInfo = {
addresses: {},
txid: txid,
timestamp: 1407292005,
satoshis: 48020000,
address: txAddress
};
transactionInfo.addresses[txAddress] = {};
transactionInfo.addresses[txAddress].outputIndexes = [1];
transactionInfo.addresses[txAddress].inputIndexes = [];
history.getDetailedInfo(txid, function(err) {
if (err) {
throw err;
}
var info = history.detailedArray[0];
info.addresses[txAddress].should.deep.equal({
outputIndexes: [1],
inputIndexes: []
});
info.satoshis.should.equal(48020000);
info.height.should.equal(314159);
info.confirmations.should.equal(1);
info.timestamp.should.equal(1407292005);
info.fees.should.equal(20000);
info.tx.should.equal(transaction);
done();
});
});
});
describe('#getAddressDetailsForTransaction', function() {
it('will calculate details for the transaction', function(done) {
/* jshint sub:true */
var tx = bitcore.Transaction({
'hash': 'b12b3ae8489c5a566b629a3c62ce4c51c3870af550fb5dc77d715b669a91343c',
'version': 1,
'inputs': [
{
'prevTxId': 'a2b7ea824a92f4a4944686e67ec1001bc8785348b8c111c226f782084077b543',
'outputIndex': 0,
'sequenceNumber': 4294967295,
'script': '47304402201b81c933297241960a57ae1b2952863b965ac8c9ec7466ff0b715712d27548d50220576e115b63864f003889443525f47c7cf0bc1e2b5108398da085b221f267ba2301210229766f1afa25ca499a51f8e01c292b0255a21a41bb6685564a1607a811ffe924',
'scriptString': '71 0x304402201b81c933297241960a57ae1b2952863b965ac8c9ec7466ff0b715712d27548d50220576e115b63864f003889443525f47c7cf0bc1e2b5108398da085b221f267ba2301 33 0x0229766f1afa25ca499a51f8e01c292b0255a21a41bb6685564a1607a811ffe924',
'output': {
'satoshis': 1000000000,
'script': '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'
}
}
],
'outputs': [
{
'satoshis': 100000000,
'script': '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'
},
{
'satoshis': 200000000,
'script': '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'
},
{
'satoshis': 50000000,
'script': '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'
},
{
'satoshis': 300000000,
'script': '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'
},
{
'satoshis': 349990000,
'script': '76a9140b2f0a0c31bfe0406b0ccc1381fdbe311946dadc88ac'
}
],
'nLockTime': 0
});
var history = new AddressHistory({
node: {
network: bitcore.Networks.testnet
},
options: {},
addresses: ['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW']
});
var details = history.getAddressDetailsForTransaction(tx);
should.exist(details.addresses['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW']);
details.addresses['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW'].inputIndexes.should.deep.equal([0]);
details.addresses['mgY65WSfEmsyYaYPQaXhmXMeBhwp4EcsQW'].outputIndexes.should.deep.equal([
0, 1, 2, 3, 4
]);
details.satoshis.should.equal(-10000);
done();
});
});
describe('#getConfirmationsDetail', function() {
it('the correct confirmations when included in the tip', function(done) {
var history = new AddressHistory({
node: {
services: {
db: {
tip: {
__height: 100
}
}
}
},
options: {},
addresses: []
});
var transaction = {
__height: 100
};
history.getConfirmationsDetail(transaction).should.equal(1);
done();
});
});
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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