diff options
Diffstat (limited to 'src/rpcwallet.cpp')
| -rw-r--r-- | src/rpcwallet.cpp | 247 |
1 files changed, 178 insertions, 69 deletions
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 929dde9c1..90a68f560 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -3,14 +3,18 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <boost/assign/list_of.hpp> + #include "wallet.h" #include "walletdb.h" #include "bitcoinrpc.h" #include "init.h" #include "base58.h" -using namespace json_spirit; using namespace std; +using namespace boost; +using namespace boost::assign; +using namespace json_spirit; int64 nWalletUnlockTime; static CCriticalSection cs_nWalletUnlockTime; @@ -25,7 +29,7 @@ std::string HelpRequiringPassphrase() void EnsureWalletIsUnlocked() { if (pwalletMain->IsLocked()) - throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); } void WalletTxToJSON(const CWalletTx& wtx, Object& entry) @@ -51,7 +55,7 @@ string AccountFromValue(const Value& value) { string strAccount = value.get_str(); if (strAccount == "*") - throw JSONRPCError(-11, "Invalid account name"); + throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME, "Invalid account name"); return strAccount; } @@ -62,8 +66,8 @@ Value getinfo(const Array& params, bool fHelp) "getinfo\n" "Returns an object containing various state info."); - CService addrProxy; - GetProxy(NET_IPV4, addrProxy); + proxyType proxy; + GetProxy(NET_IPV4, proxy); Object obj; obj.push_back(Pair("version", (int)CLIENT_VERSION)); @@ -72,7 +76,7 @@ Value getinfo(const Array& params, bool fHelp) obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); obj.push_back(Pair("blocks", (int)nBestHeight)); obj.push_back(Pair("connections", (int)vNodes.size())); - obj.push_back(Pair("proxy", (addrProxy.IsValid() ? addrProxy.ToStringIPPort() : string()))); + obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string()))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("testnet", fTestNet)); obj.push_back(Pair("keypoololdest", (boost::int64_t)pwalletMain->GetOldestKeyPoolTime())); @@ -106,7 +110,7 @@ Value getnewaddress(const Array& params, bool fHelp) // Generate a new key that is added to wallet CPubKey newKey; if (!pwalletMain->GetKeyFromPool(newKey, false)) - throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first"); + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); CKeyID keyID = newKey.GetID(); pwalletMain->SetAddressBookName(keyID, strAccount); @@ -144,7 +148,7 @@ CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed) { if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false)) - throw JSONRPCError(-12, "Error: Keypool ran out, please call keypoolrefill first"); + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); pwalletMain->SetAddressBookName(account.vchPubKey.GetID(), strAccount); walletdb.WriteAccount(strAccount, account); @@ -181,7 +185,7 @@ Value setaccount(const Array& params, bool fHelp) CBitcoinAddress address(params[0].get_str()); if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid Bitcoin address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); string strAccount; @@ -211,7 +215,7 @@ Value getaccount(const Array& params, bool fHelp) CBitcoinAddress address(params[0].get_str()); if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid Bitcoin address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); string strAccount; map<CTxDestination, string>::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); @@ -252,7 +256,7 @@ Value sendtoaddress(const Array& params, bool fHelp) CBitcoinAddress address(params[0].get_str()); if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid Bitcoin address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); // Amount int64 nAmount = AmountFromValue(params[1]); @@ -265,11 +269,11 @@ Value sendtoaddress(const Array& params, bool fHelp) wtx.mapValue["to"] = params[3].get_str(); if (pwalletMain->IsLocked()) - throw JSONRPCError(-13, "Error: Please enter the wallet passphrase with walletpassphrase first."); + throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); if (strError != "") - throw JSONRPCError(-4, strError); + throw JSONRPCError(RPC_WALLET_ERROR, strError); return wtx.GetHash().GetHex(); } @@ -319,23 +323,23 @@ Value signmessage(const Array& params, bool fHelp) CBitcoinAddress addr(strAddress); if (!addr.IsValid()) - throw JSONRPCError(-3, "Invalid address"); + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); CKeyID keyID; if (!addr.GetKeyID(keyID)) - throw JSONRPCError(-3, "Address does not refer to key"); + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); CKey key; if (!pwalletMain->GetKey(keyID, key)) - throw JSONRPCError(-4, "Private key not available"); + throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); - CDataStream ss(SER_GETHASH, 0); + CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; vector<unsigned char> vchSig; - if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig)) - throw JSONRPCError(-5, "Sign failed"); + if (!key.SignCompact(ss.GetHash(), vchSig)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); return EncodeBase64(&vchSig[0], vchSig.size()); } @@ -353,24 +357,24 @@ Value verifymessage(const Array& params, bool fHelp) CBitcoinAddress addr(strAddress); if (!addr.IsValid()) - throw JSONRPCError(-3, "Invalid address"); + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); CKeyID keyID; if (!addr.GetKeyID(keyID)) - throw JSONRPCError(-3, "Address does not refer to key"); + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); bool fInvalid = false; vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid); if (fInvalid) - throw JSONRPCError(-5, "Malformed base64 encoding"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); - CDataStream ss(SER_GETHASH, 0); + CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; CKey key; - if (!key.SetCompactSignature(Hash(ss.begin(), ss.end()), vchSig)) + if (!key.SetCompactSignature(ss.GetHash(), vchSig)) return false; return (key.GetPubKey().GetID() == keyID); @@ -388,7 +392,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp) CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); CScript scriptPubKey; if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid Bitcoin address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); scriptPubKey.SetDestination(address.Get()); if (!IsMine(*pwalletMain,scriptPubKey)) return (double)0.0; @@ -567,13 +571,13 @@ Value movecmd(const Array& params, bool fHelp) CWalletDB walletdb(pwalletMain->strWalletFile); if (!walletdb.TxnBegin()) - throw JSONRPCError(-20, "database error"); + throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); int64 nNow = GetAdjustedTime(); // Debit CAccountingEntry debit; - debit.nOrderPos = pwalletMain->nOrderPosNext++; + debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); debit.strAccount = strFrom; debit.nCreditDebit = -nAmount; debit.nTime = nNow; @@ -583,7 +587,7 @@ Value movecmd(const Array& params, bool fHelp) // Credit CAccountingEntry credit; - credit.nOrderPos = pwalletMain->nOrderPosNext++; + credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); credit.strAccount = strTo; credit.nCreditDebit = nAmount; credit.nTime = nNow; @@ -592,7 +596,7 @@ Value movecmd(const Array& params, bool fHelp) walletdb.WriteAccountingEntry(credit); if (!walletdb.TxnCommit()) - throw JSONRPCError(-20, "database error"); + throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); return true; } @@ -609,7 +613,7 @@ Value sendfrom(const Array& params, bool fHelp) string strAccount = AccountFromValue(params[0]); CBitcoinAddress address(params[1].get_str()); if (!address.IsValid()) - throw JSONRPCError(-5, "Invalid Bitcoin address"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); int64 nAmount = AmountFromValue(params[2]); int nMinDepth = 1; if (params.size() > 3) @@ -627,12 +631,12 @@ Value sendfrom(const Array& params, bool fHelp) // Check funds int64 nBalance = GetAccountBalance(strAccount, nMinDepth); if (nAmount > nBalance) - throw JSONRPCError(-6, "Account has insufficient funds"); + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); // Send string strError = pwalletMain->SendMoneyToDestination(address.Get(), nAmount, wtx); if (strError != "") - throw JSONRPCError(-4, strError); + throw JSONRPCError(RPC_WALLET_ERROR, strError); return wtx.GetHash().GetHex(); } @@ -665,10 +669,10 @@ 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(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_); if (setAddress.count(address)) - throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_); + throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); setAddress.insert(address); CScript scriptPubKey; @@ -684,7 +688,7 @@ Value sendmany(const Array& params, bool fHelp) // Check funds int64 nBalance = GetAccountBalance(strAccount, nMinDepth); if (totalAmount > nBalance) - throw JSONRPCError(-6, "Account has insufficient funds"); + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); // Send CReserveKey keyChange(pwalletMain); @@ -693,31 +697,22 @@ Value sendmany(const Array& params, bool fHelp) if (!fCreated) { if (totalAmount + nFeeRequired > pwalletMain->GetBalance()) - throw JSONRPCError(-6, "Insufficient funds"); - throw JSONRPCError(-4, "Transaction creation failed"); + throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed"); } if (!pwalletMain->CommitTransaction(wtx, keyChange)) - throw JSONRPCError(-4, "Transaction commit failed"); + throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed"); return wtx.GetHash().GetHex(); } -Value addmultisigaddress(const Array& params, bool fHelp) +// +// Used by addmultisigaddress / createmultisig: +// +static CScript _createmultisig(const Array& params) { - if (fHelp || params.size() < 2 || params.size() > 3) - { - string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n" - "Add a nrequired-to-sign multisignature address to the wallet\"\n" - "each key is a Bitcoin address or hex-encoded public key\n" - "If [account] is specified, assign address to [account]."; - throw runtime_error(msg); - } - int nRequired = params[0].get_int(); const Array& keys = params[1].get_array(); - string strAccount; - if (params.size() > 2) - strAccount = AccountFromValue(params[2]); // Gather public keys if (nRequired < 1) @@ -725,7 +720,7 @@ Value addmultisigaddress(const Array& params, bool fHelp) if ((int)keys.size() < nRequired) throw runtime_error( strprintf("not enough keys supplied " - "(got %d keys, but need at least %d to redeem)", keys.size(), nRequired)); + "(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired)); std::vector<CKey> pubkeys; pubkeys.resize(keys.size()); for (unsigned int i = 0; i < keys.size(); i++) @@ -760,10 +755,28 @@ Value addmultisigaddress(const Array& params, bool fHelp) throw runtime_error(" Invalid public key: "+ks); } } + CScript result; + result.SetMultisig(nRequired, pubkeys); + return result; +} + +Value addmultisigaddress(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 3) + { + string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n" + "Add a nrequired-to-sign multisignature address to the wallet\"\n" + "each key is a Bitcoin address or hex-encoded public key\n" + "If [account] is specified, assign address to [account]."; + throw runtime_error(msg); + } + + string strAccount; + if (params.size() > 2) + strAccount = AccountFromValue(params[2]); // Construct using pay-to-script-hash: - CScript inner; - inner.SetMultisig(nRequired, pubkeys); + CScript inner = _createmultisig(params); CScriptID innerID = inner.GetID(); pwalletMain->AddCScript(inner); @@ -771,6 +784,30 @@ Value addmultisigaddress(const Array& params, bool fHelp) return CBitcoinAddress(innerID).ToString(); } +Value createmultisig(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 2 || params.size() > 2) + { + string msg = "createmultisig <nrequired> <'[\"key\",\"key\"]'>\n" + "Creates a multi-signature address and returns a json object\n" + "with keys:\n" + "address : bitcoin address\n" + "redeemScript : hex-encoded redemption script"; + throw runtime_error(msg); + } + + // Construct using pay-to-script-hash: + CScript inner = _createmultisig(params); + CScriptID innerID = inner.GetID(); + CBitcoinAddress address(innerID); + + Object result; + result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); + + return result; +} + struct tallyitem { @@ -1000,9 +1037,9 @@ Value listtransactions(const Array& params, bool fHelp) nFrom = params[2].get_int(); if (nCount < 0) - throw JSONRPCError(-8, "Negative count"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); if (nFrom < 0) - throw JSONRPCError(-8, "Negative from"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from"); Array ret; @@ -1113,7 +1150,7 @@ Value listsinceblock(const Array& params, bool fHelp) target_confirms = params[1].get_int(); if (target_confirms < 1) - throw JSONRPCError(-8, "Invalid parameter"); + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); } int depth = pindex ? (1 + nBestHeight - pindex->nHeight) : -1; @@ -1165,7 +1202,7 @@ Value gettransaction(const Array& params, bool fHelp) Object entry; if (!pwalletMain->mapWallet.count(hash)) - throw JSONRPCError(-5, "Invalid or non-wallet transaction id"); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); const CWalletTx& wtx = pwalletMain->mapWallet[hash]; int64 nCredit = wtx.GetCredit(); @@ -1195,7 +1232,8 @@ Value backupwallet(const Array& params, bool fHelp) "Safely copies wallet.dat to destination, which can be a directory or a path with filename."); string strDest = params[0].get_str(); - BackupWallet(*pwalletMain, strDest); + if (!BackupWallet(*pwalletMain, strDest)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); return Value::null; } @@ -1214,7 +1252,7 @@ Value keypoolrefill(const Array& params, bool fHelp) pwalletMain->TopUpKeyPool(); if (pwalletMain->GetKeyPoolSize() < GetArg("-keypool", 100)) - throw JSONRPCError(-4, "Error refreshing keypool."); + throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); return Value::null; } @@ -1281,10 +1319,10 @@ Value walletpassphrase(const Array& params, bool fHelp) if (fHelp) return true; if (!pwalletMain->IsCrypted()) - throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrase was called."); + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); if (!pwalletMain->IsLocked()) - throw JSONRPCError(-17, "Error: Wallet is already unlocked."); + throw JSONRPCError(RPC_WALLET_ALREADY_UNLOCKED, "Error: Wallet is already unlocked."); // Note that the walletpassphrase is stored in params[0] which is not mlock()ed SecureString strWalletPass; @@ -1296,7 +1334,7 @@ Value walletpassphrase(const Array& params, bool fHelp) if (strWalletPass.length() > 0) { if (!pwalletMain->Unlock(strWalletPass)) - throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect."); + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); } else throw runtime_error( @@ -1320,7 +1358,7 @@ Value walletpassphrasechange(const Array& params, bool fHelp) if (fHelp) return true; if (!pwalletMain->IsCrypted()) - throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) // Alternately, find a way to make params[0] mlock()'d to begin with. @@ -1338,7 +1376,7 @@ Value walletpassphrasechange(const Array& params, bool fHelp) "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>."); if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) - throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect."); + throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); return Value::null; } @@ -1355,7 +1393,7 @@ Value walletlock(const Array& params, bool fHelp) if (fHelp) return true; if (!pwalletMain->IsCrypted()) - throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called."); + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); { LOCK(cs_nWalletUnlockTime); @@ -1376,7 +1414,7 @@ Value encryptwallet(const Array& params, bool fHelp) if (fHelp) return true; if (pwalletMain->IsCrypted()) - throw JSONRPCError(-15, "Error: running with an encrypted wallet, but encryptwallet was called."); + throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); // 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. @@ -1390,13 +1428,13 @@ Value encryptwallet(const Array& params, bool fHelp) "Encrypts the wallet with <passphrase>."); if (!pwalletMain->EncryptWallet(strWalletPass)) - throw JSONRPCError(-16, "Error: Failed to encrypt the wallet."); + throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "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(); - return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet"; + return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; } class DescribeAddressVisitor : public boost::static_visitor<Object> @@ -1463,3 +1501,74 @@ Value validateaddress(const Array& params, bool fHelp) return ret; } +Value lockunspent(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "lockunspent unlock? [array-of-Objects]\n" + "Updates list of temporarily unspendable outputs."); + + if (params.size() == 1) + RPCTypeCheck(params, list_of(bool_type)); + else + RPCTypeCheck(params, list_of(bool_type)(array_type)); + + bool fUnlock = params[0].get_bool(); + + if (params.size() == 1) { + if (fUnlock) + pwalletMain->UnlockAllCoins(); + return true; + } + + Array outputs = params[1].get_array(); + BOOST_FOREACH(Value& output, outputs) + { + if (output.type() != obj_type) + throw JSONRPCError(-8, "Invalid parameter, expected object"); + const Object& o = output.get_obj(); + + RPCTypeCheck(o, map_list_of("txid", str_type)("vout", int_type)); + + string txid = find_value(o, "txid").get_str(); + if (!IsHex(txid)) + throw JSONRPCError(-8, "Invalid parameter, expected hex txid"); + + int nOutput = find_value(o, "vout").get_int(); + if (nOutput < 0) + throw JSONRPCError(-8, "Invalid parameter, vout must be positive"); + + COutPoint outpt(uint256(txid), nOutput); + + if (fUnlock) + pwalletMain->UnlockCoin(outpt); + else + pwalletMain->LockCoin(outpt); + } + + return true; +} + +Value listlockunspent(const Array& params, bool fHelp) +{ + if (fHelp || params.size() > 0) + throw runtime_error( + "listlockunspent\n" + "Returns list of temporarily unspendable outputs."); + + vector<COutPoint> vOutpts; + pwalletMain->ListLockedCoins(vOutpts); + + Array ret; + + BOOST_FOREACH(COutPoint &outpt, vOutpts) { + Object o; + + o.push_back(Pair("txid", outpt.hash.GetHex())); + o.push_back(Pair("vout", (int)outpt.n)); + ret.push_back(o); + } + + return ret; +} + |