2014-08-12 12:03:04 -07:00
|
|
|
/**
|
|
|
|
* bitcoind.js
|
|
|
|
* Copyright (c) 2014, BitPay (MIT License)
|
|
|
|
*
|
|
|
|
* bitcoindjs.cc:
|
|
|
|
* A bitcoind node.js binding.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "nan.h"
|
|
|
|
|
2014-08-29 16:20:38 -07:00
|
|
|
#include "bitcoindjs.h"
|
|
|
|
|
2014-08-20 17:00:09 -07:00
|
|
|
/**
|
|
|
|
* Bitcoin headers
|
|
|
|
*/
|
|
|
|
|
2014-09-17 12:14:20 -07:00
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
|
|
#include "bitcoin-config.h"
|
|
|
|
#endif
|
|
|
|
|
2014-08-19 17:11:35 -07:00
|
|
|
#include "core.h"
|
|
|
|
#include "addrman.h"
|
|
|
|
#include "checkpoints.h"
|
|
|
|
#include "crypter.h"
|
|
|
|
#include "main.h"
|
|
|
|
// #include "random.h"
|
|
|
|
// #include "timedata.h"
|
2014-09-17 12:14:20 -07:00
|
|
|
|
|
|
|
#ifdef ENABLE_WALLET
|
|
|
|
#include "db.h"
|
|
|
|
#include "wallet.h"
|
2014-08-19 17:11:35 -07:00
|
|
|
#include "walletdb.h"
|
2014-09-17 12:14:20 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// #include "walletdb.h"
|
2014-08-19 17:11:35 -07:00
|
|
|
#include "alert.h"
|
|
|
|
#include "checkqueue.h"
|
2014-09-17 12:14:20 -07:00
|
|
|
// #include "db.h"
|
2014-08-19 17:11:35 -07:00
|
|
|
#include "miner.h"
|
|
|
|
#include "rpcclient.h"
|
|
|
|
#include "tinyformat.h"
|
2014-09-17 12:14:20 -07:00
|
|
|
// #include "wallet.h"
|
2014-08-19 17:11:35 -07:00
|
|
|
#include "allocators.h"
|
|
|
|
#include "clientversion.h"
|
|
|
|
#include "hash.h"
|
|
|
|
#include "mruset.h"
|
|
|
|
#include "rpcprotocol.h"
|
|
|
|
#include "txdb.h"
|
|
|
|
#include "base58.h"
|
|
|
|
#include "coincontrol.h"
|
|
|
|
#include "init.h"
|
|
|
|
#include "netbase.h"
|
|
|
|
#include "rpcserver.h"
|
2014-09-30 13:35:21 -07:00
|
|
|
#include "rpcwallet.h"
|
2014-08-19 17:11:35 -07:00
|
|
|
#include "txmempool.h"
|
|
|
|
#include "bloom.h"
|
|
|
|
#include "coins.h"
|
|
|
|
#include "key.h"
|
|
|
|
#include "net.h"
|
|
|
|
#include "script.h"
|
|
|
|
#include "ui_interface.h"
|
|
|
|
// #include "chainparamsbase.h"
|
|
|
|
#include "compat.h"
|
|
|
|
#include "keystore.h"
|
|
|
|
#include "noui.h"
|
|
|
|
#include "serialize.h"
|
|
|
|
#include "uint256.h"
|
|
|
|
#include "chainparams.h"
|
|
|
|
#include "core.h"
|
|
|
|
#include "leveldbwrapper.h"
|
|
|
|
// #include "pow.h"
|
|
|
|
#include "sync.h"
|
|
|
|
#include "util.h"
|
|
|
|
// #include "chainparamsseeds.h"
|
|
|
|
// #include "core_io.h"
|
|
|
|
#include "limitedmap.h"
|
|
|
|
#include "protocol.h"
|
|
|
|
#include "threadsafety.h"
|
|
|
|
#include "version.h"
|
|
|
|
|
2014-08-20 17:00:09 -07:00
|
|
|
/**
|
|
|
|
* Bitcoin Globals
|
|
|
|
* Relevant:
|
|
|
|
* ~/bitcoin/src/init.cpp
|
|
|
|
* ~/bitcoin/src/bitcoind.cpp
|
|
|
|
* ~/bitcoin/src/main.h
|
|
|
|
*/
|
|
|
|
|
2014-09-17 12:14:20 -07:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <signal.h>
|
2014-09-30 15:53:13 -07:00
|
|
|
#include <stdio.h>
|
2014-09-17 12:14:20 -07:00
|
|
|
|
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
#include <boost/filesystem.hpp>
|
|
|
|
#include <boost/interprocess/sync/file_lock.hpp>
|
|
|
|
#include <openssl/crypto.h>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace boost;
|
|
|
|
|
2014-09-17 11:23:38 -07:00
|
|
|
extern void DetectShutdownThread(boost::thread_group*);
|
2014-09-26 15:43:46 -07:00
|
|
|
extern int nScriptCheckThreads;
|
|
|
|
extern bool fDaemon;
|
|
|
|
extern std::map<std::string, std::string> mapArgs;
|
|
|
|
#ifdef ENABLE_WALLET
|
|
|
|
extern std::string strWalletFile;
|
|
|
|
extern CWallet *pwalletMain;
|
|
|
|
#endif
|
2014-09-29 16:26:33 -07:00
|
|
|
extern int64_t nTransactionFee;
|
2014-10-01 12:52:06 -07:00
|
|
|
extern const std::string strMessageMagic;
|
2014-08-20 17:00:09 -07:00
|
|
|
|
2014-09-22 13:21:42 -07:00
|
|
|
/**
|
|
|
|
* Node and Templates
|
|
|
|
*/
|
|
|
|
|
2014-08-19 16:40:19 -07:00
|
|
|
#include <node.h>
|
|
|
|
#include <string>
|
|
|
|
|
2014-08-12 12:03:04 -07:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
using namespace node;
|
|
|
|
using namespace v8;
|
|
|
|
|
2014-09-29 13:38:27 -07:00
|
|
|
Handle<Object> bitcoindjs_obj;
|
|
|
|
|
2014-08-12 12:03:04 -07:00
|
|
|
NAN_METHOD(StartBitcoind);
|
2014-09-19 13:53:55 -07:00
|
|
|
NAN_METHOD(IsStopping);
|
|
|
|
NAN_METHOD(IsStopped);
|
|
|
|
NAN_METHOD(StopBitcoind);
|
|
|
|
NAN_METHOD(GetBlock);
|
2014-09-22 15:31:56 -07:00
|
|
|
NAN_METHOD(GetTx);
|
2014-09-22 17:58:59 -07:00
|
|
|
NAN_METHOD(PollBlocks);
|
2014-09-23 13:57:49 -07:00
|
|
|
NAN_METHOD(PollMempool);
|
2014-09-25 12:05:39 -07:00
|
|
|
NAN_METHOD(BroadcastTx);
|
2014-09-26 11:23:21 -07:00
|
|
|
NAN_METHOD(VerifyBlock);
|
2014-09-26 11:34:55 -07:00
|
|
|
NAN_METHOD(VerifyTransaction);
|
2014-09-29 14:28:31 -07:00
|
|
|
NAN_METHOD(FillTransaction);
|
2014-09-26 12:42:04 -07:00
|
|
|
|
2014-09-26 12:20:00 -07:00
|
|
|
NAN_METHOD(WalletNewAddress);
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletGetAccountAddress);
|
|
|
|
NAN_METHOD(WalletSetAccount);
|
|
|
|
NAN_METHOD(WalletGetAccount);
|
2014-09-29 11:59:57 -07:00
|
|
|
NAN_METHOD(WalletSendTo);
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletSignMessage);
|
|
|
|
NAN_METHOD(WalletVerifyMessage);
|
|
|
|
NAN_METHOD(WalletGetBalance);
|
2014-10-01 13:11:07 -07:00
|
|
|
NAN_METHOD(WalletCreateMultiSigAddress);
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletGetUnconfirmedBalance);
|
|
|
|
NAN_METHOD(WalletSendFrom);
|
|
|
|
NAN_METHOD(WalletListTransactions);
|
|
|
|
NAN_METHOD(WalletListAccounts);
|
|
|
|
NAN_METHOD(WalletGetTransaction);
|
|
|
|
NAN_METHOD(WalletBackup);
|
2014-09-26 12:42:04 -07:00
|
|
|
NAN_METHOD(WalletPassphrase);
|
|
|
|
NAN_METHOD(WalletPassphraseChange);
|
|
|
|
NAN_METHOD(WalletLock);
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletEncrypt);
|
|
|
|
NAN_METHOD(WalletSetTxFee);
|
2014-10-01 16:00:21 -07:00
|
|
|
NAN_METHOD(WalletImportKey);
|
2014-08-12 12:03:04 -07:00
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
static void
|
2014-09-02 19:00:31 -07:00
|
|
|
async_start_node_work(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
|
|
|
|
2014-09-11 17:18:36 -07:00
|
|
|
static void
|
|
|
|
async_stop_node_work(uv_work_t *req);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2014-09-22 17:58:59 -07:00
|
|
|
static void
|
|
|
|
async_poll_blocks(uv_work_t *req);
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_poll_blocks_after(uv_work_t *req);
|
|
|
|
|
2014-09-23 10:14:35 -07:00
|
|
|
static void
|
2014-09-23 13:57:49 -07:00
|
|
|
async_poll_mempool(uv_work_t *req);
|
2014-09-23 10:14:35 -07:00
|
|
|
|
|
|
|
static void
|
2014-09-23 13:57:49 -07:00
|
|
|
async_poll_mempool_after(uv_work_t *req);
|
|
|
|
|
2014-09-25 12:05:39 -07:00
|
|
|
static void
|
|
|
|
async_broadcast_tx(uv_work_t *req);
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_broadcast_tx_after(uv_work_t *req);
|
|
|
|
|
2014-09-29 11:59:57 -07:00
|
|
|
static void
|
|
|
|
async_wallet_sendto(uv_work_t *req);
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_wallet_sendto_after(uv_work_t *req);
|
|
|
|
|
2014-09-29 12:15:59 -07:00
|
|
|
static void
|
|
|
|
async_wallet_sendfrom(uv_work_t *req);
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_wallet_sendfrom_after(uv_work_t *req);
|
|
|
|
|
2014-10-01 16:00:21 -07:00
|
|
|
static void
|
|
|
|
async_import_key(uv_work_t *req);
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_import_key_after(uv_work_t *req);
|
|
|
|
|
2014-09-23 13:57:49 -07:00
|
|
|
static inline void
|
2014-10-02 13:24:18 -07:00
|
|
|
cblock_to_jsblock(const CBlock& cblock, const CBlockIndex* cblock_index, Local<Object> jsblock);
|
2014-09-29 13:38:27 -07:00
|
|
|
|
|
|
|
static inline void
|
2014-10-02 13:24:18 -07:00
|
|
|
ctx_to_jstx(const CTransaction& ctx, uint256 block_hash, Local<Object> jstx);
|
2014-09-29 13:38:27 -07:00
|
|
|
|
|
|
|
static inline void
|
2014-10-02 13:13:54 -07:00
|
|
|
jsblock_to_cblock(const Local<Object> jsblock, CBlock& cblock);
|
2014-09-23 13:57:49 -07:00
|
|
|
|
|
|
|
static inline void
|
2014-10-02 13:13:54 -07:00
|
|
|
jstx_to_ctx(const Local<Object> jstx, CTransaction& ctx);
|
2014-09-23 10:14:35 -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-09-22 13:21:42 -07:00
|
|
|
* Private Variables
|
2014-09-19 13:53:55 -07:00
|
|
|
*/
|
|
|
|
|
2014-09-22 13:21:42 -07:00
|
|
|
static volatile bool shutdownComplete = false;
|
2014-10-01 12:05:18 -07:00
|
|
|
static int block_poll_top_height = -1;
|
2014-09-19 13:53:55 -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 {
|
2014-09-29 12:18:29 -07:00
|
|
|
std::string err_msg;
|
|
|
|
std::string result;
|
2014-09-04 15:16:32 -07:00
|
|
|
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 {
|
|
|
|
std::string err_msg;
|
|
|
|
std::string hash;
|
|
|
|
CBlock result_block;
|
|
|
|
CBlockIndex* result_blockindex;
|
|
|
|
Persistent<Function> callback;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* async_tx_data
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct async_tx_data {
|
|
|
|
std::string err_msg;
|
|
|
|
std::string txHash;
|
|
|
|
std::string blockHash;
|
2014-10-02 15:14:23 -07:00
|
|
|
CTransaction result_tx;
|
2014-09-22 13:21:42 -07:00
|
|
|
Persistent<Function> callback;
|
|
|
|
};
|
|
|
|
|
2014-09-22 17:16:27 -07:00
|
|
|
/**
|
|
|
|
* async_poll_blocks_data
|
|
|
|
*/
|
|
|
|
|
2014-10-01 11:03:27 -07:00
|
|
|
typedef struct _poll_blocks_list {
|
2014-10-02 13:24:18 -07:00
|
|
|
CBlock cblock;
|
|
|
|
CBlockIndex *cblock_index;
|
2014-10-01 11:03:27 -07:00
|
|
|
struct _poll_blocks_list *next;
|
|
|
|
} poll_blocks_list;
|
|
|
|
|
2014-09-22 17:16:27 -07:00
|
|
|
struct async_poll_blocks_data {
|
|
|
|
std::string err_msg;
|
2014-10-01 11:03:27 -07:00
|
|
|
poll_blocks_list *head;
|
2014-09-22 17:16:27 -07:00
|
|
|
Persistent<Array> result_array;
|
|
|
|
Persistent<Function> callback;
|
|
|
|
};
|
|
|
|
|
2014-09-23 13:57:49 -07:00
|
|
|
/**
|
|
|
|
* async_poll_mempool_data
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct async_poll_mempool_data {
|
|
|
|
std::string err_msg;
|
|
|
|
Persistent<Array> result_array;
|
|
|
|
Persistent<Function> callback;
|
|
|
|
};
|
|
|
|
|
2014-09-25 12:05:39 -07:00
|
|
|
/**
|
2014-09-25 12:38:42 -07:00
|
|
|
* async_broadcast_tx_data
|
2014-09-25 12:05:39 -07:00
|
|
|
*/
|
|
|
|
|
2014-09-25 12:38:42 -07:00
|
|
|
struct async_broadcast_tx_data {
|
2014-09-25 12:05:39 -07:00
|
|
|
std::string err_msg;
|
2014-10-02 15:14:23 -07:00
|
|
|
std::string tx_hex;
|
2014-09-25 12:38:42 -07:00
|
|
|
std::string tx_hash;
|
2014-09-25 12:05:39 -07:00
|
|
|
bool override_fees;
|
2014-09-25 13:12:28 -07:00
|
|
|
bool own_only;
|
2014-09-25 12:05:39 -07:00
|
|
|
Persistent<Function> callback;
|
|
|
|
};
|
2014-09-23 13:57:49 -07:00
|
|
|
|
2014-09-29 11:59:57 -07:00
|
|
|
/**
|
|
|
|
* async_wallet_sendto_data
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct async_wallet_sendto_data {
|
|
|
|
std::string err_msg;
|
|
|
|
std::string tx_hash;
|
|
|
|
std::string address;
|
|
|
|
int64_t nAmount;
|
|
|
|
CWalletTx wtx;
|
|
|
|
Persistent<Function> callback;
|
|
|
|
};
|
|
|
|
|
2014-09-29 12:15:59 -07:00
|
|
|
/**
|
|
|
|
* async_wallet_sendfrom_data
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct async_wallet_sendfrom_data {
|
|
|
|
std::string err_msg;
|
|
|
|
std::string tx_hash;
|
|
|
|
std::string address;
|
|
|
|
int64_t nAmount;
|
|
|
|
int nMinDepth;
|
|
|
|
CWalletTx wtx;
|
|
|
|
Persistent<Function> callback;
|
|
|
|
};
|
|
|
|
|
2014-10-01 16:00:21 -07:00
|
|
|
/**
|
|
|
|
* async_import_key_data
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct async_import_key_data {
|
|
|
|
std::string err_msg;
|
|
|
|
bool fRescan;
|
|
|
|
Persistent<Function> callback;
|
|
|
|
};
|
|
|
|
|
2014-08-12 12:03:04 -07:00
|
|
|
/**
|
|
|
|
* StartBitcoind
|
|
|
|
* bitcoind.start(callback)
|
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(StartBitcoind) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsFunction()) {
|
|
|
|
return NanThrowError(
|
2014-09-22 12:57:25 -07:00
|
|
|
"Usage: bitcoind.start(callback)");
|
2014-08-12 12:03:04 -07:00
|
|
|
}
|
|
|
|
|
2014-09-02 19:00:31 -07:00
|
|
|
Local<Function> callback = Local<Function>::Cast(args[0]);
|
2014-08-29 13:53:08 -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
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
async_node_data *data = new async_node_data();
|
|
|
|
data->err_msg = std::string("");
|
|
|
|
data->result = std::string("");
|
|
|
|
data->callback = Persistent<Function>::New(callback);
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
uv_work_t *req = new uv_work_t();
|
|
|
|
req->data = data;
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
int status = uv_queue_work(uv_default_loop(),
|
|
|
|
req, async_start_node_work,
|
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
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
NanReturnValue(NanNew<Number>(-1));
|
2014-08-12 12:03:04 -07:00
|
|
|
}
|
|
|
|
|
2014-08-20 17:00:09 -07:00
|
|
|
/**
|
2014-09-02 19:00:31 -07:00
|
|
|
* async_start_node_work()
|
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-09-02 19:00:31 -07:00
|
|
|
async_start_node_work(uv_work_t *req) {
|
2014-09-29 12:18:29 -07:00
|
|
|
async_node_data *data = static_cast<async_node_data*>(req->data);
|
2014-09-05 15:07:38 -07:00
|
|
|
start_node();
|
2014-09-29 12:18:29 -07:00
|
|
|
data->result = std::string("start_node(): 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
|
2014-09-02 19:00:31 -07:00
|
|
|
async_start_node_after(uv_work_t *req) {
|
2014-08-19 16:40:19 -07:00
|
|
|
NanScope();
|
2014-09-29 12:18:29 -07:00
|
|
|
async_node_data *data = static_cast<async_node_data*>(req->data);
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
if (!data->err_msg.empty()) {
|
|
|
|
Local<Value> err = Exception::Error(String::New(data->err_msg.c_str()));
|
2014-08-19 16:40:19 -07:00
|
|
|
const unsigned argc = 1;
|
2014-09-02 19:00:31 -07:00
|
|
|
Local<Value> argv[argc] = { err };
|
2014-08-19 16:40:19 -07:00
|
|
|
TryCatch try_catch;
|
2014-09-29 12:18:29 -07:00
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
2014-08-19 16:40:19 -07:00
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const unsigned argc = 2;
|
2014-09-02 19:00:31 -07:00
|
|
|
Local<Value> argv[argc] = {
|
2014-08-19 16:40:19 -07:00
|
|
|
Local<Value>::New(Null()),
|
2014-09-29 12:18:29 -07:00
|
|
|
Local<Value>::New(String::New(data->result.c_str()))
|
2014-08-19 16:40:19 -07:00
|
|
|
};
|
|
|
|
TryCatch try_catch;
|
2014-09-29 12:18:29 -07:00
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
2014-08-19 16:40:19 -07:00
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
// data->callback.Dispose();
|
2014-08-19 16:40:19 -07:00
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
delete data;
|
2014-08-19 16:40:19 -07:00
|
|
|
delete req;
|
2014-08-12 12:03:04 -07:00
|
|
|
}
|
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
/**
|
|
|
|
* IsStopping()
|
|
|
|
* bitcoind.stopping()
|
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(IsStopping) {
|
|
|
|
NanScope();
|
|
|
|
NanReturnValue(NanNew<Boolean>(ShutdownRequested()));
|
|
|
|
}
|
|
|
|
|
2014-09-17 12:52:35 -07:00
|
|
|
/**
|
|
|
|
* IsStopped()
|
|
|
|
* bitcoind.stopped()
|
|
|
|
*/
|
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
NAN_METHOD(IsStopped) {
|
2014-09-17 12:52:35 -07:00
|
|
|
NanScope();
|
2014-09-17 14:08:26 -07:00
|
|
|
NanReturnValue(NanNew<Boolean>(shutdownComplete));
|
2014-09-17 12:52:35 -07:00
|
|
|
}
|
|
|
|
|
2014-08-20 17:00:09 -07:00
|
|
|
/**
|
|
|
|
* start_node(void)
|
2014-09-17 14:08:26 -07:00
|
|
|
* start_node_thread(void)
|
2014-08-20 17:00:09 -07:00
|
|
|
* A reimplementation of AppInit2 minus
|
|
|
|
* the logging and argument parsing.
|
|
|
|
*/
|
2014-09-02 19:00:31 -07:00
|
|
|
|
2014-08-20 16:47:18 -07:00
|
|
|
static int
|
|
|
|
start_node(void) {
|
2014-09-17 12:14:20 -07:00
|
|
|
noui_connect();
|
|
|
|
|
2014-09-17 14:08:26 -07:00
|
|
|
(boost::thread *)new boost::thread(boost::bind(&start_node_thread));
|
2014-09-17 12:14:20 -07:00
|
|
|
|
2014-09-29 11:40:00 -07:00
|
|
|
// wait for wallet to be instantiated
|
|
|
|
// this also avoids a race condition with signals not being set up
|
|
|
|
while (!pwalletMain) {
|
|
|
|
useconds_t usec = 100 * 1000;
|
|
|
|
usleep(usec);
|
|
|
|
}
|
|
|
|
|
|
|
|
// drop the bitcoind signal handlers - we want our own
|
2014-09-17 14:08:26 -07:00
|
|
|
signal(SIGINT, SIG_DFL);
|
|
|
|
signal(SIGHUP, SIG_DFL);
|
2014-09-30 15:14:53 -07:00
|
|
|
signal(SIGQUIT, SIG_DFL);
|
2014-09-17 12:14:20 -07:00
|
|
|
|
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) {
|
2014-09-17 12:52:35 -07:00
|
|
|
boost::thread_group threadGroup;
|
2014-09-19 15:39:05 -07:00
|
|
|
boost::thread *detectShutdownThread = NULL;
|
2014-09-17 12:14:20 -07:00
|
|
|
|
2014-09-17 12:52:35 -07:00
|
|
|
const int argc = 0;
|
2014-10-02 16:23:26 -07:00
|
|
|
const char *argv[argc + 1] = { NULL };
|
2014-09-17 12:52:35 -07:00
|
|
|
ParseParameters(argc, argv);
|
|
|
|
ReadConfigFile(mapArgs, mapMultiArgs);
|
|
|
|
if (!SelectParamsFromCommandLine()) {
|
2014-09-17 14:08:26 -07:00
|
|
|
return;
|
2014-09-17 12:14:20 -07:00
|
|
|
}
|
2014-09-17 12:52:35 -07:00
|
|
|
// CreatePidFile(GetPidFile(), getpid());
|
|
|
|
detectShutdownThread = new boost::thread(
|
|
|
|
boost::bind(&DetectShutdownThread, &threadGroup));
|
2014-09-17 12:14:20 -07:00
|
|
|
|
2014-09-17 12:52:35 -07:00
|
|
|
int fRet = AppInit2(threadGroup);
|
2014-09-17 12:14:20 -07:00
|
|
|
|
|
|
|
if (!fRet) {
|
|
|
|
if (detectShutdownThread)
|
|
|
|
detectShutdownThread->interrupt();
|
|
|
|
threadGroup.interrupt_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (detectShutdownThread) {
|
|
|
|
detectShutdownThread->join();
|
|
|
|
delete detectShutdownThread;
|
|
|
|
detectShutdownThread = NULL;
|
|
|
|
}
|
|
|
|
Shutdown();
|
2014-09-17 14:08:26 -07:00
|
|
|
shutdownComplete = true;
|
2014-08-20 16:47:18 -07:00
|
|
|
}
|
|
|
|
|
2014-09-11 17:18:36 -07:00
|
|
|
/**
|
|
|
|
* StopBitcoind
|
|
|
|
* bitcoind.stop(callback)
|
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(StopBitcoind) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
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.
|
|
|
|
//
|
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
async_node_data *data = new async_node_data();
|
|
|
|
data->err_msg = std::string("");
|
|
|
|
data->result = std::string("");
|
|
|
|
data->callback = Persistent<Function>::New(callback);
|
2014-09-11 17:18:36 -07:00
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
uv_work_t *req = new uv_work_t();
|
|
|
|
req->data = data;
|
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, async_stop_node_work,
|
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);
|
2014-09-11 17:18:36 -07:00
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* async_stop_node_work()
|
|
|
|
* Call StartShutdown() to join the boost threads, which will call Shutdown().
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_stop_node_work(uv_work_t *req) {
|
2014-09-29 12:18:29 -07:00
|
|
|
async_node_data *data = static_cast<async_node_data*>(req->data);
|
2014-09-11 17:18:36 -07:00
|
|
|
StartShutdown();
|
2014-09-29 12:18:29 -07:00
|
|
|
data->result = std::string("stop_node(): 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 *req) {
|
|
|
|
NanScope();
|
2014-09-29 12:18:29 -07:00
|
|
|
async_node_data* data = static_cast<async_node_data*>(req->data);
|
2014-09-11 17:18:36 -07:00
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
if (!data->err_msg.empty()) {
|
|
|
|
Local<Value> err = Exception::Error(String::New(data->err_msg.c_str()));
|
2014-09-11 17:18:36 -07:00
|
|
|
const unsigned argc = 1;
|
|
|
|
Local<Value> argv[argc] = { err };
|
|
|
|
TryCatch try_catch;
|
2014-09-29 12:18:29 -07:00
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
2014-09-11 17:18:36 -07:00
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const unsigned argc = 2;
|
|
|
|
Local<Value> argv[argc] = {
|
|
|
|
Local<Value>::New(Null()),
|
2014-09-29 12:18:29 -07:00
|
|
|
Local<Value>::New(String::New(data->result.c_str()))
|
2014-09-11 17:18:36 -07:00
|
|
|
};
|
|
|
|
TryCatch try_catch;
|
2014-09-29 12:18:29 -07:00
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
2014-09-11 17:18:36 -07:00
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
data->callback.Dispose();
|
2014-09-11 17:18:36 -07:00
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
delete data;
|
2014-09-11 17:18:36 -07:00
|
|
|
delete req;
|
|
|
|
}
|
|
|
|
|
2014-09-19 13:53:55 -07:00
|
|
|
/**
|
2014-09-25 13:54:20 -07:00
|
|
|
* GetBlock
|
|
|
|
* bitcoind.getBlock(blockHash, callback)
|
2014-09-19 13:53:55 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(GetBlock) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 2
|
|
|
|
|| !args[0]->IsString()
|
|
|
|
|| !args[1]->IsFunction()) {
|
|
|
|
return NanThrowError(
|
2014-09-22 12:57:25 -07:00
|
|
|
"Usage: bitcoindjs.getBlock(blockHash, callback)");
|
2014-09-19 13:53:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
String::Utf8Value hash(args[0]->ToString());
|
|
|
|
Local<Function> callback = Local<Function>::Cast(args[1]);
|
|
|
|
|
|
|
|
std::string hashp = std::string(*hash);
|
|
|
|
|
|
|
|
async_block_data *data = new async_block_data();
|
|
|
|
data->err_msg = std::string("");
|
|
|
|
data->hash = hashp;
|
|
|
|
data->callback = Persistent<Function>::New(callback);
|
|
|
|
|
|
|
|
uv_work_t *req = new uv_work_t();
|
|
|
|
req->data = data;
|
|
|
|
|
|
|
|
int status = uv_queue_work(uv_default_loop(),
|
|
|
|
req, async_get_block,
|
|
|
|
(uv_after_work_cb)async_get_block_after);
|
|
|
|
|
|
|
|
assert(status == 0);
|
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_get_block(uv_work_t *req) {
|
|
|
|
async_block_data* data = static_cast<async_block_data*>(req->data);
|
|
|
|
std::string strHash = data->hash;
|
|
|
|
if (strHash[1] != 'x') {
|
|
|
|
strHash = "0x" + strHash;
|
|
|
|
}
|
|
|
|
uint256 hash(strHash);
|
2014-10-02 13:24:18 -07:00
|
|
|
CBlock cblock;
|
2014-09-19 13:53:55 -07:00
|
|
|
CBlockIndex* pblockindex = mapBlockIndex[hash];
|
2014-10-02 13:24:18 -07:00
|
|
|
if (ReadBlockFromDisk(cblock, pblockindex)) {
|
|
|
|
data->result_block = cblock;
|
2014-09-19 15:39:05 -07:00
|
|
|
data->result_blockindex = pblockindex;
|
2014-09-19 13:53:55 -07:00
|
|
|
} else {
|
|
|
|
data->err_msg = std::string("get_block(): failed.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_get_block_after(uv_work_t *req) {
|
|
|
|
NanScope();
|
|
|
|
async_block_data* data = static_cast<async_block_data*>(req->data);
|
|
|
|
|
|
|
|
if (!data->err_msg.empty()) {
|
|
|
|
Local<Value> err = Exception::Error(String::New(data->err_msg.c_str()));
|
|
|
|
const unsigned argc = 1;
|
|
|
|
Local<Value> argv[argc] = { err };
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
} else {
|
2014-10-02 13:24:18 -07:00
|
|
|
const CBlock& cblock = data->result_block;
|
|
|
|
const CBlockIndex* cblock_index = data->result_blockindex;
|
2014-09-19 15:39:05 -07:00
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
Local<Object> jsblock = NanNew<Object>();
|
|
|
|
cblock_to_jsblock(cblock, cblock_index, jsblock);
|
2014-09-19 15:39:05 -07:00
|
|
|
|
2014-09-19 13:53:55 -07:00
|
|
|
const unsigned argc = 2;
|
|
|
|
Local<Value> argv[argc] = {
|
|
|
|
Local<Value>::New(Null()),
|
2014-10-02 13:24:18 -07:00
|
|
|
Local<Value>::New(jsblock)
|
2014-09-19 13:53:55 -07:00
|
|
|
};
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data->callback.Dispose();
|
|
|
|
|
|
|
|
delete data;
|
|
|
|
delete req;
|
|
|
|
}
|
|
|
|
|
2014-09-22 10:19:37 -07:00
|
|
|
/**
|
2014-09-25 13:54:20 -07:00
|
|
|
* GetTx
|
|
|
|
* bitcoind.getTx(txHash, [blockHash], callback)
|
2014-09-22 10:19:37 -07:00
|
|
|
*/
|
|
|
|
|
2014-09-22 13:21:42 -07:00
|
|
|
NAN_METHOD(GetTx) {
|
|
|
|
NanScope();
|
|
|
|
|
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]->IsString()
|
|
|
|
|| !args[2]->IsFunction()) {
|
|
|
|
return NanThrowError(
|
|
|
|
"Usage: bitcoindjs.getTx(txHash, [blockHash], callback)");
|
|
|
|
}
|
|
|
|
|
|
|
|
String::Utf8Value txHash_(args[0]->ToString());
|
|
|
|
String::Utf8Value blockHash_(args[1]->ToString());
|
|
|
|
Local<Function> callback = Local<Function>::Cast(args[2]);
|
|
|
|
|
|
|
|
Persistent<Function> cb;
|
|
|
|
cb = Persistent<Function>::New(callback);
|
|
|
|
|
|
|
|
std::string txHash = std::string(*txHash_);
|
|
|
|
std::string blockHash = std::string(*blockHash_);
|
|
|
|
|
|
|
|
if (blockHash.empty()) {
|
|
|
|
blockHash = std::string("0x0000000000000000000000000000000000000000000000000000000000000000");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (txHash[1] != 'x') {
|
|
|
|
txHash = "0x" + txHash;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blockHash[1] != 'x') {
|
|
|
|
blockHash = "0x" + blockHash;
|
|
|
|
}
|
|
|
|
|
|
|
|
async_tx_data *data = new async_tx_data();
|
|
|
|
data->err_msg = std::string("");
|
|
|
|
data->txHash = txHash;
|
|
|
|
data->blockHash = blockHash;
|
|
|
|
data->callback = Persistent<Function>::New(callback);
|
|
|
|
|
|
|
|
uv_work_t *req = new uv_work_t();
|
|
|
|
req->data = data;
|
|
|
|
|
|
|
|
int status = uv_queue_work(uv_default_loop(),
|
|
|
|
req, async_get_tx,
|
|
|
|
(uv_after_work_cb)async_get_tx_after);
|
|
|
|
|
|
|
|
assert(status == 0);
|
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_get_tx(uv_work_t *req) {
|
|
|
|
async_tx_data* data = static_cast<async_tx_data*>(req->data);
|
|
|
|
|
|
|
|
uint256 hash(data->txHash);
|
2014-10-02 13:24:18 -07:00
|
|
|
uint256 block_hash(data->blockHash);
|
|
|
|
CTransaction ctx;
|
2014-09-22 13:21:42 -07:00
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
if (GetTransaction(hash, ctx, block_hash, true)) {
|
2014-10-02 15:14:23 -07:00
|
|
|
data->result_tx = ctx;
|
2014-09-22 13:21:42 -07:00
|
|
|
} else {
|
2014-09-26 10:16:10 -07:00
|
|
|
data->err_msg = std::string("get_tx(): failed.");
|
2014-09-22 13:21:42 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_get_tx_after(uv_work_t *req) {
|
|
|
|
NanScope();
|
|
|
|
async_tx_data* data = static_cast<async_tx_data*>(req->data);
|
|
|
|
|
|
|
|
std::string txHash = data->txHash;
|
|
|
|
std::string blockHash = data->blockHash;
|
2014-10-02 15:14:23 -07:00
|
|
|
CTransaction ctx = data->result_tx;
|
2014-09-22 13:21:42 -07:00
|
|
|
|
|
|
|
uint256 hash(txHash);
|
2014-10-02 13:24:18 -07:00
|
|
|
uint256 block_hash(blockHash);
|
2014-09-22 13:21:42 -07:00
|
|
|
|
|
|
|
if (!data->err_msg.empty()) {
|
|
|
|
Local<Value> err = Exception::Error(String::New(data->err_msg.c_str()));
|
|
|
|
const unsigned argc = 1;
|
|
|
|
Local<Value> argv[argc] = { err };
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
} else {
|
2014-10-02 15:14:23 -07:00
|
|
|
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
ssTx << ctx;
|
|
|
|
std::string strHex = HexStr(ssTx.begin(), ssTx.end());
|
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
Local<Object> jstx = NanNew<Object>();
|
2014-10-02 15:14:23 -07:00
|
|
|
jstx->Set(NanNew<String>("hex"), NanNew<String>(strHex));
|
2014-10-02 13:24:18 -07:00
|
|
|
ctx_to_jstx(ctx, block_hash, jstx);
|
2014-09-22 13:21:42 -07:00
|
|
|
|
|
|
|
const unsigned argc = 2;
|
|
|
|
Local<Value> argv[argc] = {
|
|
|
|
Local<Value>::New(Null()),
|
2014-10-02 13:24:18 -07:00
|
|
|
Local<Value>::New(jstx)
|
2014-09-22 13:21:42 -07:00
|
|
|
};
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data->callback.Dispose();
|
|
|
|
|
|
|
|
delete data;
|
|
|
|
delete req;
|
|
|
|
}
|
2014-09-22 10:19:37 -07:00
|
|
|
|
2014-09-22 16:36:36 -07:00
|
|
|
/**
|
2014-09-25 13:54:20 -07:00
|
|
|
* PollBlocks
|
2014-09-22 17:58:59 -07:00
|
|
|
* bitcoind.pollBlocks(callback)
|
2014-09-22 16:36:36 -07:00
|
|
|
*/
|
|
|
|
|
2014-09-22 17:16:27 -07:00
|
|
|
NAN_METHOD(PollBlocks) {
|
2014-09-22 16:36:36 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsFunction()) {
|
|
|
|
return NanThrowError(
|
2014-09-22 17:16:27 -07:00
|
|
|
"Usage: bitcoindjs.pollBlocks(callback)");
|
2014-09-22 16:36:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Local<Function> callback = Local<Function>::Cast(args[0]);
|
|
|
|
|
2014-09-22 17:16:27 -07:00
|
|
|
async_poll_blocks_data *data = new async_poll_blocks_data();
|
|
|
|
data->err_msg = std::string("");
|
|
|
|
data->callback = Persistent<Function>::New(callback);
|
|
|
|
|
|
|
|
uv_work_t *req = new uv_work_t();
|
|
|
|
req->data = data;
|
|
|
|
|
|
|
|
int status = uv_queue_work(uv_default_loop(),
|
|
|
|
req, async_poll_blocks,
|
|
|
|
(uv_after_work_cb)async_poll_blocks_after);
|
|
|
|
|
|
|
|
assert(status == 0);
|
2014-09-22 16:36:36 -07:00
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
2014-09-22 16:47:45 -07:00
|
|
|
static void
|
2014-09-22 17:16:27 -07:00
|
|
|
async_poll_blocks(uv_work_t *req) {
|
|
|
|
async_poll_blocks_data* data = static_cast<async_poll_blocks_data*>(req->data);
|
|
|
|
|
2014-10-01 11:20:53 -07:00
|
|
|
int poll_saved_height = block_poll_top_height;
|
2014-09-22 17:58:59 -07:00
|
|
|
|
2014-10-02 13:32:21 -07:00
|
|
|
// Poll, wait until we actually have a blockchain download.
|
|
|
|
// Once we've noticed the height changed, assume we gained a few blocks.
|
2014-09-22 18:34:38 -07:00
|
|
|
while (chainActive.Tip()) {
|
|
|
|
int cur_height = chainActive.Height();
|
2014-10-01 11:20:53 -07:00
|
|
|
if (cur_height != block_poll_top_height) {
|
|
|
|
block_poll_top_height = cur_height;
|
2014-09-22 17:16:27 -07:00
|
|
|
break;
|
2014-10-01 11:03:27 -07:00
|
|
|
}
|
2014-10-02 13:32:21 -07:00
|
|
|
// Try again in 100ms
|
2014-10-01 11:03:27 -07:00
|
|
|
useconds_t usec = 100 * 1000;
|
|
|
|
usleep(usec);
|
|
|
|
}
|
|
|
|
|
2014-10-02 13:32:21 -07:00
|
|
|
// NOTE: Since we can't do v8 stuff on the uv thread pool, we need to create
|
|
|
|
// a linked list for all the blocks and free them up later.
|
2014-10-01 11:03:27 -07:00
|
|
|
poll_blocks_list *head = NULL;
|
|
|
|
poll_blocks_list *cur = NULL;
|
|
|
|
|
2014-10-01 11:20:53 -07:00
|
|
|
for (int i = poll_saved_height; i < block_poll_top_height; i++) {
|
2014-10-01 11:03:27 -07:00
|
|
|
if (i == -1) continue;
|
2014-10-02 13:24:18 -07:00
|
|
|
CBlockIndex *cblock_index = chainActive[i];
|
|
|
|
if (cblock_index != NULL) {
|
|
|
|
CBlock cblock;
|
|
|
|
if (ReadBlockFromDisk(cblock, cblock_index)) {
|
2014-10-01 11:03:27 -07:00
|
|
|
poll_blocks_list *next = new poll_blocks_list();
|
|
|
|
next->next = NULL;
|
|
|
|
if (cur == NULL) {
|
|
|
|
head = next;
|
|
|
|
cur = next;
|
|
|
|
} else {
|
|
|
|
cur->next = next;
|
|
|
|
cur = next;
|
|
|
|
}
|
2014-10-02 13:24:18 -07:00
|
|
|
cur->cblock = cblock;
|
|
|
|
cur->cblock_index = cblock_index;
|
2014-10-01 11:03:27 -07:00
|
|
|
}
|
2014-09-22 17:16:27 -07:00
|
|
|
}
|
2014-09-22 16:47:45 -07:00
|
|
|
}
|
2014-10-01 11:03:27 -07:00
|
|
|
|
|
|
|
data->head = head;
|
2014-09-22 17:16:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_poll_blocks_after(uv_work_t *req) {
|
|
|
|
NanScope();
|
|
|
|
async_poll_blocks_data* data = static_cast<async_poll_blocks_data*>(req->data);
|
|
|
|
|
|
|
|
if (!data->err_msg.empty()) {
|
|
|
|
Local<Value> err = Exception::Error(String::New(data->err_msg.c_str()));
|
|
|
|
const unsigned argc = 1;
|
|
|
|
Local<Value> argv[argc] = { err };
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
2014-09-22 16:47:45 -07:00
|
|
|
}
|
2014-09-22 17:16:27 -07:00
|
|
|
} else {
|
|
|
|
const unsigned argc = 2;
|
2014-09-22 17:58:59 -07:00
|
|
|
Local<Array> blocks = NanNew<Array>();
|
|
|
|
|
2014-10-01 11:03:27 -07:00
|
|
|
poll_blocks_list *cur = static_cast<poll_blocks_list*>(data->head);
|
|
|
|
poll_blocks_list *next;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
while (cur != NULL) {
|
2014-10-02 13:24:18 -07:00
|
|
|
CBlock cblock = cur->cblock;
|
|
|
|
CBlockIndex *cblock_index = cur->cblock_index;
|
|
|
|
Local<Object> jsblock = NanNew<Object>();
|
|
|
|
cblock_to_jsblock(cblock, cblock_index, jsblock);
|
|
|
|
blocks->Set(i, jsblock);
|
2014-10-01 11:03:27 -07:00
|
|
|
i++;
|
|
|
|
next = cur->next;
|
|
|
|
delete cur;
|
|
|
|
cur = next;
|
2014-09-22 17:58:59 -07:00
|
|
|
}
|
|
|
|
|
2014-09-22 17:16:27 -07:00
|
|
|
Local<Value> argv[argc] = {
|
|
|
|
Local<Value>::New(Null()),
|
2014-09-22 17:58:59 -07:00
|
|
|
Local<Value>::New(blocks)
|
2014-09-22 17:16:27 -07:00
|
|
|
};
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data->callback.Dispose();
|
|
|
|
|
|
|
|
delete data;
|
|
|
|
delete req;
|
|
|
|
}
|
|
|
|
|
2014-09-23 13:57:49 -07:00
|
|
|
/**
|
2014-09-25 13:54:20 -07:00
|
|
|
* PollMempool
|
2014-09-23 13:57:49 -07:00
|
|
|
* bitcoind.pollMempool(callback)
|
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(PollMempool) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsFunction()) {
|
|
|
|
return NanThrowError(
|
|
|
|
"Usage: bitcoindjs.pollMempool(callback)");
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Function> callback = Local<Function>::Cast(args[0]);
|
|
|
|
|
|
|
|
async_poll_mempool_data *data = new async_poll_mempool_data();
|
|
|
|
data->err_msg = std::string("");
|
|
|
|
data->callback = Persistent<Function>::New(callback);
|
|
|
|
|
|
|
|
uv_work_t *req = new uv_work_t();
|
|
|
|
req->data = data;
|
|
|
|
|
|
|
|
int status = uv_queue_work(uv_default_loop(),
|
|
|
|
req, async_poll_mempool,
|
|
|
|
(uv_after_work_cb)async_poll_mempool_after);
|
|
|
|
|
|
|
|
assert(status == 0);
|
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_poll_mempool(uv_work_t *req) {
|
2014-10-01 12:05:18 -07:00
|
|
|
// XXX Potentially do everything async, but would it matter? Everything is in
|
|
|
|
// memory. There aren't really any harsh blocking calls. Leave this here as a
|
|
|
|
// placeholder.
|
|
|
|
useconds_t usec = 5 * 1000;
|
2014-09-23 13:57:49 -07:00
|
|
|
usleep(usec);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_poll_mempool_after(uv_work_t *req) {
|
|
|
|
NanScope();
|
|
|
|
async_poll_blocks_data* data = static_cast<async_poll_blocks_data*>(req->data);
|
|
|
|
|
|
|
|
if (!data->err_msg.empty()) {
|
|
|
|
Local<Value> err = Exception::Error(String::New(data->err_msg.c_str()));
|
|
|
|
const unsigned argc = 1;
|
|
|
|
Local<Value> argv[argc] = { err };
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int ti = 0;
|
|
|
|
Local<Array> txs = NanNew<Array>();
|
|
|
|
|
|
|
|
{
|
|
|
|
std::map<uint256, CTxMemPoolEntry>::const_iterator it = mempool.mapTx.begin();
|
|
|
|
for (; it != mempool.mapTx.end(); it++) {
|
2014-10-02 13:24:18 -07:00
|
|
|
const CTransaction& ctx = it->second.GetTx();
|
|
|
|
Local<Object> jstx = NanNew<Object>();
|
|
|
|
ctx_to_jstx(ctx, 0, jstx);
|
|
|
|
txs->Set(ti, jstx);
|
2014-09-23 13:57:49 -07:00
|
|
|
ti++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
std::map<COutPoint, CInPoint>::const_iterator it = mempool.mapNextTx.begin();
|
|
|
|
for (; it != mempool.mapNextTx.end(); it++) {
|
2014-10-02 13:24:18 -07:00
|
|
|
const CTransaction ctx = *it->second.ptx;
|
|
|
|
Local<Object> jstx = NanNew<Object>();
|
|
|
|
ctx_to_jstx(ctx, 0, jstx);
|
|
|
|
txs->Set(ti, jstx);
|
2014-09-23 13:57:49 -07:00
|
|
|
ti++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const unsigned argc = 2;
|
|
|
|
Local<Value> argv[argc] = {
|
|
|
|
Local<Value>::New(Null()),
|
|
|
|
Local<Value>::New(txs)
|
|
|
|
};
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data->callback.Dispose();
|
|
|
|
|
|
|
|
delete data;
|
|
|
|
delete req;
|
|
|
|
}
|
|
|
|
|
2014-09-25 12:05:39 -07:00
|
|
|
/**
|
2014-09-25 13:54:20 -07:00
|
|
|
* BroadcastTx
|
2014-09-25 13:12:28 -07:00
|
|
|
* bitcoind.broadcastTx(tx, override_fees, own_only, callback)
|
2014-09-25 12:05:39 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(BroadcastTx) {
|
|
|
|
NanScope();
|
|
|
|
|
2014-09-25 13:12:28 -07:00
|
|
|
if (args.Length() < 4
|
2014-09-25 12:05:39 -07:00
|
|
|
|| !args[0]->IsObject()
|
2014-09-25 12:38:42 -07:00
|
|
|
|| !args[1]->IsBoolean()
|
2014-09-25 13:12:28 -07:00
|
|
|
|| !args[2]->IsBoolean()
|
|
|
|
|| !args[3]->IsFunction()) {
|
2014-09-25 12:05:39 -07:00
|
|
|
return NanThrowError(
|
2014-09-25 13:12:28 -07:00
|
|
|
"Usage: bitcoindjs.broadcastTx(tx, override_fees, own_only, callback)");
|
2014-09-25 12:05:39 -07:00
|
|
|
}
|
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
Local<Object> jstx = Local<Object>::Cast(args[0]);
|
2014-09-25 13:12:28 -07:00
|
|
|
Local<Function> callback = Local<Function>::Cast(args[3]);
|
2014-09-25 12:05:39 -07:00
|
|
|
|
2014-10-02 15:14:23 -07:00
|
|
|
String::Utf8Value tx_hex_(jstx->Get(NanNew<String>("hex"))->ToString());
|
|
|
|
std::string tx_hex = std::string(*tx_hex_);
|
|
|
|
|
2014-09-25 12:38:42 -07:00
|
|
|
async_broadcast_tx_data *data = new async_broadcast_tx_data();
|
2014-10-02 15:14:23 -07:00
|
|
|
data->tx_hex = tx_hex;
|
2014-09-25 12:05:39 -07:00
|
|
|
data->override_fees = args[1]->ToBoolean()->IsTrue();
|
2014-09-25 13:12:28 -07:00
|
|
|
data->own_only = args[2]->ToBoolean()->IsTrue();
|
2014-09-25 12:05:39 -07:00
|
|
|
data->err_msg = std::string("");
|
|
|
|
data->callback = Persistent<Function>::New(callback);
|
|
|
|
|
|
|
|
uv_work_t *req = new uv_work_t();
|
|
|
|
req->data = data;
|
|
|
|
|
|
|
|
int status = uv_queue_work(uv_default_loop(),
|
|
|
|
req, async_broadcast_tx,
|
|
|
|
(uv_after_work_cb)async_broadcast_tx_after);
|
|
|
|
|
|
|
|
assert(status == 0);
|
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_broadcast_tx(uv_work_t *req) {
|
2014-09-25 12:38:42 -07:00
|
|
|
async_broadcast_tx_data* data = static_cast<async_broadcast_tx_data*>(req->data);
|
2014-09-25 12:05:39 -07:00
|
|
|
|
2014-10-02 15:14:23 -07:00
|
|
|
CDataStream ssData(ParseHex(data->tx_hex), SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
CTransaction ctx;
|
|
|
|
|
2014-09-25 12:05:39 -07:00
|
|
|
bool fOverrideFees = false;
|
2014-09-25 13:12:28 -07:00
|
|
|
bool fOwnOnly = false;
|
2014-09-25 12:38:42 -07:00
|
|
|
|
2014-09-25 12:05:39 -07:00
|
|
|
if (data->override_fees) {
|
|
|
|
fOverrideFees = true;
|
|
|
|
}
|
|
|
|
|
2014-09-25 13:12:28 -07:00
|
|
|
if (data->own_only) {
|
|
|
|
fOwnOnly = true;
|
|
|
|
}
|
|
|
|
|
2014-10-02 15:14:23 -07:00
|
|
|
// jstx_to_ctx(jstx, ctx);
|
|
|
|
|
|
|
|
try {
|
|
|
|
ssData >> ctx;
|
|
|
|
} catch (std::exception &e) {
|
|
|
|
data->err_msg = std::string("TX decode failed");
|
|
|
|
return;
|
|
|
|
}
|
2014-09-25 12:05:39 -07:00
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
uint256 hashTx = ctx.GetHash();
|
2014-09-25 12:05:39 -07:00
|
|
|
|
|
|
|
bool fHave = false;
|
|
|
|
CCoinsViewCache &view = *pcoinsTip;
|
|
|
|
CCoins existingCoins;
|
2014-09-25 13:40:18 -07:00
|
|
|
if (fOwnOnly) {
|
2014-09-25 12:05:39 -07:00
|
|
|
fHave = view.GetCoins(hashTx, existingCoins);
|
|
|
|
if (!fHave) {
|
|
|
|
CValidationState state;
|
2014-10-02 13:24:18 -07:00
|
|
|
if (!AcceptToMemoryPool(mempool, state, ctx, false, NULL, !fOverrideFees)) {
|
2014-09-25 12:05:39 -07:00
|
|
|
data->err_msg = std::string("TX rejected");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fHave) {
|
|
|
|
if (existingCoins.nHeight < 1000000000) {
|
|
|
|
data->err_msg = std::string("transaction already in block chain");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
2014-10-02 13:24:18 -07:00
|
|
|
SyncWithWallets(hashTx, ctx, NULL);
|
2014-09-25 12:05:39 -07:00
|
|
|
}
|
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
RelayTransaction(ctx, hashTx);
|
2014-09-25 12:05:39 -07:00
|
|
|
|
|
|
|
data->tx_hash = hashTx.GetHex();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_broadcast_tx_after(uv_work_t *req) {
|
|
|
|
NanScope();
|
2014-09-25 12:38:42 -07:00
|
|
|
async_broadcast_tx_data* data = static_cast<async_broadcast_tx_data*>(req->data);
|
2014-09-25 12:05:39 -07:00
|
|
|
|
|
|
|
if (!data->err_msg.empty()) {
|
|
|
|
Local<Value> err = Exception::Error(String::New(data->err_msg.c_str()));
|
|
|
|
const unsigned argc = 1;
|
|
|
|
Local<Value> argv[argc] = { err };
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
} else {
|
2014-10-02 15:14:23 -07:00
|
|
|
// jstx_to_ctx(jstx, ctx);
|
|
|
|
CDataStream ssData(ParseHex(data->tx_hex), SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
CTransaction ctx;
|
|
|
|
ssData >> ctx;
|
|
|
|
Local<Object> jstx = NanNew<Object>();
|
|
|
|
ctx_to_jstx(ctx, 0, jstx);
|
|
|
|
|
2014-09-25 14:01:50 -07:00
|
|
|
const unsigned argc = 3;
|
2014-09-25 12:05:39 -07:00
|
|
|
Local<Value> argv[argc] = {
|
|
|
|
Local<Value>::New(Null()),
|
2014-09-25 14:01:50 -07:00
|
|
|
Local<Value>::New(NanNew<String>(data->tx_hash)),
|
2014-10-02 15:14:23 -07:00
|
|
|
Local<Value>::New(jstx)
|
2014-09-25 12:05:39 -07:00
|
|
|
};
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data->callback.Dispose();
|
|
|
|
|
|
|
|
delete data;
|
|
|
|
delete req;
|
|
|
|
}
|
|
|
|
|
2014-09-26 11:23:21 -07:00
|
|
|
/**
|
|
|
|
* VerifyBlock
|
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(VerifyBlock) {
|
|
|
|
NanScope();
|
|
|
|
|
2014-09-26 11:39:25 -07:00
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
2014-09-26 11:23:21 -07:00
|
|
|
return NanThrowError(
|
2014-09-26 11:39:25 -07:00
|
|
|
"Usage: bitcoindjs.verifyBlock(block)");
|
2014-09-26 11:23:21 -07:00
|
|
|
}
|
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
Local<Object> jsblock = Local<Object>::Cast(args[0]);
|
2014-09-26 11:39:25 -07:00
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
String::Utf8Value block_hex_(jsblock->Get(NanNew<String>("hex"))->ToString());
|
2014-09-26 11:39:25 -07:00
|
|
|
std::string block_hex = std::string(*block_hex_);
|
2014-09-26 11:23:21 -07:00
|
|
|
|
2014-10-02 15:14:23 -07:00
|
|
|
// jsblock_to_cblock(jsblock, cblock);
|
2014-10-02 13:24:18 -07:00
|
|
|
CBlock cblock;
|
2014-10-02 15:14:23 -07:00
|
|
|
CDataStream ssData(ParseHex(block_hex), SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
ssData >> cblock;
|
2014-09-26 11:23:21 -07:00
|
|
|
|
|
|
|
CValidationState state;
|
2014-10-02 13:24:18 -07:00
|
|
|
bool valid = CheckBlock(cblock, state);
|
2014-09-26 11:23:21 -07:00
|
|
|
|
|
|
|
NanReturnValue(NanNew<Boolean>(valid));
|
|
|
|
}
|
|
|
|
|
2014-09-26 11:34:55 -07:00
|
|
|
/**
|
|
|
|
* VerifyTransaction
|
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(VerifyTransaction) {
|
|
|
|
NanScope();
|
|
|
|
|
2014-09-26 11:39:25 -07:00
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
2014-09-26 11:34:55 -07:00
|
|
|
return NanThrowError(
|
2014-09-26 11:39:25 -07:00
|
|
|
"Usage: bitcoindjs.verifyTransaction(tx)");
|
2014-09-26 11:34:55 -07:00
|
|
|
}
|
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
Local<Object> jstx = Local<Object>::Cast(args[0]);
|
2014-09-26 11:39:25 -07:00
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
String::Utf8Value tx_hex_(jstx->Get(NanNew<String>("hex"))->ToString());
|
2014-09-26 11:39:25 -07:00
|
|
|
std::string tx_hex = std::string(*tx_hex_);
|
2014-09-26 11:34:55 -07:00
|
|
|
|
2014-10-02 15:14:23 -07:00
|
|
|
// jstx_to_ctx(jstx, ctx);
|
2014-10-02 13:24:18 -07:00
|
|
|
CTransaction ctx;
|
2014-10-02 15:14:23 -07:00
|
|
|
CDataStream ssData(ParseHex(tx_hex), SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
ssData >> ctx;
|
2014-09-26 11:34:55 -07:00
|
|
|
|
|
|
|
CValidationState state;
|
2014-10-02 13:24:18 -07:00
|
|
|
bool valid = CheckTransaction(ctx, state);
|
2014-09-26 11:34:55 -07:00
|
|
|
|
2014-09-29 12:18:29 -07:00
|
|
|
std::string reason;
|
2014-10-02 13:24:18 -07:00
|
|
|
bool standard = IsStandardTx(ctx, reason);
|
2014-09-26 11:34:55 -07:00
|
|
|
|
|
|
|
NanReturnValue(NanNew<Boolean>(valid && standard));
|
|
|
|
}
|
|
|
|
|
2014-09-29 14:28:31 -07:00
|
|
|
NAN_METHOD(FillTransaction) {
|
|
|
|
NanScope();
|
|
|
|
|
2014-09-29 16:47:44 -07:00
|
|
|
if (args.Length() < 2 || !args[0]->IsObject() || !args[1]->IsObject()) {
|
2014-09-29 14:28:31 -07:00
|
|
|
return NanThrowError(
|
2014-09-29 16:47:44 -07:00
|
|
|
"Usage: bitcoindjs.fillTransaction(tx, options)");
|
2014-09-29 14:28:31 -07:00
|
|
|
}
|
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
Local<Object> jstx = Local<Object>::Cast(args[0]);
|
2014-09-30 15:53:13 -07:00
|
|
|
// Local<Object> options = Local<Object>::Cast(args[1]);
|
2014-09-29 14:28:31 -07:00
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
String::Utf8Value tx_hex_(jstx->Get(NanNew<String>("hex"))->ToString());
|
2014-09-29 14:28:31 -07:00
|
|
|
std::string tx_hex = std::string(*tx_hex_);
|
|
|
|
|
2014-10-02 15:14:23 -07:00
|
|
|
// jstx_to_ctx(jstx, ctx);
|
2014-10-02 13:24:18 -07:00
|
|
|
CTransaction ctx;
|
2014-10-02 15:14:23 -07:00
|
|
|
CDataStream ssData(ParseHex(tx_hex), SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
ssData >> ctx;
|
2014-09-29 14:28:31 -07:00
|
|
|
|
2014-09-29 16:46:26 -07:00
|
|
|
// Get total value of outputs
|
|
|
|
// Get the scriptPubKey of the first output (presumably our destination)
|
|
|
|
int64_t nValue = 0;
|
2014-10-02 13:24:18 -07:00
|
|
|
for (unsigned int vo = 0; vo < ctx.vout.size(); vo++) {
|
|
|
|
const CTxOut& txout = ctx.vout[vo];
|
2014-09-29 16:46:26 -07:00
|
|
|
int64_t value = txout.nValue;
|
2014-09-29 16:06:49 -07:00
|
|
|
const CScript& scriptPubKey = txout.scriptPubKey;
|
2014-09-29 16:46:26 -07:00
|
|
|
nValue += value;
|
2014-09-29 16:06:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (nValue <= 0)
|
|
|
|
return NanThrowError("Invalid amount");
|
|
|
|
if (nValue + nTransactionFee > pwalletMain->GetBalance())
|
|
|
|
return NanThrowError("Insufficient funds");
|
|
|
|
|
|
|
|
int64_t nFeeRet = nTransactionFee;
|
|
|
|
|
|
|
|
if (pwalletMain->IsLocked()) {
|
|
|
|
return NanThrowError("Error: Wallet locked, unable to create transaction!");
|
|
|
|
}
|
|
|
|
|
2014-09-29 16:15:30 -07:00
|
|
|
CCoinControl* coinControl = new CCoinControl();
|
|
|
|
|
2014-09-29 16:06:49 -07:00
|
|
|
int64_t nTotalValue = nValue + nFeeRet;
|
|
|
|
set<pair<const CWalletTx*,unsigned int> > setCoins;
|
|
|
|
int64_t nValueIn = 0;
|
|
|
|
|
2014-09-30 13:35:21 -07:00
|
|
|
if (!pwalletMain->SelectCoins(nTotalValue, setCoins, nValueIn, coinControl)) {
|
2014-09-29 16:06:49 -07:00
|
|
|
return NanThrowError("Insufficient funds");
|
|
|
|
}
|
|
|
|
|
2014-09-29 16:46:26 -07:00
|
|
|
// Fill inputs if they aren't already filled
|
2014-10-02 13:24:18 -07:00
|
|
|
ctx.vin.clear();
|
2014-09-30 09:42:23 -07:00
|
|
|
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) {
|
2014-10-02 13:24:18 -07:00
|
|
|
ctx.vin.push_back(CTxIn(coin.first->GetHash(), coin.second));
|
2014-09-29 14:28:31 -07:00
|
|
|
}
|
|
|
|
|
2014-09-29 16:46:26 -07:00
|
|
|
// Sign everything
|
2014-09-29 14:28:31 -07:00
|
|
|
int nIn = 0;
|
|
|
|
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) {
|
2014-10-02 13:24:18 -07:00
|
|
|
if (!SignSignature(*pwalletMain, *coin.first, ctx, nIn++)) {
|
2014-09-29 14:28:31 -07:00
|
|
|
return NanThrowError("Signing transaction failed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-29 16:46:26 -07:00
|
|
|
// Turn our CTransaction into a javascript Transaction
|
2014-10-02 14:47:17 -07:00
|
|
|
Local<Object> new_jstx = NanNew<Object>();
|
|
|
|
ctx_to_jstx(ctx, 0, new_jstx);
|
2014-09-29 14:28:31 -07:00
|
|
|
|
2014-10-02 14:47:17 -07:00
|
|
|
NanReturnValue(new_jstx);
|
2014-09-29 14:28:31 -07:00
|
|
|
}
|
|
|
|
|
2014-09-26 12:20:00 -07:00
|
|
|
/**
|
|
|
|
* Wallet
|
|
|
|
*/
|
|
|
|
|
|
|
|
NAN_METHOD(WalletNewAddress) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
|
|
|
"Usage: bitcoindjs.walletNewAddress(options)");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the account first so we don't generate a key if there's an error
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
|
|
|
String::Utf8Value name_(options->Get(NanNew<String>("name"))->ToString());
|
|
|
|
std::string strAccount = std::string(*name_);
|
|
|
|
|
|
|
|
if (!pwalletMain->IsLocked()) {
|
|
|
|
pwalletMain->TopUpKeyPool();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate a new key that is added to wallet
|
|
|
|
CPubKey newKey;
|
|
|
|
|
|
|
|
if (!pwalletMain->GetKeyFromPool(newKey)) {
|
|
|
|
// return NanThrowError("Keypool ran out, please call keypoolrefill first");
|
2014-09-26 15:43:46 -07:00
|
|
|
// EnsureWalletIsUnlocked();
|
|
|
|
if (pwalletMain->IsLocked()) {
|
|
|
|
return NanThrowError("Please enter the wallet passphrase with walletpassphrase first.");
|
|
|
|
}
|
2014-09-26 12:20:00 -07:00
|
|
|
pwalletMain->TopUpKeyPool(100);
|
|
|
|
if (pwalletMain->GetKeyPoolSize() < 100) {
|
|
|
|
return NanThrowError("Error refreshing keypool.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CKeyID keyID = newKey.GetID();
|
|
|
|
|
|
|
|
pwalletMain->SetAddressBook(keyID, strAccount, "receive");
|
|
|
|
|
2014-10-01 15:14:20 -07:00
|
|
|
NanReturnValue(NanNew<String>(CBitcoinAddress(keyID).ToString().c_str()));
|
2014-09-26 12:20:00 -07:00
|
|
|
}
|
|
|
|
|
2014-10-01 15:14:20 -07:00
|
|
|
CBitcoinAddress GetAccountAddress(std::string strAccount, bool bForceNew=false) {
|
2014-10-01 14:24:07 -07:00
|
|
|
CWalletDB walletdb(pwalletMain->strWalletFile);
|
|
|
|
|
|
|
|
CAccount account;
|
|
|
|
walletdb.ReadAccount(strAccount, account);
|
|
|
|
|
|
|
|
bool bKeyUsed = false;
|
|
|
|
|
|
|
|
// Check if the current key has been used
|
|
|
|
if (account.vchPubKey.IsValid()) {
|
|
|
|
CScript scriptPubKey;
|
|
|
|
scriptPubKey.SetDestination(account.vchPubKey.GetID());
|
|
|
|
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
|
|
|
|
it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid();
|
|
|
|
++it) {
|
|
|
|
const CWalletTx& wtx = (*it).second;
|
|
|
|
BOOST_FOREACH(const CTxOut& txout, wtx.vout) {
|
|
|
|
if (txout.scriptPubKey == scriptPubKey) {
|
|
|
|
bKeyUsed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate a new key
|
|
|
|
if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed) {
|
|
|
|
if (!pwalletMain->GetKeyFromPool(account.vchPubKey)) {
|
2014-10-01 15:14:20 -07:00
|
|
|
NanThrowError("Keypool ran out, please call keypoolrefill first");
|
|
|
|
//CTxDestination dest = CNoDestination();
|
|
|
|
CBitcoinAddress addr;
|
|
|
|
//addr.Set(dest);
|
|
|
|
return addr;
|
2014-10-01 14:24:07 -07:00
|
|
|
}
|
|
|
|
pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive");
|
|
|
|
walletdb.WriteAccount(strAccount, account);
|
|
|
|
}
|
|
|
|
|
2014-10-01 15:14:20 -07:00
|
|
|
return CBitcoinAddress(account.vchPubKey.GetID());
|
|
|
|
}
|
|
|
|
|
|
|
|
NAN_METHOD(WalletGetAccountAddress) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
|
|
|
"Usage: bitcoindjs.walletGetAccountAddress(options)");
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
|
|
|
String::Utf8Value account_(options->Get(NanNew<String>("account"))->ToString());
|
|
|
|
std::string strAccount = std::string(*account_);
|
|
|
|
|
|
|
|
std::string ret = GetAccountAddress(strAccount).ToString();
|
|
|
|
|
|
|
|
NanReturnValue(NanNew<String>(ret.c_str()));
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletSetAccount) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 12:26:46 -07:00
|
|
|
"Usage: bitcoindjs.walletSetAccount(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the account first so we don't generate a key if there's an error
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
2014-10-01 14:24:07 -07:00
|
|
|
String::Utf8Value address_(options->Get(NanNew<String>("address"))->ToString());
|
|
|
|
std::string strAddress = std::string(*address_);
|
|
|
|
|
|
|
|
CBitcoinAddress address(strAddress);
|
|
|
|
if (!address.IsValid()) {
|
|
|
|
return NanThrowError("Invalid Bitcoin address");
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string strAccount;
|
|
|
|
if (options->Get(NanNew<String>("account"))->IsString()) {
|
|
|
|
String::Utf8Value account_(options->Get(NanNew<String>("account"))->ToString());
|
|
|
|
strAccount = std::string(*account_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Detect when changing the account of an address that is the 'unused current key' of another account:
|
|
|
|
if (pwalletMain->mapAddressBook.count(address.Get())) {
|
|
|
|
string strOldAccount = pwalletMain->mapAddressBook[address.Get()].name;
|
|
|
|
if (address == GetAccountAddress(strOldAccount)) {
|
|
|
|
GetAccountAddress(strOldAccount, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pwalletMain->SetAddressBook(address.Get(), strAccount, "receive");
|
2014-09-26 12:42:04 -07:00
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletGetAccount) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 12:26:46 -07:00
|
|
|
"Usage: bitcoindjs.walletGetAccount(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
|
|
|
|
2014-10-01 14:24:07 -07:00
|
|
|
String::Utf8Value address_(options->Get(NanNew<String>("address"))->ToString());
|
|
|
|
std::string strAddress = std::string(*address_);
|
|
|
|
|
|
|
|
CBitcoinAddress address(strAddress);
|
|
|
|
if (!address.IsValid()) {
|
|
|
|
return NanThrowError("Invalid Bitcoin address");
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string strAccount;
|
|
|
|
map<CTxDestination, CAddressBookData>::iterator mi = pwalletMain->mapAddressBook.find(address.Get());
|
|
|
|
if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty()) {
|
|
|
|
strAccount = (*mi).second.name;
|
|
|
|
}
|
|
|
|
|
|
|
|
NanReturnValue(NanNew<String>(strAccount.c_str()));
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
2014-09-29 11:59:57 -07:00
|
|
|
NAN_METHOD(WalletSendTo) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 11:59:57 -07:00
|
|
|
"Usage: bitcoindjs.walletSendTo(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
|
|
|
|
2014-09-29 11:59:57 -07:00
|
|
|
async_wallet_sendto_data *data = new async_wallet_sendto_data();
|
|
|
|
|
2014-09-26 13:18:23 -07:00
|
|
|
String::Utf8Value addr_(options->Get(NanNew<String>("address"))->ToString());
|
|
|
|
std::string addr = std::string(*addr_);
|
2014-09-29 11:59:57 -07:00
|
|
|
data->address = addr;
|
2014-09-26 12:42:04 -07:00
|
|
|
|
|
|
|
// Amount
|
2014-09-26 15:43:46 -07:00
|
|
|
int64_t nAmount = options->Get(NanNew<String>("amount"))->IntegerValue();
|
2014-09-29 11:59:57 -07:00
|
|
|
data->nAmount = nAmount;
|
2014-09-26 12:42:04 -07:00
|
|
|
|
|
|
|
// Wallet comments
|
|
|
|
CWalletTx wtx;
|
2014-09-26 15:43:46 -07:00
|
|
|
if (options->Get(NanNew<String>("comment"))->IsString()) {
|
2014-09-26 13:18:23 -07:00
|
|
|
String::Utf8Value comment_(options->Get(NanNew<String>("comment"))->ToString());
|
|
|
|
std::string comment = std::string(*comment_);
|
|
|
|
wtx.mapValue["comment"] = comment;
|
|
|
|
}
|
2014-09-26 15:43:46 -07:00
|
|
|
if (options->Get(NanNew<String>("to"))->IsString()) {
|
2014-09-26 13:18:23 -07:00
|
|
|
String::Utf8Value to_(options->Get(NanNew<String>("to"))->ToString());
|
|
|
|
std::string to = std::string(*to_);
|
|
|
|
wtx.mapValue["to"] = to;
|
|
|
|
}
|
2014-09-29 11:59:57 -07:00
|
|
|
data->wtx = wtx;
|
|
|
|
|
|
|
|
uv_work_t *req = new uv_work_t();
|
|
|
|
req->data = data;
|
|
|
|
|
|
|
|
int status = uv_queue_work(uv_default_loop(),
|
|
|
|
req, async_wallet_sendto,
|
|
|
|
(uv_after_work_cb)async_wallet_sendto_after);
|
|
|
|
|
|
|
|
assert(status == 0);
|
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_wallet_sendto(uv_work_t *req) {
|
|
|
|
async_wallet_sendto_data* data = static_cast<async_wallet_sendto_data*>(req->data);
|
|
|
|
|
|
|
|
CBitcoinAddress address(data->address);
|
|
|
|
|
|
|
|
if (!address.IsValid()) {
|
|
|
|
data->err_msg = std::string("Invalid Bitcoin address");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Amount
|
|
|
|
int64_t nAmount = data->nAmount;
|
|
|
|
|
|
|
|
// Wallet Transaction
|
|
|
|
CWalletTx wtx = data->wtx;
|
2014-09-26 12:42:04 -07:00
|
|
|
|
2014-09-26 15:43:46 -07:00
|
|
|
// EnsureWalletIsUnlocked();
|
|
|
|
if (pwalletMain->IsLocked()) {
|
2014-09-29 11:59:57 -07:00
|
|
|
data->err_msg = std::string("Please enter the wallet passphrase with walletpassphrase first.");
|
|
|
|
return;
|
2014-09-26 15:43:46 -07:00
|
|
|
}
|
2014-09-26 12:42:04 -07:00
|
|
|
|
2014-09-29 11:59:57 -07:00
|
|
|
std::string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
|
2014-09-26 13:18:23 -07:00
|
|
|
if (strError != "") {
|
2014-09-29 11:59:57 -07:00
|
|
|
data->err_msg = strError;
|
|
|
|
return;
|
2014-09-26 13:18:23 -07:00
|
|
|
}
|
2014-09-26 12:42:04 -07:00
|
|
|
|
2014-09-29 11:59:57 -07:00
|
|
|
data->tx_hash = wtx.GetHash().GetHex();
|
|
|
|
}
|
2014-09-26 12:42:04 -07:00
|
|
|
|
2014-09-29 11:59:57 -07:00
|
|
|
static void
|
|
|
|
async_wallet_sendto_after(uv_work_t *req) {
|
|
|
|
NanScope();
|
|
|
|
async_wallet_sendto_data* data = static_cast<async_wallet_sendto_data*>(req->data);
|
|
|
|
|
|
|
|
if (!data->err_msg.empty()) {
|
|
|
|
Local<Value> err = Exception::Error(String::New(data->err_msg.c_str()));
|
|
|
|
const unsigned argc = 1;
|
|
|
|
Local<Value> argv[argc] = { err };
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const unsigned argc = 2;
|
|
|
|
Local<Value> argv[argc] = {
|
|
|
|
Local<Value>::New(Null()),
|
|
|
|
Local<Value>::New(NanNew<String>(data->tx_hash))
|
|
|
|
};
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data->callback.Dispose();
|
|
|
|
|
|
|
|
delete data;
|
|
|
|
delete req;
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletSignMessage) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 12:26:46 -07:00
|
|
|
"Usage: bitcoindjs.walletSignMessage(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
|
|
|
|
2014-10-01 12:52:06 -07:00
|
|
|
String::Utf8Value strAddress_(options->Get(NanNew<String>("address"))->ToString());
|
|
|
|
std::string strAddress = std::string(*strAddress_);
|
|
|
|
String::Utf8Value strMessage_(options->Get(NanNew<String>("message"))->ToString());
|
|
|
|
std::string strMessage = std::string(*strMessage_);
|
|
|
|
|
|
|
|
// EnsureWalletIsUnlocked();
|
|
|
|
if (pwalletMain->IsLocked()) {
|
|
|
|
return NanThrowError("Please enter the wallet passphrase with walletpassphrase first.");
|
|
|
|
}
|
|
|
|
|
|
|
|
CBitcoinAddress addr(strAddress);
|
|
|
|
if (!addr.IsValid()) {
|
|
|
|
return NanThrowError("Invalid address");
|
|
|
|
}
|
|
|
|
|
|
|
|
CKeyID keyID;
|
|
|
|
if (!addr.GetKeyID(keyID)) {
|
|
|
|
return NanThrowError("Address does not refer to key");
|
|
|
|
}
|
|
|
|
|
|
|
|
CKey key;
|
|
|
|
if (!pwalletMain->GetKey(keyID, key)) {
|
|
|
|
return NanThrowError("Private key not available");
|
|
|
|
}
|
|
|
|
|
|
|
|
CHashWriter ss(SER_GETHASH, 0);
|
|
|
|
ss << strMessageMagic;
|
|
|
|
ss << strMessage;
|
|
|
|
|
|
|
|
vector<unsigned char> vchSig;
|
|
|
|
if (!key.SignCompact(ss.GetHash(), vchSig)) {
|
|
|
|
return NanThrowError("Sign failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string result = EncodeBase64(&vchSig[0], vchSig.size());
|
|
|
|
|
|
|
|
NanReturnValue(NanNew<String>(result.c_str()));
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletVerifyMessage) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 12:26:46 -07:00
|
|
|
"Usage: bitcoindjs.walletVerifyMessage(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
|
|
|
|
2014-10-01 12:58:11 -07:00
|
|
|
String::Utf8Value strAddress_(options->Get(NanNew<String>("address"))->ToString());
|
|
|
|
std::string strAddress = std::string(*strAddress_);
|
2014-10-01 14:24:07 -07:00
|
|
|
|
2014-10-01 12:58:11 -07:00
|
|
|
String::Utf8Value strSign_(options->Get(NanNew<String>("signature"))->ToString());
|
|
|
|
std::string strSign = std::string(*strSign_);
|
2014-10-01 14:24:07 -07:00
|
|
|
|
2014-10-01 12:58:11 -07:00
|
|
|
String::Utf8Value strMessage_(options->Get(NanNew<String>("message"))->ToString());
|
|
|
|
std::string strMessage = std::string(*strMessage_);
|
|
|
|
|
|
|
|
CBitcoinAddress addr(strAddress);
|
|
|
|
if (!addr.IsValid()) {
|
|
|
|
return NanThrowError( "Invalid address");
|
|
|
|
}
|
|
|
|
|
|
|
|
CKeyID keyID;
|
|
|
|
if (!addr.GetKeyID(keyID)) {
|
|
|
|
return NanThrowError( "Address does not refer to key");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool fInvalid = false;
|
|
|
|
vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid);
|
|
|
|
|
|
|
|
if (fInvalid) {
|
|
|
|
return NanThrowError( "Malformed base64 encoding");
|
|
|
|
}
|
|
|
|
|
|
|
|
CHashWriter ss(SER_GETHASH, 0);
|
|
|
|
ss << strMessageMagic;
|
|
|
|
ss << strMessage;
|
|
|
|
|
|
|
|
CPubKey pubkey;
|
|
|
|
if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) {
|
|
|
|
NanReturnValue(NanNew<Boolean>(false));
|
|
|
|
}
|
|
|
|
|
|
|
|
NanReturnValue(NanNew<Boolean>(pubkey.GetID() == keyID));
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
2014-10-01 13:11:07 -07:00
|
|
|
NAN_METHOD(WalletCreateMultiSigAddress) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
|
|
|
"Usage: bitcoindjs.walletCreateMultiSigAddress(options)");
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
|
|
|
|
|
|
|
int nRequired = options->Get(NanNew<String>("nRequired"))->IntegerValue();
|
2014-10-01 15:14:20 -07:00
|
|
|
Local<Array> keys = Local<Array>::Cast(options->Get(NanNew<String>("keys")));
|
2014-10-01 13:11:07 -07:00
|
|
|
|
|
|
|
// Gather public keys
|
|
|
|
if (nRequired < 1) {
|
2014-10-01 13:37:18 -07:00
|
|
|
return NanThrowError(
|
|
|
|
"a multisignature address must require at least one key to redeem");
|
2014-10-01 13:11:07 -07:00
|
|
|
}
|
2014-10-01 15:14:20 -07:00
|
|
|
if ((int)keys->Length() < nRequired) {
|
|
|
|
char s[150] = {0};
|
|
|
|
snprintf(s, sizeof(s),
|
|
|
|
"not enough keys supplied (got %"PRIszu" keys, but need at least %d to redeem)",
|
|
|
|
keys->Length(), nRequired);
|
|
|
|
NanThrowError(s);
|
|
|
|
NanReturnValue(Undefined());
|
2014-10-01 13:11:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<CPubKey> pubkeys;
|
2014-10-01 15:14:20 -07:00
|
|
|
pubkeys.resize(keys->Length());
|
2014-10-01 13:11:07 -07:00
|
|
|
|
2014-10-01 15:14:20 -07:00
|
|
|
for (unsigned int i = 0; i < keys->Length(); i++) {
|
2014-10-01 13:11:07 -07:00
|
|
|
String::Utf8Value key_(keys->Get(i)->ToString());
|
|
|
|
const std::string& ks = std::string(*key_);
|
|
|
|
|
|
|
|
#ifdef ENABLE_WALLET
|
|
|
|
// Case 1: Bitcoin address and we have full public key:
|
|
|
|
CBitcoinAddress address(ks);
|
|
|
|
if (pwalletMain && address.IsValid()) {
|
|
|
|
CKeyID keyID;
|
|
|
|
if (!address.GetKeyID(keyID)) {
|
2014-10-01 15:14:20 -07:00
|
|
|
return NanThrowError((ks + std::string(" does not refer to a key")).c_str());
|
2014-10-01 13:11:07 -07:00
|
|
|
}
|
|
|
|
CPubKey vchPubKey;
|
|
|
|
if (!pwalletMain->GetPubKey(keyID, vchPubKey)) {
|
2014-10-01 15:14:20 -07:00
|
|
|
return NanThrowError((std::string("no full public key for address ") + ks).c_str());
|
2014-10-01 13:11:07 -07:00
|
|
|
}
|
|
|
|
if (!vchPubKey.IsFullyValid()) {
|
2014-10-01 15:14:20 -07:00
|
|
|
return NanThrowError((std::string("Invalid public key: ") + ks).c_str());
|
2014-10-01 13:11:07 -07:00
|
|
|
}
|
|
|
|
pubkeys[i] = vchPubKey;
|
|
|
|
} else
|
|
|
|
// Case 2: hex public key
|
|
|
|
#endif
|
|
|
|
if (IsHex(ks)) {
|
|
|
|
CPubKey vchPubKey(ParseHex(ks));
|
|
|
|
if (!vchPubKey.IsFullyValid()) {
|
2014-10-01 15:14:20 -07:00
|
|
|
return NanThrowError((std::string("Invalid public key: ") + ks).c_str());
|
2014-10-01 13:11:07 -07:00
|
|
|
}
|
|
|
|
pubkeys[i] = vchPubKey;
|
|
|
|
} else {
|
2014-10-01 15:14:20 -07:00
|
|
|
return NanThrowError((std::string("Invalid public key: ") + ks).c_str());
|
2014-10-01 13:11:07 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
CScript inner;
|
|
|
|
inner.SetMultisig(nRequired, pubkeys);
|
|
|
|
|
|
|
|
// Construct using pay-to-script-hash:
|
|
|
|
CScriptID innerID = inner.GetID();
|
|
|
|
CBitcoinAddress address(innerID);
|
|
|
|
|
|
|
|
Local<Object> result = NanNew<Object>();
|
2014-10-01 15:14:20 -07:00
|
|
|
result->Set(NanNew<String>("address"), NanNew<String>(address.ToString()));
|
|
|
|
result->Set(NanNew<String>("redeemScript"), NanNew<String>(HexStr(inner.begin(), inner.end())));
|
2014-10-01 13:11:07 -07:00
|
|
|
|
|
|
|
NanReturnValue(result);
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletGetBalance) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 12:26:46 -07:00
|
|
|
"Usage: bitcoindjs.walletGetBalance(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
|
|
|
|
2014-10-01 13:37:18 -07:00
|
|
|
std::string strAccount = "";
|
|
|
|
int nMinDepth = 1;
|
|
|
|
|
|
|
|
if (options->Get(NanNew<String>("account"))->IsString()) {
|
|
|
|
String::Utf8Value strAccount_(options->Get(NanNew<String>("account"))->ToString());
|
|
|
|
strAccount = std::string(*strAccount_);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options->Get(NanNew<String>("nMinDepth"))->IsNumber()) {
|
|
|
|
nMinDepth = options->Get(NanNew<String>("nMinDepth"))->IntegerValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strAccount == "*") {
|
|
|
|
// Calculate total balance a different way from GetBalance()
|
|
|
|
// (GetBalance() sums up all unspent TxOuts)
|
|
|
|
// getbalance and getbalance '*' 0 should return the same number
|
|
|
|
int64_t nBalance = 0;
|
|
|
|
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
|
|
|
|
it != pwalletMain->mapWallet.end(); ++it) {
|
|
|
|
const CWalletTx& wtx = (*it).second;
|
|
|
|
|
|
|
|
if (!wtx.IsTrusted() || wtx.GetBlocksToMaturity() > 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t allFee;
|
|
|
|
string strSentAccount;
|
|
|
|
list<pair<CTxDestination, int64_t> > listReceived;
|
|
|
|
list<pair<CTxDestination, int64_t> > listSent;
|
|
|
|
wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount);
|
|
|
|
if (wtx.GetDepthInMainChain() >= nMinDepth) {
|
|
|
|
BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived) {
|
|
|
|
nBalance += r.second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listSent) {
|
|
|
|
nBalance -= r.second;
|
|
|
|
}
|
|
|
|
nBalance -= allFee;
|
|
|
|
}
|
|
|
|
NanReturnValue(NanNew<Number>(nBalance));
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t nBalance = GetAccountBalance(strAccount, nMinDepth);
|
|
|
|
|
|
|
|
NanReturnValue(NanNew<Number>(nBalance));
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletGetUnconfirmedBalance) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
2014-10-01 13:41:25 -07:00
|
|
|
NanReturnValue(NanNew<Number>(pwalletMain->GetUnconfirmedBalance()));
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletSendFrom) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 12:26:46 -07:00
|
|
|
"Usage: bitcoindjs.walletSendFrom(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
|
|
|
|
2014-09-29 12:15:59 -07:00
|
|
|
async_wallet_sendfrom_data *data = new async_wallet_sendfrom_data();
|
|
|
|
|
2014-09-26 13:18:23 -07:00
|
|
|
String::Utf8Value addr_(options->Get(NanNew<String>("address"))->ToString());
|
|
|
|
std::string addr = std::string(*addr_);
|
2014-09-29 12:15:59 -07:00
|
|
|
data->address = addr;
|
2014-09-26 13:18:23 -07:00
|
|
|
|
2014-09-29 12:15:59 -07:00
|
|
|
String::Utf8Value from_(options->Get(NanNew<String>("from"))->ToString());
|
|
|
|
std::string from = std::string(*from_);
|
|
|
|
std::string strAccount = from;
|
2014-09-26 13:18:23 -07:00
|
|
|
|
2014-09-26 15:43:46 -07:00
|
|
|
int64_t nAmount = options->Get(NanNew<String>("amount"))->IntegerValue();
|
2014-09-29 12:15:59 -07:00
|
|
|
data->nAmount = nAmount;
|
|
|
|
|
2014-09-26 12:42:04 -07:00
|
|
|
int nMinDepth = 1;
|
2014-09-26 15:43:46 -07:00
|
|
|
if (options->Get(NanNew<String>("minDepth"))->IsNumber()) {
|
2014-09-26 13:18:23 -07:00
|
|
|
nMinDepth = options->Get(NanNew<String>("minDepth"))->IntegerValue();
|
|
|
|
}
|
2014-09-29 12:15:59 -07:00
|
|
|
data->nMinDepth = nMinDepth;
|
2014-09-26 12:42:04 -07:00
|
|
|
|
|
|
|
CWalletTx wtx;
|
|
|
|
wtx.strFromAccount = strAccount;
|
2014-09-26 15:43:46 -07:00
|
|
|
if (options->Get(NanNew<String>("comment"))->IsString()) {
|
2014-09-26 13:18:23 -07:00
|
|
|
String::Utf8Value comment_(options->Get(NanNew<String>("comment"))->ToString());
|
|
|
|
std::string comment = std::string(*comment_);
|
|
|
|
wtx.mapValue["comment"] = comment;
|
|
|
|
}
|
2014-09-26 15:43:46 -07:00
|
|
|
if (options->Get(NanNew<String>("to"))->IsString()) {
|
2014-09-26 13:18:23 -07:00
|
|
|
String::Utf8Value to_(options->Get(NanNew<String>("to"))->ToString());
|
|
|
|
std::string to = std::string(*to_);
|
|
|
|
wtx.mapValue["to"] = to;
|
|
|
|
}
|
2014-09-29 12:15:59 -07:00
|
|
|
data->wtx = wtx;
|
|
|
|
|
|
|
|
uv_work_t *req = new uv_work_t();
|
|
|
|
req->data = data;
|
|
|
|
|
|
|
|
int status = uv_queue_work(uv_default_loop(),
|
|
|
|
req, async_wallet_sendfrom,
|
|
|
|
(uv_after_work_cb)async_wallet_sendfrom_after);
|
|
|
|
|
|
|
|
assert(status == 0);
|
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_wallet_sendfrom(uv_work_t *req) {
|
|
|
|
async_wallet_sendfrom_data* data = static_cast<async_wallet_sendfrom_data*>(req->data);
|
|
|
|
|
|
|
|
CBitcoinAddress address(data->address);
|
|
|
|
|
|
|
|
if (!address.IsValid()) {
|
|
|
|
data->err_msg = std::string("Invalid Bitcoin address");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t nAmount = data->nAmount;
|
|
|
|
int nMinDepth = data->nMinDepth;
|
|
|
|
CWalletTx wtx = data->wtx;
|
|
|
|
std::string strAccount = data->wtx.strFromAccount;
|
2014-09-26 12:42:04 -07:00
|
|
|
|
2014-09-26 15:43:46 -07:00
|
|
|
// EnsureWalletIsUnlocked();
|
|
|
|
if (pwalletMain->IsLocked()) {
|
2014-09-29 12:15:59 -07:00
|
|
|
data->err_msg = std::string("Please enter the wallet passphrase with walletpassphrase first.");
|
|
|
|
return;
|
2014-09-26 15:43:46 -07:00
|
|
|
}
|
2014-09-26 12:42:04 -07:00
|
|
|
|
|
|
|
// Check funds
|
|
|
|
int64_t nBalance = GetAccountBalance(strAccount, nMinDepth);
|
2014-09-26 13:18:23 -07:00
|
|
|
if (nAmount > nBalance) {
|
2014-09-29 12:15:59 -07:00
|
|
|
data->err_msg = std::string("Account has insufficient funds");
|
|
|
|
return;
|
2014-09-26 13:18:23 -07:00
|
|
|
}
|
2014-09-26 12:42:04 -07:00
|
|
|
|
|
|
|
// Send
|
2014-09-29 12:15:59 -07:00
|
|
|
std::string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx);
|
2014-09-26 13:18:23 -07:00
|
|
|
if (strError != "") {
|
2014-09-29 12:15:59 -07:00
|
|
|
data->err_msg = strError;
|
|
|
|
return;
|
2014-09-26 13:18:23 -07:00
|
|
|
}
|
2014-09-26 12:42:04 -07:00
|
|
|
|
2014-09-29 12:15:59 -07:00
|
|
|
data->tx_hash = wtx.GetHash().GetHex();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_wallet_sendfrom_after(uv_work_t *req) {
|
|
|
|
NanScope();
|
|
|
|
async_wallet_sendfrom_data* data = static_cast<async_wallet_sendfrom_data*>(req->data);
|
2014-09-26 12:42:04 -07:00
|
|
|
|
2014-09-29 12:15:59 -07:00
|
|
|
if (!data->err_msg.empty()) {
|
|
|
|
Local<Value> err = Exception::Error(String::New(data->err_msg.c_str()));
|
|
|
|
const unsigned argc = 1;
|
|
|
|
Local<Value> argv[argc] = { err };
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const unsigned argc = 2;
|
|
|
|
Local<Value> argv[argc] = {
|
|
|
|
Local<Value>::New(Null()),
|
|
|
|
Local<Value>::New(NanNew<String>(data->tx_hash))
|
|
|
|
};
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data->callback.Dispose();
|
|
|
|
|
|
|
|
delete data;
|
|
|
|
delete req;
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletListTransactions) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 12:26:46 -07:00
|
|
|
"Usage: bitcoindjs.walletListTransactions(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
2014-10-01 15:14:20 -07:00
|
|
|
// Local<Object> options = Local<Object>::Cast(args[0]);
|
2014-09-26 12:42:04 -07:00
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletListAccounts) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 12:26:46 -07:00
|
|
|
"Usage: bitcoindjs.walletListAccounts(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
|
|
|
|
2014-09-26 15:43:46 -07:00
|
|
|
int nMinDepth = 1;
|
|
|
|
if (options->Get(NanNew<String>("minDepth"))->IsNumber()) {
|
|
|
|
nMinDepth = options->Get(NanNew<String>("minDepth"))->IntegerValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
map<string, int64_t> mapAccountBalances;
|
|
|
|
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) {
|
|
|
|
if (IsMine(*pwalletMain, entry.first)) { // This address belongs to me
|
|
|
|
mapAccountBalances[entry.second.name] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin();
|
|
|
|
it != pwalletMain->mapWallet.end(); ++it) {
|
|
|
|
const CWalletTx& wtx = (*it).second;
|
|
|
|
int64_t nFee;
|
2014-09-29 12:18:29 -07:00
|
|
|
std::string strSentAccount;
|
2014-09-26 15:43:46 -07:00
|
|
|
list<pair<CTxDestination, int64_t> > listReceived;
|
|
|
|
list<pair<CTxDestination, int64_t> > listSent;
|
|
|
|
int nDepth = wtx.GetDepthInMainChain();
|
|
|
|
if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount);
|
|
|
|
mapAccountBalances[strSentAccount] -= nFee;
|
|
|
|
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) {
|
|
|
|
mapAccountBalances[strSentAccount] -= s.second;
|
|
|
|
}
|
|
|
|
if (nDepth >= nMinDepth) {
|
|
|
|
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& r, listReceived) {
|
|
|
|
if (pwalletMain->mapAddressBook.count(r.first)) {
|
|
|
|
mapAccountBalances[pwalletMain->mapAddressBook[r.first].name] += r.second;
|
|
|
|
} else {
|
|
|
|
mapAccountBalances[""] += r.second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
list<CAccountingEntry> acentries;
|
|
|
|
CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries);
|
|
|
|
BOOST_FOREACH(const CAccountingEntry& entry, acentries) {
|
|
|
|
mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> obj = NanNew<Object>();
|
|
|
|
BOOST_FOREACH(const PAIRTYPE(string, int64_t)& accountBalance, mapAccountBalances) {
|
|
|
|
Local<Object> entry = NanNew<Object>();
|
|
|
|
entry->Set(NanNew<String>("balance"), NanNew<Number>(accountBalance.second));
|
|
|
|
Local<Array> addr = NanNew<Array>();
|
|
|
|
int i = 0;
|
|
|
|
BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) {
|
|
|
|
const CBitcoinAddress& address = item.first;
|
2014-09-29 12:18:29 -07:00
|
|
|
const std::string& strName = item.second.name;
|
2014-09-26 15:43:46 -07:00
|
|
|
if (strName == accountBalance.first) {
|
|
|
|
Local<Object> a = NanNew<Object>();
|
|
|
|
a->Set(NanNew<String>("address"), NanNew<String>(address.ToString()));
|
|
|
|
|
|
|
|
CKeyID keyID;
|
|
|
|
if (!address.GetKeyID(keyID)) {
|
|
|
|
return NanThrowError("Address does not refer to a key");
|
|
|
|
}
|
|
|
|
CKey vchSecret;
|
|
|
|
if (!pwalletMain->GetKey(keyID, vchSecret)) {
|
|
|
|
return NanThrowError("Private key for address is not known");
|
|
|
|
}
|
|
|
|
std::string priv = CBitcoinSecret(vchSecret).ToString();
|
|
|
|
a->Set(NanNew<String>("privkeycompressed"), NanNew<Boolean>(vchSecret.IsCompressed()));
|
|
|
|
a->Set(NanNew<String>("privkey"), NanNew<String>(priv));
|
|
|
|
|
|
|
|
CPubKey vchPubKey;
|
|
|
|
pwalletMain->GetPubKey(keyID, vchPubKey);
|
|
|
|
a->Set(NanNew<String>("pubkeycompressed"), NanNew<Boolean>(vchPubKey.IsCompressed()));
|
|
|
|
a->Set(NanNew<String>("pubkey"), NanNew<String>(HexStr(vchPubKey)));
|
|
|
|
|
|
|
|
addr->Set(i, a);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
entry->Set(NanNew<String>("addresses"), addr);
|
|
|
|
obj->Set(NanNew<String>(accountBalance.first), entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
NanReturnValue(obj);
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletGetTransaction) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 12:26:46 -07:00
|
|
|
"Usage: bitcoindjs.walletGetTransaction(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
2014-10-01 15:14:20 -07:00
|
|
|
// Local<Object> options = Local<Object>::Cast(args[0]);
|
2014-09-26 12:42:04 -07:00
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletBackup) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 12:26:46 -07:00
|
|
|
"Usage: bitcoindjs.walletBackup(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
2014-10-01 14:24:07 -07:00
|
|
|
|
|
|
|
String::Utf8Value path_(options->Get(NanNew<String>("path"))->ToString());
|
|
|
|
std::string strDest = std::string(*path_);
|
|
|
|
|
|
|
|
if (!BackupWallet(*pwalletMain, strDest)) {
|
|
|
|
return NanThrowError("Error: Wallet backup failed!");
|
|
|
|
}
|
2014-09-26 12:42:04 -07:00
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
NAN_METHOD(WalletPassphrase) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
|
|
|
"Usage: bitcoindjs.walletPassphrase(options)");
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
2014-10-01 14:24:07 -07:00
|
|
|
|
|
|
|
String::Utf8Value passphrase_(options->Get(NanNew<String>("passphrase"))->ToString());
|
|
|
|
std::string strPassphrase = std::string(*passphrase_);
|
|
|
|
|
|
|
|
if (!pwalletMain->IsCrypted()) {
|
|
|
|
return NanThrowError("Error: running with an unencrypted wallet, but walletpassphrase was called.");
|
|
|
|
}
|
|
|
|
|
|
|
|
SecureString strWalletPass;
|
|
|
|
strWalletPass.reserve(100);
|
|
|
|
strWalletPass = strPassphrase.c_str();
|
|
|
|
|
|
|
|
if (strWalletPass.length() > 0) {
|
|
|
|
if (!pwalletMain->Unlock(strWalletPass)) {
|
|
|
|
return NanThrowError("Error: The wallet passphrase entered was incorrect.");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return NanThrowError(
|
|
|
|
"walletpassphrase <passphrase> <timeout>\n"
|
|
|
|
"Stores the wallet decryption key in memory for <timeout> seconds.");
|
|
|
|
}
|
|
|
|
|
|
|
|
pwalletMain->TopUpKeyPool();
|
2014-09-26 12:42:04 -07:00
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
NAN_METHOD(WalletPassphraseChange) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
|
|
|
"Usage: bitcoindjs.walletPassphraseChange(options)");
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
2014-10-01 14:24:07 -07:00
|
|
|
|
|
|
|
String::Utf8Value oldPass_(options->Get(NanNew<String>("oldPass"))->ToString());
|
|
|
|
std::string oldPass = std::string(*oldPass_);
|
|
|
|
|
|
|
|
String::Utf8Value newPass_(options->Get(NanNew<String>("newPass"))->ToString());
|
|
|
|
std::string newPass = std::string(*newPass_);
|
|
|
|
|
|
|
|
if (!pwalletMain->IsCrypted()) {
|
|
|
|
return NanThrowError("Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
|
|
|
|
}
|
|
|
|
|
|
|
|
SecureString strOldWalletPass;
|
|
|
|
strOldWalletPass.reserve(100);
|
|
|
|
strOldWalletPass = oldPass.c_str();
|
|
|
|
|
|
|
|
SecureString strNewWalletPass;
|
|
|
|
strNewWalletPass.reserve(100);
|
|
|
|
strNewWalletPass = newPass.c_str();
|
|
|
|
|
|
|
|
if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) {
|
|
|
|
return NanThrowError(
|
|
|
|
"walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
|
|
|
|
"Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
|
|
|
|
return NanThrowError("Error: The wallet passphrase entered was incorrect.");
|
|
|
|
}
|
2014-09-26 12:42:04 -07:00
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
NAN_METHOD(WalletLock) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
|
|
|
"Usage: bitcoindjs.walletLock(options)");
|
|
|
|
}
|
|
|
|
|
2014-10-01 15:14:20 -07:00
|
|
|
// Local<Object> options = Local<Object>::Cast(args[0]);
|
2014-10-01 14:24:07 -07:00
|
|
|
|
|
|
|
if (!pwalletMain->IsCrypted()) {
|
|
|
|
return NanThrowError("Error: running with an unencrypted wallet, but walletlock was called.");
|
|
|
|
}
|
|
|
|
|
|
|
|
pwalletMain->Lock();
|
2014-09-26 12:42:04 -07:00
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletEncrypt) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 12:26:46 -07:00
|
|
|
"Usage: bitcoindjs.walletEncrypt(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
2014-10-01 14:24:07 -07:00
|
|
|
|
|
|
|
String::Utf8Value passphrase_(options->Get(NanNew<String>("passphrase"))->ToString());
|
|
|
|
std::string strPass = std::string(*passphrase_);
|
|
|
|
|
|
|
|
if (pwalletMain->IsCrypted()) {
|
|
|
|
return NanThrowError("Error: running with an encrypted wallet, but encryptwallet was called.");
|
|
|
|
}
|
|
|
|
|
|
|
|
SecureString strWalletPass;
|
|
|
|
strWalletPass.reserve(100);
|
|
|
|
strWalletPass = strPass.c_str();
|
|
|
|
|
|
|
|
if (strWalletPass.length() < 1) {
|
|
|
|
return NanThrowError(
|
|
|
|
"encryptwallet <passphrase>\n"
|
|
|
|
"Encrypts the wallet with <passphrase>.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pwalletMain->EncryptWallet(strWalletPass)) {
|
|
|
|
return NanThrowError("Error: Failed to encrypt the wallet.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// BDB seems to have a bad habit of writing old data into
|
|
|
|
// slack space in .dat files; that is bad if the old data is
|
|
|
|
// unencrypted private keys. So:
|
|
|
|
StartShutdown();
|
|
|
|
|
|
|
|
printf(
|
|
|
|
"bitcoind.js:"
|
2014-10-02 12:46:21 -07:00
|
|
|
" wallet encrypted; bitcoind.js stopping,"
|
2014-10-01 14:24:07 -07:00
|
|
|
" restart to run with encrypted wallet."
|
|
|
|
" The keypool has been flushed, you need"
|
|
|
|
" to make a new backup.\n"
|
|
|
|
);
|
|
|
|
|
2014-09-26 12:42:04 -07:00
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:26:46 -07:00
|
|
|
NAN_METHOD(WalletSetTxFee) {
|
2014-09-26 12:42:04 -07:00
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
2014-09-29 12:26:46 -07:00
|
|
|
"Usage: bitcoindjs.walletSetTxFee(options)");
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
|
|
|
|
2014-10-01 14:24:07 -07:00
|
|
|
int64_t fee = options->Get(NanNew<String>("fee"))->IntegerValue();
|
|
|
|
|
|
|
|
// Amount
|
|
|
|
int64_t nAmount = 0;
|
|
|
|
if (fee != 0.0) {
|
|
|
|
nAmount = fee;
|
|
|
|
}
|
|
|
|
|
|
|
|
nTransactionFee = nAmount;
|
|
|
|
|
|
|
|
NanReturnValue(True());
|
2014-09-26 12:42:04 -07:00
|
|
|
}
|
|
|
|
|
2014-10-01 16:00:21 -07:00
|
|
|
NAN_METHOD(WalletImportKey) {
|
|
|
|
NanScope();
|
|
|
|
|
|
|
|
if (args.Length() < 1 || !args[0]->IsObject()) {
|
|
|
|
return NanThrowError(
|
|
|
|
"Usage: bitcoindjs.walletImportKey(options, callback)");
|
|
|
|
}
|
|
|
|
|
|
|
|
async_import_key_data *data = new async_import_key_data();
|
|
|
|
|
|
|
|
Local<Object> options = Local<Object>::Cast(args[0]);
|
|
|
|
Local<Function> callback;
|
|
|
|
|
|
|
|
if (args.Length() > 1 && args[1]->IsFunction()) {
|
|
|
|
callback = Local<Function>::Cast(args[1]);
|
|
|
|
data->callback = Persistent<Function>::New(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string strSecret = "";
|
|
|
|
std::string strLabel = "";
|
|
|
|
|
|
|
|
String::Utf8Value key_(options->Get(NanNew<String>("key"))->ToString());
|
|
|
|
strSecret = std::string(*key_);
|
|
|
|
|
|
|
|
if (options->Get(NanNew<String>("label"))->IsString()) {
|
|
|
|
String::Utf8Value label_(options->Get(NanNew<String>("label"))->ToString());
|
|
|
|
strLabel = std::string(*label_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// EnsureWalletIsUnlocked();
|
|
|
|
if (pwalletMain->IsLocked()) {
|
|
|
|
return NanThrowError("Please enter the wallet passphrase with walletpassphrase first.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Whether to perform rescan after import
|
|
|
|
// data->fRescan = true;
|
|
|
|
data->fRescan = args.Length() > 1 && args[1]->IsFunction() ? true : false;
|
|
|
|
|
|
|
|
// if (options->Get(NanNew<String>("rescan"))->IsBoolean()
|
|
|
|
// && options->Get(NanNew<String>("rescan"))->IsFalse()) {
|
|
|
|
// data->fRescan = false;
|
|
|
|
// }
|
|
|
|
|
|
|
|
CBitcoinSecret vchSecret;
|
|
|
|
bool fGood = vchSecret.SetString(strSecret);
|
|
|
|
|
|
|
|
if (!fGood) {
|
|
|
|
return NanThrowError("Invalid private key encoding");
|
|
|
|
}
|
|
|
|
|
|
|
|
CKey key = vchSecret.GetKey();
|
|
|
|
if (!key.IsValid()) {
|
|
|
|
return NanThrowError("Private key outside allowed range");
|
|
|
|
}
|
|
|
|
|
|
|
|
CPubKey pubkey = key.GetPubKey();
|
|
|
|
CKeyID vchAddress = pubkey.GetID();
|
|
|
|
{
|
|
|
|
LOCK2(cs_main, pwalletMain->cs_wallet);
|
|
|
|
|
|
|
|
pwalletMain->MarkDirty();
|
|
|
|
pwalletMain->SetAddressBook(vchAddress, strLabel, "receive");
|
|
|
|
|
|
|
|
// Don't throw error in case a key is already there
|
|
|
|
if (pwalletMain->HaveKey(vchAddress)) {
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
pwalletMain->mapKeyMetadata[vchAddress].nCreateTime = 1;
|
|
|
|
|
|
|
|
if (!pwalletMain->AddKeyPubKey(key, pubkey)) {
|
|
|
|
return NanThrowError("Error adding key to wallet");
|
|
|
|
}
|
|
|
|
|
|
|
|
// whenever a key is imported, we need to scan the whole chain
|
|
|
|
pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'
|
|
|
|
|
|
|
|
// Do this on the threadpool instead.
|
2014-10-01 16:42:07 -07:00
|
|
|
// if (data->fRescan) {
|
2014-10-01 16:00:21 -07:00
|
|
|
// pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->fRescan) {
|
|
|
|
uv_work_t *req = new uv_work_t();
|
|
|
|
req->data = data;
|
|
|
|
|
|
|
|
int status = uv_queue_work(uv_default_loop(),
|
|
|
|
req, async_import_key,
|
|
|
|
(uv_after_work_cb)async_import_key_after);
|
|
|
|
|
|
|
|
assert(status == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
NanReturnValue(Undefined());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_import_key(uv_work_t *req) {
|
|
|
|
async_import_key_data* data = static_cast<async_import_key_data*>(req->data);
|
|
|
|
if (data->fRescan) {
|
|
|
|
// This may take a long time, do it on the libuv thread pool:
|
|
|
|
pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
async_import_key_after(uv_work_t *req) {
|
|
|
|
NanScope();
|
|
|
|
async_import_key_data* data = static_cast<async_import_key_data*>(req->data);
|
|
|
|
|
|
|
|
if (!data->err_msg.empty()) {
|
|
|
|
Local<Value> err = Exception::Error(String::New(data->err_msg.c_str()));
|
|
|
|
const unsigned argc = 1;
|
|
|
|
Local<Value> argv[argc] = { err };
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const unsigned argc = 2;
|
|
|
|
Local<Value> argv[argc] = {
|
|
|
|
Local<Value>::New(Null()),
|
|
|
|
Local<Value>::New(Null())
|
|
|
|
};
|
|
|
|
TryCatch try_catch;
|
|
|
|
data->callback->Call(Context::GetCurrent()->Global(), argc, argv);
|
|
|
|
if (try_catch.HasCaught()) {
|
|
|
|
node::FatalException(try_catch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data->callback.Dispose();
|
|
|
|
|
|
|
|
delete data;
|
|
|
|
delete req;
|
|
|
|
}
|
|
|
|
|
2014-09-23 10:14:35 -07:00
|
|
|
/**
|
|
|
|
* Conversions
|
|
|
|
*/
|
|
|
|
|
2014-09-23 13:15:59 -07:00
|
|
|
static inline void
|
2014-10-02 13:24:18 -07:00
|
|
|
cblock_to_jsblock(const CBlock& cblock, const CBlockIndex* cblock_index, Local<Object> jsblock) {
|
2014-10-02 13:13:54 -07:00
|
|
|
jsblock->Set(NanNew<String>("hash"), NanNew<String>(cblock.GetHash().GetHex().c_str()));
|
|
|
|
CMerkleTx txGen(cblock.vtx[0]);
|
|
|
|
txGen.SetMerkleBranch(&cblock);
|
|
|
|
jsblock->Set(NanNew<String>("confirmations"), NanNew<Number>((int)txGen.GetDepthInMainChain()));
|
|
|
|
jsblock->Set(NanNew<String>("size"), NanNew<Number>((int)::GetSerializeSize(cblock, SER_NETWORK, PROTOCOL_VERSION)));
|
2014-10-02 13:24:18 -07:00
|
|
|
jsblock->Set(NanNew<String>("height"), NanNew<Number>(cblock_index->nHeight));
|
2014-10-02 13:13:54 -07:00
|
|
|
jsblock->Set(NanNew<String>("version"), NanNew<Number>(cblock.nVersion));
|
|
|
|
jsblock->Set(NanNew<String>("merkleroot"), NanNew<String>(cblock.hashMerkleRoot.GetHex()));
|
2014-09-23 10:14:35 -07:00
|
|
|
|
2014-09-29 16:06:49 -07:00
|
|
|
// Build merkle tree
|
2014-10-02 13:13:54 -07:00
|
|
|
if (cblock.vMerkleTree.empty()) {
|
|
|
|
cblock.BuildMerkleTree();
|
2014-09-29 16:06:49 -07:00
|
|
|
}
|
|
|
|
Local<Array> merkle = NanNew<Array>();
|
|
|
|
int mi = 0;
|
2014-10-02 13:13:54 -07:00
|
|
|
BOOST_FOREACH(uint256& hash, cblock.vMerkleTree) {
|
2014-09-29 16:06:49 -07:00
|
|
|
merkle->Set(mi, NanNew<String>(hash.ToString()));
|
|
|
|
mi++;
|
|
|
|
}
|
2014-10-02 13:13:54 -07:00
|
|
|
jsblock->Set(NanNew<String>("merkletree"), merkle);
|
2014-09-29 16:06:49 -07:00
|
|
|
|
2014-09-23 10:14:35 -07:00
|
|
|
Local<Array> txs = NanNew<Array>();
|
|
|
|
int ti = 0;
|
2014-10-02 13:24:18 -07:00
|
|
|
BOOST_FOREACH(const CTransaction& ctx, cblock.vtx) {
|
|
|
|
Local<Object> jstx = NanNew<Object>();
|
2014-10-02 15:14:23 -07:00
|
|
|
|
|
|
|
// const uint256 block_hash = cblock.GetHash();
|
|
|
|
// ctx_to_jstx(ctx, block_hash, jstx);
|
|
|
|
|
|
|
|
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
ssTx << ctx;
|
|
|
|
std::string strHex = HexStr(ssTx.begin(), ssTx.end());
|
|
|
|
jstx->Set(NanNew<String>("hex"), NanNew<String>(strHex));
|
|
|
|
|
|
|
|
jstx->Set(NanNew<String>("txid"), NanNew<String>(ctx.GetHash().GetHex()));
|
2014-10-02 20:18:37 -07:00
|
|
|
jstx->Set(NanNew<String>("hash"), NanNew<String>(ctx.GetHash().GetHex()));
|
2014-10-02 15:14:23 -07:00
|
|
|
jstx->Set(NanNew<String>("version"), NanNew<Number>(ctx.nVersion));
|
|
|
|
jstx->Set(NanNew<String>("locktime"), NanNew<Number>(ctx.nLockTime));
|
|
|
|
|
|
|
|
Local<Array> vin = NanNew<Array>();
|
|
|
|
int vi = 0;
|
|
|
|
BOOST_FOREACH(const CTxIn& txin, ctx.vin) {
|
|
|
|
Local<Object> in = NanNew<Object>();
|
|
|
|
if (ctx.IsCoinBase()) {
|
|
|
|
in->Set(NanNew<String>("coinbase"), NanNew<String>(HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
|
|
|
} else {
|
|
|
|
in->Set(NanNew<String>("txid"), NanNew<String>(txin.prevout.hash.GetHex()));
|
|
|
|
in->Set(NanNew<String>("vout"), NanNew<Number>((boost::int64_t)txin.prevout.n));
|
|
|
|
Local<Object> o = NanNew<Object>();
|
|
|
|
o->Set(NanNew<String>("asm"), NanNew<String>(txin.scriptSig.ToString()));
|
|
|
|
o->Set(NanNew<String>("hex"), NanNew<String>(HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
|
|
|
in->Set(NanNew<String>("scriptSig"), o);
|
|
|
|
}
|
|
|
|
in->Set(NanNew<String>("sequence"), NanNew<Number>((boost::int64_t)txin.nSequence));
|
|
|
|
vin->Set(vi, in);
|
|
|
|
vi++;
|
|
|
|
}
|
|
|
|
jstx->Set(NanNew<String>("vin"), vin);
|
|
|
|
|
|
|
|
Local<Array> vout = NanNew<Array>();
|
|
|
|
for (unsigned int vo = 0; vo < ctx.vout.size(); vo++) {
|
|
|
|
const CTxOut& txout = ctx.vout[vo];
|
|
|
|
Local<Object> out = NanNew<Object>();
|
|
|
|
out->Set(NanNew<String>("value"), NanNew<Number>(txout.nValue));
|
|
|
|
out->Set(NanNew<String>("n"), NanNew<Number>((boost::int64_t)vo));
|
|
|
|
|
|
|
|
Local<Object> o = NanNew<Object>();
|
|
|
|
{
|
|
|
|
const CScript& scriptPubKey = txout.scriptPubKey;
|
|
|
|
Local<Object> out = o;
|
|
|
|
bool fIncludeHex = true;
|
|
|
|
|
|
|
|
txnouttype type;
|
|
|
|
vector<CTxDestination> addresses;
|
|
|
|
int nRequired;
|
|
|
|
out->Set(NanNew<String>("asm"), NanNew<String>(scriptPubKey.ToString()));
|
|
|
|
if (fIncludeHex) {
|
|
|
|
out->Set(NanNew<String>("hex"), NanNew<String>(HexStr(scriptPubKey.begin(), scriptPubKey.end())));
|
|
|
|
}
|
|
|
|
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
|
|
|
|
out->Set(NanNew<String>("type"), NanNew<String>(GetTxnOutputType(type)));
|
|
|
|
} else {
|
|
|
|
out->Set(NanNew<String>("reqSigs"), NanNew<Number>(nRequired));
|
|
|
|
out->Set(NanNew<String>("type"), NanNew<String>(GetTxnOutputType(type)));
|
|
|
|
Local<Array> a = NanNew<Array>();
|
|
|
|
int ai = 0;
|
|
|
|
BOOST_FOREACH(const CTxDestination& addr, addresses) {
|
|
|
|
a->Set(ai, NanNew<String>(CBitcoinAddress(addr).ToString()));
|
|
|
|
ai++;
|
|
|
|
}
|
|
|
|
out->Set(NanNew<String>("addresses"), a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out->Set(NanNew<String>("scriptPubKey"), o);
|
|
|
|
|
|
|
|
vout->Set(vo, out);
|
|
|
|
}
|
|
|
|
jstx->Set(NanNew<String>("vout"), vout);
|
|
|
|
|
|
|
|
{
|
|
|
|
const uint256 block_hash = cblock.GetHash();
|
|
|
|
if (block_hash != 0) {
|
|
|
|
jstx->Set(NanNew<String>("blockhash"), NanNew<String>(block_hash.GetHex()));
|
|
|
|
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block_hash);
|
|
|
|
if (mi != mapBlockIndex.end() && (*mi).second) {
|
|
|
|
CBlockIndex* cblock_index = (*mi).second;
|
|
|
|
if (chainActive.Contains(cblock_index)) {
|
|
|
|
jstx->Set(NanNew<String>("confirmations"),
|
|
|
|
NanNew<Number>(1 + chainActive.Height() - cblock_index->nHeight));
|
|
|
|
jstx->Set(NanNew<String>("time"), NanNew<Number>((boost::int64_t)cblock_index->nTime));
|
|
|
|
jstx->Set(NanNew<String>("blocktime"), NanNew<Number>((boost::int64_t)cblock_index->nTime));
|
|
|
|
} else {
|
|
|
|
jstx->Set(NanNew<String>("confirmations"), NanNew<Number>(0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
txs->Set(ti, jstx);
|
2014-09-23 10:14:35 -07:00
|
|
|
ti++;
|
|
|
|
}
|
2014-10-02 13:13:54 -07:00
|
|
|
jsblock->Set(NanNew<String>("tx"), txs);
|
2014-09-23 10:14:35 -07:00
|
|
|
|
2014-10-02 13:13:54 -07:00
|
|
|
jsblock->Set(NanNew<String>("time"), NanNew<Number>((boost::int64_t)cblock.GetBlockTime()));
|
|
|
|
jsblock->Set(NanNew<String>("nonce"), NanNew<Number>((boost::uint64_t)cblock.nNonce));
|
|
|
|
jsblock->Set(NanNew<String>("bits"), NanNew<Number>(cblock.nBits));
|
2014-10-02 13:24:18 -07:00
|
|
|
jsblock->Set(NanNew<String>("difficulty"), NanNew<Number>(GetDifficulty(cblock_index)));
|
|
|
|
jsblock->Set(NanNew<String>("chainwork"), NanNew<String>(cblock_index->nChainWork.GetHex()));
|
|
|
|
if (cblock_index->pprev) {
|
|
|
|
jsblock->Set(NanNew<String>("previousblockhash"), NanNew<String>(cblock_index->pprev->GetBlockHash().GetHex()));
|
2014-09-23 10:14:35 -07:00
|
|
|
}
|
2014-10-02 13:24:18 -07:00
|
|
|
CBlockIndex *pnext = chainActive.Next(cblock_index);
|
2014-09-23 10:14:35 -07:00
|
|
|
if (pnext) {
|
2014-10-02 13:13:54 -07:00
|
|
|
jsblock->Set(NanNew<String>("nextblockhash"), NanNew<String>(pnext->GetBlockHash().GetHex()));
|
2014-09-23 10:14:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-23 13:15:59 -07:00
|
|
|
static inline void
|
2014-10-02 13:24:18 -07:00
|
|
|
ctx_to_jstx(const CTransaction& ctx, uint256 block_hash, Local<Object> jstx) {
|
2014-09-25 14:12:09 -07:00
|
|
|
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
2014-10-02 13:13:54 -07:00
|
|
|
ssTx << ctx;
|
2014-09-29 12:18:29 -07:00
|
|
|
std::string strHex = HexStr(ssTx.begin(), ssTx.end());
|
2014-10-02 13:13:54 -07:00
|
|
|
jstx->Set(NanNew<String>("hex"), NanNew<String>(strHex));
|
2014-09-25 14:12:09 -07:00
|
|
|
|
2014-10-02 13:13:54 -07:00
|
|
|
jstx->Set(NanNew<String>("txid"), NanNew<String>(ctx.GetHash().GetHex()));
|
|
|
|
jstx->Set(NanNew<String>("version"), NanNew<Number>(ctx.nVersion));
|
|
|
|
jstx->Set(NanNew<String>("locktime"), NanNew<Number>(ctx.nLockTime));
|
2014-09-23 10:14:35 -07:00
|
|
|
|
|
|
|
Local<Array> vin = NanNew<Array>();
|
|
|
|
int vi = 0;
|
2014-10-02 13:13:54 -07:00
|
|
|
BOOST_FOREACH(const CTxIn& txin, ctx.vin) {
|
2014-09-23 10:14:35 -07:00
|
|
|
Local<Object> in = NanNew<Object>();
|
2014-10-02 13:13:54 -07:00
|
|
|
if (ctx.IsCoinBase()) {
|
2014-09-23 10:14:35 -07:00
|
|
|
in->Set(NanNew<String>("coinbase"), NanNew<String>(HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
|
|
|
} else {
|
|
|
|
in->Set(NanNew<String>("txid"), NanNew<String>(txin.prevout.hash.GetHex()));
|
|
|
|
in->Set(NanNew<String>("vout"), NanNew<Number>((boost::int64_t)txin.prevout.n));
|
|
|
|
Local<Object> o = NanNew<Object>();
|
|
|
|
o->Set(NanNew<String>("asm"), NanNew<String>(txin.scriptSig.ToString()));
|
|
|
|
o->Set(NanNew<String>("hex"), NanNew<String>(HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
|
|
|
in->Set(NanNew<String>("scriptSig"), o);
|
|
|
|
}
|
|
|
|
in->Set(NanNew<String>("sequence"), NanNew<Number>((boost::int64_t)txin.nSequence));
|
|
|
|
vin->Set(vi, in);
|
|
|
|
vi++;
|
|
|
|
}
|
2014-10-02 13:13:54 -07:00
|
|
|
jstx->Set(NanNew<String>("vin"), vin);
|
2014-09-23 10:14:35 -07:00
|
|
|
|
|
|
|
Local<Array> vout = NanNew<Array>();
|
2014-10-02 13:13:54 -07:00
|
|
|
for (unsigned int vo = 0; vo < ctx.vout.size(); vo++) {
|
|
|
|
const CTxOut& txout = ctx.vout[vo];
|
2014-09-23 10:14:35 -07:00
|
|
|
Local<Object> out = NanNew<Object>();
|
|
|
|
out->Set(NanNew<String>("value"), NanNew<Number>(txout.nValue));
|
|
|
|
out->Set(NanNew<String>("n"), NanNew<Number>((boost::int64_t)vo));
|
|
|
|
|
|
|
|
Local<Object> o = NanNew<Object>();
|
|
|
|
{
|
|
|
|
const CScript& scriptPubKey = txout.scriptPubKey;
|
|
|
|
Local<Object> out = o;
|
|
|
|
bool fIncludeHex = true;
|
|
|
|
|
|
|
|
txnouttype type;
|
|
|
|
vector<CTxDestination> addresses;
|
|
|
|
int nRequired;
|
|
|
|
out->Set(NanNew<String>("asm"), NanNew<String>(scriptPubKey.ToString()));
|
|
|
|
if (fIncludeHex) {
|
|
|
|
out->Set(NanNew<String>("hex"), NanNew<String>(HexStr(scriptPubKey.begin(), scriptPubKey.end())));
|
|
|
|
}
|
|
|
|
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
|
|
|
|
out->Set(NanNew<String>("type"), NanNew<String>(GetTxnOutputType(type)));
|
|
|
|
} else {
|
|
|
|
out->Set(NanNew<String>("reqSigs"), NanNew<Number>(nRequired));
|
|
|
|
out->Set(NanNew<String>("type"), NanNew<String>(GetTxnOutputType(type)));
|
|
|
|
Local<Array> a = NanNew<Array>();
|
|
|
|
int ai = 0;
|
|
|
|
BOOST_FOREACH(const CTxDestination& addr, addresses) {
|
|
|
|
a->Set(ai, NanNew<String>(CBitcoinAddress(addr).ToString()));
|
|
|
|
ai++;
|
|
|
|
}
|
|
|
|
out->Set(NanNew<String>("addresses"), a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out->Set(NanNew<String>("scriptPubKey"), o);
|
|
|
|
|
|
|
|
vout->Set(vo, out);
|
|
|
|
}
|
2014-10-02 13:13:54 -07:00
|
|
|
jstx->Set(NanNew<String>("vout"), vout);
|
2014-09-23 10:14:35 -07:00
|
|
|
|
2014-10-02 13:24:18 -07:00
|
|
|
if (block_hash != 0) {
|
|
|
|
jstx->Set(NanNew<String>("blockhash"), NanNew<String>(block_hash.GetHex()));
|
|
|
|
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block_hash);
|
2014-09-23 10:14:35 -07:00
|
|
|
if (mi != mapBlockIndex.end() && (*mi).second) {
|
2014-10-02 13:24:18 -07:00
|
|
|
CBlockIndex* cblock_index = (*mi).second;
|
|
|
|
if (chainActive.Contains(cblock_index)) {
|
2014-10-02 13:13:54 -07:00
|
|
|
jstx->Set(NanNew<String>("confirmations"),
|
2014-10-02 13:24:18 -07:00
|
|
|
NanNew<Number>(1 + chainActive.Height() - cblock_index->nHeight));
|
|
|
|
jstx->Set(NanNew<String>("time"), NanNew<Number>((boost::int64_t)cblock_index->nTime));
|
|
|
|
jstx->Set(NanNew<String>("blocktime"), NanNew<Number>((boost::int64_t)cblock_index->nTime));
|
2014-09-23 10:14:35 -07:00
|
|
|
} else {
|
2014-10-02 13:13:54 -07:00
|
|
|
jstx->Set(NanNew<String>("confirmations"), NanNew<Number>(0));
|
2014-09-23 10:14:35 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-30 14:53:19 -07:00
|
|
|
static inline void
|
2014-10-02 13:13:54 -07:00
|
|
|
jsblock_to_cblock(const Local<Object> jsblock, CBlock& cblock) {
|
2014-10-02 14:47:17 -07:00
|
|
|
cblock.nVersion = (int)jsblock->Get(NanNew<String>("version"))->IntegerValue();
|
2014-10-01 16:33:28 -07:00
|
|
|
|
2014-10-02 13:13:54 -07:00
|
|
|
String::AsciiValue mhash__(jsblock->Get(NanNew<String>("merkleroot"))->ToString());
|
2014-10-01 16:33:28 -07:00
|
|
|
std::string mhash_ = *mhash__;
|
|
|
|
if (mhash_[1] != 'x') mhash_ = "0x" + mhash_;
|
|
|
|
uint256 mhash(mhash_);
|
2014-10-02 13:07:28 -07:00
|
|
|
|
2014-10-02 14:47:17 -07:00
|
|
|
cblock.hashMerkleRoot = mhash;
|
|
|
|
cblock.nTime = (unsigned int)jsblock->Get(NanNew<String>("time"))->IntegerValue();
|
|
|
|
cblock.nNonce = (unsigned int)jsblock->Get(NanNew<String>("nonce"))->IntegerValue();
|
|
|
|
cblock.nBits = (unsigned int)jsblock->Get(NanNew<String>("bits"))->IntegerValue();
|
2014-10-02 13:07:28 -07:00
|
|
|
|
2014-10-02 13:13:54 -07:00
|
|
|
if (jsblock->Get(NanNew<String>("previousblockhash"))->IsString()) {
|
|
|
|
String::AsciiValue hash__(jsblock->Get(NanNew<String>("previousblockhash"))->ToString());
|
2014-10-02 13:07:28 -07:00
|
|
|
std::string hash_ = *hash__;
|
|
|
|
if (hash_[1] != 'x') hash_ = "0x" + hash_;
|
|
|
|
uint256 hash(hash_);
|
2014-10-02 14:47:17 -07:00
|
|
|
cblock.hashPrevBlock = hash;
|
2014-10-02 13:07:28 -07:00
|
|
|
} else {
|
2014-10-02 14:47:17 -07:00
|
|
|
// genesis block
|
|
|
|
uint256 hash(std::string("0x0000000000000000000000000000000000000000000000000000000000000000"));
|
|
|
|
cblock.hashPrevBlock = hash;
|
2014-10-02 13:07:28 -07:00
|
|
|
}
|
2014-10-01 16:33:28 -07:00
|
|
|
|
2014-10-02 14:47:17 -07:00
|
|
|
Local<Array> txs = Local<Array>::Cast(jsblock->Get(NanNew<String>("tx")));
|
|
|
|
for (unsigned int ti = 0; ti < txs->Length(); ti++) {
|
2014-10-02 13:24:18 -07:00
|
|
|
Local<Object> jstx = Local<Object>::Cast(txs->Get(ti));
|
|
|
|
CTransaction ctx;
|
2014-10-02 15:14:23 -07:00
|
|
|
|
|
|
|
// jstx_to_ctx(jstx, ctx);
|
|
|
|
|
|
|
|
ctx.nVersion = jstx->Get(NanNew<String>("version"))->IntegerValue();
|
|
|
|
ctx.nLockTime = jstx->Get(NanNew<String>("locktime"))->IntegerValue();
|
|
|
|
|
|
|
|
Local<Array> vin = Local<Array>::Cast(jstx->Get(NanNew<String>("vin")));
|
|
|
|
for (unsigned int vi = 0; vi < vin->Length(); vi++) {
|
|
|
|
CTxIn txin;
|
|
|
|
Local<Object> in = Local<Object>::Cast(vin->Get(vi));
|
|
|
|
|
|
|
|
std::string shash_;
|
|
|
|
if (in->Get(NanNew<String>("coinbase"))->IsString()) {
|
|
|
|
String::AsciiValue shash__(in->Get(NanNew<String>("coinbase"))->ToString());
|
|
|
|
shash_ = *shash__;
|
|
|
|
} else {
|
|
|
|
String::AsciiValue shash__(in->Get(NanNew<String>("scriptSig"))->ToString());
|
|
|
|
shash_ = *shash__;
|
|
|
|
}
|
|
|
|
if (shash_[1] != 'x') shash_ = "0x" + shash_;
|
|
|
|
uint256 shash(shash_);
|
|
|
|
CScript scriptSig(shash);
|
|
|
|
|
|
|
|
txin.scriptSig = scriptSig;
|
|
|
|
|
|
|
|
String::AsciiValue phash__(in->Get(NanNew<String>("txid"))->ToString());
|
|
|
|
std::string phash_ = *phash__;
|
|
|
|
if (phash_[1] != 'x') phash_ = "0x" + phash_;
|
|
|
|
uint256 phash(phash_);
|
|
|
|
|
|
|
|
txin.prevout.hash = phash;
|
|
|
|
txin.prevout.n = (boost::int64_t)in->Get(NanNew<String>("vout"))->IntegerValue();
|
|
|
|
txin.nSequence = (boost::int64_t)in->Get(NanNew<String>("sequence"))->IntegerValue();
|
|
|
|
|
|
|
|
ctx.vin.push_back(txin);
|
|
|
|
}
|
|
|
|
|
|
|
|
Local<Array> vout = Local<Array>::Cast(jstx->Get(NanNew<String>("vout")));
|
|
|
|
for (unsigned int vo = 0; vo < vout->Length(); vo++) {
|
|
|
|
CTxOut txout;
|
|
|
|
Local<Object> out = Local<Object>::Cast(vout->Get(vo));
|
|
|
|
|
|
|
|
txout.nValue = (int64_t)out->Get(NanNew<String>("value"))->IntegerValue();
|
|
|
|
|
|
|
|
Local<Object> spk = Local<Object>::Cast(out->Get(NanNew<String>("scriptPubKey")));
|
|
|
|
String::AsciiValue phash__(spk->Get(NanNew<String>("hex")));
|
|
|
|
std::string phash_ = *phash__;
|
|
|
|
if (phash_[1] != 'x') phash_ = "0x" + phash_;
|
|
|
|
uint256 phash(phash_);
|
|
|
|
CScript scriptPubKey(phash);
|
|
|
|
|
|
|
|
txout.scriptPubKey = scriptPubKey;
|
|
|
|
|
|
|
|
ctx.vout.push_back(txout);
|
|
|
|
}
|
|
|
|
|
2014-10-02 14:47:17 -07:00
|
|
|
cblock.vtx.push_back(ctx);
|
2014-09-30 14:53:19 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
2014-10-02 13:13:54 -07:00
|
|
|
jstx_to_ctx(const Local<Object> jstx, CTransaction& ctx) {
|
2014-10-02 14:47:17 -07:00
|
|
|
ctx.nVersion = jstx->Get(NanNew<String>("version"))->IntegerValue();
|
|
|
|
ctx.nLockTime = jstx->Get(NanNew<String>("locktime"))->IntegerValue();
|
2014-09-30 14:53:19 -07:00
|
|
|
|
2014-10-02 14:47:17 -07:00
|
|
|
Local<Array> vin = Local<Array>::Cast(jstx->Get(NanNew<String>("vin")));
|
|
|
|
for (unsigned int vi = 0; vi < vin->Length(); vi++) {
|
2014-10-01 16:42:07 -07:00
|
|
|
CTxIn txin;
|
|
|
|
Local<Object> in = Local<Object>::Cast(vin->Get(vi));
|
2014-10-02 13:06:08 -07:00
|
|
|
|
2014-10-02 14:47:17 -07:00
|
|
|
std::string shash_;
|
2014-10-01 16:42:07 -07:00
|
|
|
if (in->Get(NanNew<String>("coinbase"))->IsString()) {
|
2014-10-02 14:47:17 -07:00
|
|
|
String::AsciiValue shash__(in->Get(NanNew<String>("coinbase"))->ToString());
|
|
|
|
shash_ = *shash__;
|
2014-09-30 14:53:19 -07:00
|
|
|
} else {
|
2014-10-02 14:47:17 -07:00
|
|
|
String::AsciiValue shash__(in->Get(NanNew<String>("scriptSig"))->ToString());
|
|
|
|
shash_ = *shash__;
|
2014-09-30 14:53:19 -07:00
|
|
|
}
|
2014-10-02 14:47:17 -07:00
|
|
|
if (shash_[1] != 'x') shash_ = "0x" + shash_;
|
|
|
|
uint256 shash(shash_);
|
2014-10-01 16:42:07 -07:00
|
|
|
CScript scriptSig(shash);
|
2014-10-02 13:06:08 -07:00
|
|
|
|
2014-10-01 16:42:07 -07:00
|
|
|
txin.scriptSig = scriptSig;
|
2014-10-02 13:06:08 -07:00
|
|
|
|
2014-10-01 16:42:07 -07:00
|
|
|
String::AsciiValue phash__(in->Get(NanNew<String>("txid"))->ToString());
|
|
|
|
std::string phash_ = *phash__;
|
|
|
|
if (phash_[1] != 'x') phash_ = "0x" + phash_;
|
2014-10-02 13:06:08 -07:00
|
|
|
|
2014-10-01 16:42:07 -07:00
|
|
|
uint256 phash(phash_);
|
2014-10-02 13:06:08 -07:00
|
|
|
|
2014-10-01 16:42:07 -07:00
|
|
|
txin.prevout.hash = phash;
|
|
|
|
txin.prevout.n = (boost::int64_t)in->Get(NanNew<String>("vout"))->IntegerValue();
|
|
|
|
txin.nSequence = (boost::int64_t)in->Get(NanNew<String>("sequence"))->IntegerValue();
|
|
|
|
|
2014-10-02 14:47:17 -07:00
|
|
|
ctx.vin.push_back(txin);
|
2014-09-30 14:53:19 -07:00
|
|
|
}
|
|
|
|
|
2014-10-02 14:47:17 -07:00
|
|
|
Local<Array> vout = Local<Array>::Cast(jstx->Get(NanNew<String>("vout")));
|
2014-10-01 15:14:20 -07:00
|
|
|
for (unsigned int vo = 0; vo < vout->Length(); vo++) {
|
2014-10-02 13:06:08 -07:00
|
|
|
CTxOut txout;
|
2014-10-01 16:42:07 -07:00
|
|
|
Local<Object> out = Local<Object>::Cast(vout->Get(vo));
|
2014-09-30 14:53:19 -07:00
|
|
|
|
2014-10-01 16:42:07 -07:00
|
|
|
txout.nValue = (int64_t)out->Get(NanNew<String>("value"))->IntegerValue();
|
2014-09-30 14:53:19 -07:00
|
|
|
|
2014-10-02 14:47:17 -07:00
|
|
|
Local<Object> spk = Local<Object>::Cast(out->Get(NanNew<String>("scriptPubKey")));
|
2014-10-01 16:42:07 -07:00
|
|
|
String::AsciiValue phash__(spk->Get(NanNew<String>("hex")));
|
|
|
|
std::string phash_ = *phash__;
|
|
|
|
if (phash_[1] != 'x') phash_ = "0x" + phash_;
|
|
|
|
uint256 phash(phash_);
|
2014-10-02 14:47:17 -07:00
|
|
|
CScript scriptPubKey(phash);
|
2014-09-30 14:53:19 -07:00
|
|
|
|
2014-10-01 16:42:07 -07:00
|
|
|
txout.scriptPubKey = scriptPubKey;
|
|
|
|
|
2014-10-02 14:47:17 -07:00
|
|
|
ctx.vout.push_back(txout);
|
2014-09-30 14:53:19 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-12 12:03:04 -07:00
|
|
|
/**
|
|
|
|
* Init
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern "C" void
|
|
|
|
init(Handle<Object> target) {
|
|
|
|
NanScope();
|
2014-09-29 13:38:27 -07:00
|
|
|
|
|
|
|
bitcoindjs_obj = target;
|
|
|
|
|
2014-08-12 12:03:04 -07:00
|
|
|
NODE_SET_METHOD(target, "start", StartBitcoind);
|
2014-09-11 17:18:36 -07:00
|
|
|
NODE_SET_METHOD(target, "stop", StopBitcoind);
|
2014-09-17 14:08:26 -07:00
|
|
|
NODE_SET_METHOD(target, "stopping", IsStopping);
|
2014-09-17 12:52:35 -07:00
|
|
|
NODE_SET_METHOD(target, "stopped", IsStopped);
|
2014-09-18 15:32:19 -07:00
|
|
|
NODE_SET_METHOD(target, "getBlock", GetBlock);
|
2014-09-22 10:19:37 -07:00
|
|
|
NODE_SET_METHOD(target, "getTx", GetTx);
|
2014-09-22 17:58:59 -07:00
|
|
|
NODE_SET_METHOD(target, "pollBlocks", PollBlocks);
|
2014-09-23 13:57:49 -07:00
|
|
|
NODE_SET_METHOD(target, "pollMempool", PollMempool);
|
2014-09-25 12:05:39 -07:00
|
|
|
NODE_SET_METHOD(target, "broadcastTx", BroadcastTx);
|
2014-09-26 11:23:21 -07:00
|
|
|
NODE_SET_METHOD(target, "verifyBlock", VerifyBlock);
|
2014-09-26 11:34:55 -07:00
|
|
|
NODE_SET_METHOD(target, "verifyTransaction", VerifyTransaction);
|
2014-09-29 14:28:31 -07:00
|
|
|
NODE_SET_METHOD(target, "fillTransaction", FillTransaction);
|
2014-09-26 12:42:04 -07:00
|
|
|
|
2014-09-26 12:20:00 -07:00
|
|
|
NODE_SET_METHOD(target, "walletNewAddress", WalletNewAddress);
|
2014-09-29 12:26:46 -07:00
|
|
|
NODE_SET_METHOD(target, "walletGetAccountAddress", WalletGetAccountAddress);
|
|
|
|
NODE_SET_METHOD(target, "walletSetAccount", WalletSetAccount);
|
|
|
|
NODE_SET_METHOD(target, "walletGetAccount", WalletGetAccount);
|
2014-09-29 11:59:57 -07:00
|
|
|
NODE_SET_METHOD(target, "walletSendTo", WalletSendTo);
|
2014-09-29 12:26:46 -07:00
|
|
|
NODE_SET_METHOD(target, "walletSignMessage", WalletSignMessage);
|
|
|
|
NODE_SET_METHOD(target, "walletVerifyMessage", WalletVerifyMessage);
|
|
|
|
NODE_SET_METHOD(target, "walletGetBalance", WalletGetBalance);
|
2014-10-01 13:11:07 -07:00
|
|
|
NODE_SET_METHOD(target, "walletCreateMultiSigAddress", WalletCreateMultiSigAddress);
|
2014-09-29 12:26:46 -07:00
|
|
|
NODE_SET_METHOD(target, "walletGetUnconfirmedBalance", WalletGetUnconfirmedBalance);
|
|
|
|
NODE_SET_METHOD(target, "walletSendFrom", WalletSendFrom);
|
|
|
|
NODE_SET_METHOD(target, "walletListTransactions", WalletListTransactions);
|
|
|
|
NODE_SET_METHOD(target, "walletListAccounts", WalletListAccounts);
|
|
|
|
NODE_SET_METHOD(target, "walletGetTransaction", WalletGetTransaction);
|
|
|
|
NODE_SET_METHOD(target, "walletBackup", WalletBackup);
|
2014-09-26 12:42:04 -07:00
|
|
|
NODE_SET_METHOD(target, "walletPassphrase", WalletPassphrase);
|
|
|
|
NODE_SET_METHOD(target, "walletPassphraseChange", WalletPassphraseChange);
|
|
|
|
NODE_SET_METHOD(target, "walletLock", WalletLock);
|
2014-09-29 12:26:46 -07:00
|
|
|
NODE_SET_METHOD(target, "walletEncrypt", WalletEncrypt);
|
|
|
|
NODE_SET_METHOD(target, "walletSetTxFee", WalletSetTxFee);
|
2014-10-01 16:00:21 -07:00
|
|
|
NODE_SET_METHOD(target, "walletImportKey", WalletImportKey);
|
2014-08-12 12:03:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NODE_MODULE(bitcoindjs, init)
|