X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fbitcoinrpc.cpp;h=a8ad00017272267f9128d33e3a23b7cb24730d55;hb=f1af842f0fd26cb19d6f1161f7b161ae5386b980;hp=6525c151944f84cc33adc7514c397ba4550fea2c;hpb=6c055e506d93ba3f6bf448f4633a774208be735c;p=novacoin.git diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 6525c15..a8ad000 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1,12 +1,20 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers +// Copyright (c) 2011-2013 The PPCoin developers +// Copyright (c) 2013 NovaCoin Developers // Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "headers.h" +#include "main.h" +#include "wallet.h" #include "db.h" +#include "walletdb.h" #include "net.h" #include "init.h" +#include "checkpoints.h" +#include "ui_interface.h" +#include "bitcoinrpc.h" + #undef printf #include #include @@ -14,15 +22,10 @@ #include #include #include -#ifdef USE_SSL #include -#include #include typedef boost::asio::ssl::stream SSLStream; -#endif -#include "json/json_spirit_reader_template.h" -#include "json/json_spirit_writer_template.h" -#include "json/json_spirit_utils.h" + #define printf OutputDebugStringF // MinGW 3.4.5 gets "fatal error: had to relocate PCH" if the json headers are // precompiled in headers.h. The problem might be when the pch file goes over @@ -35,8 +38,6 @@ using namespace boost::asio; using namespace json_spirit; void ThreadRPCServer2(void* parg); -typedef Value(*rpcfn_type)(const Array& params, bool fHelp); -extern map mapCallTable; static std::string strRPCUserColonPass; @@ -63,7 +64,7 @@ double GetDifficulty(const CBlockIndex* blockindex = NULL) if (pindexBest == NULL) return 1.0; else - blockindex = pindexBest; + blockindex = GetLastBlockIndex(pindexBest, false); } int nShift = (blockindex->nBits >> 24) & 0xff; @@ -89,7 +90,7 @@ double GetDifficulty(const CBlockIndex* blockindex = NULL) int64 AmountFromValue(const Value& value) { double dAmount = value.get_real(); - if (dAmount <= 0.0 || dAmount > 21000000.0) + if (dAmount <= 0.0 || dAmount > MAX_MONEY) throw JSONRPCError(-3, "Invalid amount"); int64 nAmount = roundint64(dAmount * COIN); if (!MoneyRange(nAmount)) @@ -136,27 +137,44 @@ string AccountFromValue(const Value& value) return strAccount; } -Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) +Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool fPrintTransactionDetail) { Object result; result.push_back(Pair("hash", block.GetHash().GetHex())); - result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK))); + result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); result.push_back(Pair("height", blockindex->nHeight)); result.push_back(Pair("version", block.nVersion)); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("time", (boost::int64_t)block.GetBlockTime())); + result.push_back(Pair("time", DateTimeStrFormat(block.GetBlockTime()))); result.push_back(Pair("nonce", (boost::uint64_t)block.nNonce)); result.push_back(Pair("bits", HexBits(block.nBits))); result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - Array txhashes; - BOOST_FOREACH (const CTransaction&tx, block.vtx) - txhashes.push_back(tx.GetHash().GetHex()); - result.push_back(Pair("tx", txhashes)); - + result.push_back(Pair("mint", ValueFromAmount(blockindex->nMint))); if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); if (blockindex->pnext) result.push_back(Pair("nextblockhash", blockindex->pnext->GetBlockHash().GetHex())); + result.push_back(Pair("flags", strprintf("%s%s", blockindex->IsProofOfStake()? "proof-of-stake" : "proof-of-work", blockindex->GeneratedStakeModifier()? " stake-modifier": ""))); + result.push_back(Pair("proofhash", blockindex->IsProofOfStake()? blockindex->hashProofOfStake.GetHex() : blockindex->GetBlockHash().GetHex())); + result.push_back(Pair("entropybit", (int)blockindex->GetStakeEntropyBit())); + result.push_back(Pair("modifier", strprintf("%016"PRI64x, blockindex->nStakeModifier))); + result.push_back(Pair("modifierchecksum", strprintf("%08x", blockindex->nStakeModifierChecksum))); + Array txinfo; + BOOST_FOREACH (const CTransaction& tx, block.vtx) + { + if (fPrintTransactionDetail) + { + txinfo.push_back(tx.ToStringShort()); + txinfo.push_back(DateTimeStrFormat(tx.nTime)); + BOOST_FOREACH(const CTxIn& txin, tx.vin) + txinfo.push_back(txin.ToStringShort()); + BOOST_FOREACH(const CTxOut& txout, tx.vout) + txinfo.push_back(txout.ToStringShort()); + } + else + txinfo.push_back(tx.GetHash().GetHex()); + } + result.push_back(Pair("tx", txinfo)); return result; } @@ -166,23 +184,14 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) /// Note: This interface may still be subject to change. /// - -Value help(const Array& params, bool fHelp) +string CRPCTable::help(string strCommand) const { - if (fHelp || params.size() > 1) - throw runtime_error( - "help [command]\n" - "List commands, or get help for a command."); - - string strCommand; - if (params.size() > 0) - strCommand = params[0].get_str(); - string strRet; set setDone; - for (map::iterator mi = mapCallTable.begin(); mi != mapCallTable.end(); ++mi) + for (map::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) { - string strMethod = (*mi).first; + const CRPCCommand *pcmd = mi->second; + string strMethod = mi->first; // We already filter duplicates, but these deprecated screw up the sort order if (strMethod == "getamountreceived" || strMethod == "getallreceived" || @@ -194,7 +203,7 @@ Value help(const Array& params, bool fHelp) try { Array params; - rpcfn_type pfn = (*mi).second; + rpcfn_type pfn = pcmd->actor; if (setDone.insert(pfn).second) (*pfn)(params, true); } @@ -214,20 +223,30 @@ Value help(const Array& params, bool fHelp) return strRet; } +Value help(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "help [command]\n" + "List commands, or get help for a command."); + + string strCommand; + if (params.size() > 0) + strCommand = params[0].get_str(); + + return tableRPC.help(strCommand); +} + Value stop(const Array& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( "stop\n" - "Stop bitcoin server."); -#ifndef QT_GUI + "Stop novacoin server."); // Shutdown will take long enough that the response should get back - CreateThread(Shutdown, NULL); - return "bitcoin server stopping"; -#else - throw runtime_error("NYI: cannot shut down GUI with RPC command"); -#endif + StartShutdown(); + return "novacoin server stopping"; } @@ -270,9 +289,13 @@ Value getdifficulty(const Array& params, bool fHelp) if (fHelp || params.size() != 0) throw runtime_error( "getdifficulty\n" - "Returns the proof-of-work difficulty as a multiple of the minimum difficulty."); + "Returns difficulty as a multiple of the minimum difficulty."); - return GetDifficulty(); + Object obj; + obj.push_back(Pair("proof-of-work", GetDifficulty())); + obj.push_back(Pair("proof-of-stake", GetDifficulty(GetLastBlockIndex(pindexBest, true)))); + obj.push_back(Pair("search-interval", (int)nLastCoinStakeSearchInterval)); + return obj; } @@ -334,13 +357,17 @@ Value getinfo(const Array& params, bool fHelp) "Returns an object containing various state info."); Object obj; - obj.push_back(Pair("version", (int)CLIENT_VERSION)); + obj.push_back(Pair("version", FormatFullVersion())); obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint()))); + obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake()))); obj.push_back(Pair("blocks", (int)nBestHeight)); + obj.push_back(Pair("moneysupply", ValueFromAmount(pindexBest->nMoneySupply))); obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("proxy", (fUseProxy ? addrProxy.ToStringIPPort() : string()))); + obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP())); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("testnet", fTestNet)); obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); @@ -369,7 +396,7 @@ Value getmininginfo(const Array& params, bool fHelp) obj.push_back(Pair("generate", GetBoolArg("-gen"))); obj.push_back(Pair("genproclimit", (int)GetArg("-genproclimit", -1))); obj.push_back(Pair("hashespersec", gethashespersec(params, false))); - obj.push_back(Pair("pooledtx", (uint64_t)nPooledTx)); + obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); obj.push_back(Pair("testnet", fTestNet)); return obj; } @@ -380,7 +407,7 @@ Value getnewaddress(const Array& params, bool fHelp) if (fHelp || params.size() > 1) throw runtime_error( "getnewaddress [account]\n" - "Returns a new bitcoin address for receiving payments. " + "Returns a new novacoin address for receiving payments. " "If [account] is specified (recommended), it is added to the address book " "so payments received with the address will be credited to [account]."); @@ -447,7 +474,7 @@ Value getaccountaddress(const Array& params, bool fHelp) if (fHelp || params.size() != 1) throw runtime_error( "getaccountaddress \n" - "Returns the current bitcoin address for receiving payments to this account."); + "Returns the current novacoin address for receiving payments to this account."); // Parse the account first so we don't generate a key if there's an error string strAccount = AccountFromValue(params[0]); @@ -465,12 +492,12 @@ Value setaccount(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "setaccount \n" + "setaccount \n" "Sets the account associated with the given address."); CBitcoinAddress address(params[0].get_str()); if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid bitcoin address"); + throw JSONRPCError(-5, "Invalid novacoin address"); string strAccount; @@ -495,12 +522,12 @@ Value getaccount(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( - "getaccount \n" + "getaccount \n" "Returns the account associated with the given address."); CBitcoinAddress address(params[0].get_str()); if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid bitcoin address"); + throw JSONRPCError(-5, "Invalid novacoin address"); string strAccount; map::iterator mi = pwalletMain->mapAddressBook.find(address); @@ -533,17 +560,14 @@ Value getaddressesbyaccount(const Array& params, bool fHelp) Value settxfee(const Array& params, bool fHelp) { - if (fHelp || params.size() < 1 || params.size() > 1) + if (fHelp || params.size() < 1 || params.size() > 1 || AmountFromValue(params[0]) < MIN_TX_FEE) throw runtime_error( "settxfee \n" - " is a real and is rounded to the nearest 0.00000001"); - - // Amount - int64 nAmount = 0; - if (params[0].get_real() != 0.0) - nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts + " is a real and is rounded to 0.01 (cent)\n" + "Minimum and default transaction fee per KB is 1 cent"); - nTransactionFee = nAmount; + nTransactionFee = AmountFromValue(params[0]); + nTransactionFee = (nTransactionFee / CENT) * CENT; // round to cent return true; } @@ -551,20 +575,22 @@ Value sendtoaddress(const Array& params, bool fHelp) { if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4)) throw runtime_error( - "sendtoaddress [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001\n" + "sendtoaddress [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.000001\n" "requires wallet passphrase to be set with walletpassphrase first"); if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 4)) throw runtime_error( - "sendtoaddress [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001"); + "sendtoaddress [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.000001"); CBitcoinAddress address(params[0].get_str()); if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid bitcoin address"); + throw JSONRPCError(-5, "Invalid novacoin address"); // Amount int64 nAmount = AmountFromValue(params[1]); + if (nAmount < MIN_TXOUT_AMOUNT) + throw JSONRPCError(-101, "Send amount too small"); // Wallet comments CWalletTx wtx; @@ -587,7 +613,7 @@ Value signmessage(const Array& params, bool fHelp) { if (fHelp || params.size() != 2) throw runtime_error( - "signmessage \n" + "signmessage \n" "Sign a message with the private key of an address"); if (pwalletMain->IsLocked()) @@ -604,7 +630,7 @@ Value signmessage(const Array& params, bool fHelp) if (!pwalletMain->GetKey(addr, key)) throw JSONRPCError(-4, "Private key not available"); - CDataStream ss(SER_GETHASH); + CDataStream ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; @@ -619,7 +645,7 @@ Value verifymessage(const Array& params, bool fHelp) { if (fHelp || params.size() != 3) throw runtime_error( - "verifymessage \n" + "verifymessage \n" "Verify a signed message"); string strAddress = params[0].get_str(); @@ -636,7 +662,7 @@ Value verifymessage(const Array& params, bool fHelp) if (fInvalid) throw JSONRPCError(-5, "Malformed base64 encoding"); - CDataStream ss(SER_GETHASH); + CDataStream ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; @@ -652,14 +678,14 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "getreceivedbyaddress [minconf=1]\n" - "Returns the total amount received by in transactions with at least [minconf] confirmations."); + "getreceivedbyaddress [minconf=1]\n" + "Returns the total amount received by in transactions with at least [minconf] confirmations."); // Bitcoin address CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); CScript scriptPubKey; if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid bitcoin address"); + throw JSONRPCError(-5, "Invalid novacoin address"); scriptPubKey.SetBitcoinAddress(address); if (!IsMine(*pwalletMain,scriptPubKey)) return (double)0.0; @@ -674,7 +700,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) + if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) continue; BOOST_FOREACH(const CTxOut& txout, wtx.vout) @@ -721,7 +747,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) + if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) continue; BOOST_FOREACH(const CTxOut& txout, wtx.vout) @@ -840,7 +866,8 @@ Value movecmd(const Array& params, bool fHelp) strComment = params[4].get_str(); CWalletDB walletdb(pwalletMain->strWalletFile); - walletdb.TxnBegin(); + if (!walletdb.TxnBegin()) + throw JSONRPCError(-20, "database error"); int64 nNow = GetAdjustedTime(); @@ -862,7 +889,8 @@ Value movecmd(const Array& params, bool fHelp) credit.strComment = strComment; walletdb.WriteAccountingEntry(credit); - walletdb.TxnCommit(); + if (!walletdb.TxnCommit()) + throw JSONRPCError(-20, "database error"); return true; } @@ -872,19 +900,21 @@ Value sendfrom(const Array& params, bool fHelp) { if (pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6)) throw runtime_error( - "sendfrom [minconf=1] [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001\n" + "sendfrom [minconf=1] [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.000001\n" "requires wallet passphrase to be set with walletpassphrase first"); if (!pwalletMain->IsCrypted() && (fHelp || params.size() < 3 || params.size() > 6)) throw runtime_error( - "sendfrom [minconf=1] [comment] [comment-to]\n" - " is a real and is rounded to the nearest 0.00000001"); + "sendfrom [minconf=1] [comment] [comment-to]\n" + " is a real and is rounded to the nearest 0.000001"); string strAccount = AccountFromValue(params[0]); CBitcoinAddress address(params[1].get_str()); if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid bitcoin address"); + throw JSONRPCError(-5, "Invalid novacoin address"); int64 nAmount = AmountFromValue(params[2]); + if (nAmount < MIN_TXOUT_AMOUNT) + throw JSONRPCError(-101, "Send amount too small"); int nMinDepth = 1; if (params.size() > 3) nMinDepth = params[3].get_int(); @@ -944,7 +974,7 @@ Value sendmany(const Array& params, bool fHelp) { CBitcoinAddress address(s.name_); if (!address.IsValid()) - throw JSONRPCError(-5, string("Invalid bitcoin address:")+s.name_); + throw JSONRPCError(-5, string("Invalid novacoin address:")+s.name_); if (setAddress.count(address)) throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_); @@ -953,6 +983,8 @@ Value sendmany(const Array& params, bool fHelp) CScript scriptPubKey; scriptPubKey.SetBitcoinAddress(address); int64 nAmount = AmountFromValue(s.value_); + if (nAmount < MIN_TXOUT_AMOUNT) + throw JSONRPCError(-101, "Send amount too small"); totalAmount += nAmount; vecSend.push_back(make_pair(scriptPubKey, nAmount)); @@ -960,6 +992,8 @@ Value sendmany(const Array& params, bool fHelp) if (pwalletMain->IsLocked()) throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); + if (fWalletUnlockMintOnly) + throw JSONRPCError(-13, "Error: Wallet unlocked for block minting only."); // Check funds int64 nBalance = GetAccountBalance(strAccount, nMinDepth); @@ -992,8 +1026,6 @@ Value addmultisigaddress(const Array& params, bool fHelp) "If [account] is specified, assign address to [account]."; throw runtime_error(msg); } - if (!fTestNet) - throw runtime_error("addmultisigaddress available only when running -testnet\n"); int nRequired = params[0].get_int(); const Array& keys = params[1].get_array(); @@ -1002,10 +1034,12 @@ Value addmultisigaddress(const Array& params, bool fHelp) strAccount = AccountFromValue(params[2]); // Gather public keys - if (nRequired < 1 || keys.size() < nRequired) + if (nRequired < 1) + throw runtime_error("a multisignature address must require at least one key to redeem"); + if ((int)keys.size() < nRequired) throw runtime_error( - strprintf("wrong number of keys" - "(got %d, need at least %d)", keys.size(), nRequired)); + strprintf("not enough keys supplied " + "(got %d keys, but need at least %d to redeem)", keys.size(), nRequired)); std::vector pubkeys; pubkeys.resize(keys.size()); for (unsigned int i = 0; i < keys.size(); i++) @@ -1085,7 +1119,7 @@ Value ListReceived(const Array& params, bool fByAccounts) { const CWalletTx& wtx = (*it).second; - if (wtx.IsCoinBase() || !wtx.IsFinal()) + if (wtx.IsCoinBase() || wtx.IsCoinStake() || !wtx.IsFinal()) continue; int nDepth = wtx.GetDepthInMainChain(); @@ -1334,8 +1368,10 @@ Value listtransactions(const Array& params, bool fHelp) } // ret is newest to oldest - if (nFrom > ret.size()) nFrom = ret.size(); - if (nFrom+nCount > ret.size()) nCount = ret.size()-nFrom; + if (nFrom > (int)ret.size()) + nFrom = ret.size(); + if ((nFrom + nCount) > (int)ret.size()) + nCount = ret.size() - nFrom; Array::iterator first = ret.begin(); std::advance(first, nFrom); Array::iterator last = ret.begin(); @@ -1583,17 +1619,18 @@ void ThreadCleanWalletPassphrase(void* parg) Value walletpassphrase(const Array& params, bool fHelp) { - if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) + if (pwalletMain->IsCrypted() && (fHelp || params.size() < 2 || params.size() > 3)) throw runtime_error( - "walletpassphrase \n" - "Stores the wallet decryption key in memory for seconds."); + "walletpassphrase [mintonly]\n" + "Stores the wallet decryption key in memory for seconds.\n" + "mintonly is optional true/false allowing only block minting."); if (fHelp) return true; if (!pwalletMain->IsCrypted()) throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called."); if (!pwalletMain->IsLocked()) - throw JSONRPCError(-17, "Error: Wallet is already unlocked."); + throw JSONRPCError(-17, "Error: Wallet is already unlocked, use walletlock first if need to change unlock settings."); // Note that the walletpassphrase is stored in params[0] which is not mlock()ed SecureString strWalletPass; @@ -1616,6 +1653,12 @@ Value walletpassphrase(const Array& params, bool fHelp) int64* pnSleepTime = new int64(params[1].get_int64()); CreateThread(ThreadCleanWalletPassphrase, pnSleepTime); + // ppcoin: if user OS account compromised prevent trivial sendmoney commands + if (params.size() > 2) + fWalletUnlockMintOnly = params[2].get_bool(); + else + fWalletUnlockMintOnly = false; + return Value::null; } @@ -1666,8 +1709,8 @@ Value walletlock(const Array& params, bool fHelp) if (!pwalletMain->IsCrypted()) throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called."); - CRITICAL_BLOCK(cs_nWalletUnlockTime) { + LOCK(cs_nWalletUnlockTime); pwalletMain->Lock(); nWalletUnlockTime = 0; } @@ -1687,11 +1730,6 @@ Value encryptwallet(const Array& params, bool fHelp) if (pwalletMain->IsCrypted()) throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called."); -#ifdef QT_GUI - // shutting down via RPC while the GUI is running does not work (yet): - throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command"); -#endif - // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) // Alternately, find a way to make params[0] mlock()'d to begin with. SecureString strWalletPass; @@ -1709,8 +1747,8 @@ Value encryptwallet(const Array& params, bool fHelp) // 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: - CreateThread(Shutdown, NULL); - return "wallet encrypted; bitcoin server stopping, restart to run with encrypted wallet"; + StartShutdown(); + return "wallet encrypted; novacoin server stopping, restart to run with encrypted wallet"; } @@ -1718,8 +1756,8 @@ Value validateaddress(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error( - "validateaddress \n" - "Return information about ."); + "validateaddress \n" + "Return information about ."); CBitcoinAddress address(params[0].get_str()); bool isValid = address.IsValid(); @@ -1768,6 +1806,50 @@ Value validateaddress(const Array& params, bool fHelp) return ret; } +Value validatepubkey(const Array& params, bool fHelp) +{ + if (fHelp || !params.size() || params.size() > 2) + throw runtime_error( + "validatepubkey \n" + "Return information about ."); + + std::vector vchPubKey = ParseHex(params[0].get_str()); + bool isValid; + + if(vchPubKey.size() == 33) // Compressed key + isValid = (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03); + else if(vchPubKey.size() == 65) // Uncompressed key + isValid = vchPubKey[0] == 0x04; + else + isValid = false; + + CBitcoinAddress address(vchPubKey); + isValid = isValid ? address.IsValid() : false; + + Object ret; + ret.push_back(Pair("isvalid", isValid)); + if (isValid) + { + // Call Hash160ToAddress() so we always return current ADDRESSVERSION + // version of the address: + string currentAddress = address.ToString(); + ret.push_back(Pair("address", currentAddress)); + if (pwalletMain->HaveKey(address)) + { + ret.push_back(Pair("ismine", true)); + CKey key; + key.SetPubKey(vchPubKey); + ret.push_back(Pair("iscompressed", key.IsCompressed())); + } + else + ret.push_back(Pair("ismine", false)); + if (pwalletMain->mapAddressBook.count(address)) + ret.push_back(Pair("account", pwalletMain->mapAddressBook[address])); + } + return ret; +} + + Value getwork(const Array& params, bool fHelp) { if (fHelp || params.size() > 1) @@ -1781,10 +1863,10 @@ Value getwork(const Array& params, bool fHelp) "If [data] is specified, tries to solve the block and returns true if it was successful."); if (vNodes.empty()) - throw JSONRPCError(-9, "Bitcoin is not connected!"); + throw JSONRPCError(-9, "NovaCoin is not connected!"); if (IsInitialBlockDownload()) - throw JSONRPCError(-10, "Bitcoin is downloading blocks..."); + throw JSONRPCError(-10, "NovaCoin is downloading blocks..."); typedef map > mapNewBlock_t; static mapNewBlock_t mapNewBlock; @@ -1814,7 +1896,7 @@ Value getwork(const Array& params, bool fHelp) nStart = GetTime(); // Create new block - pblock = CreateNewBlock(reservekey); + pblock = CreateNewBlock(pwalletMain); if (!pblock) throw JSONRPCError(-7, "Out of memory"); vNewBlock.push_back(pblock); @@ -1867,11 +1949,197 @@ Value getwork(const Array& params, bool fHelp) pblock->nNonce = pdata->nNonce; pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second; pblock->hashMerkleRoot = pblock->BuildMerkleTree(); + if (!pblock->SignBlock(*pwalletMain)) + throw JSONRPCError(-100, "Unable to sign block, wallet locked?"); return CheckWork(pblock, *pwalletMain, reservekey); } } +Value getblocktemplate(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getblocktemplate [params]\n" + "Returns data needed to construct a block to work on:\n" + " \"version\" : block version\n" + " \"previousblockhash\" : hash of current highest block\n" + " \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n" + " \"coinbaseaux\" : data that should be included in coinbase\n" + " \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n" + " \"target\" : hash target\n" + " \"mintime\" : minimum timestamp appropriate for next block\n" + " \"curtime\" : current timestamp\n" + " \"mutable\" : list of ways the block template may be changed\n" + " \"noncerange\" : range of valid nonces\n" + " \"sigoplimit\" : limit of sigops in blocks\n" + " \"sizelimit\" : limit of block size\n" + " \"bits\" : compressed target of next block\n" + " \"height\" : height of the next block\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification."); + + std::string strMode = "template"; + if (params.size() > 0) + { + const Object& oparam = params[0].get_obj(); + const Value& modeval = find_value(oparam, "mode"); + if (modeval.type() == str_type) + strMode = modeval.get_str(); + else + throw JSONRPCError(-8, "Invalid mode"); + } + + if (strMode != "template") + throw JSONRPCError(-8, "Invalid mode"); + + if (vNodes.empty()) + throw JSONRPCError(-9, "NovaCoin is not connected!"); + + if (IsInitialBlockDownload()) + throw JSONRPCError(-10, "NovaCoin is downloading blocks..."); + + static CReserveKey reservekey(pwalletMain); + + // Update block + static unsigned int nTransactionsUpdatedLast; + static CBlockIndex* pindexPrev; + static int64 nStart; + static CBlock* pblock; + if (pindexPrev != pindexBest || + (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5)) + { + // Clear pindexPrev so future calls make a new block, despite any failures from here on + pindexPrev = NULL; + + // Store the pindexBest used before CreateNewBlock, to avoid races + nTransactionsUpdatedLast = nTransactionsUpdated; + CBlockIndex* pindexPrevNew = pindexBest; + nStart = GetTime(); + + // Create new block + if(pblock) + { + delete pblock; + pblock = NULL; + } + pblock = CreateNewBlock(pwalletMain); + if (!pblock) + throw JSONRPCError(-7, "Out of memory"); + + // Need to update only after we know CreateNewBlock succeeded + pindexPrev = pindexPrevNew; + } + + // Update nTime + pblock->UpdateTime(pindexPrev); + pblock->nNonce = 0; + + Array transactions; + map setTxIndex; + int i = 0; + CTxDB txdb("r"); + BOOST_FOREACH (CTransaction& tx, pblock->vtx) + { + uint256 txHash = tx.GetHash(); + setTxIndex[txHash] = i++; + + if (tx.IsCoinBase() || tx.IsCoinStake()) + continue; + + Object entry; + + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << tx; + entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end()))); + + entry.push_back(Pair("hash", txHash.GetHex())); + + MapPrevTx mapInputs; + map mapUnused; + bool fInvalid = false; + if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) + { + entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut()))); + + Array deps; + BOOST_FOREACH (MapPrevTx::value_type& inp, mapInputs) + { + if (setTxIndex.count(inp.first)) + deps.push_back(setTxIndex[inp.first]); + } + entry.push_back(Pair("depends", deps)); + + int64_t nSigOps = tx.GetLegacySigOpCount(); + nSigOps += tx.GetP2SHSigOpCount(mapInputs); + entry.push_back(Pair("sigops", nSigOps)); + } + + transactions.push_back(entry); + } + + Object aux; + aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()))); + + uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); + + static Array aMutable; + if (aMutable.empty()) + { + aMutable.push_back("time"); + aMutable.push_back("transactions"); + aMutable.push_back("prevblock"); + } + + Object result; + result.push_back(Pair("version", pblock->nVersion)); + result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); + result.push_back(Pair("transactions", transactions)); + result.push_back(Pair("coinbaseaux", aux)); + result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); + result.push_back(Pair("target", hashTarget.GetHex())); + result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); + result.push_back(Pair("mutable", aMutable)); + result.push_back(Pair("noncerange", "00000000ffffffff")); + result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS)); + result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE)); + result.push_back(Pair("curtime", (int64_t)pblock->nTime)); + result.push_back(Pair("bits", HexBits(pblock->nBits))); + result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); + + return result; +} + +Value submitblock(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "submitblock [optional-params-obj]\n" + "[optional-params-obj] parameter is currently ignored.\n" + "Attempts to submit new block to network.\n" + "See https://en.bitcoin.it/wiki/BIP_0022 for full specification."); + + vector blockData(ParseHex(params[0].get_str())); + CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION); + CBlock block; + try { + ssBlock >> block; + } + catch (std::exception &e) { + throw JSONRPCError(-22, "Block decode failed"); + } + + static CReserveKey reservekey(pwalletMain); + + if(!block.SignBlock(*pwalletMain)) + throw JSONRPCError(-100, "Unable to sign block, wallet locked?"); + + bool fAccepted = CheckWork(&block, *pwalletMain, reservekey); + if (!fAccepted) + return "rejected"; + + return Value::null; +} + Value getmemorypool(const Array& params, bool fHelp) { @@ -1893,10 +2161,10 @@ Value getmemorypool(const Array& params, bool fHelp) if (params.size() == 0) { if (vNodes.empty()) - throw JSONRPCError(-9, "Bitcoin is not connected!"); + throw JSONRPCError(-9, "NovaCoin is not connected!"); if (IsInitialBlockDownload()) - throw JSONRPCError(-10, "Bitcoin is downloading blocks..."); + throw JSONRPCError(-10, "NovaCoin is downloading blocks..."); static CReserveKey reservekey(pwalletMain); @@ -1915,7 +2183,7 @@ Value getmemorypool(const Array& params, bool fHelp) // Create new block if(pblock) delete pblock; - pblock = CreateNewBlock(reservekey); + pblock = CreateNewBlock(pwalletMain); if (!pblock) throw JSONRPCError(-7, "Out of memory"); } @@ -1926,10 +2194,10 @@ Value getmemorypool(const Array& params, bool fHelp) Array transactions; BOOST_FOREACH(CTransaction tx, pblock->vtx) { - if(tx.IsCoinBase()) + if(tx.IsCoinBase() || tx.IsCoinStake()) continue; - CDataStream ssTx; + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << tx; transactions.push_back(HexStr(ssTx.begin(), ssTx.end())); @@ -1951,14 +2219,46 @@ Value getmemorypool(const Array& params, bool fHelp) else { // Parse parameters - CDataStream ssBlock(ParseHex(params[0].get_str())); + CDataStream ssBlock(ParseHex(params[0].get_str()), SER_NETWORK, PROTOCOL_VERSION); CBlock pblock; ssBlock >> pblock; - return ProcessBlock(NULL, &pblock); + static CReserveKey reservekey(pwalletMain); + + if(!pblock.SignBlock(*pwalletMain)) + throw JSONRPCError(-100, "Unable to sign block, wallet locked?"); + + return CheckWork(&pblock, *pwalletMain, reservekey); } } +Value getnewpubkey(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getnewpubkey [account]\n" + "Returns new public key for coinbase generation."); + + // Parse the account first so we don't generate a key if there's an error + string strAccount; + if (params.size() > 0) + strAccount = AccountFromValue(params[0]); + + if (!pwalletMain->IsLocked()) + pwalletMain->TopUpKeyPool(); + + // Generate a new key that is added to wallet + std::vector newKey = pwalletMain->GenerateNewKey(false); + + if(!newKey.size()) + throw JSONRPCError(-12, "Error: Unable to create key"); + + CBitcoinAddress address(newKey); + pwalletMain->SetAddressBookName(address, strAccount); + + return HexStr(newKey.begin(), newKey.end()); +} + Value getblockhash(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -1979,9 +2279,10 @@ Value getblockhash(const Array& params, bool fHelp) Value getblock(const Array& params, bool fHelp) { - if (fHelp || params.size() != 1) + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "getblock \n" + "getblock [txinfo]\n" + "txinfo optional to print more detailed tx info\n" "Returns details of a block with given block-hash."); std::string strHash = params[0].get_str(); @@ -1994,16 +2295,254 @@ Value getblock(const Array& params, bool fHelp) CBlockIndex* pblockindex = mapBlockIndex[hash]; block.ReadFromDisk(pblockindex, true); - return blockToJSON(block, pblockindex); + return blockToJSON(block, pblockindex, params.size() > 1 ? params[1].get_bool() : false); +} + +Value getblockbynumber(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "getblock [txinfo]\n" + "txinfo optional to print more detailed tx info\n" + "Returns details of a block with given block-number."); + + int nHeight = params[0].get_int(); + if (nHeight < 0 || nHeight > nBestHeight) + throw runtime_error("Block number out of range."); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hashBestChain]; + while (pblockindex->nHeight > nHeight) + pblockindex = pblockindex->pprev; + + uint256 hash = *pblockindex->phashBlock; + + pblockindex = mapBlockIndex[hash]; + block.ReadFromDisk(pblockindex, true); + + return blockToJSON(block, pblockindex, params.size() > 1 ? params[1].get_bool() : false); } +// ppcoin: get information of sync-checkpoint +Value getcheckpoint(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getcheckpoint\n" + "Show info of synchronized checkpoint.\n"); + Object result; + CBlockIndex* pindexCheckpoint; + + result.push_back(Pair("synccheckpoint", Checkpoints::hashSyncCheckpoint.ToString().c_str())); + pindexCheckpoint = mapBlockIndex[Checkpoints::hashSyncCheckpoint]; + result.push_back(Pair("height", pindexCheckpoint->nHeight)); + result.push_back(Pair("timestamp", DateTimeStrFormat(pindexCheckpoint->GetBlockTime()).c_str())); + if (mapArgs.count("-checkpointkey")) + result.push_back(Pair("checkpointmaster", true)); + return result; +} +// ppcoin: reserve balance from being staked for network protection +Value reservebalance(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "reservebalance [ [amount]]\n" + " is true or false to turn balance reserve on or off.\n" + " is a real and rounded to cent.\n" + "Set reserve amount not participating in network protection.\n" + "If no parameters provided current setting is printed.\n"); + + if (params.size() > 0) + { + bool fReserve = params[0].get_bool(); + if (fReserve) + { + if (params.size() == 1) + throw runtime_error("must provide amount to reserve balance.\n"); + int64 nAmount = AmountFromValue(params[1]); + nAmount = (nAmount / CENT) * CENT; // round to cent + if (nAmount < 0) + throw runtime_error("amount cannot be negative.\n"); + mapArgs["-reservebalance"] = FormatMoney(nAmount).c_str(); + } + else + { + if (params.size() > 1) + throw runtime_error("cannot specify amount to turn off reserve.\n"); + mapArgs["-reservebalance"] = "0"; + } + } + Object result; + int64 nReserveBalance = 0; + if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) + throw runtime_error("invalid reserve balance amount\n"); + result.push_back(Pair("reserve", (nReserveBalance > 0))); + result.push_back(Pair("amount", ValueFromAmount(nReserveBalance))); + return result; +} +// ppcoin: check wallet integrity +Value checkwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "checkwallet\n" + "Check wallet for integrity.\n"); + + int nMismatchSpent; + int64 nBalanceInQuestion; + pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion, true); + Object result; + if (nMismatchSpent == 0) + result.push_back(Pair("wallet check passed", true)); + else + { + result.push_back(Pair("mismatched spent coins", nMismatchSpent)); + result.push_back(Pair("amount in question", ValueFromAmount(nBalanceInQuestion))); + } + return result; +} + + +// ppcoin: repair wallet +Value repairwallet(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "repairwallet\n" + "Repair wallet if checkwallet reports any problem.\n"); + + int nMismatchSpent; + int64 nBalanceInQuestion; + pwalletMain->FixSpentCoins(nMismatchSpent, nBalanceInQuestion); + Object result; + if (nMismatchSpent == 0) + result.push_back(Pair("wallet check passed", true)); + else + { + result.push_back(Pair("mismatched spent coins", nMismatchSpent)); + result.push_back(Pair("amount affected by repair", ValueFromAmount(nBalanceInQuestion))); + } + return result; +} + +// NovaCoin: resend unconfirmed wallet transactions +Value resendtx(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "resendtx\n" + "Re-send unconfirmed transactions.\n" + ); + + ResendWalletTransactions(); + + return Value::null; +} + + +// ppcoin: make a public-private key pair +Value makekeypair(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "makekeypair [prefix]\n" + "Make a public/private key pair.\n" + "[prefix] is optional preferred prefix for the public key.\n"); + + string strPrefix = ""; + if (params.size() > 0) + strPrefix = params[0].get_str(); + + CKey key; + int nCount = 0; + do + { + key.MakeNewKey(false); + nCount++; + } while (nCount < 10000 && strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size())); + + if (strPrefix != HexStr(key.GetPubKey()).substr(0, strPrefix.size())) + return Value::null; + + CPrivKey vchPrivKey = key.GetPrivKey(); + Object result; + result.push_back(Pair("PrivateKey", HexStr(vchPrivKey.begin(), vchPrivKey.end()))); + result.push_back(Pair("PublicKey", HexStr(key.GetPubKey()))); + return result; +} + +extern CCriticalSection cs_mapAlerts; +extern map mapAlerts; + +// ppcoin: send alert. +// There is a known deadlock situation with ThreadMessageHandler +// ThreadMessageHandler: holds cs_vSend and acquiring cs_main in SendMessages() +// ThreadRPCServer: holds cs_main and acquiring cs_vSend in alert.RelayTo()/PushMessage()/BeginMessage() +Value sendalert(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 6) + throw runtime_error( + "sendalert [cancelupto]\n" + " is the alert text message\n" + " is hex string of alert master private key\n" + " is the minimum applicable internal client version\n" + " is the maximum applicable internal client version\n" + " is integer priority number\n" + " is the alert id\n" + "[cancelupto] cancels all alert id's up to this number\n" + "Returns true or false."); + + CAlert alert; + CKey key; + + alert.strStatusBar = params[0].get_str(); + alert.nMinVer = params[2].get_int(); + alert.nMaxVer = params[3].get_int(); + alert.nPriority = params[4].get_int(); + alert.nID = params[5].get_int(); + if (params.size() > 6) + alert.nCancel = params[6].get_int(); + alert.nVersion = PROTOCOL_VERSION; + alert.nRelayUntil = GetAdjustedTime() + 365*24*60*60; + alert.nExpiration = GetAdjustedTime() + 365*24*60*60; + + CDataStream sMsg(SER_NETWORK, PROTOCOL_VERSION); + sMsg << (CUnsignedAlert)alert; + alert.vchMsg = vector(sMsg.begin(), sMsg.end()); + + vector vchPrivKey = ParseHex(params[1].get_str()); + key.SetPrivKey(CPrivKey(vchPrivKey.begin(), vchPrivKey.end())); // if key is not correct openssl may crash + if (!key.Sign(Hash(alert.vchMsg.begin(), alert.vchMsg.end()), alert.vchSig)) + throw runtime_error( + "Unable to sign alert, check private key?\n"); + if(!alert.ProcessAlert()) + throw runtime_error( + "Failed to process alert.\n"); + // Relay alert + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + alert.RelayTo(pnode); + } + + Object result; + result.push_back(Pair("strStatusBar", alert.strStatusBar)); + result.push_back(Pair("nVersion", alert.nVersion)); + result.push_back(Pair("nMinVer", alert.nMinVer)); + result.push_back(Pair("nMaxVer", alert.nMaxVer)); + result.push_back(Pair("nPriority", alert.nPriority)); + result.push_back(Pair("nID", alert.nID)); + if (alert.nCancel > 0) + result.push_back(Pair("nCancel", alert.nCancel)); + return result; +} @@ -2011,86 +2550,89 @@ Value getblock(const Array& params, bool fHelp) // Call Table // -pair pCallTable[] = -{ - make_pair("help", &help), - make_pair("stop", &stop), - make_pair("getblockcount", &getblockcount), - make_pair("getblocknumber", &getblocknumber), - make_pair("getconnectioncount", &getconnectioncount), - make_pair("getdifficulty", &getdifficulty), - make_pair("getgenerate", &getgenerate), - make_pair("setgenerate", &setgenerate), - make_pair("gethashespersec", &gethashespersec), - make_pair("getinfo", &getinfo), - make_pair("getmininginfo", &getmininginfo), - make_pair("getnewaddress", &getnewaddress), - make_pair("getaccountaddress", &getaccountaddress), - make_pair("setaccount", &setaccount), - make_pair("getaccount", &getaccount), - make_pair("getaddressesbyaccount", &getaddressesbyaccount), - make_pair("sendtoaddress", &sendtoaddress), - make_pair("getreceivedbyaddress", &getreceivedbyaddress), - make_pair("getreceivedbyaccount", &getreceivedbyaccount), - make_pair("listreceivedbyaddress", &listreceivedbyaddress), - make_pair("listreceivedbyaccount", &listreceivedbyaccount), - make_pair("backupwallet", &backupwallet), - make_pair("keypoolrefill", &keypoolrefill), - make_pair("walletpassphrase", &walletpassphrase), - make_pair("walletpassphrasechange", &walletpassphrasechange), - make_pair("walletlock", &walletlock), - make_pair("encryptwallet", &encryptwallet), - make_pair("validateaddress", &validateaddress), - make_pair("getbalance", &getbalance), - make_pair("move", &movecmd), - make_pair("sendfrom", &sendfrom), - make_pair("sendmany", &sendmany), - make_pair("addmultisigaddress", &addmultisigaddress), - make_pair("getblock", &getblock), - make_pair("getblockhash", &getblockhash), - make_pair("gettransaction", &gettransaction), - make_pair("listtransactions", &listtransactions), - make_pair("signmessage", &signmessage), - make_pair("verifymessage", &verifymessage), - make_pair("getwork", &getwork), - make_pair("listaccounts", &listaccounts), - make_pair("settxfee", &settxfee), - make_pair("getmemorypool", &getmemorypool), - make_pair("listsinceblock", &listsinceblock), - make_pair("dumpprivkey", &dumpprivkey), - make_pair("importprivkey", &importprivkey) -}; -map mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0])); - -string pAllowInSafeMode[] = -{ - "help", - "stop", - "getblockcount", - "getblocknumber", // deprecated - "getconnectioncount", - "getdifficulty", - "getgenerate", - "setgenerate", - "gethashespersec", - "getinfo", - "getmininginfo", - "getnewaddress", - "getaccountaddress", - "getaccount", - "getaddressesbyaccount", - "backupwallet", - "keypoolrefill", - "walletpassphrase", - "walletlock", - "validateaddress", - "getwork", - "getmemorypool", + +static const CRPCCommand vRPCCommands[] = +{ // name function safe mode? + // ------------------------ ----------------------- ---------- + { "help", &help, true }, + { "stop", &stop, true }, + { "getblockcount", &getblockcount, true }, + { "getblocknumber", &getblocknumber, true }, + { "getconnectioncount", &getconnectioncount, true }, + { "getdifficulty", &getdifficulty, true }, + { "getgenerate", &getgenerate, true }, + { "setgenerate", &setgenerate, true }, + { "gethashespersec", &gethashespersec, true }, + { "getinfo", &getinfo, true }, + { "getmininginfo", &getmininginfo, true }, + { "getnewaddress", &getnewaddress, true }, + { "getnewpubkey", &getnewpubkey, true }, + { "getaccountaddress", &getaccountaddress, true }, + { "setaccount", &setaccount, true }, + { "getaccount", &getaccount, false }, + { "getaddressesbyaccount", &getaddressesbyaccount, true }, + { "sendtoaddress", &sendtoaddress, false }, + { "getreceivedbyaddress", &getreceivedbyaddress, false }, + { "getreceivedbyaccount", &getreceivedbyaccount, false }, + { "listreceivedbyaddress", &listreceivedbyaddress, false }, + { "listreceivedbyaccount", &listreceivedbyaccount, false }, + { "backupwallet", &backupwallet, true }, + { "keypoolrefill", &keypoolrefill, true }, + { "walletpassphrase", &walletpassphrase, true }, + { "walletpassphrasechange", &walletpassphrasechange, false }, + { "walletlock", &walletlock, true }, + { "encryptwallet", &encryptwallet, false }, + { "validateaddress", &validateaddress, true }, + { "validatepubkey", &validatepubkey, true }, + { "getbalance", &getbalance, false }, + { "move", &movecmd, false }, + { "sendfrom", &sendfrom, false }, + { "sendmany", &sendmany, false }, + { "addmultisigaddress", &addmultisigaddress, false }, + { "getblock", &getblock, false }, + { "getblockhash", &getblockhash, false }, + { "getblockbynumber", &getblockbynumber, false }, + { "gettransaction", &gettransaction, false }, + { "listtransactions", &listtransactions, false }, + { "signmessage", &signmessage, false }, + { "verifymessage", &verifymessage, false }, + { "getwork", &getwork, true }, + { "listaccounts", &listaccounts, false }, + { "settxfee", &settxfee, false }, + { "getmemorypool", &getmemorypool, true }, + { "getblocktemplate", &getblocktemplate, true }, + { "submitblock", &submitblock, false }, + { "listsinceblock", &listsinceblock, false }, + { "dumpprivkey", &dumpprivkey, false }, + { "importprivkey", &importprivkey, false }, + { "getcheckpoint", &getcheckpoint, true }, + { "reservebalance", &reservebalance, false}, + { "checkwallet", &checkwallet, false}, + { "repairwallet", &repairwallet, false}, + { "resendtx", &resendtx, false}, + { "makekeypair", &makekeypair, false}, + { "sendalert", &sendalert, false}, }; -set setAllowInSafeMode(pAllowInSafeMode, pAllowInSafeMode + sizeof(pAllowInSafeMode)/sizeof(pAllowInSafeMode[0])); +CRPCTable::CRPCTable() +{ + unsigned int vcidx; + for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) + { + const CRPCCommand *pcmd; + pcmd = &vRPCCommands[vcidx]; + mapCommands[pcmd->name] = pcmd; + } +} +const CRPCCommand *CRPCTable::operator[](string name) const +{ + map::const_iterator it = mapCommands.find(name); + if (it == mapCommands.end()) + return NULL; + return (*it).second; +} // // HTTP protocol @@ -2103,7 +2645,7 @@ string HTTPPost(const string& strMsg, const map& mapRequestHeader { ostringstream s; s << "POST / HTTP/1.1\r\n" - << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n" + << "User-Agent: novacoin-json-rpc/" << FormatFullVersion() << "\r\n" << "Host: 127.0.0.1\r\n" << "Content-Type: application/json\r\n" << "Content-Length: " << strMsg.size() << "\r\n" @@ -2134,7 +2676,7 @@ static string HTTPReply(int nStatus, const string& strMsg) if (nStatus == 401) return strprintf("HTTP/1.0 401 Authorization Required\r\n" "Date: %s\r\n" - "Server: bitcoin-json-rpc/%s\r\n" + "Server: novacoin-json-rpc/%s\r\n" "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" "Content-Type: text/html\r\n" "Content-Length: 296\r\n" @@ -2161,7 +2703,7 @@ static string HTTPReply(int nStatus, const string& strMsg) "Connection: close\r\n" "Content-Length: %d\r\n" "Content-Type: application/json\r\n" - "Server: bitcoin-json-rpc/%s\r\n" + "Server: novacoin-json-rpc/%s\r\n" "\r\n" "%s", nStatus, @@ -2218,7 +2760,7 @@ int ReadHTTP(std::basic_istream& stream, map& mapHeadersRe // Read header int nLen = ReadHTTPHeader(stream, mapHeadersRet); - if (nLen < 0 || nLen > MAX_SIZE) + if (nLen < 0 || nLen > (int)MAX_SIZE) return 500; // Read message @@ -2295,7 +2837,6 @@ bool ClientAllowed(const string& strAddress) return false; } -#ifdef USE_SSL // // IOStream device that speaks SSL but can also speak non-SSL // @@ -2347,7 +2888,6 @@ private: bool fUseSSL; SSLStream& stream; }; -#endif void ThreadRPCServer(void* parg) { @@ -2368,10 +2908,6 @@ void ThreadRPCServer(void* parg) printf("ThreadRPCServer exiting\n"); } -#ifdef QT_GUI -extern bool HACK_SHUTDOWN; -#endif - void ThreadRPCServer2(void* parg) { printf("ThreadRPCServer started\n"); @@ -2381,7 +2917,7 @@ void ThreadRPCServer2(void* parg) { unsigned char rand_pwd[32]; RAND_bytes(rand_pwd, 32); - string strWhatAmI = "To use bitcoind"; + string strWhatAmI = "To use novacoind"; if (mapArgs.count("-server")) strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); else if (mapArgs.count("-daemon")) @@ -2389,17 +2925,15 @@ void ThreadRPCServer2(void* parg) ThreadSafeMessageBox(strprintf( _("%s, you must set a rpcpassword in the configuration file:\n %s\n" "It is recommended you use the following random password:\n" - "rpcuser=bitcoinrpc\n" + "rpcuser=novarpc\n" "rpcpassword=%s\n" "(you do not need to remember this password)\n" "If the file does not exist, create it with owner-readable-only file permissions.\n"), strWhatAmI.c_str(), - GetConfigFile().c_str(), + GetConfigFile().string().c_str(), EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()), _("Error"), wxOK | wxMODAL); -#ifndef QT_GUI - CreateThread(Shutdown, NULL); -#endif + StartShutdown(); return; } @@ -2407,12 +2941,7 @@ void ThreadRPCServer2(void* parg) asio::ip::address bindAddress = mapArgs.count("-rpcallowip") ? asio::ip::address_v4::any() : asio::ip::address_v4::loopback(); asio::io_service io_service; - ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332)); -#ifndef QT_GUI - ip::tcp::acceptor acceptor(io_service, endpoint); - - acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); -#else + ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", RPC_PORT)); ip::tcp::acceptor acceptor(io_service); try { @@ -2423,54 +2952,41 @@ void ThreadRPCServer2(void* parg) } catch(boost::system::system_error &e) { - HACK_SHUTDOWN = true; ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()), _("Error"), wxOK | wxMODAL); + StartShutdown(); return; } -#endif -#ifdef USE_SSL ssl::context context(io_service, ssl::context::sslv23); if (fUseSSL) { context.set_options(ssl::context::no_sslv2); - filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert"); - if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile; - if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str()); - else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str()); - filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem"); - if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile; - if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem); - else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str()); - - string ciphers = GetArg("-rpcsslciphers", - "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); - SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str()); - } -#else - if (fUseSSL) - throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); -#endif + + filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); + if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; + if (filesystem::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string()); + else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str()); + + filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); + if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; + if (filesystem::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string(), ssl::context::pem); + else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str()); + + string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); + SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str()); + } loop { // Accept connection -#ifdef USE_SSL SSLStream sslStream(io_service, context); SSLIOStreamDevice d(sslStream, fUseSSL); iostreams::stream stream(d); -#else - ip::tcp::iostream stream; -#endif ip::tcp::endpoint peer; vnThreadsRunning[THREAD_RPCSERVER]--; -#ifdef USE_SSL acceptor.accept(sslStream.lowest_layer(), peer); -#else - acceptor.accept(*stream.rdbuf(), peer); -#endif vnThreadsRunning[4]++; if (fShutdown) return; @@ -2547,22 +3063,24 @@ void ThreadRPCServer2(void* parg) throw JSONRPCError(-32600, "Params must be an array"); // Find method - map::iterator mi = mapCallTable.find(strMethod); - if (mi == mapCallTable.end()) + const CRPCCommand *pcmd = tableRPC[strMethod]; + if (!pcmd) throw JSONRPCError(-32601, "Method not found"); // Observe safe mode string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !GetBoolArg("-disablesafemode") && !setAllowInSafeMode.count(strMethod)) + if (strWarning != "" && !GetBoolArg("-disablesafemode") && + !pcmd->okSafeMode) throw JSONRPCError(-2, string("Safe mode: ") + strWarning); try { // Execute Value result; - CRITICAL_BLOCK(cs_main) - CRITICAL_BLOCK(pwalletMain->cs_wallet) - result = (*(*mi).second)(params, false); + { + LOCK2(cs_main, pwalletMain->cs_wallet); + result = pcmd->actor(params, false); + } // Send reply string strReply = JSONRPCReply(result, Value::null, id); @@ -2593,28 +3111,18 @@ Object CallRPC(const string& strMethod, const Array& params) throw runtime_error(strprintf( _("You must set rpcpassword= in the configuration file:\n%s\n" "If the file does not exist, create it with owner-readable-only file permissions."), - GetConfigFile().c_str())); + GetConfigFile().string().c_str())); // Connect to localhost bool fUseSSL = GetBoolArg("-rpcssl"); -#ifdef USE_SSL asio::io_service io_service; ssl::context context(io_service, ssl::context::sslv23); context.set_options(ssl::context::no_sslv2); SSLStream sslStream(io_service, context); SSLIOStreamDevice d(sslStream, fUseSSL); iostreams::stream stream(d); - if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"))) + if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", CBigNum(RPC_PORT).ToString().c_str()))) throw runtime_error("couldn't connect to server"); -#else - if (fUseSSL) - throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries."); - - ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")); - if (stream.fail()) - throw runtime_error("couldn't connect to server"); -#endif - // HTTP basic authentication string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); @@ -2707,6 +3215,9 @@ int CommandLineRPC(int argc, char *argv[]) if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); if (strMethod == "getblockhash" && n > 0) ConvertTo(params[0]); + if (strMethod == "getblockbynumber" && n > 0) ConvertTo(params[0]); + if (strMethod == "getblockbynumber" && n > 1) ConvertTo(params[1]); + if (strMethod == "getblock" && n > 1) ConvertTo(params[1]); if (strMethod == "move" && n > 2) ConvertTo(params[2]); if (strMethod == "move" && n > 3) ConvertTo(params[3]); if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); @@ -2715,7 +3226,13 @@ int CommandLineRPC(int argc, char *argv[]) if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); if (strMethod == "walletpassphrase" && n > 1) ConvertTo(params[1]); + if (strMethod == "walletpassphrase" && n > 2) ConvertTo(params[2]); if (strMethod == "listsinceblock" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendalert" && n > 2) ConvertTo(params[2]); + if (strMethod == "sendalert" && n > 3) ConvertTo(params[3]); + if (strMethod == "sendalert" && n > 4) ConvertTo(params[4]); + if (strMethod == "sendalert" && n > 5) ConvertTo(params[5]); + if (strMethod == "sendalert" && n > 6) ConvertTo(params[6]); if (strMethod == "sendmany" && n > 1) { string s = params[1].get_str(); @@ -2725,6 +3242,8 @@ int CommandLineRPC(int argc, char *argv[]) params[1] = v.get_obj(); } if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + if (strMethod == "reservebalance" && n > 0) ConvertTo(params[0]); + if (strMethod == "reservebalance" && n > 1) ConvertTo(params[1]); if (strMethod == "addmultisigaddress" && n > 0) ConvertTo(params[0]); if (strMethod == "addmultisigaddress" && n > 1) { @@ -2812,3 +3331,5 @@ int main(int argc, char *argv[]) return 0; } #endif + +const CRPCTable tableRPC;