From bf75025a3aa4f51e37b1b53b158250d7dd47b828 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 1 Oct 2014 16:00:21 -0700 Subject: [PATCH] add importprivkey. --- lib/bitcoind.js | 4 ++ src/bitcoindjs.cc | 157 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/lib/bitcoind.js b/lib/bitcoind.js index 7a63db29..db31ff8a 100644 --- a/lib/bitcoind.js +++ b/lib/bitcoind.js @@ -822,6 +822,10 @@ Wallet.prototype.setTxFee = function(options) { return bitcoindjs.walletSetTxFee(options || {}); }; +Wallet.prototype.importKey = function(options) { + return bitcoindjs.walletImportKey(options || {}); +}; + Wallet = new Wallet; /** diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index ef86d065..101a14a3 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -163,6 +163,7 @@ NAN_METHOD(WalletPassphraseChange); NAN_METHOD(WalletLock); NAN_METHOD(WalletEncrypt); NAN_METHOD(WalletSetTxFee); +NAN_METHOD(WalletImportKey); static void async_start_node_work(uv_work_t *req); @@ -224,6 +225,12 @@ async_wallet_sendfrom(uv_work_t *req); static void async_wallet_sendfrom_after(uv_work_t *req); +static void +async_import_key(uv_work_t *req); + +static void +async_import_key_after(uv_work_t *req); + static inline void ctx_to_jstx(const CTransaction& tx, uint256 hashBlock, Local entry); @@ -356,6 +363,16 @@ struct async_wallet_sendfrom_data { Persistent callback; }; +/** + * async_import_key_data + */ + +struct async_import_key_data { + std::string err_msg; + bool fRescan; + Persistent callback; +}; + /** * StartBitcoind * bitcoind.start(callback) @@ -2273,6 +2290,145 @@ NAN_METHOD(WalletSetTxFee) { NanReturnValue(True()); } +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 options = Local::Cast(args[0]); + Local callback; + + if (args.Length() > 1 && args[1]->IsFunction()) { + callback = Local::Cast(args[1]); + data->callback = Persistent::New(callback); + } + + std::string strSecret = ""; + std::string strLabel = ""; + + String::Utf8Value key_(options->Get(NanNew("key"))->ToString()); + strSecret = std::string(*key_); + + if (options->Get(NanNew("label"))->IsString()) { + String::Utf8Value label_(options->Get(NanNew("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("rescan"))->IsBoolean() + // && options->Get(NanNew("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. + // if (fRescan) { + // 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(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(req->data); + + if (!data->err_msg.empty()) { + Local err = Exception::Error(String::New(data->err_msg.c_str())); + const unsigned argc = 1; + Local 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 argv[argc] = { + Local::New(Null()), + Local::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; +} + /** * Conversions */ @@ -2740,6 +2896,7 @@ init(Handle target) { NODE_SET_METHOD(target, "walletLock", WalletLock); NODE_SET_METHOD(target, "walletEncrypt", WalletEncrypt); NODE_SET_METHOD(target, "walletSetTxFee", WalletSetTxFee); + NODE_SET_METHOD(target, "walletImportKey", WalletImportKey); } NODE_MODULE(bitcoindjs, init)