diff --git a/binding.gyp b/binding.gyp index 22b905fb..e4227f29 100644 --- a/binding.gyp +++ b/binding.gyp @@ -41,6 +41,7 @@ 'cflags_cc': [ '-fexceptions', '-frtti', + '-fpermissive', ], 'libraries': [ # standard libs: diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index 0b936329..79ec003d 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -14,6 +14,7 @@ * Bitcoin headers */ +#if V090 #if defined(HAVE_CONFIG_H) #include "bitcoin-config.h" #endif @@ -51,6 +52,7 @@ #include "init.h" #include "netbase.h" #include "rpcserver.h" +// new: #include "rpcwallet.h" #include "txmempool.h" #include "bloom.h" @@ -77,6 +79,105 @@ #include "protocol.h" #include "threadsafety.h" #include "version.h" +#endif + +#if !V090 +#if defined(HAVE_CONFIG_H) +#include "bitcoin-config.h" +#endif + +#include "addrman.h" +#include "alert.h" +#include "allocators.h" +#include "amount.h" +#include "base58.h" +#include "bloom.h" +#include "bitcoind.h" +#include "chain.h" +#include "chainparams.h" +#include "chainparamsbase.h" +// #include "chainparamsseeds.h" +#include "checkpoints.h" +#include "checkqueue.h" +#include "clientversion.h" +#include "coincontrol.h" +#include "coins.h" +#include "compat.h" +#include "core.h" +#include "core_io.h" +#include "crypter.h" +// #include "db.h" +#include "hash.h" +#include "init.h" +#include "key.h" +#include "keystore.h" +#include "leveldbwrapper.h" +#include "limitedmap.h" +#include "main.h" +#include "miner.h" +#include "mruset.h" +#include "netbase.h" +#include "net.h" +#include "noui.h" +#include "pow.h" +#include "protocol.h" +#include "random.h" +#include "rpcclient.h" +#include "rpcprotocol.h" +#include "rpcserver.h" +// new: +#include "rpcwallet.h" +#include "script/compressor.h" +#include "script/interpreter.h" +#include "script/script.h" +#include "script/sigcache.h" +#include "script/sign.h" +#include "script/standard.h" +#include "serialize.h" +#include "sync.h" +#include "threadsafety.h" +#include "timedata.h" +#include "tinyformat.h" +#include "txdb.h" +#include "txmempool.h" +#include "ui_interface.h" +#include "uint256.h" +#include "util.h" +#include "utilstrencodings.h" +#include "utilmoneystr.h" +#include "utiltime.h" +#include "version.h" +// #include "wallet.h" +#include "wallet_ismine.h" +// #include "walletdb.h" +#include "compat/sanity.h" + +#ifdef ENABLE_WALLET +#include "db.h" +#include "wallet.h" +#include "walletdb.h" +#endif + +#include "json/json_spirit.h" +#include "json/json_spirit_error_position.h" +#include "json/json_spirit_reader.h" +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_stream_reader.h" +#include "json/json_spirit_utils.h" +#include "json/json_spirit_value.h" +#include "json/json_spirit_writer.h" +#include "json/json_spirit_writer_template.h" + +// #include "obj/build.h" + +#include "crypto/common.h" +#include "crypto/sha2.h" +#include "crypto/sha1.h" +#include "crypto/ripemd160.h" + +#include "univalue/univalue_escapes.h" +#include "univalue/univalue.h" +#endif /** * Bitcoin Globals @@ -299,17 +400,6 @@ struct async_tx_data { Persistent callback; }; -/** - * async_poll_blocks_data - */ - -struct async_poll_blocks_data { - std::string err_msg; - poll_blocks_list *head; - Persistent result_array; - Persistent callback; -}; - /** * poll_blocks_list * A singly linked list containing any polled CBlocks and CBlockIndexes. @@ -322,6 +412,17 @@ typedef struct _poll_blocks_list { struct _poll_blocks_list *next; } poll_blocks_list; +/** + * async_poll_blocks_data + */ + +struct async_poll_blocks_data { + std::string err_msg; + poll_blocks_list *head; + Persistent result_array; + Persistent callback; +}; + /** * async_poll_mempool_data */ @@ -1174,10 +1275,18 @@ async_broadcast_tx(uv_work_t *req) { return; } } else { +#if V090 SyncWithWallets(hashTx, ctx, NULL); +#else + ; // this is done automatically now with AcceptToMemoryPool? +#endif } +#if V090 RelayTransaction(ctx, hashTx); +#else + RelayTransaction(ctx); +#endif data->tx_hash = hashTx.GetHex(); } @@ -1332,19 +1441,39 @@ NAN_METHOD(FillTransaction) { return NanThrowError("Insufficient funds"); } +#if V090 // Fill inputs if they aren't already filled ctx.vin.clear(); - BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) { + BOOST_FOREACH(const PAIRTYPE(const CWalletTx*, unsigned int)& coin, setCoins) { ctx.vin.push_back(CTxIn(coin.first->GetHash(), coin.second)); } // Sign everything int nIn = 0; - BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) { + BOOST_FOREACH(const PAIRTYPE(const CWalletTx*, unsigned int)& coin, setCoins) { if (!SignSignature(*pwalletMain, *coin.first, ctx, nIn++)) { return NanThrowError("Signing transaction failed"); } } +#else + // Fill vin + BOOST_FOREACH(const PAIRTYPE(const CWalletTx*, unsigned int)& coin, setCoins) { + ctx.vin.push_back(CTxIn(coin.first->GetHash(), coin.second)); + } + + // Sign + int nIn = 0; + BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) { + if (!SignSignature( + (const CKeyStore&)*pwalletMain, + (const CTransaction&)*coin.first, + (CMutableTransaction&)ctx, + nIn++) + ) { + return NanThrowError("Signing transaction failed"); + } + } +#endif // Turn our CTransaction into a javascript Transaction Local new_jstx = NanNew(); @@ -1524,6 +1653,7 @@ NAN_METHOD(WalletNewAddress) { NanReturnValue(NanNew(CBitcoinAddress(keyID).ToString().c_str())); } +#if 1 // NOTE: This function was ripped out of the bitcoin core source. It needed to // be modified to fit v8's error handling. CBitcoinAddress GetAccountAddress(std::string strAccount, bool bForceNew=false) { @@ -1536,8 +1666,49 @@ CBitcoinAddress GetAccountAddress(std::string strAccount, bool bForceNew=false) // Check if the current key has been used if (account.vchPubKey.IsValid()) { +#if V090 CScript scriptPubKey; scriptPubKey.SetDestination(account.vchPubKey.GetID()); +#else + CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID()); +#endif + for (map::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)) { + NanThrowError("Keypool ran out, please call keypoolrefill first"); + CBitcoinAddress addr; + return addr; + } + pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); + walletdb.WriteAccount(strAccount, account); + } + + return CBitcoinAddress(account.vchPubKey.GetID()); +} +#else +CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) { + 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 = GetScriptForDestination(account.vchPubKey.GetID()); for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid(); ++it) { @@ -1559,12 +1730,14 @@ CBitcoinAddress GetAccountAddress(std::string strAccount, bool bForceNew=false) //addr.Set(dest); return addr; } + pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); walletdb.WriteAccount(strAccount, account); } return CBitcoinAddress(account.vchPubKey.GetID()); } +#endif /** * WalletGetAccountAddress() @@ -1742,7 +1915,11 @@ async_wallet_sendto(uv_work_t *req) { return; } +#if V090 std::string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); +#else + std::string strError = pwalletMain->SendMoney(address.Get(), nAmount, wtx); +#endif if (strError != "") { data->err_msg = strError; return; @@ -1899,6 +2076,75 @@ NAN_METHOD(WalletVerifyMessage) { * Create a multisig address for the global wallet. */ +#if !V090 +CScript _createmultisig_redeemScript(int nRequired, Local keys) { + // Gather public keys + if (nRequired < 1) { + throw runtime_error("a multisignature address must require at least one key to redeem"); + } + if ((int)keys->Length() < nRequired) { + NanThrowError("not enough keys supplied"); + CScript s; + return s; + } + std::vector pubkeys; + pubkeys.resize(keys->Length()); + for (unsigned int i = 0; i < keys->Length(); i++) { + 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)) { + NanThrowError("does not refer to a key"); + CScript s; + return s; + } + CPubKey vchPubKey; + if (!pwalletMain->GetPubKey(keyID, vchPubKey)) { + NanThrowError("no full public key for address"); + CScript s; + return s; + } + if (!vchPubKey.IsFullyValid()) { + NanThrowError("Invalid public key"); + CScript s; + return s; + } + pubkeys[i] = vchPubKey; + } + + // Case 2: hex public key + else +#endif + if (IsHex(ks)) { + CPubKey vchPubKey(ParseHex(ks)); + if (!vchPubKey.IsFullyValid()) { + NanThrowError("Invalid public key"); + CScript s; + return s; + } + pubkeys[i] = vchPubKey; + } else { + NanThrowError("Invalid public key"); + CScript s; + return s; + } + } + CScript result = GetScriptForMultisig(nRequired, pubkeys); + + if (result.size() > MAX_SCRIPT_ELEMENT_SIZE) { + NanThrowError("redeemScript exceeds size limit"); + CScript s; + return s; + } + + return result; +} +#endif + NAN_METHOD(WalletCreateMultiSigAddress) { NanScope(); @@ -1920,12 +2166,13 @@ NAN_METHOD(WalletCreateMultiSigAddress) { 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)", + "not enough keys supplied (got %u keys, but need at least %u to redeem)", keys->Length(), nRequired); NanThrowError(s); NanReturnValue(Undefined()); } +#if V090 std::vector pubkeys; pubkeys.resize(keys->Length()); @@ -1962,8 +2209,15 @@ NAN_METHOD(WalletCreateMultiSigAddress) { return NanThrowError((std::string("Invalid public key: ") + ks).c_str()); } } + #if V090 CScript inner; inner.SetMultisig(nRequired, pubkeys); + #else + CScript inner = GetScriptForMultisig(nRequired, pubkeys); + #endif +#else + CScript inner = _createmultisig_redeemScript(nRequired, keys); +#endif // Construct using pay-to-script-hash: CScriptID innerID = inner.GetID(); @@ -2023,7 +2277,9 @@ NAN_METHOD(WalletGetBalance) { string strSentAccount; list > listReceived; list > listSent; +#if V090 wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount); +#endif if (wtx.GetDepthInMainChain() >= nMinDepth) { BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived) { nBalance += r.second; @@ -2037,9 +2293,13 @@ NAN_METHOD(WalletGetBalance) { NanReturnValue(NanNew(nBalance)); } +#if V090 int64_t nBalance = GetAccountBalance(strAccount, nMinDepth); - NanReturnValue(NanNew(nBalance)); +#else + double nBalance = (double)GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + NanReturnValue(NanNew((int64_t)(nBalance * 100000000))); +#endif } /** @@ -2140,14 +2400,26 @@ async_wallet_sendfrom(uv_work_t *req) { } // Check funds +#if V090 int64_t nBalance = GetAccountBalance(strAccount, nMinDepth); if (nAmount > nBalance) { data->err_msg = std::string("Account has insufficient funds"); return; } +#else + double nBalance = (double)GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + if (((double)(nAmount * 1.0) / 100000000) > nBalance) { + data->err_msg = std::string("Account has insufficient funds"); + return; + } +#endif // Send +#if V090 std::string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); +#else + std::string strError = pwalletMain->SendMoney(address.Get(), nAmount, wtx); +#endif if (strError != "") { data->err_msg = strError; return; @@ -2231,13 +2503,20 @@ NAN_METHOD(WalletListAccounts) { nMinDepth = options->Get(NanNew("minDepth"))->IntegerValue(); } + isminefilter includeWatchonly = ISMINE_SPENDABLE; + map mapAccountBalances; BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) { +#if V090 if (IsMine(*pwalletMain, entry.first)) { // This address belongs to me +#else + if (IsMine(*pwalletMain, entry.first) & includeWatchonly) { // This address belongs to me +#endif mapAccountBalances[entry.second.name] = 0; } } +#if V090 for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; @@ -2264,6 +2543,34 @@ NAN_METHOD(WalletListAccounts) { } } } +#else + for (map::iterator it = pwalletMain->mapWallet.begin(); + it != pwalletMain->mapWallet.end(); ++it) { + const CWalletTx& wtx = (*it).second; + CAmount nFee; + std::string strSentAccount; + list listReceived; + list listSent; + int nDepth = wtx.GetDepthInMainChain(); + if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) { + continue; + } + wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); + mapAccountBalances[strSentAccount] -= nFee; + BOOST_FOREACH(const COutputEntry& s, listSent) { + mapAccountBalances[strSentAccount] -= s.amount; + } + if (nDepth >= nMinDepth) { + BOOST_FOREACH(const COutputEntry& r, listReceived) { + if (pwalletMain->mapAddressBook.count(r.destination)) { + mapAccountBalances[pwalletMain->mapAddressBook[r.destination].name] += r.amount; + } else { + mapAccountBalances[""] += r.amount; + } + } + } + } +#endif list acentries; CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); @@ -2715,7 +3022,11 @@ static inline void cblock_to_jsblock(const CBlock& cblock, const CBlockIndex* cblock_index, Local jsblock) { jsblock->Set(NanNew("hash"), NanNew(cblock.GetHash().GetHex().c_str())); CMerkleTx txGen(cblock.vtx[0]); +#if V090 txGen.SetMerkleBranch(&cblock); +#else + txGen.SetMerkleBranch(cblock); +#endif jsblock->Set(NanNew("confirmations"), NanNew((int)txGen.GetDepthInMainChain())->ToInt32()); jsblock->Set(NanNew("size"), NanNew((int)::GetSerializeSize(cblock, SER_NETWORK, PROTOCOL_VERSION))->ToInt32()); @@ -2773,8 +3084,10 @@ cblock_to_jsblock(const CBlock& cblock, const CBlockIndex* cblock_index, Local jstx) { +#if V090 jstx->Set(NanNew("mintxfee"), NanNew((int64_t)ctx.nMinTxFee)->ToInteger()); jstx->Set(NanNew("minrelaytxfee"), NanNew((int64_t)ctx.nMinRelayTxFee)->ToInteger()); +#endif jstx->Set(NanNew("current_version"), NanNew((int)ctx.CURRENT_VERSION)->ToInt32()); jstx->Set(NanNew("txid"), NanNew(ctx.GetHash().GetHex())); @@ -2853,6 +3166,7 @@ ctx_to_jstx(const CTransaction& ctx, uint256 block_hash, Local jstx) { if (block_hash != 0) { jstx->Set(NanNew("blockhash"), NanNew(block_hash.GetHex())); +#if V090 map::iterator mi = mapBlockIndex.find(block_hash); if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* cblock_index = (*mi).second; @@ -2865,6 +3179,27 @@ ctx_to_jstx(const CTransaction& ctx, uint256 block_hash, Local jstx) { jstx->Set(NanNew("confirmations"), NanNew(0)); } } +#else + CWalletTx cwtx(pwalletMain, ctx); + int confirms = cwtx.GetDepthInMainChain(); + jstx->Set(NanNew("confirmations"), NanNew(confirms)); + if (ctx.IsCoinBase()) { + jstx->Set(NanNew("generated"), NanNew(true)); + } + if (confirms > 0) { + jstx->Set(NanNew("blockhash"), NanNew(cwtx.hashBlock.GetHex())); + jstx->Set(NanNew("blockindex"), NanNew(cwtx.nIndex)); + jstx->Set(NanNew("blocktime"), NanNew(mapBlockIndex[cwtx.hashBlock]->GetBlockTime())); + } + Local conflicts = NanNew(); + int co = 0; + BOOST_FOREACH(const uint256& conflict, cwtx.GetConflicts()) { + conflicts->Set(co++, NanNew(conflict.GetHex())); + } + jstx->Set(NanNew("walletconflicts"), conflicts); + jstx->Set(NanNew("time"), NanNew(cwtx.GetTxTime())); + jstx->Set(NanNew("timereceived"), NanNew((int64_t)cwtx.nTimeReceived)); +#endif } CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); @@ -2938,21 +3273,31 @@ jsblock_to_cblock(const Local jsblock, CBlock& cblock) { // workaround by carrying the original hex value on the object which is changed // when the tx is changed. static inline void -jstx_to_ctx(const Local jstx, CTransaction& ctx) { +jstx_to_ctx(const Local jstx, CTransaction& ctx_) { String::AsciiValue hex_string_(jstx->Get(NanNew("hex"))->ToString()); std::string hex_string = *hex_string_; CDataStream ssData(ParseHex(hex_string), SER_NETWORK, PROTOCOL_VERSION); try { - ssData >> ctx; + ssData >> ctx_; } catch (std::exception &e) { NanThrowError("Bad TX decode"); } return; +#if V090 + CTransaction& ctx = (CTransaction&)ctx_; +#else + CMutableTransaction& ctx = (CMutableTransaction&)ctx_; +#endif + +#if V090 ctx.nMinTxFee = (int64_t)jstx->Get(NanNew("mintxfee"))->IntegerValue(); ctx.nMinRelayTxFee = (int64_t)jstx->Get(NanNew("minrelaytxfee"))->IntegerValue(); +#else + ; // these properties don't exist in v2 +#endif // ctx.CURRENT_VERSION = (unsigned int)jstx->Get(NanNew("current_version"))->Int32Value(); ctx.nVersion = (int)jstx->Get(NanNew("version"))->Int32Value(); @@ -2991,6 +3336,7 @@ jstx_to_ctx(const Local jstx, CTransaction& ctx) { Local vout = Local::Cast(jstx->Get(NanNew("vout"))); for (unsigned int vo = 0; vo < vout->Length(); vo++) { CTxOut txout; + Local out = Local::Cast(vout->Get(vo)); int64_t nValue = (int64_t)out->Get(NanNew("value"))->IntegerValue(); diff --git a/src/bitcoindjs.h b/src/bitcoindjs.h index 022698c6..843aa276 100644 --- a/src/bitcoindjs.h +++ b/src/bitcoindjs.h @@ -7,3 +7,4 @@ */ #define DEBUG_TX 0 +#define V090 0