2014-08-12 12:03:04 -07:00
|
|
|
/**
|
2015-06-10 14:24:11 -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
|
|
|
*
|
2015-09-21 10:50:09 -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-08-29 16:20:38 -07:00
|
|
|
|
2014-09-17 12:14:20 -07:00
|
|
|
using namespace std;
|
|
|
|
using namespace boost;
|
2015-06-11 05:34:38 -07:00
|
|
|
using namespace node;
|
|
|
|
using namespace v8;
|
2015-09-24 10:26:37 -07:00
|
|
|
using Nan::New;
|
|
|
|
using Nan::Null;
|
|
|
|
using Nan::Set;
|
|
|
|
using Nan::ThrowError;
|
|
|
|
using Nan::GetCurrentContext;
|
|
|
|
using Nan::GetFunction;
|
|
|
|
using v8::FunctionTemplate;
|
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
|
|
|
|
2015-07-13 13:34:29 -07:00
|
|
|
extern void WaitForShutdown(boost::thread_group* threadGroup);
|
2014-12-10 11:42:33 -08:00
|
|
|
static termios orig_termios;
|
2015-07-15 15:52:36 -07:00
|
|
|
extern CTxMemPool mempool;
|
2015-07-23 06:32:46 -07:00
|
|
|
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
|
2015-08-06 21:36:38 -07:00
|
|
|
tx_notifier(uv_async_t *handle);
|
2015-07-30 12:14:14 -07:00
|
|
|
|
2015-07-23 06:32:46 -07:00
|
|
|
static void
|
|
|
|
async_tip_update(uv_work_t *req);
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_tip_update_after(uv_work_t *req);
|
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
static void
|
2014-10-06 08:10:23 -07:00
|
|
|
async_start_node(uv_work_t *req);
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
static void
|
2014-09-02 19:00:31 -07:00
|
|
|
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);
|
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
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
|
2015-09-23 12:13:24 -07:00
|
|
|
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
|
2015-09-21 10:50:09 -07:00
|
|
|
* Used only by bitcoind functions.
|
2014-09-19 13:53:55 -07:00
|
|
|
*/
|
2015-09-23 12:13:24 -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;
|
2015-09-15 13:58:30 -07:00
|
|
|
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;
|
2015-07-20 14:55:49 -07:00
|
|
|
static bool g_regtest = false;
|
2014-12-01 20:43:44 -08:00
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2015-07-23 06:32:46 -07:00
|
|
|
struct async_tip_update_data {
|
2015-09-01 07:52:24 -07:00
|
|
|
uv_work_t req;
|
2015-07-23 06:32:46 -07:00
|
|
|
size_t result;
|
2015-09-01 07:52:24 -07:00
|
|
|
Isolate* isolate;
|
|
|
|
Persistent<Function> callback;
|
2015-07-23 06:32:46 -07:00
|
|
|
};
|
|
|
|
|
2015-07-07 14:02:03 -07:00
|
|
|
/**
|
|
|
|
* async_node_data
|
|
|
|
* Where the uv async request data resides.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct async_block_ready_data {
|
2015-09-01 07:52:24 -07:00
|
|
|
uv_work_t req;
|
2015-07-07 14:02:03 -07:00
|
|
|
std::string err_msg;
|
|
|
|
std::string result;
|
2015-09-01 07:52:24 -07:00
|
|
|
Isolate* isolate;
|
|
|
|
Persistent<Function> callback;
|
2015-07-07 14:02:03 -07:00
|
|
|
};
|
|
|
|
|
2014-08-20 17:00:09 -07:00
|
|
|
/**
|
2014-09-02 19:00:31 -07:00
|
|
|
* async_node_data
|
2014-08-20 17:00:09 -07:00
|
|
|
* Where the uv async request data resides.
|
|
|
|
*/
|
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
struct async_node_data {
|
2015-09-01 07:52:24 -07:00
|
|
|
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;
|
2015-07-20 14:55:49 -07:00
|
|
|
bool regtest;
|
2014-12-01 20:43:44 -08:00
|
|
|
bool txindex;
|
2015-09-01 07:52:24 -07:00
|
|
|
Isolate* isolate;
|
|
|
|
Persistent<Function> callback;
|
2014-08-19 16:40:19 -07:00
|
|
|
};
|
|
|
|
|
2014-09-22 13:21:42 -07:00
|
|
|
/**
|
|
|
|
* async_block_data
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct async_block_data {
|
2015-09-01 07:52:24 -07:00
|
|
|
uv_work_t req;
|
2014-09-22 13:21:42 -07:00
|
|
|
std::string err_msg;
|
2015-07-14 12:17:45 -07:00
|
|
|
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;
|
2015-09-01 07:52:24 -07:00
|
|
|
Isolate* isolate;
|
|
|
|
Persistent<Function> callback;
|
2014-09-22 13:21:42 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* async_tx_data
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct async_tx_data {
|
2015-09-01 07:52:24 -07:00
|
|
|
uv_work_t req;
|
2014-09-22 13:21:42 -07:00
|
|
|
std::string err_msg;
|
2015-07-14 12:17:45 -07:00
|
|
|
std::string txid;
|
2015-08-28 09:57:01 -07:00
|
|
|
std::string blockHash;
|
2015-07-29 14:13:51 -07:00
|
|
|
uint32_t nTime;
|
|
|
|
int64_t height;
|
2015-07-16 11:31:58 -07:00
|
|
|
bool queryMempool;
|
2014-10-03 11:23:04 -07:00
|
|
|
CTransaction ctx;
|
2015-09-01 07:52:24 -07:00
|
|
|
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
|
|
|
|
2015-08-11 09:31:54 -07:00
|
|
|
/**
|
2015-08-11 14:16:04 -07:00
|
|
|
* SyncPercentage()
|
|
|
|
* bitcoind.syncPercentage()
|
2015-08-11 09:31:54 -07:00
|
|
|
* provides a float value >= indicating the progress of the blockchain sync
|
|
|
|
*/
|
2015-08-11 14:16:04 -07:00
|
|
|
NAN_METHOD(SyncPercentage) {
|
2015-08-11 09:31:54 -07:00
|
|
|
const CChainParams& chainParams = Params();
|
|
|
|
float progress = 0;
|
|
|
|
progress = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip());
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(progress * 100);
|
2015-08-11 14:16:04 -07:00
|
|
|
};
|
|
|
|
|
2015-09-17 15:11:47 -07:00
|
|
|
NAN_METHOD(GetBestBlockHash) {
|
2015-09-24 10:26:37 -07:00
|
|
|
LOCK(cs_main);
|
|
|
|
info.GetReturnValue().Set(New(chainActive.Tip()->GetBlockHash().GetHex()).ToLocalChecked());
|
2015-09-17 15:11:47 -07:00
|
|
|
}
|
|
|
|
|
2015-09-21 10:50:09 -07:00
|
|
|
NAN_METHOD(GetNextBlockHash) {
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
if (info.Length() < 1 || !info[0]->IsString()) {
|
|
|
|
return ThrowError("Usage: bitcoind.getNextBlockHash(blockhash)");
|
2015-09-21 10:50:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
CBlockIndex* pblockindex;
|
2015-09-24 10:26:37 -07:00
|
|
|
v8::String::Utf8Value param1(info[0]->ToString());
|
2015-09-21 10:50:09 -07:00
|
|
|
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();
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(New(rethash).ToLocalChecked());
|
2015-09-21 10:50:09 -07:00
|
|
|
} else {
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Null());
|
2015-09-21 10:50:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-08-11 14:16:04 -07:00
|
|
|
/**
|
|
|
|
* IsSynced()
|
|
|
|
* bitcoind.isSynced()
|
|
|
|
* returns a boolean of bitcoin is fully synced
|
|
|
|
*/
|
|
|
|
NAN_METHOD(IsSynced) {
|
|
|
|
bool isDownloading = IsInitialBlockDownload();
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(New(!isDownloading));
|
2015-08-11 09:31:54 -07:00
|
|
|
};
|
|
|
|
|
2015-07-30 12:14:14 -07:00
|
|
|
NAN_METHOD(StartTxMon) {
|
2015-09-24 10:26:37 -07:00
|
|
|
Isolate* isolate = info.GetIsolate();
|
|
|
|
Local<Function> callback = Local<Function>::Cast(info[0]);
|
2015-07-30 12:14:14 -07:00
|
|
|
Eternal<Function> cb(isolate, callback);
|
|
|
|
txmon_callback = cb;
|
2015-09-15 13:58:30 -07:00
|
|
|
txmon_callback_available = true;
|
2015-07-30 12:14:14 -07:00
|
|
|
|
|
|
|
CNodeSignals& nodeSignals = GetNodeSignals();
|
2015-09-23 12:13:24 -07:00
|
|
|
nodeSignals.TxToMemPool.connect(&queueTx);
|
2015-07-30 12:14:14 -07:00
|
|
|
|
2015-08-06 21:36:38 -07:00
|
|
|
uv_async_init(uv_default_loop(), &txmon_async, tx_notifier);
|
2015-07-30 12:14:14 -07:00
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Null());
|
2015-07-30 12:14:14 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
2015-08-06 21:36:38 -07:00
|
|
|
tx_notifier(uv_async_t *handle) {
|
2015-09-24 10:26:37 -07:00
|
|
|
Isolate* isolate = GetCurrentContext()->GetIsolate();
|
2015-07-30 12:14:14 -07:00
|
|
|
HandleScope scope(isolate);
|
|
|
|
|
2015-09-23 12:13:24 -07:00
|
|
|
Local<Array> results = Array::New(isolate);
|
|
|
|
int arrayIndex = 0;
|
2015-08-06 21:36:38 -07:00
|
|
|
|
2015-09-23 12:13:24 -07:00
|
|
|
LOCK(cs_main);
|
|
|
|
BOOST_FOREACH(const CTransaction& tx, txQueue) {
|
2015-08-06 21:36:38 -07:00
|
|
|
|
2015-09-23 12:13:24 -07:00
|
|
|
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());
|
2015-08-06 21:36:38 -07:00
|
|
|
|
2015-09-23 12:13:24 -07:00
|
|
|
uint256 hash = tx.GetHash();
|
2015-08-06 21:36:38 -07:00
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Object> obj = New<Object>();
|
2015-08-06 21:36:38 -07:00
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Set(obj, New("buffer").ToLocalChecked(), txBuffer);
|
|
|
|
Set(obj, New("hash").ToLocalChecked(), New(hash.GetHex()).ToLocalChecked());
|
|
|
|
Set(obj, New("mempool").ToLocalChecked(), New<Boolean>(true));
|
2015-07-30 12:14:14 -07:00
|
|
|
|
2015-09-23 12:13:24 -07:00
|
|
|
results->Set(arrayIndex, obj);
|
|
|
|
arrayIndex++;
|
|
|
|
}
|
2015-07-30 12:14:14 -07:00
|
|
|
|
2015-09-23 12:13:24 -07:00
|
|
|
const unsigned argc = 1;
|
|
|
|
Local<Value> argv[argc] = {
|
|
|
|
Local<Value>::New(isolate, results)
|
|
|
|
};
|
2015-07-30 12:14:14 -07:00
|
|
|
|
2015-09-23 12:13:24 -07:00
|
|
|
Local<Function> cb = txmon_callback.Get(isolate);
|
2015-07-30 12:14:14 -07:00
|
|
|
|
2015-09-23 12:13:24 -07:00
|
|
|
cb->Call(isolate->GetCurrentContext()->Global(), argc, argv);
|
2015-08-03 13:10:58 -07:00
|
|
|
|
2015-09-23 12:13:24 -07:00
|
|
|
txQueue.clear();
|
2015-07-30 12:14:14 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
static bool
|
2015-09-23 12:13:24 -07:00
|
|
|
queueTx(const CTransaction& tx) {
|
|
|
|
LOCK(cs_main);
|
|
|
|
txQueue.push_back(tx);
|
|
|
|
uv_async_send(&txmon_async);
|
2015-08-06 21:36:38 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-10-15 15:27:14 -07:00
|
|
|
/**
|
|
|
|
* Functions
|
|
|
|
*/
|
|
|
|
|
2015-07-23 06:32:46 -07:00
|
|
|
NAN_METHOD(OnTipUpdate) {
|
2015-09-24 10:26:37 -07:00
|
|
|
Isolate* isolate = info.GetIsolate();
|
2015-07-23 06:32:46 -07:00
|
|
|
HandleScope scope(isolate);
|
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
async_tip_update_data *req = new async_tip_update_data();
|
2015-07-23 06:32:46 -07:00
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Function> callback = Local<Function>::Cast(info[0]);
|
2015-09-01 07:52:24 -07:00
|
|
|
req->callback.Reset(isolate, callback);
|
|
|
|
req->req.data = req;
|
|
|
|
req->isolate = isolate;
|
2015-07-23 06:32:46 -07:00
|
|
|
|
|
|
|
int status = uv_queue_work(uv_default_loop(),
|
2015-09-01 07:52:24 -07:00
|
|
|
&req->req, async_tip_update,
|
2015-07-23 06:32:46 -07:00
|
|
|
(uv_after_work_cb)async_tip_update_after);
|
|
|
|
|
|
|
|
assert(status == 0);
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Null());
|
2015-07-23 06:32:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_tip_update(uv_work_t *req) {
|
2015-09-01 07:52:24 -07:00
|
|
|
async_tip_update_data *data = reinterpret_cast<async_tip_update_data*>(req->data);
|
2015-07-23 06:32:46 -07:00
|
|
|
|
2015-07-24 09:32:28 -07:00
|
|
|
size_t lastHeight = chainActive.Height();
|
2015-07-23 06:32:46 -07:00
|
|
|
|
2015-07-24 09:32:28 -07:00
|
|
|
while(lastHeight == (size_t)chainActive.Height() && !shutdown_complete) {
|
2015-07-23 06:32:46 -07:00
|
|
|
usleep(1E6);
|
|
|
|
}
|
|
|
|
|
|
|
|
data->result = chainActive.Height();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-09-01 07:52:24 -07:00
|
|
|
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;
|
2015-07-23 06:32:46 -07:00
|
|
|
HandleScope scope(isolate);
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Function> cb = Local<Function>::New(isolate, req->callback);
|
2015-07-23 06:32:46 -07:00
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
TryCatch try_catch;
|
2015-07-24 09:32:28 -07:00
|
|
|
Local<Value> result = Undefined(isolate);
|
2015-09-01 07:52:24 -07:00
|
|
|
|
2015-07-24 09:32:28 -07:00
|
|
|
if (!shutdown_complete) {
|
2015-09-24 10:26:37 -07:00
|
|
|
result = New<Number>(req->result);
|
2015-07-24 09:32:28 -07:00
|
|
|
}
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Value> argv[1] = {
|
2015-07-24 09:32:28 -07:00
|
|
|
Local<Value>::New(isolate, result)
|
2015-07-23 06:32:46 -07:00
|
|
|
};
|
2015-09-01 07:52:24 -07:00
|
|
|
cb->Call(isolate->GetCurrentContext()->Global(), 1, argv);
|
2015-07-23 06:32:46 -07:00
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
req->callback.Reset();
|
2015-07-23 06:32:46 -07:00
|
|
|
delete req;
|
|
|
|
}
|
|
|
|
|
2015-07-07 14:02:03 -07:00
|
|
|
NAN_METHOD(OnBlocksReady) {
|
2015-09-24 10:26:37 -07:00
|
|
|
Isolate* isolate = info.GetIsolate();
|
2015-07-07 14:02:03 -07:00
|
|
|
HandleScope scope(isolate);
|
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
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
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Function> callback = Local<Function>::Cast(info[0]);
|
2015-09-01 07:52:24 -07:00
|
|
|
req->callback.Reset(isolate, callback);
|
2015-07-07 14:02:03 -07:00
|
|
|
|
|
|
|
int status = uv_queue_work(uv_default_loop(),
|
2015-09-01 07:52:24 -07:00
|
|
|
&req->req, async_blocks_ready,
|
2015-07-07 14:02:03 -07:00
|
|
|
(uv_after_work_cb)async_blocks_ready_after);
|
|
|
|
|
|
|
|
assert(status == 0);
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Null());
|
2015-07-07 14:02:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* async_start_node()
|
|
|
|
* Call start_node() and start all our boost threads.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_blocks_ready(uv_work_t *req) {
|
2015-09-01 07:52:24 -07:00
|
|
|
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();
|
|
|
|
|
2015-07-16 13:41:11 -07:00
|
|
|
// 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
|
|
|
}
|
|
|
|
|
2015-07-16 13:41:11 -07:00
|
|
|
// Wait for chainActive to be able to get the hash
|
|
|
|
// for the genesis block for querying blocks by height
|
2015-07-16 10:43:51 -07:00
|
|
|
while(chainActive[0] == NULL) {
|
|
|
|
usleep(1E6);
|
|
|
|
}
|
2015-07-16 13:41:11 -07:00
|
|
|
|
2015-08-18 14:56:35 -07:00
|
|
|
//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
|
2015-08-19 13:33:01 -07:00
|
|
|
while(pwalletMain == NULL || RPCIsInWarmup(NULL)) {
|
2015-08-18 14:56:35 -07:00
|
|
|
usleep(1E6);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-07-16 13:41:11 -07:00
|
|
|
// 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
|
2015-09-01 07:52:24 -07:00
|
|
|
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);
|
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
TryCatch try_catch;
|
|
|
|
Local<Function> cb = Local<Function>::New(isolate, req->callback);
|
|
|
|
|
|
|
|
if (req->err_msg != "") {
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value> err = Exception::Error(New(req->err_msg).ToLocalChecked());
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Value> argv[1] = { err };
|
|
|
|
cb->Call(isolate->GetCurrentContext()->Global(), 1, argv);
|
2015-07-07 14:02:03 -07:00
|
|
|
} else {
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Value> argv[2] = {
|
2015-07-07 14:02:03 -07:00
|
|
|
v8::Null(isolate),
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value>::New(isolate, New(req->result).ToLocalChecked())
|
2015-07-07 14:02:03 -07:00
|
|
|
};
|
2015-09-01 07:52:24 -07:00
|
|
|
cb->Call(isolate->GetCurrentContext()->Global(), 2, argv);
|
2015-07-07 14:02:03 -07:00
|
|
|
}
|
|
|
|
|
2015-09-01 07:52:24 -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) {
|
2015-09-24 10:26:37 -07:00
|
|
|
Isolate* isolate = info.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;
|
2015-07-20 14:55:49 -07:00
|
|
|
bool regtest = false;
|
2014-12-01 20:43:44 -08:00
|
|
|
bool txindex = false;
|
2014-10-15 16:38:10 -07:00
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
if (info.Length() >= 2 && info[0]->IsObject() && info[1]->IsFunction()) {
|
|
|
|
Local<Object> options = Local<Object>::Cast(info[0]);
|
|
|
|
if (options->Get(New("datadir").ToLocalChecked())->IsString()) {
|
|
|
|
String::Utf8Value datadir_(options->Get(New("datadir").ToLocalChecked())->ToString());
|
2014-10-15 16:38:10 -07:00
|
|
|
datadir = std::string(*datadir_);
|
2014-10-15 15:36:35 -07:00
|
|
|
}
|
2015-09-24 10:26:37 -07:00
|
|
|
if (options->Get(New("rpc").ToLocalChecked())->IsBoolean()) {
|
|
|
|
rpc = options->Get(New("rpc").ToLocalChecked())->ToBoolean()->IsTrue();
|
2014-10-17 12:06:40 -07:00
|
|
|
}
|
2015-09-24 10:26:37 -07:00
|
|
|
if (options->Get(New("network").ToLocalChecked())->IsString()) {
|
|
|
|
String::Utf8Value network_(options->Get(New("network").ToLocalChecked())->ToString());
|
2015-07-20 14:55:49 -07:00
|
|
|
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
|
|
|
}
|
2015-09-24 10:26:37 -07:00
|
|
|
if (options->Get(New("txindex").ToLocalChecked())->IsBoolean()) {
|
|
|
|
txindex = options->Get(New("txindex").ToLocalChecked())->ToBoolean()->IsTrue();
|
2014-12-01 20:43:44 -08:00
|
|
|
}
|
2015-09-24 10:26:37 -07:00
|
|
|
callback = Local<Function>::Cast(info[1]);
|
|
|
|
} else if (info.Length() >= 2
|
|
|
|
&& (info[0]->IsUndefined() || info[0]->IsNull())
|
|
|
|
&& info[1]->IsFunction()) {
|
|
|
|
callback = Local<Function>::Cast(info[1]);
|
|
|
|
} else if (info.Length() >= 1 && info[0]->IsFunction()) {
|
|
|
|
callback = Local<Function>::Cast(info[0]);
|
2014-10-15 16:38:10 -07:00
|
|
|
} else {
|
2015-09-24 10:26:37 -07:00
|
|
|
return ThrowError(
|
2014-10-15 16:38:10 -07:00
|
|
|
"Usage: bitcoind.start(callback)");
|
2014-08-12 12:03:04 -07:00
|
|
|
}
|
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
//
|
|
|
|
// Run bitcoind's StartNode() on a separate thread.
|
|
|
|
//
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
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;
|
2015-06-08 14:18:06 -07:00
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
req->isolate = isolate;
|
|
|
|
req->callback.Reset(isolate, callback);
|
|
|
|
req->req.data = req;
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
int status = uv_queue_work(uv_default_loop(),
|
2015-09-01 07:52:24 -07:00
|
|
|
&req->req, async_start_node,
|
2014-09-02 19:00:31 -07:00
|
|
|
(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
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Null());
|
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.
|
|
|
|
*/
|
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
static void
|
2014-10-06 08:10:23 -07:00
|
|
|
async_start_node(uv_work_t *req) {
|
2015-09-01 07:52:24 -07:00
|
|
|
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;
|
2015-07-20 14:55:49 -07:00
|
|
|
g_regtest = (bool)data->regtest;
|
2014-12-01 20:43:44 -08:00
|
|
|
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-19 16:40:19 -07:00
|
|
|
}
|
|
|
|
|
2014-08-20 17:00:09 -07:00
|
|
|
/**
|
2014-09-02 19:00:31 -07:00
|
|
|
* async_start_node_after()
|
2014-08-20 17:00:09 -07:00
|
|
|
* Execute our callback.
|
|
|
|
*/
|
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
static void
|
2015-09-01 07:52:24 -07:00
|
|
|
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);
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
TryCatch try_catch;
|
|
|
|
Local<Function> cb = Local<Function>::New(isolate, req->callback);
|
|
|
|
|
|
|
|
if (req->err_msg != "") {
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value> err = Exception::Error(New(req->err_msg).ToLocalChecked());
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Value> argv[1] = { err };
|
|
|
|
cb->Call(isolate->GetCurrentContext()->Global(), 1, argv);
|
2014-08-19 16:40:19 -07:00
|
|
|
} else {
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Value> argv[2] = {
|
2015-06-10 12:38:21 -07:00
|
|
|
v8::Null(isolate),
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value>::New(isolate, New(req->result).ToLocalChecked())
|
2014-08-19 16:40:19 -07:00
|
|
|
};
|
2015-09-01 07:52:24 -07:00
|
|
|
cb->Call(isolate->GetCurrentContext()->Global(), 2, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
2014-08-19 16:40:19 -07:00
|
|
|
}
|
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
req->callback.Reset();
|
2014-08-19 16:40:19 -07:00
|
|
|
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
|
|
|
*/
|
2014-09-02 19:00:31 -07:00
|
|
|
|
2014-08-20 16:47:18 -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();
|
|
|
|
|
2015-06-10 14:24:11 -07:00
|
|
|
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) {
|
2015-07-13 13:34:29 -07:00
|
|
|
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;
|
2014-12-01 20:43:44 -08:00
|
|
|
char **argv = (char **)malloc((4 + 1) * sizeof(char **));
|
2014-10-17 12:11:34 -07:00
|
|
|
|
2014-10-21 10:47:09 -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);
|
2014-10-16 14:56:41 -07:00
|
|
|
int w = snprintf(arg, argl, "-datadir=%s", g_data_dir);
|
2014-10-21 18:06:17 -07:00
|
|
|
if (w >= 10 && w <= argl) {
|
2014-10-21 10:47:09 -07:00
|
|
|
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-16 14:56:41 -07:00
|
|
|
}
|
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++;
|
|
|
|
}
|
|
|
|
|
2015-07-20 14:55:49 -07:00
|
|
|
if (g_regtest) {
|
|
|
|
argv[argc] = (char *)"-regtest";
|
|
|
|
argc++;
|
|
|
|
}
|
|
|
|
|
2015-07-16 11:31:58 -07:00
|
|
|
argv[argc] = (char *)"-txindex";
|
|
|
|
argc++;
|
2014-12-01 20:43:44 -08:00
|
|
|
|
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
|
|
|
|
2015-07-13 13:34:29 -07:00
|
|
|
fRet = AppInit2(threadGroup, scheduler);
|
2015-06-26 13:03:43 -07:00
|
|
|
|
2014-10-13 14:00:01 -07:00
|
|
|
} catch (std::exception& e) {
|
2015-06-10 14:24:11 -07:00
|
|
|
if (set_cooked()) {
|
2015-07-13 13:34:29 -07:00
|
|
|
fprintf(stderr, "bitcoind.js: AppInit2(): std::exception\n");
|
2015-06-10 14:24:11 -07:00
|
|
|
}
|
2014-10-13 14:00:01 -07:00
|
|
|
} catch (...) {
|
2015-06-10 14:24:11 -07:00
|
|
|
if (set_cooked()) {
|
2015-07-13 13:34:29 -07:00
|
|
|
fprintf(stderr, "bitcoind.js: AppInit2(): other exception\n");
|
2014-10-13 14:00:01 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-13 13:34:29 -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;
|
2015-07-13 13:34:29 -07:00
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
}
|
|
|
|
|
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) {
|
2015-09-24 10:26:37 -07:00
|
|
|
Isolate* isolate = info.GetIsolate();
|
2015-06-09 03:57:58 -07:00
|
|
|
HandleScope scope(isolate);
|
2014-12-12 11:24:53 -08:00
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
if (info.Length() < 1 || !info[0]->IsFunction()) {
|
|
|
|
return ThrowError(
|
2014-09-22 12:57:25 -07:00
|
|
|
"Usage: bitcoind.stop(callback)");
|
2014-09-11 17:18:36 -07:00
|
|
|
}
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Function> callback = Local<Function>::Cast(info[0]);
|
2014-09-11 17:18:36 -07:00
|
|
|
|
|
|
|
//
|
|
|
|
// Run bitcoind's StartShutdown() on a separate thread.
|
|
|
|
//
|
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
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(),
|
2015-09-01 07:52:24 -07:00
|
|
|
&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);
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Null());
|
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) {
|
2015-09-01 07:52:24 -07:00
|
|
|
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
|
|
|
|
2015-07-09 11:39:23 -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
|
2015-09-01 07:52:24 -07:00
|
|
|
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
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
TryCatch try_catch;
|
|
|
|
Local<Function> cb = Local<Function>::New(isolate, req->callback);
|
|
|
|
|
|
|
|
if (req->err_msg != "") {
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value> err = Exception::Error(New(req->err_msg).ToLocalChecked());
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Value> argv[1] = { err };
|
|
|
|
cb->Call(isolate->GetCurrentContext()->Global(), 1, argv);
|
2014-09-11 17:18:36 -07:00
|
|
|
} else {
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Value> argv[2] = {
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value>::New(isolate, Null()),
|
|
|
|
Local<Value>::New(isolate, New(req->result).ToLocalChecked())
|
2014-09-11 17:18:36 -07:00
|
|
|
};
|
2015-09-01 07:52:24 -07:00
|
|
|
cb->Call(isolate->GetCurrentContext()->Global(), 2, argv);
|
2014-09-11 17:18:36 -07:00
|
|
|
}
|
|
|
|
|
2015-09-01 07:52:24 -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) {
|
2015-09-24 10:26:37 -07:00
|
|
|
Isolate* isolate = info.GetIsolate();
|
2015-06-09 03:57:58 -07:00
|
|
|
HandleScope scope(isolate);
|
2015-09-24 10:26:37 -07:00
|
|
|
if (info.Length() < 2
|
|
|
|
|| (!info[0]->IsString() && !info[0]->IsNumber())
|
|
|
|
|| !info[1]->IsFunction()) {
|
|
|
|
return ThrowError(
|
2015-09-21 10:50:09 -07:00
|
|
|
"Usage: bitcoind.getBlock([blockhash,blockheight], callback)");
|
2014-09-19 13:53:55 -07:00
|
|
|
}
|
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
async_block_data *req = new async_block_data();
|
2014-09-19 13:53:55 -07:00
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
if (info[0]->IsNumber()) {
|
|
|
|
int64_t height = info[0]->IntegerValue();
|
2015-09-01 07:52:24 -07:00
|
|
|
req->err_msg = std::string("");
|
|
|
|
req->hash = std::string("");
|
|
|
|
req->height = height;
|
2014-11-04 16:08:31 -08:00
|
|
|
} else {
|
2015-09-24 10:26:37 -07:00
|
|
|
String::Utf8Value hash_(info[0]->ToString());
|
2015-07-14 12:17:45 -07:00
|
|
|
std::string hash = std::string(*hash_);
|
2015-09-01 07:52:24 -07:00
|
|
|
req->err_msg = std::string("");
|
|
|
|
req->hash = hash;
|
|
|
|
req->height = -1;
|
2014-11-04 16:08:31 -08:00
|
|
|
}
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Function> callback = Local<Function>::Cast(info[1]);
|
2015-09-01 07:52:24 -07:00
|
|
|
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(),
|
2015-09-01 07:52:24 -07:00
|
|
|
&req->req, async_get_block,
|
2014-09-19 13:53:55 -07:00
|
|
|
(uv_after_work_cb)async_get_block_after);
|
|
|
|
|
|
|
|
assert(status == 0);
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Null());
|
2014-09-19 13:53:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_get_block(uv_work_t *req) {
|
2015-09-01 07:52:24 -07:00
|
|
|
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;
|
2015-07-13 13:34:29 -07:00
|
|
|
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];
|
2015-07-15 17:04:55 -07:00
|
|
|
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
|
2015-09-01 07:52:24 -07:00
|
|
|
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
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
TryCatch try_catch;
|
|
|
|
Local<Function> cb = Local<Function>::New(isolate, req->callback);
|
|
|
|
|
|
|
|
if (req->err_msg != "") {
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value> err = Exception::Error(New(req->err_msg).ToLocalChecked());
|
2015-09-01 07:52:24 -07:00
|
|
|
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
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Value> rawNodeBuffer = node::Buffer::New(isolate, req->buffer, req->size);
|
2014-09-19 15:39:05 -07:00
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
delete req->buffer;
|
|
|
|
req->buffer = NULL;
|
2015-07-09 14:34:37 -07:00
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Value> argv[2] = {
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value>::New(isolate, Null()),
|
2015-07-07 20:24:22 -07:00
|
|
|
rawNodeBuffer
|
2014-09-19 13:53:55 -07:00
|
|
|
};
|
2015-09-01 07:52:24 -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
|
|
|
}
|
|
|
|
|
2015-09-01 07:52:24 -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) {
|
2015-09-24 10:26:37 -07:00
|
|
|
Isolate* isolate = info.GetIsolate();
|
2015-06-09 03:57:58 -07:00
|
|
|
HandleScope scope(isolate);
|
2015-09-24 10:26:37 -07:00
|
|
|
if (info.Length() < 3
|
|
|
|
|| !info[0]->IsString()
|
|
|
|
|| !info[1]->IsBoolean()
|
|
|
|
|| !info[2]->IsFunction()) {
|
|
|
|
return ThrowError(
|
2015-07-29 14:13:51 -07:00
|
|
|
"Usage: daemon.getTransaction(txid, queryMempool, callback)");
|
2014-09-22 13:21:42 -07:00
|
|
|
}
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
String::Utf8Value txid_(info[0]->ToString());
|
|
|
|
bool queryMempool = info[1]->BooleanValue();
|
|
|
|
Local<Function> callback = Local<Function>::Cast(info[2]);
|
2014-09-22 13:21:42 -07:00
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
async_tx_data *req = new async_tx_data();
|
2015-07-13 13:34:29 -07:00
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
req->err_msg = std::string("");
|
|
|
|
req->txid = std::string("");
|
2015-07-14 12:17:45 -07:00
|
|
|
|
|
|
|
std::string txid = std::string(*txid_);
|
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
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(),
|
2015-09-01 07:52:24 -07:00
|
|
|
&req->req, async_get_tx,
|
2014-09-22 13:21:42 -07:00
|
|
|
(uv_after_work_cb)async_get_tx_after);
|
|
|
|
|
|
|
|
assert(status == 0);
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Null());
|
2014-09-22 13:21:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_get_tx(uv_work_t *req) {
|
2015-09-01 07:52:24 -07:00
|
|
|
async_tx_data* data = reinterpret_cast<async_tx_data*>(req->data);
|
2014-09-22 13:21:42 -07:00
|
|
|
|
2015-07-13 13:34:29 -07:00
|
|
|
uint256 hash = uint256S(data->txid);
|
2015-07-15 11:16:15 -07:00
|
|
|
uint256 blockhash;
|
2014-10-02 13:24:18 -07:00
|
|
|
CTransaction ctx;
|
2015-07-16 11:31:58 -07:00
|
|
|
|
2015-07-16 13:09:30 -07:00
|
|
|
if (data->queryMempool) {
|
|
|
|
LOCK(cs_main);
|
|
|
|
{
|
|
|
|
if (mempool.lookup(hash, ctx))
|
2015-07-16 11:31:58 -07:00
|
|
|
{
|
2015-07-17 18:24:59 -07:00
|
|
|
data->ctx = ctx;
|
2015-07-16 13:09:30 -07:00
|
|
|
return;
|
2015-07-16 11:31:58 -07:00
|
|
|
}
|
|
|
|
}
|
2015-07-16 13:09:30 -07:00
|
|
|
}
|
2015-07-16 11:31:58 -07:00
|
|
|
|
2015-07-16 13:09:30 -07:00
|
|
|
CDiskTxPos postx;
|
|
|
|
if (pblocktree->ReadTxIndex(hash, postx)) {
|
2015-07-16 11:31:58 -07:00
|
|
|
|
2015-07-16 13:09:30 -07:00
|
|
|
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
|
2015-07-16 11:31:58 -07:00
|
|
|
|
2015-07-16 13:09:30 -07:00
|
|
|
if (file.IsNull()) {
|
|
|
|
data->err_msg = std::string("%s: OpenBlockFile failed", __func__);
|
|
|
|
return;
|
|
|
|
}
|
2015-07-16 11:31:58 -07:00
|
|
|
|
2015-07-16 13:09:30 -07:00
|
|
|
const int HEADER_SIZE = sizeof(int32_t) + sizeof(uint32_t) * 3 + sizeof(char) * 64;
|
2015-07-16 11:31:58 -07:00
|
|
|
|
2015-07-16 13:09:30 -07:00
|
|
|
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;
|
2015-07-16 11:31:58 -07:00
|
|
|
}
|
|
|
|
|
2014-12-04 15:38:35 -08:00
|
|
|
}
|
2015-07-15 11:16:15 -07:00
|
|
|
|
2014-09-22 13:21:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2015-09-01 07:52:24 -07:00
|
|
|
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
|
|
|
|
2015-09-01 07:52:24 -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
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
if (req->err_msg != "") {
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value> err = Exception::Error(New(req->err_msg).ToLocalChecked());
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Value> argv[1] = { err };
|
|
|
|
cb->Call(isolate->GetCurrentContext()->Global(), 1, argv);
|
2014-09-22 13:21:42 -07:00
|
|
|
} else {
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value> result = Local<Value>::New(isolate, Null());
|
2015-08-03 14:41:11 -07:00
|
|
|
|
|
|
|
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());
|
|
|
|
}
|
2015-07-16 11:31:58 -07:00
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Value> argv[2] = {
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value>::New(isolate, Null()),
|
2015-08-03 14:41:11 -07:00
|
|
|
result
|
2014-09-22 13:21:42 -07:00
|
|
|
};
|
2015-09-01 07:52:24 -07:00
|
|
|
cb->Call(isolate->GetCurrentContext()->Global(), 2, argv);
|
2014-09-22 13:21:42 -07:00
|
|
|
}
|
2015-09-01 07:52:24 -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) {
|
2015-09-24 10:26:37 -07:00
|
|
|
Isolate* isolate = info.GetIsolate();
|
2015-07-29 14:13:51 -07:00
|
|
|
HandleScope scope(isolate);
|
2015-09-24 10:26:37 -07:00
|
|
|
if (info.Length() < 3
|
|
|
|
|| !info[0]->IsString()
|
|
|
|
|| !info[1]->IsBoolean()
|
|
|
|
|| !info[2]->IsFunction()) {
|
|
|
|
return ThrowError(
|
2015-09-21 10:50:09 -07:00
|
|
|
"Usage: bitcoind.getTransactionWithBlockInfo(txid, queryMempool, callback)");
|
2015-07-29 14:13:51 -07:00
|
|
|
}
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
String::Utf8Value txid_(info[0]->ToString());
|
|
|
|
bool queryMempool = info[1]->BooleanValue();
|
|
|
|
Local<Function> callback = Local<Function>::Cast(info[2]);
|
2015-07-29 14:13:51 -07:00
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
async_tx_data *req = new async_tx_data();
|
2015-07-29 14:13:51 -07:00
|
|
|
|
2015-09-01 07:52:24 -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_);
|
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
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(),
|
2015-09-01 07:52:24 -07:00
|
|
|
&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);
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Null());
|
2015-07-29 14:13:51 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_get_tx_and_info(uv_work_t *req) {
|
2015-09-01 07:52:24 -07:00
|
|
|
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();
|
2015-08-28 09:57:01 -07:00
|
|
|
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
|
2015-09-01 07:52:24 -07:00
|
|
|
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);
|
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
CTransaction ctx = req->ctx;
|
|
|
|
TryCatch try_catch;
|
|
|
|
Local<Function> cb = Local<Function>::New(isolate, req->callback);
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Object> obj = New<Object>();
|
2015-07-29 14:13:51 -07:00
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
if (req->err_msg != "") {
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value> err = Exception::Error(New(req->err_msg).ToLocalChecked());
|
2015-09-01 07:52:24 -07:00
|
|
|
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());
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Set(obj, New("blockHash").ToLocalChecked(), New(req->blockHash).ToLocalChecked());
|
|
|
|
Set(obj, New("height").ToLocalChecked(), New<Number>(req->height));
|
|
|
|
Set(obj, New("timestamp").ToLocalChecked(), New<Number>(req->nTime));
|
|
|
|
Set(obj, New("buffer").ToLocalChecked(), rawNodeBuffer);
|
2015-07-29 14:13:51 -07:00
|
|
|
|
2015-09-01 07:52:24 -07:00
|
|
|
Local<Value> argv[2] = {
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Value>::New(isolate, Null()),
|
2015-07-29 14:13:51 -07:00
|
|
|
obj
|
|
|
|
};
|
2015-09-01 07:52:24 -07:00
|
|
|
cb->Call(isolate->GetCurrentContext()->Global(), 2, argv);
|
|
|
|
}
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
2015-07-29 14:13:51 -07:00
|
|
|
}
|
2015-09-01 07:52:24 -07:00
|
|
|
req->callback.Reset();
|
2015-07-29 14:13:51 -07:00
|
|
|
delete req;
|
|
|
|
}
|
|
|
|
|
2015-07-15 14:45:36 -07:00
|
|
|
/**
|
|
|
|
* IsSpent()
|
2015-09-21 10:50:09 -07:00
|
|
|
* bitcoind.isSpent()
|
2015-07-15 14:45:36 -07:00
|
|
|
* Determine if an outpoint is spent
|
|
|
|
*/
|
|
|
|
NAN_METHOD(IsSpent) {
|
2015-09-24 10:26:37 -07:00
|
|
|
if (info.Length() > 2) {
|
|
|
|
return ThrowError(
|
2015-09-21 10:50:09 -07:00
|
|
|
"Usage: bitcoind.isSpent(txid, outputIndex)");
|
2015-07-15 14:45:36 -07:00
|
|
|
}
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
String::Utf8Value arg(info[0]->ToString());
|
2015-07-15 14:45:36 -07:00
|
|
|
std::string argStr = std::string(*arg);
|
|
|
|
const uint256 txid = uint256S(argStr);
|
2015-09-24 10:26:37 -07:00
|
|
|
int outputIndex = info[1]->IntegerValue();
|
2015-07-15 14:45:36 -07:00
|
|
|
|
2015-07-15 15:52:36 -07:00
|
|
|
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);
|
2015-07-15 15:52:36 -07:00
|
|
|
if (coins && coins->IsAvailable(outputIndex)) {
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(New<Boolean>(false));
|
2015-07-15 14:45:36 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(New<Boolean>(true));
|
2015-07-15 14:45:36 -07:00
|
|
|
};
|
|
|
|
|
2015-07-17 10:29:14 -07:00
|
|
|
/**
|
2015-07-22 12:34:15 -07:00
|
|
|
* GetBlockIndex()
|
2015-09-21 10:50:09 -07:00
|
|
|
* bitcoind.getBlockIndex()
|
2015-07-22 12:34:15 -07:00
|
|
|
* 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
|
2015-07-17 10:29:14 -07:00
|
|
|
*/
|
2015-07-22 12:34:15 -07:00
|
|
|
NAN_METHOD(GetBlockIndex) {
|
2015-07-17 10:29:14 -07:00
|
|
|
Isolate* isolate = Isolate::GetCurrent();
|
|
|
|
HandleScope scope(isolate);
|
|
|
|
|
|
|
|
CBlockIndex* blockIndex;
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
if (info[0]->IsNumber()) {
|
|
|
|
int64_t height = info[0]->IntegerValue();
|
2015-08-28 09:57:01 -07:00
|
|
|
blockIndex = chainActive[height];
|
2015-07-22 12:34:15 -07:00
|
|
|
|
2015-08-28 09:57:01 -07:00
|
|
|
if (blockIndex == NULL) {
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Null());
|
2015-09-30 21:21:14 -07:00
|
|
|
return;
|
2015-08-28 09:57:01 -07:00
|
|
|
}
|
2015-07-22 12:34:15 -07:00
|
|
|
|
2015-08-28 09:57:01 -07:00
|
|
|
} else {
|
2015-09-24 10:26:37 -07:00
|
|
|
String::Utf8Value hash_(info[0]->ToString());
|
2015-08-28 09:57:01 -07:00
|
|
|
std::string hashStr = std::string(*hash_);
|
|
|
|
uint256 hash = uint256S(hashStr);
|
|
|
|
if (mapBlockIndex.count(hash) == 0) {
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Null());
|
2015-08-28 09:57:01 -07:00
|
|
|
} else {
|
|
|
|
blockIndex = mapBlockIndex[hash];
|
|
|
|
}
|
2015-07-17 10:29:14 -07:00
|
|
|
}
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Object> obj = New<Object>();
|
2015-09-16 06:23:13 -07:00
|
|
|
|
2015-08-28 09:57:01 -07:00
|
|
|
arith_uint256 cw = blockIndex->nChainWork;
|
|
|
|
CBlockIndex* prevBlockIndex = blockIndex->pprev;
|
2015-09-16 06:23:13 -07:00
|
|
|
if (&prevBlockIndex->phashBlock != 0) {
|
|
|
|
const uint256* prevHash = prevBlockIndex->phashBlock;
|
2015-09-24 10:26:37 -07:00
|
|
|
Set(obj, New("prevHash").ToLocalChecked(), New(prevHash->GetHex()).ToLocalChecked());
|
2015-09-16 06:23:13 -07:00
|
|
|
} else {
|
2015-09-24 10:26:37 -07:00
|
|
|
Set(obj, New("prevHash").ToLocalChecked(), Null());
|
2015-09-16 06:23:13 -07:00
|
|
|
}
|
2015-08-28 09:57:01 -07:00
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Set(obj, New("hash").ToLocalChecked(), New(blockIndex->phashBlock->GetHex()).ToLocalChecked());
|
|
|
|
Set(obj, New("chainWork").ToLocalChecked(), New(cw.GetHex()).ToLocalChecked());
|
2015-09-16 06:23:13 -07:00
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Set(obj, New("height").ToLocalChecked(), New<Number>(blockIndex->nHeight));
|
2015-08-28 09:57:01 -07:00
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(obj);
|
2015-07-17 10:29:14 -07:00
|
|
|
};
|
|
|
|
|
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;
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
String::Utf8Value hash_(info[0]->ToString());
|
2015-09-08 11:04:14 -07:00
|
|
|
std::string hashStr = std::string(*hash_);
|
|
|
|
uint256 hash = uint256S(hashStr);
|
|
|
|
if (mapBlockIndex.count(hash) == 0) {
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Null());
|
2015-09-08 11:04:14 -07:00
|
|
|
} else {
|
|
|
|
blockIndex = mapBlockIndex[hash];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chainActive.Contains(blockIndex)) {
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(New<Boolean>(true));
|
2015-09-08 11:04:14 -07:00
|
|
|
} else {
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(New<Boolean>(false));
|
2015-09-08 11:04:14 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-17 13:26:27 -07:00
|
|
|
/**
|
|
|
|
* GetInfo()
|
2015-09-21 10:50:09 -07:00
|
|
|
* bitcoind.getInfo()
|
2014-10-17 13:26:27 -07:00
|
|
|
* Get miscellaneous information
|
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(GetInfo) {
|
2015-09-24 10:26:37 -07:00
|
|
|
if (info.Length() > 0) {
|
|
|
|
return ThrowError(
|
2015-09-21 10:50:09 -07:00
|
|
|
"Usage: bitcoind.getInfo()");
|
2014-10-17 13:26:27 -07:00
|
|
|
}
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Local<Object> obj = New<Object>();
|
2014-10-17 13:26:27 -07:00
|
|
|
|
|
|
|
proxyType proxy;
|
|
|
|
GetProxy(NET_IPV4, proxy);
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
Set(obj, New("version").ToLocalChecked(), New<Number>(CLIENT_VERSION));
|
|
|
|
Set(obj, New("protocolversion").ToLocalChecked(), New<Number>(PROTOCOL_VERSION));
|
|
|
|
Set(obj, New("blocks").ToLocalChecked(), New<Number>((int)chainActive.Height())->ToInt32());
|
|
|
|
Set(obj, New("timeoffset").ToLocalChecked(), New<Number>(GetTimeOffset()));
|
|
|
|
Set(obj, New("connections").ToLocalChecked(), New<Number>((int)vNodes.size())->ToInt32());
|
|
|
|
Set(obj, New("difficulty").ToLocalChecked(), New<Number>((double)GetDifficulty()));
|
|
|
|
Set(obj, New("testnet").ToLocalChecked(), New<Boolean>(Params().NetworkIDString() == "test"));
|
|
|
|
Set(obj, New("relayfee").ToLocalChecked(), New<Number>(::minRelayTxFee.GetFeePerK())); // double
|
|
|
|
Set(obj, New("errors").ToLocalChecked(), New(GetWarnings("statusbar")).ToLocalChecked());
|
|
|
|
|
|
|
|
info.GetReturnValue().Set(obj);
|
2014-10-17 13:26:27 -07:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
int nBlocks = info[0]->NumberValue();
|
2015-07-28 13:03:55 -07:00
|
|
|
if (nBlocks < 1) {
|
|
|
|
nBlocks = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
CFeeRate feeRate = mempool.estimateFee(nBlocks);
|
|
|
|
|
|
|
|
if (feeRate == CFeeRate(0)) {
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(New<Number>(-1.0));
|
2015-07-28 13:03:55 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CAmount nFee = feeRate.GetFeePerK();
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(New<Number>(nFee));
|
2015-07-28 13:03:55 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-07-20 11:43:21 -07:00
|
|
|
/**
|
|
|
|
* Send Transaction
|
2015-09-21 10:50:09 -07:00
|
|
|
* bitcoind.sendTransaction()
|
2015-07-20 11:43:21 -07:00
|
|
|
* 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
|
2015-09-24 10:26:37 -07:00
|
|
|
v8::String::Utf8Value param1(info[0]->ToString());
|
2015-07-20 11:43:21 -07:00
|
|
|
std::string *input = new std::string(*param1);
|
|
|
|
CTransaction tx;
|
|
|
|
if (!DecodeHexTx(tx, *input)) {
|
2015-09-24 10:26:37 -07:00
|
|
|
return ThrowError("TX decode failed");
|
2015-07-20 11:43:21 -07:00
|
|
|
}
|
|
|
|
uint256 hashTx = tx.GetHash();
|
|
|
|
|
|
|
|
// Skip absurdly high fee check
|
|
|
|
bool allowAbsurdFees = false;
|
2015-09-24 10:26:37 -07:00
|
|
|
if (info.Length() > 1) {
|
|
|
|
allowAbsurdFees = info[1]->BooleanValue();
|
2015-07-20 11:43:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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()) {
|
2015-09-24 10:26:37 -07:00
|
|
|
return ThrowError((boost::lexical_cast<std::string>(state.GetRejectCode()) + ": " + state.GetRejectReason()).c_str());
|
2015-07-20 11:43:21 -07:00
|
|
|
} else {
|
|
|
|
if (fMissingInputs) {
|
2015-09-24 10:26:37 -07:00
|
|
|
return ThrowError("Missing inputs");
|
2015-07-20 11:43:21 -07:00
|
|
|
}
|
2015-09-24 10:26:37 -07:00
|
|
|
return ThrowError(state.GetRejectReason().c_str());
|
2015-07-20 11:43:21 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (fHaveChain) {
|
2015-09-24 10:26:37 -07:00
|
|
|
return ThrowError("transaction already in block chain");
|
2015-07-20 11:43:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Relay the transaction connect peers
|
|
|
|
RelayTransaction(tx);
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(Local<Value>::New(isolate, New(hashTx.GetHex()).ToLocalChecked()));
|
2015-07-20 11:43:21 -07:00
|
|
|
}
|
|
|
|
|
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) {
|
2015-09-24 10:26:37 -07:00
|
|
|
Isolate* isolate = info.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-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(transactions);
|
2015-07-17 12:55:36 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2015-07-17 14:03:28 -07:00
|
|
|
/**
|
|
|
|
* AddMempoolUncheckedTransaction
|
|
|
|
*/
|
|
|
|
NAN_METHOD(AddMempoolUncheckedTransaction) {
|
2015-09-24 10:26:37 -07:00
|
|
|
v8::String::Utf8Value param1(info[0]->ToString());
|
2015-07-17 18:24:59 -07:00
|
|
|
std::string *input = new std::string(*param1);
|
2015-07-17 14:03:28 -07:00
|
|
|
|
2015-07-17 18:24:59 -07:00
|
|
|
CTransaction tx;
|
|
|
|
if (!DecodeHexTx(tx, *input)) {
|
2015-09-24 10:26:37 -07:00
|
|
|
return ThrowError("could not decode tx");
|
2015-07-17 18:24:59 -07:00
|
|
|
}
|
|
|
|
bool added = mempool.addUnchecked(tx.GetHash(), CTxMemPoolEntry(tx, 0, 0, 0.0, 1));
|
2015-09-24 10:26:37 -07:00
|
|
|
info.GetReturnValue().Set(New<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()
|
2015-09-21 10:50:09 -07:00
|
|
|
* Initialize the singleton object known as bitcoind.
|
2014-08-12 12:03:04 -07:00
|
|
|
*/
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
NAN_MODULE_INIT(init) {
|
|
|
|
Set(target, New("start").ToLocalChecked(), GetFunction(New<FunctionTemplate>(StartBitcoind)).ToLocalChecked());
|
|
|
|
Set(target, New("onBlocksReady").ToLocalChecked(), GetFunction(New<FunctionTemplate>(OnBlocksReady)).ToLocalChecked());
|
|
|
|
Set(target, New("onTipUpdate").ToLocalChecked(), GetFunction(New<FunctionTemplate>(OnTipUpdate)).ToLocalChecked());
|
|
|
|
Set(target, New("stop").ToLocalChecked(), GetFunction(New<FunctionTemplate>(StopBitcoind)).ToLocalChecked());
|
|
|
|
Set(target, New("getBlock").ToLocalChecked(), GetFunction(New<FunctionTemplate>(GetBlock)).ToLocalChecked());
|
|
|
|
Set(target, New("getTransaction").ToLocalChecked(), GetFunction(New<FunctionTemplate>(GetTransaction)).ToLocalChecked());
|
|
|
|
Set(target, New("getTransactionWithBlockInfo").ToLocalChecked(), GetFunction(New<FunctionTemplate>(GetTransactionWithBlockInfo)).ToLocalChecked());
|
|
|
|
Set(target, New("getInfo").ToLocalChecked(), GetFunction(New<FunctionTemplate>(GetInfo)).ToLocalChecked());
|
|
|
|
Set(target, New("isSpent").ToLocalChecked(), GetFunction(New<FunctionTemplate>(IsSpent)).ToLocalChecked());
|
|
|
|
Set(target, New("getBlockIndex").ToLocalChecked(), GetFunction(New<FunctionTemplate>(GetBlockIndex)).ToLocalChecked());
|
|
|
|
Set(target, New("isMainChain").ToLocalChecked(), GetFunction(New<FunctionTemplate>(IsMainChain)).ToLocalChecked());
|
|
|
|
Set(target, New("getMempoolTransactions").ToLocalChecked(), GetFunction(New<FunctionTemplate>(GetMempoolTransactions)).ToLocalChecked());
|
|
|
|
Set(target, New("addMempoolUncheckedTransaction").ToLocalChecked(), GetFunction(New<FunctionTemplate>(AddMempoolUncheckedTransaction)).ToLocalChecked());
|
|
|
|
Set(target, New("sendTransaction").ToLocalChecked(), GetFunction(New<FunctionTemplate>(SendTransaction)).ToLocalChecked());
|
|
|
|
Set(target, New("estimateFee").ToLocalChecked(), GetFunction(New<FunctionTemplate>(EstimateFee)).ToLocalChecked());
|
|
|
|
Set(target, New("startTxMon").ToLocalChecked(), GetFunction(New<FunctionTemplate>(StartTxMon)).ToLocalChecked());
|
|
|
|
Set(target, New("syncPercentage").ToLocalChecked(), GetFunction(New<FunctionTemplate>(SyncPercentage)).ToLocalChecked());
|
|
|
|
Set(target, New("isSynced").ToLocalChecked(), GetFunction(New<FunctionTemplate>(IsSynced)).ToLocalChecked());
|
|
|
|
Set(target, New("getBestBlockHash").ToLocalChecked(), GetFunction(New<FunctionTemplate>(GetBestBlockHash)).ToLocalChecked());
|
|
|
|
Set(target, New("getNextBlockHash").ToLocalChecked(), GetFunction(New<FunctionTemplate>(GetNextBlockHash)).ToLocalChecked());
|
2014-08-12 12:03:04 -07:00
|
|
|
}
|
|
|
|
|
2015-09-24 10:26:37 -07:00
|
|
|
NODE_MODULE(libbitcoind, init);
|