Diff
checker
Testo
Testo
Immagini
Documenti
Excel
Cartelle
Legal
Enterprise
Applicazione per desktop
Prezzi
Accedi
Scarica Diffchecker Desktop
Confronta il testo
Trova la differenza tra due file di testo
Strumenti
Cronologia
Visualizzazione
Layout
Diviso
Unificato
Editor live
Nascondi spazi bianchi
Comprimi invariate
Senza a capo
Livello di dettaglio
Intelligente
Sintassi
Nessuno
Modifica aspetto
Elaborazione
Ignora
Trasforma testo
Vai alla prima modifica
Modifica input
Diffchecker Desktop
Il modo più sicuro per usare Diffchecker. Ottieni l'app Diffchecker Desktop: i tuoi diff non lasciano mai il tuo computer!
Ottieni Desktop
Caricamento del resto di questo confronto.
Untitled diff
Creato
10 anni fa
Il diff non scade mai
Eliminare
Esporta
Condividere
Spiegare
9 rimozioni
Linee
Totale
Rimosso
Caratteri
Totale
Rimosso
Per continuare a utilizzare questa funzione, aggiorna a
Diff
checker
Pro
Visualizza prezzi
747 linee
Copia tutti
11 aggiunte
Linee
Totale
Aggiunto
Caratteri
Totale
Aggiunto
Per continuare a utilizzare questa funzione, aggiorna a
Diff
checker
Pro
Visualizza prezzi
745 linee
Copia tutti
// 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 Peercoin developers
// Copyright (c) 2011-2013 The Peercoin developers
Copia
Copiato
Copia
Copiato
// 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 "bitcoinrpc.h"
#include "bitcoinrpc.h"
Copia
Copiato
Copia
Copiato
#include "encryptionutils.h"
using namespace std;
using namespace std;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
//
// mapWallet
// mapWallet
//
//
std::vector<unsigned char> 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 vector<unsigned char> &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);
}
}
// peercoin: 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;
}
}
}
}
Diff salvati
Testo originale
Apri file
// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Copyright (c) 2011-2013 The Peercoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "wallet.h" #include "walletdb.h" #include "crypter.h" #include "ui_interface.h" #include "kernel.h" #include "bitcoinrpc.h" using namespace std; ////////////////////////////////////////////////////////////////////////////// // // mapWallet // std::vector<unsigned char> 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); if (!AddKey(key)) throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); return key.GetPubKey(); } bool CWallet::AddKey(const CKey& key) { if (!CCryptoKeyStore::AddKey(key)) return false; if (!fFileBacked) return true; if (!IsCrypted()) return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey()); return true; } bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector<unsigned char> &vchCryptedSecret) { if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; if (!fFileBacked) return true; { LOCK(cs_wallet); if (pwalletdbEncryption) return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret); else return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret); } return false; } bool CWallet::AddCScript(const CScript& redeemScript) { if (!CCryptoKeyStore::AddCScript(redeemScript)) return false; if (!fFileBacked) return true; return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); } // peercoin: 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 nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); nStartTime = GetTimeMillis(); 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; if (pMasterKey.second.nDeriveIterations < 25000) pMasterKey.second.nDeriveIterations = 25000; 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)) 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) { // Versions prior to 0.4.0 did not support the "minversion" record. // Use a CCorruptAddress to make them crash instead. CCorruptAddress corruptAddress; pwalletdb->WriteSetting("addrIncoming", corruptAddress); } 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 nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; if (kMasterKey.nDeriveIterations < 25000) kMasterKey.nDeriveIterations = 25000; printf("Encrypting portfolio 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); } return true; } void CWallet::WalletUpdateSpent(const CTransaction &tx) { // 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<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { CWalletTx& wtx = (*mi).second; if (!wtx.IsSpent(txin.prevout.n) && IsMine(wtx.vout[txin.prevout.n])) { printf("WalletUpdateSpent found spent unit %sshares %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkSpent(txin.prevout.n); wtx.WriteToDisk(); vWalletUpdated.push_back(txin.prevout.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<map<uint256, CWalletTx>::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(); 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.SetBitcoinAddress(vchDefaultKey); BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (txout.scriptPubKey == scriptDefaultKey) { std::vector<unsigned char> newDefaultKey; if (GetKeyFromPool(newDefaultKey, false)) { SetDefaultKey(newDefaultKey); SetAddressBookName(CBitcoinAddress(vchDefaultKey), ""); } } } #endif // Notify UI vWalletUpdated.push_back(hash); // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins WalletUpdateSpent(wtx); } // Refresh UI MainFrameRepaint(); 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, bool fFindBlock) { uint256 hash = tx.GetHash(); { LOCK(cs_wallet); bool fExisted = mapWallet.count(hash); 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; } bool CWallet::IsMine(const CTxIn &txin) const { { LOCK(cs_wallet); map<uint256, CWalletTx>::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])) return true; } } return false; } int64 CWallet::GetDebit(const CTxIn &txin) const { { LOCK(cs_wallet); map<uint256, CWalletTx>::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])) return prev.vout[txin.prevout.n].nValue; } } return 0; } bool CWallet::IsChange(const CTxOut& txout) const { CBitcoinAddress address; // 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 // 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 (ExtractAddress(txout.scriptPubKey, address) && HaveKey(address)) { LOCK(cs_wallet); if (!mapAddressBook.count(address)) return true; } return false; } int64 CWalletTx::GetTxTime() const { return nTimeReceived; } 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<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); if (mi != pwallet->mapRequestCount.end()) nRequests = (*mi).second; } } else { // Did anyone request this transaction? map<uint256, int>::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<uint256, int>::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; } void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<CBitcoinAddress, int64> >& listReceived, list<pair<CBitcoinAddress, int64> >& listSent, int64& nFee, string& strSentAccount) const { nGeneratedImmature = nGeneratedMature = nFee = 0; listReceived.clear(); listSent.clear(); strSentAccount = strFromAccount; if (IsCoinBase() || IsCoinStake()) { if (GetBlocksToMaturity() > 0) nGeneratedImmature = pwallet->GetCredit(*this); else nGeneratedMature = GetCredit(); return; } // Compute fee: int64 nDebit = GetDebit(); if (nDebit > 0) // debit>0 means we signed/sent this transaction { int64 nValueOut = GetValueOut(); nFee = nDebit - nValueOut; } // Sent/received. BOOST_FOREACH(const CTxOut& txout, vout) { CBitcoinAddress address; vector<unsigned char> vchPubKey; if (!ExtractAddress(txout.scriptPubKey, address)) { printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", this->GetHash().ToString().c_str()); address = " unknown "; } // Don't report 'change' txouts if (nDebit > 0 && pwallet->IsChange(txout)) continue; if (nDebit > 0) listSent.push_back(make_pair(address, txout.nValue)); if (pwallet->IsMine(txout)) listReceived.push_back(make_pair(address, txout.nValue)); } } void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, int64& nSent, int64& nFee) const { nGenerated = nReceived = nSent = nFee = 0; int64 allGeneratedImmature, allGeneratedMature, allFee; allGeneratedImmature = allGeneratedMature = allFee = 0; string strSentAccount; list<pair<CBitcoinAddress, int64> > listReceived; list<pair<CBitcoinAddress, int64> > listSent; GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); if (strAccount == "") nGenerated = allGeneratedMature; if (strAccount == strSentAccount) { BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& s, listSent) nSent += s.second; nFee = allFee; } { LOCK(pwallet->cs_wallet); BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived) { if (pwallet->mapAddressBook.count(r.first)) { map<CBitcoinAddress, string>::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<uint256> 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<uint256, const CMerkleTx*> mapWalletPrev; set<uint256> 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<uint256, CWalletTx>::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, true)) return 1; return 0; } void CWallet::ReacceptWalletTransactions() { CTxDB txdb("r"); bool fRepeat = true; while (fRepeat) { LOCK(cs_wallet); fRepeat = false; vector<CDiskTxPos> 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() %d != wtx.vout.size() %d\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 %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkDirty(); wtx.WriteToDisk(); } } else { // Reaccept 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 Reaccept. } } } void CWalletTx::RelayWalletTransaction(CTxDB& txdb) { BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) { if (!(tx.IsCoinBase() || tx.IsCoinStake())) { uint256 hash = tx.GetHash(); if (!txdb.ContainsTx(hash)) RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); } } if (!(IsCoinBase() || IsCoinStake())) { uint256 hash = GetHash(); if (!txdb.ContainsTx(hash)) { printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); } } } void CWalletTx::RelayWalletTransaction() { CTxDB txdb("r"); RelayWalletTransaction(txdb); } void CWallet::ResendWalletTransactions() { // Do this infrequently and randomly to avoid giving away // that these are our transactions. static int64 nNextTime; if (GetTime() < nNextTime) return; bool fFirst = (nNextTime == 0); nNextTime = GetTime() + GetRand(30 * 60); if (fFirst) return; // Only do it if there's been a new block since last time static int64 nLastTime; if (nTimeBestReceived < nLastTime) return; nLastTime = GetTime(); // Rebroadcast any of our txes that aren't in a block yet printf("ResendWalletTransactions()\n"); CTxDB txdb("r"); { LOCK(cs_wallet); // Sort them in chronological order multimap<unsigned int, CWalletTx*> mapSorted; BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) { CWalletTx& wtx = item.second; // Don't rebroadcast until it's had plenty of time that // it should have gotten in already by now. if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); } BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) { CWalletTx& wtx = *item.second; if (wtx.CheckTransaction()) wtx.RelayWalletTransaction(txdb); else printf("ResendWalletTransactions() : CheckTransaction failed for transaction %s\n", wtx.GetHash().ToString().c_str()); } } } ////////////////////////////////////////////////////////////////////////////// // // Actions // int64 CWallet::GetBalance() const { int64 nTotal = 0; { LOCK(cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) continue; nTotal += pcoin->GetAvailableCredit(); } } return nTotal; } int64 CWallet::GetUnconfirmedBalance() const { int64 nTotal = 0; { LOCK(cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsFinal() && pcoin->IsConfirmed()) continue; nTotal += pcoin->GetAvailableCredit(); } } return nTotal; } // peercoin: total coins staked (non-spendable until maturity) int64 CWallet::GetStake() const { int64 nTotal = 0; LOCK(cs_wallet); for (map<uint256, CWalletTx>::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); } return nTotal; } int64 CWallet::GetNewMint() const { int64 nTotal = 0; LOCK(cs_wallet); for (map<uint256, CWalletTx>::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); } return nTotal; } bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const { setCoinsRet.clear(); nValueRet = 0; // List of values less than target pair<int64, pair<const CWalletTx*,unsigned int> > coinLowestLarger; coinLowestLarger.first = std::numeric_limits<int64>::max(); coinLowestLarger.second.first = NULL; vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue; int64 nTotalLower = 0; { 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); BOOST_FOREACH(const CWalletTx* pcoin, vCoins) { 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; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { 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)); 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 || nTotalLower == nTargetValue + CENT) { for (unsigned int i = 0; i < vValue.size(); ++i) { setCoinsRet.insert(vValue[i].second); nValueRet += vValue[i].first; } return true; } if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0)) { if (coinLowestLarger.second.first == NULL) return false; setCoinsRet.insert(coinLowestLarger.second); nValueRet += coinLowestLarger.first; return true; } if (nTotalLower >= nTargetValue + CENT) nTargetValue += CENT; // Solve subset sum by stochastic approximation sort(vValue.rbegin(), vValue.rend()); vector<char> vfIncluded; vector<char> vfBest(vValue.size(), true); int64 nBest = nTotalLower; for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) { vfIncluded.assign(vValue.size(), false); int64 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; } } } } } // If the next larger is still closer, return it if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue) { 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; } //// debug print if (fDebug && GetBoolArg("-printselectcoin")) { 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 nTargetValue, unsigned int nSpendTime, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const { return (SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 6, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 1, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, setCoinsRet, nValueRet)); } bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) { 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 = nTransactionFee; 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 set<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) { // 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()+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/4) return false; dPriority /= nBytes; // Check that enough fee is included int64 nPayFee = nTransactionFee * (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; } bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) { vector< pair<CScript, int64> > vecSend; vecSend.push_back(make_pair(scriptPubKey, nValue)); return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); } // peercoin: create coin stake transaction bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64 nSearchInterval, CTransaction& txNew) { // The following split & combine thresholds are important to security // Should not be adjusted if you don't understand the consequences static unsigned int nStakeSplitAge = (60 * 60 * 24 * 90); int64 nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits) / 3; CBigNum bnTargetPerCoinDay; bnTargetPerCoinDay.SetCompact(nBits); LOCK2(cs_main, cs_wallet); txNew.vin.clear(); txNew.vout.clear(); // Mark coin stake transaction CScript scriptEmpty; scriptEmpty.clear(); txNew.vout.push_back(CTxOut(0, scriptEmpty)); // Choose coins to use int64 nBalance = GetBalance(); int64 nReserveBalance = 0; if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) return error("CreateCoinStake : invalid reserve balance amount"); if (nBalance <= nReserveBalance) return false; set<pair<const CWalletTx*,unsigned int> > setCoins; vector<const CWalletTx*> vwtxPrev; int64 nValueIn = 0; if (!SelectCoins(nBalance - nReserveBalance, txNew.nTime, setCoins, nValueIn)) return false; if (setCoins.empty()) return false; int64 nCredit = 0; CScript scriptPubKeyKernel; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { CTxDB txdb("r"); CTxIndex txindex; if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) continue; // Read block header CBlock block; if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) continue; static int nMaxStakeSearchInterval = 60; if (block.GetBlockTime() + nStakeMinAge > txNew.nTime - nMaxStakeSearchInterval) continue; // only count coins meeting min age requirement bool fKernelFound = false; for (unsigned int n=0; n<min(nSearchInterval,(int64)nMaxStakeSearchInterval) && !fKernelFound && !fShutdown; n++) { // Search backward in time from the given txNew timestamp // Search nSearchInterval seconds back up to nMaxStakeSearchInterval uint256 hashProofOfStake = 0; COutPoint prevoutStake = COutPoint(pcoin.first->GetHash(), pcoin.second); if (CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, *pcoin.first, prevoutStake, txNew.nTime - n, hashProofOfStake)) { // Found a kernel if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : kernel found\n"); vector<valtype> vSolutions; txnouttype whichType; CScript scriptPubKeyOut; scriptPubKeyKernel = pcoin.first->vout[pcoin.second].scriptPubKey; if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) { if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : failed to parse kernel\n", whichType); break; } if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : parsed kernel type=%d\n", whichType); if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH) { if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : no support for kernel type=%d\n", whichType); break; // only support pay to public key and pay to address } if (whichType == TX_PUBKEYHASH) // pay to address type { // convert to pay to public key type CKey key; if (!keystore.GetKey(uint160(vSolutions[0]), key)) { if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); break; // unable to find corresponding public key } scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG; } else scriptPubKeyOut = scriptPubKeyKernel; txNew.nTime -= n; txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); nCredit += pcoin.first->vout[pcoin.second].nValue; vwtxPrev.push_back(pcoin.first); txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); if (block.GetBlockTime() + nStakeSplitAge > txNew.nTime) txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); //split stake if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : added kernel type=%d\n", whichType); fKernelFound = true; break; } } if (fKernelFound || fShutdown) break; // if kernel is found stop searching } if (nCredit == 0 || nCredit > nBalance - nReserveBalance) return false; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { // Attempt to add more inputs // Only add coins of the same key/address as kernel if (txNew.vout.size() == 2 && ((pcoin.first->vout[pcoin.second].scriptPubKey == scriptPubKeyKernel || pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey)) && pcoin.first->GetHash() != txNew.vin[0].prevout.hash) { // 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; // Do not add additional significant input if (pcoin.first->vout[pcoin.second].nValue > nCombineThreshold) continue; // Do not add input that is still too young if (pcoin.first->nTime + STAKE_MAX_AGE > txNew.nTime) continue; txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); nCredit += pcoin.first->vout[pcoin.second].nValue; vwtxPrev.push_back(pcoin.first); } } // Calculate coin age reward { uint64 nCoinAge; CTxDB txdb("r"); if (!txNew.GetCoinAge(txdb, nCoinAge)) return error("CreateCoinStake : failed to calculate coin age"); nCredit += GetProofOfStakeReward(nCoinAge); } int64 nMinFee = 0; loop { // Set output amount if (txNew.vout.size() == 3) { txNew.vout[1].nValue = ((nCredit - nMinFee) / 2 / CENT) * CENT; txNew.vout[2].nValue = nCredit - nMinFee - txNew.vout[1].nValue; } else txNew.vout[1].nValue = nCredit - nMinFee; // Sign int nIn = 0; BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev) { if (!SignSignature(*this, *pcoin, txNew, nIn++)) return error("CreateCoinStake : failed to sign coinstake"); } // 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"); // Check enough fee is paid if (nMinFee < txNew.GetMinFee() - MIN_TX_FEE) { nMinFee = txNew.GetMinFee() - MIN_TX_FEE; continue; // try signing again } else { if (fDebug && GetBoolArg("-printfee")) printf("CreateCoinStake : fee for coinstake %s\n", FormatMoney(nMinFee).c_str()); break; } } // Successfully generated coinstake return true; } // Call after CreateTransaction unless you want to abort bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) { { LOCK2(cs_main, cs_wallet); printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); { // 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 set<CWalletTx*> setCoins; BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) { CWalletTx &coin = mapWallet[txin.prevout.hash]; coin.BindWallet(this); coin.MarkSpent(txin.prevout.n); coin.WriteToDisk(); vWalletUpdated.push_back(coin.GetHash()); } if (fFileBacked) delete pwalletdb; } // Track how many getdata requests our transaction gets mapRequestCount[wtxNew.GetHash()] = 0; // Broadcast if (!wtxNew.AcceptToMemoryPool()) { // This must not fail. The transaction has already been signed and recorded. printf("CommitTransaction() : Error: Transaction not valid"); return false; } wtxNew.RelayWalletTransaction(); } MainFrameRepaint(); return true; } string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) { CReserveKey reservekey(this); int64 nFeeRequired; if (IsLocked()) { string strError = _("Error: Portfolio locked, unable to create transaction "); printf("SendMoney() : %s", strError.c_str()); return strError; } if (fWalletUnlockMintOnly) { string strError = _("Error: Portfolio 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 && !ThreadSafeAskFee(nFeeRequired, _("Sending..."))) return "ABORTED"; if (!CommitTransaction(wtxNew, reservekey)) return _("Error: The transaction was rejected. This might happen if some of the shares in your portfolio were already spent, such as if you used a copy of wallet.dat and shares were spent in the copy but not marked as spent here."); MainFrameRepaint(); return ""; } string CWallet::SendMoneyToBitcoinAddress(const CBitcoinAddress& address, int64 nValue, CWalletTx& wtxNew, bool fAskFee) { // Check amount if (nValue <= 0) return _("Invalid amount"); if (nValue + nTransactionFee > GetBalance()) return _("Insufficient funds"); // Parse bitcoin address CScript scriptPubKey; scriptPubKey.SetBitcoinAddress(address); return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); } int CWallet::LoadWallet(bool& fFirstRunRet) { if (!fFileBacked) return false; fFirstRunRet = false; int 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. } nLoadWalletRet = DB_NEED_REWRITE; } if (nLoadWalletRet != DB_LOAD_OK) return nLoadWalletRet; fFirstRunRet = vchDefaultKey.empty(); CreateThread(ThreadFlushWalletDB, &strWalletFile); return DB_LOAD_OK; } bool CWallet::SetAddressBookName(const CBitcoinAddress& address, const string& strName) { mapAddressBook[address] = strName; AddressBookRepaint(); if (!fFileBacked) return false; return CWalletDB(strWalletFile).WriteName(address.ToString(), strName); } bool CWallet::DelAddressBookName(const CBitcoinAddress& address) { mapAddressBook.erase(address); AddressBookRepaint(); if (!fFileBacked) return false; return CWalletDB(strWalletFile).EraseName(address.ToString()); } void CWallet::PrintWallet(const CBlock& block) { { LOCK(cs_wallet); if (block.IsProofOfWork() && mapWallet.count(block.vtx[0].GetHash())) { CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; printf(" mine: %d %d %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str()); } if (block.IsProofOfStake() && mapWallet.count(block.vtx[1].GetHash())) { CWalletTx& wtx = mapWallet[block.vtx[1].GetHash()]; printf(" stake: %d %d %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str()); } } printf("\n"); } bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) { { LOCK(cs_wallet); map<uint256, CWalletTx>::iterator mi = mapWallet.find(hashTx); if (mi != mapWallet.end()) { wtx = (*mi).second; return true; } } return false; } bool CWallet::SetDefaultKey(const std::vector<unsigned char> &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() { { LOCK(cs_wallet); CWalletDB walletdb(strWalletFile); BOOST_FOREACH(int64 nIndex, setKeyPool) walletdb.ErasePool(nIndex); setKeyPool.clear(); if (IsLocked()) return false; int64 nKeys = max(GetArg("-keypool", 100), (int64)0); for (int i = 0; i < nKeys; i++) { int64 nIndex = i+1; walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); setKeyPool.insert(nIndex); } printf("CWallet::NewKeyPool wrote %"PRI64d" new keys\n", nKeys); } return true; } bool CWallet::TopUpKeyPool() { { LOCK(cs_wallet); if (IsLocked()) return false; CWalletDB walletdb(strWalletFile); // Top up key pool unsigned int nTargetSize = max(GetArg("-keypool", 100), 0LL); while (setKeyPool.size() < (nTargetSize + 1)) { int64 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 %"PRI64d", size=%d\n", nEnd, setKeyPool.size()); } } return true; } void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) { nIndex = -1; keypool.vchPubKey.clear(); { 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(Hash160(keypool.vchPubKey))) throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); assert(!keypool.vchPubKey.empty()); if (fDebug && GetBoolArg("-printkeypool")) printf("keypool reserve %"PRI64d"\n", nIndex); } } int64 CWallet::AddReserveKey(const CKeyPool& keypool) { { LOCK2(cs_main, cs_wallet); CWalletDB walletdb(strWalletFile); int64 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 nIndex) { // Remove from key pool if (fFileBacked) { CWalletDB walletdb(strWalletFile); walletdb.ErasePool(nIndex); } printf("keypool keep %"PRI64d"\n", nIndex); } void CWallet::ReturnKey(int64 nIndex) { // Return to key pool { LOCK(cs_wallet); setKeyPool.insert(nIndex); } if (fDebug && GetBoolArg("-printkeypool")) printf("keypool return %"PRI64d"\n", nIndex); } bool CWallet::GetKeyFromPool(vector<unsigned char>& result, bool fAllowReuse) { int64 nIndex = 0; CKeyPool keypool; { LOCK(cs_wallet); ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) { if (fAllowReuse && !vchDefaultKey.empty()) { result = vchDefaultKey; return true; } if (IsLocked()) return false; result = GenerateNewKey(); return true; } KeepKey(nIndex); result = keypool.vchPubKey; } return true; } int64 CWallet::GetOldestKeyPoolTime() { int64 nIndex = 0; CKeyPool keypool; ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) return GetTime(); ReturnKey(nIndex); return keypool.nTime; } // peercoin: check 'spent' consistency between wallet and txindex // peercoin: fix wallet spent state according to txindex void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion, bool fCheckOnly) { nMismatchFound = 0; nBalanceInQuestion = 0; LOCK(cs_wallet); vector<CWalletTx*> vCoins; vCoins.reserve(mapWallet.size()); for (map<uint256, CWalletTx>::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 (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 unit %sshares %s[%d], %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 unit %sshares %s[%d], %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(); } } } } } // peercoin: 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<uint256, CWalletTx>::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(); } } } } vector<unsigned char> 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.empty()); return vchPubKey; } void CReserveKey::KeepKey() { if (nIndex != -1) pwallet->KeepKey(nIndex); nIndex = -1; vchPubKey.clear(); } void CReserveKey::ReturnKey() { if (nIndex != -1) pwallet->ReturnKey(nIndex); nIndex = -1; vchPubKey.clear(); } void CWallet::GetAllReserveAddresses(set<CBitcoinAddress>& setAddress) { setAddress.clear(); CWalletDB walletdb(strWalletFile); LOCK2(cs_main, cs_wallet); BOOST_FOREACH(const int64& id, setKeyPool) { CKeyPool keypool; if (!walletdb.ReadPool(id, keypool)) throw runtime_error("GetAllReserveKeyHashes() : read failed"); CBitcoinAddress address(keypool.vchPubKey); assert(!keypool.vchPubKey.empty()); if (!HaveKey(address)) throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool"); setAddress.insert(address); } } void CWallet::ExportPeercoinKeys(int &nExportedCount, int &nErrorCount) { nExportedCount = 0; nErrorCount = 0; if (IsLocked()) throw runtime_error("The portfolio is locked. Please unlock it first."); if (fWalletUnlockMintOnly) throw runtime_error("Portfolio is unlocked for minting only."); LOCK(cs_wallet); BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, mapAddressBook) { const CBitcoinAddress& address = item.first; CSecret vchSecret; bool fCompressed; if (address.IsScript()) { const uint160 hash = address.GetHash160(); CScript script; if (!GetCScript(hash, script)) { printf("Failed get script of address %s\n", address.ToString().c_str()); nErrorCount++; continue; } txnouttype type; std::vector<CBitcoinAddress> vAddresses; int nRequired; if (!ExtractAddresses(script, type, vAddresses, nRequired)) { printf("Failed extract addresses from address %s\n", address.ToString().c_str()); nErrorCount++; continue; } if (type != TX_MULTISIG) { printf("Address %s is not a multisig address\n", address.ToString().c_str()); nErrorCount++; continue; } json_spirit::Array vPeercoinAddressStrings; BOOST_FOREACH(const CBitcoinAddress &address, vAddresses) vPeercoinAddressStrings.push_back(CPeercoinAddress(address).ToString()); json_spirit::Array params; params.push_back(json_spirit::Value(nRequired)); params.push_back(vPeercoinAddressStrings); params.push_back("Peershares"); try { string result = CallPeercoinRPC("addmultisigaddress", params); printf("Exported multisig address %s: %s\n", address.ToString().c_str(), result.c_str()); nExportedCount++; } catch (peercoin_rpc_error &error) { printf("Failed to add multisig address of address %s: %s\n", address.ToString().c_str(), error.what()); nErrorCount++; } } else { if (!GetSecret(address, vchSecret, fCompressed)) { printf("Private key for address %s is not known\n", address.ToString().c_str()); nErrorCount++; continue; } json_spirit::Array params; params.push_back(CPeercoinSecret(vchSecret, fCompressed).ToString()); params.push_back("Peershares"); try { string result = CallPeercoinRPC("importprivkey", params); printf("Exported private key of address %s: %s\n", address.ToString().c_str(), result.c_str()); nExportedCount++; } catch (peercoin_rpc_error &error) { printf("Failed to export private key of address %s: %s\n", address.ToString().c_str(), error.what()); nErrorCount++; } } } }
Testo modificato
Apri file
// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin 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 // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "wallet.h" #include "walletdb.h" #include "crypter.h" #include "ui_interface.h" #include "kernel.h" #include "bitcoinrpc.h" #include "encryptionutils.h" using namespace std; ////////////////////////////////////////////////////////////////////////////// // // mapWallet // std::vector<unsigned char> 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); if (!AddKey(key)) throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed"); return key.GetPubKey(); } bool CWallet::AddKey(const CKey& key) { if (!CCryptoKeyStore::AddKey(key)) return false; if (!fFileBacked) return true; if (!IsCrypted()) return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey()); return true; } bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector<unsigned char> &vchCryptedSecret) { if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; if (!fFileBacked) return true; { LOCK(cs_wallet); if (pwalletdbEncryption) return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret); else return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret); } return false; } bool CWallet::AddCScript(const CScript& redeemScript) { if (!CCryptoKeyStore::AddCScript(redeemScript)) return false; if (!fFileBacked) return true; return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); } // peercoin: 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 nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); nStartTime = GetTimeMillis(); 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; if (pMasterKey.second.nDeriveIterations < 25000) pMasterKey.second.nDeriveIterations = 25000; 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)) 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) { // Versions prior to 0.4.0 did not support the "minversion" record. // Use a CCorruptAddress to make them crash instead. CCorruptAddress corruptAddress; pwalletdb->WriteSetting("addrIncoming", corruptAddress); } 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 nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; if (kMasterKey.nDeriveIterations < 25000) kMasterKey.nDeriveIterations = 25000; printf("Encrypting portfolio 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); } return true; } void CWallet::WalletUpdateSpent(const CTransaction &tx) { // 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<uint256, CWalletTx>::iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { 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])) { printf("WalletUpdateSpent found spent unit %sDCT %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkSpent(txin.prevout.n); wtx.WriteToDisk(); vWalletUpdated.push_back(txin.prevout.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<map<uint256, CWalletTx>::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(); 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.SetBitcoinAddress(vchDefaultKey); BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (txout.scriptPubKey == scriptDefaultKey) { std::vector<unsigned char> newDefaultKey; if (GetKeyFromPool(newDefaultKey, false)) { SetDefaultKey(newDefaultKey); SetAddressBookName(CBitcoinAddress(vchDefaultKey), ""); } } } #endif // Notify UI vWalletUpdated.push_back(hash); // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins WalletUpdateSpent(wtx); } // Refresh UI MainFrameRepaint(); 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, bool fFindBlock) { uint256 hash = tx.GetHash(); { LOCK(cs_wallet); bool fExisted = mapWallet.count(hash); 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; } bool CWallet::IsMine(const CTxIn &txin) const { { LOCK(cs_wallet); map<uint256, CWalletTx>::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])) return true; } } return false; } int64 CWallet::GetDebit(const CTxIn &txin) const { { LOCK(cs_wallet); map<uint256, CWalletTx>::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])) return prev.vout[txin.prevout.n].nValue; } } return 0; } bool CWallet::IsChange(const CTxOut& txout) const { CBitcoinAddress address; // 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 // 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 (ExtractAddress(txout.scriptPubKey, address) && HaveKey(address)) { LOCK(cs_wallet); if (!mapAddressBook.count(address)) return true; } return false; } int64 CWalletTx::GetTxTime() const { return nTimeReceived; } 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<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); if (mi != pwallet->mapRequestCount.end()) nRequests = (*mi).second; } } else { // Did anyone request this transaction? map<uint256, int>::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<uint256, int>::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; } void CWalletTx::GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<CBitcoinAddress, int64> >& listReceived, list<pair<CBitcoinAddress, int64> >& listSent, int64& nFee, string& strSentAccount) const { nGeneratedImmature = nGeneratedMature = nFee = 0; listReceived.clear(); listSent.clear(); strSentAccount = strFromAccount; if (IsCoinBase() || IsCoinStake()) { if (GetBlocksToMaturity() > 0) nGeneratedImmature = pwallet->GetCredit(*this) - pwallet->GetDebit(*this); else nGeneratedMature = GetCredit() - GetDebit(); return; } // Compute fee: int64 nDebit = GetDebit(); if (nDebit > 0) // debit>0 means we signed/sent this transaction { int64 nValueOut = GetValueOut(); nFee = nDebit - nValueOut; } // Sent/received. BOOST_FOREACH(const CTxOut& txout, vout) { CBitcoinAddress address; vector<unsigned char> vchPubKey; if (!ExtractAddress(txout.scriptPubKey, address)) { printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", this->GetHash().ToString().c_str()); address = " unknown "; } // Don't report 'change' txouts if (nDebit > 0 && pwallet->IsChange(txout)) continue; if (nDebit > 0) listSent.push_back(make_pair(address, txout.nValue)); if (pwallet->IsMine(txout)) listReceived.push_back(make_pair(address, txout.nValue)); } } void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived, int64& nSent, int64& nFee) const { nGenerated = nReceived = nSent = nFee = 0; int64 allGeneratedImmature, allGeneratedMature, allFee; allGeneratedImmature = allGeneratedMature = allFee = 0; string strSentAccount; list<pair<CBitcoinAddress, int64> > listReceived; list<pair<CBitcoinAddress, int64> > listSent; GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); if (strAccount == "") nGenerated = allGeneratedMature; if (strAccount == strSentAccount) { BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& s, listSent) nSent += s.second; nFee = allFee; } { LOCK(pwallet->cs_wallet); BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress,int64)& r, listReceived) { if (pwallet->mapAddressBook.count(r.first)) { map<CBitcoinAddress, string>::const_iterator mi = pwallet->mapAddressBook.find(r.first); if (mi != pwallet->mapAddressBook.end() && ((*mi).second == strAccount || (*mi).first.ToString() == 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<uint256> 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<uint256, const CMerkleTx*> mapWalletPrev; set<uint256> 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<uint256, CWalletTx>::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, true)) return 1; return 0; } void CWallet::ReacceptWalletTransactions() { CTxDB txdb("r"); bool fRepeat = true; while (fRepeat) { LOCK(cs_wallet); fRepeat = false; vector<CDiskTxPos> 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() %d != wtx.vout.size() %d\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 %sppc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); wtx.MarkDirty(); wtx.WriteToDisk(); } } else { // Reaccept 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 Reaccept. } } } void CWalletTx::RelayWalletTransaction(CTxDB& txdb) { BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) { if (!(tx.IsCoinBase() || tx.IsCoinStake())) { uint256 hash = tx.GetHash(); if (!txdb.ContainsTx(hash)) RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); } } if (!(IsCoinBase() || IsCoinStake())) { uint256 hash = GetHash(); if (!txdb.ContainsTx(hash)) { printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); } } } void CWalletTx::RelayWalletTransaction() { CTxDB txdb("r"); RelayWalletTransaction(txdb); } void CWallet::ResendWalletTransactions() { // Do this infrequently and randomly to avoid giving away // that these are our transactions. static int64 nNextTime; if (GetTime() < nNextTime) return; bool fFirst = (nNextTime == 0); nNextTime = GetTime() + GetRand(30 * 60); if (fFirst) return; // Only do it if there's been a new block since last time static int64 nLastTime; if (nTimeBestReceived < nLastTime) return; nLastTime = GetTime(); // Rebroadcast any of our txes that aren't in a block yet printf("ResendWalletTransactions()\n"); CTxDB txdb("r"); { LOCK(cs_wallet); // Sort them in chronological order multimap<unsigned int, CWalletTx*> mapSorted; BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) { CWalletTx& wtx = item.second; // Don't rebroadcast until it's had plenty of time that // it should have gotten in already by now. if (nTimeBestReceived - (int64)wtx.nTimeReceived > 5 * 60) mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); } BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) { CWalletTx& wtx = *item.second; if (wtx.CheckTransaction()) wtx.RelayWalletTransaction(txdb); else printf("ResendWalletTransactions() : CheckTransaction failed for transaction %s\n", wtx.GetHash().ToString().c_str()); } } } ////////////////////////////////////////////////////////////////////////////// // // Actions // int64 CWallet::GetBalance() const { int64 nTotal = 0; { LOCK(cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) continue; nTotal += pcoin->GetAvailableCredit(); } } return nTotal; } int64 CWallet::GetUnconfirmedBalance() const { int64 nTotal = 0; { LOCK(cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsFinal() && pcoin->IsConfirmed()) continue; nTotal += pcoin->GetAvailableCredit(); } } return nTotal; } // peercoin: total coins staked (non-spendable until maturity) int64 CWallet::GetStake() const { int64 nTotal = 0; LOCK(cs_wallet); for (map<uint256, CWalletTx>::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); } return nTotal; } int64 CWallet::GetNewMint() const { int64 nTotal = 0; LOCK(cs_wallet); for (map<uint256, CWalletTx>::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); } return nTotal; } bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, list<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const { setCoinsRet.clear(); nValueRet = 0; // List of values less than target pair<int64, pair<const CWalletTx*,unsigned int> > coinLowestLarger; coinLowestLarger.first = std::numeric_limits<int64>::max(); coinLowestLarger.second.first = NULL; vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue; int64 nTotalLower = 0; { 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); BOOST_FOREACH(const CWalletTx* pcoin, vCoins) { 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; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { 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)); if (n == nTargetValue) { setCoinsRet.push_back(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 || nTotalLower == nTargetValue + CENT) { for (unsigned int i = 0; i < vValue.size(); ++i) { setCoinsRet.push_back(vValue[i].second); nValueRet += vValue[i].first; } return true; } if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0)) { if (coinLowestLarger.second.first == NULL) return false; setCoinsRet.push_back(coinLowestLarger.second); nValueRet += coinLowestLarger.first; return true; } if (nTotalLower >= nTargetValue + CENT) nTargetValue += CENT; // Solve subset sum by stochastic approximation sort(vValue.rbegin(), vValue.rend()); vector<char> vfIncluded; vector<char> vfBest(vValue.size(), true); int64 nBest = nTotalLower; for (int nRep = 0; nRep < 1000 && nBest != nTargetValue; nRep++) { vfIncluded.assign(vValue.size(), false); int64 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; } } } } } // If the next larger is still closer, return it if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue) { setCoinsRet.push_back(coinLowestLarger.second); nValueRet += coinLowestLarger.first; } else { for (unsigned int i = 0; i < vValue.size(); i++) if (vfBest[i]) { setCoinsRet.push_back(vValue[i].second); nValueRet += vValue[i].first; } //// debug print if (fDebug && GetBoolArg("-printselectcoin")) { 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 nTargetValue, unsigned int nSpendTime, list<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const { return (SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 6, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 1, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, 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) { int64 nValue = 0; BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) { if (nValue < 0) return error("create tx : nValue < 0"); nValue += s.second; } if (vecSend.empty() || nValue < 0) return error("create tx : vecSend.empty()"); 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 error("create tx : SelectCoins() failed"); 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) { // 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()+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 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; // 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: 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; 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; } 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 wtxNew.AddSupportingTransactions(txdb); wtxNew.fTimeReceivedIsTxTime = true; break; } } } return true; } bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) { vector< pair<CScript, int64> > vecSend; vecSend.push_back(make_pair(scriptPubKey, nValue)); return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); } // peercoin: create coin stake transaction bool CWallet::CreateCoinStake(unsigned int nBits, int64 nSearchInterval, CTransaction& txNew, int64 rewardForBlockCreator) { // The following split & combine thresholds are important to security // Should not be adjusted if you don't understand the consequences static unsigned int nStakeSplitAge = (60 * 60 * 24 * 90); int64 nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits) / 3; CBigNum bnTargetPerCoinDay; bnTargetPerCoinDay.SetCompact(nBits); LOCK2(cs_main, cs_wallet); txNew.vin.clear(); txNew.vout.clear(); // Mark coin stake transaction CScript scriptEmpty; scriptEmpty.clear(); txNew.vout.push_back(CTxOut(0, scriptEmpty)); // Choose coins to use int64 nBalance = GetBalance(); int64 nReserveBalance = 0; if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) return error("CreateCoinStake : invalid reserve balance amount"); if (nBalance <= nReserveBalance) return false; list<pair<const CWalletTx*,unsigned int> > setCoins; vector<const CWalletTx*> vwtxPrev; int64 nValueIn = 0; if (!SelectCoins(nBalance - nReserveBalance, txNew.nTime, setCoins, nValueIn)) return false; if (setCoins.empty()) return false; int64 nCredit = 0; CScript scriptPubKeyKernel; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { CTxDB txdb("r"); CTxIndex txindex; if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) continue; // Read block header CBlock block; if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) continue; static int nMaxStakeSearchInterval = 60; if (block.GetBlockTime() + nStakeMinAge > txNew.nTime - nMaxStakeSearchInterval) continue; // only count coins meeting min age requirement bool fKernelFound = false; for (unsigned int n=0; n<min(nSearchInterval,(int64)nMaxStakeSearchInterval) && !fKernelFound && !fShutdown; n++) { // Search backward in time from the given txNew timestamp // Search nSearchInterval seconds back up to nMaxStakeSearchInterval uint256 hashProofOfStake = 0; COutPoint prevoutStake = COutPoint(pcoin.first->GetHash(), pcoin.second); if (CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, *pcoin.first, prevoutStake, txNew.nTime - n, hashProofOfStake)) { // Found a kernel if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : kernel found\n"); vector<valtype> vSolutions; txnouttype whichType; CScript scriptPubKeyOut; scriptPubKeyKernel = pcoin.first->vout[pcoin.second].scriptPubKey; if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) { if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : failed to parse kernel\n", whichType); break; } if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : parsed kernel type=%d\n", whichType); if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH) { if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : no support for kernel type=%d\n", whichType); break; // only support pay to public key and pay to address } if (whichType == TX_PUBKEYHASH) // pay to address type { // convert to pay to public key type CKey key; if (!this->GetKey(uint160(vSolutions[0]), key)) { if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); break; // unable to find corresponding public key } scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG; } else scriptPubKeyOut = scriptPubKeyKernel; txNew.nTime -= n; txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); nCredit += pcoin.first->vout[pcoin.second].nValue; vwtxPrev.push_back(pcoin.first); txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); // Decent: for reward txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); if (block.GetBlockTime() + nStakeSplitAge > txNew.nTime) txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); //split stake if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : added kernel type=%d\n", whichType); fKernelFound = true; break; } } if (fKernelFound || fShutdown) break; // if kernel is found stop searching } if (nCredit == 0 || nCredit > nBalance - nReserveBalance) return false; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { // Attempt to add more inputs // Only add coins of the same key/address as kernel if (txNew.vout.size() == 2 && ((pcoin.first->vout[pcoin.second].scriptPubKey == scriptPubKeyKernel || pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey)) && pcoin.first->GetHash() != txNew.vin[0].prevout.hash) { // 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; // Do not add additional significant input if (pcoin.first->vout[pcoin.second].nValue > nCombineThreshold) continue; // Do not add input that is still too young if (pcoin.first->nTime + STAKE_MAX_AGE > txNew.nTime) continue; txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); nCredit += pcoin.first->vout[pcoin.second].nValue; vwtxPrev.push_back(pcoin.first); } } // Calculate coin age reward // { // uint64 nCoinAge; // CTxDB txdb("r"); // if (!txNew.GetCoinAge(txdb, nCoinAge)) // return error("CreateCoinStake : failed to calculate coin age"); // nCredit += GetProofOfStakeReward(nCoinAge); // } int64 nMinFee = 0; loop { // Set output amount if (txNew.vout.size() == 4) { txNew.vout[1].nValue = rewardForBlockCreator; // Decent: set reward for block creator txNew.vout[2].nValue = ((nCredit - nMinFee) / 2 / CENT) * CENT; txNew.vout[3].nValue = nCredit - nMinFee - txNew.vout[2].nValue; } else txNew.vout[2].nValue = nCredit - nMinFee; //You can put your own extra vouts inside Stake transaction in such way, but it will have //consequences.First, you shouldn't hardcode any determined address here, because we have //seen strange thing.All stake money, used for coinstake transaction, will be returned to //this determined address regardless of person, who minted it.But money, which we sent //to this address, were delivered to address of person, who minted it.We don't know, why it //was happend, but it looked like this outputs exchanged their delivery addresses. //In conclusion, you should find out with coinstake transaction building process or you //should never ever touch this process. // CScript Script; // CBitcoinAddress address = CBitcoinAddress("SU6AncdVHCpDAiG9yj9BdNscb83XeLTpcj"); // Script.SetBitcoinAddress(address); // txNew.vout.push_back(CTxOut(100000000, Script)); // Sign int nIn = 0; BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev) { if (!SignSignature(*this, *pcoin, txNew, nIn++)) return error("CreateCoinStake : failed to sign coinstake"); } // 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"); // Check enough fee is paid if (nMinFee < txNew.GetMinFee() - MIN_TX_FEE) { nMinFee = txNew.GetMinFee() - MIN_TX_FEE; continue; // try signing again } else { if (fDebug && GetBoolArg("-printfee")) printf("CreateCoinStake : fee for coinstake %s\n", FormatMoney(nMinFee).c_str()); break; } } // Successfully generated coinstake return true; } // Call after CreateTransaction unless you want to abort bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) { { LOCK2(cs_main, cs_wallet); printf("CommitTransaction:\n%s", wtxNew.ToString().c_str()); { // 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 set<CWalletTx*> setCoins; BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) { CWalletTx &coin = mapWallet[txin.prevout.hash]; coin.BindWallet(this); coin.MarkSpent(txin.prevout.n); coin.WriteToDisk(); vWalletUpdated.push_back(coin.GetHash()); } if (fFileBacked) delete pwalletdb; } // Track how many getdata requests our transaction gets mapRequestCount[wtxNew.GetHash()] = 0; // Broadcast if (!wtxNew.AcceptToMemoryPool()) { // This must not fail. The transaction has already been signed and recorded. printf("CommitTransaction() : Error: Transaction not valid"); return false; } wtxNew.RelayWalletTransaction(); } MainFrameRepaint(); return true; } bool CWallet::CommitDecentTX(CWalletTx& wtxNew, CReserveKey& reservekey) { printf("Commit Decent tx:\n%s", wtxNew.ToString().c_str()); mapRequestCount[wtxNew.GetHash()] = 0; if (!wtxNew.AcceptToMemoryPool()) { if (wtxNew.IsRedundant()) return true; printf("CommitTransaction() : Error: Transaction not valid"); return error("Decent tx is not valid"); } AddToWalletIfInvolvingMe(wtxNew, NULL, true, true); wtxNew.RelayWalletTransaction(); MainFrameRepaint(); return true; } string CWallet::SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee) { CReserveKey reservekey(this); int64 nFeeRequired; if (IsLocked()) { string strError = _("Error: Portfolio locked, unable to create transaction "); printf("SendMoney() : %s", strError.c_str()); return strError; } if (fWalletUnlockMintOnly) { string strError = _("Error: Portfolio 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 && !ThreadSafeAskFee(nFeeRequired, _("Sending..."))) return "ABORTED"; if (!CommitTransaction(wtxNew, reservekey)) return _("Error: The transaction was rejected. This might happen if some of the shares in your portfolio were already spent, such as if you used a copy of wallet.dat and shares were spent in the copy but not marked as spent here."); MainFrameRepaint(); return ""; } string CWallet::SendMoneyToBitcoinAddress(const CBitcoinAddress& address, int64 nValue, CWalletTx& wtxNew, bool fAskFee) { // Check amount if (nValue < 0) return _("Invalid amount"); if (nValue + MIN_TX_FEE > GetBalance()) return _("Insufficient funds"); // Parse bitcoin address CScript scriptPubKey; scriptPubKey.SetBitcoinAddress(address); return SendMoney(scriptPubKey, nValue, wtxNew, fAskFee); } int CWallet::LoadWallet(bool& fFirstRunRet) { if (!fFileBacked) return false; fFirstRunRet = false; int 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. } nLoadWalletRet = DB_NEED_REWRITE; } if (nLoadWalletRet != DB_LOAD_OK) return nLoadWalletRet; fFirstRunRet = vchDefaultKey.empty(); CreateThread(ThreadFlushWalletDB, &strWalletFile); return DB_LOAD_OK; } bool CWallet::SetAddressBookName(const CBitcoinAddress& address, const string& strName) { mapAddressBook[address] = strName; AddressBookRepaint(); if (!fFileBacked) return false; return CWalletDB(strWalletFile).WriteName(address.ToString(), strName); } bool CWallet::DelAddressBookName(const CBitcoinAddress& address) { mapAddressBook.erase(address); AddressBookRepaint(); if (!fFileBacked) return false; return CWalletDB(strWalletFile).EraseName(address.ToString()); } bool CWallet::AddPassword(const uint256 &hash, const uint256 &password) { mapPassword[hash] = password; if (!fFileBacked) return false; return CWalletDB(strWalletFile).WritePass(hash, password); } bool CWallet::DelPassword(const uint256 &hash) { mapPassword.erase(hash); if (!fFileBacked) return false; return CWalletDB(strWalletFile).ErasePass(hash); } bool CWallet::GetPassword(const uint256 &hash, uint256 &password) { if (mapPassword.count(hash)) { password = mapPassword[hash]; return true; } return false; } valtype CWallet::GetElGamalPublicKey() { return EncryptionUtils::GetPublicElGamalKey(GetElGamalPrivateKey()); } valtype CWallet::GetElGamalPrivateKey() { valtype retKey; if (!privateElGamalKey.size()) { // Generate new key retKey = EncryptionUtils::GeneratePrivateElGamalKey(); { LOCK(cs_wallet); privateElGamalKey = retKey; if (!fFileBacked) assert(false); if (!CWalletDB(strWalletFile).WriteElGamalKey(retKey)) assert(false); } } else { // Use already generated key LOCK(cs_wallet); retKey = privateElGamalKey; } return retKey; } void CWallet::PrintWallet(const CBlock& block) { { LOCK(cs_wallet); if (block.IsProofOfWork() && mapWallet.count(block.vtx[0].GetHash())) { CWalletTx& wtx = mapWallet[block.vtx[0].GetHash()]; printf(" mine: %d %d %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str()); } if (block.IsProofOfStake() && mapWallet.count(block.vtx[1].GetHash())) { CWalletTx& wtx = mapWallet[block.vtx[1].GetHash()]; printf(" stake: %d %d %s", wtx.GetDepthInMainChain(), wtx.GetBlocksToMaturity(), FormatMoney(wtx.GetCredit()).c_str()); } } printf("\n"); } bool CWallet::GetTransaction(const uint256 &hashTx, CWalletTx& wtx) { { LOCK(cs_wallet); map<uint256, CWalletTx>::iterator mi = mapWallet.find(hashTx); if (mi != mapWallet.end()) { wtx = (*mi).second; return true; } } return false; } bool CWallet::SetDefaultKey(const std::vector<unsigned char> &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() { { LOCK(cs_wallet); CWalletDB walletdb(strWalletFile); BOOST_FOREACH(int64 nIndex, setKeyPool) walletdb.ErasePool(nIndex); setKeyPool.clear(); if (IsLocked()) return false; int64 nKeys = max(GetArg("-keypool", 100), (int64)0); for (int i = 0; i < nKeys; i++) { int64 nIndex = i+1; walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); setKeyPool.insert(nIndex); } printf("CWallet::NewKeyPool wrote %"PRI64d" new keys\n", nKeys); } return true; } bool CWallet::TopUpKeyPool() { { LOCK(cs_wallet); if (IsLocked()) return false; CWalletDB walletdb(strWalletFile); // Top up key pool unsigned int nTargetSize = max(GetArg("-keypool", 100), 0LL); while (setKeyPool.size() < (nTargetSize + 1)) { int64 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 %"PRI64d", size=%d\n", nEnd, setKeyPool.size()); } } return true; } void CWallet::ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool) { nIndex = -1; keypool.vchPubKey.clear(); { 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(Hash160(keypool.vchPubKey))) throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); assert(!keypool.vchPubKey.empty()); if (fDebug && GetBoolArg("-printkeypool")) printf("keypool reserve %"PRI64d"\n", nIndex); } } int64 CWallet::AddReserveKey(const CKeyPool& keypool) { { LOCK2(cs_main, cs_wallet); CWalletDB walletdb(strWalletFile); int64 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 nIndex) { // Remove from key pool if (fFileBacked) { CWalletDB walletdb(strWalletFile); walletdb.ErasePool(nIndex); } printf("keypool keep %"PRI64d"\n", nIndex); } void CWallet::ReturnKey(int64 nIndex) { // Return to key pool { LOCK(cs_wallet); setKeyPool.insert(nIndex); } if (fDebug && GetBoolArg("-printkeypool")) printf("keypool return %"PRI64d"\n", nIndex); } bool CWallet::GetKeyFromPool(vector<unsigned char>& result, bool fAllowReuse) { int64 nIndex = 0; CKeyPool keypool; { LOCK(cs_wallet); ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) { if (fAllowReuse && !vchDefaultKey.empty()) { result = vchDefaultKey; return true; } if (IsLocked()) return false; result = GenerateNewKey(); return true; } KeepKey(nIndex); result = keypool.vchPubKey; } return true; } int64 CWallet::GetOldestKeyPoolTime() { int64 nIndex = 0; CKeyPool keypool; ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) return GetTime(); ReturnKey(nIndex); return keypool.nTime; } // peercoin: check 'spent' consistency between wallet and txindex // peercoin: fix wallet spent state according to txindex void CWallet::FixSpentCoins(int& nMismatchFound, int64& nBalanceInQuestion, bool fCheckOnly) { nMismatchFound = 0; nBalanceInQuestion = 0; LOCK(cs_wallet); vector<CWalletTx*> vCoins; vCoins.reserve(mapWallet.size()); for (map<uint256, CWalletTx>::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 (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 unit %sDCT %s[%d], %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 unit %sDCT %s[%d], %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(); } } } } } // peercoin: 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<uint256, CWalletTx>::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(); } } } } vector<unsigned char> 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.empty()); return vchPubKey; } void CReserveKey::KeepKey() { if (nIndex != -1) pwallet->KeepKey(nIndex); nIndex = -1; vchPubKey.clear(); } void CReserveKey::ReturnKey() { if (nIndex != -1) pwallet->ReturnKey(nIndex); nIndex = -1; vchPubKey.clear(); } void CWallet::GetAllReserveAddresses(set<CBitcoinAddress>& setAddress) { setAddress.clear(); CWalletDB walletdb(strWalletFile); LOCK2(cs_main, cs_wallet); BOOST_FOREACH(const int64& id, setKeyPool) { CKeyPool keypool; if (!walletdb.ReadPool(id, keypool)) throw runtime_error("GetAllReserveKeyHashes() : read failed"); CBitcoinAddress address(keypool.vchPubKey); assert(!keypool.vchPubKey.empty()); if (!HaveKey(address)) throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool"); setAddress.insert(address); } } void CWallet::ExportPeercoinKeys(int &nExportedCount, int &nErrorCount) { nExportedCount = 0; nErrorCount = 0; if (IsLocked()) throw runtime_error("The portfolio is locked. Please unlock it first."); if (fWalletUnlockMintOnly) throw runtime_error("Portfolio is unlocked for minting only."); LOCK(cs_wallet); BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, string)& item, mapAddressBook) { const CBitcoinAddress& address = item.first; CSecret vchSecret; bool fCompressed; if (address.IsScript()) { const uint160 hash = address.GetHash160(); CScript script; if (!GetCScript(hash, script)) { printf("Failed get script of address %s\n", address.ToString().c_str()); nErrorCount++; continue; } txnouttype type; std::vector<CBitcoinAddress> vAddresses; int nRequired; if (!ExtractAddresses(script, type, vAddresses, nRequired)) { printf("Failed extract addresses from address %s\n", address.ToString().c_str()); nErrorCount++; continue; } if (type != TX_MULTISIG) { printf("Address %s is not a multisig address\n", address.ToString().c_str()); nErrorCount++; continue; } json_spirit::Array vPeercoinAddressStrings; BOOST_FOREACH(const CBitcoinAddress &address, vAddresses) vPeercoinAddressStrings.push_back(CPeercoinAddress(address).ToString()); json_spirit::Array params; params.push_back(json_spirit::Value(nRequired)); params.push_back(vPeercoinAddressStrings); params.push_back("Peershares"); try { string result = CallPeercoinRPC("addmultisigaddress", params); printf("Exported multisig address %s: %s\n", address.ToString().c_str(), result.c_str()); nExportedCount++; } catch (peercoin_rpc_error &error) { printf("Failed to add multisig address of address %s: %s\n", address.ToString().c_str(), error.what()); nErrorCount++; } } else { if (!GetSecret(address, vchSecret, fCompressed)) { printf("Private key for address %s is not known\n", address.ToString().c_str()); nErrorCount++; continue; } json_spirit::Array params; params.push_back(CPeercoinSecret(vchSecret, fCompressed).ToString()); params.push_back("Peershares"); try { string result = CallPeercoinRPC("importprivkey", params); printf("Exported private key of address %s: %s\n", address.ToString().c_str(), result.c_str()); nExportedCount++; } catch (peercoin_rpc_error &error) { printf("Failed to export private key of address %s: %s\n", address.ToString().c_str(), error.what()); nErrorCount++; } } } }
Trovare la differenza