Diff
checker
文本
文本
图像
文档
Excel
文件夹
Legal
Enterprise
桌面版
定价
登录
下载 Diffchecker 桌面版
比较文本
查找两个文本文件之间的差异
工具
历史
实时编辑器
折叠未更改行
关闭换行
视图
拆分
统一
比对精度
智能
单词
字符
语法高亮
选择语法
忽略
文本转换
转到第一个差异
编辑输入
Diffchecker Desktop
运行Diffchecker最安全的方式。获取Diffchecker桌面应用:您的差异永远不会离开您的电脑!
获取桌面版
Untitled diff
创建于
11年前
差异永不过期
清除
导出
分享
解释
337 删除
行
总计
删除
字符
总计
删除
要继续使用此功能,请升级到
Diff
checker
Pro
查看价格
807 行
全部复制
324 添加
行
总计
添加
字符
总计
添加
要继续使用此功能,请升级到
Diff
checker
Pro
查看价格
804 行
全部复制
// 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
// 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 "alert.h"
#include "alert.h"
#include "checkpoints.h"
#include "checkpoints.h"
#include "db.h"
#include "db.h"
复制
已复制
复制
已复制
#include "txdb.h"
#include "net.h"
#include "net.h"
复制
已复制
复制
已复制
#include "init.h"
#include "init.h"
#include "ui_interface.h"
#include "ui_interface.h"
#include "kernel.h"
#include "kernel.h"
#include "scrypt_mine.h"
#include "scrypt_mine.h"
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/fstream.hpp>
复制
已复制
复制
已复制
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
using namespace std;
using namespace std;
using namespace boost;
using namespace boost;
//
//
// Global state
// Global state
//
//
复制
已复制
复制
已复制
CCriticalSection cs_setpwalletRegistered;
CCriticalSection cs_setpwalletRegistered;
复制
已复制
复制
已复制
set<CWallet*> setpwalletRegistered;
set<CWallet*> setpwalletRegistered;
CCriticalSection cs_main;
CCriticalSection cs_main;
CTxMemPool mempool;
CTxMemPool mempool;
unsigned int nTransactionsUpdated = 0;
unsigned int nTransactionsUpdated = 0;
map<uint256, CBlockIndex*> mapBlockIndex;
map<uint256, CBlockIndex*> mapBlockIndex;
set<pair<COutPoint, unsigned int> > setStakeSeen;
set<pair<COutPoint, unsigned int> > setStakeSeen;
uint256 hashGenesisBlock = hashGenesisBlockOfficial;
uint256 hashGenesisBlock = hashGenesisBlockOfficial;
static CBigNum bnProofOfWorkLimit(~uint256(0) >> 20);
static CBigNum bnProofOfWorkLimit(~uint256(0) >> 20);
复制
已复制
复制
已复制
static CBigNum bnProofOfStakeLimit(~uint256(0) >>
24);
static CBigNum bnProofOfStakeLimit(~uint256(0) >>
20);
static CBigNum bnProofOfStakeHardLimit(~uint256(0) >> 30);
复制
已复制
复制
已复制
static CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >>
16
);
static CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >>
20
);
static CBigNum bnProofOfStakeLimitTestNet(~uint256(0) >> 20);
static CBigNum bnProofOfStakeLimitTestNet(~uint256(0) >> 20);
复制
已复制
复制
已复制
unsigned int nStakeMinAge = 60 * 60 *
24 *
10;
//
minimum age
for coin age - 10
days
unsigned int nStakeMinAge = 60 * 60 *
8; // time at which weight begins to build
unsigned int nStakeMaxAge = 60 * 60 * 24 * 30;
// stake age of full weight
- 30 days
unsigned int nStakeMinAgeV2 = 60 * 60 *
24 *
88 /
10;
// functionally the
minimum age
is 8.8
days
unsigned int nStakeTargetSpacing =
1 * 30;
//
1-minute
block spacing
unsigned int nStakeMaxAge = 60 * 60 * 24 * 30;
// stake age of full weight
: -1
int64
_t
nChainStartTime =
1371910049
;
unsigned int nStakeTargetSpacing =
90;
//
90 sec
block spacing
int nCoinbaseMaturity =
5
;
int64
nChainStartTime =
1399495660
;
int nCoinbaseMaturity =
10
;
CBlockIndex* pindexGenesisBlock = NULL;
CBlockIndex* pindexGenesisBlock = NULL;
int nBestHeight = -1;
int nBestHeight = -1;
复制
已复制
复制
已复制
uint256
nBestChainTrust = 0;
CBigNum b
nBestChainTrust = 0;
uint256
nBestInvalidTrust = 0;
CBigNum b
nBestInvalidTrust = 0;
uint256 hashBestChain = 0;
uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
CBlockIndex* pindexBest = NULL;
复制
已复制
复制
已复制
int64
_t
nTimeBestReceived = 0;
int64
nTimeBestReceived = 0;
bool fHaveGUI = false;
bool fHaveGUI = false;
复制
已复制
复制
已复制
bool fImporting = false; //Tranz need to fix this 66b02c93
bool fReindex = false; // Tranz need to fix this 7fea4846
CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have
CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have
map<uint256, CBlock*> mapOrphanBlocks;
map<uint256, CBlock*> mapOrphanBlocks;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
set<pair<COutPoint, unsigned int> > setStakeSeenOrphan;
set<pair<COutPoint, unsigned int> > setStakeSeenOrphan;
复制
已复制
复制
已复制
map<uint256, uint256> mapProofOfStake;
复制
已复制
复制
已复制
map<uint256,
CTransaction
> mapOrphanTransactions;
map<uint256,
CDataStream*
> mapOrphanTransactions;
map<uint256,
set
<uint256
> > mapOrphanTransactionsByPrev;
map<uint256,
map
<uint256
, CDataStream*
> > mapOrphanTransactionsByPrev;
map<unsigned int, unsigned int> mapHashedBlocks;
map<std::string, std::pair<int, int> > mapGetBlocksRequests;
std::map <std::string, int> mapPeerRejectedBlocks;
bool fStrictProtocol = false;
bool fStrictIncoming = false;
// Constant stuff for coinbase transactions we create:
// Constant stuff for coinbase transactions we create:
CScript COINBASE_FLAGS;
CScript COINBASE_FLAGS;
复制
已复制
复制
已复制
const string strMessageMagic = "
HoboNickels
Signed Message:\n";
const string strMessageMagic = "
HyperStake
Signed Message:\n";
double dHashesPerSec;
double dHashesPerSec;
复制
已复制
复制
已复制
int64
_t
nHPSTimerStart;
int64
nHPSTimerStart;
// Settings
// Settings
复制
已复制
复制
已复制
int64
_t
nTransactionFee = MIN_TX_FEE;
int64
nTransactionFee = MIN_TX_FEE;
int64_t nMinimumInputValue = MIN_TX_FEE;
int64_t nSplitThreshold = GetProofOfWorkReward();
int64_t nCombineThreshold = GetProofOfWorkReward() * 2;
extern enum Checkpoints::CPMode CheckpointsMode;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
//
// dispatching functions
// dispatching functions
//
//
// These functions dispatch to one or all registered wallets
// These functions dispatch to one or all registered wallets
void RegisterWallet(CWallet* pwalletIn)
void RegisterWallet(CWallet* pwalletIn)
{
{
{
{
LOCK(cs_setpwalletRegistered);
LOCK(cs_setpwalletRegistered);
setpwalletRegistered.insert(pwalletIn);
setpwalletRegistered.insert(pwalletIn);
}
}
}
}
void UnregisterWallet(CWallet* pwalletIn)
void UnregisterWallet(CWallet* pwalletIn)
{
{
{
{
LOCK(cs_setpwalletRegistered);
LOCK(cs_setpwalletRegistered);
setpwalletRegistered.erase(pwalletIn);
setpwalletRegistered.erase(pwalletIn);
}
}
}
}
复制
已复制
复制
已复制
void UnregisterAllWallets()
{
{
LOCK(cs_setpwalletRegistered);
setpwalletRegistered.clear();
Text moved to lines 798-800
}
}
// check whether the passed transaction is from us
// check whether the passed transaction is from us
bool static IsFromMe(CTransaction& tx)
bool static IsFromMe(CTransaction& tx)
{
{
复制
已复制
复制
已复制
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
LOCK(cs_setpwalletRegistered);
if (pwallet->IsFromMe(tx))
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
return true;
if (pwallet->IsFromMe(tx))
return false;
return true;
}
return false;
}
}
// get the wallet transaction with the given hash (if it exists)
// get the wallet transaction with the given hash (if it exists)
bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx)
bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx)
{
{
复制
已复制
复制
已复制
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
LOCK(cs_setpwalletRegistered);
if (pwallet->GetTransaction(hashTx,wtx))
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
return true;
if (pwallet->GetTransaction(hashTx,wtx))
return false;
return true;
}
return false;
}
// erases transaction with the given hash from all wallets
void static EraseFromWallets(uint256 hash)
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->EraseFromWallet(hash);
}
}
// make sure all wallets know about the given transaction, in the given block
// make sure all wallets know about the given transaction, in the given block
void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fConnect)
void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fConnect)
{
{
if (!fConnect)
if (!fConnect)
{
{
// ppcoin: wallets need to refund inputs when disconnecting coinstake
// ppcoin: wallets need to refund inputs when disconnecting coinstake
if (tx.IsCoinStake())
if (tx.IsCoinStake())
{
{
复制
已复制
复制
已复制
LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
if (pwallet->IsFromMe(tx))
if (pwallet->IsFromMe(tx))
pwallet->DisableTransaction(tx);
pwallet->DisableTransaction(tx);
}
}
return;
return;
}
}
复制
已复制
复制
已复制
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
LOCK(cs_setpwalletRegistered);
pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
}
}
// Add wallet transactions that aren't already in a block to mapTransactions
void ReacceptWalletTransactions()
{
{
LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->ReacceptWalletTransactions();
}
}
}
// notify wallets about a new best chain
// notify wallets about a new best chain
void static SetBestChain(const CBlockLocator& loc)
void static SetBestChain(const CBlockLocator& loc)
{
{
复制
已复制
复制
已复制
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
LOCK(cs_setpwalletRegistered);
pwallet->SetBestChain(loc);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->SetBestChain(loc);
}
}
}
// notify wallets about an updated transaction
// notify wallets about an updated transaction
void static UpdatedTransaction(const uint256& hashTx)
void static UpdatedTransaction(const uint256& hashTx)
{
{
复制
已复制
复制
已复制
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
LOCK(cs_setpwalletRegistered);
pwallet->UpdatedTransaction(hashTx);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->UpdatedTransaction(hashTx);
}
}
}
// dump all wallets
// dump all wallets
void static PrintWallets(const CBlock& block)
void static PrintWallets(const CBlock& block)
{
{
复制
已复制
复制
已复制
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
LOCK(cs_setpwalletRegistered);
pwallet->PrintWallet(block);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->PrintWallet(block);
}
}
}
// notify wallets about an incoming inventory (for request counts)
// notify wallets about an incoming inventory (for request counts)
void static Inventory(const uint256& hash)
void static Inventory(const uint256& hash)
{
{
复制
已复制
复制
已复制
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
LOCK(cs_setpwalletRegistered);
pwallet->Inventory(hash);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->Inventory(hash);
}
}
}
// ask wallets to resend their transactions
// ask wallets to resend their transactions
复制
已复制
复制
已复制
void ResendWalletTransactions(
bool fForce)
void ResendWalletTransactions(
)
{
{
LOCK(cs_setpwalletRegistered);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->ResendWalletTransactions(fForce);
}
Text moved with changes to lines 712-717 (84.2% similarity)
}
//////////////////////////////////////////////////////////////////////////////
//
// Registration of network node signals.
//
namespace {
// Maintain validation-specific state about nodes, protected by cs_main, instead
// by CNode's own locks. This simplifies asynchronous operation, where
// processing of incoming data is done after the ProcessMessage call returns,
// and we're no longer holding the node's locks.
struct CNodeState {
// Accumulated misbehaviour score for this peer.
int nMisbehavior;
// Whether this peer should be disconnected and banned.
bool fShouldBan;
// String name of this peer (debugging/logging purposes).
std::string name;
CNodeState() {
nMisbehavior = 0;
fShouldBan = false;
}
};
map<NodeId, CNodeState> mapNodeState;
// Requires cs_main. (Tranz Locks removed for now due to deadlock)
CNodeState *State(NodeId pnode) {
map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
if (it == mapNodeState.end())
return NULL;
return &it->second;
}
int GetHeight()
{
return nBestHeight;
}
void InitializeNode(NodeId nodeid, const CNode *pnode) {
CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second;
state.name = pnode->addrName;
}
void FinalizeNode(NodeId nodeid) {
mapNodeState.erase(nodeid);
}
}
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
CNodeState *state = State(nodeid);
if (state == NULL)
return false;
stats.nMisbehavior = state->nMisbehavior;
Text moved to lines 693-695
return true;
}
void RegisterNodeSignals(CNodeSignals& nodeSignals)
{
nodeSignals.GetHeight.connect(&GetHeight);
nodeSignals.ProcessMessages.connect(&ProcessMessages);
nodeSignals.SendMessages.connect(&SendMessages);
nodeSignals.InitializeNode.connect(&InitializeNode);
nodeSignals.FinalizeNode.connect(&FinalizeNode);
}
void UnregisterNodeSignals(CNodeSignals& nodeSignals)
{
{
复制
已复制
复制
已复制
nodeSignals.GetHeight.disconnect(&GetHeight);
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
nodeSignals.ProcessMessages.disconnect(&ProcessMessages);
pwallet->ResendWalletTransactions();
nodeSignals.SendMessages.disconnect(&SendMessages);
nodeSignals.InitializeNode.disconnect(&InitializeNode);
nodeSignals.FinalizeNode.disconnect(&FinalizeNode);
}
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
//
// mapOrphanTransactions
// mapOrphanTransactions
//
//
复制
已复制
复制
已复制
bool AddOrphanTx(const
CTransaction& tx
)
bool AddOrphanTx(const
CDataStream& vMsg
)
{
{
复制
已复制
复制
已复制
CTransaction tx;
CDataStream(vMsg) >> tx;
uint256 hash = tx.GetHash();
uint256 hash = tx.GetHash();
if (mapOrphanTransactions.count(hash))
if (mapOrphanTransactions.count(hash))
return false;
return false;
复制
已复制
复制
已复制
CDataStream* pvMsg = new CDataStream(vMsg);
// Ignore big transactions, to avoid a
// Ignore big transactions, to avoid a
// send-big-orphans memory exhaustion attack. If a peer has a legitimate
// send-big-orphans memory exhaustion attack. If a peer has a legitimate
// large transaction with a missing parent then we assume
// large transaction with a missing parent then we assume
// it will rebroadcast it later, after the parent transaction(s)
// it will rebroadcast it later, after the parent transaction(s)
// have been mined or received.
// have been mined or received.
// 10,000 orphans, each of which is at most 5,000 bytes big is
// 10,000 orphans, each of which is at most 5,000 bytes big is
// at most 500 megabytes of orphans:
// at most 500 megabytes of orphans:
复制
已复制
复制
已复制
if (
pvMsg->s
ize
()
> 5000)
size_t nSize = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
if (
nS
ize
> 5000)
{
{
复制
已复制
复制
已复制
LogPrint("mempool",
"ignoring large orphan tx (size: %
u
, hash: %s)\n",
nS
ize
, hash.ToString().substr(0,10)
);
printf(
"ignoring large orphan tx (size: %
"PRIszu"
, hash: %s)\n",
pvMsg->s
ize
()
, hash.ToString().substr(0,10)
.c_str());
delete pvMsg;
return false;
return false;
}
}
复制
已复制
复制
已复制
mapOrphanTransactions[hash] =
tx
;
mapOrphanTransactions[hash] =
pvMsg
;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
BOOST_FOREACH(const CTxIn& txin, tx.vin)
复制
已复制
复制
已复制
mapOrphanTransactionsByPrev[txin.prevout.hash].insert(
hash
);
mapOrphanTransactionsByPrev[txin.prevout.hash].insert(
make_pair(
hash
, pvMsg)
);
复制
已复制
复制
已复制
LogPrint("mempool",
"stored orphan tx %s (mapsz %
u
)\n", hash.ToString().substr(0,10)
,
printf(
"stored orphan tx %s (mapsz %
"PRIszu"
)\n", hash.ToString().substr(0,10)
.c_str()
,
mapOrphanTransactions.size());
mapOrphanTransactions.size());
return true;
return true;
}
}
void static EraseOrphanTx(uint256 hash)
void static EraseOrphanTx(uint256 hash)
{
{
if (!mapOrphanTransactions.count(hash))
if (!mapOrphanTransactions.count(hash))
return;
return;
复制
已复制
复制
已复制
const
CTransaction& tx
= mapOrphanTransactions[hash];
const
CDataStream* pvMsg
= mapOrphanTransactions[hash];
CTransaction tx;
CDataStream(*pvMsg) >> tx;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
{
mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash);
mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash);
if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty())
if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty())
mapOrphanTransactionsByPrev.erase(txin.prevout.hash);
mapOrphanTransactionsByPrev.erase(txin.prevout.hash);
}
}
复制
已复制
复制
已复制
delete pvMsg;
mapOrphanTransactions.erase(hash);
mapOrphanTransactions.erase(hash);
}
}
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
{
{
unsigned int nEvicted = 0;
unsigned int nEvicted = 0;
while (mapOrphanTransactions.size() > nMaxOrphans)
while (mapOrphanTransactions.size() > nMaxOrphans)
{
{
// Evict a random orphan:
// Evict a random orphan:
uint256 randomhash = GetRandHash();
uint256 randomhash = GetRandHash();
复制
已复制
复制
已复制
map<uint256,
CTransaction
>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
map<uint256,
CDataStream*
>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
if (it == mapOrphanTransactions.end())
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
it = mapOrphanTransactions.begin();
EraseOrphanTx(it->first);
EraseOrphanTx(it->first);
++nEvicted;
++nEvicted;
}
}
return nEvicted;
return nEvicted;
}
}
复制
已复制
复制
已复制
Text moved to lines 749-752
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
//
// CTransaction and CTxIndex
// CTransaction and CTxIndex
//
//
bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet)
bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet)
{
{
SetNull();
SetNull();
if (!txdb.ReadTxIndex(prevout.hash, txindexRet))
if (!txdb.ReadTxIndex(prevout.hash, txindexRet))
return false;
return false;
if (!ReadFromDisk(txindexRet.pos))
if (!ReadFromDisk(txindexRet.pos))
return false;
return false;
if (prevout.n >= vout.size())
if (prevout.n >= vout.size())
{
{
SetNull();
SetNull();
return false;
return false;
}
}
return true;
return true;
}
}
bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout)
bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout)
{
{
CTxIndex txindex;
CTxIndex txindex;
return ReadFromDisk(txdb, prevout, txindex);
return ReadFromDisk(txdb, prevout, txindex);
}
}
bool CTransaction::ReadFromDisk(COutPoint prevout)
bool CTransaction::ReadFromDisk(COutPoint prevout)
{
{
CTxDB txdb("r");
CTxDB txdb("r");
CTxIndex txindex;
CTxIndex txindex;
return ReadFromDisk(txdb, prevout, txindex);
return ReadFromDisk(txdb, prevout, txindex);
}
}
复制
已复制
复制
已复制
bool
IsStandardTx(const
CTransaction
& tx)
bool
CTransaction
::IsStandard() const
{
{
复制
已复制
复制
已复制
if (
tx.
nVersion > CTransaction::CURRENT_VERSION)
if (
nVersion > CTransaction::CURRENT_VERSION)
return false;
// Treat non-final transactions as non-standard to prevent a specific type
// of double-spend attack, as well as DoS attacks. (if the transaction
// can't be mined, the attacker isn't expending resources broadcasting it)
// Basically we don't want to propagate transactions that can't included in
// the next block.
//
// However, IsFinalTx() is confusing... Without arguments, it uses
// chainActive.Height() to evaluate nLockTime; when a block is accepted, chainActive.Height()
// is set to the value of nHeight in the block. However, when IsFinalTx()
// is called within CBlock::AcceptBlock(), the height of the block *being*
// evaluated is what is used. Thus if we want to know if a transaction can
// be part of the *next* block, we need to call IsFinalTx() with one more
// than chainActive.Height().
//
// Timestamps on the other hand don't get any special treatment, because we
// can't know what timestamp the next block will have, and there aren't
// timestamp applications where it matters.
if (!IsFinalTx(tx, nBestHeight + 1)) {
return false;
}
// nTime has different purpose from nLockTime but can be used in similar attacks
if (tx.nTime > FutureDrift(GetAdjustedTime())) {
return false;
}
// Extremely large transactions with lots of inputs can cost the network
// almost as much to process as they cost the sender in fees, because
// computing signature hashes is O(ninputs*txsize). Limiting transactions
// to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks.
unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
if (sz >= MAX_STANDARD_TX_SIZE)
return false;
return false;
复制
已复制
复制
已复制
BOOST_FOREACH(const CTxIn& txin,
vin)
BOOST_FOREACH(const CTxIn& txin,
tx.
vin)
{
{
// Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG
// Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG
// pay-to-script-hash, which is 3 ~80-byte signatures, 3
// pay-to-script-hash, which is 3 ~80-byte signatures, 3
// ~65-byte public keys, plus a few script ops.
// ~65-byte public keys, plus a few script ops.
if (txin.scriptSig.size() > 500)
if (txin.scriptSig.size() > 500)
return false;
return false;
if (!txin.scriptSig.IsPushOnly())
if (!txin.scriptSig.IsPushOnly())
return false;
return false;
复制
已复制
复制
已复制
if (!txin.scriptSig.HasCanonicalPushes())
return false;
}
}
复制
已复制
复制
已复制
BOOST_FOREACH(const CTxOut& txout,
vout) {
unsigned int nDataOut = 0;
if (!::IsStandard(txout.scriptPubKey
))
txnouttype whichType;
BOOST_FOREACH(const CTxOut& txout,
tx.
vout) {
if (!::IsStandard(txout.scriptPubKey
, whichType
))
return false;
return false;
复制
已复制
复制
已复制
if (whichType == TX_NULL_DATA)
nDataOut++;
if (txout.nValue == 0)
if (txout.nValue == 0)
return false;
return false;
复制
已复制
复制
已复制
if (!txout.scriptPubKey.HasCanonicalPushes())
return false;
}
// only one OP_RETURN txout is permitted
if (nDataOut > 1) {
return false;
}
}
复制
已复制
复制
已复制
return true;
return true;
}
}
复制
已复制
复制
已复制
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
{
AssertLockHeld(cs_main);
// Time based nLockTime implemented in 0.1.6
if (tx.nLockTime == 0)
return true;
if (nBlockHeight == 0)
nBlockHeight = nBestHeight;
if (nBlockTime == 0)
nBlockTime = GetAdjustedTime();
if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime))
return true;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
if (!txin.IsFinal())
return false;
return true;
}
//
//
// Check transaction inputs, and make sure any
// Check transaction inputs, and make sure any
// pay-to-script-hash transactions are evaluating IsStandard scripts
// pay-to-script-hash transactions are evaluating IsStandard scripts
//
//
// Why bother? To avoid denial-of-service attacks; an attacker
// Why bother? To avoid denial-of-service attacks; an attacker
// can submit a standard HASH... OP_EQUAL transaction,
// can submit a standard HASH... OP_EQUAL transaction,
// which will get accepted into blocks. The redemption
// which will get accepted into blocks. The redemption
// script can be anything; an attacker could use a very
// script can be anything; an attacker could use a very
// expensive-to-check-upon-redemption script like:
// expensive-to-check-upon-redemption script like:
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
//
//
bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const
bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const
{
{
if (IsCoinBase())
if (IsCoinBase())
return true; // Coinbases don't use vin normally
return true; // Coinbases don't use vin normally
for (unsigned int i = 0; i < vin.size(); i++)
for (unsigned int i = 0; i < vin.size(); i++)
{
{
const CTxOut& prev = GetOutputFor(vin[i], mapInputs);
const CTxOut& prev = GetOutputFor(vin[i], mapInputs);
vector<vector<unsigned char> > vSolutions;
vector<vector<unsigned char> > vSolutions;
txnouttype whichType;
txnouttype whichType;
// get the scriptPubKey corresponding to this input:
// get the scriptPubKey corresponding to this input:
const CScript& prevScript = prev.scriptPubKey;
const CScript& prevScript = prev.scriptPubKey;
if (!Solver(prevScript, whichType, vSolutions))
if (!Solver(prevScript, whichType, vSolutions))
return false;
return false;
int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions);
int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions);
if (nArgsExpected < 0)
if (nArgsExpected < 0)
return false;
return false;
// Transactions with extra stuff in their scriptSigs are
// Transactions with extra stuff in their scriptSigs are
// non-standard. Note that this EvalScript() call will
// non-standard. Note that this EvalScript() call will
// be quick, because if there are any operations
// be quick, because if there are any operations
// beside "push data" in the scriptSig the
// beside "push data" in the scriptSig the
// IsStandard() call returns false
// IsStandard() call returns false
vector<vector<unsigned char> > stack;
vector<vector<unsigned char> > stack;
if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0))
if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0))
return false;
return false;
if (whichType == TX_SCRIPTHASH)
if (whichType == TX_SCRIPTHASH)
{
{
if (stack.empty())
if (stack.empty())
return false;
return false;
CScript subscript(stack.back().begin(), stack.back().end());
CScript subscript(stack.back().begin(), stack.back().end());
vector<vector<unsigned char> > vSolutions2;
vector<vector<unsigned char> > vSolutions2;
txnouttype whichType2;
txnouttype whichType2;
if (!Solver(subscript, whichType2, vSolutions2))
if (!Solver(subscript, whichType2, vSolutions2))
return false;
return false;
if (whichType2 == TX_SCRIPTHASH)
if (whichType2 == TX_SCRIPTHASH)
return false;
return false;
int tmpExpected;
int tmpExpected;
tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2);
tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2);
if (tmpExpected < 0)
if (tmpExpected < 0)
return false;
return false;
nArgsExpected += tmpExpected;
nArgsExpected += tmpExpected;
}
}
if (stack.size() != (unsigned int)nArgsExpected)
if (stack.size() != (unsigned int)nArgsExpected)
return false;
return false;
}
}
return true;
return true;
}
}
unsigned int
unsigned int
CTransaction::GetLegacySigOpCount() const
CTransaction::GetLegacySigOpCount() const
{
{
unsigned int nSigOps = 0;
unsigned int nSigOps = 0;
BOOST_FOREACH(const CTxIn& txin, vin)
BOOST_FOREACH(const CTxIn& txin, vin)
{
{
nSigOps += txin.scriptSig.GetSigOpCount(false);
nSigOps += txin.scriptSig.GetSigOpCount(false);
}
}
BOOST_FOREACH(const CTxOut& txout, vout)
BOOST_FOREACH(const CTxOut& txout, vout)
{
{
nSigOps += txout.scriptPubKey.GetSigOpCount(false);
nSigOps += txout.scriptPubKey.GetSigOpCount(false);
}
}
return nSigOps;
return nSigOps;
}
}
int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
{
{
复制
已复制
复制
已复制
AssertLockHeld(cs_main);
if (
fClient
)
CBlock blockTmp;
if (
pblock == NULL
)
{
{
复制
已复制
复制
已复制
// Load the block this tx is in
if (hashBlock == 0)
CTxIndex txindex;
if (!CTxDB("r").ReadTxIndex(GetHash(), txindex))
return 0;
if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos))
return 0;
return 0;
复制
已复制
复制
已复制
pblock = &blockTmp;
}
}
复制
已复制
复制
已复制
else
{
CBlock blockTmp;
if (pblock == NULL)
{
// Load the block this tx is in
CTxIndex txindex;
if (!CTxDB("r").ReadTxIndex(GetHash(), txindex))
return 0;
if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos))
return 0;
pblock = &blockTmp;
}
复制
已复制
复制
已复制
// Update the tx's hashBlock
// Update the tx's hashBlock
hashBlock = pblock->GetHash();
hashBlock = pblock->GetHash();
复制
已复制
复制
已复制
// Locate the transaction
// Locate the transaction
for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++)
for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++)
if (pblock->vtx[nIndex] == *(CTransaction*)this)
if (pblock->vtx[nIndex] == *(CTransaction*)this)
break;
break;
if (nIndex == (int)pblock->vtx.size())
if (nIndex == (int)pblock->vtx.size())
{
{
vMerkleBranch.clear();
vMerkleBranch.clear();
nIndex = -1;
nIndex = -1;
LogP
rintf("ERROR: SetMerkleBranch() : couldn't find tx in block\n");
p
rintf("ERROR: SetMerkleBranch() : couldn't find tx in block\n");
return 0;
return 0;
}
// Fill in merkle branch
vMerkleBranch = pblock->GetMerkleBranch(nIndex);
}
}
复制
已复制
复制
已复制
// Fill in merkle branch
vMerkleBranch = pblock->GetMerkleBranch(nIndex);
// Is the tx in a block that's in the main chain
// Is the tx in a block that's in the main chain
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
if (mi == mapBlockIndex.end())
if (mi == mapBlockIndex.end())
return 0;
return 0;
CBlockIndex* pindex = (*mi).second;
CBlockIndex* pindex = (*mi).second;
if (!pindex || !pindex->IsInMainChain())
if (!pindex || !pindex->IsInMainChain())
return 0;
return 0;
return pindexBest->nHeight - pindex->nHeight + 1;
return pindexBest->nHeight - pindex->nHeight + 1;
}
}
复制
已复制
复制
已复制
bool CTransaction::CheckTransaction() const
bool CTransaction::CheckTransaction() const
{
{
// Basic checks that don't depend on any context
// Basic checks that don't depend on any context
if (vin.empty())
if (vin.empty())
return DoS(10, error("CTransaction::CheckTransaction() : vin empty"));
return DoS(10, error("CTransaction::CheckTransaction() : vin empty"));
if (vout.empty())
if (vout.empty())
return DoS(10, error("CTransaction::CheckTransaction() : vout empty"));
return DoS(10, error("CTransaction::CheckTransaction() : vout empty"));
复制
已复制
复制
已复制
// Time (prevent mempool memory exhaustion attack)
if (nTime > GetAdjustedTime() + nMaxClockDrift)
return DoS(10, error("CTransaction::CheckTransaction() : timestamp is too far into the future"));
// Size limits
// Size limits
if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return DoS(100, error("CTransaction::CheckTransaction() : size limits failed"));
return DoS(100, error("CTransaction::CheckTransaction() : size limits failed"));
// Check for negative or overflow output values
// Check for negative or overflow output values
复制
已复制
复制
已复制
int64
_t
nValueOut = 0;
int64
nValueOut = 0;
for (unsigned int i = 0; i < vout.size(); i++)
for (unsigned int i = 0; i < vout.size(); i++)
{
{
const CTxOut& txout = vout[i];
const CTxOut& txout = vout[i];
if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake())
if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake())
return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction"));
return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction"));
// ppcoin: enforce minimum output amount
// ppcoin: enforce minimum output amount
if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT)
if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT)
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum"));
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum"));
复制
已复制
复制
已复制
if (txout.nValue < 0)
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue is negative"));
if (txout.nValue > MAX_MONEY)
if (txout.nValue > MAX_MONEY)
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high"));
return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high"));
nValueOut += txout.nValue;
nValueOut += txout.nValue;
if (!MoneyRange(nValueOut))
if (!MoneyRange(nValueOut))
return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"));
return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"));
}
}
// Check for duplicate inputs
// Check for duplicate inputs
set<COutPoint> vInOutPoints;
set<COutPoint> vInOutPoints;
BOOST_FOREACH(const CTxIn& txin, vin)
BOOST_FOREACH(const CTxIn& txin, vin)
{
{
if (vInOutPoints.count(txin.prevout))
if (vInOutPoints.count(txin.prevout))
return false;
return false;
vInOutPoints.insert(txin.prevout);
vInOutPoints.insert(txin.prevout);
}
}
if (IsCoinBase())
if (IsCoinBase())
{
{
if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100)
if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100)
return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size"));
return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size"));
}
}
else
else
{
{
BOOST_FOREACH(const CTxIn& txin, vin)
BOOST_FOREACH(const CTxIn& txin, vin)
if (txin.prevout.IsNull())
if (txin.prevout.IsNull())
return DoS(10, error("CTransaction::CheckTransaction() : prevout is null"));
return DoS(10, error("CTransaction::CheckTransaction() : prevout is null"));
}
}
return true;
return true;
}
}
复制
已复制
复制
已复制
int64
_t
CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree,
int64
CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree,
enum GetMinFee_mode mode, unsigned int nBytes) const
enum GetMinFee_mode mode, unsigned int nBytes) const
{
{
// Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE
// Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE
复制
已复制
复制
已复制
int64
_t
nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE;
int64
nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE;
unsigned int nNewBlockSize = nBlockSize + nBytes;
unsigned int nNewBlockSize = nBlockSize + nBytes;
复制
已复制
复制
已复制
int64
_t
nMinFee = (1 + (int64
_t
)nBytes / 1000) * nBaseFee;
int64
nMinFee = (1 + (int64
)nBytes / 1000) * nBaseFee;
// To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01
// To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01
if (nMinFee < nBaseFee)
if (nMinFee < nBaseFee)
{
{
BOOST_FOREACH(const CTxOut& txout, vout)
BOOST_FOREACH(const CTxOut& txout, vout)
if (txout.nValue < CENT)
if (txout.nValue < CENT)
nMinFee = nBaseFee;
nMinFee = nBaseFee;
}
}
// Raise the price as the block approaches full
// Raise the price as the block approaches full
if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2)
if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2)
{
{
if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN)
if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN)
return MAX_MONEY;
return MAX_MONEY;
nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize);
nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize);
}
}
if (!MoneyRange(nMinFee))
if (!MoneyRange(nMinFee))
nMinFee = MAX_MONEY;
nMinFee = MAX_MONEY;
复制
已复制
复制
已复制
return
nMinFee
;
return
max(
nMinFee
, MIN_TX_FEE)
;
}
}
复制
已复制
复制
已复制
bool
AcceptToMemoryPool(
CTxMemPool
& pool
, CTransaction &tx,
bool
CTxMemPool
::accept(CTxDB& txdb
, CTransaction &tx,
bool fCheckInputs,
bool* pfMissingInputs)
bool* pfMissingInputs)
{
{
复制
已复制
复制
已复制
AssertLockHeld(cs_main);
if (pfMissingInputs)
if (pfMissingInputs)
*pfMissingInputs = false;
*pfMissingInputs = false;
if (!tx.CheckTransaction())
if (!tx.CheckTransaction())
复制
已复制
复制
已复制
return error("
AcceptToMemoryPool
: CheckTransaction failed");
return error("
CTxMemPool::accept()
: CheckTransaction failed");
// Coinbase is only valid in a block, not as a loose transaction
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
if (tx.IsCoinBase())
复制
已复制
复制
已复制
return tx.DoS(100, error("
AcceptToMemoryPool
: coinbase as individual tx"));
return tx.DoS(100, error("
CTxMemPool::accept()
: coinbase as individual tx"));
// ppcoin: coinstake is also only valid in a block, not as a loose transaction
// ppcoin: coinstake is also only valid in a block, not as a loose transaction
if (tx.IsCoinStake())
if (tx.IsCoinStake())
复制
已复制
复制
已复制
return tx.DoS(100, error("
AcceptToMemoryPool
: coinstake as individual tx"));
return tx.DoS(100, error("
CTxMemPool::accept()
: coinstake as individual tx"));
// To help v0.1.5 clients who would see it as a negative number
if ((int64)tx.nLockTime > std::numeric_limits<int>::max())
return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet");
// Rather not work on nonstandard transactions (unless -testnet)
// Rather not work on nonstandard transactions (unless -testnet)
复制
已复制
复制
已复制
if (!fTestNet && !
IsStandard
Tx(tx
))
if (!fTestNet && !
tx.
IsStandard
(
))
return error("
AcceptToMemoryPool
: nonstandard transaction type");
return error("
CTxMemPool::accept()
: nonstandard transaction type");
复制
已复制
复制
已复制
//
is it
already
in the memory pool
?
//
Do we
already
have it
?
uint256 hash = tx.GetHash();
uint256 hash = tx.GetHash();
复制
已复制
复制
已复制
{
if (pool.exists
(hash))
LOCK(cs);
return false;
if (mapTx.count(hash))
return false;
}
if (fCheckInputs)
if (txdb.ContainsTx
(hash))
return false;
// Check for conflicts with in-memory transactions
// Check for conflicts with in-memory transactions
复制
已复制
复制
已复制
CTransaction* ptxOld = NULL;
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
{
复制
已复制
复制
已复制
LOCK(pool.cs); // protect pool.mapNextTx
COutPoint outpoint = tx.vin[i].prevout;
for (unsigned int i = 0; i < tx.vin.size(); i++
)
if (mapNextTx.count(outpoint)
)
{
{
复制
已复制
复制
已复制
COutPoint outpoint = tx.vin[i].prevout;
// Disable replacement feature for now
if (pool.mapNextTx.count(outpoint))
return false;
{
// Disable replacement feature for now
// Allow replacing with a newer version of the same transaction
if (i != 0)
return false;
ptxOld = mapNextTx[outpoint].ptx;
if (ptxOld->IsFinal())
return false;
if (!tx.IsNewerThan(*ptxOld))
return false;
return false;
复制
已复制
复制
已复制
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
COutPoint outpoint = tx.vin[i].prevout;
if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld)
return false;
}
}
复制
已复制
复制
已复制
break;
}
}
}
}
复制
已复制
复制
已复制
if (fCheckInputs)
{
{
复制
已复制
复制
已复制
CTxDB txdb("r");
// do we already have it?
if (txdb.ContainsTx(hash))
return false;
MapPrevTx mapInputs;
MapPrevTx mapInputs;
map<uint256, CTxIndex> mapUnused;
map<uint256, CTxIndex> mapUnused;
bool fInvalid = false;
bool fInvalid = false;
if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid))
if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid))
{
{
if (fInvalid)
if (fInvalid)
复制
已复制
复制
已复制
return error("
AcceptToMemoryPool
: FetchInputs found invalid tx %s", hash.ToString().substr(0,10)
);
return error("
CTxMemPool::accept()
: FetchInputs found invalid tx %s", hash.ToString().substr(0,10)
.c_str()
);
if (pfMissingInputs)
if (pfMissingInputs)
*pfMissingInputs = true;
*pfMissingInputs = true;
return false;
return false;
}
}
// Check for non-standard pay-to-script-hash in inputs
// Check for non-standard pay-to-script-hash in inputs
if (!tx.AreInputsStandard(mapInputs) && !fTestNet)
if (!tx.AreInputsStandard(mapInputs) && !fTestNet)
复制
已复制
复制
已复制
return error("
AcceptToMemoryPool
: nonstandard transaction input");
return error("
CTxMemPool::accept()
: nonstandard transaction input");
// Note: if you modify this code to accept non-standard transactions, then
// Note: if you modify this code to accept non-standard transactions, then
// you should add code here to check that the transaction does a
// you should add code here to check that the transaction does a
// reasonable number of ECDSA signature verifications.
// reasonable number of ECDSA signature verifications.
复制
已复制
复制
已复制
int64
_t
nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
int64
nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
// Don't accept it if it can't get into a block
// Don't accept it if it can't get into a block
复制
已复制
复制
已复制
int64
_t
txMinFee = tx.GetMinFee(1000, false, GMF_RELAY, nSize);
int64
txMinFee = tx.GetMinFee(1000, false, GMF_RELAY, nSize);
if (nFees < txMinFee)
if (nFees < txMinFee)
复制
已复制
复制
已复制
return error("
AcceptToMemoryPool : not enough fees %s, %d < %d",
return error("
CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d,
has
hash.ToString().c_str(),
nFees, txMinFee);
// Continuously rate-limit free transactions
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
// be annoying or make others' transactions take longer to confirm.
if (nFees < MIN_RELAY_TX_FEE)
{
static CCriticalSection cs;
static double dFreeCount;
static int64 nLastTime;
int64 nNow = GetTime();
{
LOCK(cs);
// Use an exponentially decaying ~10-minute window:
dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
nLastTime = nNow;
// -limitfreerelay unit is thousand-bytes-per-minute
// At default rate it would take over a month to fill 1GB
if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(tx))
return error("CTxMemPool::accept() : free transaction rejected by rate limiter");
if (fDebug)
printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
dFreeCount += nSize;
}
}
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false))
{
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
}
}
// Store transaction in memory
{
LOCK(cs);
if (ptxOld)
{
printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str());
remove(*ptxOld);
}
addUnchecked(hash, tx);
}
///// are we sure this is ok when loading transactions or restoring block txes
// If updated, erase old tx from wallet
if (ptxOld)
EraseFromWallets(ptxOld->GetHash());
printf("CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n",
hash.ToString().substr(0,10).c_str(),
mapTx.size());
Text moved from lines 284-286
return true;
}
bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs)
{
return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs);
}
bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
{
// Add to memory pool without checking anything. Don't call this directly,
// call CTxMemPool::accept to properly check the transaction first.
{
mapTx[hash] = tx;
for (unsigned int i = 0; i < tx.vin.size(); i++)
mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i);
nTransactionsUpdated++;
}
return true;
Text moved with changes from lines 227-232 (84.2% similarity)
}
bool CTxMemPool::remove(CTransaction &tx)
{
// Remove transaction from memory pool
{
LOCK(cs);
uint256 hash = tx.GetHash();
if (mapTx.count(hash))
{
BOOST_FOREACH(const CTxIn& txin, tx.vin)
mapNextTx.erase(txin.prevout);
mapTx.erase(hash);
nTransactionsUpdated++;
}
}
return true;
}
void CTxMemPool::clear()
{
LOCK(cs);
mapTx.clear();
mapNextTx.clear();
++nTransactionsUpdated;
}
void CTxMemPool::queryHashes(std::vector<uint256>& vtxid)
{
vtxid.clear();
LOCK(cs);
vtxid.reserve(mapTx.size());
for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi)
vtxid.push_back((*mi).first);
}
Text moved from lines 376-379
int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
{
if (hashBlock == 0 || nIndex == -1)
return 0;
// Find the block it claims to be in
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
if (mi == mapBlockIndex.end())
return 0;
CBlockIndex* pindex = (*mi).second;
if (!pindex || !pindex->IsInMainChain())
return 0;
// Make sure the merkle branch connects to this block
if (!fMerkleVerified)
{
if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot)
return 0;
fMerkleVerified = true;
}
pindexRet = pindex;
return pindexBest->nHeight - pindex->nHeight + 1;
}
int CMerkleTx::GetBlocksToMaturity() const
{
if (!(IsCoinBase() || IsCoinStake()))
return 0;
return max(0, (nCoinbaseMaturity+20) - GetDepthInMainChain());
}
bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs)
{
if (fClient)
{
if (!IsInMainChain() && !ClientConnectInputs())
return false;
return CTransaction::AcceptToMemoryPool(txdb, false);
}
else
{
return CTransaction::AcceptToMemoryPool(txdb, fCheckInputs);
Text moved from lines 116-118
}
}
bool CMerkleTx::AcceptToMemoryPool()
{
CTxDB txdb("r");
retu
已保存差异
原始文本
打开文件
// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "alert.h" #include "checkpoints.h" #include "db.h" #include "txdb.h" #include "net.h" #include "init.h" #include "ui_interface.h" #include "kernel.h" #include "scrypt_mine.h" #include <boost/algorithm/string/replace.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> using namespace std; using namespace boost; // // Global state // CCriticalSection cs_setpwalletRegistered; set<CWallet*> setpwalletRegistered; CCriticalSection cs_main; CTxMemPool mempool; unsigned int nTransactionsUpdated = 0; map<uint256, CBlockIndex*> mapBlockIndex; set<pair<COutPoint, unsigned int> > setStakeSeen; uint256 hashGenesisBlock = hashGenesisBlockOfficial; static CBigNum bnProofOfWorkLimit(~uint256(0) >> 20); static CBigNum bnProofOfStakeLimit(~uint256(0) >> 24); static CBigNum bnProofOfStakeHardLimit(~uint256(0) >> 30); static CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 16); static CBigNum bnProofOfStakeLimitTestNet(~uint256(0) >> 20); unsigned int nStakeMinAge = 60 * 60 * 24 * 10; // minimum age for coin age - 10 days unsigned int nStakeMaxAge = 60 * 60 * 24 * 30; // stake age of full weight - 30 days unsigned int nStakeTargetSpacing = 1 * 30; // 1-minute block spacing int64_t nChainStartTime = 1371910049; int nCoinbaseMaturity = 5; CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; uint256 nBestChainTrust = 0; uint256 nBestInvalidTrust = 0; uint256 hashBestChain = 0; CBlockIndex* pindexBest = NULL; int64_t nTimeBestReceived = 0; bool fHaveGUI = false; bool fImporting = false; //Tranz need to fix this 66b02c93 bool fReindex = false; // Tranz need to fix this 7fea4846 CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have map<uint256, CBlock*> mapOrphanBlocks; multimap<uint256, CBlock*> mapOrphanBlocksByPrev; set<pair<COutPoint, unsigned int> > setStakeSeenOrphan; map<uint256, CTransaction> mapOrphanTransactions; map<uint256, set<uint256> > mapOrphanTransactionsByPrev; // Constant stuff for coinbase transactions we create: CScript COINBASE_FLAGS; const string strMessageMagic = "HoboNickels Signed Message:\n"; double dHashesPerSec; int64_t nHPSTimerStart; // Settings int64_t nTransactionFee = MIN_TX_FEE; int64_t nMinimumInputValue = MIN_TX_FEE; int64_t nSplitThreshold = GetProofOfWorkReward(); int64_t nCombineThreshold = GetProofOfWorkReward() * 2; extern enum Checkpoints::CPMode CheckpointsMode; ////////////////////////////////////////////////////////////////////////////// // // dispatching functions // // These functions dispatch to one or all registered wallets void RegisterWallet(CWallet* pwalletIn) { { LOCK(cs_setpwalletRegistered); setpwalletRegistered.insert(pwalletIn); } } void UnregisterWallet(CWallet* pwalletIn) { { LOCK(cs_setpwalletRegistered); setpwalletRegistered.erase(pwalletIn); } } void UnregisterAllWallets() { { LOCK(cs_setpwalletRegistered); setpwalletRegistered.clear(); } } // check whether the passed transaction is from us bool static IsFromMe(CTransaction& tx) { { LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) if (pwallet->IsFromMe(tx)) return true; } return false; } // get the wallet transaction with the given hash (if it exists) bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) { { LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) if (pwallet->GetTransaction(hashTx,wtx)) return true; return false; } } // make sure all wallets know about the given transaction, in the given block void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fConnect) { if (!fConnect) { // ppcoin: wallets need to refund inputs when disconnecting coinstake if (tx.IsCoinStake()) { LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) if (pwallet->IsFromMe(tx)) pwallet->DisableTransaction(tx); } return; } { LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); } } // Add wallet transactions that aren't already in a block to mapTransactions void ReacceptWalletTransactions() { { LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->ReacceptWalletTransactions(); } } // notify wallets about a new best chain void static SetBestChain(const CBlockLocator& loc) { { LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->SetBestChain(loc); } } // notify wallets about an updated transaction void static UpdatedTransaction(const uint256& hashTx) { { LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->UpdatedTransaction(hashTx); } } // dump all wallets void static PrintWallets(const CBlock& block) { { LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->PrintWallet(block); } } // notify wallets about an incoming inventory (for request counts) void static Inventory(const uint256& hash) { { LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->Inventory(hash); } } // ask wallets to resend their transactions void ResendWalletTransactions(bool fForce) { { LOCK(cs_setpwalletRegistered); BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->ResendWalletTransactions(fForce); } } ////////////////////////////////////////////////////////////////////////////// // // Registration of network node signals. // namespace { // Maintain validation-specific state about nodes, protected by cs_main, instead // by CNode's own locks. This simplifies asynchronous operation, where // processing of incoming data is done after the ProcessMessage call returns, // and we're no longer holding the node's locks. struct CNodeState { // Accumulated misbehaviour score for this peer. int nMisbehavior; // Whether this peer should be disconnected and banned. bool fShouldBan; // String name of this peer (debugging/logging purposes). std::string name; CNodeState() { nMisbehavior = 0; fShouldBan = false; } }; map<NodeId, CNodeState> mapNodeState; // Requires cs_main. (Tranz Locks removed for now due to deadlock) CNodeState *State(NodeId pnode) { map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode); if (it == mapNodeState.end()) return NULL; return &it->second; } int GetHeight() { return nBestHeight; } void InitializeNode(NodeId nodeid, const CNode *pnode) { CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second; state.name = pnode->addrName; } void FinalizeNode(NodeId nodeid) { mapNodeState.erase(nodeid); } } bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { CNodeState *state = State(nodeid); if (state == NULL) return false; stats.nMisbehavior = state->nMisbehavior; return true; } void RegisterNodeSignals(CNodeSignals& nodeSignals) { nodeSignals.GetHeight.connect(&GetHeight); nodeSignals.ProcessMessages.connect(&ProcessMessages); nodeSignals.SendMessages.connect(&SendMessages); nodeSignals.InitializeNode.connect(&InitializeNode); nodeSignals.FinalizeNode.connect(&FinalizeNode); } void UnregisterNodeSignals(CNodeSignals& nodeSignals) { nodeSignals.GetHeight.disconnect(&GetHeight); nodeSignals.ProcessMessages.disconnect(&ProcessMessages); nodeSignals.SendMessages.disconnect(&SendMessages); nodeSignals.InitializeNode.disconnect(&InitializeNode); nodeSignals.FinalizeNode.disconnect(&FinalizeNode); } ////////////////////////////////////////////////////////////////////////////// // // mapOrphanTransactions // bool AddOrphanTx(const CTransaction& tx) { uint256 hash = tx.GetHash(); if (mapOrphanTransactions.count(hash)) return false; // Ignore big transactions, to avoid a // send-big-orphans memory exhaustion attack. If a peer has a legitimate // large transaction with a missing parent then we assume // it will rebroadcast it later, after the parent transaction(s) // have been mined or received. // 10,000 orphans, each of which is at most 5,000 bytes big is // at most 500 megabytes of orphans: size_t nSize = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); if (nSize > 5000) { LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", nSize, hash.ToString().substr(0,10)); return false; } mapOrphanTransactions[hash] = tx; BOOST_FOREACH(const CTxIn& txin, tx.vin) mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash); LogPrint("mempool", "stored orphan tx %s (mapsz %u)\n", hash.ToString().substr(0,10), mapOrphanTransactions.size()); return true; } void static EraseOrphanTx(uint256 hash) { if (!mapOrphanTransactions.count(hash)) return; const CTransaction& tx = mapOrphanTransactions[hash]; BOOST_FOREACH(const CTxIn& txin, tx.vin) { mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash); if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty()) mapOrphanTransactionsByPrev.erase(txin.prevout.hash); } mapOrphanTransactions.erase(hash); } unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) { unsigned int nEvicted = 0; while (mapOrphanTransactions.size() > nMaxOrphans) { // Evict a random orphan: uint256 randomhash = GetRandHash(); map<uint256, CTransaction>::iterator it = mapOrphanTransactions.lower_bound(randomhash); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); EraseOrphanTx(it->first); ++nEvicted; } return nEvicted; } ////////////////////////////////////////////////////////////////////////////// // // CTransaction and CTxIndex // bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet) { SetNull(); if (!txdb.ReadTxIndex(prevout.hash, txindexRet)) return false; if (!ReadFromDisk(txindexRet.pos)) return false; if (prevout.n >= vout.size()) { SetNull(); return false; } return true; } bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout) { CTxIndex txindex; return ReadFromDisk(txdb, prevout, txindex); } bool CTransaction::ReadFromDisk(COutPoint prevout) { CTxDB txdb("r"); CTxIndex txindex; return ReadFromDisk(txdb, prevout, txindex); } bool IsStandardTx(const CTransaction& tx) { if (tx.nVersion > CTransaction::CURRENT_VERSION) return false; // Treat non-final transactions as non-standard to prevent a specific type // of double-spend attack, as well as DoS attacks. (if the transaction // can't be mined, the attacker isn't expending resources broadcasting it) // Basically we don't want to propagate transactions that can't included in // the next block. // // However, IsFinalTx() is confusing... Without arguments, it uses // chainActive.Height() to evaluate nLockTime; when a block is accepted, chainActive.Height() // is set to the value of nHeight in the block. However, when IsFinalTx() // is called within CBlock::AcceptBlock(), the height of the block *being* // evaluated is what is used. Thus if we want to know if a transaction can // be part of the *next* block, we need to call IsFinalTx() with one more // than chainActive.Height(). // // Timestamps on the other hand don't get any special treatment, because we // can't know what timestamp the next block will have, and there aren't // timestamp applications where it matters. if (!IsFinalTx(tx, nBestHeight + 1)) { return false; } // nTime has different purpose from nLockTime but can be used in similar attacks if (tx.nTime > FutureDrift(GetAdjustedTime())) { return false; } // Extremely large transactions with lots of inputs can cost the network // almost as much to process as they cost the sender in fees, because // computing signature hashes is O(ninputs*txsize). Limiting transactions // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); if (sz >= MAX_STANDARD_TX_SIZE) return false; BOOST_FOREACH(const CTxIn& txin, tx.vin) { // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG // pay-to-script-hash, which is 3 ~80-byte signatures, 3 // ~65-byte public keys, plus a few script ops. if (txin.scriptSig.size() > 500) return false; if (!txin.scriptSig.IsPushOnly()) return false; if (!txin.scriptSig.HasCanonicalPushes()) return false; } unsigned int nDataOut = 0; txnouttype whichType; BOOST_FOREACH(const CTxOut& txout, tx.vout) { if (!::IsStandard(txout.scriptPubKey, whichType)) return false; if (whichType == TX_NULL_DATA) nDataOut++; if (txout.nValue == 0) return false; if (!txout.scriptPubKey.HasCanonicalPushes()) return false; } // only one OP_RETURN txout is permitted if (nDataOut > 1) { return false; } return true; } bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { AssertLockHeld(cs_main); // Time based nLockTime implemented in 0.1.6 if (tx.nLockTime == 0) return true; if (nBlockHeight == 0) nBlockHeight = nBestHeight; if (nBlockTime == 0) nBlockTime = GetAdjustedTime(); if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; BOOST_FOREACH(const CTxIn& txin, tx.vin) if (!txin.IsFinal()) return false; return true; } // // Check transaction inputs, and make sure any // pay-to-script-hash transactions are evaluating IsStandard scripts // // Why bother? To avoid denial-of-service attacks; an attacker // can submit a standard HASH... OP_EQUAL transaction, // which will get accepted into blocks. The redemption // script can be anything; an attacker could use a very // expensive-to-check-upon-redemption script like: // DUP CHECKSIG DROP ... repeated 100 times... OP_1 // bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const { if (IsCoinBase()) return true; // Coinbases don't use vin normally for (unsigned int i = 0; i < vin.size(); i++) { const CTxOut& prev = GetOutputFor(vin[i], mapInputs); vector<vector<unsigned char> > vSolutions; txnouttype whichType; // get the scriptPubKey corresponding to this input: const CScript& prevScript = prev.scriptPubKey; if (!Solver(prevScript, whichType, vSolutions)) return false; int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); if (nArgsExpected < 0) return false; // Transactions with extra stuff in their scriptSigs are // non-standard. Note that this EvalScript() call will // be quick, because if there are any operations // beside "push data" in the scriptSig the // IsStandard() call returns false vector<vector<unsigned char> > stack; if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0)) return false; if (whichType == TX_SCRIPTHASH) { if (stack.empty()) return false; CScript subscript(stack.back().begin(), stack.back().end()); vector<vector<unsigned char> > vSolutions2; txnouttype whichType2; if (!Solver(subscript, whichType2, vSolutions2)) return false; if (whichType2 == TX_SCRIPTHASH) return false; int tmpExpected; tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); if (tmpExpected < 0) return false; nArgsExpected += tmpExpected; } if (stack.size() != (unsigned int)nArgsExpected) return false; } return true; } unsigned int CTransaction::GetLegacySigOpCount() const { unsigned int nSigOps = 0; BOOST_FOREACH(const CTxIn& txin, vin) { nSigOps += txin.scriptSig.GetSigOpCount(false); } BOOST_FOREACH(const CTxOut& txout, vout) { nSigOps += txout.scriptPubKey.GetSigOpCount(false); } return nSigOps; } int CMerkleTx::SetMerkleBranch(const CBlock* pblock) { AssertLockHeld(cs_main); CBlock blockTmp; if (pblock == NULL) { // Load the block this tx is in CTxIndex txindex; if (!CTxDB("r").ReadTxIndex(GetHash(), txindex)) return 0; if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos)) return 0; pblock = &blockTmp; } // Update the tx's hashBlock hashBlock = pblock->GetHash(); // Locate the transaction for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++) if (pblock->vtx[nIndex] == *(CTransaction*)this) break; if (nIndex == (int)pblock->vtx.size()) { vMerkleBranch.clear(); nIndex = -1; LogPrintf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); return 0; } // Fill in merkle branch vMerkleBranch = pblock->GetMerkleBranch(nIndex); // Is the tx in a block that's in the main chain map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; if (!pindex || !pindex->IsInMainChain()) return 0; return pindexBest->nHeight - pindex->nHeight + 1; } bool CTransaction::CheckTransaction() const { // Basic checks that don't depend on any context if (vin.empty()) return DoS(10, error("CTransaction::CheckTransaction() : vin empty")); if (vout.empty()) return DoS(10, error("CTransaction::CheckTransaction() : vout empty")); // Time (prevent mempool memory exhaustion attack) if (nTime > GetAdjustedTime() + nMaxClockDrift) return DoS(10, error("CTransaction::CheckTransaction() : timestamp is too far into the future")); // Size limits if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); // Check for negative or overflow output values int64_t nValueOut = 0; for (unsigned int i = 0; i < vout.size(); i++) { const CTxOut& txout = vout[i]; if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake()) return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction")); // ppcoin: enforce minimum output amount if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT) return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum")); if (txout.nValue < 0) return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue is negative")); if (txout.nValue > MAX_MONEY) return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); } // Check for duplicate inputs set<COutPoint> vInOutPoints; BOOST_FOREACH(const CTxIn& txin, vin) { if (vInOutPoints.count(txin.prevout)) return false; vInOutPoints.insert(txin.prevout); } if (IsCoinBase()) { if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); } else { BOOST_FOREACH(const CTxIn& txin, vin) if (txin.prevout.IsNull()) return DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); } return true; } int64_t CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, enum GetMinFee_mode mode, unsigned int nBytes) const { // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE int64_t nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE; unsigned int nNewBlockSize = nBlockSize + nBytes; int64_t nMinFee = (1 + (int64_t)nBytes / 1000) * nBaseFee; // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01 if (nMinFee < nBaseFee) { BOOST_FOREACH(const CTxOut& txout, vout) if (txout.nValue < CENT) nMinFee = nBaseFee; } // Raise the price as the block approaches full if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) { if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) return MAX_MONEY; nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); } if (!MoneyRange(nMinFee)) nMinFee = MAX_MONEY; return nMinFee; } bool AcceptToMemoryPool(CTxMemPool& pool, CTransaction &tx, bool* pfMissingInputs) { AssertLockHeld(cs_main); if (pfMissingInputs) *pfMissingInputs = false; if (!tx.CheckTransaction()) return error("AcceptToMemoryPool : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) return tx.DoS(100, error("AcceptToMemoryPool : coinbase as individual tx")); // ppcoin: coinstake is also only valid in a block, not as a loose transaction if (tx.IsCoinStake()) return tx.DoS(100, error("AcceptToMemoryPool : coinstake as individual tx")); // Rather not work on nonstandard transactions (unless -testnet) if (!fTestNet && !IsStandardTx(tx)) return error("AcceptToMemoryPool : nonstandard transaction type"); // is it already in the memory pool? uint256 hash = tx.GetHash(); if (pool.exists(hash)) return false; // Check for conflicts with in-memory transactions { LOCK(pool.cs); // protect pool.mapNextTx for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint outpoint = tx.vin[i].prevout; if (pool.mapNextTx.count(outpoint)) { // Disable replacement feature for now return false; } } } { CTxDB txdb("r"); // do we already have it? if (txdb.ContainsTx(hash)) return false; MapPrevTx mapInputs; map<uint256, CTxIndex> mapUnused; bool fInvalid = false; if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) { if (fInvalid) return error("AcceptToMemoryPool : FetchInputs found invalid tx %s", hash.ToString().substr(0,10)); if (pfMissingInputs) *pfMissingInputs = true; return false; } // Check for non-standard pay-to-script-hash in inputs if (!tx.AreInputsStandard(mapInputs) && !fTestNet) return error("AcceptToMemoryPool : nonstandard transaction input"); // Note: if you modify this code to accept non-standard transactions, then // you should add code here to check that the transaction does a // reasonable number of ECDSA signature verifications. int64_t nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); // Don't accept it if it can't get into a block int64_t txMinFee = tx.GetMinFee(1000, false, GMF_RELAY, nSize); if (nFees < txMinFee) return error("AcceptToMemoryPool : not enough fees %s, %d < %d", hash.ToString(), nFees, txMinFee); // Continuously rate-limit free transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. if (nFees < MIN_RELAY_TX_FEE) { static CCriticalSection cs; static double dFreeCount; static int64_t nLastTime; int64_t nNow = GetTime(); { LOCK(pool.cs); // Use an exponentially decaying ~10-minute window: dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); nLastTime = nNow; // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(tx)) return error("AcceptToMemoryPool : free transaction rejected by rate limiter"); if (fDebug) LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } } // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) { return error("AcceptToMemoryPool : ConnectInputs failed %s", hash.ToString().substr(0,10)); } } // Store transaction in memory pool.addUnchecked(hash, tx); SyncWithWallets(tx, NULL, true); LogPrint("mempool", "AcceptToMemoryPool : accepted %s (poolsz %u)\n", hash.ToString().substr(0,10), pool.mapTx.size()); return true; } bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) { // Add to memory pool without checking anything. // Used by main.cpp AcceptToMemoryPool(), which DOES do // all the appropriate checks. LOCK(cs); { mapTx[hash] = tx; for (unsigned int i = 0; i < tx.vin.size(); i++) mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); nTransactionsUpdated++; } return true; } bool CTxMemPool::remove(CTransaction &tx) { // Remove transaction from memory pool { LOCK(cs); uint256 hash = tx.GetHash(); if (mapTx.count(hash)) { BOOST_FOREACH(const CTxIn& txin, tx.vin) mapNextTx.erase(txin.prevout); mapTx.erase(hash); nTransactionsUpdated++; } } return true; } void CTxMemPool::clear() { LOCK(cs); mapTx.clear(); mapNextTx.clear(); ++nTransactionsUpdated; } void CTxMemPool::queryHashes(std::vector<uint256>& vtxid) { vtxid.clear(); LOCK(cs); vtxid.reserve(mapTx.size()); for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) vtxid.push_back((*mi).first); } int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const { if (hashBlock == 0 || nIndex == -1) return 0; AssertLockHeld(cs_main); // Find the block it claims to be in map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; if (!pindex || !pindex->IsInMainChain()) return 0; // Make sure the merkle branch connects to this block if (!fMerkleVerified) { if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) return 0; fMerkleVerified = true; } pindexRet = pindex; return pindexBest->nHeight - pindex->nHeight + 1; } int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const { AssertLockHeld(cs_main); int nResult = GetDepthInMainChainINTERNAL(pindexRet); if (nResult == 0 && !mempool.exists(GetHash())) return -1; // Not in chain, not in mempool return nResult; } int CMerkleTx::GetBlocksToMaturity() const { if (!(IsCoinBase() || IsCoinStake())) return 0; return max(0, (nCoinbaseMaturity+20) - GetDepthInMainChain()); } bool CMerkleTx::AcceptToMemoryPool() { return ::AcceptToMemoryPool(mempool, *this, NULL); } bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb) { { // Add previous supporting transactions first BOOST_FOREACH(CMerkleTx& tx, vtxPrev) { if (!(tx.IsCoinBase() || tx.IsCoinStake())) { uint256 hash = tx.GetHash(); if (!mempool.exists(hash) && !txdb.ContainsTx(hash)) tx.AcceptToMemoryPool(); } } return AcceptToMemoryPool(); } return false; } bool CWalletTx::AcceptWalletTransaction() { CTxDB txdb("r"); return AcceptWalletTransaction(txdb); } int CTxIndex::GetDepthInMainChain() const { // Read block header CBlock block; if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false)) return 0; // Find the block in the index map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.GetHash()); if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; if (!pindex || !pindex->IsInMainChain()) return 0; return 1 + nBestHeight - pindex->nHeight; } // Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock) { { LOCK(cs_main); { if (mempool.lookup(hash, tx)) return true; } CTxDB txdb("r"); CTxIndex txindex; if (tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex)) { CBlock block; if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) hashBlock = block.GetHash(); return true; } } return false; } ////////////////////////////////////////////////////////////////////////////// // // CBlock and CBlockIndex // static CBlockIndex* pblockindexFBBHLast; CBlockIndex* FindBlockByHeight(int nHeight) { CBlockIndex *pblockindex; if (nHeight < nBestHeight / 2) pblockindex = pindexGenesisBlock; else pblockindex = pindexBest; if (pblockindexFBBHLast && abs(nHeight - pblockindex->nHeight) > abs(nHeight - pblockindexFBBHLast->nHeight)) pblockindex = pblockindexFBBHLast; while (pblockindex->nHeight > nHeight) pblockindex = pblockindex->pprev; while (pblockindex->nHeight < nHeight) pblockindex = pblockindex->pnext; pblockindexFBBHLast = pblockindex; return pblockindex; } bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions) { if (!fReadTransactions) { *this = pindex->GetBlockHeader(); return true; } if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions)) return false; if (GetHash() != pindex->GetBlockHash()) return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); return true; } uint256 static GetOrphanRoot(const CBlock* pblock) { // Work back to the first block in the orphan chain while (mapOrphanBlocks.count(pblock->hashPrevBlock)) pblock = mapOrphanBlocks[pblock->hashPrevBlock]; return pblock->GetHash(); } // ppcoin: find block wanted by given orphan block uint256 WantedByOrphan(const CBlock* pblockOrphan) { // Work back to the first block in the orphan chain while (mapOrphanBlocks.count(pblockOrphan->hashPrevBlock)) pblockOrphan = mapOrphanBlocks[pblockOrphan->hashPrevBlock]; return pblockOrphan->hashPrevBlock; } int64_t GetProofOfWorkReward() { int64_t nSubsidy = 5 * COIN; return nSubsidy; } // miner's coin stake reward based on nBits and coin age spent (coin-days) int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, unsigned int nTime, bool bCoinYearOnly) { int64_t nSubsidy = 0; if ( nTime > VERSION1_5_SWITCH_TIME ) nSubsidy = GetProofOfStakeRewardV2(nCoinAge, nBits, nTime, bCoinYearOnly); else nSubsidy = GetProofOfStakeRewardV1(nCoinAge, nBits, nTime, bCoinYearOnly); return nSubsidy; } int64_t GetProofOfStakeRewardV1(int64_t nCoinAge, unsigned int nBits, unsigned int nTime, bool bCoinYearOnly) { int64_t nRewardCoinYear; CBigNum bnRewardCoinYearLimit; int64_t nRewardCoinYearLimit; if(fTestNet || nTime > PROTOCOL_SWITCH_TIME) { // Stage 2 of emission process is PoS-based. It will be active on mainNet since 20 Jun 2013. if(fTestNet || nTime > POS_REWARD_FIX_TIME2) { bnRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE_FIX2; // Correct Base stake mint rate, 100% year interest nRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE_FIX2; } else if (fTestNet || nTime > POS_REWARD_FIX_TIME) { bnRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE_FIX; // Incorrect Base stake mint rate, 1000% year interest nRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE_FIX; } else { bnRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE; // Incorrect Base stake mint rate, 10% year interest nRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE; } CBigNum bnTarget; bnTarget.SetCompact(nBits); CBigNum bnTargetLimit = bnProofOfStakeLimit; bnTargetLimit.SetCompact(bnTargetLimit.GetCompact()); // HoboNickels: reward for coin-year is cut in half every 64x multiply of PoS difficulty // A reasonably continuous curve is used to avoid shock to market // (bnRewardCoinYearLimit / nRewardCoinYear) ** 4 == bnProofOfStakeLimit / bnTarget // // Human readable form: // // nRewardCoinYear = 1 / (posdiff ^ 1/4) CBigNum bnLowerBound; bnLowerBound = 10 * CENT; // Lower interest bound is 1% per year CBigNum bnUpperBound = bnRewardCoinYearLimit; while (bnLowerBound + CENT <= bnUpperBound) { CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; LogPrint("creation", "GetProofOfStakeReward() : lower=%d upper=%d mid=%d\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64()); if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnTarget) bnUpperBound = bnMidValue; else bnLowerBound = bnMidValue; } nRewardCoinYear = bnUpperBound.getuint64(); if (nTime > POS_REWARD_SWITCH_TIME) nRewardCoinYear = min(nRewardCoinYear, nRewardCoinYearLimit); else nRewardCoinYear = min((nRewardCoinYear / CENT) * CENT, nRewardCoinYearLimit); } else { // Old creation amount per coin-year, 5% fixed stake mint rate nRewardCoinYear = 0.015 * CENT; } if(bCoinYearOnly) return nRewardCoinYear; // Stake calculation fix for small tx values courtesy of Mineral And Yukon Coinelius. This will fix the rounding of small stake rewards to zero int64_t nSubsidy = nRewardCoinYear * nCoinAge * 33 / (365 * 33 + 8); if (nTime > POS_REWARD_SWITCH_TIME) nSubsidy = (nCoinAge * 33 * nRewardCoinYear) / (365 * 33 + 8); else nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear; LogPrint("creation","GetProofOfStakeReward(): create=%s nCoinAge=%d nBits=%d\n", FormatMoney(nSubsidy), nCoinAge, nBits); return nSubsidy; } int64_t GetProofOfStakeRewardV2(int64_t nCoinAge, unsigned int nBits, unsigned int nTime, bool bCoinYearOnly) { CBigNum bnRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE_FIX2; // Base stake mint rate, 100% year interest int64_t nRewardCoinYearLimit = MAX_MINT_PROOF_OF_STAKE_FIX2; CBigNum bnTarget; bnTarget.SetCompact(nBits); CBigNum bnTargetLimit = bnProofOfStakeLimit; bnTargetLimit.SetCompact(bnTargetLimit.GetCompact()); int64_t nSubsidyLimit = 250 * COIN; // HoboNickels: reward for coin-year is cut in half every 64x multiply of PoS difficulty // A reasonably continuous curve is used to avoid shock to market // (bnRewardCoinYearLimit / nRewardCoinYear) ** 4 == bnProofOfStakeLimit / bnTarget // // Human readable form: // // nRewardCoinYear = 1 / (posdiff ^ 1/4) CBigNum bnLowerBound = 10 * CENT; // Lower interest bound is 10% per year CBigNum bnUpperBound = bnRewardCoinYearLimit; while (bnLowerBound + CENT <= bnUpperBound) { CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; LogPrint("creation", "GetProofOfStakeReward() : lower=%d upper=%d mid=%d\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64()); if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnRewardCoinYearLimit * bnTarget) bnUpperBound = bnMidValue; else bnLowerBound = bnMidValue; } int64_t nRewardCoinYear = bnUpperBound.getuint64(); nRewardCoinYear = min((nRewardCoinYear / CENT) * CENT, nRewardCoinYearLimit); if(bCoinYearOnly) return nRewardCoinYear; int64_t nSubsidy = (nCoinAge * 33 * nRewardCoinYear) / (365 * 33 + 8); LogPrint("creation","GetProofOfStakeReward(): create=%s nCoinAge=%d nBits=%d, Amount Truncated %s\n", FormatMoney(nSubsidy), nCoinAge, nBits, nSubsidyLimit < nSubsidy ? FormatMoney(nSubsidy - nSubsidyLimit) : FormatMoney(0)); nSubsidy = min(nSubsidy, nSubsidyLimit); return nSubsidy; } static const int64_t nTargetTimespan = 0.16 * 24 * 60 * 60; // 4-hour static const int64_t nTargetSpacingWorkMax = 12 * nStakeTargetSpacing; // 2-hour // maximum nBits value could possible be required nTime after // unsigned int ComputeMaxBits(CBigNum bnTargetLimit, unsigned int nBase, int64_t nTime) { CBigNum bnResult; bnResult.SetCompact(nBase); bnResult *= 2; while (nTime > 0 && bnResult < bnTargetLimit) { // Maximum 200% adjustment per day... bnResult *= 2; nTime -= 24 * 60 * 60; } if (bnResult > bnTargetLimit) bnResult = bnTargetLimit; return bnResult.GetCompact(); } // // minimum amount of work that could possibly be required nTime after // minimum proof-of-work required was nBase // unsigned int ComputeMinWork(unsigned int nBase, int64_t nTime) { return ComputeMaxBits(bnProofOfWorkLimit, nBase, nTime); } // // minimum amount of stake that could possibly be required nTime after // minimum proof-of-stake required was nBase // unsigned int ComputeMinStake(unsigned int nBase, int64_t nTime, unsigned int nBlockTime) { return ComputeMaxBits(bnProofOfStakeLimit, nBase, nTime); } // ppcoin: find last block index up to pindex const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake) { while (pindex && pindex->pprev && (pindex->IsProofOfStake() != fProofOfStake)) pindex = pindex->pprev; return pindex; } unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake) { if (pindexLast->nHeight + 1 > VERSION1_5_SWITCH_BLOCK) return GetNextTargetRequiredV2(pindexLast, fProofOfStake); else return GetNextTargetRequiredV1(pindexLast, fProofOfStake); } unsigned int GetNextTargetRequiredV1(const CBlockIndex* pindexLast, bool fProofOfStake) { CBigNum bnTargetLimit = !fProofOfStake ? bnProofOfWorkLimit : bnProofOfStakeLimit; if(fProofOfStake) { // Proof-of-Stake blocks has own target limit since nVersion=3 supermajority on mainNet and always on testNet if(fTestNet) bnTargetLimit = bnProofOfStakeLimit; else { if(pindexLast->nHeight + 1 > 15000) bnTargetLimit = bnProofOfStakeLimit; else if(pindexLast->nHeight + 1 > 14060) bnTargetLimit = bnProofOfStakeHardLimit; } } if (pindexLast == NULL) return bnTargetLimit.GetCompact(); // genesis block const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake); if (pindexPrev->pprev == NULL) return bnTargetLimit.GetCompact(); // first block const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake); if (pindexPrevPrev->pprev == NULL) return bnTargetLimit.GetCompact(); // second block int64_t nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); // ppcoin: target change every block // ppcoin: retarget with exponential moving toward target spacing CBigNum bnNew; bnNew.SetCompact(pindexPrev->nBits); int64_t nTargetSpacing = fProofOfStake? nStakeTargetSpacing : min(nTargetSpacingWorkMax, (int64_t) nStakeTargetSpacing * (1 + pindexLast->nHeight - pindexPrev->nHeight)); int64_t nInterval = nTargetTimespan / nTargetSpacing; bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing); bnNew /= ((nInterval + 1) * nTargetSpacing); if (bnNew > bnTargetLimit) bnNew = bnTargetLimit; return bnNew.GetCompact(); } unsigned int GetNextTargetRequiredV2(const CBlockIndex* pindexLast, bool fProofOfStake) { CBigNum bnTargetLimit = !fProofOfStake ? bnProofOfWorkLimit : bnProofOfStakeLimit; if (pindexLast == NULL) return bnTargetLimit.GetCompact(); // genesis block const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake); if (pindexPrev->pprev == NULL) return bnTargetLimit.GetCompact(); // first block const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake); if (pindexPrevPrev->pprev == NULL) return bnTargetLimit.GetCompact(); // second block int64_t nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); int64_t nTargetSpacing = fProofOfStake? nStakeTargetSpacing : min(nTargetSpacingWorkMax, (int64_t) nStakeTargetSpacing * (1 + pindexLast->nHeight - pindexPrev->nHeight)); if (nActualSpacing < 0) nActualSpacing = nTargetSpacing; CBigNum bnNew; bnNew.SetCompact(pindexPrev->nBits); int64_t nInterval = nTargetTimespan / nTargetSpacing; bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing); bnNew /= ((nInterval + 1) * nTargetSpacing); if (bnNew > bnTargetLimit) bnNew = bnTargetLimit; return bnNew.GetCompact(); } bool CheckProofOfWork(uint256 hash, unsigned int nBits) { CBigNum bnTarget; bnTarget.SetCompact(nBits); // Check range if (bnTarget <= 0 || bnTarget > bnProofOfWorkLimit) return error("CheckProofOfWork() : nBits below minimum work"); // Check proof of work matches claimed amount if (hash > bnTarget.getuint256()) return error("CheckProofOfWork() : hash doesn't match nBits"); return true; } // Return maximum amount of blocks that other nodes claim to have int GetNumBlocksOfPeers() { return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate()); } bool IsInitialBlockDownload() { LOCK(cs_main); if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) return true; static int64_t nLastUpdate; static CBlockIndex* pindexLastBest; if (pindexBest != pindexLastBest) { pindexLastBest = pindexBest; nLastUpdate = GetTime(); } return (GetTime() - nLastUpdate < 10 && pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); } void static InvalidChainFound(CBlockIndex* pindexNew) { if (pindexNew->nChainTrust > nBestInvalidTrust) { nBestInvalidTrust = pindexNew->nChainTrust; CTxDB().WriteBestInvalidTrust(CBigNum(nBestInvalidTrust)); uiInterface.NotifyBlocksChanged(); } uint256 nBestInvalidBlockTrust = pindexNew->nChainTrust - pindexNew->pprev->nChainTrust; uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust; LogPrintf("InvalidChainFound: invalid block=%s height=%d trust=%s blocktrust=%d date=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20), pindexNew->nHeight, CBigNum(pindexNew->nChainTrust).ToString(), nBestInvalidBlockTrust.Get64(), DateTimeStrFormat("%x %H:%M:%S",pindexNew->GetBlockTime())); LogPrintf("InvalidChainFound: current best=%s height=%d trust=%s blocktrust=%d date=%s\n", hashBestChain.ToString().substr(0,20), nBestHeight, CBigNum(pindexBest->nChainTrust).ToString(), nBestBlockTrust.Get64(), DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime())); } void CBlock::UpdateTime(const CBlockIndex* pindexPrev) { nTime = max(GetBlockTime(), GetAdjustedTime()); } bool CTransaction::DisconnectInputs(CTxDB& txdb) { // Relinquish previous transactions' spent pointers if (!IsCoinBase()) { BOOST_FOREACH(const CTxIn& txin, vin) { COutPoint prevout = txin.prevout; // Get prev txindex from disk CTxIndex txindex; if (!txdb.ReadTxIndex(prevout.hash, txindex)) return error("DisconnectInputs() : ReadTxIndex failed"); if (prevout.n >= txindex.vSpent.size()) return error("DisconnectInputs() : prevout.n out of range"); // Mark outpoint as not spent txindex.vSpent[prevout.n].SetNull(); // Write back if (!txdb.UpdateTxIndex(prevout.hash, txindex)) return error("DisconnectInputs() : UpdateTxIndex failed"); } } // Remove transaction from index // This can fail if a duplicate of this transaction was in a chain that got // reorganized away. This is only possible if this transaction was completely // spent, so erasing it would be a no-op anyway. txdb.EraseTxIndex(*this); return true; } bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool, bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid) { // FetchInputs can return false either because we just haven't seen some inputs // (in which case the transaction should be stored as an orphan) // or because the transaction is malformed (in which case the transaction should // be dropped). If tx is definitely invalid, fInvalid will be set to true. fInvalid = false; if (IsCoinBase()) return true; // Coinbase transactions have no inputs to fetch. for (unsigned int i = 0; i < vin.size(); i++) { COutPoint prevout = vin[i].prevout; if (inputsRet.count(prevout.hash)) continue; // Got it already // Read txindex CTxIndex& txindex = inputsRet[prevout.hash].first; bool fFound = true; if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) { // Get txindex from current proposed changes txindex = mapTestPool.find(prevout.hash)->second; } else { // Read txindex from txdb fFound = txdb.ReadTxIndex(prevout.hash, txindex); } if (!fFound && (fBlock || fMiner)) return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10), prevout.hash.ToString().substr(0,10)); // Read txPrev CTransaction& txPrev = inputsRet[prevout.hash].second; if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) { // Get prev tx from single transactions in memory if (!mempool.lookup(prevout.hash, txPrev)) return error("FetchInputs() : %s mempool Tx prev not found %s", GetHash().ToString().substr(0,10), prevout.hash.ToString().substr(0,10)); if (!fFound) txindex.vSpent.resize(txPrev.vout.size()); } else { // Get prev tx from disk if (!txPrev.ReadFromDisk(txindex.pos)) return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10), prevout.hash.ToString().substr(0,10)); } } // Make sure all prevout.n indexes are valid: for (unsigned int i = 0; i < vin.size(); i++) { const COutPoint prevout = vin[i].prevout; assert(inputsRet.count(prevout.hash) != 0); const CTxIndex& txindex = inputsRet[prevout.hash].first; const CTransaction& txPrev = inputsRet[prevout.hash].second; if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) { // Revisit this if/when transaction replacement is implemented and allows // adding inputs: fInvalid = true; return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %u %u prev tx %s\n%s", GetHash().ToString().substr(0,10), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10), txPrev.ToString())); } } return true; } const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const { MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash); if (mi == inputs.end()) throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found"); const CTransaction& txPrev = (mi->second).second; if (input.prevout.n >= txPrev.vout.size()) throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range"); return txPrev.vout[input.prevout.n]; } int64_t CTransaction::GetValueIn(const MapPrevTx& inputs) const { if (IsCoinBase()) return 0; int64_t nResult = 0; for (unsigned int i = 0; i < vin.size(); i++) { nResult += GetOutputFor(vin[i], inputs).nValue; } return nResult; } unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const { if (IsCoinBase()) return 0; unsigned int nSigOps = 0; for (unsigned int i = 0; i < vin.size(); i++) { const CTxOut& prevout = GetOutputFor(vin[i], inputs); if (prevout.scriptPubKey.IsPayToScriptHash()) nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); } return nSigOps; } bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx, const CBlockIndex* pindexBlock, bool fBlock, bool fMiner) { // Take over previous transactions' spent pointers // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain // fMiner is true when called from the internal bitcoin miner // ... both are false when called from CTransaction::AcceptToMemoryPool if (!IsCoinBase()) { int64_t nValueIn = 0; int64_t nFees = 0; for (unsigned int i = 0; i < vin.size(); i++) { COutPoint prevout = vin[i].prevout; assert(inputs.count(prevout.hash) > 0); CTxIndex& txindex = inputs[prevout.hash].first; CTransaction& txPrev = inputs[prevout.hash].second; if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %u %u prev tx %s\n%s", GetHash().ToString().substr(0,10), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10), txPrev.ToString())); // If prev is coinbase or coinstake, check that it's matured if (txPrev.IsCoinBase() || txPrev.IsCoinStake()) for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < nCoinbaseMaturity; pindex = pindex->pprev) if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) return error("ConnectInputs() : tried to spend %s at depth %d", txPrev.IsCoinBase() ? "coinbase" : "coinstake", pindexBlock->nHeight - pindex->nHeight); // ppcoin: check transaction timestamp if (txPrev.nTime > nTime) return DoS(100, error("ConnectInputs() : transaction timestamp earlier than input transaction")); // Check for negative or overflow input values nValueIn += txPrev.vout[prevout.n].nValue; if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) return DoS(100, error("ConnectInputs() : txin values out of range")); } // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. // Helps prevent CPU exhaustion attacks. for (unsigned int i = 0; i < vin.size(); i++) { COutPoint prevout = vin[i].prevout; assert(inputs.count(prevout.hash) > 0); CTxIndex& txindex = inputs[prevout.hash].first; CTransaction& txPrev = inputs[prevout.hash].second; // Check for conflicts (double-spend) // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!txindex.vSpent[prevout.n].IsNull()) return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10), txindex.vSpent[prevout.n].ToString()); // Skip ECDSA signature verification when connecting blocks (fBlock=true) // before the last blockchain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate()))) { // Verify signature if (!VerifySignature(txPrev, *this, i, 0)) return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10))); } // Mark outpoints as spent txindex.vSpent[prevout.n] = posThisTx; // Write back if (fBlock || fMiner) { mapTestPool[prevout.hash] = txindex; } } if (IsCoinStake()) { // ppcoin: coin stake tx earns reward instead of paying fee uint64_t nCoinAge; if (!GetCoinAge(txdb, nCoinAge)) return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10)); int64_t nStakeReward = GetValueOut() - nValueIn; int64_t nCalculatedStakeReward = GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime) - GetMinFee() + MIN_TX_FEE; if (nStakeReward > nCalculatedStakeReward) return DoS(100, error("ConnectInputs() : coinstake pays too much(actual=%d vs calculated=%d)", nStakeReward, nCalculatedStakeReward)); } else { if (nValueIn < GetValueOut()) return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10))); // Tally transaction fees int64_t nTxFee = nValueIn - GetValueOut(); if (nTxFee < 0) return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10))); // ppcoin: enforce transaction fees for every block if (nTxFee < GetMinFee()) return fBlock? DoS(100, error("ConnectInputs() : %s not paying required fee=%s, paid=%s", GetHash().ToString().substr(0,10), FormatMoney(GetMinFee()), FormatMoney(nTxFee))) : false; nFees += nTxFee; if (!MoneyRange(nFees)) return DoS(100, error("ConnectInputs() : nFees out of range")); } } return true; } bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) { // Disconnect in reverse order for (int i = vtx.size()-1; i >= 0; i--) if (!vtx[i].DisconnectInputs(txdb)) return false; // Update block index on disk without changing it in memory. // The memory index structure will be changed after the db commits. if (pindex->pprev) { CDiskBlockIndex blockindexPrev(pindex->pprev); blockindexPrev.hashNext = 0; if (!txdb.WriteBlockIndex(blockindexPrev)) return error("DisconnectBlock() : WriteBlockIndex failed"); } // ppcoin: clean up wallet after disconnecting coinstake BOOST_FOREACH(CTransaction& tx, vtx) SyncWithWallets(tx, this, false, false); return true; } bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) { // Check it again in case a previous version let a bad block in, but skip BlockSig checking if (!CheckBlock(!fJustCheck, !fJustCheck, false)) return false; //// issue here: it doesn't know the version unsigned int nTxPos; if (fJustCheck) // FetchInputs treats CDiskTxPos(1,1,1) as a special "refer to memorypool" indicator // Since we're just checking the block and not actually connecting it, it might not (and probably shouldn't) be on the disk to get the transaction from nTxPos = 1; else nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size()); map<uint256, CTxIndex> mapQueuedChanges; int64_t nFees = 0; int64_t nValueIn = 0; int64_t nValueOut = 0; unsigned int nSigOps = 0; BOOST_FOREACH(CTransaction& tx, vtx) { uint256 hashTx = tx.GetHash(); // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. // If such overwrites are allowed, coinbases and transactions depending upon those // can be duplicated to remove the ability to spend the first instance -- even after // being sent to another address. // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information. // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool // already refuses previously-known transaction ids entirely. // This rule was originally applied all blocks whose timestamp was after March 15, 2012, 0:00 UTC. // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the // two in the chain that violate it. This prevents exploiting the issue against nodes in their // initial block download. CTxIndex txindexOld; if (txdb.ReadTxIndex(hashTx, txindexOld)) { BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent) if (pos.IsNull()) return false; } nSigOps += tx.GetLegacySigOpCount(); if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("ConnectBlock() : too many sigops")); CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); if (!fJustCheck) nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); MapPrevTx mapInputs; if (tx.IsCoinBase()) nValueOut += tx.GetValueOut(); else { bool fInvalid; if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid)) return false; // Add in sigops done by pay-to-script-hash inputs; // this is to prevent a "rogue miner" from creating // an incredibly-expensive-to-validate block. nSigOps += tx.GetP2SHSigOpCount(mapInputs); if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("ConnectBlock() : too many sigops")); int64_t nTxValueIn = tx.GetValueIn(mapInputs); int64_t nTxValueOut = tx.GetValueOut(); nValueIn += nTxValueIn; nValueOut += nTxValueOut; if (!tx.IsCoinStake()) nFees += nTxValueIn - nTxValueOut; if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false)) return false; } mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size()); } // ppcoin: track money supply and mint amount info pindex->nMint = nValueOut - nValueIn + nFees; pindex->nMoneySupply = (pindex->pprev? pindex->pprev->nMoneySupply : 0) + nValueOut - nValueIn; if (!txdb.WriteBlockIndex(CDiskBlockIndex(pindex))) return error("Connect() : WriteBlockIndex for pindex failed"); // ppcoin: fees are not collected by miners as in bitcoin // ppcoin: fees are destroyed to compensate the entire network LogPrint("creation", "ConnectBlock() : destroy=%s nFees=%d\n", FormatMoney(nFees), nFees); if (fJustCheck) return true; // Write queued txindex changes for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) { if (!txdb.UpdateTxIndex((*mi).first, (*mi).second)) return error("ConnectBlock() : UpdateTxIndex failed"); } // Update block index on disk without changing it in memory. // The memory index structure will be changed after the db commits. if (pindex->pprev) { CDiskBlockIndex blockindexPrev(pindex->pprev); blockindexPrev.hashNext = pindex->GetBlockHash(); if (!txdb.WriteBlockIndex(blockindexPrev)) return error("ConnectBlock() : WriteBlockIndex failed"); } // Watch for transactions paying to me BOOST_FOREACH(CTransaction& tx, vtx) SyncWithWallets(tx, this, true); return true; } bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) { LogPrintf("REORGANIZE\n"); // Find the fork CBlockIndex* pfork = pindexBest; CBlockIndex* plonger = pindexNew; while (pfork != plonger) { while (plonger->nHeight > pfork->nHeight) if (!(plonger = plonger->pprev)) return error("Reorganize() : plonger->pprev is null"); if (pfork == plonger) break; if (!(pfork = pfork->pprev)) return error("Reorganize() : pfork->pprev is null"); } // List of what to disconnect vector<CBlockIndex*> vDisconnect; for (CBlockIndex* pindex = pindexBest; pindex != pfork; pindex = pindex->pprev) vDisconnect.push_back(pindex); // List of what to connect vector<CBlockIndex*> vConnect; for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) vConnect.push_back(pindex); reverse(vConnect.begin(), vConnect.end()); LogPrintf("REORGANIZE: Disconnect %u blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20), pindexBest->GetBlockHash().ToString().substr(0,20)); LogPrintf("REORGANIZE: Connect %u blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20), pindexNew->GetBlockHash().ToString().substr(0,20)); // Disconnect shorter branch list<CTransaction> vResurrect; BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { CBlock block; if (!block.ReadFromDisk(pindex)) return error("Reorganize() : ReadFromDisk for disconnect failed"); if (!block.DisconnectBlock(txdb, pindex)) return error("Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20)); // Queue memory transactions to resurrect // We only do this for blocks after the last checkpoint (reorganisation before that // point should only happen with -reindex/-loadblock, or a misbehaving peer. BOOST_REVERSE_FOREACH(const CTransaction& tx, block.vtx) if (!(tx.IsCoinBase() || tx.IsCoinStake()) && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate()) vResurrect.push_front(tx); } // Connect longer branch vector<CTransaction> vDelete; for (unsigned int i = 0; i < vConnect.size(); i++) { CBlockIndex* pindex = vConnect[i]; CBlock block; if (!block.ReadFromDisk(pindex)) return error("Reorganize() : ReadFromDisk for connect failed"); if (!block.ConnectBlock(txdb, pindex)) { // Invalid block return error("Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20)); } // Queue memory transactions to delete BOOST_FOREACH(const CTransaction& tx, block.vtx) vDelete.push_back(tx); } if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash())) return error("Reorganize() : WriteHashBestChain failed"); // Make sure it's successfully written to disk before changing memory structure if (!txdb.TxnCommit()) return error("Reorganize() : TxnCommit failed"); // Disconnect shorter branch BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) if (pindex->pprev) pindex->pprev->pnext = NULL; // Connect longer branch BOOST_FOREACH(CBlockIndex* pindex, vConnect) if (pindex->pprev) pindex->pprev->pnext = pindex; // Resurrect memory transactions that were in the disconnected branch BOOST_FOREACH(CTransaction& tx, vResurrect) AcceptToMemoryPool(mempool, tx, NULL); // Delete redundant memory transactions that are in the connected branch BOOST_FOREACH(CTransaction& tx, vDelete) mempool.remove(tx); LogPrintf("REORGANIZE: done\n"); return true; } // Called from inside SetBestChain: attaches a block to the new best chain being built bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew) { uint256 hash = GetHash(); // Adding to current best branch if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash)) { txdb.TxnAbort(); InvalidChainFound(pindexNew); return false; } if (!txdb.TxnCommit()) return error("SetBestChain() : TxnCommit failed"); // Add to current best branch pindexNew->pprev->pnext = pindexNew; // Delete redundant memory transactions BOOST_FOREACH(CTransaction& tx, vtx) mempool.remove(tx); return true; } bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) { uint256 hash = GetHash(); if (!txdb.TxnBegin()) return error("SetBestChain() : TxnBegin failed"); if (pindexGenesisBlock == NULL && hash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) { txdb.WriteHashBestChain(hash); if (!txdb.TxnCommit()) return error("SetBestChain() : TxnCommit failed"); pindexGenesisBlock = pindexNew; } else if (hashPrevBlock == hashBestChain) { if (!SetBestChainInner(txdb, pindexNew)) return error("SetBestChain() : SetBestChainInner failed"); } else { // the first block in the new chain that will cause it to become the new best chain CBlockIndex *pindexIntermediate = pindexNew; // list of blocks that need to be connected afterwards std::vector<CBlockIndex*> vpindexSecondary; // Reorganize is costly in terms of db load, as it works in a single db transaction. // Try to limit how much needs to be done inside while (pindexIntermediate->pprev && pindexIntermediate->pprev->nChainTrust > pindexBest->nChainTrust) { vpindexSecondary.push_back(pindexIntermediate); pindexIntermediate = pindexIntermediate->pprev; } if (!vpindexSecondary.empty()) LogPrintf("Postponing %u reconnects\n", vpindexSecondary.size()); // Switch to new best branch if (!Reorganize(txdb, pindexIntermediate)) { txdb.TxnAbort(); InvalidChainFound(pindexNew); return error("SetBestChain() : Reorganize failed"); } // Connect further blocks BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary) { CBlock block; if (!block.ReadFromDisk(pindex)) { LogPrintf("SetBestChain() : ReadFromDisk failed\n"); break; } if (!txdb.TxnBegin()) { LogPrintf("SetBestChain() : TxnBegin 2 failed\n"); break; } // errors now are not fatal, we still did a reorganisation to a new chain in a valid way if (!block.SetBestChainInner(txdb, pindex)) break; } } // Update best block in wallet (so we can detect restored wallets) bool fIsInitialDownload = IsInitialBlockDownload(); if (!fIsInitialDownload) { const CBlockLocator locator(pindexNew); ::SetBestChain(locator); } // New best block hashBestChain = hash; pindexBest = pindexNew; pblockindexFBBHLast = NULL; nBestHeight = pindexBest->nHeight; nBestChainTrust = pindexNew->nChainTrust; nTimeBestReceived = GetTime(); nTransactionsUpdated++; uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust; LogPrintf("SetBestChain: new best=%s height=%d trust=%s blocktrust=%d date=%s\n", hashBestChain.ToString().substr(0,20), nBestHeight, CBigNum(nBestChainTrust).ToString(), nBestBlockTrust.Get64(), DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime())); // Check the version of the last 100 blocks to see if we need to upgrade: if (!fIsInitialDownload) { int nUpgraded = 0; const CBlockIndex* pindex = pindexBest; for (int i = 0; i < 100 && pindex != NULL; i++) { if (pindex->nVersion > CBlock::CURRENT_VERSION) ++nUpgraded; pindex = pindex->pprev; } if (nUpgraded > 0) LogPrintf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, (int)CBlock::CURRENT_VERSION); if (nUpgraded > 100/2) // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: strMiscWarning = _("Warning: This version is obsolete, upgrade required!"); } std::string strCmd = GetArg("-blocknotify", ""); if (!fIsInitialDownload && !strCmd.empty()) { boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); boost::thread t(runCommand, strCmd); // thread runs free } return true; } // ppcoin: total coin age spent in transaction, in the unit of coin-days. // Only those coins meeting minimum age requirement counts. As those // transactions not in main chain are not currently indexed so we // might not find out about their coin age. Older transactions are // guaranteed to be in main chain by sync-checkpoint. This rule is // introduced to help nodes establish a consistent view of the coin // age (trust score) of competing branches. bool CTransaction::GetCoinAge(CTxDB& txdb, uint64_t& nCoinAge) const { CBigNum bnCentSecond = 0; // coin age in the unit of cent-seconds nCoinAge = 0; if (IsCoinBase()) return true; BOOST_FOREACH(const CTxIn& txin, vin) { // First try finding the previous transaction in database CTransaction txPrev; CTxIndex txindex; if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) continue; // previous transaction not in main chain if (nTime < txPrev.nTime) return false; // Transaction timestamp violation // Read block header CBlock block; if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) return false; // unable to read block of previous transaction if (block.GetBlockTime() + nStakeMinAge > nTime) continue; // only count coins meeting min age requirement int64_t nValueIn = txPrev.vout[txin.prevout.n].nValue; bnCentSecond += CBigNum(nValueIn) * (nTime-txPrev.nTime) / CENT; LogPrint("coinage", "coin age nValueIn=%d nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString()); } CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60); LogPrint("coinage", "coin age bnCoinDay=%s\n", bnCoinDay.ToString()); nCoinAge = bnCoinDay.getuint64(); return true; } // ppcoin: total coin age spent in block, in the unit of coin-days. bool CBlock::GetCoinAge(uint64_t& nCoinAge) const { nCoinAge = 0; CTxDB txdb("r"); BOOST_FOREACH(const CTransaction& tx, vtx) { uint64_t nTxCoinAge; if (tx.GetCoinAge(txdb, nTxCoinAge)) nCoinAge += nTxCoinAge; else return false; } if (nCoinAge == 0) // block coin age minimum 1 coin-day nCoinAge = 1; LogPrint("coinage", "block coin age total nCoinDays=%d\n", nCoinAge); return true; } bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const uint256& hashProofOfStake) { // Check for duplicate uint256 hash = GetHash(); if (mapBlockIndex.count(hash)) return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20)); // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this); if (!pindexNew) return error("AddToBlockIndex() : new CBlockIndex failed"); pindexNew->phashBlock = &hash; map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock); if (miPrev != mapBlockIndex.end()) { pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; } // ppcoin: compute chain trust score pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust(); // ppcoin: compute stake entropy bit for stake modifier if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nHeight))) return error("AddToBlockIndex() : SetStakeEntropyBit() failed"); // ppcoin: record proof-of-stake hash value pindexNew->hashProofOfStake = hashProofOfStake; // ppcoin: compute stake modifier uint64_t nStakeModifier = 0; bool fGeneratedStakeModifier = false; if (!ComputeNextStakeModifier(pindexNew->pprev, nStakeModifier, fGeneratedStakeModifier)) return error("AddToBlockIndex() : ComputeNextStakeModifier() failed"); pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier); pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew); if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum)) return error("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=0x%016x", pindexNew->nHeight, nStakeModifier); // Add to mapBlockIndex map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; if (pindexNew->IsProofOfStake()) setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime)); pindexNew->phashBlock = &((*mi).first); // Write to disk block index CTxDB txdb; if (!txdb.TxnBegin()) return false; txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew)); if (!txdb.TxnCommit()) return false; LOCK(cs_main); // New best if (pindexNew->nChainTrust > nBestChainTrust) if (!SetBestChain(txdb, pindexNew)) return false; if (pindexNew == pindexBest) { // Notify UI to display prev block's coinbase if it was ours static uint256 hashPrevBestCoinBase; UpdatedTransaction(hashPrevBestCoinBase); hashPrevBestCoinBase = vtx[0].GetHash(); } uiInterface.NotifyBlocksChanged(); return true; } bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) const { // These are checks that are independent of context // that can be verified before saving an orphan block. // Size limits if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return DoS(100, error("CheckBlock() : size limits failed")); // Check proof of work matches claimed amount if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits)) return DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp if (GetBlockTime() > FutureDrift(GetAdjustedTime())) return error("CheckBlock() : block timestamp too far in the future"); // First transaction must be coinbase, the rest must not be if (vtx.empty() || !vtx[0].IsCoinBase()) return DoS(100, error("CheckBlock() : first tx is not coinbase")); for (unsigned int i = 1; i < vtx.size(); i++) if (vtx[i].IsCoinBase()) return DoS(100, error("CheckBlock() : more than one coinbase")); // Check coinbase timestamp if (GetBlockTime() > FutureDrift((int64_t)vtx[0].nTime)) return DoS(50, error("CheckBlock() : coinbase timestamp is too early")); if (IsProofOfStake()) { // ppcoin: only the second transaction can be the optional coinstake for (unsigned int i = 2; i < vtx.size(); i++) if (vtx[i].IsCoinStake()) return DoS(100, error("CheckBlock() : coinstake in wrong position")); // Coinbase output should be empty if proof-of-stake block if (IsProofOfStake() && (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty())) return DoS(100, error("CheckBlock() : coinbase output not empty for proof-of-stake block")); // Check coinstake timestamp if (IsProofOfStake() && !CheckCoinStakeTimestamp(GetBlockTime(), (int64_t)vtx[1].nTime)) return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%d nTimeTx=%u", GetBlockTime(), vtx[1].nTime)); // NovaCoin: check proof-of-stake block signature if (fCheckSig && !CheckBlockSignature(true)) return DoS(100, error("CheckBlock() : bad proof-of-stake block signature")); } else { // Check coinbase reward int64_t nTimeBlock = GetBlockTime(); if (nTimeBlock < REWARD_SWITCH_TIME) { if (vtx[0].GetValueOut() > (IsProofOfWork()? MAX_MINT_PROOF_OF_WORK : 0)) return DoS(50, error("CheckBlock() : coinbase reward exceeded %s > %s", FormatMoney(vtx[0].GetValueOut()), FormatMoney(IsProofOfWork()? GetProofOfWorkReward() : 0))); } else { if (vtx[0].GetValueOut() > (IsProofOfWork()? (GetProofOfWorkReward() - vtx[0].GetMinFee() + MIN_TX_FEE) : 0)) return DoS(50, error("CheckBlock() : coinbase reward exceeded %s > %s", FormatMoney(vtx[0].GetValueOut()), FormatMoney(IsProofOfWork()? GetProofOfWorkReward() : 0))); } } // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) { if (!tx.CheckTransaction()) return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); // ppcoin: check transaction timestamp if (GetBlockTime() < (int64_t)tx.nTime) return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp")); } // Check for duplicate txids. This is caught by ConnectInputs(), // but catching it earlier avoids a potential DoS attack: set<uint256> uniqueTx; BOOST_FOREACH(const CTransaction& tx, vtx) { uniqueTx.insert(tx.GetHash()); } if (uniqueTx.size() != vtx.size()) return DoS(100, error("CheckBlock() : duplicate transaction")); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, vtx) { nSigOps += tx.GetLegacySigOpCount(); } if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkle root if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); return true; } bool CBlock::AcceptBlock() { AssertLockHeld(cs_main); // Check for duplicate uint256 hash = GetHash(); if (mapBlockIndex.count(hash)) return error("AcceptBlock() : block already in mapBlockIndex"); // Get prev block index map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock); if (mi == mapBlockIndex.end()) return DoS(10, error("AcceptBlock() : prev block not found")); CBlockIndex* pindexPrev = (*mi).second; int nHeight = pindexPrev->nHeight+1; // Check proof-of-work or proof-of-stake if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake())) return DoS(100, error("AcceptBlock() : incorrect %s", IsProofOfWork() ? "proof-of-work" : "proof-of-stake")); // Check timestamp against prev if (GetBlockTime() <= pindexPrev->GetPastTimeLimit() || FutureDrift(GetBlockTime()) < pindexPrev->GetBlockTime()) return error("AcceptBlock() : block's timestamp is too early"); // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, vtx) if (!IsFinalTx(tx, nHeight, GetBlockTime())) return DoS(10, error("AcceptBlock() : contains a non-final transaction")); // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckHardened(nHeight, hash)) return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lock-in at %d", nHeight)); // Verify hash target and signature of coinstake tx uint256 hashProofOfStake = 0, targetProofOfStake = 0; if (IsProofOfStake()) { if (!CheckProofOfStake(vtx[1], nBits, hashProofOfStake, targetProofOfStake)) { LogPrintf("WARNING: AcceptBlock(): check proof-of-stake failed for block %s\n", hash.ToString()); return false; // do not error here as we expect this during initial block download } } bool cpSatisfies = Checkpoints::CheckSync(hash, pindexPrev); // Check that the block satisfies synchronized checkpoint if (CheckpointsMode == Checkpoints::STRICT && !cpSatisfies) return error("AcceptBlock() : rejected by synchronized checkpoint"); if (CheckpointsMode == Checkpoints::ADVISORY && !cpSatisfies) strMiscWarning = _("WARNING: syncronized checkpoint violation detected, but skipped!"); // Enforce rule that the coinbase starts with serialized block height CScript expect = CScript() << nHeight; if (vtx[0].vin[0].scriptSig.size() < expect.size() || !std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) return DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); // Write block to history file if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) return error("AcceptBlock() : out of disk space"); unsigned int nFile = -1; unsigned int nBlockPos = 0; if (!WriteToDisk(nFile, nBlockPos)) return error("AcceptBlock() : WriteToDisk failed"); if (!AddToBlockIndex(nFile, nBlockPos, hashProofOfStake)) return error("AcceptBlock() : AddToBlockIndex failed"); // Relay inventory, but don't relay old inventory during initial block download int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); if (hashBestChain == hash) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) pnode->PushInventory(CInv(MSG_BLOCK, hash)); } // ppcoin: check pending sync-checkpoint Checkpoints::AcceptPendingSyncCheckpoint(); return true; } bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck) { unsigned int nFound = 0; for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++) { if (pstart->nVersion >= minVersion) ++nFound; pstart = pstart->pprev; } return (nFound >= nRequired); } void Misbehaving(NodeId pnode, int howmuch) { if (howmuch == 0) return; CNodeState *state = State(pnode); if (state == NULL) return; state->nMisbehavior += howmuch; if (state->nMisbehavior >= GetArg("-banscore", 100)) { LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); state->fShouldBan = true; } else LogPrintf("Misbehaving: %s (%d -> %d)\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); } uint256 CBlockIndex::GetBlockTrust() const { CBigNum bnTarget; bnTarget.SetCompact(nBits); if (bnTarget <= 0) return 0; if (IsProofOfStake()) { // Return trust score as usual return ((CBigNum(1)<<256) / (bnTarget+1)).getuint256(); } else { // Calculate work amount for block uint256 nPoWTrust = (CBigNum(bnProofOfWorkLimit) / (bnTarget+1)).getuint256(); return nPoWTrust > 1 ? nPoWTrust : 1; } } bool ProcessBlock(CNode* pfrom, CBlock* pblock) { AssertLockHeld(cs_main); // Check for duplicate uint256 hash = pblock->GetHash(); if (mapBlockIndex.count(hash)) return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,20)); if (mapOrphanBlocks.count(hash)) return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20)); // ppcoin: check proof-of-stake // Limited duplicity on stake: prevents block flood attack // Duplicate stake allowed only when there is orphan child block if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash)) return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString(), pblock->GetProofOfStake().second, hash.ToString()); // Preliminary checks if (!pblock->CheckBlock()) return error("ProcessBlock() : CheckBlock FAILED"); CBlockIndex* pcheckpoint = Checkpoints::GetLastSyncCheckpoint(); if (pcheckpoint && pblock->hashPrevBlock != hashBestChain && !Checkpoints::WantedByPendingSyncCheckpoint(hash)) { // Extra checks to prevent "fill up memory by spamming with bogus blocks" int64_t deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; CBigNum bnNewBlock; bnNewBlock.SetCompact(pblock->nBits); CBigNum bnRequired; if (pblock->IsProofOfStake()) bnRequired.SetCompact(ComputeMinStake(GetLastBlockIndex(pcheckpoint, true)->nBits, deltaTime, pblock->nTime)); else bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, false)->nBits, deltaTime)); if (bnNewBlock > bnRequired) { if (pfrom) Misbehaving(pfrom->GetId(), 100); return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work"); } } // ppcoin: ask for pending sync-checkpoint if any if (!IsInitialBlockDownload()) Checkpoints::AskForPendingSyncCheckpoint(pfrom); // If don't already have its previous block, shunt it off to holding area until we get it if (!mapBlockIndex.count(pblock->hashPrevBlock)) { LogPrintf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20)); // check proof-of-stake if (pblock->IsProofOfStake()) { // Limited duplicity on stake: prevents block flood attack // Duplicate stake allowed only when there is orphan child block if (setStakeSeenOrphan.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash)) return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for orphan block %s", pblock->GetProofOfStake().first.ToString(), pblock->GetProofOfStake().second, hash.ToString()); else setStakeSeenOrphan.insert(pblock->GetProofOfStake()); } CBlock* pblock2 = new CBlock(*pblock); mapOrphanBlocks.insert(make_pair(hash, pblock2)); mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); // Ask this guy to fill in what we're missing if (pfrom) { pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2)); // ppcoin: getblocks may not obtain the ancestor block rejected // earlier by duplicate-stake check so we ask for it again directly if (!IsInitialBlockDownload()) pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(pblock2))); } return true; } // Store to disk if (!pblock->AcceptBlock()) return error("ProcessBlock() : AcceptBlock FAILED"); // Recursively process any orphan blocks that depended on this one vector<uint256> vWorkQueue; vWorkQueue.push_back(hash); for (unsigned int i = 0; i < vWorkQueue.size(); i++) { uint256 hashPrev = vWorkQueue[i]; for (multimap<uint256, CBlock*>::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev); mi != mapOrphanBlocksByPrev.upper_bound(hashPrev); ++mi) { CBlock* pblockOrphan = (*mi).second; if (pblockOrphan->AcceptBlock()) vWorkQueue.push_back(pblockOrphan->GetHash()); mapOrphanBlocks.erase(pblockOrphan->GetHash()); setStakeSeenOrphan.erase(pblockOrphan->GetProofOfStake()); delete pblockOrphan; } mapOrphanBlocksByPrev.erase(hashPrev); } LogPrintf("ProcessBlock: ACCEPTED\n"); if (fGlobalStakeForCharity && !IsInitialBlockDownload()) pWalletManager->StakeForCharity(); // ppcoin: if responsible for sync-checkpoint send it if (pfrom && !CSyncCheckpoint::strMasterPrivKey.empty()) Checkpoints::SendSyncCheckpoint(Checkpoints::AutoSelectSyncCheckpoint()); return true; } // novacoin: attempt to generate suitable proof-of-stake bool CBlock::SignPoSBlock(CWallet& wallet) { // if we are trying to sign // something except proof-of-stake block template if (!vtx[0].vout[0].IsEmpty()) return false; // if we are trying to sign // a complete proof-of-stake block if (IsProofOfStake()) return true; static int64_t nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp CKey key; CTransaction txCoinStake; int64_t nSearchTime = txCoinStake.nTime; // search to current time if (nSearchTime > nLastCoinStakeSearchTime) { if (wallet.CreateCoinStake(wallet, nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake, key)) { if (txCoinStake.nTime >= max(pindexBest->GetPastTimeLimit()+1, PastDrift(pindexBest->GetBlockTime()))) { // make sure coinstake would meet timestamp protocol // as it would be the same as the block timestamp vtx[0].nTime = nTime = txCoinStake.nTime; nTime = max(pindexBest->GetPastTimeLimit()+1, GetMaxTransactionTime()); nTime = max(GetBlockTime(), PastDrift(pindexBest->GetBlockTime())); // we have to make sure that we have no future timestamps in // our transactions set for (vector<CTransaction>::iterator it = vtx.begin(); it != vtx.end();) if (it->nTime > nTime) { it = vtx.erase(it); } else { ++it; } vtx.insert(vtx.begin() + 1, txCoinStake); hashMerkleRoot = BuildMerkleTree(); // append a signature to our block return key.Sign(GetHash(), vchBlockSig); } } nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime; nLastCoinStakeSearchTime = nSearchTime; } return false; } // hbn: sign block for PoW bool CBlock::SignBlock(const CKeyStore& keystore) { vector<valtype> vSolutions; txnouttype whichType; for(unsigned int i = 0; i < vtx[0].vout.size(); i++) { const CTxOut& txout = vtx[0].vout[i]; if (!Solver(txout.scriptPubKey, whichType, vSolutions)) continue; if (whichType == TX_PUBKEY) { // Sign valtype& vchPubKey = vSolutions[0]; CKey key; if (!keystore.GetKey(Hash160(vchPubKey), key)) continue; if (key.GetPubKey() != vchPubKey) continue; if(!key.Sign(GetHash(), vchBlockSig)) continue; return true; } } LogPrintf("Sign failed\n"); return false; } // ppcoin: check block signature bool CBlock::CheckBlockSignature(bool fProofOfStake) const { if (GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) return vchBlockSig.empty(); vector<valtype> vSolutions; txnouttype whichType; if(fProofOfStake) { const CTxOut& txout = vtx[1].vout[1]; if (!Solver(txout.scriptPubKey, whichType, vSolutions)) return false; if (whichType == TX_PUBKEY) { valtype& vchPubKey = vSolutions[0]; CKey key; if (!key.SetPubKey(vchPubKey)) return false; if (vchBlockSig.empty()) return false; return key.Verify(GetHash(), vchBlockSig); } } else { for(unsigned int i = 0; i < vtx[0].vout.size(); i++) { const CTxOut& txout = vtx[0].vout[i]; if (!Solver(txout.scriptPubKey, whichType, vSolutions)) return false; if (whichType == TX_PUBKEY) { // Verify valtype& vchPubKey = vSolutions[0]; CKey key; if (!key.SetPubKey(vchPubKey)) continue; if (vchBlockSig.empty()) continue; if(!key.Verify(GetHash(), vchBlockSig)) continue; return true; } } } return false; } bool CheckDiskSpace(uint64_t nAdditionalBytes) { uint64_t nFreeBytesAvailable = filesystem::space(GetDataDir()).available; // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) { fShutdown = true; string strMessage = _("Warning: Disk space is low!"); strMiscWarning = strMessage; LogPrintf("*** %s\n", strMessage); uiInterface.ThreadSafeMessageBox(strMessage, "HoboNickels", CClientUIInterface::MSG_WARNING); StartShutdown(); return false; } return true; } static filesystem::path BlockFilePath(unsigned int nFile) { string strBlockFn = strprintf("blk%04u.dat", nFile); return GetDataDir() / strBlockFn; } FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode) { if ((nFile < 1) || (nFile == (unsigned int) -1)) return NULL; FILE* file = fopen(BlockFilePath(nFile).string().c_str(), pszMode); if (!file) return NULL; if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w')) { if (fseek(file, nBlockPos, SEEK_SET) != 0) { fclose(file); return NULL; } } return file; } static unsigned int nCurrentBlockFile = 1; FILE* AppendBlockFile(unsigned int& nFileRet) { nFileRet = 0; while (true) { FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab"); if (!file) return NULL; if (fseek(file, 0, SEEK_END) != 0) return NULL; // FAT32 file size max 4GB, fseek and ftell max 2GB, so we must stay under 2GB if (ftell(file) < (long)(0x7F000000 - MAX_SIZE)) { nFileRet = nCurrentBlockFile; return file; } fclose(file); nCurrentBlockFile++; } } bool LoadBlockIndex(bool fAllowNew) { LOCK(cs_main); if (fTestNet) { pchMessageStart[0] = 0xcd; pchMessageStart[1] = 0xf2; pchMessageStart[2] = 0xc0; pchMessageStart[3] = 0xef; bnProofOfStakeLimit = bnProofOfStakeLimitTestNet; // 0x00000fff PoS base target is fixed in testnet bnProofOfWorkLimit = bnProofOfWorkLimitTestNet; // 0x0000ffff PoW base target is fixed in testnet nStakeMinAge = 2 * 60 * 60; // test net min age is 2 hours nModifierInterval = 20 * 60; // test modifier interval is 20 minutes nCoinbaseMaturity = 10; // test maturity is 10 blocks nStakeTargetSpacing = 1 * 60; // test block spacing is 3 minutes } // // Load block index // CTxDB txdb("cr"); if (!txdb.LoadBlockIndex()) return false; txdb.Close(); // // Init with genesis block // if (mapBlockIndex.empty()) { if (!fAllowNew) return false; // Genesis block const char* pszTimestamp = !fTestNet ? "HoboNickels are Go!" : "Tranz Testnet"; CTransaction txNew; txNew.nTime = nChainStartTime; txNew.vin.resize(1); txNew.vout.resize(1); txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); txNew.vout[0].SetEmpty(); CBlock block; block.vtx.push_back(txNew); block.hashPrevBlock = 0; block.hashMerkleRoot = block.BuildMerkleTree(); block.nVersion = 1; block.nTime = 1374635824; block.nBits = bnProofOfWorkLimit.GetCompact(); block.nNonce = 4215582; if (fTestNet) { block.nTime = 1380383684; block.nBits = bnProofOfWorkLimit.GetCompact(); block.nNonce = 47018; } if (true && (block.GetHash() != hashGenesisBlock)) { // This will figure out a valid hash and Nonce if you're // creating a different genesis block: uint256 hashTarget = CBigNum().SetCompact(block.nBits).getuint256(); while (block.GetHash() > hashTarget) { ++block.nNonce; if (block.nNonce == 0) { LogPrintf("NONCE WRAPPED, incrementing time"); ++block.nTime; } } } //// debug print LogPrintf("block.GetHash() == %s\n", block.GetHash().ToString()); LogPrintf("block.hashMerkleRoot == %s\n", block.hashMerkleRoot.ToString()); LogPrintf("block.nTime = %u \n", block.nTime); LogPrintf("block.nNonce = %u \n", block.nNonce); if (fTestNet) { assert(block.hashMerkleRoot == uint256("0x25756655bce43a9b72363c06979768cafba833a10de2e754fbef715a217ed241")); } else { assert(block.hashMerkleRoot == uint256("0xbe06386a1644f7448ff25d28862b6c28e3a334e4a58f1c5ebd99ee49daa370c7")); } assert(block.GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)); // Start new block file unsigned int nFile; unsigned int nBlockPos; if (!block.WriteToDisk(nFile, nBlockPos)) return error("LoadBlockIndex() : writing genesis block to disk failed"); if (!block.AddToBlockIndex(nFile, nBlockPos, 0)) return error("LoadBlockIndex() : genesis block not accepted"); // ppcoin: initialize synchronized checkpoint if (!Checkpoints::WriteSyncCheckpoint((!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))) return error("LoadBlockIndex() : failed to init sync checkpoint"); } // ppcoin: if checkpoint master key changed must reset sync-checkpoint { CTxDB txdb; string strPubKey = ""; if (!txdb.ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey) { // write checkpoint master key to db txdb.TxnBegin(); if (!txdb.WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey)) return error("LoadBlockIndex() : failed to write new checkpoint master key to db"); if (!txdb.TxnCommit()) return error("LoadBlockIndex() : failed to commit new checkpoint master key to db"); if ((!fTestNet) && !Checkpoints::ResetSyncCheckpoint()) return error("LoadBlockIndex() : failed to reset sync-checkpoint"); } #ifndef USE_LEVELDB txdb.Close(); #endif } return true; } void PrintBlockTree() { AssertLockHeld(cs_main); // pre-compute tree structure map<CBlockIndex*, vector<CBlockIndex*> > mapNext; for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) { CBlockIndex* pindex = (*mi).second; mapNext[pindex->pprev].push_back(pindex); } vector<pair<int, CBlockIndex*> > vStack; vStack.push_back(make_pair(0, pindexGenesisBlock)); int nPrevCol = 0; while (!vStack.empty()) { int nCol = vStack.back().first; CBlockIndex* pindex = vStack.back().second; vStack.pop_back(); // print split or gap if (nCol > nPrevCol) { for (int i = 0; i < nCol-1; i++) LogPrintf("| "); LogPrintf("|\\\n"); } else if (nCol < nPrevCol) { for (int i = 0; i < nCol; i++) LogPrintf("| "); LogPrintf("|\n"); } nPrevCol = nCol; // print columns for (int i = 0; i < nCol; i++) LogPrintf("| "); // print item CBlock block; block.ReadFromDisk(pindex); LogPrintf("%d (%u,%u) %s %08x %s mint %7s tx %u", pindex->nHeight, pindex->nFile, pindex->nBlockPos, block.GetHash().ToString(), block.nBits, DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()), FormatMoney(pindex->nMint), block.vtx.size()); PrintWallets(block); // put the main time-chain first vector<CBlockIndex*>& vNext = mapNext[pindex]; for (unsigned int i = 0; i < vNext.size(); i++) { if (vNext[i]->pnext) { swap(vNext[0], vNext[i]); break; } } // iterate children for (unsigned int i = 0; i < vNext.size(); i++) vStack.push_back(make_pair(nCol+i, vNext[i])); } } bool LoadExternalBlockFile(FILE* fileIn) { int64_t nStart = GetTimeMillis(); int nLoaded = 0; { LOCK(cs_main); try { CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION); unsigned int nPos = 0; while (nPos != (unsigned int)-1 && blkdat.good() && !fRequestShutdown) { unsigned char pchData[65536]; do { fseek(blkdat, nPos, SEEK_SET); int nRead = fread(pchData, 1, sizeof(pchData), blkdat); if (nRead <= 8) { nPos = (unsigned int)-1; break; } void* nFind = memchr(pchData, pchMessageStart[0], nRead+1-sizeof(pchMessageStart)); if (nFind) { if (memcmp(nFind, pchMessageStart, sizeof(pchMessageStart))==0) { nPos += ((unsigned char*)nFind - pchData) + sizeof(pchMessageStart); break; } nPos += ((unsigned char*)nFind - pchData) + 1; } else nPos += sizeof(pchData) - sizeof(pchMessageStart) + 1; } while(!fRequestShutdown); if (nPos == (unsigned int)-1) break; fseek(blkdat, nPos, SEEK_SET); unsigned int nSize; blkdat >> nSize; if (nSize > 0 && nSize <= MAX_BLOCK_SIZE) { CBlock block; blkdat >> block; if (ProcessBlock(NULL,&block)) { nLoaded++; nPos += 4 + nSize; } } } } catch (std::exception &e) { LogPrintf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); } } LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, GetTimeMillis() - nStart); return nLoaded > 0; } ////////////////////////////////////////////////////////////////////////////// // // CAlert // extern map<uint256, CAlert> mapAlerts; extern CCriticalSection cs_mapAlerts; extern string strMintMessage; extern string strMintWarning; string GetWarnings(string strFor) { int nPriority = 0; string strStatusBar; string strRPC; if (GetBoolArg("-testsafemode")) strRPC = "test"; if (!CLIENT_VERSION_IS_RELEASE) strStatusBar = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"); // ppcoin: wallet lock warning for minting if (strMintWarning != "") { nPriority = 0; strStatusBar = strMintWarning; } // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { nPriority = 1000; strStatusBar = strMiscWarning; } // * Should not enter safe mode for longer invalid chain // * If sync-checkpoint is too old do not enter safe mode // * Display warning only in the STRICT mode if (CheckpointsMode == Checkpoints::STRICT && Checkpoints::IsSyncCheckpointTooOld(60 * 60 * 24 * 10) && !fTestNet && !IsInitialBlockDownload()) { nPriority = 100; strStatusBar = _("WARNING: Checkpoint is too old. Wait for block chain to download, or notify developers."); } // ppcoin: if detected invalid checkpoint enter safe mode if (Checkpoints::hashInvalidCheckpoint != 0) { nPriority = 3000; strStatusBar = strRPC = _("WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or notify developers."); } // Alerts { LOCK(cs_mapAlerts); BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) { const CAlert& alert = item.second; if (alert.AppliesToMe() && alert.nPriority > nPriority) { nPriority = alert.nPriority; strStatusBar = alert.strStatusBar; if (nPriority > 1000) strRPC = strStatusBar; } } } if (strFor == "statusbar") return strStatusBar; else if (strFor == "rpc") return strRPC; assert(!"GetWarnings() : invalid parameter"); return "error"; } ////////////////////////////////////////////////////////////////////////////// // // Messages // bool static AlreadyHave(CTxDB& txdb, const CInv& inv) { switch (inv.type) { case MSG_TX: { bool txInMap = false; txInMap = mempool.exists(inv.hash); return txInMap || mapOrphanTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash); } case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash); } // Don't know what it is, just say we already got one return true; } // The message start string is designed to be unlikely to occur in normal data. // The characters are rarely used upper ASCII, not valid as UTF-8, and produce // a large 4-byte int at any alignment. unsigned char pchMessageStart[4] = { 0xe4, 0xe8, 0xe9, 0xe5 }; bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { static map<CService, CPubKey> mapReuseKey; RandAddSeedPerfmon(); LogPrint("net", "received: %s (%u bytes)\n", strCommand, vRecv.size()); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); return true; } if (strCommand == "version") { // Each connection can only send one version message if (pfrom->nVersion != 0) { Misbehaving(pfrom->GetId(), 1); return false; } int64_t nTime; CAddress addrMe; CAddress addrFrom; uint64_t nNonce = 1; vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; if (pfrom->nVersion < MIN_PEER_PROTO_VERSION) { // disconnect from peers older than this proto version LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString(), pfrom->nVersion); pfrom->fDisconnect = true; return false; } if(pfrom->nVersion < 70003) { LogPrintf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString(), pfrom->nVersion); pfrom->fDisconnect = true; return false; } if (pfrom->nVersion == 10300) pfrom->nVersion = 300; if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; if (!vRecv.empty()) { vRecv >> pfrom->strSubVer; pfrom->cleanSubVer = SanitizeString(pfrom->strSubVer); } if (!vRecv.empty()) vRecv >> pfrom->nStartingHeight; if (pfrom->fInbound && addrMe.IsRoutable()) { pfrom->addrLocal = addrMe; SeenLocal(addrMe); } // Disconnect if we connected to ourself if (nNonce == nLocalHostNonce && nNonce > 1) { LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString()); pfrom->fDisconnect = true; return true; } // Record my external IP reported by peer if (addrFrom.IsRoutable() && addrMe.IsRoutable()) addrSeenByPeer = addrMe; // Be shy and don't send version until we hear if (pfrom->fInbound) pfrom->PushVersion(); pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); if (GetBoolArg("-synctime", true)) AddTimeData(pfrom->addr, nTime); // Change version pfrom->PushMessage("verack"); pfrom->ssSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); if (!pfrom->fInbound) { // Advertise our address if (!fNoListen && !IsInitialBlockDownload()) { CAddress addr = GetLocalAddress(&pfrom->addr); if (addr.IsRoutable()) pfrom->PushAddress(addr); } // Get recent addresses if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) { pfrom->PushMessage("getaddr"); pfrom->fGetAddr = true; } addrman.Good(pfrom->addr); } else { if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom) { addrman.Add(addrFrom, addrFrom); addrman.Good(addrFrom); } } // Relay alerts { LOCK(cs_mapAlerts); BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) item.second.RelayTo(pfrom); } //Relay sync-checkpoint { LOCK(Checkpoints::cs_hashSyncCheckpoint); if (!Checkpoints::checkpointMessage.IsNull()) Checkpoints::checkpointMessage.RelayTo(pfrom); } pfrom->fSuccessfullyConnected = true; LogPrintf("receive version message: version %s, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->cleanSubVer, pfrom->nStartingHeight, addrMe.ToString(), addrFrom.ToString(), pfrom->addr.ToString()); cPeerBlockCounts.input(pfrom->nStartingHeight); // ppcoin: ask for pending sync-checkpoint if any if (!IsInitialBlockDownload()) Checkpoints::AskForPendingSyncCheckpoint(pfrom); } else if (pfrom->nVersion == 0) { // Must have a version message before anything else Misbehaving(pfrom->GetId(), 1); return false; } else if (strCommand == "verack") { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); } else if (strCommand == "addr") { vector<CAddress> vAddr; vRecv >> vAddr; // Don't want addr from older versions unless seeding if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000) return true; if (vAddr.size() > 1000) { Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %u", vAddr.size()); } // Store the new addresses vector<CAddress> vAddrOk; int64_t nNow = GetAdjustedTime(); int64_t nSince = nNow - 10 * 60; BOOST_FOREACH(CAddress& addr, vAddr) { if (fShutdown) return true; if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; pfrom->AddAddressKnown(addr); bool fReachable = IsReachable(addr); if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) { // Relay to a limited number of other nodes { LOCK(cs_vNodes); // Use deterministic randomness to send to the same nodes for 24 hours // at a time so the setAddrKnowns of the chosen nodes prevent repeats static uint256 hashSalt; if (hashSalt == 0) hashSalt = GetRandHash(); uint64_t hashAddr = addr.GetHash(); uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60)); hashRand = Hash(BEGIN(hashRand), END(hashRand)); multimap<uint256, CNode*> mapMix; BOOST_FOREACH(CNode* pnode, vNodes) { if (pnode->nVersion < CADDR_TIME_VERSION) continue; unsigned int nPointer; memcpy(&nPointer, &pnode, sizeof(nPointer)); uint256 hashKey = hashRand ^ nPointer; hashKey = Hash(BEGIN(hashKey), END(hashKey)); mapMix.insert(make_pair(hashKey, pnode)); } int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) for (multimap<uint256, CNode*>::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) ((*mi).second)->PushAddress(addr); } } // Do not store addresses outside our network if (fReachable) vAddrOk.push_back(addr); } addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60); if (vAddr.size() < 1000) pfrom->fGetAddr = false; if (pfrom->fOneShot) pfrom->fDisconnect = true; } else if (strCommand == "inv") { vector<CInv> vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { Misbehaving(pfrom->GetId(), 20); return error("message inv size() = %u", vInv.size()); } // find last block in inv vector unsigned int nLastBlock = (unsigned int)(-1); for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) { nLastBlock = vInv.size() - 1 - nInv; break; } } CTxDB txdb("r"); for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; if (fShutdown) return true; pfrom->AddInventoryKnown(inv); bool fAlreadyHave = AlreadyHave(txdb, inv); LogPrint("net", " got inventory: %s %s\n", inv.ToString(), fAlreadyHave ? "have" : "new"); if (!fAlreadyHave) pfrom->AskFor(inv); else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); } else if (nInv == nLastBlock) { // In case we are on a very long side-chain, it is possible that we already have // the last block in an inv bundle sent in response to getblocks. Try to detect // this situation and push another getblocks to continue. pfrom->PushGetBlocks(mapBlockIndex[inv.hash], uint256(0)); LogPrint("net", "force request: %s\n", inv.ToString()); } // Track requests for our stuff Inventory(inv.hash); } } else if (strCommand == "getdata") { vector<CInv> vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { Misbehaving(pfrom->GetId(), 20); return error("message getdata size() = %u", vInv.size()); } if (fDebug || (vInv.size() != 1)) LogPrint("net", "received getdata (%u invsz)\n", vInv.size()); BOOST_FOREACH(const CInv& inv, vInv) { if (fShutdown) return true; if (fDebug || (vInv.size() == 1)) LogPrint("net", "received getdata for: %s\n", inv.ToString()); if (inv.type == MSG_BLOCK) { // Send block from disk map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash); pfrom->nBlocksRequested++; if (mi != mapBlockIndex.end()) { CBlock block; block.ReadFromDisk((*mi).second); pfrom->PushMessage("block", block); // Trigger them to send a getblocks request for the next batch of inventory if (inv.hash == pfrom->hashContinue) { // ppcoin: send latest proof-of-work block to allow the // download node to accept as orphan (proof-of-stake // block might be rejected by stake connection check) vector<CInv> vInv; vInv.push_back(CInv(MSG_BLOCK, GetLastBlockIndex(pindexBest, false)->GetBlockHash())); pfrom->PushMessage("inv", vInv); pfrom->hashContinue = 0; } } } else if (inv.IsKnownType()) { // Send stream from relay memory bool pushed = false; { LOCK(cs_mapRelay); map<CInv, CDataStream>::iterator mi = mapRelay.find(inv); if (mi != mapRelay.end()) { pfrom->PushMessage(inv.GetCommand(), (*mi).second); pushed = true; } } if (!pushed && inv.type == MSG_TX) { CTransaction tx; if (mempool.lookup(inv.hash, tx)) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(1000); ss << tx; pfrom->PushMessage("tx", ss); } } } // Track requests for our stuff Inventory(inv.hash); } } else if (strCommand == "getblocks") { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; // Find the last block the caller has in the main chain CBlockIndex* pindex = locator.GetBlockIndex(); // Send the rest of the chain if (pindex) pindex = pindex->pnext; int nLimit = 500; LogPrint("net", "getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20), nLimit); for (; pindex; pindex = pindex->pnext) { if (pindex->GetBlockHash() == hashStop) { LogPrint("net", " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20)); // ppcoin: tell downloading node about the latest block if it's // without risk being rejected due to stake connection check if (hashStop != hashBestChain && pindex->GetBlockTime() + nStakeMinAge > pindexBest->GetBlockTime()) pfrom->PushInventory(CInv(MSG_BLOCK, hashBestChain)); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); if (--nLimit <= 0) { // When this block is requested, we'll send an inv that'll make them // getblocks the next batch of inventory. LogPrint("net", " getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20)); pfrom->hashContinue = pindex->GetBlockHash(); break; } } } else if (strCommand == "checkpoint") { CSyncCheckpoint checkpoint; vRecv >> checkpoint; if (checkpoint.ProcessSyncCheckpoint(pfrom)) { // Relay pfrom->hashCheckpointKnown = checkpoint.hashCheckpoint; LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) checkpoint.RelayTo(pnode); } } else if (strCommand == "getheaders") { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; CBlockIndex* pindex = NULL; if (locator.IsNull()) { // If locator is null, return the hashStop block map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashStop); if (mi == mapBlockIndex.end()) return true; pindex = (*mi).second; } else { // Find the last block the caller has in the main chain pindex = locator.GetBlockIndex(); if (pindex) pindex = pindex->pnext; } vector<CBlock> vHeaders; int nLimit = 2000; LogPrint("net", "getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20)); for (; pindex; pindex = pindex->pnext) { vHeaders.push_back(pindex->GetBlockHeader()); if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) break; } pfrom->PushMessage("headers", vHeaders); } else if (strCommand == "tx") { vector<uint256> vWorkQueue; vector<uint256> vEraseQueue; CTransaction tx; vRecv >> tx; CInv inv(MSG_TX, tx.GetHash()); pfrom->AddInventoryKnown(inv); bool fMissingInputs = false; if (AcceptToMemoryPool(mempool, tx, &fMissingInputs)) { RelayTransaction(tx, inv.hash); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); vEraseQueue.push_back(inv.hash); // Recursively process any orphan transactions that depended on this one for (unsigned int i = 0; i < vWorkQueue.size(); i++) { uint256 hashPrev = vWorkQueue[i]; for (set<uint256>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin(); mi != mapOrphanTransactionsByPrev[hashPrev].end(); ++mi) { const uint256& orphanTxHash = *mi; CTransaction& orphanTx = mapOrphanTransactions[orphanTxHash]; bool fMissingInputs2 = false; if (AcceptToMemoryPool(mempool, orphanTx, &fMissingInputs2)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanTxHash.ToString().substr(0,10)); RelayTransaction(orphanTx, orphanTxHash); mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanTxHash)); vWorkQueue.push_back(orphanTxHash); vEraseQueue.push_back(orphanTxHash); } else if (!fMissingInputs2) { // invalid orphan vEraseQueue.push_back(orphanTxHash); LogPrint("mempool", " removed invalid orphan tx %s\n", orphanTxHash.ToString().substr(0,10)); } } } BOOST_FOREACH(uint256 hash, vEraseQueue) EraseOrphanTx(hash); } else if (fMissingInputs) { AddOrphanTx(tx); // DoS prevention: do not allow mapOrphanTransactions to grow unbounded unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); if (nEvicted > 0) LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); } if (tx.nDoS) Misbehaving(pfrom->GetId(), tx.nDoS); } else if (strCommand == "block") { CBlock block; vRecv >> block; uint256 hashBlock = block.GetHash(); LogPrint("net", "received block %s sent from %s\n", hashBlock.ToString().substr(0,20), pfrom->addr.ToString()); CInv inv(MSG_BLOCK, hashBlock); pfrom->AddInventoryKnown(inv); if (ProcessBlock(pfrom, &block)) mapAlreadyAskedFor.erase(inv); if (block.nDoS) Misbehaving(pfrom->GetId(), block.nDoS); } else if (strCommand == "getaddr") { // Don't return addresses older than nCutOff timestamp int64_t nCutOff = GetTime() - (nNodeLifespan * 24 * 60 * 60); pfrom->vAddrToSend.clear(); vector<CAddress> vAddr = addrman.GetAddr(); BOOST_FOREACH(const CAddress &addr, vAddr) if(addr.nTime > nCutOff) pfrom->PushAddress(addr); } else if (strCommand == "mempool") { std::vector<uint256> vtxid; mempool.queryHashes(vtxid); vector<CInv> vInv; for (unsigned int i = 0; i < vtxid.size(); i++) { CInv inv(MSG_TX, vtxid[i]); vInv.push_back(inv); if (i == (MAX_INV_SZ - 1)) break; } if (vInv.size() > 0) pfrom->PushMessage("inv", vInv); } else if (strCommand == "checkorder") { uint256 hashReply; vRecv >> hashReply; if (!GetBoolArg("-allowreceivebyip")) { pfrom->PushMessage("reply", hashReply, (int)2, string("")); return true; } CWalletTx order; vRecv >> order; /// we have a chance to check the order here // Keep giving the same key to the same ip until they use it if (!mapReuseKey.count(pfrom->addr)) pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr], true); // Send back approval of order and pubkey to use CScript scriptPubKey; scriptPubKey << mapReuseKey[pfrom->addr] << OP_CHECKSIG; pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); } else if (strCommand == "reply") { uint256 hashReply; vRecv >> hashReply; CRequestTracker tracker; { LOCK(pfrom->cs_mapRequests); map<uint256, CRequestTracker>::iterator mi = pfrom->mapRequests.find(hashReply); if (mi != pfrom->mapRequests.end()) { tracker = (*mi).second; pfrom->mapRequests.erase(mi); } } if (!tracker.IsNull()) tracker.fn(tracker.param1, vRecv); } else if (strCommand == "ping") { if (pfrom->nVersion > BIP0031_VERSION) { uint64_t nonce = 0; vRecv >> nonce; // Echo the message back with the nonce. This allows for two useful features: // // 1) A remote node can quickly check if the connection is operational // 2) Remote nodes can measure the latency of the network thread. If this node // is overloaded it won't respond to pings quickly and the remote node can // avoid sending us more work, like chain download requests. // // The nonce stops the remote getting confused between different pings: without // it, if the remote node sends a ping once per second and this node takes 5 // seconds to respond to each, the 5th ping the remote sends would appear to // return very quickly. pfrom->PushMessage("pong", nonce); } } else if (strCommand == "pong") { int64_t pingUsecEnd = nTimeReceived; uint64_t nonce = 0; size_t nAvail = vRecv.in_avail(); bool bPingFinished = false; std::string sProblem; if (nAvail >= sizeof(nonce)) { vRecv >> nonce; // Only process pong message if there is an outstanding ping (old ping without nonce should never pong) if (pfrom->nPingNonceSent != 0) { if (nonce == pfrom->nPingNonceSent) { // Matching pong received, this ping is no longer outstanding bPingFinished = true; int64_t pingUsecTime = pingUsecEnd - pfrom->nPingUsecStart; if (pingUsecTime > 0) { // Successful ping time measurement, replace previous pfrom->nPingUsecTime = pingUsecTime; } else { // This should never happen sProblem = "Timing mishap"; } } else { // Nonce mismatches are normal when pings are overlapping sProblem = "Nonce mismatch"; if (nonce == 0) { // This is most likely a bug in another implementation somewhere, cancel this ping bPingFinished = true; sProblem = "Nonce zero"; } } } else { sProblem = "Unsolicited pong without ping"; } } else { // This is most likely a bug in another implementation somewhere, cancel this ping bPingFinished = true; sProblem = "Short payload"; } if (!(sProblem.empty())) { LogPrint("net", "pong %s %s: %s, %x expected, %x received, %u bytes\n" , pfrom->addr.ToString() , pfrom->cleanSubVer , sProblem , pfrom->nPingNonceSent , nonce , nAvail); } if (bPingFinished) { pfrom->nPingNonceSent = 0; } } else if (strCommand == "alert") { CAlert alert; vRecv >> alert; uint256 alertHash = alert.GetHash(); if (pfrom->setKnown.count(alertHash) == 0) { if (alert.ProcessAlert()) { // Relay pfrom->setKnown.insert(alertHash); { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) alert.RelayTo(pnode); } } else { // Small DoS penalty so peers that send us lots of // duplicate/expired/invalid-signature/whatever alerts // eventually get banned. // This isn't a Misbehaving(100) (immediate ban) because the // peer might be an older or different implementation with // a different signature key, etc. Misbehaving(pfrom->GetId(), 10); } } } else { // Ignore unknown commands for extensibility } // Update the last seen time for this node's address if (pfrom->fNetworkNode) if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping") AddressCurrentlyConnected(pfrom->addr); return true; } // requires LOCK(cs_vRecvMsg) bool ProcessMessages(CNode* pfrom) { // if (fDebug) // LogPrintf("ProcessMessages(%zu messages)\n", pfrom->vRecvMsg.size()); // // Message format // (4) message start // (12) command // (4) size // (4) checksum // (x) data // bool fOk = true; std::deque<CNetMessage>::iterator it = pfrom->vRecvMsg.begin(); while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->nSendSize >= SendBufferSize()) break; // get next message CNetMessage& msg = *it; //if (fDebug) // LogPrintf("ProcessMessages(message %u msgsz, %zu bytes, complete:%s)\n", // msg.hdr.nMessageSize, msg.vRecv.size(), // msg.complete() ? "Y" : "N"); // end, if an incomplete message is found if (!msg.complete()) break; // at this point, any failure means we can delete the current message it++; // Scan for message start if (memcmp(msg.hdr.pchMessageStart, pchMessageStart, sizeof(pchMessageStart)) != 0) { LogPrintf("\n\nPROCESSMESSAGE: INVALID MESSAGESTART\n\n"); fOk = false; break; } // Read header CMessageHeader& hdr = msg.hdr; if (!hdr.IsValid()) { LogPrintf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand()); continue; } string strCommand = hdr.GetCommand(); // Message size unsigned int nMessageSize = hdr.nMessageSize; // Checksum CDataStream& vRecv = msg.vRecv; uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); unsigned int nChecksum = 0; memcpy(&nChecksum, &hash, sizeof(nChecksum)); if (nChecksum != hdr.nChecksum) { LogPrintf("ProcessMessages(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", strCommand, nMessageSize, nChecksum, hdr.nChecksum); continue; } // Process message bool fRet = false; try { { LOCK(cs_main); fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime); } if (fShutdown) break; } catch (std::ios_base::failure& e) { if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand, nMessageSize, e.what()); } else if (strstr(e.what(), "size too large")) { // Allow exceptions from over-long size LogPrintf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand, nMessageSize, e.what()); } else { PrintExceptionContinue(&e, "ProcessMessages()"); } } catch (std::exception& e) { PrintExceptionContinue(&e, "ProcessMessages()"); } catch (...) { PrintExceptionContinue(NULL, "ProcessMessages()"); } if (!fRet) LogPrintf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand, nMessageSize); } // In case the connection got shut down, its receive buffer was wiped if (!pfrom->fDisconnect) pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); return fOk; } bool SendMessages(CNode* pto, bool fSendTrickle) { TRY_LOCK(cs_main, lockMain); if (lockMain) { // Don't send anything until we get their version message if (pto->nVersion == 0) return true; // // Message: ping // bool pingSend = false; if (pto->fPingQueued) { // RPC ping request by user pingSend = true; } if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) { // Ping automatically sent as a latency probe & keepalive. pingSend = true; } if (pingSend) { uint64_t nonce = 0; while (nonce == 0) { RAND_bytes((unsigned char*)&nonce, sizeof(nonce)); } pto->fPingQueued = false; pto->nPingUsecStart = GetTimeMicros(); if (pto->nVersion > BIP0031_VERSION) { pto->nPingNonceSent = nonce; pto->PushMessage("ping", nonce); } else { // Peer is too old to support ping command with nonce, pong will never arrive. pto->nPingNonceSent = 0; pto->PushMessage("ping"); } } // Start block sync if (pto->fStartSync && !fImporting && !fReindex) { pto->fStartSync = false; pto->PushGetBlocks(pindexBest, uint256(0)); } // Resend wallet transactions that haven't gotten in a block yet ResendWalletTransactions(); // Address refresh broadcast static int64_t nLastRebroadcast; if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) { { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { // Periodically clear setAddrKnown to allow refresh broadcasts if (nLastRebroadcast) pnode->setAddrKnown.clear(); // Rebroadcast our address if (!fNoListen) { CAddress addr = GetLocalAddress(&pnode->addr); if (addr.IsRoutable()) pnode->PushAddress(addr); } } } nLastRebroadcast = GetTime(); } // // Message: addr // if (fSendTrickle) { vector<CAddress> vAddr; vAddr.reserve(pto->vAddrToSend.size()); BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend) { // returns true if wasn't already contained in the set if (pto->setAddrKnown.insert(addr).second) { vAddr.push_back(addr); // receiver rejects addr messages larger than 1000 if (vAddr.size() >= 1000) { pto->PushMessage("addr", vAddr); vAddr.clear(); } } } pto->vAddrToSend.clear(); if (!vAddr.empty()) pto->PushMessage("addr", vAddr); } TRY_LOCK(cs_main, lockMain); if (!lockMain) return true; if (State(pto->GetId())->fShouldBan) { if (pto->addr.IsLocal()) LogPrint("net", "Warning: not banning local node %s!\n", pto->addr.ToString()); else { pto->fDisconnect = true; CNode::Ban(pto->addr); } State(pto->GetId())->fShouldBan = false; } // // Message: inventory // vector<CInv> vInv; vector<CInv> vInvWait; { LOCK(pto->cs_inventory); vInv.reserve(pto->vInventoryToSend.size()); vInvWait.reserve(pto->vInventoryToSend.size()); BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) { if (pto->setInventoryKnown.count(inv)) continue; // trickle out tx inv to protect privacy if (inv.type == MSG_TX && !fSendTrickle) { // 1/4 of tx invs blast to all immediately static uint256 hashSalt; if (hashSalt == 0) hashSalt = GetRandHash(); uint256 hashRand = inv.hash ^ hashSalt; hashRand = Hash(BEGIN(hashRand), END(hashRand)); bool fTrickleWait = ((hashRand & 3) != 0); // always trickle our own transactions if (!fTrickleWait) { CWalletTx wtx; if (GetTransaction(inv.hash, wtx)) if (wtx.fFromMe) fTrickleWait = true; } if (fTrickleWait) { vInvWait.push_back(inv); continue; } } // returns true if wasn't already contained in the set if (pto->setInventoryKnown.insert(inv).second) { vInv.push_back(inv); if (vInv.size() >= 1000) { pto->PushMessage("inv", vInv); vInv.clear(); } } } pto->vInventoryToSend = vInvWait; } if (!vInv.empty()) pto->PushMessage("inv", vInv); // // Message: getdata // vector<CInv> vGetData; int64_t nNow = GetTime() * 1000000; CTxDB txdb("r"); while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) { const CInv& inv = (*pto->mapAskFor.begin()).second; if (!AlreadyHave(txdb, inv)) { if (fDebug) LogPrint("net", "sending getdata: %s\n", inv.ToString()); vGetData.push_back(inv); if (vGetData.size() >= 1000) { pto->PushMessage("getdata", vGetData); vGetData.clear(); } mapAlreadyAskedFor[inv] = nNow; } pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) pto->PushMessage("getdata", vGetData); } return true; }
更改后文本
打开文件
// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "alert.h" #include "checkpoints.h" #include "db.h" #include "net.h" #include "init.h" #include "ui_interface.h" #include "kernel.h" #include "scrypt_mine.h" #include <boost/algorithm/string/replace.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> #include <boost/random/mersenne_twister.hpp> #include <boost/random/uniform_int_distribution.hpp> using namespace std; using namespace boost; // // Global state // CCriticalSection cs_setpwalletRegistered; set<CWallet*> setpwalletRegistered; CCriticalSection cs_main; CTxMemPool mempool; unsigned int nTransactionsUpdated = 0; map<uint256, CBlockIndex*> mapBlockIndex; set<pair<COutPoint, unsigned int> > setStakeSeen; uint256 hashGenesisBlock = hashGenesisBlockOfficial; static CBigNum bnProofOfWorkLimit(~uint256(0) >> 20); static CBigNum bnProofOfStakeLimit(~uint256(0) >> 20); static CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 20); static CBigNum bnProofOfStakeLimitTestNet(~uint256(0) >> 20); unsigned int nStakeMinAge = 60 * 60 * 8; // time at which weight begins to build unsigned int nStakeMinAgeV2 = 60 * 60 * 24 * 88 / 10; // functionally the minimum age is 8.8 days unsigned int nStakeMaxAge = 60 * 60 * 24 * 30; // stake age of full weight: -1 unsigned int nStakeTargetSpacing = 90; // 90 sec block spacing int64 nChainStartTime = 1399495660; int nCoinbaseMaturity = 10; CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; CBigNum bnBestChainTrust = 0; CBigNum bnBestInvalidTrust = 0; uint256 hashBestChain = 0; CBlockIndex* pindexBest = NULL; int64 nTimeBestReceived = 0; bool fHaveGUI = false; CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have map<uint256, CBlock*> mapOrphanBlocks; multimap<uint256, CBlock*> mapOrphanBlocksByPrev; set<pair<COutPoint, unsigned int> > setStakeSeenOrphan; map<uint256, uint256> mapProofOfStake; map<uint256, CDataStream*> mapOrphanTransactions; map<uint256, map<uint256, CDataStream*> > mapOrphanTransactionsByPrev; map<unsigned int, unsigned int> mapHashedBlocks; map<std::string, std::pair<int, int> > mapGetBlocksRequests; std::map <std::string, int> mapPeerRejectedBlocks; bool fStrictProtocol = false; bool fStrictIncoming = false; // Constant stuff for coinbase transactions we create: CScript COINBASE_FLAGS; const string strMessageMagic = "HyperStake Signed Message:\n"; double dHashesPerSec; int64 nHPSTimerStart; // Settings int64 nTransactionFee = MIN_TX_FEE; ////////////////////////////////////////////////////////////////////////////// // // dispatching functions // // These functions dispatch to one or all registered wallets void RegisterWallet(CWallet* pwalletIn) { { LOCK(cs_setpwalletRegistered); setpwalletRegistered.insert(pwalletIn); } } void UnregisterWallet(CWallet* pwalletIn) { { LOCK(cs_setpwalletRegistered); setpwalletRegistered.erase(pwalletIn); } } // check whether the passed transaction is from us bool static IsFromMe(CTransaction& tx) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) if (pwallet->IsFromMe(tx)) return true; return false; } // get the wallet transaction with the given hash (if it exists) bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) if (pwallet->GetTransaction(hashTx,wtx)) return true; return false; } // erases transaction with the given hash from all wallets void static EraseFromWallets(uint256 hash) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->EraseFromWallet(hash); } // make sure all wallets know about the given transaction, in the given block void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fConnect) { if (!fConnect) { // ppcoin: wallets need to refund inputs when disconnecting coinstake if (tx.IsCoinStake()) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) if (pwallet->IsFromMe(tx)) pwallet->DisableTransaction(tx); } return; } BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); } // notify wallets about a new best chain void static SetBestChain(const CBlockLocator& loc) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->SetBestChain(loc); } // notify wallets about an updated transaction void static UpdatedTransaction(const uint256& hashTx) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->UpdatedTransaction(hashTx); } // dump all wallets void static PrintWallets(const CBlock& block) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->PrintWallet(block); } // notify wallets about an incoming inventory (for request counts) void static Inventory(const uint256& hash) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->Inventory(hash); } // ask wallets to resend their transactions void ResendWalletTransactions() { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->ResendWalletTransactions(); } ////////////////////////////////////////////////////////////////////////////// // // mapOrphanTransactions // bool AddOrphanTx(const CDataStream& vMsg) { CTransaction tx; CDataStream(vMsg) >> tx; uint256 hash = tx.GetHash(); if (mapOrphanTransactions.count(hash)) return false; CDataStream* pvMsg = new CDataStream(vMsg); // Ignore big transactions, to avoid a // send-big-orphans memory exhaustion attack. If a peer has a legitimate // large transaction with a missing parent then we assume // it will rebroadcast it later, after the parent transaction(s) // have been mined or received. // 10,000 orphans, each of which is at most 5,000 bytes big is // at most 500 megabytes of orphans: if (pvMsg->size() > 5000) { printf("ignoring large orphan tx (size: %"PRIszu", hash: %s)\n", pvMsg->size(), hash.ToString().substr(0,10).c_str()); delete pvMsg; return false; } mapOrphanTransactions[hash] = pvMsg; BOOST_FOREACH(const CTxIn& txin, tx.vin) mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg)); printf("stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().substr(0,10).c_str(), mapOrphanTransactions.size()); return true; } void static EraseOrphanTx(uint256 hash) { if (!mapOrphanTransactions.count(hash)) return; const CDataStream* pvMsg = mapOrphanTransactions[hash]; CTransaction tx; CDataStream(*pvMsg) >> tx; BOOST_FOREACH(const CTxIn& txin, tx.vin) { mapOrphanTransactionsByPrev[txin.prevout.hash].erase(hash); if (mapOrphanTransactionsByPrev[txin.prevout.hash].empty()) mapOrphanTransactionsByPrev.erase(txin.prevout.hash); } delete pvMsg; mapOrphanTransactions.erase(hash); } unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) { unsigned int nEvicted = 0; while (mapOrphanTransactions.size() > nMaxOrphans) { // Evict a random orphan: uint256 randomhash = GetRandHash(); map<uint256, CDataStream*>::iterator it = mapOrphanTransactions.lower_bound(randomhash); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); EraseOrphanTx(it->first); ++nEvicted; } return nEvicted; } ////////////////////////////////////////////////////////////////////////////// // // CTransaction and CTxIndex // bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet) { SetNull(); if (!txdb.ReadTxIndex(prevout.hash, txindexRet)) return false; if (!ReadFromDisk(txindexRet.pos)) return false; if (prevout.n >= vout.size()) { SetNull(); return false; } return true; } bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout) { CTxIndex txindex; return ReadFromDisk(txdb, prevout, txindex); } bool CTransaction::ReadFromDisk(COutPoint prevout) { CTxDB txdb("r"); CTxIndex txindex; return ReadFromDisk(txdb, prevout, txindex); } bool CTransaction::IsStandard() const { if (nVersion > CTransaction::CURRENT_VERSION) return false; BOOST_FOREACH(const CTxIn& txin, vin) { // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG // pay-to-script-hash, which is 3 ~80-byte signatures, 3 // ~65-byte public keys, plus a few script ops. if (txin.scriptSig.size() > 500) return false; if (!txin.scriptSig.IsPushOnly()) return false; } BOOST_FOREACH(const CTxOut& txout, vout) { if (!::IsStandard(txout.scriptPubKey)) return false; if (txout.nValue == 0) return false; } return true; } // // Check transaction inputs, and make sure any // pay-to-script-hash transactions are evaluating IsStandard scripts // // Why bother? To avoid denial-of-service attacks; an attacker // can submit a standard HASH... OP_EQUAL transaction, // which will get accepted into blocks. The redemption // script can be anything; an attacker could use a very // expensive-to-check-upon-redemption script like: // DUP CHECKSIG DROP ... repeated 100 times... OP_1 // bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const { if (IsCoinBase()) return true; // Coinbases don't use vin normally for (unsigned int i = 0; i < vin.size(); i++) { const CTxOut& prev = GetOutputFor(vin[i], mapInputs); vector<vector<unsigned char> > vSolutions; txnouttype whichType; // get the scriptPubKey corresponding to this input: const CScript& prevScript = prev.scriptPubKey; if (!Solver(prevScript, whichType, vSolutions)) return false; int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions); if (nArgsExpected < 0) return false; // Transactions with extra stuff in their scriptSigs are // non-standard. Note that this EvalScript() call will // be quick, because if there are any operations // beside "push data" in the scriptSig the // IsStandard() call returns false vector<vector<unsigned char> > stack; if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0)) return false; if (whichType == TX_SCRIPTHASH) { if (stack.empty()) return false; CScript subscript(stack.back().begin(), stack.back().end()); vector<vector<unsigned char> > vSolutions2; txnouttype whichType2; if (!Solver(subscript, whichType2, vSolutions2)) return false; if (whichType2 == TX_SCRIPTHASH) return false; int tmpExpected; tmpExpected = ScriptSigArgsExpected(whichType2, vSolutions2); if (tmpExpected < 0) return false; nArgsExpected += tmpExpected; } if (stack.size() != (unsigned int)nArgsExpected) return false; } return true; } unsigned int CTransaction::GetLegacySigOpCount() const { unsigned int nSigOps = 0; BOOST_FOREACH(const CTxIn& txin, vin) { nSigOps += txin.scriptSig.GetSigOpCount(false); } BOOST_FOREACH(const CTxOut& txout, vout) { nSigOps += txout.scriptPubKey.GetSigOpCount(false); } return nSigOps; } int CMerkleTx::SetMerkleBranch(const CBlock* pblock) { if (fClient) { if (hashBlock == 0) return 0; } else { CBlock blockTmp; if (pblock == NULL) { // Load the block this tx is in CTxIndex txindex; if (!CTxDB("r").ReadTxIndex(GetHash(), txindex)) return 0; if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos)) return 0; pblock = &blockTmp; } // Update the tx's hashBlock hashBlock = pblock->GetHash(); // Locate the transaction for (nIndex = 0; nIndex < (int)pblock->vtx.size(); nIndex++) if (pblock->vtx[nIndex] == *(CTransaction*)this) break; if (nIndex == (int)pblock->vtx.size()) { vMerkleBranch.clear(); nIndex = -1; printf("ERROR: SetMerkleBranch() : couldn't find tx in block\n"); return 0; } // Fill in merkle branch vMerkleBranch = pblock->GetMerkleBranch(nIndex); } // Is the tx in a block that's in the main chain map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; if (!pindex || !pindex->IsInMainChain()) return 0; return pindexBest->nHeight - pindex->nHeight + 1; } bool CTransaction::CheckTransaction() const { // Basic checks that don't depend on any context if (vin.empty()) return DoS(10, error("CTransaction::CheckTransaction() : vin empty")); if (vout.empty()) return DoS(10, error("CTransaction::CheckTransaction() : vout empty")); // Size limits if (::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return DoS(100, error("CTransaction::CheckTransaction() : size limits failed")); // Check for negative or overflow output values int64 nValueOut = 0; for (unsigned int i = 0; i < vout.size(); i++) { const CTxOut& txout = vout[i]; if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake()) return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction")); // ppcoin: enforce minimum output amount if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT) return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum")); if (txout.nValue > MAX_MONEY) return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high")); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range")); } // Check for duplicate inputs set<COutPoint> vInOutPoints; BOOST_FOREACH(const CTxIn& txin, vin) { if (vInOutPoints.count(txin.prevout)) return false; vInOutPoints.insert(txin.prevout); } if (IsCoinBase()) { if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100) return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size")); } else { BOOST_FOREACH(const CTxIn& txin, vin) if (txin.prevout.IsNull()) return DoS(10, error("CTransaction::CheckTransaction() : prevout is null")); } return true; } int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree, enum GetMinFee_mode mode, unsigned int nBytes) const { // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE int64 nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE; unsigned int nNewBlockSize = nBlockSize + nBytes; int64 nMinFee = (1 + (int64)nBytes / 1000) * nBaseFee; // To limit dust spam, require MIN_TX_FEE/MIN_RELAY_TX_FEE if any output is less than 0.01 if (nMinFee < nBaseFee) { BOOST_FOREACH(const CTxOut& txout, vout) if (txout.nValue < CENT) nMinFee = nBaseFee; } // Raise the price as the block approaches full if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2) { if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN) return MAX_MONEY; nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize); } if (!MoneyRange(nMinFee)) nMinFee = MAX_MONEY; return max(nMinFee, MIN_TX_FEE); } bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs) { if (pfMissingInputs) *pfMissingInputs = false; if (!tx.CheckTransaction()) return error("CTxMemPool::accept() : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) return tx.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); // ppcoin: coinstake is also only valid in a block, not as a loose transaction if (tx.IsCoinStake()) return tx.DoS(100, error("CTxMemPool::accept() : coinstake as individual tx")); // To help v0.1.5 clients who would see it as a negative number if ((int64)tx.nLockTime > std::numeric_limits<int>::max()) return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); // Rather not work on nonstandard transactions (unless -testnet) if (!fTestNet && !tx.IsStandard()) return error("CTxMemPool::accept() : nonstandard transaction type"); // Do we already have it? uint256 hash = tx.GetHash(); { LOCK(cs); if (mapTx.count(hash)) return false; } if (fCheckInputs) if (txdb.ContainsTx(hash)) return false; // Check for conflicts with in-memory transactions CTransaction* ptxOld = NULL; for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint outpoint = tx.vin[i].prevout; if (mapNextTx.count(outpoint)) { // Disable replacement feature for now return false; // Allow replacing with a newer version of the same transaction if (i != 0) return false; ptxOld = mapNextTx[outpoint].ptx; if (ptxOld->IsFinal()) return false; if (!tx.IsNewerThan(*ptxOld)) return false; for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint outpoint = tx.vin[i].prevout; if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) return false; } break; } } if (fCheckInputs) { MapPrevTx mapInputs; map<uint256, CTxIndex> mapUnused; bool fInvalid = false; if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) { if (fInvalid) return error("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); if (pfMissingInputs) *pfMissingInputs = true; return false; } // Check for non-standard pay-to-script-hash in inputs if (!tx.AreInputsStandard(mapInputs) && !fTestNet) return error("CTxMemPool::accept() : nonstandard transaction input"); // Note: if you modify this code to accept non-standard transactions, then // you should add code here to check that the transaction does a // reasonable number of ECDSA signature verifications. int64 nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); // Don't accept it if it can't get into a block int64 txMinFee = tx.GetMinFee(1000, false, GMF_RELAY, nSize); if (nFees < txMinFee) return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d, hash.ToString().c_str(), nFees, txMinFee); // Continuously rate-limit free transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. if (nFees < MIN_RELAY_TX_FEE) { static CCriticalSection cs; static double dFreeCount; static int64 nLastTime; int64 nNow = GetTime(); { LOCK(cs); // Use an exponentially decaying ~10-minute window: dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); nLastTime = nNow; // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(tx)) return error("CTxMemPool::accept() : free transaction rejected by rate limiter"); if (fDebug) printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } } // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. if (!tx.ConnectInputs(txdb, mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) { return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); } } // Store transaction in memory { LOCK(cs); if (ptxOld) { printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); remove(*ptxOld); } addUnchecked(hash, tx); } ///// are we sure this is ok when loading transactions or restoring block txes // If updated, erase old tx from wallet if (ptxOld) EraseFromWallets(ptxOld->GetHash()); printf("CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n", hash.ToString().substr(0,10).c_str(), mapTx.size()); return true; } bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs) { return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs); } bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx) { // Add to memory pool without checking anything. Don't call this directly, // call CTxMemPool::accept to properly check the transaction first. { mapTx[hash] = tx; for (unsigned int i = 0; i < tx.vin.size(); i++) mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); nTransactionsUpdated++; } return true; } bool CTxMemPool::remove(CTransaction &tx) { // Remove transaction from memory pool { LOCK(cs); uint256 hash = tx.GetHash(); if (mapTx.count(hash)) { BOOST_FOREACH(const CTxIn& txin, tx.vin) mapNextTx.erase(txin.prevout); mapTx.erase(hash); nTransactionsUpdated++; } } return true; } void CTxMemPool::clear() { LOCK(cs); mapTx.clear(); mapNextTx.clear(); ++nTransactionsUpdated; } void CTxMemPool::queryHashes(std::vector<uint256>& vtxid) { vtxid.clear(); LOCK(cs); vtxid.reserve(mapTx.size()); for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) vtxid.push_back((*mi).first); } int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const { if (hashBlock == 0 || nIndex == -1) return 0; // Find the block it claims to be in map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock); if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; if (!pindex || !pindex->IsInMainChain()) return 0; // Make sure the merkle branch connects to this block if (!fMerkleVerified) { if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) return 0; fMerkleVerified = true; } pindexRet = pindex; return pindexBest->nHeight - pindex->nHeight + 1; } int CMerkleTx::GetBlocksToMaturity() const { if (!(IsCoinBase() || IsCoinStake())) return 0; return max(0, (nCoinbaseMaturity+20) - GetDepthInMainChain()); } bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs) { if (fClient) { if (!IsInMainChain() && !ClientConnectInputs()) return false; return CTransaction::AcceptToMemoryPool(txdb, false); } else { return CTransaction::AcceptToMemoryPool(txdb, fCheckInputs); } } bool CMerkleTx::AcceptToMemoryPool() { CTxDB txdb("r"); return AcceptToMemoryPool(txdb); } bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs) { { LOCK(mempool.cs); // Add previous supporting transactions first BOOST_FOREACH(CMerkleTx& tx, vtxPrev) { if (!(tx.IsCoinBase() || tx.IsCoinStake())) { uint256 hash = tx.GetHash(); if (!mempool.exists(hash) && !txdb.ContainsTx(hash)) tx.AcceptToMemoryPool(txdb, fCheckInputs); } } return AcceptToMemoryPool(txdb, fCheckInputs); } return false; } bool CWalletTx::AcceptWalletTransaction() { CTxDB txdb("r"); return AcceptWalletTransaction(txdb); } int CTxIndex::GetDepthInMainChain() const { // Read block header CBlock block; if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false)) return 0; // Find the block in the index map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.GetHash()); if (mi == mapBlockIndex.end()) return 0; CBlockIndex* pindex = (*mi).second; if (!pindex || !pindex->IsInMainChain()) return 0; return 1 + nBestHeight - pindex->nHeight; } // Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock) { { LOCK(cs_main); { LOCK(mempool.cs); if (mempool.exists(hash)) { tx = mempool.lookup(hash); return true; } } CTxDB txdb("r"); CTxIndex txindex; if (tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex)) { CBlock block; if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) hashBlock = block.GetHash(); return true; } } return false; } ////////////////////////////////////////////////////////////////////////////// // // CBlock and CBlockIndex // static CBlockIndex* pblockindexFBBHLast; CBlockIndex* FindBlockByHeight(int nHeight) { CBlockIndex *pblockindex; if (nHeight < nBestHeight / 2) pblockindex = pindexGenesisBlock; else pblockindex = pindexBest; if (pblockindexFBBHLast && abs(nHeight - pblockindex->nHeight) > abs(nHeight - pblockindexFBBHLast->nHeight)) pblockindex = pblockindexFBBHLast; while (pblockindex->nHeight > nHeight) pblockindex = pblockindex->pprev; while (pblockindex->nHeight < nHeight) pblockindex = pblockindex->pnext; pblockindexFBBHLast = pblockindex; return pblockindex; } bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions) { if (!fReadTransactions) { *this = pindex->GetBlockHeader(); return true; } if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions)) return false; if (GetHash() != pindex->GetBlockHash()) return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); return true; } uint256 static GetOrphanRoot(const CBlock* pblock) { // Work back to the first block in the orphan chain while (mapOrphanBlocks.count(pblock->hashPrevBlock)) pblock = mapOrphanBlocks[pblock->hashPrevBlock]; return pblock->GetHash(); } // ppcoin: find block wanted by given orphan block uint256 WantedByOrphan(const CBlock* pblockOrphan) { // Work back to the first block in the orphan chain while (mapOrphanBlocks.count(pblockOrphan->hashPrevBlock)) pblockOrphan = mapOrphanBlocks[pblockOrphan->hashPrevBlock]; return pblockOrphan->hashPrevBlock; } int generateMTRandom(unsigned int s, int range) { random::mt19937 gen(s); random::uniform_int_distribution<> dist(0, range); return dist(gen); } static const int CUTOFF_HEIGHT = POW_CUTOFF_HEIGHT; // miner's coin base reward based on nBits int64 GetProofOfWorkReward(int nHeight, int64 nFees, uint256 prevHash) { int64 nSubsidy = 0; if(fTestNet) { if(nHeight == 1) nSubsidy = 50000000 * COIN; // 50 million premine for testnet so we can be rich else nSubsidy = 1000 * COIN; return nSubsidy + nFees; } nSubsidy = 500 * COIN; if(nHeight == 1) { nSubsidy = 120000 * COIN; return nSubsidy + nFees; } return nSubsidy + nFees; } // miner's coin stake reward based on nBits and coin age spent (coin-days) // simple algorithm, not depend on the diff int64 GetProofOfStakeReward(int64 nCoinAge, unsigned int nBits, unsigned int nTime, int nHeight) { int64 nSubsidy = 0; if ( nTime > FORK_TIME ) nSubsidy = GetProofOfStakeRewardV2(nCoinAge, nBits, nTime,nHeight); else nSubsidy = GetProofOfStakeRewardV1(nCoinAge, nBits, nTime, nHeight); return nSubsidy; } int64 GetProofOfStakeRewardV1(int64 nCoinAge, unsigned int nBits, unsigned int nTime, int nHeight) { int64 nRewardCoinYear; nRewardCoinYear = MAX_MINT_PROOF_OF_STAKE; int64 nSubsidy = nCoinAge * nRewardCoinYear / 365; if (fDebug && GetBoolArg("-printcreation")) printf("GetProofOfStakeReward(): create=%s nCoinAge=%"PRI64d" nBits=%d\n", FormatMoney(nSubsidy).c_str(), nCoinAge, nBits); return nSubsidy; } int64 GetProofOfStakeRewardV2(int64 nCoinAge, unsigned int nBits, unsigned int nTime, int nHeight) { int64 nRewardCoinYear, nSubsidyLimit = 1000 * COIN; nRewardCoinYear = MAX_MINT_PROOF_OF_STAKEV2; int64 nSubsidy = nCoinAge * nRewardCoinYear / 365; if (fDebug && GetBoolArg("-printcreation")) printf("GetProofOfStakeReward(): create=%s nCoinAge=%"PRI64d" nBits=%d\n", FormatMoney(nSubsidy).c_str(), nCoinAge, nBits); nSubsidy = min(nSubsidy, nSubsidyLimit); return nSubsidy; } static const int64 nTargetTimespan = 60 * 60; static const int64 nTargetSpacingWorkMax = 2 * nStakeTargetSpacing; // // maximum nBits value could possible be required nTime after // minimum proof-of-work required was nBase // unsigned int ComputeMaxBits(CBigNum bnTargetLimit, unsigned int nBase, int64 nTime) { CBigNum bnResult; bnResult.SetCompact(nBase); bnResult *= 2; while (nTime > 0 && bnResult < bnTargetLimit) { // Maximum 200% adjustment per day... bnResult *= 2; nTime -= 24 * 60 * 60; } if (bnResult > bnTargetLimit) bnResult = bnTargetLimit; return bnResult.GetCompact(); } // // minimum amount of work that could possibly be required nTime after // minimum proof-of-work required was nBase // unsigned int ComputeMinWork(unsigned int nBase, int64 nTime) { return ComputeMaxBits(bnProofOfWorkLimit, nBase, nTime); } // // minimum amount of stake that could possibly be required nTime after // minimum proof-of-stake required was nBase // unsigned int ComputeMinStake(unsigned int nBase, int64 nTime, unsigned int nBlockTime) { return ComputeMaxBits(bnProofOfStakeLimit, nBase, nTime); } // ppcoin: find last block index up to pindex const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake) { while (pindex && pindex->pprev && (pindex->IsProofOfStake() != fProofOfStake)) pindex = pindex->pprev; return pindex; } unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake) { CBigNum bnTargetLimit = bnProofOfWorkLimit; if(fProofOfStake) { // Proof-of-Stake blocks has own target limit since nVersion=3 supermajority on mainNet and always on testNet bnTargetLimit = bnProofOfStakeLimit; } if (pindexLast == NULL) return bnTargetLimit.GetCompact(); // genesis block const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake); if (pindexPrev->pprev == NULL) return bnTargetLimit.GetCompact(); // first block const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake); if (pindexPrevPrev->pprev == NULL) return bnTargetLimit.GetCompact(); // second block int64 nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime(); if(nActualSpacing < 0) { // printf(">> nActualSpacing = %"PRI64d" corrected to 1.\n", nActualSpacing); nActualSpacing = 1; } else if(nActualSpacing > nTargetTimespan) { // printf(">> nActualSpacing = %"PRI64d" corrected to nTargetTimespan (900).\n", nActualSpacing); nActualSpacing = nTargetTimespan; } // ppcoin: target change every block // ppcoin: retarget with exponential moving toward target spacing CBigNum bnNew; bnNew.SetCompact(pindexPrev->nBits); int64 nTargetSpacing = fProofOfStake? nStakeTargetSpacing : min(nTargetSpacingWorkMax, (int64) nStakeTargetSpacing * (1 + pindexLast->nHeight - pindexPrev->nHeight)); int64 nInterval = nTargetTimespan / nTargetSpacing; bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing); bnNew /= ((nInterval + 1) * nTargetSpacing); /* printf(">> Height = %d, fProofOfStake = %d, nInterval = %"PRI64d", nTargetSpacing = %"PRI64d", nActualSpacing = %"PRI64d"\n", pindexPrev->nHeight, fProofOfStake, nInterval, nTargetSpacing, nActualSpacing); printf(">> pindexPrev->GetBlockTime() = %"PRI64d", pindexPrev->nHeight = %d, pindexPrevPrev->GetBlockTime() = %"PRI64d", pindexPrevPrev->nHeight = %d\n", pindexPrev->GetBlockTime(), pindexPrev->nHeight, pindexPrevPrev->GetBlockTime(), pindexPrevPrev->nHeight); */ if (bnNew > bnTargetLimit) bnNew = bnTargetLimit; return bnNew.GetCompact(); } bool CheckProofOfWork(uint256 hash, unsigned int nBits) { CBigNum bnTarget; bnTarget.SetCompact(nBits); // Check range if (bnTarget <= 0 || bnTarget > bnProofOfWorkLimit) return error("CheckProofOfWork() : nBits below minimum work"); // Check proof of work matches claimed amount if (hash > bnTarget.getuint256()) return error("CheckProofOfWork() : hash doesn't match nBits"); return true; } // Return maximum amount of blocks that other nodes claim to have int GetNumBlocksOfPeers() { return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate()); } bool IsInitialBlockDownload() { if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate()) return true; static int64 nLastUpdate; static CBlockIndex* pindexLastBest; if (pindexBest != pindexLastBest) { pindexLastBest = pindexBest; nLastUpdate = GetTime(); } return (GetTime() - nLastUpdate < 10 && pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); } void static InvalidChainFound(CBlockIndex* pindexNew) { if (pindexNew->bnChainTrust > bnBestInvalidTrust) { bnBestInvalidTrust = pindexNew->bnChainTrust; CTxDB().WriteBestInvalidTrust(bnBestInvalidTrust); uiInterface.NotifyBlocksChanged(); } printf("InvalidChainFound: invalid block=%s height=%d trust=%s date=%s\n", pindexNew->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->nHeight, pindexNew->bnChainTrust.ToString().c_str(), DateTimeStrFormat("%x %H:%M:%S", pindexNew->GetBlockTime()).c_str()); printf("InvalidChainFound: current best=%s height=%d trust=%s date=%s\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, bnBestChainTrust.ToString().c_str(), DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); } void CBlock::UpdateTime(const CBlockIndex* pindexPrev) { nTime = max(GetBlockTime(), GetAdjustedTime()); } bool CTransaction::DisconnectInputs(CTxDB& txdb) { // Relinquish previous transactions' spent pointers if (!IsCoinBase()) { BOOST_FOREACH(const CTxIn& txin, vin) { COutPoint prevout = txin.prevout; // Get prev txindex from disk CTxIndex txindex; if (!txdb.ReadTxIndex(prevout.hash, txindex)) return error("DisconnectInputs() : ReadTxIndex failed"); if (prevout.n >= txindex.vSpent.size()) return error("DisconnectInputs() : prevout.n out of range"); // Mark outpoint as not spent txindex.vSpent[prevout.n].SetNull(); // Write back if (!txdb.UpdateTxIndex(prevout.hash, txindex)) return error("DisconnectInputs() : UpdateTxIndex failed"); } } // Remove transaction from index // This can fail if a duplicate of this transaction was in a chain that got // reorganized away. This is only possible if this transaction was completely // spent, so erasing it would be a no-op anyway. txdb.EraseTxIndex(*this); return true; } bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool, bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid) { // FetchInputs can return false either because we just haven't seen some inputs // (in which case the transaction should be stored as an orphan) // or because the transaction is malformed (in which case the transaction should // be dropped). If tx is definitely invalid, fInvalid will be set to true. fInvalid = false; if (IsCoinBase()) return true; // Coinbase transactions have no inputs to fetch. for (unsigned int i = 0; i < vin.size(); i++) { COutPoint prevout = vin[i].prevout; if (inputsRet.count(prevout.hash)) continue; // Got it already // Read txindex CTxIndex& txindex = inputsRet[prevout.hash].first; bool fFound = true; if ((fBlock || fMiner) && mapTestPool.count(prevout.hash)) { // Get txindex from current proposed changes txindex = mapTestPool.find(prevout.hash)->second; } else { // Read txindex from txdb fFound = txdb.ReadTxIndex(prevout.hash, txindex); } if (!fFound && (fBlock || fMiner)) return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); // Read txPrev CTransaction& txPrev = inputsRet[prevout.hash].second; if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) { // Get prev tx from single transactions in memory { LOCK(mempool.cs); if (!mempool.exists(prevout.hash)) return error("FetchInputs() : %s mempool Tx prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); txPrev = mempool.lookup(prevout.hash); } if (!fFound) txindex.vSpent.resize(txPrev.vout.size()); } else { // Get prev tx from disk if (!txPrev.ReadFromDisk(txindex.pos)) return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str()); } } // Make sure all prevout.n indexes are valid: for (unsigned int i = 0; i < vin.size(); i++) { const COutPoint prevout = vin[i].prevout; assert(inputsRet.count(prevout.hash) != 0); const CTxIndex& txindex = inputsRet[prevout.hash].first; const CTransaction& txPrev = inputsRet[prevout.hash].second; if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) { // Revisit this if/when transaction replacement is implemented and allows // adding inputs: fInvalid = true; return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %"PRIszu" %"PRIszu" prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); } } return true; } const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const { MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash); if (mi == inputs.end()) throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found"); const CTransaction& txPrev = (mi->second).second; if (input.prevout.n >= txPrev.vout.size()) throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range"); return txPrev.vout[input.prevout.n]; } int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const { if (IsCoinBase()) return 0; int64 nResult = 0; for (unsigned int i = 0; i < vin.size(); i++) { nResult += GetOutputFor(vin[i], inputs).nValue; } return nResult; } unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const { if (IsCoinBase()) return 0; unsigned int nSigOps = 0; for (unsigned int i = 0; i < vin.size(); i++) { const CTxOut& prevout = GetOutputFor(vin[i], inputs); if (prevout.scriptPubKey.IsPayToScriptHash()) nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig); } return nSigOps; } bool CTransaction::ConnectInputs(CTxDB& txdb, MapPrevTx inputs, map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx, const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash) { // Take over previous transactions' spent pointers // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain // fMiner is true when called from the internal bitcoin miner // ... both are false when called from CTransaction::AcceptToMemoryPool if (!IsCoinBase()) { int64 nValueIn = 0; int64 nFees = 0; for (unsigned int i = 0; i < vin.size(); i++) { COutPoint prevout = vin[i].prevout; assert(inputs.count(prevout.hash) > 0); CTxIndex& txindex = inputs[prevout.hash].first; CTransaction& txPrev = inputs[prevout.hash].second; if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size()) return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %"PRIszu" %"PRIszu" prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); // If prev is coinbase or coinstake, check that it's matured if (txPrev.IsCoinBase() || txPrev.IsCoinStake()) for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < nCoinbaseMaturity; pindex = pindex->pprev) if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) return error("ConnectInputs() : tried to spend %s at depth %d", txPrev.IsCoinBase() ? "coinbase" : "coinstake", pindexBlock->nHeight - pindex->nHeight); // ppcoin: check transaction timestamp if (txPrev.nTime > nTime) return DoS(100, error("ConnectInputs() : transaction timestamp earlier than input transaction")); // Check for negative or overflow input values nValueIn += txPrev.vout[prevout.n].nValue; if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) return DoS(100, error("ConnectInputs() : txin values out of range")); } // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. // Helps prevent CPU exhaustion attacks. for (unsigned int i = 0; i < vin.size(); i++) { COutPoint prevout = vin[i].prevout; assert(inputs.count(prevout.hash) > 0); CTxIndex& txindex = inputs[prevout.hash].first; CTransaction& txPrev = inputs[prevout.hash].second; // Check for conflicts (double-spend) // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!txindex.vSpent[prevout.n].IsNull()) return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str()); // Skip ECDSA signature verification when connecting blocks (fBlock=true) // before the last blockchain checkpoint. This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate()))) { // Verify signature if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0)) { // only during transition phase for P2SH: do not invoke anti-DoS code for // potentially old clients relaying bad P2SH transactions if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0)) return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str()); return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); } } // Mark outpoints as spent txindex.vSpent[prevout.n] = posThisTx; // Write back if (fBlock || fMiner) { mapTestPool[prevout.hash] = txindex; } } if (IsCoinStake()) { // ppcoin: coin stake tx earns reward instead of paying fee uint64 nCoinAge; if (!GetCoinAge(txdb, nCoinAge)) return error("ConnectInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str()); int64 nStakeReward = GetValueOut() - nValueIn; if (nStakeReward > GetProofOfStakeReward(nCoinAge, pindexBlock->nBits, nTime, pindexBlock->nHeight) - GetMinFee() + MIN_TX_FEE) return DoS(100, error("ConnectInputs() : %s stake reward exceeded", GetHash().ToString().substr(0,10).c_str())); } else { if (nValueIn < GetValueOut()) return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str())); // Tally transaction fees int64 nTxFee = nValueIn - GetValueOut(); if (nTxFee < 0) return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str())); // ppcoin: enforce transaction fees for every block if (nTxFee < GetMinFee()) return fBlock? DoS(100, error("ConnectInputs() : %s not paying required fee=%s, paid=%s", GetHash().ToString().substr(0,10).c_str(), FormatMoney(GetMinFee()).c_str(), FormatMoney(nTxFee).c_str())) : false; nFees += nTxFee; if (!MoneyRange(nFees)) return DoS(100, error("ConnectInputs() : nFees out of range")); } } return true; } bool CTransaction::ClientConnectInputs() { if (IsCoinBase()) return false; // Take over previous transactions' spent pointers { LOCK(mempool.cs); int64 nValueIn = 0; for (unsigned int i = 0; i < vin.size(); i++) { // Get prev tx from single transactions in memory COutPoint prevout = vin[i].prevout; if (!mempool.exists(prevout.hash)) return false; CTransaction& txPrev = mempool.lookup(prevout.hash); if (prevout.n >= txPrev.vout.size()) return false; // Verify signature if (!VerifySignature(txPrev, *this, i, true, 0)) return error("ConnectInputs() : VerifySignature failed"); ///// this is redundant with the mempool.mapNextTx stuff, ///// not sure which I want to get rid of ///// this has to go away now that posNext is gone // // Check for conflicts // if (!txPrev.vout[prevout.n].posNext.IsNull()) // return error("ConnectInputs() : prev tx already used"); // // // Flag outpoints as used // txPrev.vout[prevout.n].posNext = posThisTx; nValueIn += txPrev.vout[prevout.n].nValue; if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn)) return error("ClientConnectInputs() : txin values out of range"); } if (GetValueOut() > nValueIn) return false; } return true; } bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) { // Disconnect in reverse order for (int i = vtx.size()-1; i >= 0; i--) if (!vtx[i].DisconnectInputs(txdb)) return false; // Update block index on disk without changing it in memory. // The memory index structure will be changed after the db commits. if (pindex->pprev) { CDiskBlockIndex blockindexPrev(pindex->pprev); blockindexPrev.hashNext = 0; if (!txdb.WriteBlockIndex(blockindexPrev)) return error("DisconnectBlock() : WriteBlockIndex failed"); } // ppcoin: clean up wallet after disconnecting coinstake BOOST_FOREACH(CTransaction& tx, vtx) SyncWithWallets(tx, this, false, false); return true; } bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) { // Check it again in case a previous version let a bad block in if (!CheckBlock(!fJustCheck, !fJustCheck)) return false; // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. // If such overwrites are allowed, coinbases and transactions depending upon those // can be duplicated to remove the ability to spend the first instance -- even after // being sent to another address. // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information. // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool // already refuses previously-known transaction ids entirely. // This rule was originally applied all blocks whose timestamp was after March 15, 2012, 0:00 UTC. // Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the // two in the chain that violate it. This prevents exploiting the issue against nodes in their // initial block download. bool fEnforceBIP30 = true; // Always active in HyperStake bool fStrictPayToScriptHash = true; // Always active in HyperStake //// issue here: it doesn't know the version unsigned int nTxPos; if (fJustCheck) // FetchInputs treats CDiskTxPos(1,1,1) as a special "refer to memorypool" indicator // Since we're just checking the block and not actually connecting it, it might not (and probably shouldn't) be on the disk to get the transaction from nTxPos = 1; else nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(vtx.size()); map<uint256, CTxIndex> mapQueuedChanges; int64 nFees = 0; int64 nValueIn = 0; int64 nValueOut = 0; unsigned int nSigOps = 0; BOOST_FOREACH(CTransaction& tx, vtx) { uint256 hashTx = tx.GetHash(); if (fEnforceBIP30) { CTxIndex txindexOld; if (txdb.ReadTxIndex(hashTx, txindexOld)) { BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent) if (pos.IsNull()) return false; } } nSigOps += tx.GetLegacySigOpCount(); if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("ConnectBlock() : too many sigops")); CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); if (!fJustCheck) nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); MapPrevTx mapInputs; if (tx.IsCoinBase()) nValueOut += tx.GetValueOut(); else { bool fInvalid; if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid)) return false; if (fStrictPayToScriptHash) { // Add in sigops done by pay-to-script-hash inputs; // this is to prevent a "rogue miner" from creating // an incredibly-expensive-to-validate block. nSigOps += tx.GetP2SHSigOpCount(mapInputs); if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("ConnectBlock() : too many sigops")); } int64 nTxValueIn = tx.GetValueIn(mapInputs); int64 nTxValueOut = tx.GetValueOut(); nValueIn += nTxValueIn; nValueOut += nTxValueOut; if (!tx.IsCoinStake()) nFees += nTxValueIn - nTxValueOut; if (!tx.ConnectInputs(txdb, mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash)) return false; } mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size()); } // ppcoin: track money supply and mint amount info pindex->nMint = nValueOut - nValueIn + nFees; pindex->nMoneySupply = (pindex->pprev? pindex->pprev->nMoneySupply : 0) + nValueOut - nValueIn; if (!txdb.WriteBlockIndex(CDiskBlockIndex(pindex))) return error("Connect() : WriteBlockIndex for pindex failed"); // ppcoin: fees are not collected by miners as in bitcoin // ppcoin: fees are destroyed to compensate the entire network if (fDebug && GetBoolArg("-printcreation")) printf("ConnectBlock() : destroy=%s nFees=%"PRI64d"\n", FormatMoney(nFees).c_str(), nFees); if (fJustCheck) return true; // Write queued txindex changes for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) { if (!txdb.UpdateTxIndex((*mi).first, (*mi).second)) return error("ConnectBlock() : UpdateTxIndex failed"); } uint256 prevHash = 0; if(pindex->pprev) { prevHash = pindex->pprev->GetBlockHash(); // printf("==> Got prevHash = %s\n", prevHash.ToString().c_str()); } if (vtx[0].GetValueOut() > GetProofOfWorkReward(pindex->nHeight, nFees, prevHash)) return false; // Update block index on disk without changing it in memory. // The memory index structure will be changed after the db commits. if (pindex->pprev) { CDiskBlockIndex blockindexPrev(pindex->pprev); blockindexPrev.hashNext = pindex->GetBlockHash(); if (!txdb.WriteBlockIndex(blockindexPrev)) return error("ConnectBlock() : WriteBlockIndex failed"); } // Watch for transactions paying to me BOOST_FOREACH(CTransaction& tx, vtx) SyncWithWallets(tx, this, true); return true; } bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) { printf("REORGANIZE\n"); // Find the fork CBlockIndex* pfork = pindexBest; CBlockIndex* plonger = pindexNew; while (pfork != plonger) { while (plonger->nHeight > pfork->nHeight) if (!(plonger = plonger->pprev)) return error("Reorganize() : plonger->pprev is null"); if (pfork == plonger) break; if (!(pfork = pfork->pprev)) return error("Reorganize() : pfork->pprev is null"); } // List of what to disconnect vector<CBlockIndex*> vDisconnect; for (CBlockIndex* pindex = pindexBest; pindex != pfork; pindex = pindex->pprev) vDisconnect.push_back(pindex); // List of what to connect vector<CBlockIndex*> vConnect; for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev) vConnect.push_back(pindex); reverse(vConnect.begin(), vConnect.end()); printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str()); printf("REORGANIZE: Connect %"PRIszu" blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str()); // Disconnect shorter branch vector<CTransaction> vResurrect; BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { CBlock block; if (!block.ReadFromDisk(pindex)) return error("Reorganize() : ReadFromDisk for disconnect failed"); if (!block.DisconnectBlock(txdb, pindex)) return error("Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); // Queue memory transactions to resurrect BOOST_FOREACH(const CTransaction& tx, block.vtx) if (!(tx.IsCoinBase() || tx.IsCoinStake())) vResurrect.push_back(tx); } // Connect longer branch vector<CTransaction> vDelete; for (unsigned int i = 0; i < vConnect.size(); i++) { CBlockIndex* pindex = vConnect[i]; CBlock block; if (!block.ReadFromDisk(pindex)) return error("Reorganize() : ReadFromDisk for connect failed"); if (!block.ConnectBlock(txdb, pindex)) { // Invalid block return error("Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); } // Queue memory transactions to delete BOOST_FOREACH(const CTransaction& tx, block.vtx) vDelete.push_back(tx); } if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash())) return error("Reorganize() : WriteHashBestChain failed"); // Make sure it's successfully written to disk before changing memory structure if (!txdb.TxnCommit()) return error("Reorganize() : TxnCommit failed"); // Disconnect shorter branch BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) if (pindex->pprev) pindex->pprev->pnext = NULL; // Connect longer branch BOOST_FOREACH(CBlockIndex* pindex, vConnect) if (pindex->pprev) pindex->pprev->pnext = pindex; // Resurrect memory transactions that were in the disconnected branch BOOST_FOREACH(CTransaction& tx, vResurrect) tx.AcceptToMemoryPool(txdb, false); // Delete redundant memory transactions that are in the connected branch BOOST_FOREACH(CTransaction& tx, vDelete) mempool.remove(tx); printf("REORGANIZE: done\n"); return true; } // Called from inside SetBestChain: attaches a block to the new best chain being built bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew) { uint256 hash = GetHash(); // Adding to current best branch if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash)) { txdb.TxnAbort(); InvalidChainFound(pindexNew); return false; } if (!txdb.TxnCommit()) return error("SetBestChain() : TxnCommit failed"); // Add to current best branch pindexNew->pprev->pnext = pindexNew; // Delete redundant memory transactions BOOST_FOREACH(CTransaction& tx, vtx) mempool.remove(tx); return true; } bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew) { uint256 hash = GetHash(); if (!txdb.TxnBegin()) return error("SetBestChain() : TxnBegin failed"); if (pindexGenesisBlock == NULL && hash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) { txdb.WriteHashBestChain(hash); if (!txdb.TxnCommit()) return error("SetBestChain() : TxnCommit failed"); pindexGenesisBlock = pindexNew; } else if (hashPrevBlock == hashBestChain) { if (!SetBestChainInner(txdb, pindexNew)) return error("SetBestChain() : SetBestChainInner failed"); } else { // the first block in the new chain that will cause it to become the new best chain CBlockIndex *pindexIntermediate = pindexNew; // list of blocks that need to be connected afterwards std::vector<CBlockIndex*> vpindexSecondary; // Reorganize is costly in terms of db load, as it works in a single db transaction. // Try to limit how much needs to be done inside while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainTrust > pindexBest->bnChainTrust) { vpindexSecondary.push_back(pindexIntermediate); pindexIntermediate = pindexIntermediate->pprev; } if (!vpindexSecondary.empty()) printf("Postponing %"PRIszu" reconnects\n", vpindexSecondary.size()); // Switch to new best branch if (!Reorganize(txdb, pindexIntermediate)) { txdb.TxnAbort(); InvalidChainFound(pindexNew); return error("SetBestChain() : Reorganize failed"); } // Connect further blocks BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary) { CBlock block; if (!block.ReadFromDisk(pindex)) { printf("SetBestChain() : ReadFromDisk failed\n"); break; } if (!txdb.TxnBegin()) { printf("SetBestChain() : TxnBegin 2 failed\n"); break; } // errors now are not fatal, we still did a reorganisation to a new chain in a valid way if (!block.SetBestChainInner(txdb, pindex)) break; } } // Update best block in wallet (so we can detect restored wallets) bool fIsInitialDownload = IsInitialBlockDownload(); if (!fIsInitialDownload) { const CBlockLocator locator(pindexNew); ::SetBestChain(locator); } // New best block hashBestChain = hash; pindexBest = pindexNew; pblockindexFBBHLast = NULL; nBestHeight = pindexBest->nHeight; bnBestChainTrust = pindexNew->bnChainTrust; nTimeBestReceived = GetTime(); nTransactionsUpdated++; printf("SetBestChain: new best=%s height=%d trust=%s date=%s\n", hashBestChain.ToString().c_str(), nBestHeight, bnBestChainTrust.ToString().c_str(), DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); printf("Stake checkpoint: %x\n", pindexBest->nStakeModifierChecksum); // Check the version of the last 100 blocks to see if we need to upgrade: if (!fIsInitialDownload) { int nUpgraded = 0; const CBlockIndex* pindex = pindexBest; for (int i = 0; i < 100 && pindex != NULL; i++) { if (pindex->nVersion > CBlock::CURRENT_VERSION) ++nUpgraded; pindex = pindex->pprev; } if (nUpgraded > 0) printf("SetBestChain: %d of last 100 blocks above version %d\n", nUpgraded, CBlock::CURRENT_VERSION); if (nUpgraded > 100/2) // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: strMiscWarning = _("Warning: This version is obsolete, upgrade required!"); } std::string strCmd = GetArg("-blocknotify", ""); if (!fIsInitialDownload && !strCmd.empty()) { boost::replace_all(strCmd, "%s", hashBestChain.GetHex()); boost::thread t(runCommand, strCmd); // thread runs free } return true; } // ppcoin: total coin age spent in transaction, in the unit of coin-days. // Only those coins meeting minimum age requirement counts. As those // transactions not in main chain are not currently indexed so we // might not find out about their coin age. Older transactions are // guaranteed to be in main chain by sync-checkpoint. This rule is // introduced to help nodes establish a consistent view of the coin // age (trust score) of competing branches. bool CTransaction::GetCoinAge(CTxDB& txdb, uint64& nCoinAge) const { CBigNum bnCentSecond = 0; // coin age in the unit of cent-seconds nCoinAge = 0; if (IsCoinBase()) return true; BOOST_FOREACH(const CTxIn& txin, vin) { // First try finding the previous transaction in database CTransaction txPrev; CTxIndex txindex; if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) continue; // previous transaction not in main chain if (nTime < txPrev.nTime) { printf("GetCoinAge: Timestamp Violation: txtime less than txPrev.nTime"); return false; // Transaction timestamp violation } // Read block header CBlock block; if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) return false; // unable to read block of previous transaction if (block.GetBlockTime() + nStakeMinAge > nTime) continue; // only count coins meeting min age requirement int64 nValueIn = txPrev.vout[txin.prevout.n].nValue; bnCentSecond += CBigNum(nValueIn) * (nTime-txPrev.nTime) / CENT; if (fDebug && GetBoolArg("-printcoinage")) printf("coin age nValueIn=%"PRI64d" nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nTime - txPrev.nTime, bnCentSecond.ToString().c_str()); } CBigNum bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60); if (fDebug && GetBoolArg("-printcoinage")) printf("coin age bnCoinDay=%s\n", bnCoinDay.ToString().c_str()); nCoinAge = bnCoinDay.getuint64(); return true; } // ppcoin: total coin age spent in block, in the unit of coin-days. bool CBlock::GetCoinAge(uint64& nCoinAge) const { nCoinAge = 0; CTxDB txdb("r"); BOOST_FOREACH(const CTransaction& tx, vtx) { uint64 nTxCoinAge; if (tx.GetCoinAge(txdb, nTxCoinAge)) nCoinAge += nTxCoinAge; else return false; } if (nCoinAge == 0) // block coin age minimum 1 coin-day nCoinAge = 1; if (fDebug && GetBoolArg("-printcoinage")) printf("block coin age total nCoinDays=%"PRI64d"\n", nCoinAge); return true; } bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) { // Check for duplicate uint256 hash = GetHash(); if (mapBlockIndex.count(hash)) return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20).c_str()); // Construct new block index object CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this); if (!pindexNew) return error("AddToBlockIndex() : new CBlockIndex failed"); pindexNew->phashBlock = &hash; map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(hashPrevBlock); if (miPrev != mapBlockIndex.end()) { pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; } // ppcoin: compute chain trust score pindexNew->bnChainTrust = (pindexNew->pprev ? pindexNew->pprev->bnChainTrust : 0) + pindexNew->GetBlockTrust(); // ppcoin: compute stake entropy bit for stake modifier if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nHeight))) return error("AddToBlockIndex() : SetStakeEntropyBit() failed"); // ppcoin: record proof-of-stake hash value if (pindexNew->IsProofOfStake()) { if (!mapProofOfStake.count(hash)) return error("AddToBlockIndex() : hashProofOfStake not found in map"); pindexNew->hashProofOfStake = mapProofOfStake[hash]; } // ppcoin: compute stake modifier uint64 nStakeModifier = 0; bool fGeneratedStakeModifier = false; if (!ComputeNextStakeModifier(pindexNew->pprev, nStakeModifier, fGeneratedStakeModifier)) return error("AddToBlockIndex() : ComputeNextStakeModifier() failed"); pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier); pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew); if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum)) return error("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=0x%016"PRI64x, pindexNew->nHeight, nStakeModifier); // Add to mapBlockIndex map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; if (pindexNew->IsProofOfStake()) setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime)); pindexNew->phashBlock = &((*mi).first); // Write to disk block index CTxDB txdb; if (!txdb.TxnBegin()) return false; txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew)); if (!txdb.TxnCommit()) return false; // New best if (pindexNew->bnChainTrust > bnBestChainTrust) if (!SetBestChain(txdb, pindexNew)) return false; txdb.Close(); if (pindexNew == pindexBest) { // Notify UI to display prev block's coinbase if it was ours static uint256 hashPrevBestCoinBase; UpdatedTransaction(hashPrevBestCoinBase); hashPrevBestCoinBase = vtx[0].GetHash(); } uiInterface.NotifyBlocksChanged(); return true; } bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const { // These are checks that are independent of context // that can be verified before saving an orphan block. // Size limits if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return DoS(100, error("CheckBlock() : size limits failed")); // Check proof of work matches claimed amount if (fCheckPOW && IsProofOfWork() && !CheckProofOfWork(GetHash(), nBits)) return DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp if (GetBlockTime() > GetAdjustedTime() + GetClockDrift(GetBlockTime())) return error("CheckBlock() : block timestamp too far in the future"); // First transaction must be coinbase, the rest must not be if (vtx.empty() || !vtx[0].IsCoinBase()) return DoS(100, error("CheckBlock() : first tx is not coinbase")); for (unsigned int i = 1; i < vtx.size(); i++) if (vtx[i].IsCoinBase()) return DoS(100, error("CheckBlock() : more than one coinbase")); // ppcoin: only the second transaction can be the optional coinstake for (unsigned int i = 2; i < vtx.size(); i++) if (vtx[i].IsCoinStake()) return DoS(100, error("CheckBlock() : coinstake in wrong position")); // ppcoin: coinbase output should be empty if proof-of-stake block if (IsProofOfStake() && (vtx[0].vout.size() != 1 || !vtx[0].vout[0].IsEmpty())) return error("CheckBlock() : coinbase output not empty for proof-of-stake block"); // Check coinbase timestamp if (GetBlockTime() > (int64)vtx[0].nTime + GetClockDrift(GetBlockTime())) return DoS(50, error("CheckBlock() : coinbase timestamp is too early")); // Check coinstake timestamp if (IsProofOfStake() && !CheckCoinStakeTimestamp(GetBlockTime(), (int64)vtx[1].nTime)) return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%"PRI64d" nTimeTx=%u", GetBlockTime(), vtx[1].nTime)); // Check transactions BOOST_FOREACH(const CTransaction& tx, vtx) { if (!tx.CheckTransaction()) return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed")); // ppcoin: check transaction timestamp if (GetBlockTime() < (int64)tx.nTime) return DoS(50, error("CheckBlock() : block timestamp earlier than transaction timestamp")); } // Check for duplicate txids. This is caught by ConnectInputs(), // but catching it earlier avoids a potential DoS attack: set<uint256> uniqueTx; BOOST_FOREACH(const CTransaction& tx, vtx) { uniqueTx.insert(tx.GetHash()); } if (uniqueTx.size() != vtx.size()) return DoS(100, error("CheckBlock() : duplicate transaction")); unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, vtx) { nSigOps += tx.GetLegacySigOpCount(); } if (nSigOps > MAX_BLOCK_SIGOPS) return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkle root if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); // ppcoin: check block signature if (!CheckBlockSignature()) return DoS(100, error("CheckBlock() : bad block signature")); return true; } bool CBlock::AcceptBlock() { // Check for duplicate uint256 hash = GetHash(); if (mapBlockIndex.count(hash)) return error("AcceptBlock() : block already in mapBlockIndex"); // Get prev block index map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock); if (mi == mapBlockIndex.end()) return DoS(10, error("AcceptBlock() : prev block not found")); CBlockIndex* pindexPrev = (*mi).second; int nHeight = pindexPrev->nHeight+1; if (IsProofOfWork() && nHeight > POW_CUTOFF_HEIGHT) return DoS(100, error("AcceptBlock() : No proof-of-work allowed anymore (height = %d)", nHeight)); // Check proof-of-work or proof-of-stake if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake())) return DoS(100, error("AcceptBlock() : incorrect %s", IsProofOfWork() ? "proof-of-work" : "proof-of-stake")); // Check timestamp against prev if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || GetBlockTime() + GetClockDrift(GetBlockTime()) < pindexPrev->GetBlockTime()) return error("AcceptBlock() : block's timestamp is too early"); // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, vtx) if (!tx.IsFinal(nHeight, GetBlockTime())) return DoS(10, error("AcceptBlock() : contains a non-final transaction")); // Check that the block chain matches the known block chain up to a checkpoint if (!Checkpoints::CheckHardened(nHeight, hash)) return DoS(100, error("AcceptBlock() : rejected by hardened checkpoint lock-in at %d", nHeight)); /* HyperStake is not using a checkpoint server // ppcoin: check that the block satisfies synchronized checkpoint if (!Checkpoints::CheckSync(hash, pindexPrev)) { if(!GetBoolArg("-nosynccheckpoints", false)) { return error("AcceptBlock() : rejected by synchronized checkpoint"); } else { strMiscWarning = _("WARNING: syncronized checkpoint violation detected, but skipped!"); } }*/ // Reject block.nVersion < 3 blocks since 95% threshold on mainNet and always on testNet: if (nVersion < 3 && ((!fTestNet && nHeight > 14060) || (fTestNet && nHeight > 0))) return error("CheckBlock() : rejected nVersion < 3 block"); // Enforce rule that the coinbase starts with serialized block height CScript expect = CScript() << nHeight; if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) return DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); // Write block to history file if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) return error("AcceptBlock() : out of disk space"); unsigned int nFile = -1; unsigned int nBlockPos = 0; if (!WriteToDisk(nFile, nBlockPos)) return error("AcceptBlock() : WriteToDisk failed"); if (!AddToBlockIndex(nFile, nBlockPos)) return error("AcceptBlock() : AddToBlockIndex failed"); // Relay inventory, but don't relay old inventory during initial block download int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); if (hashBestChain == hash) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) pnode->PushInventory(CInv(MSG_BLOCK, hash)); } /* HyperStake is not using a checkpoint server // ppcoin: check pending sync-checkpoint Checkpoints::AcceptPendingSyncCheckpoint();*/ return true; } CBigNum CBlockIndex::GetBlockTrust() const { CBigNum bnTarget; bnTarget.SetCompact(nBits); if (bnTarget <= 0) return 0; if (IsProofOfStake()) { // Return trust score as usual return (CBigNum(1)<<256) / (bnTarget+1); } else { // Calculate work amount for block CBigNum bnPoWTrust = (bnProofOfWorkLimit / (bnTarget+1)); return bnPoWTrust > 1 ? bnPoWTrust : 1; } } bool CBlockIndex::IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck) { unsigned int nFound = 0; for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; i++) { if (pstart->nVersion >= minVersion) ++nFound; pstart = pstart->pprev; } return (nFound >= nRequired); } bool ProcessBlock(CNode* pfrom, CBlock* pblock) { // Check for duplicate uint256 hash = pblock->GetHash(); if (mapBlockIndex.count(hash)) return error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString().substr(0,20).c_str()); if (mapOrphanBlocks.count(hash)) return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str()); // ppcoin: check proof-of-stake // Limited duplicity on stake: prevents block flood attack // Duplicate stake allowed only when there is orphan child block if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash)) return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str()); // Preliminary checks if (!pblock->CheckBlock()) return error("ProcessBlock() : CheckBlock FAILED"); // ppcoin: verify hash target and signature of coinstake tx if (pblock->IsProofOfStake()) { uint256 hashProofOfStake = 0; if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, hashProofOfStake)) { printf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str()); return false; } if (!mapProofOfStake.count(hash)) // add to mapProofOfStake mapProofOfStake.insert(make_pair(hash, hashProofOfStake)); } CBlockIndex* pcheckpoint = Checkpoints::GetLastSyncCheckpoint(); if (pcheckpoint && pblock->hashPrevBlock != hashBestChain && !Checkpoints::WantedByPendingSyncCheckpoint(hash)) { // Extra checks to prevent "fill up memory by spamming with bogus blocks" int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime; CBigNum bnNewBlock; bnNewBlock.SetCompact(pblock->nBits); CBigNum bnRequired; if (pblock->IsProofOfStake()) bnRequired.SetCompact(ComputeMinStake(GetLastBlockIndex(pcheckpoint, true)->nBits, deltaTime, pblock->nTime)); else bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, false)->nBits, deltaTime)); if (bnNewBlock > bnRequired) { if (pfrom) pfrom->Misbehaving(100); return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work"); } } /* HyperStake is not using checkpoint server // ppcoin: ask for pending sync-checkpoint if any if (!IsInitialBlockDownload()) Checkpoints::AskForPendingSyncCheckpoint(pfrom);*/ // If don't already have its previous block, shunt it off to holding area until we get it if (!mapBlockIndex.count(pblock->hashPrevBlock)) { printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str()); // ppcoin: check proof-of-stake if (pblock->IsProofOfStake()) { // Limited duplicity on stake: prevents block flood attack // Duplicate stake allowed only when there is orphan child block if (setStakeSeenOrphan.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash)) return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for orphan block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str()); else setStakeSeenOrphan.insert(pblock->GetProofOfStake()); } CBlock* pblock2 = new CBlock(*pblock); mapOrphanBlocks.insert(make_pair(hash, pblock2)); mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2)); // Ask this guy to fill in what we're missing if (pfrom) { pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(pblock2)); // ppcoin: getblocks may not obtain the ancestor block rejected // earlier by duplicate-stake check so we ask for it again directly if (!IsInitialBlockDownload()) pfrom->AskFor(CInv(MSG_BLOCK, WantedByOrphan(pblock2))); } return true; } // Store to disk if (!pblock->AcceptBlock()) return error("ProcessBlock() : AcceptBlock FAILED"); // Recursively process any orphan blocks that depended on this one vector<uint256> vWorkQueue; vWorkQueue.push_back(hash); for (unsigned int i = 0; i < vWorkQueue.size(); i++) { uint256 hashPrev = vWorkQueue[i]; for (multimap<uint256, CBlock*>::iterator mi = mapOrphanBlocksByPrev.lower_bound(hashPrev); mi != mapOrphanBlocksByPrev.upper_bound(hashPrev); ++mi) { CBlock* pblockOrphan = (*mi).second; if (pblockOrphan->AcceptBlock()) vWorkQueue.push_back(pblockOrphan->GetHash()); mapOrphanBlocks.erase(pblockOrphan->GetHash()); setStakeSeenOrphan.erase(pblockOrphan->GetProofOfStake()); delete pblockOrphan; } mapOrphanBlocksByPrev.erase(hashPrev); } printf("ProcessBlock: ACCEPTED\n"); // If turned on MultiSend will send a transaction (or more) on the 30th confirmation of a stake if (pwalletMain->fMultiSend) if (!pwalletMain->MultiSend() ) printf("ERROR While trying to use MultiSend"); /* HyperStake is not using a checkpoint server // ppcoin: if responsible for sync-checkpoint send it if (pfrom && !CSyncCheckpoint::strMasterPrivKey.empty()) Checkpoints::SendSyncCheckpoint(Checkpoints::AutoSelectSyncCheckpoint());*/ // presstab HyperStake: enable of disable staking based on block difficulty if(pwalletMain->fStakeRequirement) { if(pwalletMain->strDisableType == "diff") { int nShift = (pblock->nBits >> 24) & 0xff; double dDiff = (double)0x0000ffff / (double)(pblock->nBits & 0x00ffffff); while (nShift < 29) { dDiff *= 256.0; nShift++; } while (nShift > 29) { dDiff /= 256.0; nShift--; } if(pwalletMain->strDisableArg == ">") { if(dDiff > (pwalletMain->dUserNumber)) pwalletMain->fDisableStake = true; else pwalletMain->fDisableStake = false; } else if (pwalletMain->strDisableArg == "<") { if(dDiff < (pwalletMain->dUserNumber)) pwalletMain->fDisableStake = true; else pwalletMain->fDisableStake = false; } } else if (pwalletMain->strDisableType == "weight") { uint64 nMinMax; uint64 nWeight; uint64 nHoursToMaturity; uint64 nAmount; pwalletMain->GetStakeWeight(*pwalletMain, nMinMax, nMinMax, nWeight, nHoursToMaturity, nAmount); if(pwalletMain->strDisableArg == ">") { if(nWeight > (pwalletMain->dUserNumber)) pwalletMain->fDisableStake = true; else pwalletMain->fDisableStake = false; } else if (pwalletMain->strDisableArg == "<") { if(nWeight < (pwalletMain->dUserNumber)) pwalletMain->fDisableStake = true; else pwalletMain->fDisableStake = false; } } } return true; } // ppcoin: sign block bool CBlock::SignBlock(const CKeyStore& keystore) { vector<valtype> vSolutions; txnouttype whichType; if(!IsProofOfStake()) { for(unsigned int i = 0; i < vtx[0].vout.size(); i++) { const CTxOut& txout = vtx[0].vout[i]; if (!Solver(txout.scriptPubKey, whichType, vSolutions)) continue; if (whichType == TX_PUBKEY) { // Sign valtype& vchPubKey = vSolutions[0]; CKey key; if (!keystore.GetKey(Hash160(vchPubKey), key)) continue; if (key.GetPubKey() != vchPubKey) continue; if(!key.Sign(GetHash(), vchBlockSig)) continue; return true; } } } else { const CTxOut& txout = vtx[1].vout[1]; if (!Solver(txout.scriptPubKey, whichType, vSolutions)) return false; if (whichType == TX_PUBKEY) { // Sign valtype& vchPubKey = vSolutions[0]; CKey key; if (!keystore.GetKey(Hash160(vchPubKey), key)) return false; if (key.GetPubKey() != vchPubKey) return false; return key.Sign(GetHash(), vchBlockSig); } } printf("Sign failed\n"); return false; } // ppcoin: check block signature bool CBlock::CheckBlockSignature() const { if (GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) return vchBlockSig.empty(); vector<valtype> vSolutions; txnouttype whichType; if(IsProofOfStake()) { const CTxOut& txout = vtx[1].vout[1]; if (!Solver(txout.scriptPubKey, whichType, vSolutions)) return false; if (whichType == TX_PUBKEY) { valtype& vchPubKey = vSolutions[0]; CKey key; if (!key.SetPubKey(vchPubKey)) return false; if (vchBlockSig.empty()) return false; return key.Verify(GetHash(), vchBlockSig); } } else { for(unsigned int i = 0; i < vtx[0].vout.size(); i++) { const CTxOut& txout = vtx[0].vout[i]; if (!Solver(txout.scriptPubKey, whichType, vSolutions)) return false; if (whichType == TX_PUBKEY) { // Verify valtype& vchPubKey = vSolutions[0]; CKey key; if (!key.SetPubKey(vchPubKey)) continue; if (vchBlockSig.empty()) continue; if(!key.Verify(GetHash(), vchBlockSig)) continue; return true; } } } return false; } bool CheckDiskSpace(uint64 nAdditionalBytes) { uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available; // Check for nMinDiskSpace bytes (currently 50MB) if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes) { fShutdown = true; string strMessage = _("Warning: Disk space is low!"); strMiscWarning = strMessage; printf("*** %s\n", strMessage.c_str()); uiInterface.ThreadSafeMessageBox(strMessage, "HyperStake", CClientUIInterface::OK | CClientUIInterface::ICON_ETRKLAMATION | CClientUIInterface::MODAL); StartShutdown(); return false; } return true; } static filesystem::path BlockFilePath(unsigned int nFile) { string strBlockFn = strprintf("blk%04u.dat", nFile); return GetDataDir() / strBlockFn; } FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode) { if ((nFile < 1) || (nFile == (unsigned int) -1)) return NULL; FILE* file = fopen(BlockFilePath(nFile).string().c_str(), pszMode); if (!file) return NULL; if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w')) { if (fseek(file, nBlockPos, SEEK_SET) != 0) { fclose(file); return NULL; } } return file; } static unsigned int nCurrentBlockFile = 1; FILE* AppendBlockFile(unsigned int& nFileRet) { nFileRet = 0; while (true) { FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab"); if (!file) return NULL; if (fseek(file, 0, SEEK_END) != 0) return NULL; // FAT32 file size max 4GB, fseek and ftell max 2GB, so we must stay under 2GB if (ftell(file) < (long)(0x7F000000 - MAX_SIZE)) { nFileRet = nCurrentBlockFile; return file; } fclose(file); nCurrentBlockFile++; } } bool LoadBlockIndex(bool fAllowNew) { if (fTestNet) { pchMessageStart[0] = 0xdd; pchMessageStart[1] = 0x4d; pchMessageStart[2] = 0xdd; pchMessageStart[3] = 0x4d; bnProofOfStakeLimit = bnProofOfStakeLimitTestNet; // 0x00000fff PoS base target is fixed in testnet bnProofOfWorkLimit = bnProofOfWorkLimitTestNet; // 0x0000ffff PoW base target is fixed in testnet nStakeMinAge = 20 * 60; // test net min age is 20 min nStakeMaxAge = 60 * 60 * 24; // test net min age is 24 hours nCoinbaseMaturity = 10; // test maturity is 10 blocks nStakeTargetSpacing = 90; // test block spacing is 90 seconds } // // Load block index // CTxDB txdb("cr"); if (!txdb.LoadBlockIndex()) return false; txdb.Close(); // // Init with genesis block // if (mapBlockIndex.empty()) { if (!fAllowNew) return false; // Genesis block const char* pszTimestamp = "29/5/14 - Replica Ghostbusters car stops the traffic - BBC UK"; CTransaction txNew; txNew.nTime = nChainStartTime; txNew.vin.resize(1); txNew.vout.resize(1); txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(9999) << vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp)); txNew.vout[0].SetEmpty(); CBlock block; block.vtx.push_back(txNew); block.hashPrevBlock = 0; block.hashMerkleRoot = block.BuildMerkleTree(); block.nVersion = 1; block.nTime = 1401331380; block.nBits = bnProofOfWorkLimit.GetCompact(); block.nNonce = 1779291; if(fTestNet) { block.nTime = 1424304823; block.nNonce = 0; } if (false ) { // This will figure out a valid hash and Nonce if you're // creating a different genesis block: uint256 hashTarget = CBigNum().SetCompact(block.nBits).getuint256(); while (block.GetHash() > hashTarget) { ++block.nNonce; if (block.nNonce == 0) { printf("NONCE WRAPPED, incrementing time"); ++block.nTime; } } } //// debug print block.print(); printf("block.GetHash() == %s\n", block.GetHash().ToString().c_str()); printf("block.hashMerkleRoot == %s\n", block.hashMerkleRoot.ToString().c_str()); printf("block.nTime = %u \n", block.nTime); printf("block.nNonce = %u \n", block.nNonce); assert(block.hashMerkleRoot == uint256("37ad323037e6e55553fadebbe60690a1bff2752f947b7af8cb6b54929f5fee3d")); assert(block.GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)); // Start new block file unsigned int nFile; unsigned int nBlockPos; if (!block.WriteToDisk(nFile, nBlockPos)) return error("LoadBlockIndex() : writing genesis block to disk failed"); if (!block.AddToBlockIndex(nFile, nBlockPos)) return error("LoadBlockIndex() : genesis block not accepted"); // ppcoin: initialize synchronized checkpoint if (!Checkpoints::WriteSyncCheckpoint((!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))) return error("LoadBlockIndex() : failed to init sync checkpoint"); } // ppcoin: if checkpoint master key changed must reset sync-checkpoint { CTxDB txdb; string strPubKey = ""; if (!txdb.ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey) { // write checkpoint master key to db txdb.TxnBegin(); if (!txdb.WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey)) return error("LoadBlockIndex() : failed to write new checkpoint master key to db"); if (!txdb.TxnCommit()) return error("LoadBlockIndex() : failed to commit new checkpoint master key to db"); if ((!fTestNet) && !Checkpoints::ResetSyncCheckpoint()) return error("LoadBlockIndex() : failed to reset sync-checkpoint"); } txdb.Close(); } return true; } void PrintBlockTree() { // pre-compute tree structure map<CBlockIndex*, vector<CBlockIndex*> > mapNext; for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi) { CBlockIndex* pindex = (*mi).second; mapNext[pindex->pprev].push_back(pindex); // test //while (rand() % 3 == 0) // mapNext[pindex->pprev].push_back(pindex); } vector<pair<int, CBlockIndex*> > vStack; vStack.push_back(make_pair(0, pindexGenesisBlock)); int nPrevCol = 0; while (!vStack.empty()) { int nCol = vStack.back().first; CBlockIndex* pindex = vStack.back().second; vStack.pop_back(); // print split or gap if (nCol > nPrevCol) { for (int i = 0; i < nCol-1; i++) printf("| "); printf("|\\\n"); } else if (nCol < nPrevCol) { for (int i = 0; i < nCol; i++) printf("| "); printf("|\n"); } nPrevCol = nCol; // print columns for (int i = 0; i < nCol; i++) printf("| "); // print item CBlock block; block.ReadFromDisk(pindex); printf("%d (%u,%u) %s %08x %s mint %7s tx %"PRIszu"", pindex->nHeight, pindex->nFile, pindex->nBlockPos, block.GetHash().ToString().c_str(), block.nBits, DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(), FormatMoney(pindex->nMint).c_str(), block.vtx.size()); PrintWallets(block); // put the main time-chain first vector<CBlockIndex*>& vNext = mapNext[pindex]; for (unsigned int i = 0; i < vNext.size(); i++) { if (vNext[i]->pnext) { swap(vNext[0], vNext[i]); break; } } // iterate children for (unsigned int i = 0; i < vNext.size(); i++) vStack.push_back(make_pair(nCol+i, vNext[i])); } } bool LoadExternalBlockFile(FILE* fileIn) { int64 nStart = GetTimeMillis(); int nLoaded = 0; { LOCK(cs_main); try { CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION); unsigned int nPos = 0; while (nPos != (unsigned int)-1 && blkdat.good() && !fRequestShutdown) { unsigned char pchData[65536]; do { fseek(blkdat, nPos, SEEK_SET); int nRead = fread(pchData, 1, sizeof(pchData), blkdat); if (nRead <= 8) { nPos = (unsigned int)-1; break; } void* nFind = memchr(pchData, pchMessageStart[0], nRead+1-sizeof(pchMessageStart)); if (nFind) { if (memcmp(nFind, pchMessageStart, sizeof(pchMessageStart))==0) { nPos += ((unsigned char*)nFind - pchData) + sizeof(pchMessageStart); break; } nPos += ((unsigned char*)nFind - pchData) + 1; } else nPos += sizeof(pchData) - sizeof(pchMessageStart) + 1; } while(!fRequestShutdown); if (nPos == (unsigned int)-1) break; fseek(blkdat, nPos, SEEK_SET); unsigned int nSize; blkdat >> nSize; if (nSize > 0 && nSize <= MAX_BLOCK_SIZE) { CBlock block; blkdat >> block; if (ProcessBlock(NULL,&block)) { nLoaded++; nPos += 4 + nSize; } } } } catch (std::exception &e) { printf("%s() : Deserialize or I/O error caught during load\n", __PRETTY_FUNCTION__); } } printf("Loaded %i blocks from external file in %"PRI64d"ms\n", nLoaded, GetTimeMillis() - nStart); return nLoaded > 0; } ////////////////////////////////////////////////////////////////////////////// // // CAlert // extern map<uint256, CAlert> mapAlerts; extern CCriticalSection cs_mapAlerts; static string strMintMessage = "Info: Minting suspended due to locked wallet."; static string strMintWarning; string GetWarnings(string strFor) { int nPriority = 0; string strStatusBar; string strRPC; if (GetBoolArg("-testsafemode")) strRPC = "test"; // ppcoin: wallet lock warning for minting if (strMintWarning != "") { nPriority = 0; strStatusBar = strMintWarning; } // Misc warnings like out of disk space and clock is wrong if (strMiscWarning != "") { nPriority = 1000; strStatusBar = strMiscWarning; } // ppcoin: should not enter safe mode for longer invalid chain // ppcoin: if sync-checkpoint is too old do not enter safe mode /*if (Checkpoints::IsSyncCheckpointTooOld(60 * 60 * 24 * 365) && !fTestNet && !IsInitialBlockDownload()) { nPriority = 100; strStatusBar = "WARNING: Checkpoint is too old. Wait for block chain to download, or notify developers."; } // ppcoin: if detected invalid checkpoint enter safe mode if (Checkpoints::hashInvalidCheckpoint != 0) { nPriority = 3000; strStatusBar = strRPC = "WARNING: Invalid checkpoint found! Displayed transactions may not be correct! You may need to upgrade, or notify developers."; }*/ // Alerts { LOCK(cs_mapAlerts); BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) { const CAlert& alert = item.second; if (alert.AppliesToMe() && alert.nPriority > nPriority) { nPriority = alert.nPriority; strStatusBar = alert.strStatusBar; if (nPriority > 1000) strRPC = strStatusBar; // ppcoin: safe mode for high alert } } } if (strFor == "statusbar") return strStatusBar; else if (strFor == "rpc") return strRPC; assert(!"GetWarnings() : invalid parameter"); return "error"; } ////////////////////////////////////////////////////////////////////////////// // // Messages // bool static AlreadyHave(CTxDB& txdb, const CInv& inv) { switch (inv.type) { case MSG_TX: { bool txInMap = false; { LOCK(mempool.cs); txInMap = (mempool.exists(inv.hash)); } return txInMap || mapOrphanTransactions.count(inv.hash) || txdb.ContainsTx(inv.hash); } case MSG_BLOCK: return mapBlockIndex.count(inv.hash) || mapOrphanBlocks.count(inv.hash); } // Don't know what it is, just say we already got one return true; } // The message start string is designed to be unlikely to occur in normal data. // The characters are rarely used upper ASCII, not valid as UTF-8, and produce // a large 4-byte int at any alignment. unsigned char pchMessageStart[4] = { 0xdb, 0xad, 0xbd, 0xda }; unsigned int nLastMapGetBlocksClear = 0; bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) { static map<CService, CPubKey> mapReuseKey; RandAddSeedPerfmon(); if (fDebug) printf("received: %s (%"PRIszu" bytes)\n", strCommand.c_str(), vRecv.size()); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { printf("dropmessagestest DROPPING RECV MESSAGE\n"); return true; } if (strCommand == "version") { // Each connection can only send one version message if (pfrom->nVersion != 0) { pfrom->Misbehaving(1); return false; } int64 nTime; CAddress addrMe; CAddress addrFrom; uint64 nNonce = 1; vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; if (pfrom->nVersion < PROTOCOL_START) { // Since February 20, 2012, the protocol is initiated at version 209, // and earlier versions are no longer supported printf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); pfrom->fDisconnect = true; return false; } if((fStrictProtocol) && pfrom->nVersion != PROTOCOL_VERSION) { printf("Strict Protocol: partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); pfrom->fDisconnect = true; return false; } if (pfrom->nVersion == 10300) pfrom->nVersion = 300; if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; if (!vRecv.empty()) vRecv >> pfrom->strSubVer; if (!vRecv.empty()) vRecv >> pfrom->nStartingHeight; if (pfrom->fInbound && addrMe.IsRoutable()) { pfrom->addrLocal = addrMe; SeenLocal(addrMe); } // Disconnect if we connected to ourself if (nNonce == nLocalHostNonce && nNonce > 1) { printf("connected to self at %s, disconnecting\n", pfrom->addr.ToString().c_str()); pfrom->fDisconnect = true; return true; } if (pfrom->nVersion < 72000) { printf("partner %s using an old client %d, disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion); pfrom->fDisconnect = true; return true; } // ppcoin: record my external IP reported by peer if (addrFrom.IsRoutable() && addrMe.IsRoutable()) addrSeenByPeer = addrMe; // Be shy and don't send version until we hear if (pfrom->fInbound) pfrom->PushVersion(); pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); AddTimeData(pfrom->addr, nTime); // Change version pfrom->PushMessage("verack"); pfrom->vSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); if (!pfrom->fInbound) { // Advertise our address if (!fNoListen && !IsInitialBlockDownload()) { CAddress addr = GetLocalAddress(&pfrom->addr); if (addr.IsRoutable()) pfrom->PushAddress(addr); } // Get recent addresses if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) { pfrom->PushMessage("getaddr"); pfrom->fGetAddr = true; } addrman.Good(pfrom->addr); } else { if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom) { addrman.Add(addrFrom, addrFrom); addrman.Good(addrFrom); } } // Ask the first connected node for block updates static int nAskedForBlocks = 0; if (!pfrom->fClient && !pfrom->fOneShot && (pfrom->nStartingHeight > (nBestHeight - 144)) && (pfrom->nVersion < NOBLKS_VERSION_START || pfrom->nVersion >= NOBLKS_VERSION_END) && (nAskedForBlocks < 1 || vNodes.size() <= 1)) { nAskedForBlocks++; pfrom->PushGetBlocks(pindexBest, uint256(0)); } // Relay alerts { LOCK(cs_mapAlerts); BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) item.second.RelayTo(pfrom); } /* HyperStake does not use checkpoint server // ppcoin: relay sync-checkpoint { LOCK(Checkpoints::cs_hashSyncCheckpoint); if (!Checkpoints::checkpointMessage.IsNull()) Checkpoints::checkpointMessage.RelayTo(pfrom); }*/ pfrom->fSuccessfullyConnected = true; printf("receive version message: version %d, blocks=%d, us=%s, them=%s, peer=%s\n", pfrom->nVersion, pfrom->nStartingHeight, addrMe.ToString().c_str(), addrFrom.ToString().c_str(), pfrom->addr.ToString().c_str()); cPeerBlockCounts.input(pfrom->nStartingHeight); // Be more aggressive with blockchain download. Send new getblocks() message after connection // to new node if waited longer than MAX_TIME_SINCE_BEST_BLOCK. int64 TimeSinceBestBlock = GetTime() - nTimeBestReceived; if (TimeSinceBestBlock > MAX_TIME_SINCE_BEST_BLOCK) { printf("INFO: Waiting %"PRI64d" sec which is too long. Sending GetBlocks(0)\n", TimeSinceBestBlock); pfrom->PushGetBlocks(pindexBest, uint256(0)); } /* HyperStake does not use checkpoint server // ppcoin: ask for pending sync-checkpoint if any if (!IsInitialBlockDownload()) Checkpoints::AskForPendingSyncCheckpoint(pfrom);*/ } else if (pfrom->nVersion == 0) { // Must have a version message before anything else pfrom->Misbehaving(1); return false; } else if (strCommand == "verack") { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); } else if (strCommand == "addr") { vector<CAddress> vAddr; vRecv >> vAddr; // Don't want addr from older versions unless seeding if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000) return true; if (vAddr.size() > 1000) { pfrom->Misbehaving(20); return error("message addr size() = %"PRIszu"", vAddr.size()); } // Store the new addresses vector<CAddress> vAddrOk; int64 nNow = GetAdjustedTime(); int64 nSince = nNow - 10 * 60; BOOST_FOREACH(CAddress& addr, vAddr) { if (fShutdown) return true; if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; pfrom->AddAddressKnown(addr); bool fReachable = IsReachable(addr); if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) { // Relay to a limited number of other nodes { LOCK(cs_vNodes); // Use deterministic randomness to send to the same nodes for 24 hours // at a time so the setAddrKnowns of the chosen nodes prevent repeats static uint256 hashSalt; if (hashSalt == 0) hashSalt = GetRandHash(); uint64 hashAddr = addr.GetHash(); uint256 hashRand = hashSalt ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60)); hashRand = Hash(BEGIN(hashRand), END(hashRand)); multimap<uint256, CNode*> mapMix; BOOST_FOREACH(CNode* pnode, vNodes) { if (pnode->nVersion < CADDR_TIME_VERSION) continue; unsigned int nPointer; memcpy(&nPointer, &pnode, sizeof(nPointer)); uint256 hashKey = hashRand ^ nPointer; hashKey = Hash(BEGIN(hashKey), END(hashKey)); mapMix.insert(make_pair(hashKey, pnode)); } int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) for (multimap<uint256, CNode*>::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) ((*mi).second)->PushAddress(addr); } } // Do not store addresses outside our network if (fReachable) vAddrOk.push_back(addr); } addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60); if (vAddr.size() < 1000) pfrom->fGetAddr = false; if (pfrom->fOneShot) pfrom->fDisconnect = true; } else if (strCommand == "inv") { vector<CInv> vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { pfrom->Misbehaving(20); return error("message inv size() = %"PRIszu"", vInv.size()); } // find last block in inv vector unsigned int nLastBlock = (unsigned int)(-1); for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) { nLastBlock = vInv.size() - 1 - nInv; break; } } CTxDB txdb("r"); for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; if (fShutdown) return true; pfrom->AddInventoryKnown(inv); bool fAlreadyHave = AlreadyHave(txdb, inv); if (fDebug) printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new"); if (!fAlreadyHave) pfrom->AskFor(inv); else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); } else if (nInv == nLastBlock) { // In case we are on a very long side-chain, it is possible that we already have // the last block in an inv bundle sent in response to getblocks. Try to detect // this situation and push another getblocks to continue. pfrom->PushGetBlocks(mapBlockIndex[inv.hash], uint256(0)); if (fDebug) printf("force request: %s\n", inv.ToString().c_str()); } // Track requests for our stuff Inventory(inv.hash); } } else if (strCommand == "getdata") { vector<CInv> vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { pfrom->Misbehaving(20); return error("message getdata size() = %"PRIszu"", vInv.size()); } if (fDebugNet || (vInv.size() != 1)) printf("received getdata (%"PRIszu" invsz)\n", vInv.size()); BOOST_FOREACH(const CInv& inv, vInv) { if (fShutdown) return true; if (fDebugNet || (vInv.size() == 1)) printf("received getdata for: %s\n", inv.ToString().c_str()); if (inv.type == MSG_BLOCK) { // Send block from disk map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash); if (mi != mapBlockIndex.end()) { CBlock block; block.ReadFromDisk((*mi).second); pfrom->PushMessage("block", block); // Trigger them to send a getblocks request for the next batch of inventory if (inv.hash == pfrom->hashContinue) { vector<CInv> vInv; vInv.push_back(CInv(MSG_BLOCK, hashBestChain)); pfrom->PushMessage("inv", vInv); pfrom->hashContinue = 0; } } } else if (inv.IsKnownType()) { // Send stream from relay memory bool pushed = false; { LOCK(cs_mapRelay); map<CInv, CDataStream>::iterator mi = mapRelay.find(inv); if (mi != mapRelay.end()) { pfrom->PushMessage(inv.GetCommand(), (*mi).second); pushed = true; } } if (!pushed && inv.type == MSG_TX) { LOCK(mempool.cs); if (mempool.exists(inv.hash)) { CTransaction tx = mempool.lookup(inv.hash); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(1000); ss << tx; pfrom->PushMessage("tx", ss); } } } // Track requests for our stuff Inventory(inv.hash); } } else if (strCommand == "getblocks") { //clear our mapGetBlocksRequests every 3 minutes to prevent storing lots of data if(nLastMapGetBlocksClear != 0 && GetTime() - nLastMapGetBlocksClear > (180)) mapGetBlocksRequests.clear(); CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; // Find the last block the caller has in the main chain CBlockIndex* pindex = locator.GetBlockIndex(); // Send the rest of the chain if (pindex) pindex = pindex->pnext; int nLimit = 1000; int nFirst = (pindex ? pindex->nHeight : -1); printf("getblocks %d to %s limit %d\n", nFirst, hashStop.ToString().substr(0,20).c_str(), nLimit); for (; pindex; pindex = pindex->pnext) { if(fStrictIncoming) { nLastMapGetBlocksClear = GetTime(); std::string strFrom = pfrom->addrName; if(mapGetBlocksRequests.count(strFrom)) { if(mapGetBlocksRequests[strFrom].first == nFirst) // if the peer has already requested this mapGetBlocksRequests[strFrom].second += 1; // count times this has been requested else { // this has not been requested from this peer, so record it mapGetBlocksRequests[strFrom].first = nFirst; mapGetBlocksRequests[strFrom].second = 1; } if(mapGetBlocksRequests[strFrom].first != -1 && mapGetBlocksRequests[strFrom].second > 100) { printf("GetBlocksRequest: disconnect from peer %s that has requested the same thing more than 100 times\n", strFrom.c_str()); pfrom->fDisconnect = true; return false; } } else { // if peer hasn't requested any blocks yet then add them to map printf("GetBlocksRequest: adding peer to map\n"); mapGetBlocksRequests[strFrom].first = nFirst; mapGetBlocksRequests[strFrom].second = 1; } } if (pindex->GetBlockHash() == hashStop) { printf(" getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); break; } pfrom->PushInventory(CInv(MSG_BLOCK, pindex->GetBlockHash())); if (--nLimit <= 0) { // When this block is requested, we'll send an inv that'll make them // getblocks the next batch of inventory. printf(" getblocks stopping at limit %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,20).c_str()); pfrom->hashContinue = pindex->GetBlockHash(); break; } } } else if (strCommand == "checkpoint") { CSyncCheckpoint checkpoint; vRecv >> checkpoint; if (checkpoint.ProcessSyncCheckpoint(pfrom)) { // Relay pfrom->hashCheckpointKnown = checkpoint.hashCheckpoint; LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) checkpoint.RelayTo(pnode); } } else if (strCommand == "getheaders") { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; CBlockIndex* pindex = NULL; if (locator.IsNull()) { // If locator is null, return the hashStop block map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashStop); if (mi == mapBlockIndex.end()) return true; pindex = (*mi).second; } else { // Find the last block the caller has in the main chain pindex = locator.GetBlockIndex(); if (pindex) pindex = pindex->pnext; } vector<CBlock> vHeaders; int nLimit = 2000; printf("getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str()); for (; pindex; pindex = pindex->pnext) { vHeaders.push_back(pindex->GetBlockHeader()); if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) break; } pfrom->PushMessage("headers", vHeaders); } else if (strCommand == "tx") { vector<uint256> vWorkQueue; vector<uint256> vEraseQueue; CDataStream vMsg(vRecv); CTxDB txdb("r"); CTransaction tx; vRecv >> tx; CInv inv(MSG_TX, tx.GetHash()); pfrom->AddInventoryKnown(inv); bool fMissingInputs = false; if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs)) { SyncWithWallets(tx, NULL, true); RelayMessage(inv, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); vEraseQueue.push_back(inv.hash); // Recursively process any orphan transactions that depended on this one for (unsigned int i = 0; i < vWorkQueue.size(); i++) { uint256 hashPrev = vWorkQueue[i]; for (map<uint256, CDataStream*>::iterator mi = mapOrphanTransactionsByPrev[hashPrev].begin(); mi != mapOrphanTransactionsByPrev[hashPrev].end(); ++mi) { const CDataStream& vMsg = *((*mi).second); CTransaction tx; CDataStream(vMsg) >> tx; CInv inv(MSG_TX, tx.GetHash()); bool fMissingInputs2 = false; if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs2)) { printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); SyncWithWallets(tx, NULL, true); RelayMessage(inv, vMsg); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); vEraseQueue.push_back(inv.hash); } else if (!fMissingInputs2) { // invalid orphan vEraseQueue.push_back(inv.hash); printf(" removed invalid orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str()); } } } BOOST_FOREACH(uint256 hash, vEraseQueue) EraseOrphanTx(hash); } else if (fMissingInputs) { AddOrphanTx(vMsg); // DoS prevention: do not allow mapOrphanTransactions to grow unbounded unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); if (nEvicted > 0) printf("mapOrphan overflow, removed %u tx\n", nEvicted); } if (tx.nDoS) pfrom->Misbehaving(tx.nDoS); } else if (strCommand == "block") { CBlock block; vRecv >> block; printf("received block %s\n", block.GetHash().ToString().substr(0,20).c_str()); // block.print(); CInv inv(MSG_BLOCK, block.GetHash()); pfrom->AddInventoryKnown(inv); if (ProcessBlock(pfrom, &block)) mapAlreadyAskedFor.erase(inv); else { if(fStrictIncoming) { string strFrom = pfrom->addrName; if(mapPeerRejectedBlocks.count(strFrom) == 0) mapPeerRejectedBlocks[strFrom] = 1; else mapPeerRejectedBlocks[strFrom] += 1; if (mapPeerRejectedBlocks[strFrom] > 100) { printf("MapPeerRejectedBlocks: %s has sent 100 orphans, disconnecting\n", strFrom.c_str()); pfrom->fDisconnect = true; return false; } } // Be more aggressive with blockchain download. Send getblocks() message after // an error related to new block download int64 TimeSinceBestBlock = GetTime() - nTimeBestReceived; if (TimeSinceBestBlock > MAX_TIME_SINCE_BEST_BLOCK) { printf("INFO: Waiting %"PRI64d" sec which is too long. Sending GetBlocks(0)\n", TimeSinceBestBlock); pfrom->PushGetBlocks(pindexBest, uint256(0)); } } if (block.nDoS) pfrom->Misbehaving(block.nDoS); } else if (strCommand == "getaddr") { pfrom->vAddrToSend.clear(); vector<CAddress> vAddr = addrman.GetAddr(); BOOST_FOREACH(const CAddress &addr, vAddr) pfrom->PushAddress(addr); } else if (strCommand == "mempool") { std::vector<uint256> vtxid; mempool.queryHashes(vtxid); vector<CInv> vInv; for (unsigned int i = 0; i < vtxid.size(); i++) { CInv inv(MSG_TX, vtxid[i]); vInv.push_back(inv); if (i == (MAX_INV_SZ - 1)) break; } if (vInv.size() > 0) pfrom->PushMessage("inv", vInv); } else if (strCommand == "checkorder") { uint256 hashReply; vRecv >> hashReply; if (!GetBoolArg("-allowreceivebyip")) { pfrom->PushMessage("reply", hashReply, (int)2, string("")); return true; } CWalletTx order; vRecv >> order; /// we have a chance to check the order here // Keep giving the same key to the same ip until they use it if (!mapReuseKey.count(pfrom->addr)) pwalletMain->GetKeyFromPool(mapReuseKey[pfrom->addr], true); // Send back approval of order and pubkey to use CScript scriptPubKey; scriptPubKey << mapReuseKey[pfrom->addr] << OP_CHECKSIG; pfrom->PushMessage("reply", hashReply, (int)0, scriptPubKey); } else if (strCommand == "reply") { uint256 hashReply; vRecv >> hashReply; CRequestTracker tracker; { LOCK(pfrom->cs_mapRequests); map<uint256, CRequestTracker>::iterator mi = pfrom->mapRequests.find(hashReply); if (mi != pfrom->mapRequests.end()) { tracker = (*mi).second; pfrom->mapRequests.erase(mi); } } if (!tracker.IsNull()) tracker.fn(tracker.param1, vRecv); } else if (strCommand == "ping") { if (pfrom->nVersion > BIP0031_VERSION) { uint64 nonce = 0; vRecv >> nonce; // Echo the message back with the nonce. This allows for two useful features: // // 1) A remote node can quickly check if the connection is operational // 2) Remote nodes can measure the latency of the network thread. If this node // is overloaded it won't respond to pings quickly and the remote node can // avoid sending us more work, like chain download requests. // // The nonce stops the remote getting confused between different pings: without // it, if the remote node sends a ping once per second and this node takes 5 // seconds to respond to each, the 5th ping the remote sends would appear to // return very quickly. pfrom->PushMessage("pong", nonce); } } else if (strCommand == "alert") { CAlert alert; vRecv >> alert; uint256 alertHash = alert.GetHash(); if (pfrom->setKnown.count(alertHash) == 0) { if (alert.ProcessAlert()) { // Relay pfrom->setKnown.insert(alertHash); { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) alert.RelayTo(pnode); } } else { // Small DoS penalty so peers that send us lots of // duplicate/expired/invalid-signature/whatever alerts // eventually get banned. // This isn't a Misbehaving(100) (immediate ban) because the // peer might be an older or different implementation with // a different signature key, etc. if(!alert.CheckSignature()) pfrom->Misbehaving(10); } } } else { // Ignore unknown commands for extensibility } // Update the last seen time for this node's address if (pfrom->fNetworkNode) if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping") AddressCurrentlyConnected(pfrom->addr); return true; } // requires LOCK(cs_vRecvMsg) bool ProcessMessages(CNode* pfrom) { //if (fDebug) // printf("ProcessMessages(%zu messages)\n", pfrom->vRecvMsg.size()); // // Message format // (4) message start // (12) command // (4) size // (4) checksum // (x) data // bool fOk = true; std::deque<CNetMessage>::iterator it = pfrom->vRecvMsg.begin(); while (it != pfrom->vRecvMsg.end()) { // Don't bother if send buffer is too full to respond anyway if (pfrom->vSend.size() >= SendBufferSize()) break; // get next message CNetMessage& msg = *it; //if (fDebug) // printf("ProcessMessages(message %u msgsz, %zu bytes, complete:%s)\n", // msg.hdr.nMessageSize, msg.vRecv.size(), // msg.complete() ? "Y" : "N"); // end, if an incomplete message is found if (!msg.complete()) break; // at this point, any failure means we can delete the current message it++; // Scan for message start if (memcmp(msg.hdr.pchMessageStart, pchMessageStart, sizeof(pchMessageStart)) != 0) { printf("\n\nPROCESSMESSAGE: INVALID MESSAGESTART\n\n"); fOk = false; break; } // Read header CMessageHeader& hdr = msg.hdr; if (!hdr.IsValid()) { printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); continue; } string strCommand = hdr.GetCommand(); // Message size unsigned int nMessageSize = hdr.nMessageSize; // Checksum CDataStream& vRecv = msg.vRecv; uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize); unsigned int nChecksum = 0; memcpy(&nChecksum, &hash, sizeof(nChecksum)); if (nChecksum != hdr.nChecksum) { printf("ProcessMessages(%s, %u bytes) : CHECKSUM ERROR nChecksum=%08x hdr.nChecksum=%08x\n", strCommand.c_str(), nMessageSize, nChecksum, hdr.nChecksum); continue; } // Process message bool fRet = false; try { { LOCK(cs_main); fRet = ProcessMessage(pfrom, strCommand, vRecv); } if (fShutdown) break; } catch (std::ios_base::failure& e) { if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv printf("ProcessMessages(%s, %u bytes) : Exception '%s' caught, normally caused by a message being shorter than its stated length\n", strCommand.c_str(), nMessageSize, e.what()); } else if (strstr(e.what(), "size too large")) { // Allow exceptions from over-long size printf("ProcessMessages(%s, %u bytes) : Exception '%s' caught\n", strCommand.c_str(), nMessageSize, e.what()); } else { PrintExceptionContinue(&e, "ProcessMessages()"); } } catch (std::exception& e) { PrintExceptionContinue(&e, "ProcessMessages()"); } catch (...) { PrintExceptionContinue(NULL, "ProcessMessages()"); } if (!fRet) printf("ProcessMessage(%s, %u bytes) FAILED\n", strCommand.c_str(), nMessageSize); } pfrom->vRecvMsg.erase(pfrom->vRecvMsg.begin(), it); return fOk; } bool SendMessages(CNode* pto, bool fSendTrickle) { TRY_LOCK(cs_main, lockMain); if (lockMain) { // Don't send anything until we get their version message if (pto->nVersion == 0) return true; // Keep-alive ping. We send a nonce of zero because we don't use it anywhere // right now. if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSend.empty()) { uint64 nonce = 0; if (pto->nVersion > BIP0031_VERSION) pto->PushMessage("ping", nonce); else pto->PushMessage("ping"); } // Resend wallet transactions that haven't gotten in a block yet ResendWalletTransactions(); // Address refresh broadcast static int64 nLastRebroadcast; if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60)) { { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { // Periodically clear setAddrKnown to allow refresh broadcasts if (nLastRebroadcast) pnode->setAddrKnown.clear(); // Rebroadcast our address if (!fNoListen) { CAddress addr = GetLocalAddress(&pnode->addr); if (addr.IsRoutable()) pnode->PushAddress(addr); } } } nLastRebroadcast = GetTime(); } // // Message: addr // if (fSendTrickle) { vector<CAddress> vAddr; vAddr.reserve(pto->vAddrToSend.size()); BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend) { // returns true if wasn't already contained in the set if (pto->setAddrKnown.insert(addr).second) { vAddr.push_back(addr); // receiver rejects addr messages larger than 1000 if (vAddr.size() >= 1000) { pto->PushMessage("addr", vAddr); vAddr.clear(); } } } pto->vAddrToSend.clear(); if (!vAddr.empty()) pto->PushMessage("addr", vAddr); } // // Message: inventory // vector<CInv> vInv; vector<CInv> vInvWait; { LOCK(pto->cs_inventory); vInv.reserve(pto->vInventoryToSend.size()); vInvWait.reserve(pto->vInventoryToSend.size()); BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) { if (pto->setInventoryKnown.count(inv)) continue; // trickle out tx inv to protect privacy if (inv.type == MSG_TX && !fSendTrickle) { // 1/4 of tx invs blast to all immediately static uint256 hashSalt; if (hashSalt == 0) hashSalt = GetRandHash(); uint256 hashRand = inv.hash ^ hashSalt; hashRand = Hash(BEGIN(hashRand), END(hashRand)); bool fTrickleWait = ((hashRand & 3) != 0); // always trickle our own transactions if (!fTrickleWait) { CWalletTx wtx; if (GetTransaction(inv.hash, wtx)) if (wtx.fFromMe) fTrickleWait = true; } if (fTrickleWait) { vInvWait.push_back(inv); continue; } } // returns true if wasn't already contained in the set if (pto->setInventoryKnown.insert(inv).second) { vInv.push_back(inv); if (vInv.size() >= 1000) { pto->PushMessage("inv", vInv); vInv.clear(); } } } pto->vInventoryToSend = vInvWait; } if (!vInv.empty()) pto->PushMessage("inv", vInv); // // Message: getdata // vector<CInv> vGetData; int64 nNow = GetTime() * 1000000; CTxDB txdb("r"); while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow) { const CInv& inv = (*pto->mapAskFor.begin()).second; if (!AlreadyHave(txdb, inv)) { if (fDebugNet) printf("sending getdata: %s\n", inv.ToString().c_str()); vGetData.push_back(inv); if (vGetData.size() >= 1000) { pto->PushMessage("getdata", vGetData); vGetData.clear(); } mapAlreadyAskedFor[inv] = nNow; } pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) pto->PushMessage("getdata", vGetData); } return true; } ////////////////////////////////////////////////////////////////////////////// // // BitcoinMiner // int static FormatHashBlocks(void* pbuffer, unsigned int len) { unsigned char* pdata = (unsigned char*)pbuffer; unsigned int blocks = 1 + ((len + 8) / 64); unsigned char* pend = pdata + 64 * blocks; memset(pdata + len, 0, 64 * blocks - len); pdata[len] = 0x80; unsigned int bits = len * 8; pend[-1] = (bits >> 0) & 0xff; pend[-2] = (bits >> 8) & 0xff; pend[-3] = (bits >> 16) & 0xff; pend[-4] = (bits >> 24) & 0xff; return blocks; } static const unsigned int pSHA256InitState[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; void SHA256Transform(void* pstate, void* pinput, const void* pinit) { SHA256_CTX ctx; unsigned char data[64]; SHA256_Init(&ctx); for (int i = 0; i < 16; i++) ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]); for (int i = 0; i < 8; i++) ctx.h[i] = ((uint32_t*)pinit)[i]; SHA256_Update(&ctx, data, sizeof(data)); for (int i = 0; i < 8; i++) ((uint32_t*)pstate)[i] = ctx.h[i]; } // Some explaining would be appreciated class COrphan { public: CTransaction* ptx; set<uint256> setDependsOn; double dPriority; double dFeePerKb; COrphan(CTransaction* ptxIn) { ptx = ptxIn; dPriority = dFeePerKb = 0; } void print() const { printf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n", ptx->GetHash().ToString().substr(0,10).c_str(), dPriority, dFeePerKb); BOOST_FOREACH(uint256 hash, setDependsOn) printf(" setDependsOn %s\n", hash.ToString().substr(0,10).c_str()); } }; uint64 nLastBlockTx = 0; uint64 nLastBlockSize = 0; int64 nLastCoinStakeSearchInterval = 0; // We want to sort transactions by priority and fee, so: typedef boost::tuple<double, double, CTransaction*> TxPriority; class TxPriorityCompare { bool byFee; public: TxPriorityCompare(bool _byFee) : byFee(_byFee) { } bool operator()(const TxPriority& a, const TxPriority& b) { if (byFee) { if (a.get<1>() == b.get<1>()) return a.get<0>() < b.get<0>(); return a.get<1>() < b.get<1>(); } else { if (a.get<0>() == b.get<0>()) return a.get<1>() < b.get<1>(); return a.get<0>() < b.get<0>(); } } }; // CreateNewBlock: // fProofOfStake: try (best effort) to make a proof-of-stake block CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) { CReserveKey reservekey(pwallet); // Create new block auto_ptr<CBlock> pblock(new CBlock()); if (!pblock.get()) return NULL; // Create coinbase tx CTransaction txNew; txNew.vin.resize(1); txNew.vin[0].prevout.SetNull(); txNew.vout.resize(1); txNew.vout[0].scriptPubKey << reservekey.GetReservedKey() << OP_CHECKSIG; // Add our coinbase tx as first transaction pblock->vtx.push_back(txNew); // Largest block you're willing to create: unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2); // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity: nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); // How much of the block should be dedicated to high-priority transactions, // included regardless of the fees they pay unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", 27000); nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); // Minimum block size you want to create; block will be filled with free transactions // until there are no more or the block reaches this size: unsigned int nBlockMinSize = GetArg("-blockminsize", 0); nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); // Fee-per-kilobyte amount considered the same as "free" // Be careful setting this: if you set it to zero then // a transaction spammer can cheaply fill blocks using // 1-satoshi-fee transactions. It should be set above the real // cost to you of processing a transaction. int64 nMinTxFee = MIN_TX_FEE; if (mapArgs.count("-mintxfee")) ParseMoney(mapArgs["-mintxfee"], nMinTxFee); // ppcoin: if coinstake available add coinstake tx static int64 nLastCoinStakeSearchTime = GetAdjustedTime(); // only initialized at startup CBlockIndex* pindexPrev = pindexBest; if (fProofOfStake && !pwalletMain->fDisableStake) // attempt to find a coinstake && make sure settings allow PoS (presstab HyperStake) { pblock->nBits = GetNextTargetRequired(pindexPrev, true); CTransaction txCoinStake; int64 nSearchTime = txCoinStake.nTime; // search to current time if (nSearchTime > nLastCoinStakeSearchTime) { // printf(">>> OK1\n"); if (pwallet->CreateCoinStake(*pwallet, pblock->nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake)) { if (txCoinStake.nTime >= max(pindexPrev->GetMedianTimePast()+1, pindexPrev->GetBlockTime() - GetClockDrift(pindexPrev->GetBlockTime()))) { // make sure coinstake would meet timestamp protocol // as it would be the same as the block timestamp pblock->vtx[0].vout[0].SetEmpty(); pblock->vtx[0].nTime = txCoinStake.nTime; pblock->vtx.push_back(txCoinStake); } } nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime; nLastCoinStakeSearchTime = nSearchTime; } } pblock->nBits = GetNextTargetRequired(pindexPrev, pblock->IsProofOfStake()); // Collect memory pool transactions into the block int64 nFees = 0; { LOCK2(cs_main, mempool.cs); CBlockIndex* pindexPrev = pindexBest; CTxDB txdb("r"); // Priority order to process transactions list<COrphan> vOrphan; // list memory doesn't move map<uint256, vector<COrphan*> > mapDependers; // This vector will be sorted into a priority queue: vector<TxPriority> vecPriority; vecPriority.reserve(mempool.mapTx.size()); for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) { CTransaction& tx = (*mi).second; if (tx.IsCoinBase() || tx.IsCoinStake() || !tx.IsFinal()) continue; COrphan* porphan = NULL; double dPriority = 0; int64 nTotalIn = 0; bool fMissingInputs = false; BOOST_FOREACH(const CTxIn& txin, tx.vin) { // Read prev transaction CTransaction txPrev; CTxIndex txindex; if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex)) { // This should never happen; all transactions in the memory // pool should connect to either transactions in the chain // or other transactions in the memory pool. if (!mempool.mapTx.count(txin.prevout.hash)) { printf("ERROR: mempool transaction missing input\n"); if (fDebug) assert("mempool transaction missing input" == 0); fMissingInputs = true; if (porphan) vOrphan.pop_back(); break; } // Has to wait for dependencies if (!porphan) { // Use list for automatic deletion vOrphan.push_back(COrphan(&tx)); porphan = &vOrphan.back(); } mapDependers[txin.prevout.hash].push_back(porphan); porphan->setDependsOn.insert(txin.prevout.hash); nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue; continue; } int64 nValueIn = txPrev.vout[txin.prevout.n].nValue; nTotalIn += nValueIn; int nConf = txindex.GetDepthInMainChain(); dPriority += (double)nValueIn * nConf; } if (fMissingInputs) continue; // Priority is sum(valuein * age) / txsize unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); dPriority /= nTxSize; // This is a more accurate fee-per-kilobyte than is used by the client code, because the // client code rounds up the size to the nearest 1K. That's good, because it gives an // incentive to create smaller transactions. double dFeePerKb = double(nTotalIn-tx.GetValueOut()) / (double(nTxSize)/1000.0); if (porphan) { porphan->dPriority = dPriority; porphan->dFeePerKb = dFeePerKb; } else vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second)); } // Collect transactions into block map<uint256, CTxIndex> mapTestPool; uint64 nBlockSize = 1000; uint64 nBlockTx = 0; int nBlockSigOps = 100; bool fSortedByFee = (nBlockPrioritySize <= 0); TxPriorityCompare comparer(fSortedByFee); std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); while (!vecPriority.empty()) { // Take highest priority transaction off the priority queue: double dPriority = vecPriority.front().get<0>(); double dFeePerKb = vecPriority.front().get<1>(); CTransaction& tx = *(vecPriority.front().get<2>()); std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer); vecPriority.pop_back(); // Size limits unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); if (nBlockSize + nTxSize >= nBlockMaxSize) continue; // Legacy limits on sigOps: unsigned int nTxSigOps = tx.GetLegacySigOpCount(); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; // Timestamp limit if (tx.nTime > GetAdjustedTime() || (pblock->IsProofOfStake() && tx.nTime > pblock->vtx[1].nTime)) continue; // ppcoin: simplify transaction fee - allow free = false int64 nMinFee = tx.GetMinFee(nBlockSize, false, GMF_BLOCK); // Skip free transactions if we're past the minimum block size: if (fSortedByFee && (dFeePerKb < nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize)) continue; // Prioritize by fee once past the priority size or we run out of high-priority // transactions: if (!fSortedByFee && ((nBlockSize + nTxSize >= nBlockPrioritySize) || (dPriority < COIN * 144 / 250))) { fSortedByFee = true; comparer = TxPriorityCompare(fSortedByFee); std::make_heap(vecPriority.begin(), vecPriority.end(), comparer); } // Connecting shouldn't fail due to dependency on other memory pool transactions // because we're already processing them in order of dependency map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool); MapPrevTx mapInputs; bool fInvalid; if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid)) continue; int64 nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut(); if (nTxFees < nMinFee) continue; nTxSigOps += tx.GetP2SHSigOpCount(mapInputs); if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) continue; if (!tx.ConnectInputs(txdb, mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true)) continue; mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size()); swap(mapTestPool, mapTestPoolTmp); // Added pblock->vtx.push_back(tx); nBlockSize += nTxSize; ++nBlockTx; nBlockSigOps += nTxSigOps; nFees += nTxFees; if (fDebug && GetBoolArg("-printpriority")) { printf("priority %.1f feeperkb %.1f txid %s\n", dPriority, dFeePerKb, tx.GetHash().ToString().c_str()); } // Add transactions that depend on this one to the priority queue uint256 hash = tx.GetHash(); if (mapDependers.count(hash)) { BOOST_FOREACH(COrphan* porphan, mapDependers[hash]) { if (!porphan->setDependsOn.empty()) { porphan->setDependsOn.erase(hash); if (porphan->setDependsOn.empty()) { vecPriority.push_back(TxPriority(porphan->dPriority, porphan->dFeePerKb, porphan->ptx)); std::push_heap(vecPriority.begin(), vecPriority.end(), comparer); } } } } } nLastBlockTx = nBlockTx; nLastBlockSize = nBlockSize; if (fDebug && GetBoolArg("-printpriority")) printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); if (pblock->IsProofOfWork()) pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pindexPrev->nHeight+1, nFees, pindexPrev->GetBlockHash()); // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); if (pblock->IsProofOfStake()) pblock->nTime = pblock->vtx[1].nTime; //same as coinstake timestamp pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime()); pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - GetClockDrift(pindexPrev->GetBlockTime())); if (pblock->IsProofOfWork()) pblock->UpdateTime(pindexPrev); pblock->nNonce = 0; } return pblock.release(); } void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce) { // Update nExtraNonce static uint256 hashPrevBlock; if (hashPrevBlock != pblock->hashPrevBlock) { nExtraNonce = 0; hashPrevBlock = pblock->hashPrevBlock; } ++nExtraNonce; unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2 pblock->vtx[0].vin[0].scriptSig = (CScript() << nHeight << CBigNum(nExtraNonce)) + COINBASE_FLAGS; assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100); pblock->hashMerkleRoot = pblock->BuildMerkleTree(); } void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1) { // // Pre-build hash buffers // struct { struct unnamed2 { int nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; unsigned int nTime; unsigned int nBits; unsigned int nNonce; } block; unsigned char pchPadding0[64]; uint256 hash1; unsigned char pchPadding1[64]; } tmp; memset(&tmp, 0, sizeof(tmp)); tmp.block.nVersion = pblock->nVersion; tmp.block.hashPrevBlock = pblock->hashPrevBlock; tmp.block.hashMerkleRoot = pblock->hashMerkleRoot; tmp.block.nTime = pblock->nTime; tmp.block.nBits = pblock->nBits; tmp.block.nNonce = pblock->nNonce; FormatHashBlocks(&tmp.block, sizeof(tmp.block)); FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1)); // Byte swap all the input buffer for (unsigned int i = 0; i < sizeof(tmp)/4; i++) ((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]); // Precalc the first half of the first hash, which stays constant SHA256Transform(pmidstate, &tmp.block, pSHA256InitState); memcpy(pdata, &tmp.block, 128); memcpy(phash1, &tmp.hash1, 64); } bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey) { uint256 hash = pblock->GetHash(); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); if (hash > hashTarget && pblock->IsProofOfWork()) return error("BitcoinMiner : proof-of-work not meeting target"); //// debug print printf("BitcoinMiner:\n"); printf("new block found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str()); pblock->print(); printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str()); // Found a solution { LOCK(cs_main); if (pblock->hashPrevBlock != hashBestChain) return error("BitcoinMiner : generated block is stale"); // Remove key from key pool reservekey.KeepKey(); // Track how many getdata requests this block gets { LOCK(wallet.cs_wallet); wallet.mapRequestCount[pblock->GetHash()] = 0; } // Process this block the same as if we had received it from another node if (!ProcessBlock(NULL, pblock)) return error("BitcoinMiner : ProcessBlock, block not accepted"); } return true; } void static ThreadBitcoinMiner(void* parg); static bool fGenerateBitcoins = false; static bool fLimitProcessors = false; static int nLimitProcessors = -1; void BitcoinMiner(CWallet *pwallet, bool fProofOfStake) { printf("CPUMiner started for proof-of-%s\n", fProofOfStake? "stake" : "work"); SetThreadPriority(THREAD_PRIORITY_LOWEST); // Make this thread recognisable as the mining thread RenameThread("bitcoin-miner"); // Each thread has its own key and counter CReserveKey reservekey(pwallet); unsigned int nExtraNonce = 0; while (fGenerateBitcoins || fProofOfStake) { if (fShutdown) return; while (vNodes.empty() || IsInitialBlockDownload() || pwallet->IsLocked() || !pwallet->MintableCoins()) { nLastCoinStakeSearchInterval = 0; Sleep(1000); if (fShutdown) return; if (!fGenerateBitcoins && !fProofOfStake) return; } if(mapHashedBlocks.count(nBestHeight)) //search our map of hashed blocks, see if bestblock has been hashed yet { if(GetTime() - mapHashedBlocks[nBestHeight] < max(pwallet->nHashInterval, (unsigned int)1)) // wait half of the nHashDrift with max wait of 3 minutes { Sleep(2500); // 2.5 second sleep continue; } } // // Create new block // unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; CBlockIndex* pindexPrev = pindexBest; auto_ptr<CBlock> pblock(CreateNewBlock(pwallet, fProofOfStake)); if (!pblock.get()) return; IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce); if (fProofOfStake) { // ppcoin: if proof-of-stake block found then process block if (pblock->IsProofOfStake()) { printf("CPUMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString().c_str()); if (!pblock->SignBlock(*pwalletMain)) { continue; } strMintWarning = ""; printf("CPUMiner : proof-of-stake block was signed %s\n", pblock->GetHash().ToString().c_str()); SetThreadPriority(THREAD_PRIORITY_NORMAL); CheckWork(pblock.get(), *pwalletMain, reservekey); SetThreadPriority(THREAD_PRIORITY_LOWEST); } continue; } printf("Running BitcoinMiner with %"PRIszu" transactions in block (%u bytes)\n", pblock->vtx.size(), ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); // // Pre-build hash buffers // char pmidstatebuf[32+16]; char* pmidstate = alignup<16>(pmidstatebuf); char pdatabuf[128+16]; char* pdata = alignup<16>(pdatabuf); char phash1buf[64+16]; char* phash1 = alignup<16>(phash1buf); FormatHashBuffers(pblock.get(), pmidstate, pdata, phash1); unsigned int& nBlockTime = *(unsigned int*)(pdata + 64 + 4); unsigned int& nBlockBits = *(unsigned int*)(pdata + 64 + 8); // // Search // int64 nStart = GetTime(); uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); while (true) { unsigned int nHashesDone = 0; uint256 thash; while (true) { thash = pblock->GetHash(); if (thash <= hashTarget) { if (!pblock->SignBlock(*pwalletMain)) { break; } SetThreadPriority(THREAD_PRIORITY_NORMAL); CheckWork(pblock.get(), *pwallet, reservekey); SetThreadPriority(THREAD_PRIORITY_LOWEST); break; } pblock->nNonce += 1; nHashesDone += 1; if ((pblock->nNonce & 0xFF) == 0) break; } // Meter hashes/sec static int64 nHashCounter; if (nHPSTimerStart == 0) { nHPSTimerStart = GetTimeMillis(); nHashCounter = 0; } else nHashCounter += nHashesDone; if (GetTimeMillis() - nHPSTimerStart > 4000) { static CCriticalSection cs; { LOCK(cs); if (GetTimeMillis() - nHPSTimerStart > 4000) { dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart); nHPSTimerStart = GetTimeMillis(); nHashCounter = 0; static int64 nLogTime; if (GetTime() - nLogTime > 30 * 60) { nLogTime = GetTime(); printf("hashmeter %6.0f khash/s\n", dHashesPerSec/1000.0); } } } } // Check for stop or if block needs to be rebuilt boost::this_thread::interruption_point(); if (vNodes.empty()) break; if (pblock->nNonce >= 0xffff0000) break; if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60) break; if (pindexPrev != pindexBest) break; // Update nTime every few seconds pblock->UpdateTime(pindexPrev); nBlockTime = ByteReverse(pblock->nTime); if (fTestNet) { // Changing pblock->nTime can change work required on testnet: nBlockBits = ByteReverse(pblock->nBits); hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); } } } } void static ThreadBitcoinMiner(void* parg) { CWallet* pwallet = (CWallet*)parg; try { vnThreadsRunning[THREAD_MINER]++; BitcoinMiner(pwallet, false); vnThreadsRunning[THREAD_MINER]--; } catch (std::exception& e) { vnThreadsRunning[THREAD_MINER]--; PrintException(&e, "ThreadBitcoinMiner()"); } catch (...) { vnThreadsRunning[THREAD_MINER]--; PrintException(NULL, "ThreadBitcoinMiner()"); } nHPSTimerStart = 0; if (vnThreadsRunning[THREAD_MINER] == 0) dHashesPerSec = 0; printf("ThreadBitcoinMiner exiting, %d threads remaining\n", vnThreadsRunning[THREAD_MINER]); } void GenerateBitcoins(bool fGenerate, CWallet* pwallet) { fGenerateBitcoins = fGenerate; nLimitProcessors = GetArg("-genproclimit", -1); if (nLimitProcessors == 0) fGenerateBitcoins = false; fLimitProcessors = (nLimitProcessors != -1); if (fGenerate) { int nProcessors = boost::thread::hardware_concurrency(); printf("%d processors\n", nProcessors); if (nProcessors < 1) nProcessors = 1; if (fLimitProcessors && nProcessors > nLimitProcessors) nProcessors = nLimitProcessors; int nAddThreads = nProcessors - vnThreadsRunning[THREAD_MINER]; printf("Starting %d BitcoinMiner threads\n", nAddThreads); for (int i = 0; i < nAddThreads; i++) { if (!NewThread(ThreadBitcoinMiner, pwallet)) printf("Error: NewThread(ThreadBitcoinMiner) failed\n"); Sleep(10); } } }
查找差异