// Copyright (c) 2009-2011 Satoshi Nakamoto & Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_WALLET_H #define BITCOIN_WALLET_H #include "bignum.h" #include "script.h" class CWalletTx; class CReserveKey; class CWalletDB; extern std::map mapWallet; extern std::vector vWalletUpdated; extern CCriticalSection cs_mapWallet; extern std::map mapRequestCount; extern CCriticalSection cs_mapRequestCount; extern std::map mapAddressBook; extern CCriticalSection cs_mapAddressBook; extern std::vector vchDefaultKey; extern CKey keyUser; bool AddToWallet(const CWalletTx& wtxIn); bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false); bool EraseFromWallet(uint256 hash); void WalletUpdateSpent(const COutPoint& prevout); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); void ResendWalletTransactions(); int64 GetBalance(); bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); bool BroadcastTransaction(CWalletTx& wtxNew); std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); std::string SendMoneyToBitcoinAddress(std::string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); // // A transaction with a bunch of additional info that only the owner cares // about. It includes any unrecorded transactions needed to link it back // to the block chain. // class CWalletTx : public CMerkleTx { public: std::vector vtxPrev; std::map mapValue; std::vector > vOrderForm; unsigned int fTimeReceivedIsTxTime; unsigned int nTimeReceived; // time received by this node char fFromMe; std::string strFromAccount; std::vector vfSpent; // memory only mutable char fDebitCached; mutable char fCreditCached; mutable char fAvailableCreditCached; mutable char fChangeCached; mutable int64 nDebitCached; mutable int64 nCreditCached; mutable int64 nAvailableCreditCached; mutable int64 nChangeCached; // memory only UI hints mutable unsigned int nTimeDisplayed; mutable int nLinesDisplayed; mutable char fConfirmedDisplayed; CWalletTx() { Init(); } CWalletTx(const CMerkleTx& txIn) : CMerkleTx(txIn) { Init(); } CWalletTx(const CTransaction& txIn) : CMerkleTx(txIn) { Init(); } void Init() { vtxPrev.clear(); mapValue.clear(); vOrderForm.clear(); fTimeReceivedIsTxTime = false; nTimeReceived = 0; fFromMe = false; strFromAccount.clear(); vfSpent.clear(); fDebitCached = false; fCreditCached = false; fAvailableCreditCached = false; fChangeCached = false; nDebitCached = 0; nCreditCached = 0; nAvailableCreditCached = 0; nChangeCached = 0; nTimeDisplayed = 0; nLinesDisplayed = 0; fConfirmedDisplayed = false; } IMPLEMENT_SERIALIZE ( CWalletTx* pthis = const_cast(this); if (fRead) pthis->Init(); char fSpent = false; if (!fRead) { pthis->mapValue["fromaccount"] = pthis->strFromAccount; std::string str; BOOST_FOREACH(char f, vfSpent) { str += (f ? '1' : '0'); if (f) fSpent = true; } pthis->mapValue["spent"] = str; } nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action); READWRITE(vtxPrev); READWRITE(mapValue); READWRITE(vOrderForm); READWRITE(fTimeReceivedIsTxTime); READWRITE(nTimeReceived); READWRITE(fFromMe); READWRITE(fSpent); if (fRead) { pthis->strFromAccount = pthis->mapValue["fromaccount"]; if (mapValue.count("spent")) BOOST_FOREACH(char c, pthis->mapValue["spent"]) pthis->vfSpent.push_back(c != '0'); else pthis->vfSpent.assign(vout.size(), fSpent); } pthis->mapValue.erase("fromaccount"); pthis->mapValue.erase("version"); pthis->mapValue.erase("spent"); ) // marks certain txout's as spent // returns true if any update took place bool UpdateSpent(const std::vector& vfNewSpent) { bool fReturn = false; for (int i=0; i < vfNewSpent.size(); i++) { if (i == vfSpent.size()) break; if (vfNewSpent[i] && !vfSpent[i]) { vfSpent[i] = true; fReturn = true; fAvailableCreditCached = false; } } return fReturn; } void MarkDirty() { fCreditCached = false; fAvailableCreditCached = false; fDebitCached = false; fChangeCached = false; } void MarkSpent(unsigned int nOut) { if (nOut >= vout.size()) throw std::runtime_error("CWalletTx::MarkSpent() : nOut out of range"); vfSpent.resize(vout.size()); if (!vfSpent[nOut]) { vfSpent[nOut] = true; fAvailableCreditCached = false; } } bool IsSpent(unsigned int nOut) const { if (nOut >= vout.size()) throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range"); if (nOut >= vfSpent.size()) return false; return (!!vfSpent[nOut]); } int64 GetDebit() const { if (vin.empty()) return 0; if (fDebitCached) return nDebitCached; nDebitCached = CTransaction::GetDebit(); fDebitCached = true; return nDebitCached; } int64 GetCredit(bool fUseCache=true) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if (IsCoinBase() && GetBlocksToMaturity() > 0) return 0; // GetBalance can assume transactions in mapWallet won't change if (fUseCache && fCreditCached) return nCreditCached; nCreditCached = CTransaction::GetCredit(); fCreditCached = true; return nCreditCached; } int64 GetAvailableCredit(bool fUseCache=true) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if (IsCoinBase() && GetBlocksToMaturity() > 0) return 0; if (fUseCache && fAvailableCreditCached) return nAvailableCreditCached; int64 nCredit = 0; for (int i = 0; i < vout.size(); i++) { if (!IsSpent(i)) { const CTxOut &txout = vout[i]; nCredit += txout.GetCredit(); if (!MoneyRange(nCredit)) throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); } } nAvailableCreditCached = nCredit; fAvailableCreditCached = true; return nCredit; } int64 GetChange() const { if (fChangeCached) return nChangeCached; nChangeCached = CTransaction::GetChange(); fChangeCached = true; return nChangeCached; } void GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, std::list >& listReceived, std::list >& listSent, int64& nFee, std::string& strSentAccount) const; void GetAccountAmounts(const std::string& strAccount, int64& nGenerated, int64& nReceived, int64& nSent, int64& nFee) const; bool IsFromMe() const { return (GetDebit() > 0); } bool IsConfirmed() const { // Quick answer in most cases if (!IsFinal()) return false; if (GetDepthInMainChain() >= 1) return true; if (!IsFromMe()) // using wtx's cached debit return false; // If no confirmations but it's from us, we can still // consider it confirmed if all dependencies are confirmed std::map mapPrev; std::vector vWorkQueue; vWorkQueue.reserve(vtxPrev.size()+1); vWorkQueue.push_back(this); for (int i = 0; i < vWorkQueue.size(); i++) { const CMerkleTx* ptx = vWorkQueue[i]; if (!ptx->IsFinal()) return false; if (ptx->GetDepthInMainChain() >= 1) continue; if (!ptx->IsFromMe()) return false; if (mapPrev.empty()) BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) mapPrev[tx.GetHash()] = &tx; BOOST_FOREACH(const CTxIn& txin, ptx->vin) { if (!mapPrev.count(txin.prevout.hash)) return false; vWorkQueue.push_back(mapPrev[txin.prevout.hash]); } } return true; } bool WriteToDisk(); int64 GetTxTime() const; int GetRequestCount() const; void AddSupportingTransactions(CTxDB& txdb); bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true); bool AcceptWalletTransaction(); void RelayWalletTransaction(CTxDB& txdb); void RelayWalletTransaction(); }; // // Private key that includes an expiration date in case it never gets used. // class CWalletKey { public: CPrivKey vchPrivKey; int64 nTimeCreated; int64 nTimeExpires; std::string strComment; //// todo: add something to note what created it (user, getnewaddress, change) //// maybe should have a map property map CWalletKey(int64 nExpires=0) { nTimeCreated = (nExpires ? GetTime() : 0); nTimeExpires = nExpires; } IMPLEMENT_SERIALIZE ( if (!(nType & SER_GETHASH)) READWRITE(nVersion); READWRITE(vchPrivKey); READWRITE(nTimeCreated); READWRITE(nTimeExpires); READWRITE(strComment); ) }; // // Account information. // Stored in wallet with key "acc"+string account name // class CAccount { public: std::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: std::string strAccount; int64 nCreditDebit; int64 nTime; std::string strOtherAccount; std::string strComment; CAccountingEntry() { SetNull(); } void SetNull() { nCreditDebit = 0; nTime = 0; strAccount.clear(); strOtherAccount.clear(); strComment.clear(); } IMPLEMENT_SERIALIZE ( if (!(nType & SER_GETHASH)) READWRITE(nVersion); // Note: strAccount is serialized as part of the key, not here. READWRITE(nCreditDebit); READWRITE(nTime); READWRITE(strOtherAccount); READWRITE(strComment); ) }; #endif