aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGavin Andresen <gavinandresen@gmail.com>2010-11-22 11:34:13 -0500
committerGavin Andresen <gavinandresen@gmail.com>2010-11-22 11:34:13 -0500
commit262430f705501011dfada435dd1a9240e5311819 (patch)
treed1e0fcc65c3563b530c9bc47baca2aff2ff4f853
parent593f74b9cbaabc3952d0491d502c0405659121c3 (diff)
parente4ff4e6898d378b1a3e83791034a7af455fde3ab (diff)
Merge remote branch 'refs/remotes/svn/trunk' into svn
-rw-r--r--db.cpp81
-rw-r--r--db.h11
-rw-r--r--main.h92
-rw-r--r--rpc.cpp420
-rw-r--r--serialize.h2
5 files changed, 515 insertions, 91 deletions
diff --git a/db.cpp b/db.cpp
index 35da17055d..28ae5583b1 100644
--- a/db.cpp
+++ b/db.cpp
@@ -8,7 +8,7 @@ void ThreadFlushWalletDB(void* parg);
unsigned int nWalletDBUpdated;
-
+uint64 nAccountingEntryNumber = 0;
@@ -579,6 +579,66 @@ bool LoadAddresses()
static set<int64> setKeyPool;
static CCriticalSection cs_setKeyPool;
+bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
+{
+ account.SetNull();
+ return Read(make_pair(string("acc"), strAccount), account);
+}
+
+bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
+{
+ return Write(make_pair(string("acc"), strAccount), account);
+}
+
+bool CWalletDB::WriteAccountingEntry(const string& strAccount, const CAccountingEntry& acentry)
+{
+ return Write(make_pair(string("acentry"), make_pair(strAccount, ++nAccountingEntryNumber)), acentry);
+}
+
+int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
+{
+ int64 nCreditDebit = 0;
+
+ Dbc* pcursor = GetCursor();
+ if (!pcursor)
+ throw runtime_error("CWalletDB::GetAccountCreditDebit() : cannot create DB cursor");
+ unsigned int fFlags = DB_SET_RANGE;
+ loop
+ {
+ // Read next record
+ CDataStream ssKey;
+ if (fFlags == DB_SET_RANGE)
+ ssKey << make_pair(string("acentry"), make_pair(strAccount, uint64(0)));
+ CDataStream ssValue;
+ int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
+ fFlags = DB_NEXT;
+ if (ret == DB_NOTFOUND)
+ break;
+ else if (ret != 0)
+ {
+ pcursor->close();
+ throw runtime_error("CWalletDB::GetAccountCreditDebit() : error scanning DB");
+ }
+
+ // Unserialize
+ string strType;
+ ssKey >> strType;
+ if (strType != "acentry")
+ break;
+ string strAccountName;
+ ssKey >> strAccountName;
+ if (strAccountName != strAccount)
+ break;
+
+ CAccountingEntry acentry;
+ ssValue >> acentry;
+ nCreditDebit += acentry.nCreditDebit;
+ }
+
+ pcursor->close();
+ return nCreditDebit;
+}
+
bool CWalletDB::LoadWallet()
{
vchDefaultKey.clear();
@@ -640,6 +700,15 @@ bool CWalletDB::LoadWallet()
// wtx.hashBlock.ToString().substr(0,20).c_str(),
// wtx.mapValue["message"].c_str());
}
+ else if (strType == "acentry")
+ {
+ string strAccount;
+ ssKey >> strAccount;
+ uint64 nNumber;
+ ssKey >> nNumber;
+ if (nNumber > nAccountingEntryNumber)
+ nAccountingEntryNumber = nNumber;
+ }
else if (strType == "key" || strType == "wkey")
{
vector<unsigned char> vchPubKey;
@@ -894,18 +963,20 @@ void CWalletDB::ReturnKey(int64 nIndex)
vector<unsigned char> CWalletDB::GetKeyFromKeyPool()
{
+ CWalletDB walletdb;
int64 nIndex = 0;
CKeyPool keypool;
- ReserveKeyFromKeyPool(nIndex, keypool);
- KeepKey(nIndex);
+ walletdb.ReserveKeyFromKeyPool(nIndex, keypool);
+ walletdb.KeepKey(nIndex);
return keypool.vchPubKey;
}
int64 CWalletDB::GetOldestKeyPoolTime()
{
+ CWalletDB walletdb;
int64 nIndex = 0;
CKeyPool keypool;
- ReserveKeyFromKeyPool(nIndex, keypool);
- ReturnKey(nIndex);
+ walletdb.ReserveKeyFromKeyPool(nIndex, keypool);
+ walletdb.ReturnKey(nIndex);
return keypool.nTime;
}
diff --git a/db.h b/db.h
index a0054aa138..df48699e91 100644
--- a/db.h
+++ b/db.h
@@ -11,6 +11,8 @@ class CUser;
class CReview;
class CAddress;
class CWalletTx;
+class CAccount;
+class CAccountingEntry;
extern map<string, string> mapAddressBook;
extern CCriticalSection cs_mapAddressBook;
@@ -341,7 +343,9 @@ public:
class CWalletDB : public CDB
{
public:
- CWalletDB(const char* pszMode="r+") : CDB("wallet.dat", pszMode) { }
+ CWalletDB(const char* pszMode="r+") : CDB("wallet.dat", pszMode)
+ {
+ }
private:
CWalletDB(const CWalletDB&);
void operator=(const CWalletDB&);
@@ -425,6 +429,11 @@ public:
return Write(make_pair(string("setting"), strKey), value);
}
+ bool ReadAccount(const string& strAccount, CAccount& account);
+ bool WriteAccount(const string& strAccount, const CAccount& account);
+ bool WriteAccountingEntry(const string& strAccount, const CAccountingEntry& acentry);
+ int64 GetAccountCreditDebit(const string& strAccount);
+
bool LoadWallet();
protected:
void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool);
diff --git a/main.h b/main.h
index 7d1145b5ff..dda5f730c7 100644
--- a/main.h
+++ b/main.h
@@ -717,11 +717,12 @@ public:
vector<CMerkleTx> vtxPrev;
map<string, string> mapValue;
vector<pair<string, string> > vOrderForm;
- unsigned int fTimeReceivedIsTxTime;
unsigned int nTimeReceived; // time received by this node
char fFromMe;
char fSpent;
- //// probably need to sign the order info so know it came from payer
+ char fTimeReceivedIsTxTime;
+ char fUnused;
+ string strFromAccount;
// memory only
mutable char fDebitCached;
@@ -752,10 +753,11 @@ public:
void Init()
{
- fTimeReceivedIsTxTime = false;
nTimeReceived = 0;
fFromMe = false;
fSpent = false;
+ fTimeReceivedIsTxTime = false;
+ fUnused = false;
fDebitCached = false;
fCreditCached = false;
nDebitCached = 0;
@@ -767,14 +769,21 @@ public:
IMPLEMENT_SERIALIZE
(
nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion, ser_action);
- nVersion = this->nVersion;
READWRITE(vtxPrev);
READWRITE(mapValue);
READWRITE(vOrderForm);
- READWRITE(fTimeReceivedIsTxTime);
+ READWRITE(nVersion);
+ if (fRead && nVersion < 100)
+ const_cast<CWalletTx*>(this)->fTimeReceivedIsTxTime = nVersion;
READWRITE(nTimeReceived);
READWRITE(fFromMe);
READWRITE(fSpent);
+ if (nVersion >= 31404)
+ {
+ READWRITE(fTimeReceivedIsTxTime);
+ READWRITE(fUnused);
+ READWRITE(strFromAccount);
+ }
)
int64 GetDebit() const
@@ -1537,6 +1546,79 @@ public:
//
+// Account information.
+// Stored in wallet with key "acc"+string account name
+//
+class CAccount
+{
+public:
+ vector<unsigned char> vchPubKey;
+
+ CAccount()
+ {
+ SetNull();
+ }
+
+ void SetNull()
+ {
+ vchPubKey.clear();
+ }
+
+ IMPLEMENT_SERIALIZE
+ (
+ if (!(nType & SER_GETHASH))
+ READWRITE(nVersion);
+ READWRITE(vchPubKey);
+ )
+};
+
+
+
+//
+// Internal transfers.
+// Database key is acentry<account><counter>
+//
+class CAccountingEntry
+{
+public:
+ int64 nCreditDebit;
+ int64 nTime;
+ string strOtherAccount;
+ string strComment;
+
+ CAccountingEntry()
+ {
+ SetNull();
+ }
+
+ void SetNull()
+ {
+ nCreditDebit = 0;
+ nTime = 0;
+ strOtherAccount.clear();
+ strComment.clear();
+ }
+
+ IMPLEMENT_SERIALIZE
+ (
+ if (!(nType & SER_GETHASH))
+ READWRITE(nVersion);
+ READWRITE(nCreditDebit);
+ READWRITE(nTime);
+ READWRITE(strOtherAccount);
+ READWRITE(strComment);
+ )
+};
+
+
+
+
+
+
+
+
+
+//
// Alert messages are broadcast as a vector of signed data. Unserializing may
// not read the entire buffer if the alert is for a newer version, but older
// versions can still relay the original data.
diff --git a/rpc.cpp b/rpc.cpp
index 0c71993c2c..271d36c2f6 100644
--- a/rpc.cpp
+++ b/rpc.cpp
@@ -58,6 +58,17 @@ void PrintConsole(const char* format, ...)
}
+int64 AmountFromValue(const Value& value)
+{
+ double dAmount = value.get_real();
+ if (dAmount <= 0.0 || dAmount > 21000000.0)
+ throw JSONRPCError(-3, "Invalid amount");
+ int64 nAmount = roundint64(dAmount * 100.00) * CENT;
+ if (!MoneyRange(nAmount))
+ throw JSONRPCError(-3, "Invalid amount");
+ return nAmount;
+}
+
@@ -86,7 +97,8 @@ Value help(const Array& params, bool fHelp)
string strMethod = (*mi).first;
// We already filter duplicates, but these deprecated screw up the sort order
if (strMethod == "getamountreceived" ||
- strMethod == "getallreceived")
+ strMethod == "getallreceived" ||
+ (strMethod.find("label") != string::npos))
continue;
if (strCommand != "" && strMethod != strCommand)
continue;
@@ -183,17 +195,6 @@ Value getdifficulty(const Array& params, bool fHelp)
}
-Value getbalance(const Array& params, bool fHelp)
-{
- if (fHelp || params.size() != 0)
- throw runtime_error(
- "getbalance\n"
- "Returns the server's available balance.");
-
- return ((double)GetBalance() / (double)COIN);
-}
-
-
Value getgenerate(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 0)
@@ -273,71 +274,120 @@ Value getnewaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
throw runtime_error(
- "getnewaddress [label]\n"
+ "getnewaddress [account]\n"
"Returns a new bitcoin address for receiving payments. "
- "If [label] is specified (recommended), it is added to the address book "
- "so payments received with the address will be labeled.");
+ "If [account] is specified (recommended), it is added to the address book "
+ "so payments received with the address will be credited to [account].");
- // Parse the label first so we don't generate a key if there's an error
- string strLabel;
+ // Parse the account first so we don't generate a key if there's an error
+ string strAccount;
if (params.size() > 0)
- strLabel = params[0].get_str();
+ strAccount = params[0].get_str();
// Generate a new key that is added to wallet
string strAddress = PubKeyToAddress(CWalletDB().GetKeyFromKeyPool());
- SetAddressBookName(strAddress, strLabel);
+ SetAddressBookName(strAddress, strAccount);
return strAddress;
}
-Value setlabel(const Array& params, bool fHelp)
+Value getaccountaddress(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() != 1)
+ throw runtime_error(
+ "getaccountaddress <account>\n"
+ "Returns the current bitcoin 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 = params[0].get_str();
+
+ CRITICAL_BLOCK(cs_mapWallet)
+ {
+ CWalletDB walletdb;
+ walletdb.TxnBegin();
+
+ CAccount account;
+ walletdb.ReadAccount(strAccount, account);
+
+ // Check if the current key has been used
+ if (!account.vchPubKey.empty())
+ {
+ CScript scriptPubKey;
+ scriptPubKey.SetBitcoinAddress(account.vchPubKey);
+ for (map<uint256, CWalletTx>::iterator it = mapWallet.begin();
+ it != mapWallet.end() && !account.vchPubKey.empty();
+ ++it)
+ {
+ const CWalletTx& wtx = (*it).second;
+ foreach(const CTxOut& txout, wtx.vout)
+ if (txout.scriptPubKey == scriptPubKey)
+ account.vchPubKey.clear();
+ }
+ }
+
+ // Generate a new key
+ if (account.vchPubKey.empty())
+ {
+ account.vchPubKey = CWalletDB().GetKeyFromKeyPool();
+ string strAddress = PubKeyToAddress(account.vchPubKey);
+ SetAddressBookName(strAddress, strAccount);
+ walletdb.WriteAccount(strAccount, account);
+ }
+
+ walletdb.TxnCommit();
+ return PubKeyToAddress(account.vchPubKey);
+ }
+}
+
+
+Value setaccount(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
- "setlabel <bitcoinaddress> <label>\n"
- "Sets the label associated with the given address.");
+ "setaccount <bitcoinaddress> <account>\n"
+ "Sets the account associated with the given address.");
string strAddress = params[0].get_str();
- string strLabel;
+ string strAccount;
if (params.size() > 1)
- strLabel = params[1].get_str();
+ strAccount = params[1].get_str();
- SetAddressBookName(strAddress, strLabel);
+ SetAddressBookName(strAddress, strAccount);
return Value::null;
}
-Value getlabel(const Array& params, bool fHelp)
+Value getaccount(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
- "getlabel <bitcoinaddress>\n"
- "Returns the label associated with the given address.");
+ "getaccount <bitcoinaddress>\n"
+ "Returns the account associated with the given address.");
string strAddress = params[0].get_str();
- string strLabel;
+ string strAccount;
CRITICAL_BLOCK(cs_mapAddressBook)
{
map<string, string>::iterator mi = mapAddressBook.find(strAddress);
if (mi != mapAddressBook.end() && !(*mi).second.empty())
- strLabel = (*mi).second;
+ strAccount = (*mi).second;
}
- return strLabel;
+ return strAccount;
}
-Value getaddressesbylabel(const Array& params, bool fHelp)
+Value getaddressesbyaccount(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
throw runtime_error(
- "getaddressesbylabel <label>\n"
- "Returns the list of addresses with the given label.");
+ "getaddressesbyaccount <account>\n"
+ "Returns the list of addresses for the given account.");
- string strLabel = params[0].get_str();
+ string strAccount = params[0].get_str();
- // Find all addresses that have the given label
+ // Find all addresses that have the given account
Array ret;
CRITICAL_BLOCK(cs_mapAddressBook)
{
@@ -345,7 +395,7 @@ Value getaddressesbylabel(const Array& params, bool fHelp)
{
const string& strAddress = item.first;
const string& strName = item.second;
- if (strName == strLabel)
+ if (strName == strAccount)
{
// We're only adding valid bitcoin addresses and not ip addresses
CScript scriptPubKey;
@@ -357,7 +407,6 @@ Value getaddressesbylabel(const Array& params, bool fHelp)
return ret;
}
-
Value sendtoaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 4)
@@ -368,9 +417,7 @@ Value sendtoaddress(const Array& params, bool fHelp)
string strAddress = params[0].get_str();
// Amount
- if (params[1].get_real() <= 0.0 || params[1].get_real() > 21000000.0)
- throw JSONRPCError(-3, "Invalid amount");
- int64 nAmount = roundint64(params[1].get_real() * 100.00) * CENT;
+ int64 nAmount = AmountFromValue(params[1]);
// Wallet comments
CWalletTx wtx;
@@ -382,7 +429,7 @@ Value sendtoaddress(const Array& params, bool fHelp)
string strError = SendMoneyToBitcoinAddress(strAddress, nAmount, wtx);
if (strError != "")
throw JSONRPCError(-4, strError);
- return "sent";
+ return wtx.GetHash().GetHex();
}
@@ -448,15 +495,20 @@ Value getreceivedbyaddress(const Array& params, bool fHelp)
}
-Value getreceivedbylabel(const Array& params, bool fHelp)
+Value getreceivedbyaccount(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
- "getreceivedbylabel <label> [minconf=1]\n"
- "Returns the total amount received by addresses with <label> in transactions with at least [minconf] confirmations.");
+ "getreceivedbyaccount <account> [minconf=1]\n"
+ "Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.");
+
+ // Minimum confirmations
+ int nMinDepth = 1;
+ if (params.size() > 1)
+ nMinDepth = params[1].get_int();
// Get the set of pub keys that have the label
- string strLabel = params[0].get_str();
+ string strAccount = params[0].get_str();
set<CScript> setPubKey;
CRITICAL_BLOCK(cs_mapAddressBook)
{
@@ -464,7 +516,7 @@ Value getreceivedbylabel(const Array& params, bool fHelp)
{
const string& strAddress = item.first;
const string& strName = item.second;
- if (strName == strLabel)
+ if (strName == strAccount)
{
// We're only counting our own valid bitcoin addresses and not ip addresses
CScript scriptPubKey;
@@ -475,11 +527,6 @@ Value getreceivedbylabel(const Array& params, bool fHelp)
}
}
- // Minimum confirmations
- int nMinDepth = 1;
- if (params.size() > 1)
- nMinDepth = params[1].get_int();
-
// Tally
int64 nAmount = 0;
CRITICAL_BLOCK(cs_mapWallet)
@@ -501,6 +548,194 @@ Value getreceivedbylabel(const Array& params, bool fHelp)
}
+int64 GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
+{
+ // Get the set of pub keys that have the account
+ set<CScript> setPubKey;
+ CRITICAL_BLOCK(cs_mapAddressBook)
+ {
+ foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
+ {
+ const string& strAddress = item.first;
+ const string& strName = item.second;
+ if (strName == strAccount)
+ {
+ // We're only counting our own valid bitcoin addresses and not ip addresses
+ CScript scriptPubKey;
+ if (scriptPubKey.SetBitcoinAddress(strAddress))
+ if (IsMine(scriptPubKey))
+ setPubKey.insert(scriptPubKey);
+ }
+ }
+ }
+
+ int64 nBalance = 0;
+ CRITICAL_BLOCK(cs_mapWallet)
+ {
+ // Tally wallet transactions
+ for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx& wtx = (*it).second;
+ if (!wtx.IsFinal())
+ continue;
+
+ // Count generated blocks to account ""
+ if (wtx.IsCoinBase() && strAccount == "" && wtx.GetBlocksToMaturity() == 0)
+ {
+ nBalance += wtx.GetCredit();
+ continue;
+ }
+
+ // Tally received
+ foreach(const CTxOut& txout, wtx.vout)
+ if (setPubKey.count(txout.scriptPubKey))
+ if (wtx.GetDepthInMainChain() >= nMinDepth)
+ nBalance += txout.nValue;
+
+ // Tally sent
+ if (wtx.strFromAccount == strAccount)
+ {
+ int64 nNet = wtx.GetCredit() - wtx.GetDebit();
+ if (nNet < 0)
+ nBalance += nNet;
+ }
+ }
+
+ // Tally internal accounting entries
+ nBalance += walletdb.GetAccountCreditDebit(strAccount);
+ }
+
+ return nBalance;
+}
+
+int64 GetAccountBalance(const string& strAccount, int nMinDepth)
+{
+ CWalletDB walletdb;
+ return GetAccountBalance(walletdb, strAccount, nMinDepth);
+}
+
+
+Value getbalance(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() < 0 || params.size() > 2)
+ throw runtime_error(
+ "getbalance [account] [minconf=1]\n"
+ "If [account] is not specified, returns the server's total available balance.\n"
+ "If [account] is specified, returns the balance in the account.");
+
+ if (params.size() == 0)
+ return ((double)GetBalance() / (double)COIN);
+
+ string strAccount = params[0].get_str();
+ int nMinDepth = 1;
+ if (params.size() > 1)
+ nMinDepth = params[1].get_int();
+
+ int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
+
+ return (double)nBalance / (double)COIN;
+}
+
+
+Value movecmd(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() < 3 || params.size() > 5)
+ throw runtime_error(
+ "move <fromaccount> <toaccount> <amount> [minconf=1] [comment]\n"
+ "Move from one account in your wallet to another.");
+
+ string strFrom = params[0].get_str();
+ string strTo = params[1].get_str();
+ int64 nAmount = AmountFromValue(params[2]);
+ int nMinDepth = 1;
+ if (params.size() > 3)
+ nMinDepth = params[3].get_int();
+ string strComment;
+ if (params.size() > 4)
+ strComment = params[4].get_str();
+
+ CRITICAL_BLOCK(cs_mapWallet)
+ {
+ CWalletDB walletdb;
+ walletdb.TxnBegin();
+
+ // Check funds
+ if (!strFrom.empty())
+ {
+ int64 nBalance = GetAccountBalance(walletdb, strFrom, nMinDepth);
+ if (nAmount > nBalance)
+ throw JSONRPCError(-6, "Account has insufficient funds");
+ }
+ else
+ {
+ // move from "" account special case
+ int64 nBalance = GetAccountBalance(walletdb, strTo, nMinDepth);
+ if (nAmount > GetBalance() - nBalance)
+ throw JSONRPCError(-6, "Account has insufficient funds");
+ }
+
+ int64 nNow = GetAdjustedTime();
+
+ // Debit
+ CAccountingEntry debit;
+ debit.nCreditDebit = -nAmount;
+ debit.nTime = nNow;
+ debit.strOtherAccount = strTo;
+ debit.strComment = strComment;
+ walletdb.WriteAccountingEntry(strFrom, debit);
+
+ // Credit
+ CAccountingEntry credit;
+ credit.nCreditDebit = nAmount;
+ credit.nTime = nNow;
+ credit.strOtherAccount = strFrom;
+ credit.strComment = strComment;
+ walletdb.WriteAccountingEntry(strTo, credit);
+
+ walletdb.TxnCommit();
+ }
+ return true;
+}
+
+
+Value sendfrom(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() < 3 || params.size() > 6)
+ throw runtime_error(
+ "sendfrom <fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
+ "<amount> is a real and is rounded to the nearest 0.01");
+
+ string strAccount = params[0].get_str();
+ string strAddress = params[1].get_str();
+ int64 nAmount = AmountFromValue(params[2]);
+ int nMinDepth = 1;
+ if (params.size() > 3)
+ nMinDepth = params[3].get_int();
+
+ CWalletTx wtx;
+ wtx.strFromAccount = strAccount;
+ if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty())
+ wtx.mapValue["message"] = params[4].get_str();
+ if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty())
+ wtx.mapValue["to"] = params[5].get_str();
+
+ CRITICAL_BLOCK(cs_mapWallet)
+ {
+ // Check funds
+ int64 nBalance = GetAccountBalance(strAccount, nMinDepth);
+ if (nAmount > nBalance)
+ throw JSONRPCError(-6, "Account has insufficient funds");
+
+ // Send
+ string strError = SendMoneyToBitcoinAddress(strAddress, nAmount, wtx);
+ if (strError != "")
+ throw JSONRPCError(-4, strError);
+ }
+
+ return wtx.GetHash().GetHex();
+}
+
+
struct tallyitem
{
@@ -513,7 +748,7 @@ struct tallyitem
}
};
-Value ListReceived(const Array& params, bool fByLabels)
+Value ListReceived(const Array& params, bool fByAccounts)
{
// Minimum confirmations
int nMinDepth = 1;
@@ -555,13 +790,13 @@ Value ListReceived(const Array& params, bool fByLabels)
// Reply
Array ret;
- map<string, tallyitem> mapLabelTally;
+ map<string, tallyitem> mapAccountTally;
CRITICAL_BLOCK(cs_mapAddressBook)
{
foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
{
const string& strAddress = item.first;
- const string& strLabel = item.second;
+ const string& strAccount = item.second;
uint160 hash160;
if (!AddressToHash160(strAddress, hash160))
continue;
@@ -577,9 +812,9 @@ Value ListReceived(const Array& params, bool fByLabels)
nConf = (*it).second.nConf;
}
- if (fByLabels)
+ if (fByAccounts)
{
- tallyitem& item = mapLabelTally[strLabel];
+ tallyitem& item = mapAccountTally[strAccount];
item.nAmount += nAmount;
item.nConf = min(item.nConf, nConf);
}
@@ -587,7 +822,8 @@ Value ListReceived(const Array& params, bool fByLabels)
{
Object obj;
obj.push_back(Pair("address", strAddress));
- obj.push_back(Pair("label", strLabel));
+ obj.push_back(Pair("account", strAccount));
+ obj.push_back(Pair("label", strAccount)); // deprecated
obj.push_back(Pair("amount", (double)nAmount / (double)COIN));
obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
ret.push_back(obj);
@@ -595,14 +831,15 @@ Value ListReceived(const Array& params, bool fByLabels)
}
}
- if (fByLabels)
+ if (fByAccounts)
{
- for (map<string, tallyitem>::iterator it = mapLabelTally.begin(); it != mapLabelTally.end(); ++it)
+ for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it)
{
int64 nAmount = (*it).second.nAmount;
int nConf = (*it).second.nConf;
Object obj;
- obj.push_back(Pair("label", (*it).first));
+ obj.push_back(Pair("account", (*it).first));
+ obj.push_back(Pair("label", (*it).first)); // deprecated
obj.push_back(Pair("amount", (double)nAmount / (double)COIN));
obj.push_back(Pair("confirmations", (nConf == INT_MAX ? 0 : nConf)));
ret.push_back(obj);
@@ -621,23 +858,23 @@ Value listreceivedbyaddress(const Array& params, bool fHelp)
"[includeempty] whether to include addresses that haven't received any payments.\n"
"Returns an array of objects containing:\n"
" \"address\" : receiving address\n"
- " \"label\" : the label of the 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");
return ListReceived(params, false);
}
-Value listreceivedbylabel(const Array& params, bool fHelp)
+Value listreceivedbyaccount(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 2)
throw runtime_error(
- "listreceivedbylabel [minconf=1] [includeempty=false]\n"
+ "listreceivedbyaccount [minconf=1] [includeempty=false]\n"
"[minconf] is the minimum number of confirmations before payments are included.\n"
- "[includeempty] whether to include labels that haven't received any payments.\n"
+ "[includeempty] whether to include accounts that haven't received any payments.\n"
"Returns an array of objects containing:\n"
- " \"label\" : the label of the receiving addresses\n"
- " \"amount\" : total amount received by addresses with this label\n"
+ " \"account\" : the account of the receiving addresses\n"
+ " \"amount\" : total amount received by addresses with this account\n"
" \"confirmations\" : number of confirmations of the most recent transaction included");
return ListReceived(params, true);
@@ -675,8 +912,14 @@ Value validateaddress(const Array& params, bool fHelp)
{
// Call Hash160ToAddress() so we always return current ADDRESSVERSION
// version of the address:
- ret.push_back(Pair("address", Hash160ToAddress(hash160)));
+ string currentAddress = Hash160ToAddress(hash160);
+ ret.push_back(Pair("address", currentAddress));
ret.push_back(Pair("ismine", (mapPubKeys.count(hash160) > 0)));
+ CRITICAL_BLOCK(cs_mapAddressBook)
+ {
+ if (mapAddressBook.count(currentAddress))
+ ret.push_back(Pair("account", mapAddressBook[currentAddress]));
+ }
}
return ret;
}
@@ -703,24 +946,32 @@ pair<string, rpcfn_type> pCallTable[] =
make_pair("getblocknumber", &getblocknumber),
make_pair("getconnectioncount", &getconnectioncount),
make_pair("getdifficulty", &getdifficulty),
- make_pair("getbalance", &getbalance),
make_pair("getgenerate", &getgenerate),
make_pair("setgenerate", &setgenerate),
make_pair("gethashespersec", &gethashespersec),
make_pair("getinfo", &getinfo),
make_pair("getnewaddress", &getnewaddress),
- make_pair("setlabel", &setlabel),
- make_pair("getlabel", &getlabel),
- make_pair("getaddressesbylabel", &getaddressesbylabel),
+ make_pair("getaccountaddress", &getaccountaddress),
+ make_pair("setaccount", &setaccount),
+ make_pair("setlabel", &setaccount), // deprecated
+ make_pair("getaccount", &getaccount),
+ make_pair("getlabel", &getaccount), // deprecated
+ make_pair("getaddressesbyaccount", &getaddressesbyaccount),
+ make_pair("getaddressesbylabel", &getaddressesbyaccount), // deprecated
make_pair("sendtoaddress", &sendtoaddress),
make_pair("getamountreceived", &getreceivedbyaddress), // deprecated, renamed to getreceivedbyaddress
make_pair("getallreceived", &listreceivedbyaddress), // deprecated, renamed to listreceivedbyaddress
make_pair("getreceivedbyaddress", &getreceivedbyaddress),
- make_pair("getreceivedbylabel", &getreceivedbylabel),
+ make_pair("getreceivedbyaccount", &getreceivedbyaccount),
+ make_pair("getreceivedbylabel", &getreceivedbyaccount), // deprecated
make_pair("listreceivedbyaddress", &listreceivedbyaddress),
- make_pair("listreceivedbylabel", &listreceivedbylabel),
+ make_pair("listreceivedbyaccount", &listreceivedbyaccount),
+ make_pair("listreceivedbylabel", &listreceivedbyaccount), // deprecated
make_pair("backupwallet", &backupwallet),
make_pair("validateaddress", &validateaddress),
+ make_pair("getbalance", &getbalance),
+ make_pair("move", &movecmd),
+ make_pair("sendfrom", &sendfrom),
};
map<string, rpcfn_type> mapCallTable(pCallTable, pCallTable + sizeof(pCallTable)/sizeof(pCallTable[0]));
@@ -737,9 +988,12 @@ string pAllowInSafeMode[] =
"gethashespersec",
"getinfo",
"getnewaddress",
+ "getaccountaddress",
"setlabel",
- "getlabel",
- "getaddressesbylabel",
+ "getaccount",
+ "getlabel", // deprecated
+ "getaddressesbyaccount",
+ "getaddressesbylabel", // deprecated
"backupwallet",
"validateaddress",
};
@@ -1339,13 +1593,21 @@ int CommandLineRPC(int argc, char *argv[])
if (strMethod == "listtransactions" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "getamountreceived" && n > 1) ConvertTo<boost::int64_t>(params[1]); // deprecated
if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
- if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "getreceivedbylabel" && n > 1) ConvertTo<boost::int64_t>(params[1]); // deprecated
if (strMethod == "getallreceived" && n > 0) ConvertTo<boost::int64_t>(params[0]); // deprecated
if (strMethod == "getallreceived" && n > 1) ConvertTo<bool>(params[1]);
if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
- if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo<boost::int64_t>(params[0]);
- if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo<bool>(params[1]);
+ if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
+ if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
+ if (strMethod == "listreceivedbylabel" && n > 0) ConvertTo<boost::int64_t>(params[0]); // deprecated
+ if (strMethod == "listreceivedbylabel" && n > 1) ConvertTo<bool>(params[1]); // deprecated
+ if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
+ if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
+ if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
+ if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
+ if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
// Execute
Object reply = CallRPC(strMethod, params);
diff --git a/serialize.h b/serialize.h
index 05d7303058..dde2aeb9a1 100644
--- a/serialize.h
+++ b/serialize.h
@@ -22,7 +22,7 @@ class CDataStream;
class CAutoFile;
static const unsigned int MAX_SIZE = 0x02000000;
-static const int VERSION = 31505;
+static const int VERSION = 31600;
static const char* pszSubVer = "";