// 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 "db.h"
# include "util.h"
# include "main.h"
# include <boost/version.hpp>
# include <boost/filesystem.hpp>
# include <boost/filesystem/fstream.hpp>
# ifndef WIN32
# include "sys/stat.h"
# endif
using namespace std ;
using namespace boost ;
unsigned int nWalletDBUpdated ;
//
// CDB
//
CDBEnv bitdb ;
void CDBEnv : : EnvShutdown ( )
{
if ( ! fDbEnvInit )
return ;
fDbEnvInit = false ;
try
{
dbenv . close ( 0 ) ;
}
catch ( const DbException & e )
{
printf ( " EnvShutdown exception: %s (%d) \n " , e . what ( ) , e . get_errno ( ) ) ;
}
DbEnv ( 0 ) . remove ( GetDataDir ( ) . string ( ) . c_str ( ) , 0 ) ;
}
CDBEnv : : CDBEnv ( ) : dbenv ( 0 )
{
}
CDBEnv : : ~ CDBEnv ( )
{
EnvShutdown ( ) ;
}
void CDBEnv : : Close ( )
{
EnvShutdown ( ) ;
}
bool CDBEnv : : Open ( boost : : filesystem : : path pathEnv_ )
{
if ( fDbEnvInit )
return true ;
if ( fShutdown )
return false ;
pathEnv = pathEnv_ ;
filesystem : : path pathDataDir = pathEnv ;
filesystem : : path pathLogDir = pathDataDir / " database " ;
filesystem : : create_directory ( pathLogDir ) ;
filesystem : : path pathErrorFile = pathDataDir / " db.log " ;
printf ( " dbenv.open LogDir=%s ErrorFile=%s \n " , pathLogDir . string ( ) . c_str ( ) , pathErrorFile . string ( ) . c_str ( ) ) ;
unsigned int nEnvFlags = 0 ;
if ( GetBoolArg ( " -privdb " , true ) )
nEnvFlags | = DB_PRIVATE ;
int nDbCache = GetArg ( " -dbcache " , 25 ) ;
dbenv . set_lg_dir ( pathLogDir . string ( ) . c_str ( ) ) ;
dbenv . set_cachesize ( nDbCache / 1024 , ( nDbCache % 1024 ) * 1048576 , 1 ) ;
dbenv . set_lg_bsize ( 1048576 ) ;
dbenv . set_lg_max ( 10485760 ) ;
dbenv . set_lk_max_locks ( 10000 ) ;
dbenv . set_lk_max_objects ( 10000 ) ;
dbenv . set_errfile ( fopen ( pathErrorFile . string ( ) . c_str ( ) , " a " ) ) ; /// debug
dbenv . set_flags ( DB_AUTO_COMMIT , 1 ) ;
dbenv . set_flags ( DB_TXN_WRITE_NOSYNC , 1 ) ;
dbenv . log_set_config ( DB_LOG_AUTO_REMOVE , 1 ) ;
int ret = dbenv . open ( pathDataDir . string ( ) . c_str ( ) ,
DB_CREATE |
DB_INIT_LOCK |
DB_INIT_LOG |
DB_INIT_MPOOL |
DB_INIT_TXN |
DB_THREAD |
DB_RECOVER |
nEnvFlags ,
S_IRUSR | S_IWUSR ) ;
if ( ret > 0 )
return error ( " CDB() : error % d opening database environment " , ret) ;
fDbEnvInit = true ;
return true ;
}
void CDBEnv : : CheckpointLSN ( std : : string strFile )
{
dbenv . txn_checkpoint ( 0 , 0 , 0 ) ;
dbenv . lsn_reset ( strFile . c_str ( ) , 0 ) ;
}
CDB : : CDB ( const char * pszFile , const char * pszMode ) :
pdb ( NULL ) , activeTxn ( NULL )
{
int ret ;
if ( pszFile = = NULL )
return ;
fReadOnly = ( ! strchr ( pszMode , ' + ' ) & & ! strchr ( pszMode , ' w ' ) ) ;
bool fCreate = strchr ( pszMode , ' c ' ) ;
unsigned int nFlags = DB_THREAD ;
if ( fCreate )
nFlags | = DB_CREATE ;
{
LOCK ( bitdb . cs_db ) ;
if ( ! bitdb . Open ( GetDataDir ( ) ) )
throw runtime_error ( " env open failed " ) ;
strFile = pszFile ;
+ + bitdb . mapFileUseCount [ strFile ] ;
pdb = bitdb . mapDb [ strFile ] ;
if ( pdb = = NULL )
{
pdb = new Db ( & bitdb . dbenv , 0 ) ;
ret = pdb - > open ( NULL , // Txn pointer
pszFile , // Filename
" main " , // Logical db name
DB_BTREE , // Database type
nFlags , // Flags
0 ) ;
if ( ret > 0 )
{
delete pdb ;
pdb = NULL ;
{
LOCK ( bitdb . cs_db ) ;
- - bitdb . mapFileUseCount [ strFile ] ;
}
strFile = " " ;
throw runtime_error ( strprintf ( " CDB() : can ' t open database file % s , error % d " , pszFile, ret)) ;
}
if ( fCreate & & ! Exists ( string ( " version " ) ) )
{
bool fTmp = fReadOnly ;
fReadOnly = false ;
WriteVersion ( CLIENT_VERSION ) ;
fReadOnly = fTmp ;
}
bitdb . mapDb [ strFile ] = pdb ;
}
}
}
static bool IsChainFile ( std : : string strFile )
{
if ( strFile = = " blkindex.dat " )
return true ;
return false ;
}
void CDB : : Close ( )
{
if ( ! pdb )
return ;
if ( activeTxn )
activeTxn - > abort ( ) ;
activeTxn = NULL ;
pdb = NULL ;
// Flush database activity from memory pool to disk log
unsigned int nMinutes = 0 ;
if ( fReadOnly )
nMinutes = 1 ;
if ( IsChainFile ( strFile ) )
nMinutes = 2 ;
if ( IsChainFile ( strFile ) & & IsInitialBlockDownload ( ) )
nMinutes = 5 ;
bitdb . dbenv . txn_checkpoint ( nMinutes ? GetArg ( " -dblogsize " , 100 ) * 1024 : 0 , nMinutes , 0 ) ;
{
LOCK ( bitdb . cs_db ) ;
- - bitdb . mapFileUseCount [ strFile ] ;
}
}
void CDBEnv : : CloseDb ( const string & strFile )
{
{
LOCK ( cs_db ) ;
if ( mapDb [ strFile ] ! = NULL )
{
// Close the database handle
Db * pdb = mapDb [ strFile ] ;
pdb - > close ( 0 ) ;
delete pdb ;
mapDb [ strFile ] = NULL ;
}
}
}
bool CDB : : Rewrite ( const string & strFile , const char * pszSkip )
{
while ( ! fShutdown )
{
{
LOCK ( bitdb . cs_db ) ;
if ( ! bitdb . mapFileUseCount . count ( strFile ) | | bitdb . mapFileUseCount [ strFile ] = = 0 )
{
// Flush log data to the dat file
bitdb . CloseDb ( strFile ) ;
bitdb . CheckpointLSN ( strFile ) ;
bitdb . mapFileUseCount . erase ( strFile ) ;
bool fSuccess = true ;
printf ( " Rewriting %s... \n " , strFile . c_str ( ) ) ;
string strFileRes = strFile + " .rewrite " ;
{ // surround usage of db with extra {}
CDB db ( strFile . c_str ( ) , " r " ) ;
Db * pdbCopy = new Db ( & bitdb . dbenv , 0 ) ;
int ret = pdbCopy - > open ( NULL , // Txn pointer
strFileRes . c_str ( ) , // Filename
" main " , // Logical db name
DB_BTREE , // Database type
DB_CREATE , // Flags
0 ) ;
if ( ret > 0 )
{
printf ( " Cannot create database file %s \n " , strFileRes . c_str ( ) ) ;
fSuccess = false ;
}
Dbc * pcursor = db . GetCursor ( ) ;
if ( pcursor )
while ( fSuccess )
{
CDataStream ssKey ( SER_DISK , CLIENT_VERSION ) ;
CDataStream ssValue ( SER_DISK , CLIENT_VERSION ) ;
int ret = db . ReadAtCursor ( pcursor , ssKey , ssValue , DB_NEXT ) ;
if ( ret = = DB_NOTFOUND )
{
pcursor - > close ( ) ;
break ;
}
else if ( ret ! = 0 )
{
pcursor - > close ( ) ;
fSuccess = false ;
break ;
}
if ( pszSkip & &
strncmp ( & ssKey [ 0 ] , pszSkip , std : : min ( ssKey . size ( ) , strlen ( pszSkip ) ) ) = = 0 )
continue ;
if ( strncmp ( & ssKey [ 0 ] , " \x07 version " , 8 ) = = 0 )
{
// Update version:
ssValue . clear ( ) ;
ssValue < < CLIENT_VERSION ;
}
Dbt datKey ( & ssKey [ 0 ] , ssKey . size ( ) ) ;
Dbt datValue ( & ssValue [ 0 ] , ssValue . size ( ) ) ;
int ret2 = pdbCopy - > put ( NULL , & datKey , & datValue , DB_NOOVERWRITE ) ;
if ( ret2 > 0 )
fSuccess = false ;
}
if ( fSuccess )
{
db . Close ( ) ;
bitdb . CloseDb ( strFile ) ;
if ( pdbCopy - > close ( 0 ) )
fSuccess = false ;
delete pdbCopy ;
}
}
if ( fSuccess )
{
Db dbA ( & bitdb . dbenv , 0 ) ;
if ( dbA . remove ( strFile . c_str ( ) , NULL , 0 ) )
fSuccess = false ;
Db dbB ( & bitdb . dbenv , 0 ) ;
if ( dbB . rename ( strFileRes . c_str ( ) , NULL , strFile . c_str ( ) , 0 ) )
fSuccess = false ;
}
if ( ! fSuccess )
printf ( " Rewriting of %s FAILED! \n " , strFileRes . c_str ( ) ) ;
return fSuccess ;
}
}
Sleep ( 100 ) ;
}
return false ;
}
void CDBEnv : : Flush ( bool fShutdown )
{
int64 nStart = GetTimeMillis ( ) ;
// Flush log data to the actual data file
// on all files that are not in use
printf ( " Flush(%s)%s \n " , fShutdown ? " true " : " false " , fDbEnvInit ? " " : " db not started " ) ;
if ( ! fDbEnvInit )
return ;
{
LOCK ( cs_db ) ;
map < string , int > : : iterator mi = mapFileUseCount . begin ( ) ;
while ( mi ! = mapFileUseCount . end ( ) )
{
string strFile = ( * mi ) . first ;
int nRefCount = ( * mi ) . second ;
printf ( " %s refcount=%d \n " , strFile . c_str ( ) , nRefCount ) ;
if ( nRefCount = = 0 )
{
// Move log data to the dat file
CloseDb ( strFile ) ;
printf ( " %s checkpoint \n " , strFile . c_str ( ) ) ;
dbenv . txn_checkpoint ( 0 , 0 , 0 ) ;
if ( ! IsChainFile ( strFile ) | | fDetachDB ) {
printf ( " %s detach \n " , strFile . c_str ( ) ) ;
dbenv . lsn_reset ( strFile . c_str ( ) , 0 ) ;
}
printf ( " %s closed \n " , strFile . c_str ( ) ) ;
mapFileUseCount . erase ( mi + + ) ;
}
else
mi + + ;
}
printf ( " DBFlush(%s)%s ended %15 " PRI64d " ms \n " , fShutdown ? " true " : " false " , fDbEnvInit ? " " : " db not started " , GetTimeMillis ( ) - nStart ) ;
if ( fShutdown )
{
char * * listp ;
if ( mapFileUseCount . empty ( ) )
{
dbenv . log_archive ( & listp , DB_ARCH_REMOVE ) ;
Close ( ) ;
}
}
}
}
//
// CTxDB
//
bool CTxDB : : ReadTxIndex ( uint256 hash , CTxIndex & txindex )
{
assert ( ! fClient ) ;
txindex . SetNull ( ) ;
return Read ( make_pair ( string ( " tx " ) , hash ) , txindex ) ;
}
bool CTxDB : : UpdateTxIndex ( uint256 hash , const CTxIndex & txindex )
{
assert ( ! fClient ) ;
return Write ( make_pair ( string ( " tx " ) , hash ) , txindex ) ;
}
bool CTxDB : : AddTxIndex ( const CTransaction & tx , const CDiskTxPos & pos , int nHeight )
{
assert ( ! fClient ) ;
// Add to tx index
uint256 hash = tx . GetHash ( ) ;
CTxIndex txindex ( pos , tx . vout . size ( ) ) ;
return Write ( make_pair ( string ( " tx " ) , hash ) , txindex ) ;
}
bool CTxDB : : EraseTxIndex ( const CTransaction & tx )
{
assert ( ! fClient ) ;
uint256 hash = tx . GetHash ( ) ;
return Erase ( make_pair ( string ( " tx " ) , hash ) ) ;
}
bool CTxDB : : ContainsTx ( uint256 hash )
{
assert ( ! fClient ) ;
return Exists ( make_pair ( string ( " tx " ) , hash ) ) ;
}
bool CTxDB : : ReadOwnerTxes ( uint160 hash160 , int nMinHeight , vector < CTransaction > & vtx )
{
assert ( ! fClient ) ;
vtx . clear ( ) ;
// Get cursor
Dbc * pcursor = GetCursor ( ) ;
if ( ! pcursor )
return false ;
unsigned int fFlags = DB_SET_RANGE ;
loop
{
// Read next record
CDataStream ssKey ( SER_DISK , CLIENT_VERSION ) ;
if ( fFlags = = DB_SET_RANGE )
ssKey < < string ( " owner " ) < < hash160 < < CDiskTxPos ( 0 , 0 , 0 ) ;
CDataStream ssValue ( SER_DISK , CLIENT_VERSION ) ;
int ret = ReadAtCursor ( pcursor , ssKey , ssValue , fFlags ) ;
fFlags = DB_NEXT ;
if ( ret = = DB_NOTFOUND )
break ;
else if ( ret ! = 0 )
{
pcursor - > close ( ) ;
return false ;
}
// Unserialize
string strType ;
uint160 hashItem ;
CDiskTxPos pos ;
int nItemHeight ;
try {
ssKey > > strType > > hashItem > > pos ;
ssValue > > nItemHeight ;
}
catch ( std : : exception & e ) {
return error ( " %s() : deserialize error " , __PRETTY_FUNCTION__) ;
}
// Read transaction
if ( strType ! = " owner " | | hashItem ! = hash160 )
break ;
if ( nItemHeight > = nMinHeight )
{
vtx . resize ( vtx . size ( ) + 1 ) ;
if ( ! vtx . back ( ) . ReadFromDisk ( pos ) )
{
pcursor - > close ( ) ;
return false ;
}
}
}
pcursor - > close ( ) ;
return true ;
}
bool CTxDB : : ReadDiskTx ( uint256 hash , CTransaction & tx , CTxIndex & txindex )
{
assert ( ! fClient ) ;
tx . SetNull ( ) ;
if ( ! ReadTxIndex ( hash , txindex ) )
return false ;
return ( tx . ReadFromDisk ( txindex . pos ) ) ;
}
bool CTxDB : : ReadDiskTx ( uint256 hash , CTransaction & tx )
{
CTxIndex txindex ;
return ReadDiskTx ( hash , tx , txindex ) ;
}
bool CTxDB : : ReadDiskTx ( COutPoint outpoint , CTransaction & tx , CTxIndex & txindex )
{
return ReadDiskTx ( outpoint . hash , tx , txindex ) ;
}
bool CTxDB : : ReadDiskTx ( COutPoint outpoint , CTransaction & tx )
{
CTxIndex txindex ;
return ReadDiskTx ( outpoint . hash , tx , txindex ) ;
}
bool CTxDB : : WriteBlockIndex ( const CDiskBlockIndex & blockindex )
{
return Write ( make_pair ( string ( " blockindex " ) , blockindex . GetBlockHash ( ) ) , blockindex ) ;
}
bool CTxDB : : ReadHashBestChain ( uint256 & hashBestChain )
{
return Read ( string ( " hashBestChain " ) , hashBestChain ) ;
}
bool CTxDB : : WriteHashBestChain ( uint256 hashBestChain )
{
return Write ( string ( " hashBestChain " ) , hashBestChain ) ;
}
bool CTxDB : : ReadBestInvalidWork ( CBigNum & bnBestInvalidWork )
{
return Read ( string ( " bnBestInvalidWork " ) , bnBestInvalidWork ) ;
}
bool CTxDB : : WriteBestInvalidWork ( CBigNum bnBestInvalidWork )
{
return Write ( string ( " bnBestInvalidWork " ) , bnBestInvalidWork ) ;
}
CWallet class
* A new class CKeyStore manages private keys, and script.cpp depends on access to CKeyStore.
* A new class CWallet extends CKeyStore, and contains all former wallet-specific globals; CWallet depends on script.cpp, not the other way around.
* Wallet-specific functions in CTransaction/CTxIn/CTxOut (GetDebit, GetCredit, GetChange, IsMine, IsFromMe), are moved to CWallet, taking their former 'this' argument as an explicit parameter
* CWalletTx objects know which CWallet they belong to, for convenience, so they have their own direct (and caching) GetDebit/... functions.
* Some code was moved from CWalletDB to CWallet, such as handling of reserve keys.
* Main.cpp keeps a set of all 'registered' wallets, which should be informed about updates to the block chain, and does not have any notion about any 'main' wallet. Function in main.cpp that require a wallet (such as GenerateCoins), take an explicit CWallet* argument.
* The actual CWallet instance used by the application is defined in init.cpp as "CWallet* pwalletMain". rpc.cpp and ui.cpp use this variable.
* Functions in main.cpp and db.cpp that are not used by other modules are marked static.
* The code for handling the 'submitorder' message is removed, as it not really compatible with the idea that a node is independent from the wallet(s) connected to it, and obsolete anyway.
14 years ago
CBlockIndex static * InsertBlockIndex ( uint256 hash )
{
if ( hash = = 0 )
return NULL ;
// Return existing
map < uint256 , CBlockIndex * > : : iterator mi = mapBlockIndex . find ( hash ) ;
if ( mi ! = mapBlockIndex . end ( ) )
return ( * mi ) . second ;
// Create new
CBlockIndex * pindexNew = new CBlockIndex ( ) ;
if ( ! pindexNew )
throw runtime_error ( " LoadBlockIndex() : new CBlockIndex failed " ) ;
mi = mapBlockIndex . insert ( make_pair ( hash , pindexNew ) ) . first ;
pindexNew - > phashBlock = & ( ( * mi ) . first ) ;
return pindexNew ;
}
bool CTxDB : : LoadBlockIndex ( )
{
if ( ! LoadBlockIndexGuts ( ) )
return false ;
if ( fRequestShutdown )
return true ;
// Calculate bnChainWork
vector < pair < int , CBlockIndex * > > vSortedByHeight ;
vSortedByHeight . reserve ( mapBlockIndex . size ( ) ) ;
BOOST_FOREACH ( const PAIRTYPE ( uint256 , CBlockIndex * ) & item , mapBlockIndex )
{
CBlockIndex * pindex = item . second ;
vSortedByHeight . push_back ( make_pair ( pindex - > nHeight , pindex ) ) ;
}
sort ( vSortedByHeight . begin ( ) , vSortedByHeight . end ( ) ) ;
BOOST_FOREACH ( const PAIRTYPE ( int , CBlockIndex * ) & item , vSortedByHeight )
{
CBlockIndex * pindex = item . second ;
pindex - > bnChainWork = ( pindex - > pprev ? pindex - > pprev - > bnChainWork : 0 ) + pindex - > GetBlockWork ( ) ;
}
// Load hashBestChain pointer to end of best chain
if ( ! ReadHashBestChain ( hashBestChain ) )
{
if ( pindexGenesisBlock = = NULL )
return true ;
return error ( " CTxDB::LoadBlockIndex() : hashBestChain not loaded " ) ;
}
if ( ! mapBlockIndex . count ( hashBestChain ) )
return error ( " CTxDB::LoadBlockIndex() : hashBestChain not found in the block index " ) ;
pindexBest = mapBlockIndex [ hashBestChain ] ;
nBestHeight = pindexBest - > nHeight ;
bnBestChainWork = pindexBest - > bnChainWork ;
printf ( " LoadBlockIndex(): hashBestChain=%s height=%d \n " , hashBestChain . ToString ( ) . substr ( 0 , 20 ) . c_str ( ) , nBestHeight ) ;
// Load bnBestInvalidWork, OK if it doesn't exist
ReadBestInvalidWork ( bnBestInvalidWork ) ;
// Verify blocks in the best chain
int nCheckLevel = GetArg ( " -checklevel " , 1 ) ;
int nCheckDepth = GetArg ( " -checkblocks " , 2500 ) ;
if ( nCheckDepth = = 0 )
nCheckDepth = 1000000000 ; // suffices until the year 19000
if ( nCheckDepth > nBestHeight )
nCheckDepth = nBestHeight ;
printf ( " Verifying last %i blocks at level %i \n " , nCheckDepth , nCheckLevel ) ;
CBlockIndex * pindexFork = NULL ;
map < pair < unsigned int , unsigned int > , CBlockIndex * > mapBlockPos ;
for ( CBlockIndex * pindex = pindexBest ; pindex & & pindex - > pprev ; pindex = pindex - > pprev )
{
if ( fRequestShutdown | | pindex - > nHeight < nBestHeight - nCheckDepth )
break ;
CBlock block ;
if ( ! block . ReadFromDisk ( pindex ) )
return error ( " LoadBlockIndex() : block . ReadFromDisk failed " ) ;
// check level 1: verify block validity
if ( nCheckLevel > 0 & & ! block . CheckBlock ( ) )
{
printf ( " LoadBlockIndex() : *** found bad block at %d, hash=%s \n " , pindex - > nHeight , pindex - > GetBlockHash ( ) . ToString ( ) . c_str ( ) ) ;
pindexFork = pindex - > pprev ;
}
// check level 2: verify transaction index validity
if ( nCheckLevel > 1 )
{
pair < unsigned int , unsigned int > pos = make_pair ( pindex - > nFile , pindex - > nBlockPos ) ;
mapBlockPos [ pos ] = pindex ;
BOOST_FOREACH ( const CTransaction & tx , block . vtx )
{
uint256 hashTx = tx . GetHash ( ) ;
CTxIndex txindex ;
if ( ReadTxIndex ( hashTx , txindex ) )
{
// check level 3: checker transaction hashes
if ( nCheckLevel > 2 | | pindex - > nFile ! = txindex . pos . nFile | | pindex - > nBlockPos ! = txindex . pos . nBlockPos )
{
// either an error or a duplicate transaction
CTransaction txFound ;
if ( ! txFound . ReadFromDisk ( txindex . pos ) )
{
printf ( " LoadBlockIndex() : *** cannot read mislocated transaction %s \n " , hashTx . ToString ( ) . c_str ( ) ) ;
pindexFork = pindex - > pprev ;
}
else
if ( txFound . GetHash ( ) ! = hashTx ) // not a duplicate tx
{
printf ( " LoadBlockIndex(): *** invalid tx position for %s \n " , hashTx . ToString ( ) . c_str ( ) ) ;
pindexFork = pindex - > pprev ;
}
}
// check level 4: check whether spent txouts were spent within the main chain
unsigned int nOutput = 0 ;
if ( nCheckLevel > 3 )
{
BOOST_FOREACH ( const CDiskTxPos & txpos , txindex . vSpent )
{
if ( ! txpos . IsNull ( ) )
{
pair < unsigned int , unsigned int > posFind = make_pair ( txpos . nFile , txpos . nBlockPos ) ;
if ( ! mapBlockPos . count ( posFind ) )
{
printf ( " LoadBlockIndex(): *** found bad spend at %d, hashBlock=%s, hashTx=%s \n " , pindex - > nHeight , pindex - > GetBlockHash ( ) . ToString ( ) . c_str ( ) , hashTx . ToString ( ) . c_str ( ) ) ;
pindexFork = pindex - > pprev ;
}
// check level 6: check whether spent txouts were spent by a valid transaction that consume them
if ( nCheckLevel > 5 )
{
CTransaction txSpend ;
if ( ! txSpend . ReadFromDisk ( txpos ) )
{
printf ( " LoadBlockIndex(): *** cannot read spending transaction of %s:%i from disk \n " , hashTx . ToString ( ) . c_str ( ) , nOutput ) ;
pindexFork = pindex - > pprev ;
}
else if ( ! txSpend . CheckTransaction ( ) )
{
printf ( " LoadBlockIndex(): *** spending transaction of %s:%i is invalid \n " , hashTx . ToString ( ) . c_str ( ) , nOutput ) ;
pindexFork = pindex - > pprev ;
}
else
{
bool fFound = false ;
BOOST_FOREACH ( const CTxIn & txin , txSpend . vin )
if ( txin . prevout . hash = = hashTx & & txin . prevout . n = = nOutput )
fFound = true ;
if ( ! fFound )
{
printf ( " LoadBlockIndex(): *** spending transaction of %s:%i does not spend it \n " , hashTx . ToString ( ) . c_str ( ) , nOutput ) ;
pindexFork = pindex - > pprev ;
}
}
}
}
nOutput + + ;
}
}
}
// check level 5: check whether all prevouts are marked spent
if ( nCheckLevel > 4 )
{
BOOST_FOREACH ( const CTxIn & txin , tx . vin )
{
CTxIndex txindex ;
if ( ReadTxIndex ( txin . prevout . hash , txindex ) )
if ( txindex . vSpent . size ( ) - 1 < txin . prevout . n | | txindex . vSpent [ txin . prevout . n ] . IsNull ( ) )
{
printf ( " LoadBlockIndex(): *** found unspent prevout %s:%i in %s \n " , txin . prevout . hash . ToString ( ) . c_str ( ) , txin . prevout . n , hashTx . ToString ( ) . c_str ( ) ) ;
pindexFork = pindex - > pprev ;
}
}
}
}
}
}
if ( pindexFork & & ! fRequestShutdown )
{
// Reorg back to the fork
printf ( " LoadBlockIndex() : *** moving best chain pointer back to block %d \n " , pindexFork - > nHeight ) ;
CBlock block ;
if ( ! block . ReadFromDisk ( pindexFork ) )
return error ( " LoadBlockIndex() : block . ReadFromDisk failed " ) ;
CTxDB txdb ;
block . SetBestChain ( txdb , pindexFork ) ;
}
return true ;
}
bool CTxDB : : LoadBlockIndexGuts ( )
{
// Get database cursor
Dbc * pcursor = GetCursor ( ) ;
if ( ! pcursor )
return false ;
// Load mapBlockIndex
unsigned int fFlags = DB_SET_RANGE ;
loop
{
// Read next record
CDataStream ssKey ( SER_DISK , CLIENT_VERSION ) ;
if ( fFlags = = DB_SET_RANGE )
ssKey < < make_pair ( string ( " blockindex " ) , uint256 ( 0 ) ) ;
CDataStream ssValue ( SER_DISK , CLIENT_VERSION ) ;
int ret = ReadAtCursor ( pcursor , ssKey , ssValue , fFlags ) ;
fFlags = DB_NEXT ;
if ( ret = = DB_NOTFOUND )
break ;
else if ( ret ! = 0 )
return false ;
// Unserialize
try {
string strType ;
ssKey > > strType ;
if ( strType = = " blockindex " & & ! fRequestShutdown )
{
CDiskBlockIndex diskindex ;
ssValue > > diskindex ;
// Construct block index object
CBlockIndex * pindexNew = InsertBlockIndex ( diskindex . GetBlockHash ( ) ) ;
pindexNew - > pprev = InsertBlockIndex ( diskindex . hashPrev ) ;
pindexNew - > pnext = InsertBlockIndex ( diskindex . hashNext ) ;
pindexNew - > nFile = diskindex . nFile ;
pindexNew - > nBlockPos = diskindex . nBlockPos ;
pindexNew - > nHeight = diskindex . nHeight ;
pindexNew - > nVersion = diskindex . nVersion ;
pindexNew - > hashMerkleRoot = diskindex . hashMerkleRoot ;
pindexNew - > nTime = diskindex . nTime ;
pindexNew - > nBits = diskindex . nBits ;
pindexNew - > nNonce = diskindex . nNonce ;
// Watch for genesis block
if ( pindexGenesisBlock = = NULL & & diskindex . GetBlockHash ( ) = = hashGenesisBlock )
pindexGenesisBlock = pindexNew ;
if ( ! pindexNew - > CheckIndex ( ) )
return error ( " LoadBlockIndex() : CheckIndex failed at % d " , pindexNew->nHeight) ;
}
else
{
break ; // if shutdown requested or finished loading block index
}
} // try
catch ( std : : exception & e ) {
return error ( " %s() : deserialize error " , __PRETTY_FUNCTION__) ;
}
}
pcursor - > close ( ) ;
return true ;
}
//
// CAddrDB
//
CAddrDB : : CAddrDB ( )
{
pathAddr = GetDataDir ( ) / " peers.dat " ;
}
bool CAddrDB : : Write ( const CAddrMan & addr )
{
// Generate random temporary filename
unsigned short randv = 0 ;
RAND_bytes ( ( unsigned char * ) & randv , sizeof ( randv ) ) ;
std : : string tmpfn = strprintf ( " peers.dat.%04x " , randv ) ;
// serialize addresses, checksum data up to that point, then append csum
CDataStream ssPeers ( SER_DISK , CLIENT_VERSION ) ;
ssPeers < < FLATDATA ( pchMessageStart ) ;
ssPeers < < addr ;
uint256 hash = Hash ( ssPeers . begin ( ) , ssPeers . end ( ) ) ;
ssPeers < < hash ;
// open temp output file, and associate with CAutoFile
boost : : filesystem : : path pathTmp = GetDataDir ( ) / tmpfn ;
FILE * file = fopen ( pathTmp . string ( ) . c_str ( ) , " wb " ) ;
CAutoFile fileout = CAutoFile ( file , SER_DISK , CLIENT_VERSION ) ;
if ( ! fileout )
return error ( " CAddrman::Write() : open failed " ) ;
// Write and commit header, data
try {
fileout < < ssPeers ;
}
catch ( std : : exception & e ) {
return error ( " CAddrman::Write() : I / O error " ) ;
}
FileCommit ( fileout ) ;
fileout . fclose ( ) ;
// replace existing peers.dat, if any, with new peers.dat.XXXX
if ( ! RenameOver ( pathTmp , pathAddr ) )
return error ( " CAddrman::Write() : Rename - into - place failed " ) ;
return true ;
}
bool CAddrDB : : Read ( CAddrMan & addr )
{
// open input file, and associate with CAutoFile
FILE * file = fopen ( pathAddr . string ( ) . c_str ( ) , " rb " ) ;
CAutoFile filein = CAutoFile ( file , SER_DISK , CLIENT_VERSION ) ;
if ( ! filein )
return error ( " CAddrman::Read() : open failed " ) ;
// use file size to size memory buffer
int fileSize = GetFilesize ( filein ) ;
int dataSize = fileSize - sizeof ( uint256 ) ;
vector < unsigned char > vchData ;
vchData . resize ( dataSize ) ;
uint256 hashIn ;
// read data and checksum from file
try {
filein . read ( ( char * ) & vchData [ 0 ] , dataSize ) ;
filein > > hashIn ;
}
catch ( std : : exception & e ) {
return error ( " CAddrman::Read() 2 : I / O error or stream data corrupted " ) ;
}
filein . fclose ( ) ;
CDataStream ssPeers ( vchData , SER_DISK , CLIENT_VERSION ) ;
// verify stored checksum matches input data
uint256 hashTmp = Hash ( ssPeers . begin ( ) , ssPeers . end ( ) ) ;
if ( hashIn ! = hashTmp )
return error ( " CAddrman::Read() : checksum mismatch ; data corrupted " );
// de-serialize address data
unsigned char pchMsgTmp [ 4 ] ;
try {
ssPeers > > FLATDATA ( pchMsgTmp ) ;
ssPeers > > addr ;
}
catch ( std : : exception & e ) {
return error ( " CAddrman::Read() : I / O error or stream data corrupted " ) ;
}
// finally, verify the network matches ours
if ( memcmp ( pchMsgTmp , pchMessageStart , sizeof ( pchMsgTmp ) ) )
return error ( " CAddrman::Read() : invalid network magic number " ) ;
return true ;
}