Merge pull request #44 from braydonf/regtest

Add "regtest" testing capabilities
This commit is contained in:
Chris Kleeschulte 2015-07-22 10:31:47 -04:00
commit 6cc6d7f4de
20 changed files with 395 additions and 1138 deletions

View File

@ -1,7 +1,15 @@
language: node_js
env:
- BITCOINDJS_ENV=test
node_js:
- "0.12"
before_install:
- sudo apt-get install libboost1.48-all-dev
- 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"
- git config --global user.name "BitPay, Inc."
script:
- _mocha -R spec integration/regtest.js
- _mocha -R spec --recursive

View File

@ -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}`.

View File

@ -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) {

View File

@ -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

View File

@ -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=

View File

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

View File

@ -1,18 +1,8 @@
From 05084f2a640b862132588b322461ec8e13058fc3 Mon Sep 17 00:00:00 2001
From: Chris Kleeschulte <chrisk@bitpay.com>
Date: Mon, 13 Jul 2015 12:49:30 -0400
Subject: [PATCH] libbitcoind
commit 29c1ca452ba6178d6b17be0a0b5a65567ba846af
Author: Chris Kleeschulte <chrisk@bitpay.com>
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 <boost/filesystem/path.hpp>
+#include <boost/thread/mutex.hpp>
@ -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)

View File

@ -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);
});

View File

@ -13,7 +13,8 @@ process.title = 'bitcoind.js';
*/
var daemon = require('../').daemon({
directory: process.env.BITCOINDJS_DIR || '~/.bitcoin'
datadir: process.env.BITCOINDJS_DIR || '~/.bitcoin',
network: 'regtest'
});
daemon.on('error', function(err) {

View File

@ -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);
});

View File

@ -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);

View File

@ -0,0 +1,8 @@
server=1
whitelist=127.0.0.1
txindex=1
rpcallowip=127.0.0.1
rpcuser=bitcoin
rpcpassword=local321

View File

@ -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');
@ -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) {

172
integration/regtest.js Normal file
View File

@ -0,0 +1,172 @@
'use strict';
// 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');
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 blockHashes = [];
describe('Basic Functionality', function() {
before(function(done) {
this.timeout(30000);
var datadir = __dirname + '/data';
rimraf(datadir + '/regtest', function(err) {
if (err) {
throw err;
}
bitcoind = require('../').daemon({
datadir: datadir,
network: 'regtest'
});
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'
});
console.log('Generating 100 blocks...');
client.generate(100, function(err, response) {
if (err) {
throw err;
}
blockHashes = response.result;
done();
});
});
});
});
after(function(done) {
this.timeout(20000);
bitcoind.stop(function(err, result) {
done();
});
});
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);
});
});
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,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();
});
});
});
});
});

View File

@ -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 = {
]
};
Daemon.regtest = {
name: 'regtest',
peers: [
// hardcoded peers
]
};
// Make sure signal handlers are not overwritten
Daemon._signalQueue = [];
Daemon._processOn = process.on;

View File

@ -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;

View File

@ -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);
}

View File

@ -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"
},
@ -51,10 +50,12 @@
"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",
"rimraf": "^2.4.2",
"sinon": "^1.15.4"
}
}

View File

@ -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<Function> 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<String>("rpc"))->IsBoolean()) {
rpc = options->Get(NanNew<String>("rpc"))->ToBoolean()->IsTrue();
}
if (options->Get(NanNew<String>("testnet"))->IsBoolean()) {
testnet = options->Get(NanNew<String>("testnet"))->ToBoolean()->IsTrue();
if (options->Get(NanNew<String>("network"))->IsString()) {
String::Utf8Value network_(options->Get(NanNew<String>("network"))->ToString());
std::string network = std::string(*network_);
if (network == "testnet") {
testnet = true;
} else if (network == "regtest") {
regtest = true;
}
}
if (options->Get(NanNew<String>("txindex"))->IsBoolean()) {
txindex = options->Get(NanNew<String>("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<Function> 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++;

View File

@ -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() {