From e4ff4e6898d378b1a3e83791034a7af455fde3ab Mon Sep 17 00:00:00 2001 From: gavinandresen Date: Mon, 22 Nov 2010 15:53:20 +0000 Subject: Depracate "label" API, replacing with account New RPC methods: move, sendfrom Change to getbalance (now takes optional [account] argument) Renamed methods with "label" in their names. sendtoaddress returns hexadecimal transaction ID instead of "sent". git-svn-id: https://bitcoin.svn.sourceforge.net/svnroot/bitcoin/trunk@188 1a98c847-1fd6-4fd8-948a-caf3550aa51b --- db.cpp | 81 +++++++++++- db.h | 11 +- main.h | 92 ++++++++++++- rpc.cpp | 420 ++++++++++++++++++++++++++++++++++++++++++++++++------------ serialize.h | 2 +- 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 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 vchPubKey; @@ -894,18 +963,20 @@ void CWalletDB::ReturnKey(int64 nIndex) vector 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 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 vtxPrev; map mapValue; vector > 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(this)->fTimeReceivedIsTxTime = nVersion; READWRITE(nTimeReceived); READWRITE(fFromMe); READWRITE(fSpent); + if (nVersion >= 31404) + { + READWRITE(fTimeReceivedIsTxTime); + READWRITE(fUnused); + READWRITE(strFromAccount); + } ) int64 GetDebit() const @@ -1536,6 +1545,79 @@ public: +// +// Account information. +// Stored in wallet with key "acc"+string account name +// +class CAccount +{ +public: + vector vchPubKey; + + CAccount() + { + SetNull(); + } + + void SetNull() + { + vchPubKey.clear(); + } + + IMPLEMENT_SERIALIZE + ( + if (!(nType & SER_GETHASH)) + READWRITE(nVersion); + READWRITE(vchPubKey); + ) +}; + + + +// +// Internal transfers. +// Database key is acentry +// +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 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 \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::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