From cf6225c4957e224b5e4b84f322fa92917309c6da Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Mon, 20 Jul 2015 17:55:49 -0400 Subject: [PATCH 01/17] Regtest testing - Removed config option for build scripts, replaced with env variable - Updated README - Added regtest option under test build, wallet built-in - added network key to bindings - datadir for the bitcoind object instead of directory - added new config_options scripts for test and debug --- README.md | 13 +++++++++---- benchmarks/index.js | 2 +- bin/build-libbitcoind | 17 +++++++++++------ bin/config_options_debug.sh | 2 ++ bin/config_options_test.sh | 2 ++ example/daemon.js | 15 +++++++++++++++ lib/daemon.js | 33 +++++++++++++++++++++++---------- package.json | 1 - src/bitcoindjs.cc | 20 ++++++++++++++++++-- 9 files changed, 81 insertions(+), 24 deletions(-) create mode 100644 bin/config_options_debug.sh create mode 100644 bin/config_options_test.sh create mode 100644 example/daemon.js diff --git a/README.md b/README.md index 8dbc5d61..bdaf110e 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ var BitcoinNode = require('bitcoind.js'); var configuration = { datadir: '~/.bitcoin', - testnet: true + network: 'testnet' }; var node = new BitcoinNode(configuration); @@ -110,7 +110,7 @@ $ tail -f ~/.bitcoin/debug.log ## Building -There are two main parts of the build, compiling Bitcoin Core and the Node.js bindings. You can run both by using `npm install` and `npm run debug_install`. +There are two main parts of the build, compiling Bitcoin Core and the Node.js bindings. You can run both by using `npm install` and set environment variable, $BITCOINDJS_ENV to 'test' or 'debug'. Both 'test' and 'debug' build libbitcoind with debug symbols whereas 'test' adds wallet capability so that regtest can be used. ### Node.js Bindings @@ -130,6 +130,11 @@ To be able to debug you'll need to have `gdb` and `node` compiled for debugging $ gdb --args node_g path/to/example.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/index.js +``` + To run integration tests against testnet or livenet data: ```bash @@ -161,7 +166,7 @@ Most of all the dependencies for building Bitcoin Core are needed, for more info #### Shared 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 shared library. +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 shared 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. @@ -176,7 +181,7 @@ $ cd /path/to/bitcoind.js $ ./bin/build-libbitcoind ``` -The first argument is 'debug', this will compile node bindings and bitcoind with debug flags. The `PATCH_VERSION` file dictates what version/tag the patch goes clean against. +The `PATCH_VERSION` file dictates what version/tag the patch goes clean against. There is a config_options.sh that has the configure options used to build libbitcoind. `make` will then compile `libbitcoind/src/.libs/libbitcoind.{so|dylib}`. This will completely ignore compiling tests, QT object files and the wallet features in `bitcoind/libbitcoind.{so|dylib}`. diff --git a/benchmarks/index.js b/benchmarks/index.js index 574d512c..436ccabf 100644 --- a/benchmarks/index.js +++ b/benchmarks/index.js @@ -28,7 +28,7 @@ var fixtureData = { var bitcoind = require('../').daemon({ datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin', - testnet: true + network: 'testnet' }); bitcoind.on('error', function(err) { diff --git a/bin/build-libbitcoind b/bin/build-libbitcoind index b73d2d75..dbd1dcf9 100755 --- a/bin/build-libbitcoind +++ b/bin/build-libbitcoind @@ -3,6 +3,7 @@ set -e root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.." cd "${root_dir}" +options=`cat ${root_dir}/bin/config_options.sh` os_dir=$(./platform/os.sh osdir) @@ -10,8 +11,13 @@ os_dir=$(./platform/os.sh osdir) export LD_LIBRARY_PATH="${root_dir}/libbitcoind/src/leveldb":"${os_dir}":$LD_LIBRARY_PATH debug= -if test x"$1" = x'debug'; then - debug=--enable-debug +if [ "${BITCOINDJS_ENV}" == "debug" ]; then + options=`cat ${root_dir}/bin/config_options_debug.sh` +fi + +test= +if [ "${BITCOINDJS_ENV}" == "test" ]; then + options=`cat ${root_dir}/bin/config_options_test.sh` fi btc_dir="${root_dir}/libbitcoind" @@ -50,12 +56,11 @@ if [ "${only_make}" = false ]; then echo './autogen.sh' ./autogen.sh - options=`cat ${root_dir}/bin/config_options.sh` - full_options="${options}${os_dir} ${debug}" - echo "running the configure script with the following options:\n :::[\"${full_options}\"]:::" - ${full_options} fi +full_options="${options}${os_dir}" +echo "running the configure script with the following options:\n :::[\"${full_options}\"]:::" +${full_options} echo 'make V=1' make V=1 diff --git a/bin/config_options_debug.sh b/bin/config_options_debug.sh new file mode 100644 index 00000000..b8f11c73 --- /dev/null +++ b/bin/config_options_debug.sh @@ -0,0 +1,2 @@ +./configure --enable_debug --enable-tests=no --enable-daemonlib --with-gui=no --without-qt --without-miniupnpc --without-bdb --disable-wallet --without-utils --prefix= + diff --git a/bin/config_options_test.sh b/bin/config_options_test.sh new file mode 100644 index 00000000..8ba4a8ae --- /dev/null +++ b/bin/config_options_test.sh @@ -0,0 +1,2 @@ +./configure --enable-tests=no --enable-daemonlib --with-gui=no --without-qt --without-miniupnpc --prefix= + diff --git a/example/daemon.js b/example/daemon.js new file mode 100644 index 00000000..d5dcf871 --- /dev/null +++ b/example/daemon.js @@ -0,0 +1,15 @@ +'use strict'; + +process.title = 'bitcoind.js'; + +var daemon = require('../').daemon({ + directory: process.env.BITCOINDJS_DIR || '~/.bitcoin' +}); + +daemon.on('error', function(err) { + daemon.log('error="%s"', err.message); +}); + +daemon.on('open', function(status) { + daemon.log('status="%s"', status); +}); diff --git a/lib/daemon.js b/lib/daemon.js index 5295c925..f1d18045 100644 --- a/lib/daemon.js +++ b/lib/daemon.js @@ -37,14 +37,6 @@ function Daemon(options) { this.options = options || {}; - if (typeof this.options === 'string') { - this.options = { datadir: this.options }; - } - - if (this.options.directory) { - this.options.datadir = this.options.directory; - delete this.options.directory; - } if (!this.options.datadir) { this.options.datadir = '~/.bitcoind.js'; @@ -54,7 +46,13 @@ function Daemon(options) { this.datadir = this.options.datadir; this.config = this.datadir + '/bitcoin.conf'; - this.network = Daemon[this.options.testnet ? 'testnet' : 'livenet']; + this.network = Daemon['livenet']; + + if (this.options.network === 'testnet') { + this.network = Daemon['testnet']; + } else if(this.options.network === 'regtest') { + this.network = Daemon['regtest']; + } if (!fs.existsSync(this.datadir)) { mkdirp.sync(this.datadir); @@ -83,7 +81,6 @@ function Daemon(options) { fs.writeFileSync(data + peers); } - // Copy config into testnet dir if (this.network.name === 'testnet') { if (!fs.existsSync(this.datadir + '/testnet3')) { fs.mkdirSync(this.datadir + '/testnet3'); @@ -93,6 +90,15 @@ function Daemon(options) { fs.readFileSync(this.config)); } + if (this.network.name === 'regtest') { + if (!fs.existsSync(this.datadir + '/regtest')) { + fs.mkdirSync(this.datadir + '/regtest'); + } + fs.writeFileSync( + this.datadir + '/regtest/bitcoin.conf', + fs.readFileSync(this.config)); + } + Object.keys(exports).forEach(function(key) { self[key] = exports[key]; }); @@ -120,6 +126,13 @@ Daemon.testnet = { ] }; +Bitcoin.regtest = { + name: 'regtest', + peers: [ + // hardcoded peers + ] +}; + // Make sure signal handlers are not overwritten Daemon._signalQueue = []; Daemon._processOn = process.on; diff --git a/package.json b/package.json index 74b63809..f8e0794e 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "preinstall": "./bin/build-libbitcoind", "install": "./bin/build-bindings", "start": "node example", - "debug_install": "./bin/build-libbitcoind debug && ./bin/build-bindings debug", "test": "NODE_ENV=test mocha --recursive", "coverage": "istanbul cover _mocha -- --recursive" }, diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index c349de82..82a035c8 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -76,6 +76,7 @@ static volatile bool shutdown_complete = false; static char *g_data_dir = NULL; static bool g_rpc = false; static bool g_testnet = false; +static bool g_regtest = false; static bool g_txindex = false; /** @@ -105,6 +106,7 @@ struct async_node_data { std::string datadir; bool rpc; bool testnet; + bool regtest; bool txindex; Eternal callback; }; @@ -298,6 +300,7 @@ NAN_METHOD(StartBitcoind) { std::string datadir = std::string(""); bool rpc = false; bool testnet = false; + bool regtest = false; bool txindex = false; if (args.Length() >= 2 && args[0]->IsObject() && args[1]->IsFunction()) { @@ -309,8 +312,14 @@ NAN_METHOD(StartBitcoind) { if (options->Get(NanNew("rpc"))->IsBoolean()) { rpc = options->Get(NanNew("rpc"))->ToBoolean()->IsTrue(); } - if (options->Get(NanNew("testnet"))->IsBoolean()) { - testnet = options->Get(NanNew("testnet"))->ToBoolean()->IsTrue(); + if (options->Get(NanNew("network"))->IsString()) { + String::Utf8Value network_(options->Get(NanNew("network"))->ToString()); + std::string network = std::string(*network_); + if (network == "testnet") { + testnet = true; + } else if (network == "regtest") { + regtest = true; + } } if (options->Get(NanNew("txindex"))->IsBoolean()) { txindex = options->Get(NanNew("txindex"))->ToBoolean()->IsTrue(); @@ -337,6 +346,7 @@ NAN_METHOD(StartBitcoind) { data->datadir = datadir; data->rpc = rpc; data->testnet = testnet; + data->regtest = regtest; data->txindex = txindex; Eternal eternal(isolate, callback); @@ -370,6 +380,7 @@ async_start_node(uv_work_t *req) { } g_rpc = (bool)data->rpc; g_testnet = (bool)data->testnet; + g_regtest = (bool)data->regtest; g_txindex = (bool)data->txindex; tcgetattr(STDIN_FILENO, &orig_termios); start_node(); @@ -475,6 +486,11 @@ start_node_thread(void) { argc++; } + if (g_regtest) { + argv[argc] = (char *)"-regtest"; + argc++; + } + argv[argc] = (char *)"-txindex"; argc++; From 66af5935b71ce84b90284610f403f5981bece886 Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Tue, 21 Jul 2015 10:34:19 -0400 Subject: [PATCH 02/17] Patch update for wallet inclusion under the test environment - Added the patch to allow the building of the test environment for which the wallet is required --- etc/bitcoin.patch | 112 +++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 60 deletions(-) diff --git a/etc/bitcoin.patch b/etc/bitcoin.patch index 5606ee64..c71c258f 100644 --- a/etc/bitcoin.patch +++ b/etc/bitcoin.patch @@ -1,18 +1,8 @@ -From 05084f2a640b862132588b322461ec8e13058fc3 Mon Sep 17 00:00:00 2001 -From: Chris Kleeschulte -Date: Mon, 13 Jul 2015 12:49:30 -0400 -Subject: [PATCH] libbitcoind +commit 29c1ca452ba6178d6b17be0a0b5a65567ba846af +Author: Chris Kleeschulte +Date: Mon Jul 13 16:35:37 2015 -0400 ---- - config_me.sh | 1 + - configure.ac | 37 ++++++++++++++++++++++++++++++++----- - src/Makefile.am | 42 ++++++++++++++++++++++++++++++++++-------- - src/bitcoind.cpp | 6 ++++++ - src/init.h | 5 +++++ - src/leveldb/Makefile | 6 +++++- - src/leveldbwrapper.h | 12 ++++++++++++ - 7 files changed, 95 insertions(+), 14 deletions(-) - create mode 100644 config_me.sh + allow compiling of libbitcoind.so. diff --git a/config_me.sh b/config_me.sh new file mode 100644 @@ -22,13 +12,13 @@ index 0000000..19e9a1b @@ -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 37fe47e..27a9b9a 100644 +index 37fe47e..83cfe70 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)])], @@ -45,13 +35,13 @@ index 37fe47e..27a9b9a 100644 + 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 @@ -60,7 +50,7 @@ index 37fe47e..27a9b9a 100644 @@ -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 @@ -78,13 +68,13 @@ index 37fe47e..27a9b9a 100644 @@ -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]) @@ -96,18 +86,19 @@ index 37fe47e..27a9b9a 100644 LIBLEVELDB= LIBMEMENV= diff --git a/src/Makefile.am b/src/Makefile.am -index 1c2f770..632f608 100644 +index 1c2f770..ddcae0f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am -@@ -1,6 +1,7 @@ +@@ -1,6 +1,8 @@ DIST_SUBDIRS = secp256k1 AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) - + +lib_LTLIBRARIES = - ++libbitcoind_la_LIBADD = + if EMBEDDED_LEVELDB LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include -@@ -15,6 +16,10 @@ $(LIBLEVELDB) $(LIBMEMENV): +@@ -15,6 +17,10 @@ $(LIBLEVELDB) $(LIBMEMENV): @echo "Building LevelDB ..." && $(MAKE) -C $(@D) $(@F) CXX="$(CXX)" \ CC="$(CC)" PLATFORM=$(TARGET_OS) AR="$(AR)" $(LEVELDB_TARGET_FLAGS) \ OPT="$(CXXFLAGS) $(CPPFLAGS) -D__STDC_LIMIT_MACROS" @@ -116,12 +107,12 @@ index 1c2f770..632f608 100644 + @echo "Building the LevelDB shared library..." && $(MAKE) -C ./leveldb + endif - + BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config -@@ -49,16 +54,16 @@ BITCOIN_INCLUDES += $(BDB_CPPFLAGS) +@@ -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 @@ -132,7 +123,7 @@ index 1c2f770..632f608 100644 +LIBBITCOIN_CONSENSUS = bin_PROGRAMS = TESTS = - + +if BUILD_BITCOIN_LIBS +lib_LTLIBRARIES += libbitcoinconsensus.la +LIBBITCOIN_CONSENSUS += libbitcoinconsensus.la @@ -142,28 +133,28 @@ index 1c2f770..632f608 100644 if BUILD_BITCOIND bin_PROGRAMS += bitcoind endif -@@ -66,6 +71,9 @@ endif +@@ -66,6 +72,9 @@ endif if BUILD_BITCOIN_UTILS bin_PROGRAMS += bitcoin-cli bitcoin-tx endif +else +lib_LTLIBRARIES += libbitcoind.la +endif - + .PHONY: FORCE # bitcoin core # -@@ -169,8 +177,9 @@ obj/build.h: FORCE +@@ -169,8 +178,9 @@ 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 - + +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 = \ -@@ -309,9 +318,18 @@ nodist_libbitcoin_util_a_SOURCES = $(srcdir)/obj/build.h +@@ -309,9 +319,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) @@ -175,37 +166,41 @@ index 1c2f770..632f608 100644 +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 = \ -@@ -328,7 +346,15 @@ if ENABLE_WALLET +@@ -326,9 +345,19 @@ bitcoind_LDADD = \ + + if ENABLE_WALLET bitcoind_LDADD += libbitcoin_wallet.a ++libbitcoind_la_LIBADD += $(BDB_LIBS) ++libbitcoind_la_SOURCES += $(libbitcoin_wallet_a_SOURCES) endif - + +MEMOBJ = helpers/memenv/memenv.lo +$(MEMOBJ): + @echo "Building the Memenv shared library..." && $(MAKE) -C ./leveldb $@ + bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) -+libbitcoind_la_LIBADD = $(BOOST_LIBS) $(SSL_LIBS) $(LIBSECP256K1) $(CRYPTO_LIBS) leveldb/$(MEMOBJ) ++libbitcoind_la_LIBADD += $(BOOST_LIBS) $(SSL_LIBS) $(LIBSECP256K1) $(CRYPTO_LIBS) leveldb/$(MEMOBJ) +libbitcoind_la_CPPFLAGS = $(BITCOIN_INCLUDES) +libbitcoind_la_LDFLAGS = -lleveldb -L./leveldb $(RELDFLAGS) -no-undefined +libbitcoind_la_DEPENDENCIES = $(LIBSECP256K1) LIBLEVELDB_SHARED $(MEMOBJ) # - + # bitcoin-cli binary # diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index cce687a..0f162ff 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 @@ -216,13 +211,13 @@ index cce687a..0f162ff 100644 @@ -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 @@ -231,9 +226,9 @@ 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 +#include @@ -248,24 +243,24 @@ index 2bd2cad..490ba66 100644 +++ b/src/leveldb/Makefile @@ -103,7 +103,7 @@ check: all $(PROGRAMS) $(TESTS) for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done - + clean: - -rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk + -rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.lo helpers/memenv/.deps/*.Plo helpers/memenv/.deps/*.Tpo */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk -rm -rf ios-x86/* ios-arm/* - + $(LIBRARY): $(LIBOBJECTS) @@ -192,6 +192,10 @@ $(MEMENVLIBRARY) : $(MEMENVOBJECTS) rm -f $@ $(AR) -rs $@ $(MEMENVOBJECTS) - + +helpers/memenv/memenv.lo: helpers/memenv/memenv.cc + -mkdir -p helpers/memenv/.deps + /bin/bash ../../libtool --tag=CXX --mode=compile $(CXX) $(CXXFLAGS) $(CFLAGS) -fPIC -MT $@ -MD -MP -MF helpers/memenv/.deps/memenv.Tpo -c -o $@ $< + memenv_test : helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) $(CXX) $(LDFLAGS) helpers/memenv/memenv_test.o $(MEMENVLIBRARY) $(LIBRARY) $(TESTHARNESS) -o $@ $(LIBS) - + diff --git a/src/leveldbwrapper.h b/src/leveldbwrapper.h index c65e842..0e44bb5 100644 --- a/src/leveldbwrapper.h @@ -273,14 +268,14 @@ index c65e842..0e44bb5 100644 @@ -29,10 +29,16 @@ class CLevelDBBatch { friend class CLevelDBWrapper; - + +#if ENABLE_DAEMONLIB +public: +#else private: +#endif leveldb::WriteBatch batch; - + +#if !ENABLE_DAEMONLIB public: +#endif @@ -288,7 +283,7 @@ index c65e842..0e44bb5 100644 void Write(const K& key, const V& value) { @@ -63,7 +69,11 @@ public: - + class CLevelDBWrapper { +#if ENABLE_DAEMONLIB @@ -298,17 +293,14 @@ index c65e842..0e44bb5 100644 +#endif //! custom environment this database is using (may be NULL in case of default environment) leveldb::Env* penv; - + @@ -85,7 +95,9 @@ private: //! the database itself leveldb::DB* pdb; - + +#if !ENABLE_DAEMONLIB public: +#endif CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false); ~CLevelDBWrapper(); - --- -2.3.2 (Apple Git-55) - + From e3d041b594436d1dbfcd8c94ee64742f576b688b Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 11:35:02 -0400 Subject: [PATCH 03/17] Include debug flag when building test build. --- bin/config_options_test.sh | 2 +- example/daemon.js | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 example/daemon.js diff --git a/bin/config_options_test.sh b/bin/config_options_test.sh index 8ba4a8ae..2653ee1e 100644 --- a/bin/config_options_test.sh +++ b/bin/config_options_test.sh @@ -1,2 +1,2 @@ -./configure --enable-tests=no --enable-daemonlib --with-gui=no --without-qt --without-miniupnpc --prefix= +./configure --enable_debug --enable-tests=no --enable-daemonlib --with-gui=no --without-qt --without-miniupnpc --prefix= diff --git a/example/daemon.js b/example/daemon.js deleted file mode 100644 index d5dcf871..00000000 --- a/example/daemon.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -process.title = 'bitcoind.js'; - -var daemon = require('../').daemon({ - directory: process.env.BITCOINDJS_DIR || '~/.bitcoin' -}); - -daemon.on('error', function(err) { - daemon.log('error="%s"', err.message); -}); - -daemon.on('open', function(status) { - daemon.log('status="%s"', status); -}); From 53968e6f8d21dd894eff6c49db328b750c2754b6 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 13:19:18 -0400 Subject: [PATCH 04/17] Fix debug flag and readme formatting. --- README.md | 2 +- bin/config_options_debug.sh | 2 +- bin/config_options_test.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bdaf110e..a76252e8 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ To be able to debug you'll need to have `gdb` and `node` compiled for debugging $ gdb --args node_g path/to/example.js ``` -To run mocha from within gdb (notice _mocha and not mocha so that the tests run in the same process): +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/index.js ``` diff --git a/bin/config_options_debug.sh b/bin/config_options_debug.sh index b8f11c73..afacbc9f 100644 --- a/bin/config_options_debug.sh +++ b/bin/config_options_debug.sh @@ -1,2 +1,2 @@ -./configure --enable_debug --enable-tests=no --enable-daemonlib --with-gui=no --without-qt --without-miniupnpc --without-bdb --disable-wallet --without-utils --prefix= +./configure --enable-debug --enable-tests=no --enable-daemonlib --with-gui=no --without-qt --without-miniupnpc --without-bdb --disable-wallet --without-utils --prefix= diff --git a/bin/config_options_test.sh b/bin/config_options_test.sh index 2653ee1e..225f7aa0 100644 --- a/bin/config_options_test.sh +++ b/bin/config_options_test.sh @@ -1,2 +1,2 @@ -./configure --enable_debug --enable-tests=no --enable-daemonlib --with-gui=no --without-qt --without-miniupnpc --prefix= +./configure --enable-debug --enable-tests=no --enable-daemonlib --with-gui=no --without-qt --without-miniupnpc --prefix= From 787aa37e7a4cc8662fa6b5fa8f592c7a8d8382a0 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 13:23:38 -0400 Subject: [PATCH 05/17] Removed extra files and updated config options. --- example/getblock.js | 35 -- example/index.js | 2 +- example/index_stripped.js | 23 - example/node.js | 2 +- integration/index.js | 2 +- lib/daemon.js | 2 +- lib/daemon_stripped.js | 959 -------------------------------------- 7 files changed, 4 insertions(+), 1021 deletions(-) delete mode 100644 example/getblock.js delete mode 100755 example/index_stripped.js delete mode 100644 lib/daemon_stripped.js diff --git a/example/getblock.js b/example/getblock.js deleted file mode 100644 index 06a96fff..00000000 --- a/example/getblock.js +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env node - -/** - * bitcoind.js example - */ - -process.title = 'bitcoind.js'; - -/** - * daemon - */ - -var daemon = require('../index.js').daemon({ - directory: process.env.BITCOINDJS_DIR || '~/.bitcoin' -}); - -daemon.on('error', function(err) { - daemon.log('error="%s"', err.message); -}); - -daemon.on('ready', function(err, result) { - console.log('Ready!'); - - daemon.getBlock('000000000000000082ccf8f1557c5d40b21edabb18d2d691cfbf87118bac7254', function(err, block) { - if (err) { - console.log(err); - } - console.log('block', block); - }); - -}); - -daemon.on('open', function(status) { - daemon.log('status="%s"', status); -}); diff --git a/example/index.js b/example/index.js index f910357a..c04b17c9 100755 --- a/example/index.js +++ b/example/index.js @@ -13,7 +13,7 @@ process.title = 'bitcoind.js'; */ var daemon = require('../').daemon({ - directory: process.env.BITCOINDJS_DIR || '~/.bitcoin' + datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin' }); daemon.on('error', function(err) { diff --git a/example/index_stripped.js b/example/index_stripped.js deleted file mode 100755 index b45d5e6b..00000000 --- a/example/index_stripped.js +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env node - -/** - * bitcoind.js example - */ - -process.title = 'bitcoind_stripped.js'; - -/** - * daemon - */ - -var daemon = require('../index_stripped.js')({ - directory: '~/.libbitcoind-example' -}); - -daemon.on('error', function(err) { - daemon.log('error="%s"', err.message); -}); - -daemon.on('open', function(status) { - daemon.log('status="%s"', status); -}); diff --git a/example/node.js b/example/node.js index 7d50950d..3ab2d5a4 100644 --- a/example/node.js +++ b/example/node.js @@ -8,7 +8,7 @@ var log = chainlib.log; var configuration = { datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin', - testnet: true + network: 'testnet' }; var node = new BitcoinNode(configuration); diff --git a/integration/index.js b/integration/index.js index 94c9024c..8fa7c20a 100644 --- a/integration/index.js +++ b/integration/index.js @@ -23,7 +23,7 @@ describe('Basic Functionality', function() { before(function(done) { this.timeout(30000); bitcoind = require('../').daemon({ - directory: process.env.BITCOINDJS_DIR || '~/.bitcoin', + datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin', }); bitcoind.on('error', function(err) { diff --git a/lib/daemon.js b/lib/daemon.js index f1d18045..4e529ebc 100644 --- a/lib/daemon.js +++ b/lib/daemon.js @@ -126,7 +126,7 @@ Daemon.testnet = { ] }; -Bitcoin.regtest = { +Daemon.regtest = { name: 'regtest', peers: [ // hardcoded peers diff --git a/lib/daemon_stripped.js b/lib/daemon_stripped.js deleted file mode 100644 index 1783389c..00000000 --- a/lib/daemon_stripped.js +++ /dev/null @@ -1,959 +0,0 @@ -/** - * bitcoind.js - * Copyright (c) 2014, BitPay (MIT License) - * A bitcoind node.js binding. - */ - -var net = require('net'); -var EventEmitter = require('events').EventEmitter; -var bitcoindjs = require('../build/Debug/bitcoindjs.node'); -var util = require('util'); -var fs = require('fs'); -var mkdirp = require('mkdirp'); -var tiny = require('tiny').json; - -// Compatibility with old node versions: -var setImmediate = global.setImmediate || process.nextTick.bind(process); - -/** - * Daemon - */ - -var daemon = Daemon; - -function Daemon(options) { - var self = this; - - if (!(this instanceof Daemon)) { - return new Daemon(options); - } - - if (Object.keys(this.instances).length) { - throw new - Error('bitcoind.js cannot be instantiated more than once.'); - } - - EventEmitter.call(this); - - this.options = options || {}; - - if (typeof this.options === 'string') { - this.options = { datadir: this.options }; - } - - if (this.options.directory) { - this.options.datadir = this.options.directory; - delete this.options.directory; - } - - if (!this.options.datadir) { - this.options.datadir = '~/.bitcoind.js'; - } - - this.options.datadir = this.options.datadir.replace(/^~/, process.env.HOME); - - this.datadir = this.options.datadir; - this.config = this.datadir + '/bitcoin.conf'; - this.network = Daemon[this.options.testnet ? 'testnet' : 'livenet']; - - if (!fs.existsSync(this.datadir)) { - mkdirp.sync(this.datadir); - } - - if (!fs.existsSync(this.config)) { - var password = '' - + Math.random().toString(36).slice(2) - + Math.random().toString(36).slice(2) - + Math.random().toString(36).slice(2); - fs.writeFileSync(this.config, '' - + 'rpcuser=bitcoinrpc\n' - + 'rpcpassword=' + password + '\n' - ); - } - - // Add hardcoded peers - var data = fs.readFileSync(this.config, 'utf8'); - if (this.network.peers.length) { - var peers = this.network.peers.reduce(function(out, peer) { - if (!~data.indexOf('addnode=' + peer)) { - return out + 'addnode=' + peer + '\n'; - } - return out; - }, '\n'); - fs.writeFileSync(data + peers); - } - - // Copy config into testnet dir - if (this.network.name === 'testnet') { - if (!fs.existsSync(this.datadir + '/testnet3')) { - fs.mkdirSync(this.datadir + '/testnet3'); - } - fs.writeFileSync( - this.datadir + '/testnet3/bitcoin.conf', - fs.readFileSync(this.config)); - } - - Object.keys(exports).forEach(function(key) { - self[key] = exports[key]; - }); - - this.on('newListener', function(name) { - if (name === 'open') { - self.start(); - } - }); -} - -Daemon.prototype.__proto__ = EventEmitter.prototype; - -Daemon.livenet = { - name: 'livenet', - peers: [ - // hardcoded peers - ] -}; - -Daemon.testnet = { - name: 'testnet', - peers: [ - // hardcoded peers - ] -}; - -// Make sure signal handlers are not overwritten -Daemon._signalQueue = []; -Daemon._processOn = process.on; -process.addListener = -process.on = function(name, listener) { - if (~['SIGINT', 'SIGHUP', 'SIGQUIT'].indexOf(name.toUpperCase())) { - if (!Daemon.global || !Daemon.global._started) { - Daemon._signalQueue.push([name, listener]); - return; - } - } - return Daemon._processOn.apply(this, arguments); -}; - -Daemon.instances = {}; -Daemon.prototype.instances = Daemon.instances; - -Daemon.__defineGetter__('global', function() { - if (daemon.stopping) return []; - return Daemon.instances[Object.keys(Daemon.instances)[0]]; -}); - -Daemon.prototype.__defineGetter__('global', function() { - if (daemon.stopping) return []; - return Daemon.global; -}); - -tiny.debug = function() {}; -tiny.prototype.debug = function() {}; -tiny.error = function() {}; -tiny.prototype.error = function() {}; - -Daemon.db = tiny({ - file: process.env.HOME + '/.bitcoindjs.db', - saveIndex: false, - initialCache: false -}); - -Daemon.prototype.start = function(options, callback) { - var self = this; - - if (!callback) { - callback = options; - options = null; - } - - if (!options) { - options = {}; - } - - if (!callback) { - callback = utils.NOOP; - } - - if (this.instances[this.datadir]) { - return; - } - this.instances[this.datadir] = true; - - var none = {}; - var isSignal = {}; - var sigint = { name: 'SIGINT', signal: isSignal }; - var sighup = { name: 'SIGHUP', signal: isSignal }; - var sigquit = { name: 'SIGQUIT', signal: isSignal }; - var exitCaught = none; - var errorCaught = none; - - Object.keys(this.options).forEach(function(key) { - if (options[key] == null) { - options[key] = self.options[key]; - } - }); - - bitcoindjs.start(options, function(err, status) { - self._started = true; - - - [sigint, sighup, sigquit].forEach(function(signal) { - process.on(signal.name, signal.listener = function() { - if (process.listeners(signal.name).length > 1) { - return; - } - if (!self._shutdown) { - process.exit(0); - } else { - self.stop(); - exitCaught = signal; - } - }); - }); - - // Finally set signal handlers - process.on = process.addListener = Daemon._processOn; - Daemon._signalQueue.forEach(function(event) { - process.on(event[0], event[1]); - }); - - var exit = process.exit; - self._exit = function() { - return exit.apply(process, arguments); - }; - - process.exit = function(code) { - exitCaught = code || 0; - if (!self._shutdown) { - return self._exit(code); - } - self.stop(); - }; - - process.on('uncaughtException', function(err) { - if (process.listeners('uncaughtException').length > 1) { - return; - } - errorCaught = err; - self.error('Uncaught error: shutting down safely before throwing...'); - if (!self._shutdown) { - if (err && err.stack) { - console.error(err.stack); - } - self._exit(1); - return; - } - self.stop(); - }); - - setTimeout(function callee() { - // Wait until wallet is loaded: - if (callback) { - callback(err ? err : null); - } - - if (err) { - self.emit('error', err); - } else { - if (callback) { - self.emit('open', status); - } else { - self.emit('status', status); - } - } - - if (callback) { - callback = null; - } - }, 100); - }); - - // bitcoind's boost threads aren't in the thread pool - // or on node's event loop, so we need to keep node open. - this._shutdown = setInterval(function() { - if (!self._stoppingSaid && bitcoindjs.stopping()) { - self._stoppingSaid = true; - self.log('shutting down...'); - } - - if (bitcoindjs.stopped()) { - self.log('shut down.'); - - clearInterval(self._shutdown); - delete self._shutdown; - - if (exitCaught !== none) { - if (exitCaught.signal === isSignal) { - process.removeListener(exitCaught.name, exitCaught.listener); - setImmediate(function() { - process.kill(process.pid, exitCaught.name); - }); - return; - } - return self._exit(exitCaught); - } - - if (errorCaught !== none) { - if (errorCaught && errorCaught.stack) { - console.error(errorCaught.stack); - } - return self._exit(0); - } - } - }, 1000); -}; - -Daemon.prototype.getBlock = function(blockhash, callback) { - if (daemon.stopping) return []; - return bitcoindjs.getBlock(blockhash, function(err, block) { - if (err) return callback(err); - return callback(null, daemon.block(block)); - }); -}; - -Daemon.prototype.getBlockHeight = function(height, callback) { - if (daemon.stopping) return []; - return bitcoindjs.getBlock(+height, function(err, block) { - if (err) return callback(err); - return callback(null, daemon.block(block)); - }); -}; - -Daemon.prototype.getTransaction = -Daemon.prototype.getTx = function(txid, blockhash, callback) { - if (daemon.stopping) return []; - if (typeof txid === 'object' && txid) { - var options = txid; - callback = blockhash; - txid = options.txid || options.tx || options.txhash || options.id || options.hash; - blockhash = options.blockhash || options.block; - } - - if (typeof blockhash === 'function') { - callback = blockhash; - blockhash = ''; - } - - if (typeof blockhash !== 'string') { - if (blockhash) { - blockhash = blockhash.hash - || blockhash.blockhash - || (blockhash.getHash && blockhash.getHash()) - || ''; - } else { - blockhash = ''; - } - } - - return bitcoindjs.getTransaction(txid, blockhash, function(err, tx) { - if (err) return callback(err); - return callback(null, daemon.tx(tx)); - }); -}; - -Daemon.prototype.getTransactionWithBlock = function(txid, blockhash, callback) { - if (daemon.stopping) return []; - - var self = this; - var slow = true; - - if (typeof txid === 'object' && txid) { - var options = txid; - callback = blockhash; - txid = options.txid || options.tx || options.txhash || options.id || options.hash; - blockhash = options.blockhash || options.block; - slow = options.slow !== false; - } - - if (typeof blockhash === 'function') { - callback = blockhash; - blockhash = ''; - } - - if (typeof blockhash !== 'string') { - if (blockhash) { - blockhash = blockhash.hash - || blockhash.blockhash - || (blockhash.getHash && blockhash.getHash()) - || ''; - } else { - blockhash = ''; - } - } - - return bitcoindjs.getTransaction(txid, blockhash, function(err, tx) { - if (err) return callback(err); - - if (slow && !tx.blockhash) { - return self.getBlockByTx(txid, function(err, block, tx_) { - if (err) return callback(err); - return callback(null, tx, block); - }); - } - - return bitcoindjs.getBlock(tx.blockhash, function(err, block) { - if (err) return callback(err); - return callback(null, daemon.tx(tx), daemon.block(block)); - }); - }); -}; - -Daemon.prototype.getInfo = function() { - if (daemon.stopping) return []; - return bitcoindjs.getInfo(); -}; - -Daemon.prototype.getPeerInfo = function() { - if (daemon.stopping) return []; - return bitcoindjs.getPeerInfo(); -}; - -Daemon.prototype.getAddresses = function() { - if (daemon.stopping) return []; - return bitcoindjs.getAddresses(); -}; - -Daemon.prototype.getProgress = function(callback) { - if (daemon.stopping) return []; - return bitcoindjs.getProgress(callback); -}; - -Daemon.prototype.setGenerate = function(options) { - if (daemon.stopping) return []; - return bitcoindjs.setGenerate(options || {}); -}; - -Daemon.prototype.getGenerate = function(options) { - if (daemon.stopping) return []; - return bitcoindjs.getGenerate(options || {}); -}; - -Daemon.prototype.getMiningInfo = function() { - if (daemon.stopping) return []; - return bitcoindjs.getMiningInfo(); -}; - -Daemon.prototype.getAddrTransactions = function(address, callback) { - if (daemon.stopping) return []; - return daemon.db.get('addr-tx/' + address, function(err, records) { - var options = { - address: address, - blockheight: (records || []).reduce(function(out, record) { - return record.blockheight > out - ? record.blockheight - : out; - }, -1), - blocktime: (records || []).reduce(function(out, record) { - return record.blocktime > out - ? record.blocktime - : out; - }, -1) - }; - return bitcoindjs.getAddrTransactions(options, function(err, addr) { - if (err) return callback(err); - addr = daemon.addr(addr); - if (addr.tx[0] && !addr.tx[0].vout[0]) { - return daemon.db.set('addr-tx/' + address, [{ - txid: null, - blockhash: null, - blockheight: null, - blocktime: null - }], function() { - return callback(null, daemon.addr({ - address: addr.address, - tx: [] - })); - }); - } - var set = []; - if (records && records.length) { - set = records; - } - addr.tx.forEach(function(tx) { - set.push({ - txid: tx.txid, - blockhash: tx.blockhash, - blockheight: tx.blockheight, - blocktime: tx.blocktime - }); - }); - return daemon.db.set('addr-tx/' + address, set, function() { - return callback(null, addr); - }); - }); - }); -}; - -Daemon.prototype.getBestBlock = function(callback) { - if (daemon.stopping) return []; - var hash = bitcoindjs.getBestBlock(); - return bitcoindjs.getBlock(hash, callback); -}; - -Daemon.prototype.getChainHeight = function() { - if (daemon.stopping) return []; - return bitcoindjs.getChainHeight(); -}; - -Daemon.prototype.__defineGetter__('chainHeight', function() { - if (daemon.stopping) return []; - return this.getChainHeight(); -}); - -Daemon.prototype.getBlockByTxid = -Daemon.prototype.getBlockByTx = function(txid, callback) { - if (daemon.stopping) return []; - return daemon.db.get('block-tx/' + txid, function(err, block) { - if (block) { - return self.getBlock(block.hash, function(err, block) { - if (err) return callback(err); - var tx_ = block.tx.filter(function(tx) { - return tx.txid === txid; - })[0]; - return callback(null, block, tx_); - }); - } - return bitcoindjs.getBlockByTx(txid, function(err, block, tx_) { - if (err) return callback(err); - daemon.db.set('block-tx/' + txid, { hash: block.hash }, utils.NOOP); - return callback(null, daemon.block(block), daemon.tx(tx_)); - }); - }); -}; - -Daemon.prototype.getBlocksByDate = -Daemon.prototype.getBlocksByTime = function(options, callback) { - if (daemon.stopping) return []; - return bitcoindjs.getBlocksByTime(options, function(err, blocks) { - if (err) return callback(err); - return callback(null, blocks.map(function(block) { - return daemon.block(block) - })); - }); -}; - -Daemon.prototype.getFromTx = function(txid, callback) { - if (daemon.stopping) return []; - return bitcoindjs.getFromTx(txid, function(err, txs) { - if (err) return callback(err); - return callback(null, txs.map(function(tx) { - return daemon.tx(tx) - })); - }); -}; - -Daemon.prototype.getLastFileIndex = function() { - if (daemon.stopping) return []; - return bitcoindjs.getLastFileIndex(); -}; - -Daemon.prototype.log = -Daemon.prototype.info = function() { - if (daemon.stopping) return []; - if (this.options.silent) return; - if (typeof arguments[0] !== 'string') { - var out = util.inspect(arguments[0], null, 20, true); - return process.stdout.write('bitcoind.js: ' + out + '\n'); - } - var out = util.format.apply(util, arguments); - return process.stdout.write('bitcoind.js: ' + out + '\n'); -}; - -Daemon.prototype.error = function() { - if (daemon.stopping) return []; - if (this.options.silent) return; - if (typeof arguments[0] !== 'string') { - var out = util.inspect(arguments[0], null, 20, true); - return process.stderr.write('bitcoind.js: ' + out + '\n'); - } - var out = util.format.apply(util, arguments); - return process.stderr.write('bitcoind.js: ' + out + '\n'); -}; - -Daemon.prototype.stop = -Daemon.prototype.close = function(callback) { - if (daemon.stopping) return []; - var self = this; - return bitcoindjs.stop(function(err, status) { - if (err) { - self.error(err.message); - } else { - self.log(status); - } - if (!callback) return; - return callback(err, status); - }); -}; - -Daemon.prototype.__defineGetter__('stopping', function() { - return bitcoindjs.stopping() || bitcoindjs.stopped(); -}); - -Daemon.prototype.__defineGetter__('stopped', function() { - return bitcoindjs.stopped(); -}); - -Daemon.__defineGetter__('stopping', function() { - return bitcoindjs.stopping() || bitcoindjs.stopped(); -}); - -Daemon.__defineGetter__('stopped', function() { - return bitcoindjs.stopped(); -}); - -/** - * Block - */ - -function Block(data) { - if (!(this instanceof Block)) { - return new Block(data); - } - - if (typeof data === 'string') { - return Block.fromHex(data); - } - - if (data instanceof Block) { - return data; - } - - if (daemon.stopping) return []; - - var self = this; - - Object.keys(data).forEach(function(key) { - if (!self[key]) { - self[key] = data[key]; - } - }); - - this.tx = this.tx.map(function(tx) { - return daemon.tx(tx); - }); - - if (!this.hex) { - this.hex = this.toHex(); - } -} - -Object.defineProperty(Block.prototype, '_blockFlag', { - __proto__: null, - configurable: false, - enumerable: false, - writable: false, - value: {} -}); - -Block.isBlock = function(block) { - if (daemon.stopping) return []; - return block._blockFlag === Block.prototype._blockFlag; -}; - -Block.fromHex = function(hex) { - if (daemon.stopping) return []; - return daemon.block(bitcoindjs.blockFromHex(hex)); -}; - -Block.prototype.getHash = function(enc) { - if (daemon.stopping) return []; - var data = bitcoindjs.getBlockHex(this); - if (!this.hash || this.hash !== data.hash) { - this.hash = data.hash; - } - if (enc === 'hex') return data.hash; - var buf = new Buffer(data.hash, 'hex'); - var out = enc ? buf.toString(enc) : buf; - return out; -}; - -Block.prototype.verify = function() { - if (daemon.stopping) return []; - return this.verified = this.verified || bitcoindjs.verifyBlock(this); -}; - -Block.prototype.toHex = function() { - if (daemon.stopping) return []; - var hex = Block.toHex(this); - if (!this.hex || this.hex !== hex) { - this.hex = hex; - } - return hex; -}; - -Block.toHex = function(block) { - if (daemon.stopping) return []; - var data = bitcoindjs.getBlockHex(block); - return data.hex; -}; - -Block.prototype.toBinary = function() { - if (daemon.stopping) return []; - return Block.toBinary(this); -}; - -Block.toBinary = function(block) { - if (daemon.stopping) return []; - var data = bitcoindjs.getBlockHex(block); - return new Buffer(data.hex, 'hex'); -}; - -/** - * Transaction - */ - -function Transaction(data) { - if (!(this instanceof Transaction)) { - return new Transaction(data); - } - - if (typeof data === 'string') { - return Transaction.fromHex(data); - } - - if (data instanceof Transaction) { - return data; - } - - if (daemon.stopping) return []; - - var self = this; - - Object.keys(data).forEach(function(key) { - if (!self[key]) { - self[key] = data[key]; - } - }); - - if (!this.hex) { - this.hex = this.toHex(); - } -} - -Object.defineProperty(Transaction.prototype, '_txFlag', { - __proto__: null, - configurable: false, - enumerable: false, - writable: false, - value: {} -}); - -Transaction.isTransaction = -Transaction.isTx = function(tx) { - if (daemon.stopping) return []; - return tx._txFlag === Transaction.prototype._txFlag; -}; - -Transaction.fromHex = function(hex) { - if (daemon.stopping) return []; - return daemon.tx(bitcoindjs.txFromHex(hex)); -}; - -Transaction.prototype.verify = function() { - if (daemon.stopping) return []; - return this.verified = this.verified || bitcoindjs.verifyTransaction(this); -}; - -Transaction.prototype.sign = -Transaction.prototype.fill = function(options) { - if (daemon.stopping) return []; - return Transaction.fill(this, options); -}; - -Transaction.sign = -Transaction.fill = function(tx, options) { - if (daemon.stopping) return []; - var isTx = daemon.tx.isTx(tx) - , newTx; - - if (!isTx) { - tx = daemon.tx(tx); - } - - try { - newTx = bitcoindjs.fillTransaction(tx, options || {}); - } catch (e) { - return false; - } - - Object.keys(newTx).forEach(function(key) { - tx[key] = newTx[key]; - }); - - return tx; -}; - -Transaction.prototype.getHash = function(enc) { - if (daemon.stopping) return []; - var data = bitcoindjs.getTxHex(this); - if (!this.txid || this.txid !== data.hash) { - this.txid = data.hash; - } - if (enc === 'hex') return data.hash; - var buf = new Buffer(data.hash, 'hex'); - var out = enc ? buf.toString(enc) : buf; - return out; -}; - -Transaction.prototype.isCoinbase = function() { - if (daemon.stopping) return []; - return this.vin.length === 1 && this.vin[0].coinbase; -}; - -Transaction.prototype.toHex = function() { - if (daemon.stopping) return []; - var hex = Transaction.toHex(this); - if (!this.hex || hex !== this.hex) { - this.hex = hex; - } - return hex; -}; - -Transaction.toHex = function(tx) { - if (daemon.stopping) return []; - var data = bitcoindjs.getTxHex(tx); - return data.hex; -}; - -Transaction.prototype.toBinary = function() { - if (daemon.stopping) return []; - return Transaction.toBinary(this); -}; - -Transaction.toBinary = function(tx) { - if (daemon.stopping) return []; - var data = bitcoindjs.getTxHex(tx); - return new Buffer(data.hex, 'hex'); -}; - -Transaction.broadcast = function(tx, options, callback) { - if (daemon.stopping) return []; - if (typeof tx === 'string') { - tx = { hex: tx }; - } - - if (!callback) { - callback = options; - options = null; - } - - if (!options) { - options = {}; - } - - var fee = options.overrideFees = options.overrideFees || false; - var own = options.ownOnly = options.ownOnly || false; - - if (!callback) { - callback = utils.NOOP; - } - - if (!daemon.isTx(tx)) { - tx = daemon.tx(tx); - } - - return bitcoindjs.broadcastTx(tx, fee, own, function(err, hash, tx) { - if (err) { - if (callback === utils.NOOP) { - daemon.global.emit('error', err); - } - return callback(err); - } - tx = daemon.tx(tx); - daemon.global.emit('broadcast', tx); - return callback(null, hash, tx); - }); -}; - -Transaction.prototype.broadcast = function(options, callback) { - if (daemon.stopping) return []; - if (!callback) { - callback = options; - options = null; - } - return Transaction.broadcast(this, options, callback); -}; - -/** - * Addresses - */ - -function Addresses(data) { - if (!(this instanceof Addresses)) { - return new Addresses(data); - } - - if (data instanceof Addresses) { - return data; - } - - if (daemon.stopping) return []; - - var self = this; - - Object.keys(data).forEach(function(key) { - if (!self[key]) { - self[key] = data[key]; - } - }); -} - -Object.defineProperty(Transaction.prototype, '_addrFlag', { - __proto__: null, - configurable: false, - enumerable: false, - writable: false, - value: {} -}); - -Addresses.isAddresses = -Addresses.isAddr = function(addr) { - if (daemon.stopping) return []; - return addr._txFlag === Addresses.prototype._addrFlag; -}; - -/** - * Utils - */ - -var utils = {}; - -utils.forEach = function(obj, iter, done) { - if (daemon.stopping) return []; - var pending = obj.length; - if (!pending) return done(); - var next = function() { - if (!--pending) done(); - }; - obj.forEach(function(item) { - iter(item, next); - }); -}; - -utils.NOOP = function() {}; - -/** - * Expose - */ - -module.exports = exports = daemon; - -exports.Daemon = daemon; -exports.daemon = daemon; -exports.bitcoind = daemon; - -exports.native = bitcoindjs; -exports.bitcoindjs = bitcoindjs; - -exports.Block = Block; -exports.block = Block; - -exports.Transaction = Transaction; -exports.transaction = Transaction; -exports.tx = Transaction; - -exports.Addresses = Addresses; -exports.addresses = Addresses; -exports.addr = Addresses; - -exports.utils = utils; From 7f5c509254ac4c39d1fdcb26250a258dadcaec7b Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 13:47:25 -0400 Subject: [PATCH 06/17] Update tests to pass network option with regtest option. --- lib/node.js | 31 +++++++++++++----- test/node.unit.js | 80 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 80 insertions(+), 31 deletions(-) diff --git a/lib/node.js b/lib/node.js index decc99e6..1c0ef8dd 100644 --- a/lib/node.js +++ b/lib/node.js @@ -74,7 +74,7 @@ Node.prototype._loadBitcoinConf = function(config) { Node.prototype._loadBitcoind = function(config) { var bitcoindConfig = {}; bitcoindConfig.datadir = config.datadir; - bitcoindConfig.testnet = config.testnet; + bitcoindConfig.network = config.network; // start the bitcoind daemon this.bitcoind = daemon(bitcoindConfig); @@ -117,11 +117,23 @@ Node.prototype._syncBitcoind = function() { }; Node.prototype._loadNetwork = function(config) { - if (config.network) { - Networks.add(config.network); - this.network = Networks.get(config.network.name); - } else if (config.testnet) { + 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: [ ] + }); + this.network = Networks.get('regtest'); } else { this.network = Networks.get('livenet'); } @@ -142,11 +154,14 @@ Node.prototype._loadDB = function(config) { // based on the network configuration and the datadir $.checkArgument(config.datadir, 'Please specify "datadir" in configuration options'); $.checkState(this.network, 'Network property not defined'); + var regtest = Networks.get('regtest'); var datadir = config.datadir.replace(/^~/, process.env.HOME); - if (this.network === Networks.testnet) { - config.db.path = datadir + '/testnet3/bitcoindjs.db'; - } else if (this.network === Networks.livenet) { + if (this.network === Networks.livenet) { config.db.path = datadir + '/bitcoindjs.db'; + } else if (this.network === Networks.testnet) { + config.db.path = datadir + '/testnet3/bitcoindjs.db'; + } else if (this.network === regtest) { + config.db.path = datadir + '/regtest/bitcoindjs.db'; } else { throw new Error('Unknown network: ' + this.network); } diff --git a/test/node.unit.js b/test/node.unit.js index 5d607681..1660836e 100644 --- a/test/node.unit.js +++ b/test/node.unit.js @@ -168,38 +168,24 @@ describe('Bitcoind Node', function() { }); }); describe('#_loadNetwork', function() { - it('should add the network that was listed in the config', function() { - var config = { - network: { - name: 'chainlib', - alias: 'chainlib', - pubkeyhash: 0x1c, - privatekey: 0x1e, - scripthash: 0x28, - xpubkey: 0x02e8de8f, - xprivkey: 0x02e8da54, - networkMagic: 0x0c110907, - port: 9333 - } - }; - var node = new Node(config); - node._loadNetwork(config); - var network = Networks.get('chainlib'); - should.exist(network); - node.network.name.should.equal('chainlib'); - }); it('should use the testnet network if testnet is specified', function() { var config = { - testnet: true + network: 'testnet' }; - var node = new Node(config); node._loadNetwork(config); node.network.name.should.equal('testnet'); }); + it('should use the regtest network if regtest is specified', function() { + var config = { + network: 'regtest' + }; + var node = new Node(config); + node._loadNetwork(config); + node.network.name.should.equal('regtest'); + }); it('should use the livenet network if nothing is specified', function() { var config = {}; - var node = new Node(config); node._loadNetwork(config); node.network.name.should.equal('livenet'); @@ -245,6 +231,54 @@ describe('Bitcoind Node', function() { node._loadDB(config); }).should.throw('Unknown network'); }); + it('should load the db with regtest', function() { + var DB = function(config) { + config.path.should.equal(process.env.HOME + '/.bitcoin/regtest/bitcoindjs.db'); + }; + var config = { + DB: DB, + datadir: '~/.bitcoin' + }; + + var node = new Node(config); + // Switch to use 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: [ ] + }); + var regtest = Networks.get('regtest'); + node.network = regtest; + node._loadDB(config); + node.db.should.be.instanceof(DB); + Networks.remove(regtest); + // Add testnet back + 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' + ] + }); + }); }); describe('#_loadP2P', function() { it('should load p2p', function() { From bcdf75a5e1d0feafbb3d9e8f1e546ea23f7a925a Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 13:49:20 -0400 Subject: [PATCH 07/17] Add environment variable to travis to compile for testing. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index f66abf3a..1487b003 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: node_js +env: +- BITCOINDJS_ENV=test node_js: - "0.12" before_install: From 910fb561acdd9609f419f91eee83a2cebe7692e0 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 13:56:57 -0400 Subject: [PATCH 08/17] Add Berkeley DB depends for testing regtest. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1487b003..cfdc0571 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,5 +5,8 @@ node_js: - "0.12" before_install: - sudo apt-get install libboost1.48-all-dev + - sudo add-apt-repository ppa:bitcoin/bitcoin + - sudo apt-get update + - sudo apt-get install libdb4.8-dev libdb4.8++-dev - git config --global user.email "dev@bitpay.com" - git config --global user.name "BitPay, Inc." From 787dcde00b7251c1a74307c964029533fc1afe79 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 14:00:10 -0400 Subject: [PATCH 09/17] Answer yes to adding the ppa. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cfdc0571..a9ffc9a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ node_js: - "0.12" before_install: - sudo apt-get install libboost1.48-all-dev - - sudo add-apt-repository ppa:bitcoin/bitcoin + - sudo add-apt-repository -y ppa:bitcoin/bitcoin - sudo apt-get update - sudo apt-get install libdb4.8-dev libdb4.8++-dev - git config --global user.email "dev@bitpay.com" From 4c7309838bd4895d75efb3e1739a8f92193a6bfc Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 14:33:19 -0400 Subject: [PATCH 10/17] Run integration/regtest.js in ci tests. --- .travis.yml | 2 + integration/{index.js => livenet.js} | 0 integration/regtest.js | 189 +++++++++++++++++++++++++++ 3 files changed, 191 insertions(+) rename integration/{index.js => livenet.js} (100%) create mode 100644 integration/regtest.js diff --git a/.travis.yml b/.travis.yml index a9ffc9a7..337cde38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,3 +10,5 @@ before_install: - sudo apt-get install libdb4.8-dev libdb4.8++-dev - git config --global user.email "dev@bitpay.com" - git config --global user.name "BitPay, Inc." +script: + - mocha -R spec integration/regtest.js \ No newline at end of file diff --git a/integration/index.js b/integration/livenet.js similarity index 100% rename from integration/index.js rename to integration/livenet.js diff --git a/integration/regtest.js b/integration/regtest.js new file mode 100644 index 00000000..8fa7c20a --- /dev/null +++ b/integration/regtest.js @@ -0,0 +1,189 @@ +'use strict'; + +// These tests require a fully synced Bitcore Code data directory. +// To run the tests: $ mocha -R spec index.js + +var chai = require('chai'); +var bitcore = require('bitcore'); +var bitcoind; + +/* jshint unused: false */ +var should = chai.should(); +var assert = chai.assert; +var sinon = require('sinon'); +var txData = require('./livenet-tx-data.json'); +var blockData = require('./livenet-block-data.json'); +var testTxData = require('./livenet-tx-data.json'); +var spentData = require('./livenet-spents.json').spent; +var unspentData = require('./livenet-spents.json').unspent; +var testBlockData = require('./testnet-block-data.json'); + +describe('Basic Functionality', function() { + + before(function(done) { + this.timeout(30000); + bitcoind = require('../').daemon({ + datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin', + }); + + bitcoind.on('error', function(err) { + bitcoind.log('error="%s"', err.message); + }); + + bitcoind.on('open', function(status) { + bitcoind.log('status="%s"', status); + }); + + console.log('Waiting for Bitcoin Core to initialize...'); + + bitcoind.on('ready', function() { + done(); + }); + + }); + + after(function(done) { + this.timeout(20000); + bitcoind.stop(function(err, result) { + done(); + }); + }); + + describe('get transactions by hash', function() { + txData.forEach(function(data) { + var tx = bitcore.Transaction(); + tx.fromString(data); + it('for tx ' + tx.hash, function(done) { + bitcoind.getTransaction(tx.hash, true, function(err, response) { + if (err) { + throw err; + } + assert(response.toString('hex') === data, 'incorrect tx data for ' + tx.hash); + done(); + }); + }); + }); + }); + + describe('determine if outpoint is unspent/spent', function() { + spentData.forEach(function(data) { + it('for spent txid ' + data.txid + ' and output ' + data.outputIndex, function() { + var spent = bitcoind.isSpent(data.txid, data.outputIndex, true); + spent.should.equal(true); + }); + }); + + unspentData.forEach(function(data) { + it('for unspent txid ' + data.txid + ' and output ' + data.outputIndex, function() { + var spent = bitcoind.isSpent(data.txid, data.outputIndex, true); + spent.should.equal(false); + }); + }); + }); + + describe('get blocks by hash', function() { + + blockData.forEach(function(data) { + var block = bitcore.Block.fromString(data); + it('block ' + block.hash, function(done) { + bitcoind.getBlock(block.hash, function(err, response) { + assert(response.toString('hex') === data, 'incorrect block data for ' + block.hash); + done(); + }); + }); + }); + }); + + describe('get blocks by height', function() { + + var knownHeights = [ + [0, '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'], + [1, '00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048'], + [100000,'000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506'], + [314159, '00000000000000001bb82a7f5973618cfd3185ba1ded04dd852a653f92a27c45'] + ]; + + knownHeights.forEach(function(data) { + it('block at height ' + data[0], function(done) { + bitcoind.getBlock(data[0], function(err, response) { + if (err) { + throw err; + } + var block = bitcore.Block.fromBuffer(response); + block.hash.should.equal(data[1]); + done(); + }); + }); + }); + }); + + describe('get chain work', function() { + it('will get the total work for the genesis block via hash', function() { + var hash = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'; + var work = bitcoind.getChainWork(hash); + work.should.equal('0000000000000000000000000000000000000000000000000000000100010001'); + }); + it('will get the total work for block #300000 via hash', function() { + var hash = '000000000000000082ccf8f1557c5d40b21edabb18d2d691cfbf87118bac7254'; + var work = bitcoind.getChainWork(hash); + work.should.equal('000000000000000000000000000000000000000000005a7b3c42ea8b844374e9'); + }); + it('will return undefined for unknown block', function() { + var hash = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + var work = bitcoind.getChainWork(hash); + should.equal(work, undefined); + }); + }); + + describe('mempool functionality', function() { + + var fromAddress = 'mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1'; + var utxo = { + address: fromAddress, + txId: 'a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458', + outputIndex: 0, + script: bitcore.Script.buildPublicKeyHashOut(fromAddress).toString(), + satoshis: 100000 + }; + var toAddress = 'mrU9pEmAx26HcbKVrABvgL7AwA5fjNFoDc'; + var changeAddress = 'mgBCJAsvzgT2qNNeXsoECg2uPKrUsZ76up'; + var changeAddressP2SH = '2N7T3TAetJrSCruQ39aNrJvYLhG1LJosujf'; + var privateKey = 'cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY'; + var private1 = '6ce7e97e317d2af16c33db0b9270ec047a91bff3eff8558afb5014afb2bb5976'; + var private2 = 'c9b26b0f771a0d2dad88a44de90f05f416b3b385ff1d989343005546a0032890'; + var tx = new bitcore.Transaction(); + tx.from(utxo); + tx.to(toAddress, 50000); + tx.change(changeAddress); + tx.sign(privateKey); + + 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 outputs by address', function() { + var outputs = bitcoind.getMempoolOutputs(changeAddress); + var expected = [ + { + script: 'OP_DUP OP_HASH160 073b7eae2823efa349e3b9155b8a735526463a0f OP_EQUALVERIFY OP_CHECKSIG', + satoshis: 40000, + txid: tx.hash, + outputIndex: 1 + } + ]; + outputs.should.deep.equal(expected); + }); + + }); + +}); + From 072c109174a9251f88f2cc36673c1c697c4fe516 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 16:40:12 -0400 Subject: [PATCH 11/17] Update regtest tests. --- example/index.js | 3 +- integration/regtest.js | 115 ++++++++--------------------------------- package.json | 5 +- 3 files changed, 26 insertions(+), 97 deletions(-) diff --git a/example/index.js b/example/index.js index c04b17c9..98e5c7b4 100755 --- a/example/index.js +++ b/example/index.js @@ -13,7 +13,8 @@ process.title = 'bitcoind.js'; */ var daemon = require('../').daemon({ - datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin' + datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin', + network: 'regtest' }); daemon.on('error', function(err) { diff --git a/integration/regtest.js b/integration/regtest.js index 8fa7c20a..46973ddc 100644 --- a/integration/regtest.js +++ b/integration/regtest.js @@ -11,12 +11,7 @@ var bitcoind; var should = chai.should(); var assert = chai.assert; var sinon = require('sinon'); -var txData = require('./livenet-tx-data.json'); -var blockData = require('./livenet-block-data.json'); -var testTxData = require('./livenet-tx-data.json'); -var spentData = require('./livenet-spents.json').spent; -var unspentData = require('./livenet-spents.json').unspent; -var testBlockData = require('./testnet-block-data.json'); +var BitcoinRPC = require('bitcoind-rpc'); describe('Basic Functionality', function() { @@ -24,6 +19,11 @@ describe('Basic Functionality', function() { this.timeout(30000); bitcoind = require('../').daemon({ datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin', + network: 'regtest', + server: true, + rpcuser: 'bitcoin', + rpcpassword: 'local321', + rpcallowip: '127.0.0.1' }); bitcoind.on('error', function(err) { @@ -37,7 +37,21 @@ describe('Basic Functionality', function() { console.log('Waiting for Bitcoin Core to initialize...'); bitcoind.on('ready', function() { - done(); + + var client = new BitcoinRPC({ + protocol: 'http', + host: '127.0.0.1', + port: 18332, + user: 'bitcoin', + pass: 'local321' + }); + + client.generate(100, function(err, result) { + console.log('err', err); + console.log('result', result); + done(); + }); + }); }); @@ -49,92 +63,6 @@ describe('Basic Functionality', function() { }); }); - describe('get transactions by hash', function() { - txData.forEach(function(data) { - var tx = bitcore.Transaction(); - tx.fromString(data); - it('for tx ' + tx.hash, function(done) { - bitcoind.getTransaction(tx.hash, true, function(err, response) { - if (err) { - throw err; - } - assert(response.toString('hex') === data, 'incorrect tx data for ' + tx.hash); - done(); - }); - }); - }); - }); - - describe('determine if outpoint is unspent/spent', function() { - spentData.forEach(function(data) { - it('for spent txid ' + data.txid + ' and output ' + data.outputIndex, function() { - var spent = bitcoind.isSpent(data.txid, data.outputIndex, true); - spent.should.equal(true); - }); - }); - - unspentData.forEach(function(data) { - it('for unspent txid ' + data.txid + ' and output ' + data.outputIndex, function() { - var spent = bitcoind.isSpent(data.txid, data.outputIndex, true); - spent.should.equal(false); - }); - }); - }); - - describe('get blocks by hash', function() { - - blockData.forEach(function(data) { - var block = bitcore.Block.fromString(data); - it('block ' + block.hash, function(done) { - bitcoind.getBlock(block.hash, function(err, response) { - assert(response.toString('hex') === data, 'incorrect block data for ' + block.hash); - done(); - }); - }); - }); - }); - - describe('get blocks by height', function() { - - var knownHeights = [ - [0, '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'], - [1, '00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048'], - [100000,'000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506'], - [314159, '00000000000000001bb82a7f5973618cfd3185ba1ded04dd852a653f92a27c45'] - ]; - - knownHeights.forEach(function(data) { - it('block at height ' + data[0], function(done) { - bitcoind.getBlock(data[0], function(err, response) { - if (err) { - throw err; - } - var block = bitcore.Block.fromBuffer(response); - block.hash.should.equal(data[1]); - done(); - }); - }); - }); - }); - - describe('get chain work', function() { - it('will get the total work for the genesis block via hash', function() { - var hash = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f'; - var work = bitcoind.getChainWork(hash); - work.should.equal('0000000000000000000000000000000000000000000000000000000100010001'); - }); - it('will get the total work for block #300000 via hash', function() { - var hash = '000000000000000082ccf8f1557c5d40b21edabb18d2d691cfbf87118bac7254'; - var work = bitcoind.getChainWork(hash); - work.should.equal('000000000000000000000000000000000000000000005a7b3c42ea8b844374e9'); - }); - it('will return undefined for unknown block', function() { - var hash = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; - var work = bitcoind.getChainWork(hash); - should.equal(work, undefined); - }); - }); - describe('mempool functionality', function() { var fromAddress = 'mszYqVnqKoQx4jcTdJXxwKAissE3Jbrrc1'; @@ -186,4 +114,3 @@ describe('Basic Functionality', function() { }); }); - diff --git a/package.json b/package.json index f8e0794e..ed670d4d 100644 --- a/package.json +++ b/package.json @@ -50,10 +50,11 @@ "devDependencies": { "benchmark": "1.0.0", "bitcoin": "^2.3.2", + "bitcoind-rpc": "^0.3.0", "bitcore": "^0.12.12", "chai": "^3.0.0", "mocha": "~1.16.2", - "sinon": "^1.15.4", - "proxyquire": "^1.3.1" + "proxyquire": "^1.3.1", + "sinon": "^1.15.4" } } From bcc3a4c2e1ce1695d19adf5e07436cf07e589b54 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 17:32:24 -0400 Subject: [PATCH 12/17] Use a local data directory specific for reg testing. --- .travis.yml | 2 +- integration/data/bitcoin.conf | 8 +++++ integration/regtest.js | 63 +++++++++++++++++++---------------- package.json | 1 + 4 files changed, 45 insertions(+), 29 deletions(-) create mode 100644 integration/data/bitcoin.conf diff --git a/.travis.yml b/.travis.yml index 337cde38..dc85ddea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,4 +11,4 @@ before_install: - git config --global user.email "dev@bitpay.com" - git config --global user.name "BitPay, Inc." script: - - mocha -R spec integration/regtest.js \ No newline at end of file + - _mocha -R spec integration/regtest.js \ No newline at end of file diff --git a/integration/data/bitcoin.conf b/integration/data/bitcoin.conf new file mode 100644 index 00000000..2ebd94ba --- /dev/null +++ b/integration/data/bitcoin.conf @@ -0,0 +1,8 @@ +server=1 +whitelist=127.0.0.1 +txindex=1 +rpcallowip=127.0.0.1 +rpcuser=bitcoin +rpcpassword=local321 + + diff --git a/integration/regtest.js b/integration/regtest.js index 46973ddc..e4f6976e 100644 --- a/integration/regtest.js +++ b/integration/regtest.js @@ -5,6 +5,7 @@ var chai = require('chai'); var bitcore = require('bitcore'); +var rimraf = require('rimraf'); var bitcoind; /* jshint unused: false */ @@ -17,39 +18,45 @@ describe('Basic Functionality', function() { before(function(done) { this.timeout(30000); - bitcoind = require('../').daemon({ - datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin', - network: 'regtest', - server: true, - rpcuser: 'bitcoin', - rpcpassword: 'local321', - rpcallowip: '127.0.0.1' - }); - bitcoind.on('error', function(err) { - bitcoind.log('error="%s"', err.message); - }); + rimraf('./data/regtest', function(err) { - bitcoind.on('open', function(status) { - bitcoind.log('status="%s"', status); - }); + if (err) { + throw err; + } - console.log('Waiting for Bitcoin Core to initialize...'); - - bitcoind.on('ready', function() { - - var client = new BitcoinRPC({ - protocol: 'http', - host: '127.0.0.1', - port: 18332, - user: 'bitcoin', - pass: 'local321' + bitcoind = require('../').daemon({ + datadir: './data', + network: 'regtest' }); - client.generate(100, function(err, result) { - console.log('err', err); - console.log('result', result); - done(); + bitcoind.on('error', function(err) { + bitcoind.log('error="%s"', err.message); + }); + + bitcoind.on('open', function(status) { + bitcoind.log('status="%s"', status); + }); + + console.log('Waiting for Bitcoin Core to initialize...'); + + bitcoind.on('ready', function() { + + var client = new BitcoinRPC({ + protocol: 'http', + host: '127.0.0.1', + port: 18332, + user: 'bitcoin', + pass: 'local321' + }); + + client.generate(100, function(err) { + if (err) { + throw err; + } + done(); + }); + }); }); diff --git a/package.json b/package.json index ed670d4d..1183d515 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "chai": "^3.0.0", "mocha": "~1.16.2", "proxyquire": "^1.3.1", + "rimraf": "^2.4.2", "sinon": "^1.15.4" } } From be5e885f96161a0f1984beb106b8ba10ef2dacd0 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 18:18:42 -0400 Subject: [PATCH 13/17] Use datadir path relative to test file. --- integration/regtest.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/integration/regtest.js b/integration/regtest.js index e4f6976e..696f25dd 100644 --- a/integration/regtest.js +++ b/integration/regtest.js @@ -19,14 +19,16 @@ describe('Basic Functionality', function() { before(function(done) { this.timeout(30000); - rimraf('./data/regtest', function(err) { + var datadir = __dirname + '/data'; + + rimraf(datadir + '/regtest', function(err) { if (err) { throw err; } bitcoind = require('../').daemon({ - datadir: './data', + datadir: datadir, network: 'regtest' }); From ebdab57b78f163ec76aec0631b9385c0f8e963a5 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 18:46:00 -0400 Subject: [PATCH 14/17] Added get block by hash regtest. --- integration/regtest.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/integration/regtest.js b/integration/regtest.js index 696f25dd..31a90097 100644 --- a/integration/regtest.js +++ b/integration/regtest.js @@ -13,6 +13,7 @@ var should = chai.should(); var assert = chai.assert; var sinon = require('sinon'); var BitcoinRPC = require('bitcoind-rpc'); +var blockHashes = []; describe('Basic Functionality', function() { @@ -52,10 +53,13 @@ describe('Basic Functionality', function() { pass: 'local321' }); - client.generate(100, function(err) { + console.log('Generating 100 blocks...'); + + client.generate(100, function(err, response) { if (err) { throw err; } + blockHashes = response.result; done(); }); @@ -122,4 +126,21 @@ describe('Basic Functionality', function() { }); + 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(); + }); + }); + }); + }); + }); From 5a8dfc1e077c26df2b33c0319b180c98f609e6f3 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 18:48:07 -0400 Subject: [PATCH 15/17] Added tests for getting blocks by height. --- integration/regtest.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/integration/regtest.js b/integration/regtest.js index 31a90097..31ba40b0 100644 --- a/integration/regtest.js +++ b/integration/regtest.js @@ -143,4 +143,23 @@ describe('Basic Functionality', function() { }); }); + describe('get blocks by height', function() { + + [0,1,2,3,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(); + }); + }); + }); + }); + }); From 2abb0cc393cad90ca8f64f7521b5d696d3cdb55c Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 21 Jul 2015 19:06:27 -0400 Subject: [PATCH 16/17] Add unit tests to travis ci config. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dc85ddea..d050d692 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,4 +11,5 @@ before_install: - git config --global user.email "dev@bitpay.com" - git config --global user.name "BitPay, Inc." script: - - _mocha -R spec integration/regtest.js \ No newline at end of file + - _mocha -R spec integration/regtest.js + - _mocha -R spec --recursive From 73b359425d36194cdcbf3c112778cbf4f8f94fea Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Wed, 22 Jul 2015 09:52:23 -0400 Subject: [PATCH 17/17] Added comments to test files and guard for environment variables. --- integration/livenet.js | 2 +- integration/regtest.js | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/integration/livenet.js b/integration/livenet.js index 8fa7c20a..245b19d5 100644 --- a/integration/livenet.js +++ b/integration/livenet.js @@ -1,7 +1,7 @@ 'use strict'; // These tests require a fully synced Bitcore Code data directory. -// To run the tests: $ mocha -R spec index.js +// To run the tests: $ mocha -R spec livenet.js var chai = require('chai'); var bitcore = require('bitcore'); diff --git a/integration/regtest.js b/integration/regtest.js index 31ba40b0..bc313a3b 100644 --- a/integration/regtest.js +++ b/integration/regtest.js @@ -1,7 +1,14 @@ 'use strict'; -// These tests require a fully synced Bitcore Code data directory. -// To run the tests: $ mocha -R spec index.js +// These tests require bitcoind.js Bitcoin Core bindings to be compiled with +// the environment variable BITCOINDJS_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 + +if (process.env.BITCOINDJS_ENV !== 'test') { + console.log('Please set the environment variable BITCOINDJS_ENV=test and make sure bindings are compiled for testing'); + process.exit(); +} var chai = require('chai'); var bitcore = require('bitcore');