bitcore-node-zcash/src/libbitcoind.cc

1580 lines
39 KiB
C++
Raw Normal View History

2014-08-12 12:03:04 -07:00
/**
* bitcoind.js - a binding for node.js which links to libbitcoind.so/dylib.
* Copyright (c) 2015, BitPay (MIT License)
2014-08-12 12:03:04 -07:00
*
* libbitcoind.cc:
2014-08-12 12:03:04 -07:00
* A bitcoind node.js binding.
*/
2015-07-31 08:40:15 -07:00
#include "libbitcoind.h"
2014-09-17 12:14:20 -07:00
using namespace std;
using namespace boost;
using namespace node;
using namespace v8;
2014-09-17 12:14:20 -07:00
2014-10-15 15:27:14 -07:00
/**
* Bitcoin Globals
*/
// These global functions and variables are
// required to be defined/exposed here.
2014-10-06 11:05:52 -07:00
extern void WaitForShutdown(boost::thread_group* threadGroup);
2014-12-10 11:42:33 -08:00
static termios orig_termios;
extern CTxMemPool mempool;
extern int64_t nTimeBestReceived;
2014-12-10 11:42:33 -08:00
2014-10-15 15:27:14 -07:00
/**
* Node.js Internal Function Templates
*/
2015-07-30 12:14:14 -07:00
static void
tx_notifier(uv_async_t *handle);
2015-07-30 12:14:14 -07:00
static void
async_tip_update(uv_work_t *req);
static void
async_tip_update_after(uv_work_t *req);
static void
2014-10-06 08:10:23 -07:00
async_start_node(uv_work_t *req);
static void
async_start_node_after(uv_work_t *req);
2014-08-12 12:03:04 -07:00
2015-07-07 14:02:03 -07:00
static void
async_blocks_ready(uv_work_t *req);
static void
async_blocks_ready_after(uv_work_t *req);
2014-09-11 17:18:36 -07:00
static void
2014-10-06 08:10:23 -07:00
async_stop_node(uv_work_t *req);
2014-09-11 17:18:36 -07:00
static void
async_stop_node_after(uv_work_t *req);
static int
start_node(void);
2014-09-17 14:08:26 -07:00
static void
start_node_thread(void);
2014-09-17 12:52:35 -07:00
2014-09-19 13:53:55 -07:00
static void
async_get_block(uv_work_t *req);
static void
async_get_block_after(uv_work_t *req);
2014-09-22 13:21:42 -07:00
static void
async_get_tx(uv_work_t *req);
static void
async_get_tx_after(uv_work_t *req);
2015-07-29 14:13:51 -07:00
static void
async_get_tx_and_info(uv_work_t *req);
static void
async_get_tx_and_info_after(uv_work_t *req);
2015-07-30 12:14:14 -07:00
static bool
queueTx(const CTransaction&);
2015-07-30 12:14:14 -07:00
2014-08-12 12:03:04 -07:00
extern "C" void
init(Handle<Object>);
2014-09-19 13:53:55 -07:00
/**
2014-10-15 15:27:14 -07:00
* Private Global Variables
* Used only by bitcoind functions.
2014-09-19 13:53:55 -07:00
*/
static std::vector<CTransaction> txQueue;
2015-07-30 12:14:14 -07:00
static uv_async_t txmon_async;
static Eternal<Function> txmon_callback;
static bool txmon_callback_available;
2014-09-19 13:53:55 -07:00
2014-10-15 15:37:29 -07:00
static volatile bool shutdown_complete = false;
2014-10-15 15:36:35 -07:00
static char *g_data_dir = NULL;
2014-10-17 12:06:40 -07:00
static bool g_rpc = false;
2014-10-20 09:38:35 -07:00
static bool g_testnet = false;
static bool g_regtest = false;
static bool g_txindex = false;
2014-09-19 13:53:55 -07:00
2015-08-21 12:39:46 -07:00
static boost::thread_group threadGroup;
2014-10-06 11:05:52 -07:00
/**
* Private Structs
* Used for async functions and necessary linked lists at points.
*/
struct async_tip_update_data {
uv_work_t req;
size_t result;
Isolate* isolate;
Persistent<Function> callback;
};
2015-07-07 14:02:03 -07:00
/**
* async_node_data
* Where the uv async request data resides.
*/
struct async_block_ready_data {
uv_work_t req;
2015-07-07 14:02:03 -07:00
std::string err_msg;
std::string result;
Isolate* isolate;
Persistent<Function> callback;
2015-07-07 14:02:03 -07:00
};
2014-08-20 17:00:09 -07:00
/**
* async_node_data
2014-08-20 17:00:09 -07:00
* Where the uv async request data resides.
*/
struct async_node_data {
uv_work_t req;
2014-09-29 12:18:29 -07:00
std::string err_msg;
std::string result;
2014-10-15 15:36:35 -07:00
std::string datadir;
2014-10-17 12:06:40 -07:00
bool rpc;
2014-10-20 09:38:35 -07:00
bool testnet;
bool regtest;
bool txindex;
Isolate* isolate;
Persistent<Function> callback;
};
2014-09-22 13:21:42 -07:00
/**
* async_block_data
*/
struct async_block_data {
uv_work_t req;
2014-09-22 13:21:42 -07:00
std::string err_msg;
std::string hash;
2014-11-04 16:08:31 -08:00
int64_t height;
2015-07-07 20:24:22 -07:00
char* buffer;
uint32_t size;
2014-11-04 16:08:31 -08:00
CBlock cblock;
CBlockIndex* cblock_index;
Isolate* isolate;
Persistent<Function> callback;
2014-09-22 13:21:42 -07:00
};
/**
* async_tx_data
*/
struct async_tx_data {
uv_work_t req;
2014-09-22 13:21:42 -07:00
std::string err_msg;
std::string txid;
std::string blockHash;
2015-07-29 14:13:51 -07:00
uint32_t nTime;
int64_t height;
bool queryMempool;
CTransaction ctx;
Isolate* isolate;
Persistent<Function> callback;
2014-09-22 13:21:42 -07:00
};
2014-12-10 10:44:01 -08:00
/**
* Helpers
*/
static bool
2014-12-10 11:42:33 -08:00
set_cooked(void);
2014-12-10 10:44:01 -08:00
/**
* SyncPercentage()
* bitcoind.syncPercentage()
* provides a float value >= indicating the progress of the blockchain sync
*/
NAN_METHOD(SyncPercentage) {
const CChainParams& chainParams = Params();
float progress = 0;
progress = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip());
NanReturnValue(NanNew<Number>(progress * 100));
};
NAN_METHOD(GetTxOutSetInfo) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
{
LOCK(cs_main);
CCoinsStats stats;
FlushStateToDisk();
if (pcoinsTip->GetStats(stats)) {
Local<Object> obj = NanNew<Object>();
obj->Set(NanNew<String>("height"), NanNew<Number>((int64_t)stats.nHeight));
obj->Set(NanNew<String>("bestblock"), NanNew<String>(stats.hashBlock.GetHex()));
obj->Set(NanNew<String>("transactions"), NanNew<Number>((int64_t)stats.nTransactions));
obj->Set(NanNew<String>("txouts"), NanNew<Number>((int64_t)stats.nTransactionOutputs));
obj->Set(NanNew<String>("bytes_serialized"), NanNew<Number>((int64_t)stats.nSerializedSize));
obj->Set(NanNew<String>("hash_serialized"), NanNew<String>(stats.hashSerialized.GetHex()));
obj->Set(NanNew<String>("total_amount"), NanNew<Number>(stats.nTotalAmount));
NanReturnValue(obj);
}
}
NanReturnValue(NanNull());
};
NAN_METHOD(GetBestBlockHash) {
{
LOCK(cs_main);
NanReturnValue(NanNew<String>(chainActive.Tip()->GetBlockHash().GetHex()));
}
}
NAN_METHOD(GetNextBlockHash) {
if (args.Length() < 1 || !args[0]->IsString()) {
return NanThrowError("Usage: bitcoind.getNextBlockHash(blockhash)");
}
CBlockIndex* pblockindex;
v8::String::Utf8Value param1(args[0]->ToString());
std::string *hash = new std::string(*param1);
uint256 shash = uint256S(*hash);
pblockindex = mapBlockIndex[shash];
CBlockIndex* pnextblockindex = chainActive.Next(pblockindex);
if (pnextblockindex) {
uint256 nexthash = pnextblockindex->GetBlockHash();
std::string rethash = nexthash.ToString();
NanReturnValue(NanNew<String>(rethash));
} else {
NanReturnValue(NanNull());
}
}
/**
* IsSynced()
* bitcoind.isSynced()
* returns a boolean of bitcoin is fully synced
*/
NAN_METHOD(IsSynced) {
bool isDownloading = IsInitialBlockDownload();
NanReturnValue(NanNew<Boolean>(!isDownloading));
};
2015-07-30 12:14:14 -07:00
NAN_METHOD(StartTxMon) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
Local<Function> callback = Local<Function>::Cast(args[0]);
Eternal<Function> cb(isolate, callback);
txmon_callback = cb;
txmon_callback_available = true;
2015-07-30 12:14:14 -07:00
CNodeSignals& nodeSignals = GetNodeSignals();
nodeSignals.TxToMemPool.connect(&queueTx);
2015-07-30 12:14:14 -07:00
uv_async_init(uv_default_loop(), &txmon_async, tx_notifier);
2015-07-30 12:14:14 -07:00
NanReturnValue(Undefined(isolate));
};
static void
tx_notifier(uv_async_t *handle) {
2015-07-30 12:14:14 -07:00
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
Local<Array> results = Array::New(isolate);
int arrayIndex = 0;
LOCK(cs_main);
BOOST_FOREACH(const CTransaction& tx, txQueue) {
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << tx;
std::string stx = ssTx.str();
Local<Value> txBuffer = node::Buffer::New(isolate, stx.c_str(), stx.size());
uint256 hash = tx.GetHash();
Local<Object> obj = NanNew<Object>();
obj->Set(NanNew<String>("buffer"), txBuffer);
obj->Set(NanNew<String>("hash"), NanNew<String>(hash.GetHex()));
obj->Set(NanNew<String>("mempool"), NanNew<Boolean>(true));
2015-07-30 12:14:14 -07:00
results->Set(arrayIndex, obj);
arrayIndex++;
}
2015-07-30 12:14:14 -07:00
const unsigned argc = 1;
Local<Value> argv[argc] = {
Local<Value>::New(isolate, results)
};
2015-07-30 12:14:14 -07:00
Local<Function> cb = txmon_callback.Get(isolate);
2015-07-30 12:14:14 -07:00
cb->Call(isolate->GetCurrentContext()->Global(), argc, argv);
txQueue.clear();
2015-07-30 12:14:14 -07:00
}
static bool
queueTx(const CTransaction& tx) {
LOCK(cs_main);
txQueue.push_back(tx);
uv_async_send(&txmon_async);
return true;
}
2014-10-15 15:27:14 -07:00
/**
* Functions
*/
NAN_METHOD(OnTipUpdate) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
async_tip_update_data *req = new async_tip_update_data();
Local<Function> callback = Local<Function>::Cast(args[0]);
req->callback.Reset(isolate, callback);
req->req.data = req;
req->isolate = isolate;
int status = uv_queue_work(uv_default_loop(),
&req->req, async_tip_update,
(uv_after_work_cb)async_tip_update_after);
assert(status == 0);
NanReturnValue(Undefined(isolate));
}
static void
async_tip_update(uv_work_t *req) {
async_tip_update_data *data = reinterpret_cast<async_tip_update_data*>(req->data);
size_t lastHeight = chainActive.Height();
while(lastHeight == (size_t)chainActive.Height() && !shutdown_complete) {
usleep(1E6);
}
data->result = chainActive.Height();
}
static void
async_tip_update_after(uv_work_t *r) {
async_tip_update_data *req = reinterpret_cast<async_tip_update_data*>(r->data);
Isolate* isolate = req->isolate;
HandleScope scope(isolate);
Local<Function> cb = Local<Function>::New(isolate, req->callback);
TryCatch try_catch;
Local<Value> result = Undefined(isolate);
if (!shutdown_complete) {
result = NanNew<Number>(req->result);
}
Local<Value> argv[1] = {
Local<Value>::New(isolate, result)
};
cb->Call(isolate->GetCurrentContext()->Global(), 1, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
req->callback.Reset();
delete req;
}
2015-07-07 14:02:03 -07:00
NAN_METHOD(OnBlocksReady) {
Isolate* isolate = args.GetIsolate();
2015-07-07 14:02:03 -07:00
HandleScope scope(isolate);
async_block_ready_data *req = new async_block_ready_data();
req->err_msg = std::string("");
req->result = std::string("");
req->req.data = req;
req->isolate = isolate;
2015-07-07 14:02:03 -07:00
Local<Function> callback = Local<Function>::Cast(args[0]);
req->callback.Reset(isolate, callback);
2015-07-07 14:02:03 -07:00
int status = uv_queue_work(uv_default_loop(),
&req->req, async_blocks_ready,
2015-07-07 14:02:03 -07:00
(uv_after_work_cb)async_blocks_ready_after);
assert(status == 0);
NanReturnValue(Undefined(isolate));
}
/**
* async_start_node()
* Call start_node() and start all our boost threads.
*/
static void
async_blocks_ready(uv_work_t *req) {
async_block_ready_data *data = reinterpret_cast<async_block_ready_data*>(req->data);
2015-07-07 14:02:03 -07:00
data->result = std::string("");
while(!chainActive.Tip()) {
2015-07-07 17:28:48 -07:00
usleep(1E6);
}
CBlockIndex* tip = chainActive.Tip();
uint256 tipHash = tip->GetBlockHash();
// Wait to be able to query for blocks by hash
2015-07-07 17:28:48 -07:00
while(mapBlockIndex.count(tipHash) == 0) {
usleep(1E6);
2015-07-07 14:02:03 -07:00
}
// Wait for chainActive to be able to get the hash
// for the genesis block for querying blocks by height
while(chainActive[0] == NULL) {
usleep(1E6);
}
//If the wallet is enabled, then we should make sure we can load it
2015-08-19 08:01:48 -07:00
#ifdef ENABLE_WALLET
while(pwalletMain == NULL || RPCIsInWarmup(NULL)) {
usleep(1E6);
}
#endif
// Wait until we can get a lock on cs_main
// And therefore ready to be able to quickly
// query for transactions from the mempool.
LOCK(cs_main);
{
return;
}
2015-07-07 14:02:03 -07:00
}
static void
async_blocks_ready_after(uv_work_t *r) {
async_block_ready_data* req = reinterpret_cast<async_block_ready_data*>(r->data);
Isolate* isolate = req->isolate;
2015-07-07 14:02:03 -07:00
HandleScope scope(isolate);
TryCatch try_catch;
Local<Function> cb = Local<Function>::New(isolate, req->callback);
if (req->err_msg != "") {
Local<Value> err = Exception::Error(NanNew<String>(req->err_msg));
Local<Value> argv[1] = { err };
cb->Call(isolate->GetCurrentContext()->Global(), 1, argv);
2015-07-07 14:02:03 -07:00
} else {
Local<Value> argv[2] = {
2015-07-07 14:02:03 -07:00
v8::Null(isolate),
Local<Value>::New(isolate, NanNew<String>(req->result))
2015-07-07 14:02:03 -07:00
};
cb->Call(isolate->GetCurrentContext()->Global(), 2, argv);
2015-07-07 14:02:03 -07:00
}
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
req->callback.Reset();
2015-07-07 14:02:03 -07:00
delete req;
}
2014-08-12 12:03:04 -07:00
/**
2014-10-06 11:05:52 -07:00
* StartBitcoind()
2014-08-12 12:03:04 -07:00
* bitcoind.start(callback)
2014-10-06 11:05:52 -07:00
* Start the bitcoind node with AppInit2() on a separate thread.
2014-08-12 12:03:04 -07:00
*/
NAN_METHOD(StartBitcoind) {
Isolate* isolate = args.GetIsolate();
2015-06-09 03:57:58 -07:00
HandleScope scope(isolate);
2014-08-12 12:03:04 -07:00
2014-10-15 15:36:35 -07:00
Local<Function> callback;
std::string datadir = std::string("");
2014-10-17 12:06:40 -07:00
bool rpc = false;
2014-10-20 09:38:35 -07:00
bool testnet = false;
bool regtest = false;
bool txindex = false;
2014-10-15 16:38:10 -07:00
if (args.Length() >= 2 && args[0]->IsObject() && args[1]->IsFunction()) {
2014-10-15 15:36:35 -07:00
Local<Object> options = Local<Object>::Cast(args[0]);
2014-10-15 16:38:10 -07:00
if (options->Get(NanNew<String>("datadir"))->IsString()) {
String::Utf8Value datadir_(options->Get(NanNew<String>("datadir"))->ToString());
datadir = std::string(*datadir_);
2014-10-15 15:36:35 -07:00
}
2014-10-17 12:06:40 -07:00
if (options->Get(NanNew<String>("rpc"))->IsBoolean()) {
rpc = options->Get(NanNew<String>("rpc"))->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;
}
2014-10-20 09:38:35 -07:00
}
if (options->Get(NanNew<String>("txindex"))->IsBoolean()) {
txindex = options->Get(NanNew<String>("txindex"))->ToBoolean()->IsTrue();
}
2014-10-15 16:38:10 -07:00
callback = Local<Function>::Cast(args[1]);
2014-10-16 11:24:21 -07:00
} else if (args.Length() >= 2
2014-10-15 16:38:10 -07:00
&& (args[0]->IsUndefined() || args[0]->IsNull())
&& args[1]->IsFunction()) {
callback = Local<Function>::Cast(args[1]);
2014-10-16 11:24:21 -07:00
} else if (args.Length() >= 1 && args[0]->IsFunction()) {
2014-10-15 15:36:35 -07:00
callback = Local<Function>::Cast(args[0]);
2014-10-15 16:38:10 -07:00
} else {
return NanThrowError(
"Usage: bitcoind.start(callback)");
2014-08-12 12:03:04 -07:00
}
//
// Run bitcoind's StartNode() on a separate thread.
//
async_node_data *req = new async_node_data();
req->err_msg = std::string("");
req->result = std::string("");
req->datadir = datadir;
req->rpc = rpc;
req->testnet = testnet;
req->regtest = regtest;
req->txindex = txindex;
req->isolate = isolate;
req->callback.Reset(isolate, callback);
req->req.data = req;
2014-09-29 12:18:29 -07:00
int status = uv_queue_work(uv_default_loop(),
&req->req, async_start_node,
(uv_after_work_cb)async_start_node_after);
2014-09-29 12:18:29 -07:00
assert(status == 0);
2014-08-12 12:03:04 -07:00
NanReturnValue(Undefined(isolate));
2014-08-12 12:03:04 -07:00
}
2014-08-20 17:00:09 -07:00
/**
2014-10-06 08:10:23 -07:00
* async_start_node()
2014-08-20 17:00:09 -07:00
* Call start_node() and start all our boost threads.
*/
static void
2014-10-06 08:10:23 -07:00
async_start_node(uv_work_t *req) {
async_node_data *data = reinterpret_cast<async_node_data*>(req->data);
2014-11-11 11:38:51 -08:00
if (data->datadir != "") {
2014-10-16 11:24:21 -07:00
g_data_dir = (char *)data->datadir.c_str();
2014-11-11 13:36:08 -08:00
} else {
g_data_dir = (char *)malloc(sizeof(char) * 512);
snprintf(g_data_dir, sizeof(char) * 512, "%s/.bitcoind.js", getenv("HOME"));
2014-10-15 15:36:35 -07:00
}
2014-10-17 12:06:40 -07:00
g_rpc = (bool)data->rpc;
2014-10-20 09:38:35 -07:00
g_testnet = (bool)data->testnet;
g_regtest = (bool)data->regtest;
g_txindex = (bool)data->txindex;
2014-12-10 11:42:33 -08:00
tcgetattr(STDIN_FILENO, &orig_termios);
2014-09-05 15:07:38 -07:00
start_node();
2014-12-10 10:47:02 -08:00
data->result = std::string("bitcoind opened.");
}
2014-08-20 17:00:09 -07:00
/**
* async_start_node_after()
2014-08-20 17:00:09 -07:00
* Execute our callback.
*/
static void
async_start_node_after(uv_work_t *r) {
async_node_data *req = reinterpret_cast<async_node_data*>(r->data);
Isolate* isolate = req->isolate;
2015-06-09 03:57:58 -07:00
HandleScope scope(isolate);
TryCatch try_catch;
Local<Function> cb = Local<Function>::New(isolate, req->callback);
if (req->err_msg != "") {
Local<Value> err = Exception::Error(NanNew<String>(req->err_msg));
Local<Value> argv[1] = { err };
cb->Call(isolate->GetCurrentContext()->Global(), 1, argv);
} else {
Local<Value> argv[2] = {
v8::Null(isolate),
Local<Value>::New(isolate, NanNew<String>(req->result))
};
cb->Call(isolate->GetCurrentContext()->Global(), 2, argv);
}
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
req->callback.Reset();
delete req;
2014-08-12 12:03:04 -07:00
}
2014-08-20 17:00:09 -07:00
/**
* start_node(void)
2014-10-06 11:05:52 -07:00
* Start AppInit2() on a separate thread, wait for
* Unfortunately, we need to wait for the initialization
* to unhook the signal handlers so we can use them
* from node.js in javascript.
2014-08-20 17:00:09 -07:00
*/
static int
start_node(void) {
2014-10-13 14:00:01 -07:00
SetupEnvironment();
2014-09-17 12:14:20 -07:00
noui_connect();
new boost::thread(boost::bind(&start_node_thread));
2014-09-17 12:52:35 -07:00
return 0;
}
2014-09-17 12:14:20 -07:00
2014-09-17 14:08:26 -07:00
static void
start_node_thread(void) {
CScheduler scheduler;
2014-09-17 12:14:20 -07:00
2014-10-13 14:16:43 -07:00
// Workaround for AppInit2() arg parsing. Not ideal, but it works.
2014-10-15 15:36:35 -07:00
int argc = 0;
char **argv = (char **)malloc((4 + 1) * sizeof(char **));
2014-10-17 12:11:34 -07:00
argv[argc] = (char *)"bitcoind";
argc++;
2014-10-17 12:38:41 -07:00
2014-10-15 15:36:35 -07:00
if (g_data_dir) {
2014-10-21 18:06:17 -07:00
const int argl = 9 + strlen(g_data_dir) + 1;
char *arg = (char *)malloc(sizeof(char) * argl);
int w = snprintf(arg, argl, "-datadir=%s", g_data_dir);
2014-10-21 18:06:17 -07:00
if (w >= 10 && w <= argl) {
arg[w] = '\0';
argv[argc] = arg;
argc++;
} else {
2014-12-10 11:42:33 -08:00
if (set_cooked()) {
2014-12-10 10:44:01 -08:00
fprintf(stderr, "bitcoind.js: Bad -datadir value.\n");
}
}
2014-10-13 14:16:43 -07:00
}
2014-10-13 14:00:01 -07:00
2014-10-17 12:11:34 -07:00
if (g_rpc) {
argv[argc] = (char *)"-server";
argc++;
}
2014-10-20 09:38:35 -07:00
if (g_testnet) {
argv[argc] = (char *)"-testnet";
argc++;
}
if (g_regtest) {
argv[argc] = (char *)"-regtest";
argc++;
}
argv[argc] = (char *)"-txindex";
argc++;
2014-10-17 12:11:34 -07:00
argv[argc] = NULL;
2014-10-13 14:00:01 -07:00
bool fRet = false;
try {
2014-10-15 15:36:35 -07:00
ParseParameters((const int)argc, (const char **)argv);
2014-10-13 14:00:01 -07:00
if (!boost::filesystem::is_directory(GetDataDir(false))) {
2014-12-10 11:42:33 -08:00
if (set_cooked()) {
2014-12-10 10:44:01 -08:00
fprintf(stderr,
"bitcoind.js: Specified data directory \"%s\" does not exist.\n",
mapArgs["-datadir"].c_str());
}
2014-12-10 11:12:31 -08:00
shutdown_complete = true;
2014-12-10 11:29:29 -08:00
_exit(1);
2014-10-13 14:00:01 -07:00
return;
}
try {
ReadConfigFile(mapArgs, mapMultiArgs);
} catch(std::exception &e) {
2014-12-10 11:42:33 -08:00
if (set_cooked()) {
2014-12-10 10:44:01 -08:00
fprintf(stderr,
"bitcoind.js: Error reading configuration file: %s\n", e.what());
}
2014-12-10 11:12:31 -08:00
shutdown_complete = true;
2014-12-10 11:29:29 -08:00
_exit(1);
2014-10-13 14:00:01 -07:00
return;
}
if (!SelectParamsFromCommandLine()) {
2014-12-10 11:42:33 -08:00
if (set_cooked()) {
2014-12-10 10:44:01 -08:00
fprintf(stderr,
"bitcoind.js: Invalid combination of -regtest and -testnet.\n");
}
2014-12-10 11:12:31 -08:00
shutdown_complete = true;
2014-12-10 11:29:29 -08:00
_exit(1);
2014-12-10 11:12:31 -08:00
return;
}
2014-10-15 15:36:35 -07:00
CreatePidFile(GetPidFile(), getpid());
2014-10-13 14:00:01 -07:00
fRet = AppInit2(threadGroup, scheduler);
2014-10-13 14:00:01 -07:00
} catch (std::exception& e) {
if (set_cooked()) {
fprintf(stderr, "bitcoind.js: AppInit2(): std::exception\n");
}
2014-10-13 14:00:01 -07:00
} catch (...) {
if (set_cooked()) {
fprintf(stderr, "bitcoind.js: AppInit2(): other exception\n");
2014-10-13 14:00:01 -07:00
}
}
if (!fRet)
{
threadGroup.interrupt_all();
} else {
WaitForShutdown(&threadGroup);
2014-10-13 14:00:01 -07:00
}
Shutdown();
2014-10-15 15:37:29 -07:00
shutdown_complete = true;
}
2014-09-11 17:18:36 -07:00
/**
2014-10-06 11:05:52 -07:00
* StopBitcoind()
2014-09-11 17:18:36 -07:00
* bitcoind.stop(callback)
*/
NAN_METHOD(StopBitcoind) {
Isolate* isolate = args.GetIsolate();
2015-06-09 03:57:58 -07:00
HandleScope scope(isolate);
2014-09-11 17:18:36 -07:00
if (args.Length() < 1 || !args[0]->IsFunction()) {
return NanThrowError(
2014-09-22 12:57:25 -07:00
"Usage: bitcoind.stop(callback)");
2014-09-11 17:18:36 -07:00
}
Local<Function> callback = Local<Function>::Cast(args[0]);
//
// Run bitcoind's StartShutdown() on a separate thread.
//
async_node_data *req = new async_node_data();
req->err_msg = std::string("");
req->result = std::string("");
req->callback.Reset(isolate, callback);
req->req.data = req;
req->isolate = isolate;
2014-09-11 17:18:36 -07:00
2014-09-29 12:18:29 -07:00
int status = uv_queue_work(uv_default_loop(),
&req->req, async_stop_node,
2014-09-11 17:18:36 -07:00
(uv_after_work_cb)async_stop_node_after);
2014-09-29 12:18:29 -07:00
assert(status == 0);
NanReturnValue(Undefined(isolate));
2014-09-11 17:18:36 -07:00
}
/**
2014-10-06 08:10:23 -07:00
* async_stop_node()
2014-10-06 11:05:52 -07:00
* Call StartShutdown() to join the boost threads, which will call Shutdown()
2014-10-15 15:37:29 -07:00
* and set shutdown_complete to true to notify the main node.js thread.
2014-09-11 17:18:36 -07:00
*/
static void
2014-10-06 08:10:23 -07:00
async_stop_node(uv_work_t *req) {
async_node_data *data = reinterpret_cast<async_node_data*>(req->data);
2015-08-21 12:39:46 -07:00
2014-09-11 17:18:36 -07:00
StartShutdown();
2015-08-21 12:39:46 -07:00
while(!shutdown_complete) {
usleep(1E6);
}
2014-12-10 10:47:02 -08:00
data->result = std::string("bitcoind shutdown.");
2014-09-11 17:18:36 -07:00
}
/**
* async_stop_node_after()
* Execute our callback.
*/
static void
async_stop_node_after(uv_work_t *r) {
async_node_data* req = reinterpret_cast<async_node_data*>(r->data);
Isolate* isolate = req->isolate;
2015-06-09 03:57:58 -07:00
HandleScope scope(isolate);
2014-09-11 17:18:36 -07:00
TryCatch try_catch;
Local<Function> cb = Local<Function>::New(isolate, req->callback);
if (req->err_msg != "") {
Local<Value> err = Exception::Error(NanNew<String>(req->err_msg));
Local<Value> argv[1] = { err };
cb->Call(isolate->GetCurrentContext()->Global(), 1, argv);
2014-09-11 17:18:36 -07:00
} else {
Local<Value> argv[2] = {
2015-06-08 16:08:18 -07:00
Local<Value>::New(isolate, NanNull()),
Local<Value>::New(isolate, NanNew<String>(req->result))
2014-09-11 17:18:36 -07:00
};
cb->Call(isolate->GetCurrentContext()->Global(), 2, argv);
2014-09-11 17:18:36 -07:00
}
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
req->callback.Reset();
2014-09-11 17:18:36 -07:00
delete req;
}
2014-09-19 13:53:55 -07:00
/**
2014-10-06 11:05:52 -07:00
* GetBlock()
2014-12-08 11:31:00 -08:00
* bitcoind.getBlock([blockhash,blockheight], callback)
2014-10-06 11:05:52 -07:00
* Read any block from disk asynchronously.
2014-09-19 13:53:55 -07:00
*/
NAN_METHOD(GetBlock) {
Isolate* isolate = args.GetIsolate();
2015-06-09 03:57:58 -07:00
HandleScope scope(isolate);
2014-09-19 13:53:55 -07:00
if (args.Length() < 2
2014-11-19 17:04:19 -08:00
|| (!args[0]->IsString() && !args[0]->IsNumber())
2014-09-19 13:53:55 -07:00
|| !args[1]->IsFunction()) {
return NanThrowError(
"Usage: bitcoind.getBlock([blockhash,blockheight], callback)");
2014-09-19 13:53:55 -07:00
}
async_block_data *req = new async_block_data();
2014-09-19 13:53:55 -07:00
2014-11-04 16:08:31 -08:00
if (args[0]->IsNumber()) {
int64_t height = args[0]->IntegerValue();
req->err_msg = std::string("");
req->hash = std::string("");
req->height = height;
2014-11-04 16:08:31 -08:00
} else {
String::Utf8Value hash_(args[0]->ToString());
std::string hash = std::string(*hash_);
req->err_msg = std::string("");
req->hash = hash;
req->height = -1;
2014-11-04 16:08:31 -08:00
}
Local<Function> callback = Local<Function>::Cast(args[1]);
req->req.data = req;
req->isolate = isolate;
req->callback.Reset(isolate, callback);
2014-09-19 13:53:55 -07:00
int status = uv_queue_work(uv_default_loop(),
&req->req, async_get_block,
2014-09-19 13:53:55 -07:00
(uv_after_work_cb)async_get_block_after);
assert(status == 0);
NanReturnValue(Undefined(isolate));
2014-09-19 13:53:55 -07:00
}
static void
async_get_block(uv_work_t *req) {
async_block_data* data = reinterpret_cast<async_block_data*>(req->data);
2014-11-04 16:08:31 -08:00
2015-07-09 13:45:30 -07:00
CBlockIndex* pblockindex;
uint256 hash = uint256S(data->hash);
2014-11-04 16:08:31 -08:00
2015-07-09 13:45:30 -07:00
if (data->height != -1) {
pblockindex = chainActive[data->height];
if (pblockindex == NULL) {
data->err_msg = std::string("Block not found.");
return;
}
2014-09-19 13:53:55 -07:00
} else {
2015-07-09 13:45:30 -07:00
if (mapBlockIndex.count(hash) == 0) {
data->err_msg = std::string("Block not found.");
return;
} else {
pblockindex = mapBlockIndex[hash];
}
}
2015-07-07 12:36:17 -07:00
2015-07-09 13:45:30 -07:00
const CDiskBlockPos& pos = pblockindex->GetBlockPos();
2015-07-07 12:36:17 -07:00
2015-07-09 13:45:30 -07:00
// We can read directly from the file, and pass that, we don't need to
// deserialize the entire block only for it to then be serialized
// and then deserialized again in JavaScript
2015-07-07 20:24:22 -07:00
2015-07-09 13:45:30 -07:00
// Open history file to read
CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
if (filein.IsNull()) {
data->err_msg = std::string("ReadBlockFromDisk: OpenBlockFile failed");
return;
}
2015-07-07 20:24:22 -07:00
2015-07-09 13:45:30 -07:00
// Get the actual file, seeked position and rewind a uint32_t
FILE* blockFile = filein.release();
long int filePos = ftell(blockFile);
fseek(blockFile, filePos - sizeof(uint32_t), SEEK_SET);
2015-07-07 20:24:22 -07:00
2015-07-09 13:45:30 -07:00
// Read the size of the block
uint32_t size = 0;
fread(&size, sizeof(uint32_t), 1, blockFile);
2015-07-07 20:24:22 -07:00
2015-07-09 13:45:30 -07:00
// Read block
char* buffer = (char *)malloc(sizeof(char) * size);
fread((void *)buffer, sizeof(char), size, blockFile);
fclose(blockFile);
2015-07-07 20:24:22 -07:00
2015-07-09 13:45:30 -07:00
data->buffer = buffer;
data->size = size;
data->cblock_index = pblockindex;
2015-07-07 20:24:22 -07:00
2014-09-19 13:53:55 -07:00
}
static void
async_get_block_after(uv_work_t *r) {
async_block_data* req = reinterpret_cast<async_block_data*>(r->data);
Isolate *isolate = req->isolate;
2015-06-09 03:57:58 -07:00
HandleScope scope(isolate);
2014-09-19 13:53:55 -07:00
TryCatch try_catch;
Local<Function> cb = Local<Function>::New(isolate, req->callback);
if (req->err_msg != "") {
Local<Value> err = Exception::Error(NanNew<String>(req->err_msg));
Local<Value> argv[1] = { err };
cb->Call(isolate->GetCurrentContext()->Global(), 1, argv);
2014-09-19 13:53:55 -07:00
} else {
2014-09-19 15:39:05 -07:00
Local<Value> rawNodeBuffer = node::Buffer::New(isolate, req->buffer, req->size);
2014-09-19 15:39:05 -07:00
delete req->buffer;
req->buffer = NULL;
2015-07-09 14:34:37 -07:00
Local<Value> argv[2] = {
2015-06-08 16:08:18 -07:00
Local<Value>::New(isolate, NanNull()),
2015-07-07 20:24:22 -07:00
rawNodeBuffer
2014-09-19 13:53:55 -07:00
};
cb->Call(isolate->GetCurrentContext()->Global(), 2, argv);
}
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
2014-09-19 13:53:55 -07:00
}
req->callback.Reset();
2014-09-19 13:53:55 -07:00
delete req;
}
2014-09-22 10:19:37 -07:00
/**
2014-12-03 10:57:56 -08:00
* GetTransaction()
2015-07-29 14:13:51 -07:00
* bitcoind.getTransaction(txid, queryMempool, callback)
2014-10-06 11:05:52 -07:00
* Read any transaction from disk asynchronously.
2014-09-22 10:19:37 -07:00
*/
2014-12-03 10:57:56 -08:00
NAN_METHOD(GetTransaction) {
Isolate* isolate = args.GetIsolate();
2015-06-09 03:57:58 -07:00
HandleScope scope(isolate);
2014-09-26 11:23:21 -07:00
if (args.Length() < 3
2014-09-22 13:21:42 -07:00
|| !args[0]->IsString()
|| !args[1]->IsBoolean()
2014-12-08 13:36:54 -08:00
|| !args[2]->IsFunction()) {
2014-09-22 13:21:42 -07:00
return NanThrowError(
2015-07-29 14:13:51 -07:00
"Usage: daemon.getTransaction(txid, queryMempool, callback)");
2014-09-22 13:21:42 -07:00
}
2014-12-08 11:31:00 -08:00
String::Utf8Value txid_(args[0]->ToString());
bool queryMempool = args[1]->BooleanValue();
2014-12-08 13:36:54 -08:00
Local<Function> callback = Local<Function>::Cast(args[2]);
2014-09-22 13:21:42 -07:00
async_tx_data *req = new async_tx_data();
req->err_msg = std::string("");
req->txid = std::string("");
std::string txid = std::string(*txid_);
req->txid = txid;
req->queryMempool = queryMempool;
req->isolate = isolate;
req->req.data = req;
req->callback.Reset(isolate, callback);
2014-09-22 13:21:42 -07:00
int status = uv_queue_work(uv_default_loop(),
&req->req, async_get_tx,
2014-09-22 13:21:42 -07:00
(uv_after_work_cb)async_get_tx_after);
assert(status == 0);
NanReturnValue(Undefined(isolate));
2014-09-22 13:21:42 -07:00
}
static void
async_get_tx(uv_work_t *req) {
async_tx_data* data = reinterpret_cast<async_tx_data*>(req->data);
2014-09-22 13:21:42 -07:00
uint256 hash = uint256S(data->txid);
uint256 blockhash;
2014-10-02 13:24:18 -07:00
CTransaction ctx;
if (data->queryMempool) {
LOCK(cs_main);
{
if (mempool.lookup(hash, ctx))
{
data->ctx = ctx;
return;
}
}
}
CDiskTxPos postx;
if (pblocktree->ReadTxIndex(hash, postx)) {
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
if (file.IsNull()) {
data->err_msg = std::string("%s: OpenBlockFile failed", __func__);
return;
}
const int HEADER_SIZE = sizeof(int32_t) + sizeof(uint32_t) * 3 + sizeof(char) * 64;
try {
fseek(file.Get(), postx.nTxOffset + HEADER_SIZE, SEEK_CUR);
file >> ctx;
data->ctx = ctx;
} catch (const std::exception& e) {
data->err_msg = std::string("Deserialize or I/O error - %s", __func__);
return;
}
2014-12-04 15:38:35 -08:00
}
2014-09-22 13:21:42 -07:00
}
static void
async_get_tx_after(uv_work_t *r) {
async_tx_data* req = reinterpret_cast<async_tx_data*>(r->data);
Isolate* isolate = req->isolate;
2015-06-09 03:57:58 -07:00
HandleScope scope(isolate);
2014-09-22 13:21:42 -07:00
CTransaction ctx = req->ctx;
TryCatch try_catch;
Local<Function> cb = Local<Function>::New(isolate, req->callback);
2014-09-22 13:21:42 -07:00
if (req->err_msg != "") {
Local<Value> err = Exception::Error(NanNew<String>(req->err_msg));
Local<Value> argv[1] = { err };
cb->Call(isolate->GetCurrentContext()->Global(), 1, argv);
2014-09-22 13:21:42 -07:00
} else {
2015-08-03 14:41:11 -07:00
Local<Value> result = Local<Value>::New(isolate, NanNull());
if (!ctx.IsNull()) {
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << ctx;
std::string stx = ssTx.str();
result = node::Buffer::New(isolate, stx.c_str(), stx.size());
}
Local<Value> argv[2] = {
2015-06-08 16:08:18 -07:00
Local<Value>::New(isolate, NanNull()),
2015-08-03 14:41:11 -07:00
result
2014-09-22 13:21:42 -07:00
};
cb->Call(isolate->GetCurrentContext()->Global(), 2, argv);
2014-09-22 13:21:42 -07:00
}
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
req->callback.Reset();
2014-09-22 13:21:42 -07:00
delete req;
}
2014-09-22 10:19:37 -07:00
2015-07-29 14:13:51 -07:00
/**
* GetTransactionWithBlockInfo()
* bitcoind.getTransactionWithBlockInfo(txid, queryMempool, callback)
* Read any transaction from disk asynchronously with block timestamp and height.
*/
NAN_METHOD(GetTransactionWithBlockInfo) {
Isolate* isolate = args.GetIsolate();
2015-07-29 14:13:51 -07:00
HandleScope scope(isolate);
if (args.Length() < 3
|| !args[0]->IsString()
|| !args[1]->IsBoolean()
|| !args[2]->IsFunction()) {
return NanThrowError(
"Usage: bitcoind.getTransactionWithBlockInfo(txid, queryMempool, callback)");
2015-07-29 14:13:51 -07:00
}
String::Utf8Value txid_(args[0]->ToString());
bool queryMempool = args[1]->BooleanValue();
Local<Function> callback = Local<Function>::Cast(args[2]);
async_tx_data *req = new async_tx_data();
2015-07-29 14:13:51 -07:00
req->err_msg = std::string("");
req->txid = std::string("");
2015-07-29 14:13:51 -07:00
std::string txid = std::string(*txid_);
req->txid = txid;
req->queryMempool = queryMempool;
req->req.data = req;
req->isolate = isolate;
req->callback.Reset(isolate, callback);
2015-07-29 14:13:51 -07:00
int status = uv_queue_work(uv_default_loop(),
&req->req, async_get_tx_and_info,
2015-07-29 14:13:51 -07:00
(uv_after_work_cb)async_get_tx_and_info_after);
assert(status == 0);
NanReturnValue(Undefined(isolate));
}
static void
async_get_tx_and_info(uv_work_t *req) {
async_tx_data* data = reinterpret_cast<async_tx_data*>(req->data);
2015-07-29 14:13:51 -07:00
uint256 hash = uint256S(data->txid);
uint256 blockHash;
CTransaction ctx;
if (data->queryMempool) {
LOCK(mempool.cs);
map<uint256, CTxMemPoolEntry>::const_iterator i = mempool.mapTx.find(hash);
if (i != mempool.mapTx.end()) {
data->ctx = i->second.GetTx();
data->nTime = i->second.GetTime();
data->height = -1;
return;
}
}
CDiskTxPos postx;
if (pblocktree->ReadTxIndex(hash, postx)) {
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
if (file.IsNull()) {
data->err_msg = std::string("%s: OpenBlockFile failed", __func__);
return;
}
CBlockHeader blockHeader;
try {
// Read header first to get block timestamp and hash
file >> blockHeader;
blockHash = blockHeader.GetHash();
data->blockHash = blockHash.GetHex();
2015-07-29 14:13:51 -07:00
data->nTime = blockHeader.nTime;
fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
file >> ctx;
data->ctx = ctx;
} catch (const std::exception& e) {
data->err_msg = std::string("Deserialize or I/O error - %s", __func__);
return;
}
// get block height
CBlockIndex* blockIndex;
if (mapBlockIndex.count(blockHash) == 0) {
data->height = -1;
} else {
blockIndex = mapBlockIndex[blockHash];
data->height = blockIndex->nHeight;
}
}
}
static void
async_get_tx_and_info_after(uv_work_t *r) {
async_tx_data* req = reinterpret_cast<async_tx_data*>(r->data);
Isolate* isolate = req->isolate;
2015-07-29 14:13:51 -07:00
HandleScope scope(isolate);
CTransaction ctx = req->ctx;
TryCatch try_catch;
Local<Function> cb = Local<Function>::New(isolate, req->callback);
2015-07-29 14:13:51 -07:00
Local<Object> obj = NanNew<Object>();
if (req->err_msg != "") {
Local<Value> err = Exception::Error(NanNew<String>(req->err_msg));
Local<Value> argv[1] = { err };
cb->Call(isolate->GetCurrentContext()->Global(), 1, argv);
2015-07-29 14:13:51 -07:00
} else {
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << ctx;
std::string stx = ssTx.str();
Local<Value> rawNodeBuffer = node::Buffer::New(isolate, stx.c_str(), stx.size());
obj->Set(NanNew<String>("blockHash"), NanNew<String>(req->blockHash));
obj->Set(NanNew<String>("height"), NanNew<Number>(req->height));
obj->Set(NanNew<String>("timestamp"), NanNew<Number>(req->nTime));
2015-07-29 14:13:51 -07:00
obj->Set(NanNew<String>("buffer"), rawNodeBuffer);
Local<Value> argv[2] = {
2015-07-29 14:13:51 -07:00
Local<Value>::New(isolate, NanNull()),
obj
};
cb->Call(isolate->GetCurrentContext()->Global(), 2, argv);
}
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
2015-07-29 14:13:51 -07:00
}
req->callback.Reset();
2015-07-29 14:13:51 -07:00
delete req;
}
2015-07-15 14:45:36 -07:00
/**
* IsSpent()
* bitcoind.isSpent()
2015-07-15 14:45:36 -07:00
* Determine if an outpoint is spent
*/
NAN_METHOD(IsSpent) {
NanScope();
if (args.Length() > 2) {
2015-07-15 14:45:36 -07:00
return NanThrowError(
"Usage: bitcoind.isSpent(txid, outputIndex)");
2015-07-15 14:45:36 -07:00
}
String::Utf8Value arg(args[0]->ToString());
std::string argStr = std::string(*arg);
const uint256 txid = uint256S(argStr);
int outputIndex = args[1]->IntegerValue();
CCoinsView dummy;
CCoinsViewCache view(&dummy);
CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
view.SetBackend(viewMemPool);
2015-07-15 14:45:36 -07:00
if (view.HaveCoins(txid)) {
const CCoins* coins = view.AccessCoins(txid);
if (coins && coins->IsAvailable(outputIndex)) {
2015-07-15 14:45:36 -07:00
NanReturnValue(NanNew<Boolean>(false));
return;
}
}
NanReturnValue(NanNew<Boolean>(true));
};
/**
* GetBlockIndex()
* bitcoind.getBlockIndex()
* Get index information about a block by hash including:
* - the total amount of work (expected number of hashes) in the chain up to
* and including this block.
* - the previous hash of the block
*/
NAN_METHOD(GetBlockIndex) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
CBlockIndex* blockIndex;
if (args[0]->IsNumber()) {
int64_t height = args[0]->IntegerValue();
blockIndex = chainActive[height];
if (blockIndex == NULL) {
NanReturnValue(Undefined(isolate));
}
} else {
String::Utf8Value hash_(args[0]->ToString());
std::string hashStr = std::string(*hash_);
uint256 hash = uint256S(hashStr);
if (mapBlockIndex.count(hash) == 0) {
NanReturnValue(Undefined(isolate));
} else {
blockIndex = mapBlockIndex[hash];
}
}
Local<Object> obj = NanNew<Object>();
arith_uint256 cw = blockIndex->nChainWork;
CBlockIndex* prevBlockIndex = blockIndex->pprev;
if (&prevBlockIndex->phashBlock != 0) {
const uint256* prevHash = prevBlockIndex->phashBlock;
obj->Set(NanNew<String>("prevHash"), NanNew<String>(prevHash->GetHex()));
} else {
obj->Set(NanNew<String>("prevHash"), NanNull());
}
obj->Set(NanNew<String>("hash"), NanNew<String>(blockIndex->phashBlock->GetHex()));
obj->Set(NanNew<String>("chainWork"), NanNew<String>(cw.GetHex()));
obj->Set(NanNew<String>("height"), NanNew<Number>(blockIndex->nHeight));
NanReturnValue(obj);
};
2015-09-08 11:04:14 -07:00
/**
* IsMainChain()
* bitcoind.isMainChain()
*
* @param {string} - block hash
* @returns {boolean} - True if the block is in the main chain. False if it is an orphan.
*/
NAN_METHOD(IsMainChain) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
CBlockIndex* blockIndex;
String::Utf8Value hash_(args[0]->ToString());
std::string hashStr = std::string(*hash_);
uint256 hash = uint256S(hashStr);
if (mapBlockIndex.count(hash) == 0) {
NanReturnValue(Undefined(isolate));
} else {
blockIndex = mapBlockIndex[hash];
}
if (chainActive.Contains(blockIndex)) {
NanReturnValue(NanNew<Boolean>(true));
} else {
NanReturnValue(NanNew<Boolean>(false));
}
}
2014-10-17 13:26:27 -07:00
/**
* GetInfo()
* bitcoind.getInfo()
2014-10-17 13:26:27 -07:00
* Get miscellaneous information
*/
NAN_METHOD(GetInfo) {
NanScope();
if (args.Length() > 0) {
return NanThrowError(
"Usage: bitcoind.getInfo()");
2014-10-17 13:26:27 -07:00
}
2014-10-17 13:47:56 -07:00
Local<Object> obj = NanNew<Object>();
2014-10-17 13:26:27 -07:00
proxyType proxy;
GetProxy(NET_IPV4, proxy);
2014-10-17 13:47:56 -07:00
obj->Set(NanNew<String>("version"), NanNew<Number>(CLIENT_VERSION));
obj->Set(NanNew<String>("protocolversion"), NanNew<Number>(PROTOCOL_VERSION));
obj->Set(NanNew<String>("blocks"), NanNew<Number>((int)chainActive.Height())->ToInt32());
obj->Set(NanNew<String>("timeoffset"), NanNew<Number>(GetTimeOffset()));
obj->Set(NanNew<String>("connections"), NanNew<Number>((int)vNodes.size())->ToInt32());
obj->Set(NanNew<String>("difficulty"), NanNew<Number>((double)GetDifficulty()));
obj->Set(NanNew<String>("testnet"), NanNew<Boolean>(Params().NetworkIDString() == "test"));
2014-10-17 13:47:56 -07:00
obj->Set(NanNew<String>("relayfee"), NanNew<Number>(::minRelayTxFee.GetFeePerK())); // double
2014-10-17 14:00:12 -07:00
obj->Set(NanNew<String>("errors"), NanNew<String>(GetWarnings("statusbar")));
2014-10-17 13:26:27 -07:00
NanReturnValue(obj);
}
2015-07-28 13:03:55 -07:00
/**
* Estimate Fee
* @blocks {number} - The number of blocks until confirmed
*/
NAN_METHOD(EstimateFee) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
int nBlocks = args[0]->NumberValue();
if (nBlocks < 1) {
nBlocks = 1;
}
CFeeRate feeRate = mempool.estimateFee(nBlocks);
if (feeRate == CFeeRate(0)) {
NanReturnValue(NanNew<Number>(-1.0));
return;
}
CAmount nFee = feeRate.GetFeePerK();
NanReturnValue(NanNew<Number>(nFee));
}
/**
* Send Transaction
* bitcoind.sendTransaction()
* Will add a transaction to the mempool and broadcast to connected peers.
* @param {string} - The serialized hex string of the transaction.
* @param {boolean} - Skip absurdly high fee checks
*/
NAN_METHOD(SendTransaction) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
LOCK(cs_main);
// Decode the transaction
v8::String::Utf8Value param1(args[0]->ToString());
std::string *input = new std::string(*param1);
CTransaction tx;
if (!DecodeHexTx(tx, *input)) {
return NanThrowError("TX decode failed");
}
uint256 hashTx = tx.GetHash();
// Skip absurdly high fee check
bool allowAbsurdFees = false;
if (args.Length() > 1) {
allowAbsurdFees = args[1]->BooleanValue();
}
CCoinsViewCache &view = *pcoinsTip;
const CCoins* existingCoins = view.AccessCoins(hashTx);
bool fHaveMempool = mempool.exists(hashTx);
bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000;
if (!fHaveMempool && !fHaveChain) {
CValidationState state;
bool fMissingInputs;
// Attempt to add the transaction to the mempool
if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !allowAbsurdFees)) {
if (state.IsInvalid()) {
return NanThrowError((boost::lexical_cast<std::string>(state.GetRejectCode()) + ": " + state.GetRejectReason()).c_str());
} else {
if (fMissingInputs) {
return NanThrowError("Missing inputs");
}
return NanThrowError(state.GetRejectReason().c_str());
}
}
} else if (fHaveChain) {
return NanThrowError("transaction already in block chain");
}
// Relay the transaction connect peers
RelayTransaction(tx);
NanReturnValue(Local<Value>::New(isolate, NanNew<String>(hashTx.GetHex())));
}
2015-07-17 12:55:36 -07:00
/**
2015-09-16 10:35:54 -07:00
* GetMempoolTransactions
* bitcoind.getMempoolTransactions()
* Will return an array of transaction buffers.
2015-07-17 12:55:36 -07:00
*/
2015-09-16 10:35:54 -07:00
NAN_METHOD(GetMempoolTransactions) {
Isolate* isolate = args.GetIsolate();
2015-07-17 12:55:36 -07:00
HandleScope scope(isolate);
2015-09-16 10:35:54 -07:00
Local<Array> transactions = Array::New(isolate);
2015-07-17 12:55:36 -07:00
int arrayIndex = 0;
2015-09-16 10:35:54 -07:00
{
LOCK(mempool.cs);
2015-07-17 12:55:36 -07:00
2015-09-16 10:35:54 -07:00
// Iterate through the entire mempool
std::map<uint256, CTxMemPoolEntry> mapTx = mempool.mapTx;
for(std::map<uint256, CTxMemPoolEntry>::iterator it = mapTx.begin();
it != mapTx.end();
it++) {
CTxMemPoolEntry entry = it->second;
const CTransaction tx = entry.GetTx();
CDataStream dataStreamTx(SER_NETWORK, PROTOCOL_VERSION);
dataStreamTx << tx;
std::string txString = dataStreamTx.str();
Local<Value> txBuffer = node::Buffer::New(isolate, txString.c_str(), txString.size());
transactions->Set(arrayIndex, txBuffer);
arrayIndex++;
2015-07-17 12:55:36 -07:00
}
}
2015-09-16 10:35:54 -07:00
NanReturnValue(transactions);
2015-07-17 12:55:36 -07:00
}
2015-07-17 14:03:28 -07:00
/**
* AddMempoolUncheckedTransaction
*/
NAN_METHOD(AddMempoolUncheckedTransaction) {
NanScope();
2015-07-17 14:03:28 -07:00
v8::String::Utf8Value param1(args[0]->ToString());
std::string *input = new std::string(*param1);
2015-07-17 14:03:28 -07:00
CTransaction tx;
if (!DecodeHexTx(tx, *input)) {
return NanThrowError("could not decode tx");
}
bool added = mempool.addUnchecked(tx.GetHash(), CTxMemPoolEntry(tx, 0, 0, 0.0, 1));
NanReturnValue(NanNew<Boolean>(added));
2015-07-17 14:03:28 -07:00
}
2014-12-10 10:44:01 -08:00
/**
* Helpers
*/
static bool
2014-12-10 11:42:33 -08:00
set_cooked(void) {
uv_tty_t tty;
tty.mode = 1;
tty.orig_termios = orig_termios;
if (!uv_tty_set_mode(&tty, 0)) {
printf("\x1b[H\x1b[J");
return true;
2014-12-10 10:44:01 -08:00
}
return false;
}
2014-11-13 11:56:15 -08:00
2014-08-12 12:03:04 -07:00
/**
2014-10-06 11:05:52 -07:00
* Init()
* Initialize the singleton object known as bitcoind.
2014-08-12 12:03:04 -07:00
*/
extern "C" void
init(Handle<Object> target) {
NanScope();
2014-09-29 13:38:27 -07:00
2014-08-12 12:03:04 -07:00
NODE_SET_METHOD(target, "start", StartBitcoind);
2015-07-07 14:02:03 -07:00
NODE_SET_METHOD(target, "onBlocksReady", OnBlocksReady);
NODE_SET_METHOD(target, "onTipUpdate", OnTipUpdate);
2014-09-11 17:18:36 -07:00
NODE_SET_METHOD(target, "stop", StopBitcoind);
2014-09-18 15:32:19 -07:00
NODE_SET_METHOD(target, "getBlock", GetBlock);
2014-12-03 11:03:54 -08:00
NODE_SET_METHOD(target, "getTransaction", GetTransaction);
2015-07-29 14:13:51 -07:00
NODE_SET_METHOD(target, "getTransactionWithBlockInfo", GetTransactionWithBlockInfo);
2014-10-17 13:26:27 -07:00
NODE_SET_METHOD(target, "getInfo", GetInfo);
2015-07-15 14:45:36 -07:00
NODE_SET_METHOD(target, "isSpent", IsSpent);
NODE_SET_METHOD(target, "getBlockIndex", GetBlockIndex);
2015-09-08 11:04:14 -07:00
NODE_SET_METHOD(target, "isMainChain", IsMainChain);
2015-09-16 10:35:54 -07:00
NODE_SET_METHOD(target, "getMempoolTransactions", GetMempoolTransactions);
2015-07-17 14:03:28 -07:00
NODE_SET_METHOD(target, "addMempoolUncheckedTransaction", AddMempoolUncheckedTransaction);
NODE_SET_METHOD(target, "sendTransaction", SendTransaction);
2015-07-28 13:03:55 -07:00
NODE_SET_METHOD(target, "estimateFee", EstimateFee);
2015-07-30 12:14:14 -07:00
NODE_SET_METHOD(target, "startTxMon", StartTxMon);
NODE_SET_METHOD(target, "syncPercentage", SyncPercentage);
NODE_SET_METHOD(target, "isSynced", IsSynced);
NODE_SET_METHOD(target, "getTxOutSetInfo", GetTxOutSetInfo);
NODE_SET_METHOD(target, "getBestBlockHash", GetBestBlockHash);
NODE_SET_METHOD(target, "getNextBlockHash", GetNextBlockHash);
2015-07-28 13:03:55 -07:00
2014-08-12 12:03:04 -07:00
}
2015-07-31 08:40:15 -07:00
NODE_MODULE(libbitcoind, init)