Diff
checker
テキスト
テキスト
画像
ドキュメント
Excel
フォルダ
Legal
Enterprise
デスクトップ
料金
ログイン
Diffchecker デスクトップのダウンロード
テキスト比較
2 つのテキスト ファイルの違いを見つける
ツール
履歴
表示
レイアウト
分割
統合
ライブエディター
空白の変更を非表示
未変更行を折りたたむ
折り返しなし
比較精度
スマート
構文
なし
外観を変更
処理
無視
テキスト変換
最初の差分へ移動
入力を編集
Diffchecker Desktop
Diffcheckerを実行する最も安全な方法。Diffchecker Desktopアプリを入手:あなたの差分はコンピューターから出ることはありません!
Desktopを入手
この比較の残りの部分を読み込んでいます。
Untitled diff
作成日
10 年前
差分は期限切れになりません
クリア
エクスポート
共有
説明
34 削除
行
合計
削除
文字
合計
削除
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
748 行
すべてコピー
34 追加
行
合計
追加
文字
合計
追加
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
745 行
すべてコピー
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Copyright (c) 2009-2012 The Bitcoin developers
コピー
コピー済み
コピー
コピー済み
// Copyright (c) 2011-2013 The
PPCoin
developers
// Copyright (c) 2011-2013 The
Peercoin
developers
// Copyright (c) 2015-2015 The Decent developers
// Distributed under the MIT/X11 software license, see the accompanying
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "wallet.h"
#include "wallet.h"
#include "walletdb.h"
#include "walletdb.h"
#include "crypter.h"
#include "crypter.h"
#include "ui_interface.h"
#include "ui_interface.h"
#include "kernel.h"
#include "kernel.h"
コピー
コピー済み
コピー
コピー済み
#include
<boost/algorithm/string/replace.hpp>
#include
"bitcoinrpc.h"
#include "
base58
.h"
#include "
encryptionutils
.h"
using namespace std;
using namespace std;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
//
// mapWallet
// mapWallet
//
//
コピー
コピー済み
コピー
コピー済み
CPubKey
CWallet::GenerateNewKey()
std::vector<unsigned char>
CWallet::GenerateNewKey()
{
{
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets
RandAddSeedPerfmon();
RandAddSeedPerfmon();
CKey key;
CKey key;
key.MakeNewKey(fCompressed);
key.MakeNewKey(fCompressed);
// Compressed public keys were introduced in version 0.6.0
// Compressed public keys were introduced in version 0.6.0
if (fCompressed)
if (fCompressed)
SetMinVersion(FEATURE_COMPRPUBKEY);
SetMinVersion(FEATURE_COMPRPUBKEY);
if (!AddKey(key))
if (!AddKey(key))
throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed");
throw std::runtime_error("CWallet::GenerateNewKey() : AddKey failed");
return key.GetPubKey();
return key.GetPubKey();
}
}
bool CWallet::AddKey(const CKey& key)
bool CWallet::AddKey(const CKey& key)
{
{
if (!CCryptoKeyStore::AddKey(key))
if (!CCryptoKeyStore::AddKey(key))
return false;
return false;
if (!fFileBacked)
if (!fFileBacked)
return true;
return true;
if (!IsCrypted())
if (!IsCrypted())
return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey());
return CWalletDB(strWalletFile).WriteKey(key.GetPubKey(), key.GetPrivKey());
return true;
return true;
}
}
コピー
コピー済み
コピー
コピー済み
bool CWallet::AddCryptedKey(const
CPubKey
&vchPubKey, const vector<unsigned char> &vchCryptedSecret)
bool CWallet::AddCryptedKey(const
vector<unsigned char>
&vchPubKey, const vector<unsigned char> &vchCryptedSecret)
{
{
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))
return false;
return false;
if (!fFileBacked)
if (!fFileBacked)
return true;
return true;
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
if (pwalletdbEncryption)
if (pwalletdbEncryption)
return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret);
return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret);
else
else
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret);
return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret);
}
}
return false;
return false;
}
}
bool CWallet::AddCScript(const CScript& redeemScript)
bool CWallet::AddCScript(const CScript& redeemScript)
{
{
if (!CCryptoKeyStore::AddCScript(redeemScript))
if (!CCryptoKeyStore::AddCScript(redeemScript))
return false;
return false;
if (!fFileBacked)
if (!fFileBacked)
return true;
return true;
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);
}
}
コピー
コピー済み
コピー
コピー済み
// p
p
coin: optional setting to unlock wallet for block minting only;
// p
eer
coin: optional setting to unlock wallet for block minting only;
// serves to disable the trivial sendmoney when OS account compromised
// serves to disable the trivial sendmoney when OS account compromised
bool fWalletUnlockMintOnly = false;
bool fWalletUnlockMintOnly = false;
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{
{
if (!IsLocked())
if (!IsLocked())
return false;
return false;
CCrypter crypter;
CCrypter crypter;
CKeyingMaterial vMasterKey;
CKeyingMaterial vMasterKey;
{
{
LOCK(cs_wallet);
LOCK(cs_wallet);
BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys)
{
{
if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))
return false;
return false;
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))
return false;
return false;
if (CCryptoKeyStore::Unlock(vMasterKey))
if (CCryptoKeyStore::Unlock(vMasterKey))
return true;
return true;
}
}
}
}
return false;
return false;
}
}
保存された差分
原文
ファイルを開く
// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Copyright (c) 2011-2013 The PPCoin 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 <boost/algorithm/string/replace.hpp> #include "base58.h" using namespace std; ////////////////////////////////////////////////////////////////////////////// // // mapWallet // CPubKey CWallet::GenerateNewKey() { bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets RandAddSeedPerfmon(); CKey key; key.MakeNewKey(fCompressed); // Compressed public keys were introduced in version 0.6.0 if (fCompressed) SetMinVersion(FEATURE_COMPRPUBKEY); 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 CPubKey &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); } // ppcoin: optional setting to unlock wallet for block minting only; // serves to disable the trivial sendmoney when OS account compromised bool fWalletUnlockMintOnly = false; bool CWallet::Unlock(const SecureString& strWalletPassphrase) { if (!IsLocked()) return false; CCrypter crypter; CKeyingMaterial vMasterKey; { LOCK(cs_wallet); BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) { if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) return false; if (CCryptoKeyStore::Unlock(vMasterKey)) return true; } } return false; } bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) { bool fWasLocked = IsLocked(); { LOCK(cs_wallet); Lock(); CCrypter crypter; CKeyingMaterial vMasterKey; BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys) { if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) return false; if (CCryptoKeyStore::Unlock(vMasterKey)) { int64 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("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey)) return false; CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second); if (fWasLocked) Lock(); return true; } } } return false; } void CWallet::SetBestChain(const CBlockLocator& loc) { CWalletDB walletdb(strWalletFile); walletdb.WriteBestBlock(loc); } // This class implements an addrIncoming entry that causes pre-0.4 // clients to crash on startup if reading a private-key-encrypted wallet. class CCorruptAddress { public: IMPLEMENT_SERIALIZE ( if (nType & SER_DISK) READWRITE(nVersion); ) }; bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) { if (nWalletVersion >= nVersion) return true; // when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way if (fExplicit && nVersion > nWalletMaxVersion) nVersion = FEATURE_LATEST; nWalletVersion = nVersion; if (nVersion > nWalletMaxVersion) nWalletMaxVersion = nVersion; if (fFileBacked) { CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); if (nWalletVersion >= 40000) { // 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 Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod)) return false; if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey)) return false; { LOCK(cs_wallet); mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; if (fFileBacked) { pwalletdbEncryption = new CWalletDB(strWalletFile); if (!pwalletdbEncryption->TxnBegin()) return false; pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); } if (!EncryptKeys(vMasterKey)) { if (fFileBacked) pwalletdbEncryption->TxnAbort(); exit(1); //We now probably have half of our keys encrypted in memory, and half not...die and let the user reload their unencrypted wallet. } // Encryption was introduced in version 0.4.0 SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); if (fFileBacked) { if (!pwalletdbEncryption->TxnCommit()) exit(1); //We now have keys encrypted in memory, but no on disk...die to avoid confusion and let the user reload their unencrypted wallet. delete pwalletdbEncryption; pwalletdbEncryption = NULL; } Lock(); Unlock(strWalletPassphrase); NewKeyPool(); Lock(); // Need to completely rewrite the wallet file; if we don't, bdb might keep // bits of the unencrypted private key in slack space in the database file. CDB::Rewrite(strWalletFile); } 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 coin %sppc %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.SetDestination(vchDefaultKey.GetID()); BOOST_FOREACH(const CTxOut& txout, wtx.vout) { if (txout.scriptPubKey == scriptDefaultKey) { CPubKey newDefaultKey; if (GetKeyFromPool(newDefaultKey, false)) { SetDefaultKey(newDefaultKey); SetAddressBookName(vchDefaultKey.GetID(), ""); } } } #endif // Notify UI vWalletUpdated.push_back(hash); // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins WalletUpdateSpent(wtx); // notify an external script when a wallet transaction comes in or is updated std::string strCmd = GetArg("-walletnotify", ""); if ( !strCmd.empty()) { boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); boost::thread t(runCommand, strCmd); // thread runs free } } // Refresh UI 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 { CTxDestination 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 (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, 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<CTxDestination, int64> >& listReceived, list<pair<CTxDestination, 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) { CTxDestination address; vector<unsigned char> vchPubKey; if (!ExtractDestination(txout.scriptPubKey, address)) { printf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", this->GetHash().ToString().c_str()); } // 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<CTxDestination, int64> > listReceived; list<pair<CTxDestination, int64> > listSent; GetAmounts(allGeneratedImmature, allGeneratedMature, listReceived, listSent, allFee, strSentAccount); if (strAccount == "") nGenerated = allGeneratedMature; if (strAccount == strSentAccount) { BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& s, listSent) nSent += s.second; nFee = allFee; } { LOCK(pwallet->cs_wallet); BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64)& r, listReceived) { if (pwallet->mapAddressBook.count(r.first)) { map<CTxDestination, 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; } // ppcoin: 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; } // populate vCoins with vector of spendable (age, (value, (transaction, output_number))) outputs void CWallet::AvailableCoins(unsigned int nSpendTime, vector<COutput>& vCoins, bool fOnlyConfirmed) const { vCoins.clear(); { LOCK(cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (!pcoin->IsFinal()) continue; if (fOnlyConfirmed && !pcoin->IsConfirmed()) continue; if ((pcoin->IsCoinBase() || pcoin->IsCoinStake()) && pcoin->GetBlocksToMaturity() > 0) continue; for (int i = 0; i < pcoin->vout.size(); i++) { if (pcoin->nTime > nSpendTime) continue; // ppcoin: timestamp must not exceed spend time if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue > 0) vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); } } } } bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const { setCoinsRet.clear(); 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; BOOST_FOREACH(COutput output, vCoins) { const CWalletTx *pcoin = output.tx; if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) continue; int i = output.i; int64 n = pcoin->vout[i].nValue; 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; } #ifdef DEBUG //// debug print printf("SelectCoins() best subset: "); for (unsigned int i = 0; i < vValue.size(); i++) if (vfBest[i]) printf("%s ", FormatMoney(vValue[i].first).c_str()); printf("total %s\n", FormatMoney(nBest).c_str()); #endif } return true; } bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const { vector<COutput> vCoins; AvailableCoins(nSpendTime, vCoins); return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, 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; } // ppcoin: 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")) // ppcoin: not avatar mode { // Reserve a new key pair from key pool CPubKey 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.SetDestination(vchPubKey.GetID()); } // 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_GEN/5) 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); } // ppcoin: 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: Wallet locked, unable to create transaction "); printf("SendMoney() : %s", strError.c_str()); return strError; } if (fWalletUnlockMintOnly) { string strError = _("Error: Wallet unlocked for block minting only, unable to create transaction."); printf("SendMoney() : %s", strError.c_str()); return strError; } if (!CreateTransaction(scriptPubKey, nValue, wtxNew, reservekey, nFeeRequired)) { string strError; if (nValue + nFeeRequired > GetBalance()) strError = strprintf(_("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds "), FormatMoney(nFeeRequired).c_str()); else strError = _("Error: Transaction creation failed "); printf("SendMoney() : %s", strError.c_str()); return strError; } if (fAskFee && !ThreadSafeAskFee(nFeeRequired, _("Sending..."))) return "ABORTED"; if (!CommitTransaction(wtxNew, reservekey)) return _("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); MainFrameRepaint(); return ""; } string CWallet::SendMoneyToDestination(const CTxDestination& 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.SetDestination(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.IsValid(); CreateThread(ThreadFlushWalletDB, &strWalletFile); return DB_LOAD_OK; } bool CWallet::SetAddressBookName(const CTxDestination& address, const string& strName) { mapAddressBook[address] = strName; AddressBookRepaint(); if (!fFileBacked) return false; return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName); } bool CWallet::DelAddressBookName(const CTxDestination& address) { mapAddressBook.erase(address); AddressBookRepaint(); if (!fFileBacked) return false; return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(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 CPubKey &vchPubKey) { if (fFileBacked) { if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) return false; } vchDefaultKey = vchPubKey; return true; } bool GetWalletFile(CWallet* pwallet, string &strWalletFileOut) { if (!pwallet->fFileBacked) return false; strWalletFileOut = pwallet->strWalletFile; return true; } // // Mark old keypool keys as used, // and generate all new keys // bool CWallet::NewKeyPool() { { 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 = CPubKey(); { LOCK(cs_wallet); if (!IsLocked()) TopUpKeyPool(); // Get the oldest key if(setKeyPool.empty()) return; CWalletDB walletdb(strWalletFile); nIndex = *(setKeyPool.begin()); setKeyPool.erase(setKeyPool.begin()); if (!walletdb.ReadPool(nIndex, keypool)) throw runtime_error("ReserveKeyFromKeyPool() : read failed"); if (!HaveKey(keypool.vchPubKey.GetID())) throw runtime_error("ReserveKeyFromKeyPool() : unknown key in key pool"); assert(keypool.vchPubKey.IsValid()); 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(CPubKey& result, bool fAllowReuse) { int64 nIndex = 0; CKeyPool keypool; { LOCK(cs_wallet); ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) { if (fAllowReuse && vchDefaultKey.IsValid()) { result = vchDefaultKey; return true; } if (IsLocked()) return false; result = GenerateNewKey(); return true; } KeepKey(nIndex); result = keypool.vchPubKey; } return true; } int64 CWallet::GetOldestKeyPoolTime() { int64 nIndex = 0; CKeyPool keypool; ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) return GetTime(); ReturnKey(nIndex); return keypool.nTime; } // ppcoin: check 'spent' consistency between wallet and txindex // ppcoin: 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 coin %sppc %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 coin %sppc %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(); } } } } } // ppcoin: disable transaction (only for coinstake) void CWallet::DisableTransaction(const CTransaction &tx) { if (!tx.IsCoinStake() || !IsFromMe(tx)) return; // only disconnecting coinstake requires marking input unspent LOCK(cs_wallet); BOOST_FOREACH(const CTxIn& txin, tx.vin) { map<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(); } } } } CPubKey CReserveKey::GetReservedKey() { if (nIndex == -1) { CKeyPool keypool; pwallet->ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex != -1) vchPubKey = keypool.vchPubKey; else { printf("CReserveKey::GetReservedKey(): Warning: using default key instead of a new key, top up your keypool."); vchPubKey = pwallet->vchDefaultKey; } } assert(vchPubKey.IsValid()); return vchPubKey; } void CReserveKey::KeepKey() { if (nIndex != -1) pwallet->KeepKey(nIndex); nIndex = -1; vchPubKey = CPubKey(); } void CReserveKey::ReturnKey() { if (nIndex != -1) pwallet->ReturnKey(nIndex); nIndex = -1; vchPubKey = CPubKey(); } void CWallet::GetAllReserveKeys(set<CKeyID>& 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"); assert(keypool.vchPubKey.IsValid()); CKeyID keyID = keypool.vchPubKey.GetID(); if (!HaveKey(keyID)) throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool"); setAddress.insert(keyID); } }
変更されたテキスト
ファイルを開く
// 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++; } } } }
違いを見つける