main.cpp
811 lines
// 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/config.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/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.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) >> 20);
static CBigNum bnProofOfStakeLimit(~uint256(0) >> 20);
static CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 20);
static CBigNum bnProofOfWorkLimitTestNet(~uint256(0) >> 20);
static CBigNum bnProofOfStakeLimitTestNet(~uint256(0) >> 20);
static CBigNum bnProofOfStakeLimitTestNet(~uint256(0) >> 20);
unsigned int nStakeMinAge = 20 * 60; // minimum age for coin age: 30m
unsigned int nStakeMinAge = 20 * 60; // minimum age for coin age: 20m
unsigned int nStakeMaxAge = 60 * 45 ; // stake age of full weight: 1hr
unsigned int nStakeMaxAge = 60 * 45 ; // stake age of full weight: 45m
unsigned int nStakeTargetSpacing = 3*60; // 30 sec block spacing
unsigned int nStakeTargetSpacing = 3*60; // 180 sec block spacing
int64 nChainStartTime = 1503369778;
int64 nChainStartTime = 1503369778;
int nCoinbaseMaturity = 10;
int nCoinbaseMaturity = 10;
CBlockIndex* pindexGenesisBlock = NULL;
CBlockIndex* pindexGenesisBlock = NULL;
int nBestHeight = -1;
int nBestHeight = -1;
CBigNum bnBestChainTrust = 0;
uint256 nBestChainTrust = 0;
CBigNum bnBestInvalidTrust = 0;
uint256 nBestInvalidTrust = 0;
uint256 hashBestChain = 0;
uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
CBlockIndex* pindexBest = NULL;
int64 nTimeBestReceived = 0;
int64 nTimeBestReceived = 0;
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, uint256> mapProofOfStake;
map<uint256, CDataStream*> mapOrphanTransactions;
map<uint256, CDataStream*> mapOrphanTransactions;
map<uint256, map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
map<uint256, map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
// Constant stuff for coinbase transactions we create:
// Constant stuff for coinbase transactions we create:
CScript COINBASE_FLAGS;
CScript COINBASE_FLAGS;
const string strMessageMagic = "Morningstar Signed Message:\n";
const string strMessageMagic = "Eidas Signed Message:\n";
double dHashesPerSec;
double dHashesPerSec;
int64 nHPSTimerStart;
int64 nHPSTimerStart;
// Settings
// Settings
int64 nTransactionFee = MIN_TX_FEE;
int64 nTransactionFee = MIN_TX_FEE;
// Used during database migration.
bool fDisableSignatureChecking = false;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
//
// 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);
}
}
}
}
// 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)
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
if (pwallet->IsFromMe(tx))
if (pwallet->IsFromMe(tx))
return true;
return true;
return false;
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)
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
if (pwallet->GetTransaction(hashTx,wtx))
if (pwallet->GetTransaction(hashTx,wtx))
return true;
return true;
return false;
return false;
}
}
// erases transaction with the given hash from all wallets
// erases transaction with the given hash from all wallets
void static EraseFromWallets(uint256 hash)
void static EraseFromWallets(uint256 hash)
{
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->EraseFromWallet(hash);
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
// Eidas: wallets need to refund inputs when disconnecting coinstake
if (tx.IsCoinStake())
if (tx.IsCoinStake())
{
{
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)
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
}
}
// 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)
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->SetBestChain(loc);
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)
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->UpdatedTransaction(hashTx);
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)
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->PrintWallet(block);
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)
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->Inventory(hash);
pwallet->Inventory(hash);
}
}
// ask wallets to resend their transactions
// ask wallets to resend their transactions
void ResendWalletTransactions()
void ResendWalletTransactions()
{
{
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
pwallet->ResendWalletTransactions();
pwallet->ResendWalletTransactions();
}
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
//
// mapOrphanTransactions
// mapOrphanTransactions
//
//
bool AddOrphanTx(const CDataStream& vMsg)
bool AddOrphanTx(const CDataStream& vMsg)
{
{
CTransaction tx;
CTransaction tx;
CDataStream(vMsg) >> 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);
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->size() > 5000)
if (pvMsg->size() > 5000)
{
{
printf("ignoring large orphan tx (size: %"PRIszu", hash: %s)\n", pvMsg->size(), hash.ToString().substr(0,10).c_str());
printf("ignoring large orphan tx (size: %" PRIszu ", hash: %s)\n", pvMsg->size(), hash.ToString().substr(0,10).c_str());
delete pvMsg;
delete pvMsg;
return false;
return false;
}
}
mapOrphanTransactions[hash] = pvMsg;
mapOrphanTransactions[hash] = pvMsg;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
BOOST_FOREACH(const CTxIn& txin, tx.vin)
mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg));
mapOrphanTransactionsByPrev[txin.prevout.hash].insert(make_pair(hash, pvMsg));
printf("stored orphan tx %s (mapsz %"PRIszu")\n", hash.ToString().substr(0,10).c_str(),
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 CDataStream* pvMsg = mapOrphanTransactions[hash];
const CDataStream* pvMsg = mapOrphanTransactions[hash];
CTransaction tx;
CTransaction tx;
CDataStream(*pvMsg) >> 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;
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, CDataStream*>::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;
}
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
//
// 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 CTransaction::IsStandard() const
bool CTransaction::IsStandard() const
{
{
if (nVersion > CTransaction::CURRENT_VERSION)
if (nVersion > CTransaction::CURRENT_VERSION)
return false;
return false;
BOOST_FOREACH(const CTxIn& txin, vin)
BOOST_FOREACH(const CTxIn& txin, 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;
}
}
BOOST_FOREACH(const CTxOut& txout, vout) {
BOOST_FOREACH(const CTxOut& txout, vout) {
if (!::IsStandard(txout.scriptPubKey))
if (!::IsStandard(txout.scriptPubKey))
return false;
return false;
if (txout.nValue == 0)
if (txout.nValue == 0)
return false;
return false;
}
}
return true;
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, false, 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)
{
{
if (fClient)
if (fClient)
{
{
if (hashBlock == 0)
if (hashBlock == 0)
return 0;
return 0;
}
}
else
else
{
{
CBlock blockTmp;
CBlock blockTmp;
if (pblock == NULL)
if (pblock == NULL)
{
{
// Load the block this tx is in
// Load the block this tx is in
CTxIndex txindex;
CTxIndex txindex;
if (!CTxDB("r").ReadTxIndex(GetHash(), txindex))
if (!CTxDB("r").ReadTxIndex(GetHash(), txindex))
return 0;
return 0;
if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos))
if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos))
return 0;
return 0;
pblock = &blockTmp;
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;
printf("ERROR: SetMerkleBranch() : couldn't find tx in block\n");
printf("ERROR: SetMerkleBranch() : couldn't find tx in block\n");
return 0;
return 0;
}
}
// Fill in merkle branch
// Fill in merkle branch
vMerkleBranch = pblock->GetMerkleBranch(nIndex);
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"));
// 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 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
// Eidas: 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 > 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 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 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 nMinFee = (1 + (int64)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 nMinFee;
}
}
bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
bool* pfMissingInputs)
bool* pfMissingInputs)
{
{
if (pfMissingInputs)
if (pfMissingInputs)
*pfMissingInputs = false;
*pfMissingInputs = false;
if (!tx.CheckTransaction())
if (!tx.CheckTransaction())
return error("CTxMemPool::accept() : 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("CTxMemPool::accept() : 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
// Eidas: coinstake is also only valid in a block, not as a loose transaction
if (tx.IsCoinStake())
if (tx.IsCoinStake())
return tx.DoS(100, error("CTxMemPool::accept() : 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
// To help v0.1.5 clients who would see it as a negative number
if ((int64)tx.nLockTime > std::numeric_limits<int>::max())
if ((int64)tx.nLockTime > std::numeric_limits<int>::max())
return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet");
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 && !tx.IsStandard())
if (!fTestNet && !tx.IsStandard())
return error("CTxMemPool::accept() : nonstandard transaction type");
return error("CTxMemPool::accept() : nonstandard transaction type");
// Do we already have it?
// Do we already have it?
uint256 hash = tx.GetHash();
uint256 hash = tx.GetHash();
{
{
LOCK(cs);
LOCK(cs);
if (mapTx.count(hash))
if (mapTx.count(hash))
return false;
return false;
}
}
if (fCheckInputs)
if (fCheckInputs)
if (txdb.ContainsTx(hash))
if (txdb.ContainsTx(hash))
return false;
return false;
// Check for conflicts with in-memory transactions
// Check for conflicts with in-memory transactions
CTransaction* ptxOld = NULL;
CTransaction* ptxOld = NULL;
for (unsigned int i = 0; i < tx.vin.size(); i++)
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
{
COutPoint outpoint = tx.vin[i].prevout;
COutPoint outpoint = tx.vin[i].prevout;
if (mapNextTx.count(outpoint))
if (mapNextTx.count(outpoint))
{
{
// Disable replacement feature for now
// Disable replacement feature for now
return false;
return false;
// Allow replacing with a newer version of the same transaction
// Allow replacing with a newer version of the same transaction
if (i != 0)
if (i != 0)
return false;
return false;
ptxOld = mapNextTx[outpoint].ptx;
ptxOld = mapNextTx[outpoint].ptx;
if (ptxOld->IsFinal())
if (ptxOld->IsFinal())
return false;
return false;
if (!tx.IsNewerThan(*ptxOld))
if (!tx.IsNewerThan(*ptxOld))
return false;
return false;
for (unsigned int i = 0; i < tx.vin.size(); i++)
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
{
COutPoint outpoint = tx.vin[i].prevout;
COutPoint outpoint = tx.vin[i].prevout;
if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld)
if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld)
return false;
return false;
}
}
break;
break;
}
}
}
}
if (fCheckInputs)
if (fCheckInputs)
{
{
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("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str());
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("CTxMemPool::accept() : 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 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 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("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d,
return error("CTxMemPool::accept() : not enough fees %s, %" PRI64d " < %" PRI64d,
hash.ToString().c_str(),
hash.ToString().c_str(),
nFees, txMinFee);
nFees, txMinFee);
// Continuously rate-limit free transactions
// Continuously rate-limit free transactions
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
// be annoying or make others' transactions take longer to confirm.
// be annoying or make others' transactions take longer to confirm.
if (nFees < MIN_RELAY_TX_FEE)
if (nFees < MIN_RELAY_TX_FEE)
{
{
static CCriticalSection cs;
static CCriticalSection cs;
static double dFreeCount;
static double dFreeCount;
static int64 nLastTime;
static int64 nLastTime;
int64 nNow = GetTime();
int64 nNow = GetTime();
{
{
LOCK(cs);
LOCK(cs);
// Use an exponentially decaying ~10-minute window:
// Use an exponentially decaying ~10-minute window:
dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
nLastTime = nNow;
nLastTime = nNow;
// -limitfreerelay unit is thousand-bytes-per-minute
// -limitfreerelay unit is thousand-bytes-per-minute
// At default rate it would take over a month to fill 1GB
// At default rate it would take over a month to fill 1GB
if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(tx))
if (dFreeCount > GetArg("-limitfreerelay", 15)*10*1000 && !IsFromMe(tx))
return error("CTxMemPool::accept() : free transaction rejected by rate limiter");
return error("CTxMemPool::accept() : free transaction rejected by rate limiter");
if (fDebug)
if (fDebug)
printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
printf("Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
dFreeCount += nSize;
dFreeCount += nSize;
}
}
}
}
// Check against previous transactions
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
// 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))
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());
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
}
}
}
}
// Store transaction in memory
// Store transaction in memory
{
{
LOCK(cs);
LOCK(cs);
if (ptxOld)
if (ptxOld)
{
{
printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str());
printf("CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str());
remove(*ptxOld);
remove(*ptxOld);
}
}
addUnchecked(hash, tx);
addUnchecked(hash, tx);
}
}
///// are we sure this is ok when loading transactions or restoring block txes
///// are we sure this is ok when loading transactions or restoring block txes
// If updated, erase old tx from wallet
// If updated, erase old tx from wallet
if (ptxOld)
if (ptxOld)
EraseFromWallets(ptxOld->GetHash());
EraseFromWallets(ptxOld->GetHash());
printf("CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n",
printf("CTxMemPool::accept() : accepted %s (poolsz %" PRIszu ")\n",
hash.ToString().substr(0,10).c_str(),
hash.ToString().substr(0,10).c_str(),
mapTx.size());
mapTx.size());
return true;
return true;
}
}
bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs)
bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs)
{
{
return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs);
return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs);
}
}
bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
{
{
// Add to memory pool without checking anything. Don't call this directly,
// Add to memory pool without checking anything. Don't call this directly,
// call CTxMemPool::accept to properly check the transaction first.
// call CTxMemPool::accept to properly check the transaction first.
{
{
mapTx[hash] = tx;
mapTx[hash] = tx;
for (unsigned int i = 0; i < tx.vin.size(); i++)
for (unsigned int i = 0; i < tx.vin.size(); i++)
mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i);
mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i);
nTransactionsUpdated++;
nTransactionsUpdated++;
}
}
return true;
return true;
}
}
bool CTxMemPool::remove(CTransaction &tx)
bool CTxMemPool::remove(CTransaction &tx)
{
{
// Remove transaction from memory pool
// Remove transaction from memory pool
{
{
LOCK(cs);
LOCK(cs);
uint256 hash = tx.GetHash();
uint256 hash = tx.GetHash();
if (mapTx.count(hash))
if (mapTx.count(hash))
{
{
BOOST_FOREACH(const CTxIn& txin, tx.vin)
BOOST_FOREACH(const CTxIn& txin, tx.vin)
mapNextTx.erase(txin.prevout);
mapNextTx.erase(txin.prevout);
mapTx.erase(hash);
mapTx.erase(hash);
nTransactionsUpdated++;
nTransactionsUpdated++;
}
}
}
}
return true;
return true;
}
}
void CTxMemPool::clear()
void CTxMemPool::clear()
{
{
LOCK(cs);
LOCK(cs);
mapTx.clear();
mapTx.clear();
mapNextTx.clear();
mapNextTx.clear();
++nTransactionsUpdated;
++nTransactionsUpdated;
}
}
void CTxMemPool::queryHashes(std::vector<uint256>& vtxid)
void CTxMemPool::queryHashes(std::vector<uint256>& vtxid)
{
{
vtxid.clear();
vtxid.clear();
LOCK(cs);
LOCK(cs);
vtxid.reserve(mapTx.size());
vtxid.reserve(mapTx.size());
for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi)
for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi)
vtxid.push_back((*mi).first);
vtxid.push_back((*mi).first);
}
}
int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
{
{
if (hashBlock == 0 || nIndex == -1)
if (hashBlock == 0 || nIndex == -1)
return 0;
return 0;
// Find the block it claims to be in
// Find the block it claims to be in
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;
// Make sure the merkle branch connects to this block
// Make sure the merkle branch connects to this block
if (!fMerkleVerified)
if (!fMerkleVerified)
{
{
if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot)
if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot)
return 0;
return 0;
fMerkleVerified = true;
fMerkleVerified = true;
}
}
pindexRet = pindex;
pindexRet = pindex;
return pindexBest->nHeight - pindex->nHeight + 1;
return pindexBest->nHeight - pindex->nHeight + 1;
}
}
int CMerkleTx::GetBlocksToMaturity() const
int CMerkleTx::GetBlocksToMaturity() const
{
{
if (!(IsCoinBase() || IsCoinStake()))
if (!(IsCoinBase() || IsCoinStake()))
return 0;
return 0;
return max(0, (nCoinbaseMaturity+20) - GetDepthInMainChain());
return max(0, (nCoinbaseMaturity+(GetAdjustedTime() > FORK_TIME ? 30 : 20)) - GetDepthInMainChain());
}
}
bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs)
bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs)
{
{
if (fClient)
if (fClient)
{
{
if (!IsInMainChain() && !ClientConnectInputs())
if (!IsInMainChain() && !ClientConnectInputs())
return false;
return false;
return CTransaction::AcceptToMemoryPool(txdb, false);
return CTransaction::AcceptToMemoryPool(txdb, false);
}
}
else
else
{
{
return CTransaction::AcceptToMemoryPool(txdb, fCheckInputs);
return CTransaction::AcceptToMemoryPool(txdb, fCheckInputs);
}
}
}
}
bool CMerkleTx::AcceptToMemoryPool()
bool CMerkleTx::AcceptToMemoryPool()
{
{
CTxDB txdb("r");
CTxDB txdb("r");
return AcceptToMemoryPool(txdb);
return AcceptToMemoryPool(txdb);
}
}
bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs)
bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs)
{
{
{
{
LOCK(mempool.cs);
LOCK(mempool.cs);
// Add previous supporting transactions first
// Add previous supporting transactions first
BOOST_FOREACH(CMerkleTx& tx, vtxPrev)
BOOST_
{
if (!(tx.IsCoinBase() || tx.IsCoinStake()))
{
uint256 hash = tx.GetHash();