-164 Removals
+916 Additions
// Copyright (c) 2009-2010 Satoshi Nakamoto// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers// Copyright (c) 2009-2012 The Bitcoin developers
// Copyright (c) 2011-2013 The PPCoin developers// Copyright (c) 2011-2013 The Peercoin developers
// Copyright (c) 2015-2015 The Decent developers
// Distributed under the MIT/X11 software license, see the accompanying// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "wallet.h"#include "wallet.h"
#include "walletdb.h"#include "walletdb.h"
#include "crypter.h"#include "crypter.h"
#include "ui_interface.h"#include "ui_interface.h"
#include "kernel.h"#include "kernel.h"
#include <boost/algorithm/string/replace.hpp>#include "bitcoinrpc.h"
#include "base58.h"#include "encryptionutils.h"
using namespace std;using namespace std;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////
// mapWallet// mapWallet
////
CPubKey CWallet::GenerateNewKey()std::vector<unsigned char> CWallet::GenerateNewKey()
{{
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
RandAddSeedPerfmon(); RandAddSeedPerfmon();
CKey key; CKey key;
key.MakeNewKey(fCompressed); key.MakeNewKey(fCompressed);
// Compressed public keys were introduced in version 0.6.0 // Compressed public keys were introduced in version 0.6.0
if (fCompressed) if (fCompressed)
SetMinVersion(FEATURE_COMPRPUBKEY); SetMinVersion(FEATURE_COMPRPUBKEY);
if (!AddKey(key)) if (!AddKey(key))
throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed");
return key.GetPubKey(); return key.GetPubKey();
}}
bool CWallet::AddKey(const CKey& key)bool CWallet::AddKey(const CKey& key)
{{
if (!CCryptoKeyStore::AddKey(key)) if (!CCryptoKeyStore::AddKey(key))
return false; return false;
if (!fFileBacked) if (!fFileBacked)
return true; return true;
if (!IsCrypted()) if (!IsCrypted())
return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey()); return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey());
return true; return true;
}}
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const vector<unsigned char> &vchCryptedSecret)bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector<unsigned char> &vchCryptedSecret)
{{
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
return false; return false;
if (!fFileBacked) if (!fFileBacked)
return true; return true;
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
if (pwalletdbEncryption) if (pwalletdbEncryption)
return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret); return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret);
else else
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret); return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret);
} }
return false; return false;
}}
bool CWallet::AddCScript(const CScript& redeemScript)bool CWallet::AddCScript(const CScript& redeemScript)
{{
if (!CCryptoKeyStore::AddCScript(redeemScript)) if (!CCryptoKeyStore::AddCScript(redeemScript))
return false; return false;
if (!fFileBacked) if (!fFileBacked)
return true; return true;
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
}}
// ppcoin: optional setting to unlock wallet for block minting only;// peercoin: optional setting to unlock wallet for block minting only;
// serves to disable the trivial sendmoney when OS account compromised// serves to disable the trivial sendmoney when OS account compromised
bool fWalletUnlockMintOnly = false;bool fWalletUnlockMintOnly = false;
bool CWallet::Unlock(const SecureString& strWalletPassphrase)bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{{
if (!IsLocked()) if (!IsLocked())
return false; return false;
CCrypter crypter; CCrypter crypter;
CKeyingMaterial vMasterKey; CKeyingMaterial vMasterKey;
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
{ {
if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false; return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
return false; return false;
if (CCryptoKeyStore::Unlock(vMasterKey)) if (CCryptoKeyStore::Unlock(vMasterKey))
return true; return true;
} }
} }
return false; return false;
}}
bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
{{
bool fWasLocked = IsLocked(); bool fWasLocked = IsLocked();
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
Lock(); Lock();
CCrypter crypter; CCrypter crypter;
CKeyingMaterial vMasterKey; CKeyingMaterial vMasterKey;
BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys) BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
{ {
if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false; return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
return false; return false;
if (CCryptoKeyStore::Unlock(vMasterKey)) if (CCryptoKeyStore::Unlock(vMasterKey))
{ {
int64 nStartTime = GetTimeMillis(); int64 nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)));
nStartTime = GetTimeMillis(); nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);
pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
if (pMasterKey.second.nDeriveIterations < 25000) if (pMasterKey.second.nDeriveIterations < 25000)
pMasterKey.second.nDeriveIterations = 25000; pMasterKey.second.nDeriveIterations = 25000;
printf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); printf("Portfolio passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations);
if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false; return false;
if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey)) if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey))
return false; return false;
CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second); CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second);
if (fWasLocked) if (fWasLocked)
Lock(); Lock();
return true; return true;
} }
} }
} }
return false; return false;
}}
void CWallet::SetBestChain(const CBlockLocator& loc)void CWallet::SetBestChain(const CBlockLocator& loc)
{{
CWalletDB walletdb(strWalletFile); CWalletDB walletdb(strWalletFile);
walletdb.WriteBestBlock(loc); walletdb.WriteBestBlock(loc);
}}
// This class implements an addrIncoming entry that causes pre-0.4// This class implements an addrIncoming entry that causes pre-0.4
// clients to crash on startup if reading a private-key-encrypted wallet.// clients to crash on startup if reading a private-key-encrypted wallet.
class CCorruptAddressclass CCorruptAddress
{{
public:public:
IMPLEMENT_SERIALIZE IMPLEMENT_SERIALIZE
( (
if (nType & SER_DISK) if (nType & SER_DISK)
READWRITE(nVersion); READWRITE(nVersion);
) )
};};
bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)
{{
if (nWalletVersion >= nVersion) if (nWalletVersion >= nVersion)
return true; return true;
// when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way // when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way
if (fExplicit && nVersion > nWalletMaxVersion) if (fExplicit && nVersion > nWalletMaxVersion)
nVersion = FEATURE_LATEST; nVersion = FEATURE_LATEST;
nWalletVersion = nVersion; nWalletVersion = nVersion;
if (nVersion > nWalletMaxVersion) if (nVersion > nWalletMaxVersion)
nWalletMaxVersion = nVersion; nWalletMaxVersion = nVersion;
if (fFileBacked) if (fFileBacked)
{ {
CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile);
if (nWalletVersion >= 40000) if (nWalletVersion >= 40000)
{ {
// Versions prior to 0.4.0 did not support the "minversion" record. // Versions prior to 0.4.0 did not support the "minversion" record.
// Use a CCorruptAddress to make them crash instead. // Use a CCorruptAddress to make them crash instead.
CCorruptAddress corruptAddress; CCorruptAddress corruptAddress;
pwalletdb->WriteSetting("addrIncoming", corruptAddress); pwalletdb->WriteSetting("addrIncoming", corruptAddress);
} }
if (nWalletVersion > 40000) if (nWalletVersion > 40000)
pwalletdb->WriteMinVersion(nWalletVersion); pwalletdb->WriteMinVersion(nWalletVersion);
if (!pwalletdbIn) if (!pwalletdbIn)
delete pwalletdb; delete pwalletdb;
} }
return true; return true;
}}
bool CWallet::SetMaxVersion(int nVersion)bool CWallet::SetMaxVersion(int nVersion)
{{
// cannot downgrade below current version // cannot downgrade below current version
if (nWalletVersion > nVersion) if (nWalletVersion > nVersion)
return false; return false;
nWalletMaxVersion = nVersion; nWalletMaxVersion = nVersion;
return true; return true;
}}
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{{
if (IsCrypted()) if (IsCrypted())
return false; return false;
CKeyingMaterial vMasterKey; CKeyingMaterial vMasterKey;
RandAddSeedPerfmon(); RandAddSeedPerfmon();
vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
CMasterKey kMasterKey; CMasterKey kMasterKey;
RandAddSeedPerfmon(); RandAddSeedPerfmon();
kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); RAND_bytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
CCrypter crypter; CCrypter crypter;
int64 nStartTime = GetTimeMillis(); int64 nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);
kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime));
nStartTime = GetTimeMillis(); nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);
kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;
if (kMasterKey.nDeriveIterations < 25000) if (kMasterKey.nDeriveIterations < 25000)
kMasterKey.nDeriveIterations = 25000; kMasterKey.nDeriveIterations = 25000;
printf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); printf("Encrypting portfolio with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations);
if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod)) if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod))
return false; return false;
if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey)) if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey))
return false; return false;
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
if (fFileBacked) if (fFileBacked)
{ {
pwalletdbEncryption = new CWalletDB(strWalletFile); pwalletdbEncryption = new CWalletDB(strWalletFile);
if (!pwalletdbEncryption->TxnBegin()) if (!pwalletdbEncryption->TxnBegin())
return false; return false;
pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
} }
if (!EncryptKeys(vMasterKey)) if (!EncryptKeys(vMasterKey))
{ {
if (fFileBacked) if (fFileBacked)
pwalletdbEncryption->TxnAbort(); 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. 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 // Encryption was introduced in version 0.4.0
SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);
if (fFileBacked) if (fFileBacked)
{ {
if (!pwalletdbEncryption->TxnCommit()) 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. 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; delete pwalletdbEncryption;
pwalletdbEncryption = NULL; pwalletdbEncryption = NULL;
} }
Lock(); Lock();
Unlock(strWalletPassphrase); Unlock(strWalletPassphrase);
NewKeyPool(); NewKeyPool();
Lock(); Lock();
// Need to completely rewrite the wallet file; if we don't, bdb might keep // 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. // bits of the unencrypted private key in slack space in the database file.
CDB::Rewrite(strWalletFile); CDB::Rewrite(strWalletFile);
} }
return true; return true;
}}
void CWallet::WalletUpdateSpent(const CTransaction &tx)void CWallet::WalletUpdateSpent(const CTransaction &tx)
{{
// Anytime a signature is successfully verified, it's proof the outpoint is spent. // 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 // 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. // restored from backup or the user making copies of wallet.dat.
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
BOOST_FOREACH(const CTxIn& txin, tx.vin) BOOST_FOREACH(const CTxIn& txin, tx.vin)
{ {
map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash); map<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end()) if (mi != mapWallet.end())
{ {
CWalletTx& wtx = (*mi).second; CWalletTx& wtx = (*mi).second;
// Decent: skip empty TX
if (wtx.vout.empty())
continue;
if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n]))
{ {
printf("WalletUpdateSpent found spent coin %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); printf("WalletUpdateSpent found spent unit %sDCT %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkSpent(txin.prevout.n); wtx.MarkSpent(txin.prevout.n);
wtx.WriteToDisk(); wtx.WriteToDisk();
vWalletUpdated.push_back(txin.prevout.hash); vWalletUpdated.push_back(txin.prevout.hash);
} }
} }
} }
} }
}}
void CWallet::MarkDirty()void CWallet::MarkDirty()
{{
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
item.second.MarkDirty(); item.second.MarkDirty();
} }
}}
bool CWallet::AddToWallet(const CWalletTx& wtxIn)bool CWallet::AddToWallet(const CWalletTx& wtxIn)
{{
uint256 hash = wtxIn.GetHash(); uint256 hash = wtxIn.GetHash();
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
// Inserts only if not already there, returns tx inserted or tx found // Inserts only if not already there, returns tx inserted or tx found
pair<map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); pair<map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn));
CWalletTx& wtx = (*ret.first).second; CWalletTx& wtx = (*ret.first).second;
wtx.BindWallet(this); wtx.BindWallet(this);
bool fInsertedNew = ret.second; bool fInsertedNew = ret.second;
if (fInsertedNew) if (fInsertedNew)
wtx.nTimeReceived = GetAdjustedTime(); wtx.nTimeReceived = GetAdjustedTime();
bool fUpdated = false; bool fUpdated = false;
if (!fInsertedNew) if (!fInsertedNew)
{ {
// Merge // Merge
if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock) if (wtxIn.hashBlock != 0 && wtxIn.hashBlock != wtx.hashBlock)
{ {
wtx.hashBlock = wtxIn.hashBlock; wtx.hashBlock = wtxIn.hashBlock;
fUpdated = true; fUpdated = true;
} }
if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex))
{ {
wtx.vMerkleBranch = wtxIn.vMerkleBranch; wtx.vMerkleBranch = wtxIn.vMerkleBranch;
wtx.nIndex = wtxIn.nIndex; wtx.nIndex = wtxIn.nIndex;
fUpdated = true; fUpdated = true;
} }
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe)
{ {
wtx.fFromMe = wtxIn.fFromMe; wtx.fFromMe = wtxIn.fFromMe;
fUpdated = true; fUpdated = true;
} }
fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent); fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent);
} }
//// debug print //// debug print
printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); printf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString().substr(0,10).c_str(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
// Write to disk // Write to disk
if (fInsertedNew || fUpdated) if (fInsertedNew || fUpdated)
if (!wtx.WriteToDisk()) if (!wtx.WriteToDisk())
return false; return false;
#ifndef QT_GUI#ifndef QT_GUI
// If default receiving address gets used, replace it with a new one // If default receiving address gets used, replace it with a new one
CScript scriptDefaultKey; CScript scriptDefaultKey;
scriptDefaultKey.SetDestination(vchDefaultKey.GetID()); scriptDefaultKey.SetBitcoinAddress(vchDefaultKey);
BOOST_FOREACH(const CTxOut& txout, wtx.vout) BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{ {
if (txout.scriptPubKey == scriptDefaultKey) if (txout.scriptPubKey == scriptDefaultKey)
{ {
CPubKey newDefaultKey; std::vector<unsigned char> newDefaultKey;
if (GetKeyFromPool(newDefaultKey, false)) if (GetKeyFromPool(newDefaultKey, false))
{ {
SetDefaultKey(newDefaultKey); SetDefaultKey(newDefaultKey);
SetAddressBookName(vchDefaultKey.GetID(), ""); SetAddressBookName(CBitcoinAddress(vchDefaultKey), "");
} }
} }
} }
#endif#endif
// Notify UI // Notify UI
vWalletUpdated.push_back(hash); vWalletUpdated.push_back(hash);
// since AddToWallet is called directly for self-originating transactions, check for consumption of own coins // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins
WalletUpdateSpent(wtx); WalletUpdateSpent(wtx);
// 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
}
} }
// Refresh UI // Refresh UI
MainFrameRepaint(); MainFrameRepaint();
return true; return true;
}}
// Add a transaction to the wallet, or update it.// 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.// 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.// If fUpdate is true, existing transactions will be updated.
bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock)bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock)
{{
uint256 hash = tx.GetHash(); uint256 hash = tx.GetHash();
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
bool fExisted = mapWallet.count(hash); bool fExisted = mapWallet.count(hash);
if (fExisted && !fUpdate) return false; if (fExisted && !fUpdate) return false;
if (fExisted || IsMine(tx) || IsFromMe(tx)) if (fExisted || IsMine(tx) || IsFromMe(tx))
{ {
CWalletTx wtx(this,tx); CWalletTx wtx(this,tx);
// Get merkle branch if transaction was found in a block // Get merkle branch if transaction was found in a block
if (pblock) if (pblock)
wtx.SetMerkleBranch(pblock); wtx.SetMerkleBranch(pblock);
return AddToWallet(wtx); return AddToWallet(wtx);
} }
else else
WalletUpdateSpent(tx); WalletUpdateSpent(tx);
} }
return false; return false;
}}
bool CWallet::EraseFromWallet(uint256 hash)bool CWallet::EraseFromWallet(uint256 hash)
{{
if (!fFileBacked) if (!fFileBacked)
return false; return false;
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
if (mapWallet.erase(hash)) if (mapWallet.erase(hash))
CWalletDB(strWalletFile).EraseTx(hash); CWalletDB(strWalletFile).EraseTx(hash);
} }
return true; return true;
}}
bool CWallet::IsMine(const CTxIn &txin) constbool CWallet::IsMine(const CTxIn &txin) const
{{
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end()) if (mi != mapWallet.end())
{ {
const CWalletTx& prev = (*mi).second; const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.vout.size()) if (txin.prevout.n < prev.vout.size())
if (IsMine(prev.vout[txin.prevout.n])) if (IsMine(prev.vout[txin.prevout.n]))
return true; return true;
} }
} }
return false; return false;
}}
int64 CWallet::GetDebit(const CTxIn &txin) constint64 CWallet::GetDebit(const CTxIn &txin) const
{{
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end()) if (mi != mapWallet.end())
{ {
const CWalletTx& prev = (*mi).second; const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.vout.size()) if (txin.prevout.n < prev.vout.size())
if (IsMine(prev.vout[txin.prevout.n])) if (IsMine(prev.vout[txin.prevout.n]))
return prev.vout[txin.prevout.n].nValue; return prev.vout[txin.prevout.n].nValue;
} }
} }
return 0; return 0;
}}
bool CWallet::IsChange(const CTxOut& txout) constbool CWallet::IsChange(const CTxOut& txout) const
{{
CTxDestination address; CBitcoinAddress address;
// TODO: fix handling of 'change' outputs. The assumption is that any // TODO: fix handling of 'change' outputs. The assumption is that any
// payment to a TX_PUBKEYHASH that is mine but isn't in the address book // payment to a TX_PUBKEYHASH that is mine but isn't in the address book
// is change. That assumption is likely to break when we implement multisignature // is change. That assumption is likely to break when we implement multisignature
// wallets that return change back into a multi-signature-protected address; // wallets that return change back into a multi-signature-protected address;
// a better way of identifying which outputs are 'the send' and which are // 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 // 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change). // which output, if any, was change).
if (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address)) if (ExtractAddress(txout.scriptPubKey, address) && HaveKey(address))
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
if (!mapAddressBook.count(address)) if (!mapAddressBook.count(address))
return true; return true;
} }
return false; return false;
}}
int64 CWalletTx::GetTxTime() constint64 CWalletTx::GetTxTime() const
{{
return nTimeReceived; return nTimeReceived;
}}
int CWalletTx::GetRequestCount() constint CWalletTx::GetRequestCount() const
{{
// Returns -1 if it wasn't being tracked // Returns -1 if it wasn't being tracked
int nRequests = -1; int nRequests = -1;
{ {
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
if (IsCoinBase() || IsCoinStake()) if (IsCoinBase() || IsCoinStake())
{ {
// Generated block // Generated block
if (hashBlock != 0) if (hashBlock != 0)
{ {
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
if (mi != pwallet->mapRequestCount.end()) if (mi != pwallet->mapRequestCount.end())
nRequests = (*mi).second; nRequests = (*mi).second;
} }
} }
else else
{ {
// Did anyone request this transaction? // Did anyone request this transaction?
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash());
if (mi != pwallet->mapRequestCount.end()) if (mi != pwallet->mapRequestCount.end())
{ {
nRequests = (*mi).second; nRequests = (*mi).second;
// How about the block it's in? // How about the block it's in?
if (nRequests == 0 && hashBlock != 0) if (nRequests == 0 && hashBlock != 0)
{ {
map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);
if (mi != pwallet->mapRequestCount.end()) if (mi != pwallet->mapRequestCount.end())
nRequests = (*mi).second; nRequests = (*mi).second;
else else
nRequests = 1; // If it's in someone else's block it must have got out nRequests = 1; // If it's in someone else's block it must have got out
} }
} }
} }
} }
return nRequests; return nRequests;
}}
void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<CTxDestination, int64> >& listReceived,void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<CBitcoinAddress, int64> >& listReceived,
list<pair<CTxDestination, int64> >& listSent, int64& nFee, string& strSentAccount) const list<pair<CBitcoinAddress, int64> >& listSent, int64& nFee, string& strSentAccount) const
{{
nGeneratedImmature = nGeneratedMature = nFee = 0; nGeneratedImmature = nGeneratedMature = nFee = 0;
listReceived.clear(); listReceived.clear();
listSent.clear(); listSent.clear();
strSentAccount = strFromAccount; strSentAccount = strFromAccount;
if (IsCoinBase() || IsCoinStake()) if (IsCoinBase() || IsCoinStake())
{ {
if (GetBlocksToMaturity() > 0) if (GetBlocksToMaturity() > 0)
nGeneratedImmature = pwallet->GetCredit(*this) - pwallet->GetDebit(*this); nGeneratedImmature = pwallet->GetCredit(*this) - pwallet->GetDebit(*this);
else else
nGeneratedMature = GetCredit() - GetDebit(); nGeneratedMature = GetCredit() - GetDebit();
return; return;
} }
// Compute fee: // Compute fee:
int64 nDebit = GetDebit(); int64 nDebit = GetDebit();
if (nDebit > 0) // debit>0 means we signed/sent this transaction if (nDebit > 0) // debit>0 means we signed/sent this transaction
{ {
int64 nValueOut = GetValueOut(); int64 nValueOut = GetValueOut();
nFee = nDebit - nValueOut; nFee = nDebit - nValueOut;
} }
// Sent/received. // Sent/received.
BOOST_FOREACH(const CTxOut& txout, vout) BOOST_FOREACH(const CTxOut& txout, vout)
{ {
CTxDestination address; CBitcoinAddress address;
vector<unsigned char> vchPubKey; vector<unsigned char> vchPubKey;
if (!ExtractDestination(txout.scriptPubKey, address)) if (!ExtractAddress(txout.scriptPubKey, address))
{ {
printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
this->GetHash().ToString().c_str()); this->GetHash().ToString().c_str());
address = " unknown ";
} }
// Don't report 'change' txouts // Don't report 'change' txouts
if (nDebit > 0 && pwallet->IsChange(txout)) if (nDebit > 0 && pwallet->IsChange(txout))
continue; continue;
if (nDebit > 0) if (nDebit > 0)
listSent.push_back(make_pair(address, txout.nValue)); listSent.push_back(make_pair(address, txout.nValue));
if (pwallet->IsMine(txout)) if (pwallet->IsMine(txout))
listReceived.push_back(make_pair(address, txout.nValue)); listReceived.push_back(make_pair(address, txout.nValue));
} }
}}
void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived,
int64& nSent, int64& nFee) const int64& nSent, int64& nFee) const
{{
nGenerated = nReceived = nSent = nFee = 0; nGenerated = nReceived = nSent = nFee = 0;
int64 allGeneratedImmature, allGeneratedMature, allFee; int64 allGeneratedImmature, allGeneratedMature, allFee;
allGeneratedImmature = allGeneratedMature = allFee = 0; allGeneratedImmature = allGeneratedMature = allFee = 0;
string strSentAccount; string strSentAccount;
list<pair<CTxDestination, int64> > listReceived; list<pair<CBitcoinAddress, int64> > listReceived;
list<pair<CTxDestination, int64> > listSent; list<pair<CBitcoinAddress, int64> > listSent;
GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount);
if (strAccount == "") if (strAccount == "")
nGenerated = allGeneratedMature; nGenerated = allGeneratedMature;
if (strAccount == strSentAccount) if (strAccount == strSentAccount)
{ {
BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& s, listSent) BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& s, listSent)
nSent += s.second; nSent += s.second;
nFee = allFee; nFee = allFee;
} }
{ {
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived) BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived)
{ {
if (pwallet->mapAddressBook.count(r.first)) if (pwallet->mapAddressBook.count(r.first))
{ {
map<CTxDestination, string>::const_iterator mi = pwallet->mapAddressBook.find(r.first); map<CBitcoinAddress, string>::const_iterator mi = pwallet->mapAddressBook.find(r.first);
if (mi != pwallet->mapAddressBook.end() && (*mi).second == strAccount) if (mi != pwallet->mapAddressBook.end() && ((*mi).second == strAccount || (*mi).first.ToString() == strAccount))
nReceived += r.second; nReceived += r.second;
} }
else if (strAccount.empty()) else if (strAccount.empty())
{ {
nReceived += r.second; nReceived += r.second;
} }
} }
} }
}}
void CWalletTx::AddSupportingTransactions(CTxDB& txdb)void CWalletTx::AddSupportingTransactions(CTxDB& txdb)
{{
vtxPrev.clear(); vtxPrev.clear();
const int COPY_DEPTH = 3; const int COPY_DEPTH = 3;
if (SetMerkleBranch() < COPY_DEPTH) if (SetMerkleBranch() < COPY_DEPTH)
{ {
vector<uint256> vWorkQueue; vector<uint256> vWorkQueue;
BOOST_FOREACH(const CTxIn& txin, vin) BOOST_FOREACH(const CTxIn& txin, vin)
vWorkQueue.push_back(txin.prevout.hash); vWorkQueue.push_back(txin.prevout.hash);
// This critsect is OK because txdb is already open // This critsect is OK because txdb is already open
{ {
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
map<uint256, const CMerkleTx*> mapWalletPrev; map<uint256, const CMerkleTx*> mapWalletPrev;
set<uint256> setAlreadyDone; set<uint256> setAlreadyDone;
for (unsigned int i = 0; i < vWorkQueue.size(); i++) for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{ {
uint256 hash = vWorkQueue[i]; uint256 hash = vWorkQueue[i];
if (setAlreadyDone.count(hash)) if (setAlreadyDone.count(hash))
continue; continue;
setAlreadyDone.insert(hash); setAlreadyDone.insert(hash);
CMerkleTx tx; CMerkleTx tx;
map<uint256, CWalletTx>::const_iterator mi = pwallet->mapWallet.find(hash); map<uint256, CWalletTx>::const_iterator mi = pwallet->mapWallet.find(hash);
if (mi != pwallet->mapWallet.end()) if (mi != pwallet->mapWallet.end())
{ {
tx = (*mi).second; tx = (*mi).second;
BOOST_FOREACH(const CMerkleTx& txWalletPrev, (*mi).second.vtxPrev) BOOST_FOREACH(const CMerkleTx& txWalletPrev, (*mi).second.vtxPrev)
mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev; mapWalletPrev[txWalletPrev.GetHash()] = &txWalletPrev;
} }
else if (mapWalletPrev.count(hash)) else if (mapWalletPrev.count(hash))
{ {
tx = *mapWalletPrev[hash]; tx = *mapWalletPrev[hash];
} }
else if (!fClient && txdb.ReadDiskTx(hash, tx)) else if (!fClient && txdb.ReadDiskTx(hash, tx))
{ {
; ;
} }
else else
{ {
printf("ERROR: AddSupportingTransactions() : unsupported transaction\n"); printf("ERROR: AddSupportingTransactions() : unsupported transaction\n");
continue; continue;
} }
int nDepth = tx.SetMerkleBranch(); int nDepth = tx.SetMerkleBranch();
vtxPrev.push_back(tx); vtxPrev.push_back(tx);
if (nDepth < COPY_DEPTH) if (nDepth < COPY_DEPTH)
{ {
BOOST_FOREACH(const CTxIn& txin, tx.vin) BOOST_FOREACH(const CTxIn& txin, tx.vin)
vWorkQueue.push_back(txin.prevout.hash); vWorkQueue.push_back(txin.prevout.hash);
} }
} }
} }
} }
reverse(vtxPrev.begin(), vtxPrev.end()); reverse(vtxPrev.begin(), vtxPrev.end());
}}
bool CWalletTx::WriteToDisk()bool CWalletTx::WriteToDisk()
{{
return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this); return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this);
}}
// Scan the block chain (starting in pindexStart) for transactions// Scan the block chain (starting in pindexStart) for transactions
// from or to us. If fUpdate is true, found transactions that already// from or to us. If fUpdate is true, found transactions that already
// exist in the wallet will be updated.// exist in the wallet will be updated.
int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
{{
int ret = 0; int ret = 0;
CBlockIndex* pindex = pindexStart; CBlockIndex* pindex = pindexStart;
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
while (pindex) while (pindex)
{ {
CBlock block; CBlock block;
block.ReadFromDisk(pindex, true); block.ReadFromDisk(pindex, true);
BOOST_FOREACH(CTransaction& tx, block.vtx) BOOST_FOREACH(CTransaction& tx, block.vtx)
{ {
if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) if (AddToWalletIfInvolvingMe(tx, &block, fUpdate))
ret++; ret++;
} }
pindex = pindex->pnext; pindex = pindex->pnext;
} }
} }
return ret; return ret;
}}
int CWallet::ScanForWalletTransaction(const uint256& hashTx)int CWallet::ScanForWalletTransaction(const uint256& hashTx)
{{
CTransaction tx; CTransaction tx;
tx.ReadFromDisk(COutPoint(hashTx, 0)); tx.ReadFromDisk(COutPoint(hashTx, 0));
if (AddToWalletIfInvolvingMe(tx, NULL, true, true)) if (AddToWalletIfInvolvingMe(tx, NULL, true, true))
return 1; return 1;
return 0; return 0;
}}
void CWallet::ReacceptWalletTransactions()void CWallet::ReacceptWalletTransactions()
{{
CTxDB txdb("r"); CTxDB txdb("r");
bool fRepeat = true; bool fRepeat = true;
while (fRepeat) while (fRepeat)
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
fRepeat = false; fRepeat = false;
vector<CDiskTxPos> vMissingTx; vector<CDiskTxPos> vMissingTx;
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{ {
CWalletTx& wtx = item.second; CWalletTx& wtx = item.second;
if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1))) if ((wtx.IsCoinBase() && wtx.IsSpent(0)) || (wtx.IsCoinStake() && wtx.IsSpent(1)))
continue; continue;
CTxIndex txindex; CTxIndex txindex;
bool fUpdated = false; bool fUpdated = false;
if (txdb.ReadTxIndex(wtx.GetHash(), txindex)) if (txdb.ReadTxIndex(wtx.GetHash(), txindex))
{ {
// Update fSpent if a tx got spent somewhere else by a copy of wallet.dat // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat
if (txindex.vSpent.size() != wtx.vout.size()) if (txindex.vSpent.size() != wtx.vout.size())
{ {
printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size()); printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size());
continue; continue;
} }
for (unsigned int i = 0; i < txindex.vSpent.size(); i++) for (unsigned int i = 0; i < txindex.vSpent.size(); i++)
{ {
if (wtx.IsSpent(i)) if (wtx.IsSpent(i))
continue; continue;
if (!txindex.vSpent[i].IsNull() && IsMine(wtx.vout[i])) if (!txindex.vSpent[i].IsNull() && IsMine(wtx.vout[i]))
{ {
wtx.MarkSpent(i); wtx.MarkSpent(i);
fUpdated = true; fUpdated = true;
vMissingTx.push_back(txindex.vSpent[i]); vMissingTx.push_back(txindex.vSpent[i]);
} }
} }
if (fUpdated) if (fUpdated)
{ {
printf("ReacceptWalletTransactions found spent coin %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); printf("ReacceptWalletTransactions found spent coin %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkDirty(); wtx.MarkDirty();
wtx.WriteToDisk(); wtx.WriteToDisk();
} }
} }
else else
{ {
// Reaccept any txes of ours that aren't already in a block // Reaccept any txes of ours that aren't already in a block
if (!(wtx.IsCoinBase() || wtx.IsCoinStake())) if (!(wtx.IsCoinBase() || wtx.IsCoinStake()))
wtx.AcceptWalletTransaction(txdb, false); wtx.AcceptWalletTransaction(txdb, false);
} }
} }
if (!vMissingTx.empty()) if (!vMissingTx.empty())
{ {
// TODO: optimize this to scan just part of the block chain? // TODO: optimize this to scan just part of the block chain?
if (ScanForWalletTransactions(pindexGenesisBlock)) if (ScanForWalletTransactions(pindexGenesisBlock))
fRepeat = true; // Found missing transactions: re-do Reaccept. fRepeat = true; // Found missing transactions: re-do Reaccept.
} }
} }
}}
void CWalletTx::RelayWalletTransaction(CTxDB& txdb)void CWalletTx::RelayWalletTransaction(CTxDB& txdb)
{{
BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) BOOST_FOREACH(const CMerkleTx& tx, vtxPrev)
{ {
if (!(tx.IsCoinBase() || tx.IsCoinStake())) if (!(tx.IsCoinBase() || tx.IsCoinStake()))
{ {
uint256 hash = tx.GetHash(); uint256 hash = tx.GetHash();
if (!txdb.ContainsTx(hash)) if (!txdb.ContainsTx(hash))
RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx);
} }
} }
if (!(IsCoinBase() || IsCoinStake())) if (!(IsCoinBase() || IsCoinStake()))
{ {
uint256 hash = GetHash(); uint256 hash = GetHash();
if (!txdb.ContainsTx(hash)) if (!txdb.ContainsTx(hash))
{ {
printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str());
RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this);
} }
} }
}}
void CWalletTx::RelayWalletTransaction()void CWalletTx::RelayWalletTransaction()
{{
CTxDB txdb("r"); CTxDB txdb("r");
RelayWalletTransaction(txdb); RelayWalletTransaction(txdb);
}}
void CWallet::ResendWalletTransactions()void CWallet::ResendWalletTransactions()
{{
// Do this infrequently and randomly to avoid giving away // Do this infrequently and randomly to avoid giving away
// that these are our transactions. // that these are our transactions.
static int64 nNextTime; static int64 nNextTime;
if (GetTime() < nNextTime) if (GetTime() < nNextTime)
return; return;
bool fFirst = (nNextTime == 0); bool fFirst = (nNextTime == 0);
nNextTime = GetTime() + GetRand(30 * 60); nNextTime = GetTime() + GetRand(30 * 60);
if (fFirst) if (fFirst)
return; return;
// Only do it if there's been a new block since last time // Only do it if there's been a new block since last time
static int64 nLastTime; static int64 nLastTime;
if (nTimeBestReceived < nLastTime) if (nTimeBestReceived < nLastTime)
return; return;
nLastTime = GetTime(); nLastTime = GetTime();
// Rebroadcast any of our txes that aren't in a block yet // Rebroadcast any of our txes that aren't in a block yet
printf("ResendWalletTransactions()\n"); printf("ResendWalletTransactions()\n");
CTxDB txdb("r"); CTxDB txdb("r");
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
// Sort them in chronological order // Sort them in chronological order
multimap<unsigned int, CWalletTx*> mapSorted; multimap<unsigned int, CWalletTx*> mapSorted;
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{ {
CWalletTx& wtx = item.second; CWalletTx& wtx = item.second;
// Don't rebroadcast until it's had plenty of time that // Don't rebroadcast until it's had plenty of time that
// it should have gotten in already by now. // it should have gotten in already by now.
if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60)
mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx));
} }
BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted)
{ {
CWalletTx& wtx = *item.second; CWalletTx& wtx = *item.second;
if (wtx.CheckTransaction()) if (wtx.CheckTransaction())
wtx.RelayWalletTransaction(txdb); wtx.RelayWalletTransaction(txdb);
else else
printf("ResendWalletTransactions() : CheckTransaction failed for transaction %s\n", wtx.GetHash().ToString().c_str()); printf("ResendWalletTransactions() : CheckTransaction failed for transaction %s\n", wtx.GetHash().ToString().c_str());
} }
} }
}}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////
// Actions// Actions
////
int64 CWallet::GetBalance() constint64 CWallet::GetBalance() const
{{
int64 nTotal = 0; int64 nTotal = 0;
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{ {
const CWalletTx* pcoin = &(*it).second; const CWalletTx* pcoin = &(*it).second;
if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
continue; continue;
nTotal += pcoin->GetAvailableCredit(); nTotal += pcoin->GetAvailableCredit();
} }
} }
return nTotal; return nTotal;
}}
int64 CWallet::GetUnconfirmedBalance() constint64 CWallet::GetUnconfirmedBalance() const
{{
int64 nTotal = 0; int64 nTotal = 0;
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{ {
const CWalletTx* pcoin = &(*it).second; const CWalletTx* pcoin = &(*it).second;
if (pcoin->IsFinal() && pcoin->IsConfirmed()) if (pcoin->IsFinal() && pcoin->IsConfirmed())
continue; continue;
nTotal += pcoin->GetAvailableCredit(); nTotal += pcoin->GetAvailableCredit();
} }
} }
return nTotal; return nTotal;
}}
// ppcoin: total coins staked (non-spendable until maturity)// peercoin: total coins staked (non-spendable until maturity)
int64 CWallet::GetStake() constint64 CWallet::GetStake() const
{{
int64 nTotal = 0; int64 nTotal = 0;
LOCK(cs_wallet); LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{ {
const CWalletTx* pcoin = &(*it).second; const CWalletTx* pcoin = &(*it).second;
if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) if (pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
nTotal += CWallet::GetCredit(*pcoin); nTotal += CWallet::GetCredit(*pcoin);
} }
return nTotal; return nTotal;
}}
int64 CWallet::GetNewMint() constint64 CWallet::GetNewMint() const
{{
int64 nTotal = 0; int64 nTotal = 0;
LOCK(cs_wallet); LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{ {
const CWalletTx* pcoin = &(*it).second; const CWalletTx* pcoin = &(*it).second;
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0) if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0 && pcoin->GetDepthInMainChain() > 0)
nTotal += CWallet::GetCredit(*pcoin); nTotal += CWallet::GetCredit(*pcoin);
} }
return nTotal; return nTotal;
}}
// populate vCoins with vector of spendable (age, (value, (transaction, output_number))) outputs
void CWallet::AvailableCoins(unsigned int nSpendTime, vector<COutput>& vCoins, bool fOnlyConfirmed) const
{
vCoins.clear();
{bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, list<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
LOCK(cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
if (!pcoin->IsFinal())
continue;
if (fOnlyConfirmed && !pcoin->IsConfirmed())
continue;
if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0)
continue;
for (int i = 0; i < pcoin->vout.size(); i++)
{
if (pcoin->nTime > nSpendTime)
continue; // ppcoin: timestamp must not exceed spend time
if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue > 0)
vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain()));
}
}
}
}
bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins,
set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
{{
setCoinsRet.clear(); setCoinsRet.clear();
nValueRet = 0; nValueRet = 0;
// List of values less than target // List of values less than target
pair<int64, pair<const CWalletTx*,unsigned int> > coinLowestLarger; pair<int64, pair<const CWalletTx*,unsigned int> > coinLowestLarger;
coinLowestLarger.first = std::numeric_limits<int64>::max(); coinLowestLarger.first = std::numeric_limits<int64>::max();
coinLowestLarger.second.first = NULL; coinLowestLarger.second.first = NULL;
vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue; vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue;
int64 nTotalLower = 0; int64 nTotalLower = 0;
BOOST_FOREACH(COutput output, vCoins)
{ {
const CWalletTx *pcoin = output.tx; LOCK(cs_wallet);
vector<const CWalletTx*> vCoins;
vCoins.reserve(mapWallet.size());
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
vCoins.push_back(&(*it).second);
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) BOOST_FOREACH(const CWalletTx* pcoin, vCoins)
continue; {
if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
continue;
if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0)
continue;
int nDepth = pcoin->GetDepthInMainChain();
if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
continue;
int i = output.i; for (unsigned int i = 0; i < pcoin->vout.size(); i++)
int64 n = pcoin->vout[i].nValue; {
if (pcoin->IsSpent(i) || !IsMine(pcoin->vout[i]))
continue;
if (pcoin->nTime > nSpendTime)
continue; // peercoin: timestamp must not exceed spend time
int64 n = pcoin->vout[i].nValue;
if (n <= 0)
continue;
pair<int64,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i)); pair<int64,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin,i));
if (n == nTargetValue) if (n == nTargetValue)
{ {
setCoinsRet.insert(coin.second); setCoinsRet.push_back(coin.second);
nValueRet += coin.first; nValueRet += coin.first;
return true; return true;
} }
else if (n < nTargetValue + CENT) else if (n < nTargetValue + CENT)
{ {
vValue.push_back(coin); vValue.push_back(coin);
nTotalLower += n; nTotalLower += n;
} }
else if (n < coinLowestLarger.first) else if (n < coinLowestLarger.first)
{ {
coinLowestLarger = coin; coinLowestLarger = coin;
}
}
} }
} }
if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT) if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT)
{ {
for (unsigned int i = 0; i < vValue.size(); ++i) for (unsigned int i = 0; i < vValue.size(); ++i)
{ {
setCoinsRet.insert(vValue[i].second); setCoinsRet.push_back(vValue[i].second);
nValueRet += vValue[i].first; nValueRet += vValue[i].first;
} }
return true; return true;
} }
if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0)) if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0))
{ {
if (coinLowestLarger.second.first == NULL) if (coinLowestLarger.second.first == NULL)
return false; return false;
setCoinsRet.insert(coinLowestLarger.second); setCoinsRet.push_back(coinLowestLarger.second);
nValueRet += coinLowestLarger.first; nValueRet += coinLowestLarger.first;
return true; return true;
} }
if (nTotalLower >= nTargetValue + CENT) if (nTotalLower >= nTargetValue + CENT)
nTargetValue += CENT; nTargetValue += CENT;
// Solve subset sum by stochastic approximation // Solve subset sum by stochastic approximation
sort(vValue.rbegin(), vValue.rend()); sort(vValue.rbegin(), vValue.rend());
vector<char> vfIncluded; vector<char> vfIncluded;
vector<char> vfBest(vValue.size(), true); vector<char> vfBest(vValue.size(), true);
int64 nBest = nTotalLower; int64 nBest = nTotalLower;
for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++)
{ {
vfIncluded.assign(vValue.size(), false); vfIncluded.assign(vValue.size(), false);
int64 nTotal = 0; int64 nTotal = 0;
bool fReachedTarget = false; bool fReachedTarget = false;
for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)
{ {
for (unsigned int i = 0; i < vValue.size(); i++) for (unsigned int i = 0; i < vValue.size(); i++)
{ {
if (nPass == 0 ? rand() % 2 : !vfIncluded[i]) if (nPass == 0 ? rand() % 2 : !vfIncluded[i])
{ {
nTotal += vValue[i].first; nTotal += vValue[i].first;
vfIncluded[i] = true; vfIncluded[i] = true;
if (nTotal >= nTargetValue) if (nTotal >= nTargetValue)
{ {
fReachedTarget = true; fReachedTarget = true;
if (nTotal < nBest) if (nTotal < nBest)
{ {
nBest = nTotal; nBest = nTotal;
vfBest = vfIncluded; vfBest = vfIncluded;
} }
nTotal -= vValue[i].first; nTotal -= vValue[i].first;
vfIncluded[i] = false; vfIncluded[i] = false;
} }
} }
} }
} }
} }
// If the next larger is still closer, return it // If the next larger is still closer, return it
if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue) if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue)
{ {
setCoinsRet.insert(coinLowestLarger.second); setCoinsRet.push_back(coinLowestLarger.second);
nValueRet += coinLowestLarger.first; nValueRet += coinLowestLarger.first;
} }
else { else {
for (unsigned int i = 0; i < vValue.size(); i++) for (unsigned int i = 0; i < vValue.size(); i++)
if (vfBest[i]) if (vfBest[i])
{ {
setCoinsRet.insert(vValue[i].second); setCoinsRet.push_back(vValue[i].second);
nValueRet += vValue[i].first; nValueRet += vValue[i].first;
} }
#ifdef DEBUG
//// debug print //// debug print
printf("SelectCoins() best subset: "); if (fDebug && GetBoolArg("-printselectcoin"))
for (unsigned int i = 0; i < vValue.size(); i++) {
if (vfBest[i]) printf("SelectCoins() best subset: ");
printf("%s ", FormatMoney(vValue[i].first).c_str()); for (unsigned int i = 0; i < vValue.size(); i++)
printf("total %s\n", FormatMoney(nBest).c_str()); if (vfBest[i])
#endif printf("%s ", FormatMoney(vValue[i].first).c_str());
printf("total %s\n", FormatMoney(nBest).c_str());
}
} }
return true; return true;
}}
bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) constbool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, list<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
{{
vector<COutput> vCoins; return (SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 6, setCoinsRet, nValueRet) ||
AvailableCoins(nSpendTime, vCoins); SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 1, setCoinsRet, nValueRet) ||
SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, setCoinsRet, nValueRet));
return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) ||
SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) ||
SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet));
}}
// Decent: populate vCoins with vector of available CTxOut
void CWallet::AvailableCoins(vector<CTxOut>& vCoins) const
{
bool fIncludeZeroValue = true;
vCoins.clear();
{
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
if (!(*pcoin).IsFinal())
continue;
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
continue;
int nDepth = pcoin->GetDepthInMainChain();
if (nDepth < 0)
continue;
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
bool mine = IsMine(pcoin->vout[i]);
if (!pcoin->IsSpent(i) && mine && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue))
vCoins.push_back(pcoin->vout[i]);
}
}
}
}
bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)
{{
int64 nValue = 0; int64 nValue = 0;
BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend)
{ {
if (nValue < 0) if (nValue < 0)
return false; return error("create tx : nValue < 0");
nValue += s.second; nValue += s.second;
} }
if (vecSend.empty() || nValue < 0) if (vecSend.empty() || nValue < 0)
return false; return error("create tx : vecSend.empty()");
wtxNew.BindWallet(this); wtxNew.BindWallet(this);
{ {
LOCK2(cs_main, cs_wallet); LOCK2(cs_main, cs_wallet);
// txdb must be opened before the mapWallet lock // txdb must be opened before the mapWallet lock
CTxDB txdb("r"); CTxDB txdb("r");
{ {
nFeeRet = nTransactionFee; nFeeRet = MIN_TX_FEE;
loop loop
{ {
wtxNew.vin.clear(); wtxNew.vin.clear();
wtxNew.vout.clear(); wtxNew.vout.clear();
wtxNew.fFromMe = true; wtxNew.fFromMe = true;
int64 nTotalValue = nValue + nFeeRet; int64 nTotalValue = nValue + nFeeRet;
double dPriority = 0; double dPriority = 0;
// vouts to the payees // vouts to the payees
BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend)
wtxNew.vout.push_back(CTxOut(s.second, s.first)); wtxNew.vout.push_back(CTxOut(s.second, s.first));
// Choose coins to use // Choose coins to use
set<pair<const CWalletTx*,unsigned int> > setCoins; list<pair<const CWalletTx*,unsigned int> > setCoins;
int64 nValueIn = 0; int64 nValueIn = 0;
if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn)) if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn))
return false; return error("create tx : SelectCoins() failed");
CScript scriptChange; CScript scriptChange;
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{ {
int64 nCredit = pcoin.first->vout[pcoin.second].nValue; int64 nCredit = pcoin.first->vout[pcoin.second].nValue;
dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain(); dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain();
scriptChange = pcoin.first->vout[pcoin.second].scriptPubKey; scriptChange = pcoin.first->vout[pcoin.second].scriptPubKey;
} }
int64 nChange = nValueIn - nValue - nFeeRet; int64 nChange = nValueIn - nValue - nFeeRet;
// if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE // if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE
// or until nChange becomes zero // or until nChange becomes zero
// NOTE: this depends on the exact behaviour of GetMinFee // NOTE: this depends on the exact behaviour of GetMinFee
if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT) if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT)
{ {
int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet); int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet);
nChange -= nMoveToFee; nChange -= nMoveToFee;
nFeeRet += nMoveToFee; nFeeRet += nMoveToFee;
} }
// ppcoin: sub-cent change is moved to fee // peercoin: sub-cent change is moved to fee
if (nChange > 0 && nChange < MIN_TXOUT_AMOUNT) if (nChange > 0 && nChange < MIN_TXOUT_AMOUNT)
{ {
nFeeRet += nChange; nFeeRet += nChange;
nChange = 0; nChange = 0;
} }
if (nChange > 0) if (nChange > 0)
{ {
// Note: We use a new key here to keep it from being obvious which side is the change. // 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 // 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. // 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 // 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 // rediscover unknown transactions that were written with keys of ours to recover
// post-backup change. // post-backup change.
if (!GetBoolArg("-avatar")) // ppcoin: not avatar mode if (!GetBoolArg("-avatar", true)) // peercoin: not avatar mode; peershares: avatar mode enabled by default to avoid change being sent to hidden address
{ {
// Reserve a new key pair from key pool // Reserve a new key pair from key pool
CPubKey vchPubKey = reservekey.GetReservedKey(); vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
// assert(mapKeys.count(vchPubKey)); // assert(mapKeys.count(vchPubKey));
// Fill a vout to ourself // Fill a vout to ourself
// TODO: pass in scriptChange instead of reservekey so // TODO: pass in scriptChange instead of reservekey so
// change transaction isn't always pay-to-bitcoin-address // change transaction isn't always pay-to-bitcoin-address
scriptChange.SetDestination(vchPubKey.GetID()); scriptChange.SetBitcoinAddress(vchPubKey);
} }
else
reservekey.ReturnKey(); // return key in avatar mode
// Insert change txn at random position: // Insert change txn at random position:
vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size());
wtxNew.vout.insert(position, CTxOut(nChange, scriptChange)); wtxNew.vout.insert(position, CTxOut(nChange, scriptChange));
} }
else else
reservekey.ReturnKey(); reservekey.ReturnKey();
// Fill vin // Fill vin
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second));
// Sign // Sign
int nIn = 0; int nIn = 0;
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
if (!SignSignature(*this, *coin.first, wtxNew, nIn++)) if (!SignSignature(*this, *coin.first, wtxNew, nIn++))
return error("create tx : SignSignature() failed");
// Limit size
unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBytes >= MAX_BLOCK_SIZE/4)
return error("create tx : nBytes >= MAX_BLOCK_SIZE/4");
dPriority /= nBytes;
// Check that enough fee is included
int64 nPayFee = MIN_TX_FEE * (1 + (int64)nBytes / 1000);
int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND);
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;
}
// Decent: create content_submit TX
bool CWallet::CreateContentSubmitTX(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64 custodyPayment, int64& nFeeRet)
{
int64 nValue = custodyPayment;
wtxNew.BindWallet(this);
{
LOCK2(cs_main, cs_wallet);
// txdb must be opened before the mapWallet lock
CTxDB txdb("r");
{
nFeeRet = MIN_TX_FEE;
loop
{
wtxNew.vin.clear();
wtxNew.vout.clear();
wtxNew.fFromMe = true;
int64 nTotalValue = nValue + nFeeRet;
double dPriority = 0;
// vouts to the payees
BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend)
wtxNew.vout.push_back(CTxOut(s.second, s.first));
// Choose coins to use
list<pair<const CWalletTx*,unsigned int> > setCoins;
int64 nValueIn = 0;
if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn))
return false;
CScript scriptChange;
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
int64 nCredit = pcoin.first->vout[pcoin.second].nValue;
dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain();
scriptChange = pcoin.first->vout[pcoin.second].scriptPubKey;
}
int64 nChange = nValueIn - nValue - nFeeRet;
// if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE
// or until nChange becomes zero
// NOTE: this depends on the exact behaviour of GetMinFee
if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT)
{
int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet);
nChange -= nMoveToFee;
nFeeRet += nMoveToFee;
}
// peercoin: sub-cent change is moved to fee
if (nChange > 0 && nChange < MIN_TXOUT_AMOUNT)
{
nFeeRet += nChange;
nChange = 0;
}
if (nChange > 0)
{
if (!GetBoolArg("-avatar", true)) // peercoin: not avatar mode; peershares: avatar mode enabled by default to avoid change being sent to hidden address
{
// Reserve a new key pair from key pool
vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
// assert(mapKeys.count(vchPubKey));
// Fill a vout to ourself
// TODO: pass in scriptChange instead of reservekey so
// change transaction isn't always pay-to-bitcoin-address
scriptChange.SetBitcoinAddress(vchPubKey);
}
else
reservekey.ReturnKey(); // return key in avatar mode
// Insert change txn at random position: Decent: TODO
// vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size());
wtxNew.vout.insert(wtxNew.vout.begin(), 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; return false;
// Limit size // Limit size
unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION); unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBytes >= MAX_BLOCK_SIZE_GEN/5) if (nBytes >= MAX_BLOCK_SIZE/4)
return false;
dPriority /= nBytes;
// Check that enough fee is included
int64 nPayFee = MIN_TX_FEE * (1 + (int64)nBytes / 1000);
int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND);
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;
}
//Decent: build TX to "spend" from OP_PROMISE_TO_PAY output
bool CWallet::CreateRequestToBuyTX(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, COutPoint& content)
{
int64 nValue = 0;
BOOST_FOREACH (const PAIRTYPE(CScript, int64)& 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 = MIN_TX_FEE;
loop
{
wtxNew.vin.clear();
wtxNew.vout.clear();
wtxNew.fFromMe = true;
int64 nTotalValue = nValue + nFeeRet;
double dPriority = 0;
// vouts to the payees
BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend)
if (!s.first.empty()) // Decent: skip price out, payment shall be destroyed
wtxNew.vout.push_back(CTxOut(s.second, s.first));
// Choose coins to use
list<pair<const CWalletTx*,unsigned int> > setCoins;
int64 nValueIn = 0;
if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn))
return false;
CScript scriptChange;
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
int64 nCredit = pcoin.first->vout[pcoin.second].nValue;
dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain();
scriptChange = pcoin.first->vout[pcoin.second].scriptPubKey;
}
// Decent: just for count
pair<const CWalletTx*,unsigned int> emptyCoin;
setCoins.push_front(emptyCoin);
int64 nChange = nValueIn - nValue - nFeeRet;
// if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE
// or until nChange becomes zero
// NOTE: this depends on the exact behaviour of GetMinFee
if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT)
{
int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet);
nChange -= nMoveToFee;
nFeeRet += nMoveToFee;
}
// peercoin: sub-cent change is moved to fee
if (nChange > 0 && nChange < MIN_TXOUT_AMOUNT)
{
nFeeRet += nChange;
nChange = 0;
}
if (nChange > 0)
{
// 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.
if (!GetBoolArg("-avatar", true)) // peercoin: not avatar mode; peershares: avatar mode enabled by default to avoid change being sent to hidden address
{
// Reserve a new key pair from key pool
vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
// assert(mapKeys.count(vchPubKey));
// Fill a vout to ourself
// TODO: pass in scriptChange instead of reservekey so
// change transaction isn't always pay-to-bitcoin-address
scriptChange.SetBitcoinAddress(vchPubKey);
}
else
reservekey.ReturnKey(); // return key in avatar mode
// Insert change txn at random position:
vector<CTxOut>::iterator position = wtxNew.vout.begin();
wtxNew.vout.insert(position, CTxOut(nChange, scriptChange));
}
else
reservekey.ReturnKey();
// Fill vin
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
{
if(!coin.first)
{
// Decent: here is "spend" from OP_PROMISETOPAY output
wtxNew.vin.push_back(CTxIn(content.hash,content.n));
}
else
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(coin.first)
{
if (!SignSignature(*this, *coin.first, wtxNew, nIn++))
return false;
}
else
nIn++;
// Limit size
unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBytes >= MAX_BLOCK_SIZE/4)
return false;
dPriority /= nBytes;
// Check that enough fee is included
int64 nPayFee = MIN_TX_FEE * (1 + (int64)nBytes / 1000);
int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND);
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;
}
//Decent: proof_of_custody TX
bool CWallet::CreateTxWithProof(CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, COutPoint& prevPoint, valtype& proof)
{
int64 nValue = 0;
wtxNew.BindWallet(this);
{
LOCK2(cs_main, cs_wallet);
// txdb must be opened before the mapWallet lock
CTxDB txdb("r");
{
nFeeRet = MIN_TX_FEE;
loop
{
wtxNew.vin.clear();
wtxNew.vout.clear();
wtxNew.fFromMe = true;
int64 nTotalValue = nValue + nFeeRet;
// Decent: it will returned as change, to avoid empty vout
nTotalValue += 1;
double dPriority = 0;
// Choose coins to use
list<pair<const CWalletTx*,unsigned int> > setCoins;
int64 nValueIn = 0;
if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn))
return false;
CScript scriptChange;
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
int64 nCredit = pcoin.first->vout[pcoin.second].nValue;
dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain();
scriptChange = pcoin.first->vout[pcoin.second].scriptPubKey;
}
// Decent:
CWalletTx prevTx;
if (!prevTx.SetByHash(prevPoint.hash))
return error("Create tx : read prevPoint failed");
setCoins.push_front(make_pair(&prevTx, prevPoint.n));
int64 nChange = nValueIn - nValue - nFeeRet;
// if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE
// or until nChange becomes zero
// NOTE: this depends on the exact behaviour of GetMinFee
if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT)
{
int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet);
nChange -= nMoveToFee;
nFeeRet += nMoveToFee;
}
// peercoin: sub-cent change is moved to fee
if (nChange > 0 && nChange < MIN_TXOUT_AMOUNT)
{
nFeeRet += nChange;
nChange = 0;
}
if (nChange > 0)
{
if (!GetBoolArg("-avatar", true)) // peercoin: not avatar mode; peershares: avatar mode enabled by default to avoid change being sent to hidden address
{
// Reserve a new key pair from key pool
vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
// assert(mapKeys.count(vchPubKey));
// Fill a vout to ourself
// TODO: pass in scriptChange instead of reservekey so
// change transaction isn't always pay-to-bitcoin-address
scriptChange.SetBitcoinAddress(vchPubKey);
}
else
reservekey.ReturnKey(); // return key in avatar mode
// Insert change txout, must be before special_decent outs
wtxNew.vout.insert(wtxNew.vout.begin(), 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(wtxNew.vin[nIn].prevout == prevPoint)
{
wtxNew.vin[nIn].scriptSig << proof;
if (!SignSignature(*this, *coin.first, wtxNew, nIn++, SIGHASH_WITHPROOF))
return false;
}
else
{
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/4)
return false; return false;
dPriority /= nBytes; dPriority /= nBytes;
// Check that enough fee is included // Check that enough fee is included
int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); int64 nPayFee = MIN_TX_FEE * (1 + (int64)nBytes / 1000);
int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND); int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND);
if (nFeeRet < max(nPayFee, nMinFee)) if (nFeeRet < max(nPayFee, nMinFee))
{ {
nFeeRet = max(nPayFee, nMinFee); nFeeRet = max(nPayFee, nMinFee);
continue; continue;
} }
if (!(wtxNew.IsProofOfCustody() || wtxNew.IsLeaveRating()))
return error("Create tx: invalid type");
// Fill vtxPrev by copying from previous transactions vtxPrev
wtxNew.AddSupportingTransactions(txdb);
wtxNew.fTimeReceivedIsTxTime = true;
break;
}
}
}
return true;
}
//Decent: delivery_keys TX
bool CWallet::CreateDeliveryTX(CWalletTx& wtxNew, CReserveKey& reservekey, COutPoint& prevPoint, valtype &encryptedKey, valtype& proof)
{
int64 nFee = 0;
int64 nValue = 0;
wtxNew.BindWallet(this);
{
LOCK2(cs_main, cs_wallet);
// txdb must be opened before the mapWallet lock
CTxDB txdb("r");
{
nFee = MIN_TX_FEE;
loop
{
wtxNew.vin.clear();
wtxNew.vout.clear();
wtxNew.fFromMe = true;
int64 nTotalValue = nValue + nFee;
// Decent: it will returned as change, to avoid empty vout
nTotalValue += 1;
double dPriority = 0;
// Choose coins to use
list<pair<const CWalletTx*,unsigned int> > setCoins;
int64 nValueIn = 0;
if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn))
return false;
CScript scriptChange;
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
int64 nCredit = pcoin.first->vout[pcoin.second].nValue;
dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain();
scriptChange = pcoin.first->vout[pcoin.second].scriptPubKey;
}
// Decent:
CWalletTx prevTx;
if (!prevTx.SetByHash(prevPoint.hash))
return error("Create tx : read prevPoint failed");
setCoins.push_front(make_pair(&prevTx, prevPoint.n));
int64 nChange = nValueIn - nValue - nFee;
// if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE
// or until nChange becomes zero
// NOTE: this depends on the exact behaviour of GetMinFee
if (nFee < MIN_TX_FEE && nChange > 0 && nChange < CENT)
{
int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFee);
nChange -= nMoveToFee;
nFee += nMoveToFee;
}
// peercoin: sub-cent change is moved to fee
if (nChange > 0 && nChange < MIN_TXOUT_AMOUNT)
{
nFee += nChange;
nChange = 0;
}
if (nChange > 0)
{
if (!GetBoolArg("-avatar", true)) // peercoin: not avatar mode; peershares: avatar mode enabled by default to avoid change being sent to hidden address
{
// Reserve a new key pair from key pool
vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
// assert(mapKeys.count(vchPubKey));
// Fill a vout to ourself
// TODO: pass in scriptChange instead of reservekey so
// change transaction isn't always pay-to-bitcoin-address
scriptChange.SetBitcoinAddress(vchPubKey);
}
else
reservekey.ReturnKey(); // return key in avatar mode
// Insert change txout, must be before special_decent outs
wtxNew.vout.insert(wtxNew.vout.begin(), 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(wtxNew.vin[nIn].prevout == prevPoint)
{
wtxNew.vin[nIn].scriptSig << encryptedKey << proof;
if (!SignSignature(*this, *coin.first, wtxNew, nIn++, SIGHASH_WITHPROOF))
return false;
}
else
{
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/4)
return false;
dPriority /= nBytes;
// Check that enough fee is included
int64 nPayFee = MIN_TX_FEE * (1 + (int64)nBytes / 1000);
int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND);
if (nFee < max(nPayFee, nMinFee))
{
nFee = max(nPayFee, nMinFee);
continue;
}
if (!wtxNew.CheckDeliverKeys())
return error("Create tx: invalid type");
// Fill vtxPrev by copying from previous transactions vtxPrev // Fill vtxPrev by copying from previous transactions vtxPrev
wtxNew.AddSupportingTransactions(txdb); wtxNew.AddSupportingTransactions(txdb);
wtxNew.fTimeReceivedIsTxTime = true; wtxNew.fTimeReceivedIsTxTime = true;
break; break;
} }
} }
} }
return true; return true;
}}
bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet)
{{
vector< pair<CScript, int64> > vecSend; vector< pair<CScript, int64> > vecSend;
vecSend.push_back(make_pair(scriptPubKey, nValue)); vecSend.push_back(make_pair(scriptPubKey, nValue));
return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet);
}}