aboutsummaryrefslogtreecommitdiff
path: root/src/rpcwallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpcwallet.cpp')
-rw-r--r--src/rpcwallet.cpp276
1 files changed, 176 insertions, 100 deletions
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index cc2e8ab46..585bdb2bf 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;
@@ -71,6 +75,7 @@ Value getinfo(const Array& params, bool fHelp)
obj.push_back(Pair("walletversion", pwalletMain->GetVersion()));
obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance())));
obj.push_back(Pair("blocks", (int)nBestHeight));
+ obj.push_back(Pair("timeoffset", (boost::int64_t)GetTimeOffset()));
obj.push_back(Pair("connections", (int)vNodes.size()));
obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
@@ -79,7 +84,7 @@ Value getinfo(const Array& params, bool fHelp)
obj.push_back(Pair("keypoolsize", pwalletMain->GetKeyPoolSize()));
obj.push_back(Pair("paytxfee", ValueFromAmount(nTransactionFee)));
if (pwalletMain->IsCrypted())
- obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime / 1000));
+ obj.push_back(Pair("unlocked_until", (boost::int64_t)nWalletUnlockTime));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
return obj;
}
@@ -329,12 +334,12 @@ Value signmessage(const Array& params, bool fHelp)
if (!pwalletMain->GetKey(keyID, key))
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))
+ if (!key.SignCompact(ss.GetHash(), vchSig))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
return EncodeBase64(&vchSig[0], vchSig.size());
@@ -365,15 +370,15 @@ Value verifymessage(const Array& params, bool fHelp)
if (fInvalid)
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))
+ CPubKey pubkey;
+ if (!pubkey.RecoverCompact(ss.GetHash(), vchSig))
return false;
- return (key.GetPubKey().GetID() == keyID);
+ return (pubkey.GetID() == keyID);
}
@@ -403,7 +408,7 @@ Value getreceivedbyaddress(const Array& params, bool fHelp)
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
- if (wtx.IsCoinBase() || !wtx.IsFinal())
+ if (wtx.IsCoinBase() || !IsFinalTx(wtx))
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
@@ -449,7 +454,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
- if (wtx.IsCoinBase() || !wtx.IsFinal())
+ if (wtx.IsCoinBase() || !IsFinalTx(wtx))
continue;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
@@ -473,7 +478,7 @@ int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinD
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
- if (!wtx.IsFinal())
+ if (!IsFinalTx(wtx))
continue;
int64 nReceived, nSent, nFee;
@@ -515,12 +520,12 @@ Value getbalance(const Array& params, bool fHelp)
if (params[0].get_str() == "*") {
// Calculate total balance a different way from GetBalance()
// (GetBalance() sums up all unspent TxOuts)
- // getbalance and getbalance '*' should always return the same number.
+ // getbalance and getbalance '*' 0 should return the same number
int64 nBalance = 0;
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
const CWalletTx& wtx = (*it).second;
- if (!wtx.IsFinal())
+ if (!wtx.IsConfirmed())
continue;
int64 allFee;
@@ -573,7 +578,7 @@ Value movecmd(const Array& params, bool fHelp)
// Debit
CAccountingEntry debit;
- debit.nOrderPos = pwalletMain->IncOrderPosNext();
+ debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
debit.strAccount = strFrom;
debit.nCreditDebit = -nAmount;
debit.nTime = nNow;
@@ -583,7 +588,7 @@ Value movecmd(const Array& params, bool fHelp)
// Credit
CAccountingEntry credit;
- credit.nOrderPos = pwalletMain->IncOrderPosNext();
+ credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb);
credit.strAccount = strTo;
credit.nCreditDebit = nAmount;
credit.nTime = nNow;
@@ -689,35 +694,23 @@ Value sendmany(const Array& params, bool fHelp)
// Send
CReserveKey keyChange(pwalletMain);
int64 nFeeRequired = 0;
- bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired);
+ string strFailReason;
+ bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason);
if (!fCreated)
- {
- if (totalAmount + nFeeRequired > pwalletMain->GetBalance())
- throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
- throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed");
- }
+ throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
if (!pwalletMain->CommitTransaction(wtx, keyChange))
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)
@@ -726,7 +719,7 @@ Value addmultisigaddress(const Array& params, bool fHelp)
throw runtime_error(
strprintf("not enough keys supplied "
"(got %"PRIszu" keys, but need at least %d to redeem)", keys.size(), nRequired));
- std::vector<CKey> pubkeys;
+ std::vector<CPubKey> pubkeys;
pubkeys.resize(keys.size());
for (unsigned int i = 0; i < keys.size(); i++)
{
@@ -744,26 +737,46 @@ Value addmultisigaddress(const Array& params, bool fHelp)
if (!pwalletMain->GetPubKey(keyID, vchPubKey))
throw runtime_error(
strprintf("no full public key for address %s",ks.c_str()));
- if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
+ if (!vchPubKey.IsFullyValid())
throw runtime_error(" Invalid public key: "+ks);
+ pubkeys[i] = vchPubKey;
}
// Case 2: hex public key
else if (IsHex(ks))
{
CPubKey vchPubKey(ParseHex(ks));
- if (!vchPubKey.IsValid() || !pubkeys[i].SetPubKey(vchPubKey))
+ if (!vchPubKey.IsFullyValid())
throw runtime_error(" Invalid public key: "+ks);
+ pubkeys[i] = vchPubKey;
}
else
{
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,11 +784,36 @@ 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
{
int64 nAmount;
int nConf;
+ vector<uint256> txids;
tallyitem()
{
nAmount = 0;
@@ -801,7 +839,7 @@ Value ListReceived(const Array& params, bool fByAccounts)
{
const CWalletTx& wtx = (*it).second;
- if (wtx.IsCoinBase() || !wtx.IsFinal())
+ if (wtx.IsCoinBase() || !IsFinalTx(wtx))
continue;
int nDepth = wtx.GetDepthInMainChain();
@@ -817,6 +855,7 @@ Value ListReceived(const Array& params, bool fByAccounts)
tallyitem& item = mapTally[address];
item.nAmount += txout.nValue;
item.nConf = min(item.nConf, nDepth);
+ item.txids.push_back(wtx.GetHash());
}
}
@@ -852,6 +891,15 @@ Value ListReceived(const Array& params, bool fByAccounts)
obj.push_back(Pair("account", strAccount));
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
+ Array transactions;
+ if (it != mapTally.end())
+ {
+ BOOST_FOREACH(const uint256& item, (*it).second.txids)
+ {
+ transactions.push_back(item.GetHex());
+ }
+ }
+ obj.push_back(Pair("txids", transactions));
ret.push_back(obj);
}
}
@@ -884,7 +932,8 @@ Value listreceivedbyaddress(const Array& params, bool fHelp)
" \"address\" : receiving address\n"
" \"account\" : the account of the receiving address\n"
" \"amount\" : total amount received by the address\n"
- " \"confirmations\" : number of confirmations of the most recent transaction included");
+ " \"confirmations\" : number of confirmations of the most recent transaction included\n"
+ " \"txids\" : list of transactions with outputs to the address\n");
return ListReceived(params, false);
}
@@ -1171,7 +1220,7 @@ Value gettransaction(const Array& params, bool fHelp)
int64 nCredit = wtx.GetCredit();
int64 nDebit = wtx.GetDebit();
int64 nNet = nCredit - nDebit;
- int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
+ int64 nFee = (wtx.IsFromMe() ? GetValueOut(wtx) - nDebit : 0);
entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
if (wtx.IsFromMe())
@@ -1195,7 +1244,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;
}
@@ -1220,56 +1270,11 @@ Value keypoolrefill(const Array& params, bool fHelp)
}
-void ThreadTopUpKeyPool(void* parg)
-{
- // Make this thread recognisable as the key-topping-up thread
- RenameThread("bitcoin-key-top");
-
- pwalletMain->TopUpKeyPool();
-}
-
-void ThreadCleanWalletPassphrase(void* parg)
+static void LockWallet(CWallet* pWallet)
{
- // Make this thread recognisable as the wallet relocking thread
- RenameThread("bitcoin-lock-wa");
-
- int64 nMyWakeTime = GetTimeMillis() + *((int64*)parg) * 1000;
-
- ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
-
- if (nWalletUnlockTime == 0)
- {
- nWalletUnlockTime = nMyWakeTime;
-
- do
- {
- if (nWalletUnlockTime==0)
- break;
- int64 nToSleep = nWalletUnlockTime - GetTimeMillis();
- if (nToSleep <= 0)
- break;
-
- LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
- Sleep(nToSleep);
- ENTER_CRITICAL_SECTION(cs_nWalletUnlockTime);
-
- } while(1);
-
- if (nWalletUnlockTime)
- {
- nWalletUnlockTime = 0;
- pwalletMain->Lock();
- }
- }
- else
- {
- if (nWalletUnlockTime < nMyWakeTime)
- nWalletUnlockTime = nMyWakeTime;
- }
-
- LEAVE_CRITICAL_SECTION(cs_nWalletUnlockTime);
-
- delete (int64*)parg;
+ LOCK(cs_nWalletUnlockTime);
+ nWalletUnlockTime = 0;
+ pWallet->Lock();
}
Value walletpassphrase(const Array& params, bool fHelp)
@@ -1283,9 +1288,6 @@ Value walletpassphrase(const Array& params, bool fHelp)
if (!pwalletMain->IsCrypted())
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
- if (!pwalletMain->IsLocked())
- 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;
strWalletPass.reserve(100);
@@ -1303,9 +1305,12 @@ Value walletpassphrase(const Array& params, bool fHelp)
"walletpassphrase <passphrase> <timeout>\n"
"Stores the wallet decryption key in memory for <timeout> seconds.");
- NewThread(ThreadTopUpKeyPool, NULL);
- int64* pnSleepTime = new int64(params[1].get_int64());
- NewThread(ThreadCleanWalletPassphrase, pnSleepTime);
+ pwalletMain->TopUpKeyPool();
+
+ int64 nSleepTime = params[1].get_int64();
+ LOCK(cs_nWalletUnlockTime);
+ nWalletUnlockTime = GetTime() + nSleepTime;
+ RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime);
return Value::null;
}
@@ -1396,7 +1401,7 @@ Value encryptwallet(const Array& params, bool fHelp)
// 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. The keypool has been flushed, you need to make a new backup.";
+ 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>
@@ -1409,7 +1414,7 @@ public:
CPubKey vchPubKey;
pwalletMain->GetPubKey(keyID, vchPubKey);
obj.push_back(Pair("isscript", false));
- obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw())));
+ obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
return obj;
}
@@ -1463,3 +1468,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;
+}
+