// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "txdb.h" #include "wallet.h" #include "walletdb.h" #include "crypter.h" #include "ui_interface.h" #include "base58.h" #include "kernel.h" #include "coincontrol.h" #include #include #include "main.h" using namespace std; extern int64_t nReserveBalance; ////////////////////////////////////////////////////////////////////////////// // // mapWallet // struct CompareValueOnly { bool operator()(const pair >& t1, const pair >& t2) const { return t1.first < t2.first; } }; const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const { LOCK(cs_wallet); std::map::const_iterator it = mapWallet.find(hash); if (it == mapWallet.end()) return NULL; return &(it->second); } CPubKey CWallet::GenerateNewKey() { bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets RandAddSeedPerfmon(); CKey key; key.MakeNewKey(fCompressed); // Compressed public keys were introduced in version 0.6.0 if (fCompressed) SetMinVersion(FEATURE_COMPRPUBKEY); CPubKey pubkey = key.GetPubKey(); // Create new metadata int64_t nCreationTime = GetTime(); mapKeyMetadata[CBitcoinAddress(pubkey.GetID())] = CKeyMetadata(nCreationTime); if (!nTimeFirstKey || nCreationTime < nTimeFirstKey) nTimeFirstKey = nCreationTime; if (!AddKey(key)) throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); return key.GetPubKey(); } CMalleableKeyView CWallet::GenerateNewMalleableKey() { RandAddSeedPerfmon(); // Compressed public keys were introduced in version 0.6.0 SetMinVersion(FEATURE_MALLKEY); CMalleableKey mKey; mKey.MakeNewKeys(); const CMalleableKeyView &keyView(mKey); // Create new metadata int64_t nCreationTime = GetTime(); mapKeyMetadata[CBitcoinAddress(keyView.GetMalleablePubKey())] = CKeyMetadata(nCreationTime); if (!nTimeFirstKey || nCreationTime < nTimeFirstKey) nTimeFirstKey = nCreationTime; if (!AddKey(mKey)) throw std::runtime_error("CWallet::GenerateNewMalleableKey() : AddKey failed"); return CMalleableKeyView(mKey); } bool CWallet::AddKey(const CKey& key) { CPubKey pubkey = key.GetPubKey(); if (!CCryptoKeyStore::AddKey(key)) return false; if (!fFileBacked) return true; if (!IsCrypted()) return CWalletDB(strWalletFile).WriteKey(pubkey, key.GetPrivKey(), mapKeyMetadata[CBitcoinAddress(pubkey.GetID())]); return true; } bool CWallet::AddKey(const CMalleableKey& mKey) { CMalleableKeyView keyView = CMalleableKeyView(mKey); CSecret vchSecretH = mKey.GetSecretH(); if (!CCryptoKeyStore::AddMalleableKey(keyView, vchSecretH)) return false; if (!fFileBacked) return true; if (!IsCrypted()) return CWalletDB(strWalletFile).WriteMalleableKey(keyView, vchSecretH, mapKeyMetadata[CBitcoinAddress(keyView.GetMalleablePubKey())]); return true; } bool CWallet::AddCryptedMalleableKey(const CMalleableKeyView& keyView, const std::vector &vchCryptedSecretH) { if (!CCryptoKeyStore::AddCryptedMalleableKey(keyView, vchCryptedSecretH)) return false; if (!fFileBacked) return true; { LOCK(cs_wallet); CBitcoinAddress addr(keyView.GetMalleablePubKey()); if (pwalletdbEncryption) return pwalletdbEncryption->WriteCryptedMalleableKey(keyView, vchCryptedSecretH, mapKeyMetadata[addr]); else return CWalletDB(strWalletFile).WriteCryptedMalleableKey(keyView, vchCryptedSecretH, mapKeyMetadata[addr]); } return true; } bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector &vchCryptedSecret) { if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; // check if we need to remove from watch-only CScript script; script.SetDestination(vchPubKey.GetID()); if (HaveWatchOnly(script)) RemoveWatchOnly(script); if (!fFileBacked) return true; { LOCK(cs_wallet); CBitcoinAddress addr(vchPubKey.GetID()); if (pwalletdbEncryption) return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[addr]); else return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[addr]); } return false; } bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta) { if (meta.nCreateTime && (!nTimeFirstKey || meta.nCreateTime < nTimeFirstKey)) nTimeFirstKey = meta.nCreateTime; mapKeyMetadata[CBitcoinAddress(pubkey.GetID())] = meta; return true; } bool CWallet::LoadKeyMetadata(const CMalleableKeyView &keyView, const CKeyMetadata &metadata) { if (metadata.nCreateTime && (!nTimeFirstKey || metadata.nCreateTime < nTimeFirstKey)) nTimeFirstKey = metadata.nCreateTime; mapKeyMetadata[CBitcoinAddress(keyView.GetMalleablePubKey())] = metadata; return true; } bool CWallet::AddCScript(const CScript& redeemScript) { if (!CCryptoKeyStore::AddCScript(redeemScript)) return false; if (!fFileBacked) return true; return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); } bool CWallet::LoadCScript(const CScript& redeemScript) { /* A sanity check was added in commit 5ed0a2b to avoid adding redeemScripts * that never can be redeemed. However, old wallets may still contain * these. Do not add them to the wallet and warn. */ if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) { std::string strAddr = CBitcoinAddress(redeemScript.GetID()).ToString(); printf("LoadCScript() : Warning: This wallet contains a redeemScript of size %" PRIszu " which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr.c_str()); return true; } return CCryptoKeyStore::AddCScript(redeemScript); } bool CWallet::AddWatchOnly(const CScript &dest) { if (!CCryptoKeyStore::AddWatchOnly(dest)) return false; nTimeFirstKey = 1; // No birthday information for watch-only keys. NotifyWatchonlyChanged(true); if (!fFileBacked) return true; return CWalletDB(strWalletFile).WriteWatchOnly(dest); } bool CWallet::RemoveWatchOnly(const CScript &dest) { LOCK(cs_wallet); if (!CCryptoKeyStore::RemoveWatchOnly(dest)) return false; if (!HaveWatchOnly()) NotifyWatchonlyChanged(false); if (fFileBacked) if (!CWalletDB(strWalletFile).EraseWatchOnly(dest)) return false; return true; } bool CWallet::LoadWatchOnly(const CScript &dest) { return CCryptoKeyStore::AddWatchOnly(dest); } // ppcoin: optional setting to unlock wallet for block minting only; // serves to disable the trivial sendmoney when OS account compromised bool fWalletUnlockMintOnly = false; bool CWallet::Unlock(const SecureString& strWalletPassphrase) { if (!IsLocked()) return false; CCrypter crypter; CKeyingMaterial vMasterKey; { LOCK(cs_wallet); BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) { if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) return false; if (CCryptoKeyStore::Unlock(vMasterKey)) return true; } } return false; } bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) { bool fWasLocked = IsLocked(); { LOCK(cs_wallet); Lock(); CCrypter crypter; CKeyingMaterial vMasterKey; BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys) { if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) return false; if (CCryptoKeyStore::Unlock(vMasterKey)) { int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); double nFirstMultiplier = 1e2 / (GetTimeMillis() - nStartTime); pMasterKey.second.nDeriveIterations = (uint32_t)(pMasterKey.second.nDeriveIterations *nFirstMultiplier); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); double nSecondMultiplier = 1e2 / (GetTimeMillis() - nStartTime); pMasterKey.second.nDeriveIterations = (uint32_t)((pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * nSecondMultiplier) / 2); if (pMasterKey.second.nDeriveIterations < 25000) pMasterKey.second.nDeriveIterations = 25000; printf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey)) return false; CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second); if (fWasLocked) Lock(); return true; } } } return false; } void CWallet::SetBestChain(const CBlockLocator& loc) { CWalletDB walletdb(strWalletFile); walletdb.WriteBestBlock(loc); } // This class implements an addrIncoming entry that causes pre-0.4 // clients to crash on startup if reading a private-key-encrypted wallet. class CCorruptAddress { public: IMPLEMENT_SERIALIZE ( if (nType & SER_DISK) READWRITE(nVersion); ) }; bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) { if (nWalletVersion >= nVersion) return true; // when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way if (fExplicit && nVersion > nWalletMaxVersion) nVersion = FEATURE_LATEST; nWalletVersion = nVersion; if (nVersion > nWalletMaxVersion) nWalletMaxVersion = nVersion; if (fFileBacked) { CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); if (nWalletVersion > 40000) pwalletdb->WriteMinVersion(nWalletVersion); if (!pwalletdbIn) delete pwalletdb; } return true; } bool CWallet::SetMaxVersion(int nVersion) { // cannot downgrade below current version if (nWalletVersion > nVersion) return false; nWalletMaxVersion = nVersion; return true; } bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { if (IsCrypted()) return false; CKeyingMaterial vMasterKey; RandAddSeedPerfmon(); vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); CMasterKey kMasterKey; RandAddSeedPerfmon(); kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); CCrypter crypter; int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); int64_t nDivider = GetTimeMillis() - nStartTime; kMasterKey.nDeriveIterations = (uint32_t)(25e5 / (double)(nDivider)); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); double nMultiplier = 1e2 / (GetTimeMillis() - nStartTime); kMasterKey.nDeriveIterations = (uint32_t)((kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * nMultiplier) / 2); if (kMasterKey.nDeriveIterations < 25000) kMasterKey.nDeriveIterations = 25000; printf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod)) return false; if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey)) return false; { LOCK(cs_wallet); mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; if (fFileBacked) { pwalletdbEncryption = new CWalletDB(strWalletFile); if (!pwalletdbEncryption->TxnBegin()) return false; pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); } if (!EncryptKeys(vMasterKey)) { if (fFileBacked) pwalletdbEncryption->TxnAbort(); exit(1); //We now probably have half of our keys encrypted in memory, and half not...die and let the user reload their unencrypted wallet. } // Encryption was introduced in version 0.4.0 SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); if (fFileBacked) { if (!pwalletdbEncryption->TxnCommit()) exit(1); //We now have keys encrypted in memory, but no on disk...die to avoid confusion and let the user reload their unencrypted wallet. delete pwalletdbEncryption; pwalletdbEncryption = NULL; } Lock(); Unlock(strWalletPassphrase); NewKeyPool(); Lock(); // Need to completely rewrite the wallet file; if we don't, bdb might keep // bits of the unencrypted private key in slack space in the database file. CDB::Rewrite(strWalletFile); } NotifyStatusChanged(this); return true; } bool CWallet::DecryptWallet(const SecureString& strWalletPassphrase) { if (!IsCrypted()) return false; CCrypter crypter; CKeyingMaterial vMasterKey; { LOCK(cs_wallet); BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) { if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) return false; if (!CCryptoKeyStore::Unlock(vMasterKey)) return false; } if (fFileBacked) { pwalletdbDecryption = new CWalletDB(strWalletFile); if (!pwalletdbDecryption->TxnBegin()) return false; } if (!DecryptKeys(vMasterKey)) { if (fFileBacked) pwalletdbDecryption->TxnAbort(); exit(1); //We now probably have half of our keys decrypted in memory, and half not...die and let the user reload their encrypted wallet. } if (fFileBacked) { // Overwrite crypted keys KeyMap::const_iterator mi = mapKeys.begin(); while (mi != mapKeys.end()) { CKey key; key.SetSecret((*mi).second.first, (*mi).second.second); pwalletdbDecryption->EraseCryptedKey(key.GetPubKey()); pwalletdbDecryption->WriteKey(key.GetPubKey(), key.GetPrivKey(), mapKeyMetadata[CBitcoinAddress(mi->first)]); mi++; } MalleableKeyMap::const_iterator mi2 = mapMalleableKeys.begin(); while (mi2 != mapMalleableKeys.end()) { const CSecret &vchSecretH = mi2->second; const CMalleableKeyView &keyView = mi2->first; pwalletdbDecryption->EraseCryptedMalleableKey(keyView); pwalletdbDecryption->WriteMalleableKey(keyView, vchSecretH, mapKeyMetadata[CBitcoinAddress(keyView.GetMalleablePubKey())]); mi2++; } // Erase master keys MasterKeyMap::const_iterator mk = mapMasterKeys.begin(); while (mk != mapMasterKeys.end()) { pwalletdbDecryption->EraseMasterKey((*mk).first); mk++; } if (!pwalletdbDecryption->TxnCommit()) exit(1); //We now have keys decrypted in memory, but no on disk...die to avoid confusion and let the user reload their encrypted wallet. delete pwalletdbDecryption; pwalletdbDecryption = NULL; } // Need to completely rewrite the wallet file; if we don't, bdb might keep // encrypted private keys in the database file which can be a reason of consistency issues. CDB::Rewrite(strWalletFile); } NotifyStatusChanged(this); return true; } bool CWallet::GetPEM(const CKeyID &keyID, const std::string &fileName, const SecureString &strPassKey) const { BIO *pemOut = BIO_new_file(fileName.c_str(), "w"); if (pemOut == NULL) return error("GetPEM() : failed to create file %s\n", fileName.c_str()); CKey key; if (!GetKey(keyID, key)) return error("GetPEM() : failed to get key for address=%s\n", CBitcoinAddress(keyID).ToString().c_str()); bool result = key.WritePEM(pemOut, strPassKey); BIO_free(pemOut); return result; } int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) { int64_t nRet = nOrderPosNext++; if (pwalletdb) { pwalletdb->WriteOrderPosNext(nOrderPosNext); } else { CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); } return nRet; } CWallet::TxItems CWallet::OrderedTxItems(std::list& acentries, std::string strAccount) { CWalletDB walletdb(strWalletFile); // First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap. TxItems txOrdered; // Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry // would make this much faster for applications that do this a lot. for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { CWalletTx* wtx = &((*it).second); txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0))); } acentries.clear(); walletdb.ListAccountCreditDebit(strAccount, acentries); BOOST_FOREACH(CAccountingEntry& entry, acentries) { txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry))); } return txOrdered; } void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock) { // Anytime a signature is successfully verified, it's proof the outpoint is spent. // Update the wallet spent flag if it doesn't know due to wallet.dat being // restored from backup or the user making copies of wallet.dat. { LOCK(cs_wallet); BOOST_FOREACH(const CTxIn& txin, tx.vin) { map::iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { CWalletTx& wtx = (*mi).second; if (txin.prevout.n >= wtx.vout.size()) printf("WalletUpdateSpent: bad wtx %s\n", wtx.GetHash().ToString().c_str()); else if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) { printf("WalletUpdateSpent found spent coin %snvc %s\n", FormatMoney(wtx.GetCredit(MINE_ALL)).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkSpent(txin.prevout.n); wtx.WriteToDisk(); NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED); vMintingWalletUpdated.push_back(txin.prevout.hash); } } } if (fBlock) { uint256 hash = tx.GetHash(); map::iterator mi = mapWallet.find(hash); CWalletTx& wtx = (*mi).second; BOOST_FOREACH(const CTxOut& txout, tx.vout) { if (IsMine(txout)) { wtx.MarkUnspent(&txout - &tx.vout[0]); wtx.WriteToDisk(); NotifyTransactionChanged(this, hash, CT_UPDATED); vMintingWalletUpdated.push_back(hash); } } } } } void CWallet::MarkDirty() { { LOCK(cs_wallet); BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) item.second.MarkDirty(); } } bool CWallet::AddToWallet(const CWalletTx& wtxIn) { uint256 hash = wtxIn.GetHash(); { LOCK(cs_wallet); // Inserts only if not already there, returns tx inserted or tx found pair::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); CWalletTx& wtx = (*ret.first).second; wtx.BindWallet(this); bool fInsertedNew = ret.second; if (fInsertedNew) { wtx.nTimeReceived = GetAdjustedTime(); wtx.nOrderPos = IncOrderPosNext(); wtx.nTimeSmart = wtx.nTimeReceived; if (wtxIn.hashBlock != 0) { if (mapBlockIndex.count(wtxIn.hashBlock)) { unsigned int latestNow = wtx.nTimeReceived; unsigned int latestEntry = 0; { // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future int64_t latestTolerated = latestNow + 300; std::list acentries; TxItems txOrdered = OrderedTxItems(acentries); for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { CWalletTx *const pwtx = (*it).second.first; if (pwtx == &wtx) continue; CAccountingEntry *const pacentry = (*it).second.second; int64_t nSmartTime; if (pwtx) { nSmartTime = pwtx->nTimeSmart; if (!nSmartTime) nSmartTime = pwtx->nTimeReceived; } else nSmartTime = pacentry->nTime; if (nSmartTime <= latestTolerated) { latestEntry = nSmartTime; if (nSmartTime > latestNow) latestNow = nSmartTime; break; } } } unsigned int& blocktime = mapBlockIndex[wtxIn.hashBlock]->nTime; wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); } else printf("AddToWallet() : found %s in block %s not in index\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), wtxIn.hashBlock.ToString().c_str()); } } bool fUpdated = false; if (!fInsertedNew) { // Merge if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) { wtx.hashBlock = wtxIn.hashBlock; fUpdated = true; } if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) { wtx.vMerkleBranch = wtxIn.vMerkleBranch; wtx.nIndex = wtxIn.nIndex; fUpdated = true; } if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) { wtx.fFromMe = wtxIn.fFromMe; fUpdated = true; } fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent); } //// debug print printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); // Write to disk if (fInsertedNew || fUpdated) if (!wtx.WriteToDisk()) return false; #ifndef QT_GUI // If default receiving address gets used, replace it with a new one CScript scriptDefaultKey; scriptDefaultKey.SetDestination(vchDefaultKey.GetID()); BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (txout.scriptPubKey == scriptDefaultKey) { CPubKey newDefaultKey; if (GetKeyFromPool(newDefaultKey, false)) { SetDefaultKey(newDefaultKey); SetAddressBookName(vchDefaultKey.GetID(), ""); } } } #endif // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins WalletUpdateSpent(wtx, (wtxIn.hashBlock != 0)); // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); vMintingWalletUpdated.push_back(hash); // notify an external script when a wallet transaction comes in or is updated std::string strCmd = GetArg("-walletnotify", ""); if ( !strCmd.empty()) { boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); boost::thread t(runCommand, strCmd); // thread runs free } } return true; } // Add a transaction to the wallet, or update it. // pblock is optional, but should be provided if the transaction is known to be in a block. // If fUpdate is true, existing transactions will be updated. bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) { uint256 hash = tx.GetHash(); { LOCK(cs_wallet); bool fExisted = mapWallet.count(hash) != 0; if (fExisted && !fUpdate) return false; if (fExisted || IsMine(tx) || IsFromMe(tx)) { CWalletTx wtx(this,tx); // Get merkle branch if transaction was found in a block if (pblock) wtx.SetMerkleBranch(*pblock); return AddToWallet(wtx); } else WalletUpdateSpent(tx); } return false; } bool CWallet::EraseFromWallet(uint256 hash) { if (!fFileBacked) return false; { LOCK(cs_wallet); if (mapWallet.erase(hash)) CWalletDB(strWalletFile).EraseTx(hash); } return true; } isminetype CWallet::IsMine(const CTxIn &txin) const { { LOCK(cs_wallet); map::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { const CWalletTx& prev = (*mi).second; if (txin.prevout.n < prev.vout.size()) return IsMine(prev.vout[txin.prevout.n]); } } return MINE_NO; } // marks certain txout's as spent // returns true if any update took place bool CWalletTx::UpdateSpent(const std::vector& vfNewSpent) { bool fReturn = false; for (unsigned int i = 0; i < vfNewSpent.size(); i++) { if (i == vfSpent.size()) break; if (vfNewSpent[i] && !vfSpent[i]) { vfSpent[i] = true; fReturn = true; fAvailableCreditCached = fAvailableWatchCreditCached = false; } } return fReturn; } // make sure balances are recalculated void CWalletTx::MarkDirty() { fCreditCached = false; fAvailableCreditCached = fAvailableWatchCreditCached = false; fDebitCached = fWatchDebitCached = false; fChangeCached = false; } void CWalletTx::BindWallet(CWallet *pwalletIn) { pwallet = pwalletIn; MarkDirty(); } void CWalletTx::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 = fAvailableWatchCreditCached = false; } } void CWalletTx::MarkUnspent(unsigned int nOut) { if (nOut >= vout.size()) throw std::runtime_error("CWalletTx::MarkUnspent() : nOut out of range"); vfSpent.resize(vout.size()); if (vfSpent[nOut]) { vfSpent[nOut] = false; fAvailableCreditCached = fAvailableWatchCreditCached = false; } } bool CWalletTx::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_t CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const { { LOCK(cs_wallet); map::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { const CWalletTx& prev = (*mi).second; if (txin.prevout.n < prev.vout.size()) if (IsMine(prev.vout[txin.prevout.n]) & filter) return prev.vout[txin.prevout.n].nValue; } } return 0; } isminetype CWallet::IsMine(const CTxOut& txout) const { return ::IsMine(*this, txout.scriptPubKey); } int64_t CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const { if (!MoneyRange(txout.nValue)) throw std::runtime_error("CWallet::GetCredit() : value out of range"); return (IsMine(txout) & filter ? txout.nValue : 0); } bool CWallet::IsChange(const CTxOut& txout) const { // TODO: fix handling of 'change' outputs. The assumption is that any // payment to a script that is ours, but isn't in the address book // is change. That assumption is likely to break when we implement multisignature // wallets that return change back into a multi-signature-protected address; // a better way of identifying which outputs are 'the send' and which are // 'the change' will need to be implemented (maybe extend CWalletTx to remember // which output, if any, was change). if (::IsMine(*this, txout.scriptPubKey)) { CTxDestination address; if (!ExtractDestination(txout.scriptPubKey, address)) return true; LOCK(cs_wallet); if (!mapAddressBook.count(address)) return true; } return false; } int64_t CWallet::GetChange(const CTxOut& txout) const { if (!MoneyRange(txout.nValue)) throw std::runtime_error("CWallet::GetChange() : value out of range"); return (IsChange(txout) ? txout.nValue : 0); } bool CWallet::IsMine(const CTransaction& tx) const { BOOST_FOREACH(const CTxOut& txout, tx.vout) if (IsMine(txout) && txout.nValue >= nMinimumInputValue) return true; return false; } bool CWallet::IsFromMe(const CTransaction& tx) const { return (GetDebit(tx, MINE_ALL) > 0); } int64_t CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) const { int64_t nDebit = 0; BOOST_FOREACH(const CTxIn& txin, tx.vin) { nDebit += GetDebit(txin, filter); if (!MoneyRange(nDebit)) throw std::runtime_error("CWallet::GetDebit() : value out of range"); } return nDebit; } int64_t CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const { int64_t nCredit = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { nCredit += GetCredit(txout, filter); if (!MoneyRange(nCredit)) throw std::runtime_error("CWallet::GetCredit() : value out of range"); } return nCredit; } int64_t CWallet::GetChange(const CTransaction& tx) const { int64_t nChange = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { nChange += GetChange(txout); if (!MoneyRange(nChange)) throw std::runtime_error("CWallet::GetChange() : value out of range"); } return nChange; } int64_t CWalletTx::GetTxTime() const { return nTime; } int CWalletTx::GetRequestCount() const { // Returns -1 if it wasn't being tracked int nRequests = -1; { LOCK(pwallet->cs_wallet); if (IsCoinBase() || IsCoinStake()) { // Generated block if (hashBlock != 0) { map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); if (mi != pwallet->mapRequestCount.end()) nRequests = (*mi).second; } } else { // Did anyone request this transaction? map::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); if (mi != pwallet->mapRequestCount.end()) { nRequests = (*mi).second; // How about the block it's in? if (nRequests == 0 && hashBlock != 0) { map::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); if (mi != pwallet->mapRequestCount.end()) nRequests = (*mi).second; else nRequests = 1; // If it's in someone else's block it must have got out } } } } return nRequests; } bool CWalletTx::InMempool() const { LOCK(mempool.cs); if (mempool.exists(GetHash())) { return true; } return false; } bool CWalletTx::IsTrusted() const { // Quick answer in most cases if (!IsFinal()) return false; int nDepth = GetDepthInMainChain(); if (nDepth >= 1) return true; if (nDepth < 0) return false; if (!fConfChange || !IsFromMe(MINE_ALL)) // using wtx's cached debit return false; // Don't trust unconfirmed transactions from us unless they are in the mempool. if (!InMempool()) return false; // Trusted if all inputs are from us and are in the mempool: BOOST_FOREACH(const CTxIn& txin, vin) { // Transactions not sent by us: not trusted const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); if (parent == NULL) return false; const CTxOut& parentOut = parent->vout[txin.prevout.n]; if (pwallet->IsMine(parentOut) != MINE_SPENDABLE) return false; } return true; } int64_t CWalletTx::GetDebit(const isminefilter& filter) const { if (vin.empty()) return 0; int64_t nDebit = 0; if (filter & MINE_SPENDABLE) { if (fDebitCached) nDebit += nDebitCached; else { nDebitCached = pwallet->GetDebit(*this, MINE_SPENDABLE); fDebitCached = true; nDebit += nDebitCached; } } if (filter & MINE_WATCH_ONLY) { if (fWatchDebitCached) nDebit += nWatchDebitCached; else { nWatchDebitCached = pwallet->GetDebit(*this, MINE_WATCH_ONLY); fWatchDebitCached = true; nDebit += nWatchDebitCached; } } return nDebit; } int64_t CWalletTx::GetCredit(const isminefilter& filter) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0) return 0; int64_t credit = 0; if (filter & MINE_SPENDABLE) { // GetBalance can assume transactions in mapWallet won't change if (fCreditCached) credit += nCreditCached; else { nCreditCached = pwallet->GetCredit(*this, MINE_SPENDABLE); fCreditCached = true; credit += nCreditCached; } } if (filter & MINE_WATCH_ONLY) { if (fWatchCreditCached) credit += nWatchCreditCached; else { nWatchCreditCached = pwallet->GetCredit(*this, MINE_WATCH_ONLY); fWatchCreditCached = true; credit += nWatchCreditCached; } } return credit; } int64_t CWalletTx::GetImmatureCredit(bool fUseCache) const { if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) { if (fUseCache && fImmatureCreditCached) return nImmatureCreditCached; nImmatureCreditCached = pwallet->GetCredit(*this, MINE_SPENDABLE); fImmatureCreditCached = true; return nImmatureCreditCached; } return 0; } int64_t CWalletTx::GetImmatureWatchOnlyCredit(bool fUseCache) const { if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) { if (fUseCache && fImmatureWatchCreditCached) return nImmatureWatchCreditCached; nImmatureWatchCreditCached = pwallet->GetCredit(*this, MINE_WATCH_ONLY); fImmatureWatchCreditCached = true; return nImmatureWatchCreditCached; } return 0; } int64_t CWalletTx::GetAvailableCredit(bool fUseCache) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0) return 0; if (fUseCache) { if (fAvailableCreditCached) return nAvailableCreditCached; } int64_t nCredit = 0; for (unsigned int i = 0; i < vout.size(); i++) { if (!IsSpent(i)) { const CTxOut &txout = vout[i]; nCredit += pwallet->GetCredit(txout, MINE_SPENDABLE); if (!MoneyRange(nCredit)) throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); } } nAvailableCreditCached = nCredit; fAvailableCreditCached = true; return nCredit; } int64_t CWalletTx::GetAvailableWatchCredit(bool fUseCache) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0) return 0; if (fUseCache) { if (fAvailableWatchCreditCached) return nAvailableWatchCreditCached; } int64_t nCredit = 0; for (unsigned int i = 0; i < vout.size(); i++) { if (!IsSpent(i)) { const CTxOut &txout = vout[i]; nCredit += pwallet->GetCredit(txout, MINE_WATCH_ONLY); if (!MoneyRange(nCredit)) throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); } } nAvailableWatchCreditCached = nCredit; fAvailableWatchCreditCached = true; return nCredit; } int64_t CWalletTx::GetChange() const { if (fChangeCached) return nChangeCached; nChangeCached = pwallet->GetChange(*this); fChangeCached = true; return nChangeCached; } void CWalletTx::GetAmounts(int64_t& nGeneratedImmature, int64_t& nGeneratedMature, list >& listReceived, list >& listSent, int64_t& nFee, string& strSentAccount, const isminefilter& filter) const { nGeneratedImmature = nGeneratedMature = nFee = 0; listReceived.clear(); listSent.clear(); strSentAccount = strFromAccount; if (IsCoinBase() || IsCoinStake()) { if (GetBlocksToMaturity() > 0) nGeneratedImmature = pwallet->GetCredit(*this, filter); else nGeneratedMature = GetCredit(filter); return; } // Compute fee: int64_t nDebit = GetDebit(filter); if (nDebit > 0) // debit>0 means we signed/sent this transaction { int64_t nValueOut = GetValueOut(); nFee = nDebit - nValueOut; } // Sent/received. BOOST_FOREACH(const CTxOut& txout, vout) { isminetype fIsMine = pwallet->IsMine(txout); // Only need to handle txouts if AT LEAST one of these is true: // 1) they debit from us (sent) // 2) the output is to us (received) if (nDebit > 0) { // Don't report 'change' txouts if (pwallet->IsChange(txout)) continue; } else if (!(fIsMine & filter)) continue; // In either case, we need to get the destination address CBitcoinAddress address; if (!ExtractAddress(*pwallet, txout.scriptPubKey, address)) { printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", this->GetHash().ToString().c_str()); address = CBitcoinAddress(); } // If we are debited by the transaction, add the output as a "sent" entry if (nDebit > 0) listSent.push_back(make_pair(address, txout.nValue)); // If we are receiving the output, add it as a "received" entry if (fIsMine & filter) listReceived.push_back(make_pair(address, txout.nValue)); } } void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nGenerated, int64_t& nReceived, int64_t& nSent, int64_t& nFee, const isminefilter& filter) const { nGenerated = nReceived = nSent = nFee = 0; int64_t allGeneratedImmature, allGeneratedMature, allFee; allGeneratedImmature = allGeneratedMature = allFee = 0; string strSentAccount; list > listReceived; list > listSent; GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount, filter); if (strAccount.empty()) nGenerated = allGeneratedMature; if (strAccount == strSentAccount) { BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64_t)& s, listSent) nSent += s.second; nFee = allFee; } { LOCK(pwallet->cs_wallet); BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64_t)& r, listReceived) { if (pwallet->mapAddressBook.count(r.first)) { map::const_iterator mi = pwallet->mapAddressBook.find(r.first); if (mi != pwallet->mapAddressBook.end() && (*mi).second == strAccount) nReceived += r.second; } else if (strAccount.empty()) { nReceived += r.second; } } } } void CWalletTx::AddSupportingTransactions(CTxDB& txdb) { vtxPrev.clear(); const int COPY_DEPTH = 3; if (SetMerkleBranch() < COPY_DEPTH) { vector vWorkQueue; BOOST_FOREACH(const CTxIn& txin, vin) vWorkQueue.push_back(txin.prevout.hash); // This critsect is OK because txdb is already open { LOCK(pwallet->cs_wallet); map mapWalletPrev; set setAlreadyDone; for (unsigned int i = 0; i < vWorkQueue.size(); i++) { uint256 hash = vWorkQueue[i]; if (setAlreadyDone.count(hash)) continue; setAlreadyDone.insert(hash); CMerkleTx tx; map::const_iterator mi = pwallet->mapWallet.find(hash); if (mi != pwallet->mapWallet.end()) { tx = (*mi).second; BOOST_FOREACH(const CMerkleTx& txWalletPrev, (*mi).second.vtxPrev) mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; } else if (mapWalletPrev.count(hash)) { tx = *mapWalletPrev[hash]; } else if (!fClient && txdb.ReadDiskTx(hash, tx)) { ; } else { printf("ERROR: AddSupportingTransactions() : unsupported transaction\n"); continue; } int nDepth = tx.SetMerkleBranch(); vtxPrev.push_back(tx); if (nDepth < COPY_DEPTH) { BOOST_FOREACH(const CTxIn& txin, tx.vin) vWorkQueue.push_back(txin.prevout.hash); } } } } reverse(vtxPrev.begin(), vtxPrev.end()); } bool CWalletTx::WriteToDisk() { return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); } // Scan the block chain (starting in pindexStart) for transactions // from or to us. If fUpdate is true, found transactions that already // exist in the wallet will be updated. int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) { int ret = 0; CBlockIndex* pindex = pindexStart; { LOCK(cs_wallet); while (pindex) { CBlock block; block.ReadFromDisk(pindex, true); BOOST_FOREACH(CTransaction& tx, block.vtx) { if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) ret++; } pindex = pindex->pnext; } } return ret; } int CWallet::ScanForWalletTransaction(const uint256& hashTx) { CTransaction tx; tx.ReadFromDisk(COutPoint(hashTx, 0)); if (AddToWalletIfInvolvingMe(tx, NULL, true)) return 1; return 0; } void CWallet::ReacceptWalletTransactions() { CTxDB txdb("r"); bool fRepeat = true; while (fRepeat) { LOCK(cs_wallet); fRepeat = false; vector vMissingTx; BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) { CWalletTx& wtx = item.second; if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1))) continue; CTxIndex txindex; bool fUpdated = false; if (txdb.ReadTxIndex(wtx.GetHash(), txindex)) { // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat if (txindex.vSpent.size() != wtx.vout.size()) { printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %" PRIszu " != wtx.vout.size() %" PRIszu "\n", txindex.vSpent.size(), wtx.vout.size()); continue; } for (unsigned int i = 0; i < txindex.vSpent.size(); i++) { if (wtx.IsSpent(i)) continue; if (!txindex.vSpent[i].IsNull() && IsMine(wtx.vout[i])) { wtx.MarkSpent(i); fUpdated = true; vMissingTx.push_back(txindex.vSpent[i]); } } if (fUpdated) { printf("ReacceptWalletTransactions found spent coin %snvc %s\n", FormatMoney(wtx.GetCredit(MINE_ALL)).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkDirty(); wtx.WriteToDisk(); } } else { // Re-accept any txes of ours that aren't already in a block if (!(wtx.IsCoinBase() || wtx.IsCoinStake())) wtx.AcceptWalletTransaction(txdb, false); } } if (!vMissingTx.empty()) { // TODO: optimize this to scan just part of the block chain? if (ScanForWalletTransactions(pindexGenesisBlock)) fRepeat = true; // Found missing transactions: re-do re-accept. } } } bool CWalletTx::RelayWalletTransaction(CTxDB& txdb) { uint256 hash = GetHash(); if (IsCoinBase() || IsCoinStake() || txdb.ContainsTx(hash) || !InMempool()) return false; for(std::vector::const_iterator it = vtxPrev.begin(); it != vtxPrev.end(); it++) { const CMerkleTx& tx = *it; uint256 hash = tx.GetHash(); if (tx.IsCoinBase() || tx.IsCoinStake()) continue; if (!txdb.ContainsTx(hash)) RelayTransaction((CTransaction)tx, hash); } printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); RelayTransaction((CTransaction)*this, hash); return true; } bool CWalletTx::RelayWalletTransaction() { CTxDB txdb("r"); return RelayWalletTransaction(txdb); } std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) { std::vector result; LOCK(cs_wallet); // Sort them in chronological order map mapSorted; BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) { CWalletTx& wtx = item.second; // Don't rebroadcast if newer than nTime: if (wtx.nTimeReceived > nTime) continue; mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); } BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) { CWalletTx& wtx = *item.second; if (wtx.RelayWalletTransaction()) result.push_back(wtx.GetHash()); } return result; } void CWallet::ResendWalletTransactions(int64_t nBestBlockTime) { int64_t nNow = GetTime(); // Do this infrequently and randomly to avoid giving away // that these are our transactions. if (nNow < nNextResend) return; bool fFirst = (nNextResend == 0); nNextResend = PoissonNextSend(nNow, 5*60); if (fFirst) return; // Only do it if there's been a new block since last time if (nBestBlockTime < nLastResend) return; nLastResend = nNow; // Rebroadcast unconfirmed txes older than 5 minutes before the last // block was found: std::vector relayed = ResendWalletTransactionsBefore(nBestBlockTime - 5*60); if (!relayed.empty()) printf("CWallet::ResendWalletTransactions: rebroadcast %" PRIszu " unconfirmed transactions\n", relayed.size()); } ////////////////////////////////////////////////////////////////////////////// // // Actions // int64_t CWallet::GetBalance() const { int64_t nTotal = 0; { LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsTrusted()) nTotal += pcoin->GetAvailableCredit(); } } return nTotal; } int64_t CWallet::GetWatchOnlyBalance() const { int64_t nTotal = 0; { LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsTrusted()) nTotal += pcoin->GetAvailableWatchCredit(); } } return nTotal; } int64_t CWallet::GetUnconfirmedBalance() const { int64_t nTotal = 0; { LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (!pcoin->IsFinal() || !pcoin->IsTrusted()) nTotal += pcoin->GetAvailableCredit(); } } return nTotal; } int64_t CWallet::GetUnconfirmedWatchOnlyBalance() const { int64_t nTotal = 0; { LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (!pcoin->IsFinal() || !pcoin->IsTrusted()) nTotal += pcoin->GetAvailableWatchCredit(); } } return nTotal; } int64_t CWallet::GetImmatureBalance() const { int64_t nTotal = 0; { LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; nTotal += pcoin->GetImmatureCredit(); } } return nTotal; } int64_t CWallet::GetImmatureWatchOnlyBalance() const { int64_t nTotal = 0; { LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; nTotal += pcoin->GetImmatureWatchOnlyCredit(); } } return nTotal; } // populate vCoins with vector of spendable COutputs void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const { vCoins.clear(); { LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (!pcoin->IsFinal()) continue; if (fOnlyConfirmed && !pcoin->IsTrusted()) continue; if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) continue; if(pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0) continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { isminetype mine = IsMine(pcoin->vout[i]); if (!(pcoin->IsSpent(i)) && mine != MINE_NO && pcoin->vout[i].nValue >= nMinimumInputValue && (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) { vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain(), mine == MINE_SPENDABLE)); } } } } } void CWallet::AvailableCoinsMinConf(vector& vCoins, int nConf, int64_t nMinValue, int64_t nMaxValue) const { vCoins.clear(); { LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (!pcoin->IsFinal()) continue; if(pcoin->GetDepthInMainChain() < nConf) continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { isminetype mine = IsMine(pcoin->vout[i]); // ignore coin if it was already spent or we don't own it if (pcoin->IsSpent(i) || mine == MINE_NO) continue; // if coin value is between required limits then add new item to vector if (pcoin->vout[i].nValue >= nMinValue && pcoin->vout[i].nValue < nMaxValue) vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain(), mine == MINE_SPENDABLE)); } } } } static void ApproximateBestSubset(vector > >vValue, int64_t nTotalLower, int64_t nTargetValue, vector& vfBest, int64_t& nBest, int iterations = 1000) { vector vfIncluded; vfBest.assign(vValue.size(), true); nBest = nTotalLower; for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) { vfIncluded.assign(vValue.size(), false); int64_t nTotal = 0; bool fReachedTarget = false; for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) { for (unsigned int i = 0; i < vValue.size(); i++) { if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) { nTotal += vValue[i].first; vfIncluded[i] = true; if (nTotal >= nTargetValue) { fReachedTarget = true; if (nTotal < nBest) { nBest = nTotal; vfBest = vfIncluded; } nTotal -= vValue[i].first; vfIncluded[i] = false; } } } } } } int64_t CWallet::GetStake() const { int64_t nTotal = 0; LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) nTotal += CWallet::GetCredit(*pcoin, MINE_ALL); } return nTotal; } int64_t CWallet::GetWatchOnlyStake() const { int64_t nTotal = 0; LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) nTotal += CWallet::GetCredit(*pcoin, MINE_WATCH_ONLY); } return nTotal; } int64_t CWallet::GetNewMint() const { int64_t nTotal = 0; LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) nTotal += CWallet::GetCredit(*pcoin, MINE_ALL); } return nTotal; } int64_t CWallet::GetWatchOnlyNewMint() const { int64_t nTotal = 0; LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) nTotal += CWallet::GetCredit(*pcoin, MINE_WATCH_ONLY); } return nTotal; } bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, vector vCoins, set >& setCoinsRet, int64_t& nValueRet) const { setCoinsRet.clear(); nValueRet = 0; // List of values less than target pair > coinLowestLarger; coinLowestLarger.first = std::numeric_limits::max(); coinLowestLarger.second.first = NULL; vector > > vValue; int64_t nTotalLower = 0; random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); BOOST_FOREACH(const COutput &output, vCoins) { if (!output.fSpendable) continue; const CWalletTx *pcoin = output.tx; if (output.nDepth < (pcoin->IsFromMe(MINE_ALL) ? nConfMine : nConfTheirs)) continue; int i = output.i; // Follow the timestamp rules if (pcoin->nTime > nSpendTime) continue; int64_t n = pcoin->vout[i].nValue; pair > coin = make_pair(n,make_pair(pcoin, i)); if (n == nTargetValue) { setCoinsRet.insert(coin.second); nValueRet += coin.first; return true; } else if (n < nTargetValue + CENT) { vValue.push_back(coin); nTotalLower += n; } else if (n < coinLowestLarger.first) { coinLowestLarger = coin; } } if (nTotalLower == nTargetValue) { for (unsigned int i = 0; i < vValue.size(); ++i) { setCoinsRet.insert(vValue[i].second); nValueRet += vValue[i].first; } return true; } if (nTotalLower < nTargetValue) { if (coinLowestLarger.second.first == NULL) return false; setCoinsRet.insert(coinLowestLarger.second); nValueRet += coinLowestLarger.first; return true; } // Solve subset sum by stochastic approximation sort(vValue.rbegin(), vValue.rend(), CompareValueOnly()); vector vfBest; int64_t nBest; ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000); if (nBest != nTargetValue && nTotalLower >= nTargetValue + CENT) ApproximateBestSubset(vValue, nTotalLower, nTargetValue + CENT, vfBest, nBest, 1000); // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, // or the next bigger coin is closer), return the bigger coin if (coinLowestLarger.second.first && ((nBest != nTargetValue && nBest < nTargetValue + CENT) || coinLowestLarger.first <= nBest)) { setCoinsRet.insert(coinLowestLarger.second); nValueRet += coinLowestLarger.first; } else { for (unsigned int i = 0; i < vValue.size(); i++) if (vfBest[i]) { setCoinsRet.insert(vValue[i].second); nValueRet += vValue[i].first; } if (fDebug && GetBoolArg("-printpriority")) { //// debug print printf("SelectCoins() best subset: "); for (unsigned int i = 0; i < vValue.size(); i++) if (vfBest[i]) printf("%s ", FormatMoney(vValue[i].first).c_str()); printf("total %s\n", FormatMoney(nBest).c_str()); } } return true; } bool CWallet::SelectCoins(int64_t nTargetValue, unsigned int nSpendTime, set >& setCoinsRet, int64_t& nValueRet, const CCoinControl* coinControl) const { vector vCoins; AvailableCoins(vCoins, true, coinControl); // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) if (coinControl && coinControl->HasSelected()) { BOOST_FOREACH(const COutput& out, vCoins) { if(!out.fSpendable) continue; nValueRet += out.tx->vout[out.i].nValue; setCoinsRet.insert(make_pair(out.tx, out.i)); } return (nValueRet >= nTargetValue); } return (SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 6, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 1, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, vCoins, setCoinsRet, nValueRet)); } // Select some coins without random shuffle or best subset approximation bool CWallet::SelectCoinsSimple(int64_t nTargetValue, int64_t nMinValue, int64_t nMaxValue, unsigned int nSpendTime, int nMinConf, set >& setCoinsRet, int64_t& nValueRet) const { vector vCoins; AvailableCoinsMinConf(vCoins, nMinConf, nMinValue, nMaxValue); setCoinsRet.clear(); nValueRet = 0; BOOST_FOREACH(COutput output, vCoins) { if(!output.fSpendable) continue; const CWalletTx *pcoin = output.tx; int i = output.i; // Ignore immature coins if (pcoin->GetBlocksToMaturity() > 0) continue; // Stop if we've chosen enough inputs if (nValueRet >= nTargetValue) break; // Follow the timestamp rules if (pcoin->nTime > nSpendTime) continue; int64_t n = pcoin->vout[i].nValue; pair > coin = make_pair(n,make_pair(pcoin, i)); if (n >= nTargetValue) { // If input value is greater or equal to target then simply insert // it into the current subset and exit setCoinsRet.insert(coin.second); nValueRet += coin.first; break; } else if (n < nTargetValue + CENT) { setCoinsRet.insert(coin.second); nValueRet += coin.first; } } return true; } bool CWallet::CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl) { int64_t nValue = 0; BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend) { if (nValue < 0) return false; nValue += s.second; } if (vecSend.empty() || nValue < 0) return false; wtxNew.BindWallet(this); { LOCK2(cs_main, cs_wallet); // txdb must be opened before the mapWallet lock CTxDB txdb("r"); { nFeeRet = nTransactionFee; for ( ; ; ) { wtxNew.vin.clear(); wtxNew.vout.clear(); wtxNew.fFromMe = true; int64_t nTotalValue = nValue + nFeeRet; double dPriority = 0; // vouts to the payees BOOST_FOREACH (const PAIRTYPE(CScript, int64_t)& s, vecSend) wtxNew.vout.push_back(CTxOut(s.second, s.first)); // Choose coins to use set > setCoins; int64_t nValueIn = 0; if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn, coinControl)) return false; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { int64_t nCredit = pcoin.first->vout[pcoin.second].nValue; dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); } int64_t nChange = nValueIn - nValue - nFeeRet; if (nChange > 0) { // Fill a vout to ourself // TODO: pass in scriptChange instead of reservekey so // change transaction isn't always pay-to-bitcoin-address CScript scriptChange; // coin control: send change to custom address if (coinControl && coinControl->destChange.IsValid()) scriptChange.SetAddress(coinControl->destChange); // no coin control: send change to newly generated address else { // Note: We use a new key here to keep it from being obvious which side is the change. // The drawback is that by not reusing a previous key, the change may be lost if a // backup is restored, if the backup doesn't have the new private key for the change. // If we reused the old key, it would be possible to add code to look for and // rediscover unknown transactions that were written with keys of ours to recover // post-backup change. // Reserve a new key pair from key pool CPubKey vchPubKey = reservekey.GetReservedKey(); scriptChange.SetDestination(vchPubKey.GetID()); } // Insert change txn at random position: vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); } else reservekey.ReturnKey(); // Fill vin BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); // Sign int nIn = 0; BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) if (!SignSignature(*this, *coin.first, wtxNew, nIn++)) return false; // Limit size unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION); if (nBytes >= MAX_BLOCK_SIZE_GEN/5) return false; dPriority /= nBytes; // Check that enough fee is included bool fAllowFree = CTransaction::AllowFree(dPriority); int64_t nPayFee = nTransactionFee * (1 + (int64_t)nBytes / 1000); int64_t nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND, nBytes); if (nFeeRet < max(nPayFee, nMinFee)) { nFeeRet = max(nPayFee, nMinFee); continue; } // Fill vtxPrev by copying from previous transactions vtxPrev wtxNew.AddSupportingTransactions(txdb); wtxNew.fTimeReceivedIsTxTime = true; break; } } } return true; } bool CWallet::CreateTransaction(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl* coinControl) { vector< pair > vecSend; vecSend.push_back(make_pair(scriptPubKey, nValue)); return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, coinControl); } void CWallet::GetStakeWeightFromValue(const int64_t& nTime, const int64_t& nValue, uint64_t& nWeight) { int64_t nTimeWeight = GetWeight(nTime, GetTime()); // If time weight is lower or equal to zero then weight is zero. if (nTimeWeight <= 0) { nWeight = 0; return; } CBigNum bnCoinDayWeight = CBigNum(nValue) * nTimeWeight / COIN / nOneDay; nWeight = bnCoinDayWeight.getuint64(); } bool CWallet::MergeCoins(const int64_t& nAmount, const int64_t& nMinValue, const int64_t& nOutputValue, list& listMerged) { int64_t nBalance = GetBalance(); if (nAmount > nBalance) return false; listMerged.clear(); int64_t nValueIn = 0; set > setCoins; // Simple coins selection - no randomization if (!SelectCoinsSimple(nAmount, nMinValue, nOutputValue, GetTime(), 1, setCoins, nValueIn)) return false; if (setCoins.empty()) return false; CWalletTx wtxNew; vector vwtxPrev; // Reserve a new key pair from key pool CReserveKey reservekey(this); CPubKey vchPubKey = reservekey.GetReservedKey(); // Output script CScript scriptOutput; scriptOutput.SetDestination(vchPubKey.GetID()); // Insert output wtxNew.vout.push_back(CTxOut(0, scriptOutput)); double dWeight = 0; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { int64_t nCredit = pcoin.first->vout[pcoin.second].nValue; // Add current coin to inputs list and add its credit to transaction output wtxNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); wtxNew.vout[0].nValue += nCredit; vwtxPrev.push_back(pcoin.first); /* // Replaced with estimation for performance purposes for (unsigned int i = 0; i < wtxNew.vin.size(); i++) { const CWalletTx *txin = vwtxPrev[i]; // Sign scripts to get actual transaction size for fee calculation if (!SignSignature(*this, *txin, wtxNew, i)) return false; } */ // Assuming that average scriptsig size is 110 bytes int64_t nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION) + wtxNew.vin.size() * 110; dWeight += (double)nCredit * pcoin.first->GetDepthInMainChain(); double dFinalPriority = dWeight /= nBytes; bool fAllowFree = CTransaction::AllowFree(dFinalPriority); // Get actual transaction fee according to its estimated size and priority int64_t nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND, nBytes); // Prepare transaction for commit if sum is enough ot its size is too big if (nBytes >= MAX_BLOCK_SIZE_GEN/6 || wtxNew.vout[0].nValue >= nOutputValue) { wtxNew.vout[0].nValue -= nMinFee; // Set actual fee for (unsigned int i = 0; i < wtxNew.vin.size(); i++) { const CWalletTx *txin = vwtxPrev[i]; // Sign all scripts if (!SignSignature(*this, *txin, wtxNew, i)) return false; } // Try to commit, return false on failure if (!CommitTransaction(wtxNew, reservekey)) return false; listMerged.push_back(wtxNew.GetHash()); // Add to hashes list dWeight = 0; // Reset all temporary values vwtxPrev.clear(); wtxNew.SetNull(); wtxNew.vout.push_back(CTxOut(0, scriptOutput)); } } // Create transactions if there are some unhandled coins left if (wtxNew.vout[0].nValue > 0) { int64_t nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION) + wtxNew.vin.size() * 110; double dFinalPriority = dWeight /= nBytes; bool fAllowFree = CTransaction::AllowFree(dFinalPriority); // Get actual transaction fee according to its size and priority int64_t nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND, nBytes); wtxNew.vout[0].nValue -= nMinFee; // Set actual fee if (wtxNew.vout[0].nValue <= 0) return false; for (unsigned int i = 0; i < wtxNew.vin.size(); i++) { const CWalletTx *txin = vwtxPrev[i]; // Sign all scripts again if (!SignSignature(*this, *txin, wtxNew, i)) return false; } // Try to commit, return false on failure if (!CommitTransaction(wtxNew, reservekey)) return false; listMerged.push_back(wtxNew.GetHash()); // Add to hashes list } return true; } bool CWallet::CreateCoinStake(uint256 &hashTx, uint32_t nOut, uint32_t nGenerationTime, uint32_t nBits, CTransaction &txNew, CKey& key) { CWalletTx wtx; if (!GetTransaction(hashTx, wtx)) return error("Transaction %s is not found\n", hashTx.GetHex().c_str()); vector vSolutions; txnouttype whichType; CScript scriptPubKeyOut; CScript scriptPubKeyKernel = wtx.vout[nOut].scriptPubKey; if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) return error("CreateCoinStake : failed to parse kernel\n"); if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : parsed kernel type=%d\n", whichType); if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH) return error("CreateCoinStake : no support for kernel type=%d\n", whichType); if (whichType == TX_PUBKEYHASH) // pay to address type { // convert to pay to public key type if (!GetKey(uint160(vSolutions[0]), key)) return error("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG; } if (whichType == TX_PUBKEY) { valtype& vchPubKey = vSolutions[0]; if (!GetKey(Hash160(vchPubKey), key)) return error("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); if (key.GetPubKey() != vchPubKey) return error("CreateCoinStake : invalid key for kernel type=%d\n", whichType); // keys mismatch scriptPubKeyOut = scriptPubKeyKernel; } // The following combine threshold is important to security // Should not be adjusted if you don't understand the consequences int64_t nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits) / 3; int64_t nBalance = GetBalance(); int64_t nCredit = wtx.vout[nOut].nValue; txNew.vin.clear(); txNew.vout.clear(); // List of constake dependencies vector vwtxPrev; vwtxPrev.push_back(&wtx); // Set generation time, and kernel input txNew.nTime = nGenerationTime; txNew.vin.push_back(CTxIn(hashTx, nOut)); // Mark coin stake transaction with empty vout[0] CScript scriptEmpty; scriptEmpty.clear(); txNew.vout.push_back(CTxOut(0, scriptEmpty)); if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : added kernel type=%d\n", whichType); int64_t nValueIn = 0; CoinsSet setCoins; if (!SelectCoinsSimple(nBalance - nReserveBalance, MIN_TX_FEE, MAX_MONEY, nGenerationTime, nCoinbaseMaturity * 10, setCoins, nValueIn)) return false; if (setCoins.empty()) return false; bool fDontSplitCoins = false; if (GetWeight((int64_t)wtx.nTime, (int64_t)nGenerationTime) == nStakeMaxAge) { // Only one output for old kernel inputs txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); // Iterate through set of (wtx*, nout) in order to find some additional inputs for our new coinstake transaction. // // * Value is higher than 0.01 NVC; // * Only add inputs of the same key/address as kernel; // * Input hash and kernel parent hash should be different. for(CoinsSet::iterator pcoin = setCoins.begin(); pcoin != setCoins.end(); pcoin++) { // Stop adding more inputs if already too many inputs if (txNew.vin.size() >= 100) break; // Stop adding more inputs if value is already pretty significant if (nCredit > nCombineThreshold) break; // Stop adding inputs if reached reserve limit if (nCredit + pcoin->first->vout[pcoin->second].nValue > nBalance - nReserveBalance) break; int64_t nTimeWeight = GetWeight((int64_t)pcoin->first->nTime, (int64_t)nGenerationTime); // Do not add input that is still too young if (nTimeWeight < nStakeMaxAge) continue; // Do not add input if key/address is not the same as kernel if (pcoin->first->vout[pcoin->second].scriptPubKey != scriptPubKeyKernel && pcoin->first->vout[pcoin->second].scriptPubKey != txNew.vout[1].scriptPubKey) continue; // Do not add input if parents are the same if (pcoin->first->GetHash() != txNew.vin[0].prevout.hash) continue; // Do not add additional significant input if (pcoin->first->vout[pcoin->second].nValue > nCombineThreshold) continue; txNew.vin.push_back(CTxIn(pcoin->first->GetHash(), pcoin->second)); nCredit += pcoin->first->vout[pcoin->second].nValue; vwtxPrev.push_back(pcoin->first); } fDontSplitCoins = true; } else { int64_t nSplitThreshold = GetArg("-splitthreshold", nCombineThreshold); if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : nSplitThreshold=%" PRId64 "\n", nSplitThreshold); if (nCredit > nSplitThreshold) { // Split stake input if credit is lower than combine threshold and maximum weight isn't reached yet txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : splitting coinstake\n"); } else { txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); fDontSplitCoins = true; } } // Calculate coin age reward uint64_t nCoinAge; CTxDB txdb("r"); if (!txNew.GetCoinAge(txdb, nCoinAge)) return error("CreateCoinStake : failed to calculate coin age\n"); nCredit += GetProofOfStakeReward(nCoinAge, nBits, nGenerationTime); int64_t nMinFee = 0; for ( ; ; ) { // Set output amount if (fDontSplitCoins) txNew.vout[1].nValue = nCredit - nMinFee; else { txNew.vout[1].nValue = ((nCredit - nMinFee) / 2 / CENT) * CENT; txNew.vout[2].nValue = nCredit - nMinFee - txNew.vout[1].nValue; } // Sign int nIn = 0; BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev) { if (!SignSignature(*this, *pcoin, txNew, nIn++)) return error("CreateCoinStake : failed to sign coinstake\n"); } // Limit size unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); if (nBytes >= MAX_BLOCK_SIZE_GEN/5) return error("CreateCoinStake : exceeded coinstake size limit\n"); // Check enough fee is paid if (nMinFee < txNew.GetMinFee(1, false, GMF_BLOCK, nBytes) - CENT) { nMinFee = txNew.GetMinFee(1, false, GMF_BLOCK, nBytes) - CENT; continue; // try signing again } else { if (fDebug && GetBoolArg("-printfee")) printf("CreateCoinStake : fee for coinstake %s\n", FormatMoney(nMinFee).c_str()); break; } } // Successfully created coinstake return true; } // Call after CreateTransaction unless you want to abort bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) { { printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); // Track how many getdata requests our transaction gets mapRequestCount[wtxNew.GetHash()] = 0; // Try to broadcast before saving if (!wtxNew.AcceptToMemoryPool()) { // This must not fail. The transaction has already been signed. printf("CommitTransaction() : Error: Transaction not valid"); return false; } wtxNew.RelayWalletTransaction(); { LOCK2(cs_main, cs_wallet); // This is only to keep the database open to defeat the auto-flush for the // duration of this scope. This is the only place where this optimization // maybe makes sense; please don't do it anywhere else. CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r") : NULL; // Take key pair from key pool so it won't be used again reservekey.KeepKey(); // Add tx to wallet, because if it has change it's also ours, // otherwise just for transaction history. AddToWallet(wtxNew); // Mark old coins as spent BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) { CWalletTx &coin = mapWallet[txin.prevout.hash]; coin.BindWallet(this); coin.MarkSpent(txin.prevout.n); coin.WriteToDisk(); NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); vMintingWalletUpdated.push_back(coin.GetHash()); } if (fFileBacked) delete pwalletdb; } } return true; } string CWallet::SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee) { // Check amount if (nValue <= 0) return _("Invalid amount"); if (nValue + nTransactionFee > GetBalance()) return _("Insufficient funds"); CReserveKey reservekey(this); int64_t nFeeRequired; if (IsLocked()) { string strError = _("Error: Wallet locked, unable to create transaction "); printf("SendMoney() : %s", strError.c_str()); return strError; } if (fWalletUnlockMintOnly) { string strError = _("Error: Wallet unlocked for block minting only, unable to create transaction."); printf("SendMoney() : %s", strError.c_str()); return strError; } if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) { string strError; if (nValue + nFeeRequired > GetBalance()) strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds "), FormatMoney(nFeeRequired).c_str()); else strError = _("Error: Transaction creation failed "); printf("SendMoney() : %s", strError.c_str()); return strError; } if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired, _("Sending..."))) return "ABORTED"; if (!CommitTransaction(wtxNew, reservekey)) return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); return ""; } DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { if (!fFileBacked) return DB_LOAD_OK; fFirstRunRet = false; DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); if (nLoadWalletRet == DB_NEED_REWRITE) { if (CDB::Rewrite(strWalletFile, "\x04pool")) { setKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // the requires a new key. } } if (nLoadWalletRet != DB_LOAD_OK) return nLoadWalletRet; fFirstRunRet = !vchDefaultKey.IsValid(); NewThread(ThreadFlushWalletDB, &strWalletFile); return DB_LOAD_OK; } DBErrors CWallet::ZapWalletTx() { if (!fFileBacked) return DB_LOAD_OK; DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this); if (nZapWalletTxRet == DB_NEED_REWRITE) { if (CDB::Rewrite(strWalletFile, "\x04pool")) { LOCK(cs_wallet); setKeyPool.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // the requires a new key. } } if (nZapWalletTxRet != DB_LOAD_OK) return nZapWalletTxRet; return DB_LOAD_OK; } bool CWallet::SetAddressBookName(const CTxDestination& address, const string& strName) { return SetAddressBookName(CBitcoinAddress(address), strName); } bool CWallet::SetAddressBookName(const CBitcoinAddress& address, const string& strName) { std::map::iterator mi = mapAddressBook.find(address); mapAddressBook[address] = strName; NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != MINE_NO, (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED); if (!fFileBacked) return false; return CWalletDB(strWalletFile).WriteName(address.ToString(), strName); } bool CWallet::DelAddressBookName(const CBitcoinAddress& address) { mapAddressBook.erase(address); NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != MINE_NO, CT_DELETED); if (!fFileBacked) return false; return CWalletDB(strWalletFile).EraseName(address.ToString()); } void CWallet::PrintWallet(const CBlock& block) { { LOCK(cs_wallet); if (block.IsProofOfStake() && mapWallet.count(block.vtx[1].GetHash())) { CWalletTx& wtx = mapWallet[block.vtx[1].GetHash()]; printf(" PoS: %d %d %" PRId64 "", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit(MINE_ALL)); } else if (mapWallet.count(block.vtx[0].GetHash())) { CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; printf(" PoW: %d %d %" PRId64 "", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), wtx.GetCredit(MINE_ALL)); } } printf("\n"); } bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) { { LOCK(cs_wallet); map::iterator mi = mapWallet.find(hashTx); if (mi != mapWallet.end()) { wtx = (*mi).second; return true; } } return false; } bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) { if (fFileBacked) { if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) return false; } vchDefaultKey = vchPubKey; return true; } bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) { if (!pwallet->fFileBacked) return false; strWalletFileOut = pwallet->strWalletFile; return true; } // // Mark old keypool keys as used, // and generate all new keys // bool CWallet::NewKeyPool(unsigned int nSize) { { LOCK(cs_wallet); CWalletDB walletdb(strWalletFile); BOOST_FOREACH(int64_t nIndex, setKeyPool) walletdb.ErasePool(nIndex); setKeyPool.clear(); if (IsLocked()) return false; uint64_t nKeys; if (nSize > 0) nKeys = nSize; else nKeys = max(GetArg("-keypool", 100), 0); for (uint64_t i = 0; i < nKeys; i++) { uint64_t nIndex = i+1; walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); setKeyPool.insert(nIndex); } printf("CWallet::NewKeyPool wrote %" PRIu64 " new keys\n", nKeys); } return true; } bool CWallet::TopUpKeyPool(unsigned int nSize) { { LOCK(cs_wallet); if (IsLocked()) return false; CWalletDB walletdb(strWalletFile); // Top up key pool uint64_t nTargetSize; if (nSize > 0) nTargetSize = nSize; else nTargetSize = max(GetArg("-keypool", 100), 0); while (setKeyPool.size() < (nTargetSize + 1)) { uint64_t nEnd = 1; if (!setKeyPool.empty()) nEnd = *(--setKeyPool.end()) + 1; if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) throw runtime_error("TopUpKeyPool() : writing generated key failed"); setKeyPool.insert(nEnd); printf("keypool added key %" PRIu64 ", size=%" PRIszu "\n", nEnd, setKeyPool.size()); } } return true; } void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool) { nIndex = -1; keypool.vchPubKey = CPubKey(); { LOCK(cs_wallet); if (!IsLocked()) TopUpKeyPool(); // Get the oldest key if(setKeyPool.empty()) return; CWalletDB walletdb(strWalletFile); nIndex = *(setKeyPool.begin()); setKeyPool.erase(setKeyPool.begin()); if (!walletdb.ReadPool(nIndex, keypool)) throw runtime_error("ReserveKeyFromKeyPool() : read failed"); if (!HaveKey(keypool.vchPubKey.GetID())) throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); assert(keypool.vchPubKey.IsValid()); if (fDebug && GetBoolArg("-printkeypool")) printf("keypool reserve %" PRId64 "\n", nIndex); } } int64_t CWallet::AddReserveKey(const CKeyPool& keypool) { { LOCK2(cs_main, cs_wallet); CWalletDB walletdb(strWalletFile); int64_t nIndex = 1 + *(--setKeyPool.end()); if (!walletdb.WritePool(nIndex, keypool)) throw runtime_error("AddReserveKey() : writing added key failed"); setKeyPool.insert(nIndex); return nIndex; } return -1; } void CWallet::KeepKey(int64_t nIndex) { // Remove from key pool if (fFileBacked) { CWalletDB walletdb(strWalletFile); walletdb.ErasePool(nIndex); } if(fDebug) printf("keypool keep %" PRId64 "\n", nIndex); } void CWallet::ReturnKey(int64_t nIndex) { // Return to key pool { LOCK(cs_wallet); setKeyPool.insert(nIndex); } if(fDebug) printf("keypool return %" PRId64 "\n", nIndex); } bool CWallet::GetKeyFromPool(CPubKey& result, bool fAllowReuse) { int64_t nIndex = 0; CKeyPool keypool; { LOCK(cs_wallet); ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) { if (fAllowReuse && vchDefaultKey.IsValid()) { result = vchDefaultKey; return true; } if (IsLocked()) return false; result = GenerateNewKey(); return true; } KeepKey(nIndex); result = keypool.vchPubKey; } return true; } int64_t CWallet::GetOldestKeyPoolTime() { int64_t nIndex = 0; CKeyPool keypool; ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) return GetTime(); ReturnKey(nIndex); return keypool.nTime; } std::map CWallet::GetAddressBalances() { map balances; { LOCK(cs_wallet); BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) { CWalletTx *pcoin = &walletEntry.second; if (!pcoin->IsFinal() || !pcoin->IsTrusted()) continue; if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0) continue; int nDepth = pcoin->GetDepthInMainChain(); if (nDepth < (pcoin->IsFromMe(MINE_ALL) ? 0 : 1)) continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { CBitcoinAddress addr; if (!IsMine(pcoin->vout[i])) continue; if(!ExtractAddress(*this, pcoin->vout[i].scriptPubKey, addr)) continue; int64_t n = pcoin->IsSpent(i) ? 0 : pcoin->vout[i].nValue; if (!balances.count(addr)) balances[addr] = 0; balances[addr] += n; } } } return balances; } set< set > CWallet::GetAddressGroupings() { set< set > groupings; set grouping; BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) { CWalletTx *pcoin = &walletEntry.second; if (pcoin->vin.size() > 0 && IsMine(pcoin->vin[0])) { // group all input addresses with each other BOOST_FOREACH(CTxIn txin, pcoin->vin) { CBitcoinAddress address; if(!ExtractAddress(*this, mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address)) continue; grouping.insert(address); } // group change with input addresses BOOST_FOREACH(CTxOut txout, pcoin->vout) if (IsChange(txout)) { CWalletTx tx = mapWallet[pcoin->vin[0].prevout.hash]; CBitcoinAddress txoutAddr; if(!ExtractAddress(*this, txout.scriptPubKey, txoutAddr)) continue; grouping.insert(txoutAddr); } groupings.insert(grouping); grouping.clear(); } // group lone addrs by themselves for (unsigned int i = 0; i < pcoin->vout.size(); i++) if (IsMine(pcoin->vout[i])) { CBitcoinAddress address; if(!ExtractAddress(*this, pcoin->vout[i].scriptPubKey, address)) continue; grouping.insert(address); groupings.insert(grouping); grouping.clear(); } } set< set* > uniqueGroupings; // a set of pointers to groups of addresses map< CBitcoinAddress, set* > setmap; // map addresses to the unique group containing it BOOST_FOREACH(set grouping, groupings) { // make a set of all the groups hit by this new group set< set* > hits; map< CBitcoinAddress, set* >::iterator it; BOOST_FOREACH(CBitcoinAddress address, grouping) if ((it = setmap.find(address)) != setmap.end()) hits.insert((*it).second); // merge all hit groups into a new single group and delete old groups set* merged = new set(grouping); BOOST_FOREACH(set* hit, hits) { merged->insert(hit->begin(), hit->end()); uniqueGroupings.erase(hit); delete hit; } uniqueGroupings.insert(merged); // update setmap BOOST_FOREACH(CBitcoinAddress element, *merged) setmap[element] = merged; } set< set > ret; BOOST_FOREACH(set* uniqueGrouping, uniqueGroupings) { ret.insert(*uniqueGrouping); delete uniqueGrouping; } return ret; } // ppcoin: check 'spent' consistency between wallet and txindex // ppcoin: fix wallet spent state according to txindex void CWallet::FixSpentCoins(int& nMismatchFound, int64_t& nBalanceInQuestion, bool fCheckOnly) { nMismatchFound = 0; nBalanceInQuestion = 0; LOCK(cs_wallet); vector vCoins; vCoins.reserve(mapWallet.size()); for (map::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) vCoins.push_back(&(*it).second); CTxDB txdb("r"); BOOST_FOREACH(CWalletTx* pcoin, vCoins) { // Find the corresponding transaction index CTxIndex txindex; if (!txdb.ReadTxIndex(pcoin->GetHash(), txindex)) continue; for (unsigned int n=0; n < pcoin->vout.size(); n++) { if (IsMine(pcoin->vout[n]) && pcoin->IsSpent(n) && (txindex.vSpent.size() <= n || txindex.vSpent[n].IsNull())) { printf("FixSpentCoins found lost coin %sppc %s[%u], %s\n", FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing"); nMismatchFound++; nBalanceInQuestion += pcoin->vout[n].nValue; if (!fCheckOnly) { pcoin->MarkUnspent(n); pcoin->WriteToDisk(); } } else if (IsMine(pcoin->vout[n]) && !pcoin->IsSpent(n) && (txindex.vSpent.size() > n && !txindex.vSpent[n].IsNull())) { printf("FixSpentCoins found spent coin %sppc %s[%u], %s\n", FormatMoney(pcoin->vout[n].nValue).c_str(), pcoin->GetHash().ToString().c_str(), n, fCheckOnly? "repair not attempted" : "repairing"); nMismatchFound++; nBalanceInQuestion += pcoin->vout[n].nValue; if (!fCheckOnly) { pcoin->MarkSpent(n); pcoin->WriteToDisk(); } } } if(IsMine((CTransaction)*pcoin) && (pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetDepthInMainChain() == 0) { printf("FixSpentCoins %s tx %s\n", fCheckOnly ? "found" : "removed", pcoin->GetHash().ToString().c_str()); if (!fCheckOnly) { EraseFromWallet(pcoin->GetHash()); } } } } // ppcoin: disable transaction (only for coinstake) void CWallet::DisableTransaction(const CTransaction &tx) { if (!tx.IsCoinStake() || !IsFromMe(tx)) return; // only disconnecting coinstake requires marking input unspent LOCK(cs_wallet); BOOST_FOREACH(const CTxIn& txin, tx.vin) { map::iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { CWalletTx& prev = (*mi).second; if (txin.prevout.n < prev.vout.size() && IsMine(prev.vout[txin.prevout.n])) { prev.MarkUnspent(txin.prevout.n); prev.WriteToDisk(); } } } } CPubKey CReserveKey::GetReservedKey() { if (nIndex == -1) { CKeyPool keypool; pwallet->ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex != -1) vchPubKey = keypool.vchPubKey; else { printf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!"); vchPubKey = pwallet->vchDefaultKey; } } assert(vchPubKey.IsValid()); return vchPubKey; } void CReserveKey::KeepKey() { if (nIndex != -1) pwallet->KeepKey(nIndex); nIndex = -1; vchPubKey = CPubKey(); } void CReserveKey::ReturnKey() { if (nIndex != -1) pwallet->ReturnKey(nIndex); nIndex = -1; vchPubKey = CPubKey(); } void CWallet::GetAllReserveKeys(set& setAddress) const { setAddress.clear(); CWalletDB walletdb(strWalletFile); LOCK2(cs_main, cs_wallet); BOOST_FOREACH(const int64_t& id, setKeyPool) { CKeyPool keypool; if (!walletdb.ReadPool(id, keypool)) throw runtime_error("GetAllReserveKeyHashes() : read failed"); assert(keypool.vchPubKey.IsValid()); CKeyID keyID = keypool.vchPubKey.GetID(); if (!HaveKey(keyID)) throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool"); setAddress.insert(keyID); } } void CWallet::UpdatedTransaction(const uint256 &hashTx) { { LOCK(cs_wallet); // Only notify UI if this transaction is in this wallet map::const_iterator mi = mapWallet.find(hashTx); if (mi != mapWallet.end()) { NotifyTransactionChanged(this, hashTx, CT_UPDATED); vMintingWalletUpdated.push_back(hashTx); } } } void CWallet::GetAddresses(std::map &mapAddresses) const { mapAddresses.clear(); // get birth times for keys with metadata for (std::map::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) { mapAddresses[it->first] = it->second.nCreateTime ? it->second.nCreateTime : 0; } for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) { // iterate over all wallet transactions... const CWalletTx &wtx = (*it).second; if (wtx.hashBlock == 0) continue; // skip unconfirmed transactions for(std::vector::const_iterator it2 = wtx.vout.begin(); it2 != wtx.vout.end(); it2++) { const CTxOut &out = (*it2); // iterate over all their outputs CBitcoinAddress addressRet; if (ExtractAddress(*this, out.scriptPubKey, addressRet)) { if (mapAddresses.find(addressRet) != mapAddresses.end() && (mapAddresses[addressRet] == 0 || mapAddresses[addressRet] > wtx.nTime)) mapAddresses[addressRet] = wtx.nTime; } else { // multisig output affects more than one key std::vector vAffected; ::ExtractAffectedKeys(*this, out.scriptPubKey, vAffected); for(std::vector::const_iterator it3 = vAffected.begin(); it3 != vAffected.end(); it3++) { CBitcoinAddress addrAffected(*it3); if (mapAddresses.find(addrAffected) != mapAddresses.end() && (mapAddresses[addrAffected] == 0 || mapAddresses[addrAffected] > wtx.nTime)) mapAddresses[addrAffected] = wtx.nTime; } vAffected.clear(); } } } } void CWallet::ClearOrphans() { list orphans; LOCK(cs_wallet); for(map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx *wtx = &(*it).second; if((wtx->IsCoinBase() || wtx->IsCoinStake()) && !wtx->IsInMainChain()) { orphans.push_back(wtx->GetHash()); } } for(list::const_iterator it = orphans.begin(); it != orphans.end(); ++it) EraseFromWallet(*it); }