mirror of
https://github.com/dashpay/dash.git
synced 2024-12-28 13:32:47 +01:00
630fd8dcb6
Refactor of the block storage code, which now stores one file per block. This will allow easier pruning, as blocks can be removed individually.
1782 lines
51 KiB
C++
1782 lines
51 KiB
C++
// 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.
|
|
#ifndef BITCOIN_MAIN_H
|
|
#define BITCOIN_MAIN_H
|
|
|
|
#include "bignum.h"
|
|
#include "sync.h"
|
|
#include "net.h"
|
|
#include "script.h"
|
|
|
|
#include <list>
|
|
|
|
class CWallet;
|
|
class CBlock;
|
|
class CBlockIndex;
|
|
class CKeyItem;
|
|
class CReserveKey;
|
|
|
|
class CAddress;
|
|
class CInv;
|
|
class CRequestTracker;
|
|
class CNode;
|
|
|
|
static const unsigned int MAX_BLOCK_SIZE = 1000000;
|
|
static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
|
|
static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
|
|
static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
|
|
static const unsigned int MAX_INV_SZ = 50000;
|
|
static const int64 MIN_TX_FEE = 50000;
|
|
static const int64 MIN_RELAY_TX_FEE = 10000;
|
|
static const int64 MAX_MONEY = 21000000 * COIN;
|
|
inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
|
|
static const int COINBASE_MATURITY = 100;
|
|
// Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp.
|
|
static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC
|
|
#ifdef USE_UPNP
|
|
static const int fHaveUPnP = true;
|
|
#else
|
|
static const int fHaveUPnP = false;
|
|
#endif
|
|
|
|
|
|
extern CScript COINBASE_FLAGS;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern CCriticalSection cs_main;
|
|
extern std::map<uint256, CBlockIndex*> mapBlockIndex;
|
|
extern uint256 hashGenesisBlock;
|
|
extern CBlockIndex* pindexGenesisBlock;
|
|
extern int nBestHeight;
|
|
extern CBigNum bnBestChainWork;
|
|
extern CBigNum bnBestInvalidWork;
|
|
extern uint256 hashBestChain;
|
|
extern CBlockIndex* pindexBest;
|
|
extern unsigned int nTransactionsUpdated;
|
|
extern uint64 nLastBlockTx;
|
|
extern uint64 nLastBlockSize;
|
|
extern const std::string strMessageMagic;
|
|
extern double dHashesPerSec;
|
|
extern int64 nHPSTimerStart;
|
|
extern int64 nTimeBestReceived;
|
|
extern CCriticalSection cs_setpwalletRegistered;
|
|
extern std::set<CWallet*> setpwalletRegistered;
|
|
extern unsigned char pchMessageStart[4];
|
|
extern bool fImporting;
|
|
|
|
// Settings
|
|
extern int64 nTransactionFee;
|
|
|
|
// Minimum disk space required - used in CheckDiskSpace()
|
|
static const uint64 nMinDiskSpace = 52428800;
|
|
|
|
|
|
class CReserveKey;
|
|
class CTxDB;
|
|
class CTxIndex;
|
|
class CDiskBlockPos;
|
|
|
|
void RegisterWallet(CWallet* pwalletIn);
|
|
void UnregisterWallet(CWallet* pwalletIn);
|
|
void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false);
|
|
bool ProcessBlock(CNode* pfrom, CBlock* pblock);
|
|
bool CheckDiskSpace(uint64 nAdditionalBytes=0);
|
|
FILE* OpenBlockFile(const CDiskBlockPos &pos, const char* pszMode="rb");
|
|
bool LoadBlockIndex(bool fAllowNew=true);
|
|
void PrintBlockTree();
|
|
CBlockIndex* FindBlockByHeight(int nHeight);
|
|
bool ProcessMessages(CNode* pfrom);
|
|
bool SendMessages(CNode* pto, bool fSendTrickle);
|
|
void ThreadImport(void *parg);
|
|
void GenerateBitcoins(bool fGenerate, CWallet* pwallet);
|
|
CBlock* CreateNewBlock(CReserveKey& reservekey);
|
|
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
|
|
void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1);
|
|
bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
|
|
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
|
|
unsigned int ComputeMinWork(unsigned int nBase, int64 nTime);
|
|
int GetNumBlocksOfPeers();
|
|
bool IsInitialBlockDownload();
|
|
std::string GetWarnings(std::string strFor);
|
|
bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut);
|
|
|
|
class CDiskBlockPos
|
|
{
|
|
public:
|
|
int nHeight;
|
|
int nAlternative;
|
|
|
|
CDiskBlockPos() {
|
|
SetNull();
|
|
}
|
|
|
|
CDiskBlockPos(int nHeightIn, int nAlternativeIn = 0) {
|
|
nHeight = nHeightIn;
|
|
nAlternative = nAlternativeIn;
|
|
}
|
|
|
|
std::string GetAlternative() const {
|
|
char c[9]={0,0,0,0,0,0,0,0,0};
|
|
char *cp = &c[8];
|
|
unsigned int n = nAlternative;
|
|
while (n > 0 && cp>c) {
|
|
n--;
|
|
*(--cp) = 'a' + (n % 26);
|
|
n /= 26;
|
|
}
|
|
return std::string(cp);
|
|
}
|
|
|
|
boost::filesystem::path GetDirectory(const boost::filesystem::path &base) const {
|
|
assert(nHeight != -1);
|
|
return base / strprintf("era%02u", nHeight / 210000) /
|
|
strprintf("cycle%04u", nHeight / 2016);
|
|
}
|
|
|
|
boost::filesystem::path GetFileName(const boost::filesystem::path &base) const {
|
|
return GetDirectory(base) / strprintf("%08u%s.blk", nHeight, GetAlternative().c_str());
|
|
}
|
|
|
|
// TODO: make thread-safe (lockfile, atomic file creation, ...?)
|
|
void MakeUnique(const boost::filesystem::path &base) {
|
|
while (boost::filesystem::exists(GetFileName(base)))
|
|
nAlternative++;
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE(({
|
|
CDiskBlockPos *me = const_cast<CDiskBlockPos*>(this);
|
|
if (!fRead) {
|
|
unsigned int nCode = (nHeight + 1) * 2 + (nAlternative > 0);
|
|
READWRITE(VARINT(nCode));
|
|
if (nAlternative > 0) {
|
|
unsigned int nAlt = nAlternative - 1;
|
|
READWRITE(VARINT(nAlt));
|
|
}
|
|
} else {
|
|
unsigned int nCode = 0;
|
|
READWRITE(VARINT(nCode));
|
|
me->nHeight = (nCode / 2) - 1;
|
|
if (nCode & 1) {
|
|
unsigned int nAlt = 0;
|
|
READWRITE(VARINT(nAlt));
|
|
me->nAlternative = 1 + nAlt;
|
|
} else {
|
|
me->nAlternative = 0;
|
|
}
|
|
}
|
|
});)
|
|
|
|
friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) {
|
|
return ((a.nHeight == b.nHeight) && (a.nAlternative == b.nAlternative));
|
|
}
|
|
|
|
friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) {
|
|
return !(a == b);
|
|
}
|
|
|
|
void SetNull() { nHeight = -1; nAlternative = 0; }
|
|
bool IsNull() const { return ((nHeight == -1) && (nAlternative == 0)); }
|
|
|
|
void SetMemPool() { nHeight = -1; nAlternative = -1; }
|
|
bool IsMemPool() const { return ((nHeight == -1) && (nAlternative == -1)); }
|
|
};
|
|
|
|
/** Position on disk for a particular transaction. */
|
|
class CDiskTxPos
|
|
{
|
|
public:
|
|
CDiskBlockPos blockPos;
|
|
unsigned int nTxPos;
|
|
|
|
CDiskTxPos(bool fInMemPool = false)
|
|
{
|
|
SetNull();
|
|
if (fInMemPool)
|
|
blockPos.SetMemPool();
|
|
}
|
|
|
|
CDiskTxPos(const CDiskBlockPos &block, unsigned int nTxPosIn) : blockPos(block), nTxPos(nTxPosIn) { }
|
|
|
|
IMPLEMENT_SERIALIZE(
|
|
READWRITE(blockPos);
|
|
READWRITE(VARINT(nTxPos));
|
|
)
|
|
|
|
void SetNull() { blockPos.SetNull(); nTxPos = 0; }
|
|
bool IsNull() const { return blockPos.IsNull(); }
|
|
bool IsMemPool() const { return blockPos.IsMemPool(); }
|
|
|
|
friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b)
|
|
{
|
|
return (a.blockPos == b.blockPos &&
|
|
a.nTxPos == b.nTxPos);
|
|
}
|
|
|
|
friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
std::string ToString() const
|
|
{
|
|
if (IsNull())
|
|
return "null";
|
|
else if (blockPos.IsMemPool())
|
|
return "mempool";
|
|
else
|
|
return strprintf("(%s, nTxPos=%u)", blockPos.GetFileName("").string().c_str(), nTxPos);
|
|
}
|
|
|
|
void print() const
|
|
{
|
|
printf("%s", ToString().c_str());
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/** An inpoint - a combination of a transaction and an index n into its vin */
|
|
class CInPoint
|
|
{
|
|
public:
|
|
CTransaction* ptx;
|
|
unsigned int n;
|
|
|
|
CInPoint() { SetNull(); }
|
|
CInPoint(CTransaction* ptxIn, unsigned int nIn) { ptx = ptxIn; n = nIn; }
|
|
void SetNull() { ptx = NULL; n = (unsigned int) -1; }
|
|
bool IsNull() const { return (ptx == NULL && n == (unsigned int) -1); }
|
|
};
|
|
|
|
|
|
|
|
/** An outpoint - a combination of a transaction hash and an index n into its vout */
|
|
class COutPoint
|
|
{
|
|
public:
|
|
uint256 hash;
|
|
unsigned int n;
|
|
|
|
COutPoint() { SetNull(); }
|
|
COutPoint(uint256 hashIn, unsigned int nIn) { hash = hashIn; n = nIn; }
|
|
IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); )
|
|
void SetNull() { hash = 0; n = (unsigned int) -1; }
|
|
bool IsNull() const { return (hash == 0 && n == (unsigned int) -1); }
|
|
|
|
friend bool operator<(const COutPoint& a, const COutPoint& b)
|
|
{
|
|
return (a.hash < b.hash || (a.hash == b.hash && a.n < b.n));
|
|
}
|
|
|
|
friend bool operator==(const COutPoint& a, const COutPoint& b)
|
|
{
|
|
return (a.hash == b.hash && a.n == b.n);
|
|
}
|
|
|
|
friend bool operator!=(const COutPoint& a, const COutPoint& b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
std::string ToString() const
|
|
{
|
|
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10).c_str(), n);
|
|
}
|
|
|
|
void print() const
|
|
{
|
|
printf("%s\n", ToString().c_str());
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
/** An input of a transaction. It contains the location of the previous
|
|
* transaction's output that it claims and a signature that matches the
|
|
* output's public key.
|
|
*/
|
|
class CTxIn
|
|
{
|
|
public:
|
|
COutPoint prevout;
|
|
CScript scriptSig;
|
|
unsigned int nSequence;
|
|
|
|
CTxIn()
|
|
{
|
|
nSequence = std::numeric_limits<unsigned int>::max();
|
|
}
|
|
|
|
explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits<unsigned int>::max())
|
|
{
|
|
prevout = prevoutIn;
|
|
scriptSig = scriptSigIn;
|
|
nSequence = nSequenceIn;
|
|
}
|
|
|
|
CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits<unsigned int>::max())
|
|
{
|
|
prevout = COutPoint(hashPrevTx, nOut);
|
|
scriptSig = scriptSigIn;
|
|
nSequence = nSequenceIn;
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
READWRITE(prevout);
|
|
READWRITE(scriptSig);
|
|
READWRITE(nSequence);
|
|
)
|
|
|
|
bool IsFinal() const
|
|
{
|
|
return (nSequence == std::numeric_limits<unsigned int>::max());
|
|
}
|
|
|
|
friend bool operator==(const CTxIn& a, const CTxIn& b)
|
|
{
|
|
return (a.prevout == b.prevout &&
|
|
a.scriptSig == b.scriptSig &&
|
|
a.nSequence == b.nSequence);
|
|
}
|
|
|
|
friend bool operator!=(const CTxIn& a, const CTxIn& b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
std::string ToString() const
|
|
{
|
|
std::string str;
|
|
str += "CTxIn(";
|
|
str += prevout.ToString();
|
|
if (prevout.IsNull())
|
|
str += strprintf(", coinbase %s", HexStr(scriptSig).c_str());
|
|
else
|
|
str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str());
|
|
if (nSequence != std::numeric_limits<unsigned int>::max())
|
|
str += strprintf(", nSequence=%u", nSequence);
|
|
str += ")";
|
|
return str;
|
|
}
|
|
|
|
void print() const
|
|
{
|
|
printf("%s\n", ToString().c_str());
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
/** An output of a transaction. It contains the public key that the next input
|
|
* must be able to sign with to claim it.
|
|
*/
|
|
class CTxOut
|
|
{
|
|
public:
|
|
int64 nValue;
|
|
CScript scriptPubKey;
|
|
|
|
CTxOut()
|
|
{
|
|
SetNull();
|
|
}
|
|
|
|
CTxOut(int64 nValueIn, CScript scriptPubKeyIn)
|
|
{
|
|
nValue = nValueIn;
|
|
scriptPubKey = scriptPubKeyIn;
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
READWRITE(nValue);
|
|
READWRITE(scriptPubKey);
|
|
)
|
|
|
|
void SetNull()
|
|
{
|
|
nValue = -1;
|
|
scriptPubKey.clear();
|
|
}
|
|
|
|
bool IsNull() const
|
|
{
|
|
return (nValue == -1);
|
|
}
|
|
|
|
uint256 GetHash() const
|
|
{
|
|
return SerializeHash(*this);
|
|
}
|
|
|
|
friend bool operator==(const CTxOut& a, const CTxOut& b)
|
|
{
|
|
return (a.nValue == b.nValue &&
|
|
a.scriptPubKey == b.scriptPubKey);
|
|
}
|
|
|
|
friend bool operator!=(const CTxOut& a, const CTxOut& b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
std::string ToString() const
|
|
{
|
|
if (scriptPubKey.size() < 6)
|
|
return "CTxOut(error)";
|
|
return strprintf("CTxOut(nValue=%"PRI64d".%08"PRI64d", scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30).c_str());
|
|
}
|
|
|
|
void print() const
|
|
{
|
|
printf("%s\n", ToString().c_str());
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
enum GetMinFee_mode
|
|
{
|
|
GMF_BLOCK,
|
|
GMF_RELAY,
|
|
GMF_SEND,
|
|
};
|
|
|
|
typedef std::map<uint256, std::pair<CTxIndex, CTransaction> > MapPrevTx;
|
|
|
|
/** The basic transaction that is broadcasted on the network and contained in
|
|
* blocks. A transaction can contain multiple inputs and outputs.
|
|
*/
|
|
class CTransaction
|
|
{
|
|
public:
|
|
static const int CURRENT_VERSION=1;
|
|
int nVersion;
|
|
std::vector<CTxIn> vin;
|
|
std::vector<CTxOut> vout;
|
|
unsigned int nLockTime;
|
|
|
|
// Denial-of-service detection:
|
|
mutable int nDoS;
|
|
bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
|
|
|
|
CTransaction()
|
|
{
|
|
SetNull();
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
READWRITE(this->nVersion);
|
|
nVersion = this->nVersion;
|
|
READWRITE(vin);
|
|
READWRITE(vout);
|
|
READWRITE(nLockTime);
|
|
)
|
|
|
|
void SetNull()
|
|
{
|
|
nVersion = CTransaction::CURRENT_VERSION;
|
|
vin.clear();
|
|
vout.clear();
|
|
nLockTime = 0;
|
|
nDoS = 0; // Denial-of-service prevention
|
|
}
|
|
|
|
bool IsNull() const
|
|
{
|
|
return (vin.empty() && vout.empty());
|
|
}
|
|
|
|
uint256 GetHash() const
|
|
{
|
|
return SerializeHash(*this);
|
|
}
|
|
|
|
bool IsFinal(int nBlockHeight=0, int64 nBlockTime=0) const
|
|
{
|
|
// Time based nLockTime implemented in 0.1.6
|
|
if (nLockTime == 0)
|
|
return true;
|
|
if (nBlockHeight == 0)
|
|
nBlockHeight = nBestHeight;
|
|
if (nBlockTime == 0)
|
|
nBlockTime = GetAdjustedTime();
|
|
if ((int64)nLockTime < ((int64)nLockTime < LOCKTIME_THRESHOLD ? (int64)nBlockHeight : nBlockTime))
|
|
return true;
|
|
BOOST_FOREACH(const CTxIn& txin, vin)
|
|
if (!txin.IsFinal())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool IsNewerThan(const CTransaction& old) const
|
|
{
|
|
if (vin.size() != old.vin.size())
|
|
return false;
|
|
for (unsigned int i = 0; i < vin.size(); i++)
|
|
if (vin[i].prevout != old.vin[i].prevout)
|
|
return false;
|
|
|
|
bool fNewer = false;
|
|
unsigned int nLowest = std::numeric_limits<unsigned int>::max();
|
|
for (unsigned int i = 0; i < vin.size(); i++)
|
|
{
|
|
if (vin[i].nSequence != old.vin[i].nSequence)
|
|
{
|
|
if (vin[i].nSequence <= nLowest)
|
|
{
|
|
fNewer = false;
|
|
nLowest = vin[i].nSequence;
|
|
}
|
|
if (old.vin[i].nSequence < nLowest)
|
|
{
|
|
fNewer = true;
|
|
nLowest = old.vin[i].nSequence;
|
|
}
|
|
}
|
|
}
|
|
return fNewer;
|
|
}
|
|
|
|
bool IsCoinBase() const
|
|
{
|
|
return (vin.size() == 1 && vin[0].prevout.IsNull());
|
|
}
|
|
|
|
/** Check for standard transaction types
|
|
@return True if all outputs (scriptPubKeys) use only standard transaction forms
|
|
*/
|
|
bool IsStandard() const;
|
|
|
|
/** Check for standard transaction types
|
|
@param[in] mapInputs Map of previous transactions that have outputs we're spending
|
|
@return True if all inputs (scriptSigs) use only standard transaction forms
|
|
@see CTransaction::FetchInputs
|
|
*/
|
|
bool AreInputsStandard(const MapPrevTx& mapInputs) const;
|
|
|
|
/** Count ECDSA signature operations the old-fashioned (pre-0.6) way
|
|
@return number of sigops this transaction's outputs will produce when spent
|
|
@see CTransaction::FetchInputs
|
|
*/
|
|
unsigned int GetLegacySigOpCount() const;
|
|
|
|
/** Count ECDSA signature operations in pay-to-script-hash inputs.
|
|
|
|
@param[in] mapInputs Map of previous transactions that have outputs we're spending
|
|
@return maximum number of sigops required to validate this transaction's inputs
|
|
@see CTransaction::FetchInputs
|
|
*/
|
|
unsigned int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const;
|
|
|
|
/** Amount of bitcoins spent by this transaction.
|
|
@return sum of all outputs (note: does not include fees)
|
|
*/
|
|
int64 GetValueOut() const
|
|
{
|
|
int64 nValueOut = 0;
|
|
BOOST_FOREACH(const CTxOut& txout, vout)
|
|
{
|
|
nValueOut += txout.nValue;
|
|
if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut))
|
|
throw std::runtime_error("CTransaction::GetValueOut() : value out of range");
|
|
}
|
|
return nValueOut;
|
|
}
|
|
|
|
/** Amount of bitcoins coming in to this transaction
|
|
Note that lightweight clients may not know anything besides the hash of previous transactions,
|
|
so may not be able to calculate this.
|
|
|
|
@param[in] mapInputs Map of previous transactions that have outputs we're spending
|
|
@return Sum of value of all inputs (scriptSigs)
|
|
@see CTransaction::FetchInputs
|
|
*/
|
|
int64 GetValueIn(const MapPrevTx& mapInputs) const;
|
|
|
|
static bool AllowFree(double dPriority)
|
|
{
|
|
// Large (in bytes) low-priority (new, small-coin) transactions
|
|
// need a fee.
|
|
return dPriority > COIN * 144 / 250;
|
|
}
|
|
|
|
int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const;
|
|
|
|
bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL)
|
|
{
|
|
CAutoFile filein = CAutoFile(OpenBlockFile(pos.blockPos, pfileRet ? "rb+" : "rb"), SER_DISK, CLIENT_VERSION);
|
|
if (!filein)
|
|
return error("CTransaction::ReadFromDisk() : OpenBlockFile failed");
|
|
|
|
// Read transaction
|
|
if (fseek(filein, pos.nTxPos, SEEK_SET) != 0)
|
|
return error("CTransaction::ReadFromDisk() : fseek failed");
|
|
|
|
try {
|
|
filein >> *this;
|
|
}
|
|
catch (std::exception &e) {
|
|
return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
|
|
}
|
|
|
|
// Return file pointer
|
|
if (pfileRet)
|
|
{
|
|
if (fseek(filein, pos.nTxPos, SEEK_SET) != 0)
|
|
return error("CTransaction::ReadFromDisk() : second fseek failed");
|
|
*pfileRet = filein.release();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
friend bool operator==(const CTransaction& a, const CTransaction& b)
|
|
{
|
|
return (a.nVersion == b.nVersion &&
|
|
a.vin == b.vin &&
|
|
a.vout == b.vout &&
|
|
a.nLockTime == b.nLockTime);
|
|
}
|
|
|
|
friend bool operator!=(const CTransaction& a, const CTransaction& b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
|
|
std::string ToString() const
|
|
{
|
|
std::string str;
|
|
str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%"PRIszu", vout.size=%"PRIszu", nLockTime=%u)\n",
|
|
GetHash().ToString().substr(0,10).c_str(),
|
|
nVersion,
|
|
vin.size(),
|
|
vout.size(),
|
|
nLockTime);
|
|
for (unsigned int i = 0; i < vin.size(); i++)
|
|
str += " " + vin[i].ToString() + "\n";
|
|
for (unsigned int i = 0; i < vout.size(); i++)
|
|
str += " " + vout[i].ToString() + "\n";
|
|
return str;
|
|
}
|
|
|
|
void print() const
|
|
{
|
|
printf("%s", ToString().c_str());
|
|
}
|
|
|
|
|
|
bool ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet);
|
|
bool ReadFromDisk(CTxDB& txdb, COutPoint prevout);
|
|
bool ReadFromDisk(COutPoint prevout);
|
|
bool DisconnectInputs(CTxDB& txdb);
|
|
|
|
/** Fetch from memory and/or disk. inputsRet keys are transaction hashes.
|
|
|
|
@param[in] txdb Transaction database
|
|
@param[in] mapTestPool List of pending changes to the transaction index database
|
|
@param[in] fBlock True if being called to add a new best-block to the chain
|
|
@param[in] fMiner True if being called by CreateNewBlock
|
|
@param[out] inputsRet Pointers to this transaction's inputs
|
|
@param[out] fInvalid returns true if transaction is invalid
|
|
@return Returns true if all inputs are in txdb or mapTestPool
|
|
*/
|
|
bool FetchInputs(CTxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool,
|
|
bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid);
|
|
|
|
/** Sanity check previous transactions, then, if all checks succeed,
|
|
mark them as spent by this transaction.
|
|
|
|
@param[in] inputs Previous transactions (from FetchInputs)
|
|
@param[out] mapTestPool Keeps track of inputs that need to be updated on disk
|
|
@param[in] posThisTx Position of this transaction on disk
|
|
@param[in] pindexBlock
|
|
@param[in] fBlock true if called from ConnectBlock
|
|
@param[in] fMiner true if called from CreateNewBlock
|
|
@param[in] fStrictPayToScriptHash true if fully validating p2sh transactions
|
|
@return Returns true if all checks succeed
|
|
*/
|
|
bool ConnectInputs(MapPrevTx inputs,
|
|
std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
|
|
const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true);
|
|
bool ClientConnectInputs();
|
|
bool CheckTransaction() const;
|
|
bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
|
|
|
|
protected:
|
|
const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const;
|
|
};
|
|
|
|
/** wrapper for CTxOut that provides a more compact serialization */
|
|
class CTxOutCompressor
|
|
{
|
|
private:
|
|
CTxOut &txout;
|
|
|
|
public:
|
|
static uint64 CompressAmount(uint64 nAmount);
|
|
static uint64 DecompressAmount(uint64 nAmount);
|
|
|
|
CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { }
|
|
|
|
IMPLEMENT_SERIALIZE(({
|
|
if (!fRead) {
|
|
uint64 nVal = CompressAmount(txout.nValue);
|
|
READWRITE(VARINT(nVal));
|
|
} else {
|
|
uint64 nVal = 0;
|
|
READWRITE(VARINT(nVal));
|
|
txout.nValue = DecompressAmount(nVal);
|
|
}
|
|
CScriptCompressor cscript(REF(txout.scriptPubKey));
|
|
READWRITE(cscript);
|
|
});)
|
|
};
|
|
|
|
/** Undo information for a CTxIn
|
|
*
|
|
* Contains the prevout's CTxOut being spent, and if this was the
|
|
* last output of the affected transaction, its metadata as well
|
|
* (coinbase or not, height, transaction version)
|
|
*/
|
|
class CTxInUndo
|
|
{
|
|
public:
|
|
CTxOut txout; // the txout data before being spent
|
|
bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase
|
|
unsigned int nHeight; // if the outpoint was the last unspent: its height
|
|
int nVersion; // if the outpoint was the last unspent: its version
|
|
|
|
CTxInUndo() : txout(), fCoinBase(false), nHeight(0), nVersion(0) {}
|
|
CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), nVersion(nVersionIn) { }
|
|
|
|
unsigned int GetSerializeSize(int nType, int nVersion) const {
|
|
return ::GetSerializeSize(VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion) +
|
|
(nHeight > 0 ? ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion) : 0) +
|
|
::GetSerializeSize(CTxOutCompressor(REF(txout)), nType, nVersion);
|
|
}
|
|
|
|
template<typename Stream>
|
|
void Serialize(Stream &s, int nType, int nVersion) const {
|
|
::Serialize(s, VARINT(nHeight*2+(fCoinBase ? 1 : 0)), nType, nVersion);
|
|
if (nHeight > 0)
|
|
::Serialize(s, VARINT(this->nVersion), nType, nVersion);
|
|
::Serialize(s, CTxOutCompressor(REF(txout)), nType, nVersion);
|
|
}
|
|
|
|
template<typename Stream>
|
|
void Unserialize(Stream &s, int nType, int nVersion) {
|
|
unsigned int nCode = 0;
|
|
::Unserialize(s, VARINT(nCode), nType, nVersion);
|
|
nHeight = nCode / 2;
|
|
fCoinBase = nCode & 1;
|
|
if (nHeight > 0)
|
|
::Unserialize(s, VARINT(this->nVersion), nType, nVersion);
|
|
::Unserialize(s, REF(CTxOutCompressor(REF(txout))), nType, nVersion);
|
|
}
|
|
};
|
|
|
|
/** Undo information for a CTransaction */
|
|
class CTxUndo
|
|
{
|
|
public:
|
|
std::vector<CTxInUndo> vprevout;
|
|
|
|
IMPLEMENT_SERIALIZE(
|
|
READWRITE(vprevout);
|
|
)
|
|
};
|
|
|
|
/** pruned version of CTransaction: only retains metadata and unspent transaction outputs
|
|
*
|
|
* Serialized format:
|
|
* - VARINT(nVersion)
|
|
* - VARINT(nCode)
|
|
* - unspentness bitvector, for vout[2] and further; least significant byte first
|
|
* - the non-spent CTxOuts (via CTxOutCompressor)
|
|
* - VARINT(nHeight)
|
|
*
|
|
* The nCode value consists of:
|
|
* - bit 1: IsCoinBase()
|
|
* - bit 2: vout[0] is not spent
|
|
* - bit 4: vout[1] is not spent
|
|
* - The higher bits encode N, the number of non-zero bytes in the following bitvector.
|
|
* - In case both bit 2 and bit 4 are unset, they encode N-1, as there must be at
|
|
* least one non-spent output).
|
|
*
|
|
* Example: 0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e
|
|
* <><><--------------------------------------------><---->
|
|
* | \ | /
|
|
* version code vout[1] height
|
|
*
|
|
* - version = 1
|
|
* - code = 4 (vout[1] is not spent, and 0 non-zero bytes of bitvector follow)
|
|
* - unspentness bitvector: as 0 non-zero bytes follow, it has length 0
|
|
* - vout[1]: 835800816115944e077fe7c803cfa57f29b36bf87c1d35
|
|
* * 8358: compact amount representation for 60000000000 (600 BTC)
|
|
* * 00: special txout type pay-to-pubkey-hash
|
|
* * 816115944e077fe7c803cfa57f29b36bf87c1d35: address uint160
|
|
* - height = 203998
|
|
*
|
|
*
|
|
* Example: 0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b
|
|
* <><><--><--------------------------------------------------><----------------------------------------------><---->
|
|
* / \ \ | | /
|
|
* version code unspentness vout[4] vout[16] height
|
|
*
|
|
* - version = 1
|
|
* - code = 9 (coinbase, neither vout[0] or vout[1] are unspent,
|
|
* 2 (1, +1 because both bit 2 and bit 4 are unset) non-zero bitvector bytes follow)
|
|
* - unspentness bitvector: bits 2 (0x04) and 14 (0x4000) are set, so vout[2+2] and vout[14+2] are unspent
|
|
* - vout[4]: 86ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4ee
|
|
* * 86ef97d579: compact amount representation for 234925952 (2.35 BTC)
|
|
* * 00: special txout type pay-to-pubkey-hash
|
|
* * 61b01caab50f1b8e9c50a5057eb43c2d9563a4ee: address uint160
|
|
* - vout[16]: bbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa4
|
|
* * bbd123: compact amount representation for 110397 (0.001 BTC)
|
|
* * 00: special txout type pay-to-pubkey-hash
|
|
* * 8c988f1a4a4de2161e0f50aac7f17e7f9555caa4: address uint160
|
|
* - height = 120891
|
|
*/
|
|
class CCoins
|
|
{
|
|
public:
|
|
// whether transaction is a coinbase
|
|
bool fCoinBase;
|
|
|
|
// unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped
|
|
std::vector<CTxOut> vout;
|
|
|
|
// at which height this transaction was included in the active blockchain
|
|
int nHeight;
|
|
|
|
// version of the CTransaction; accesses to this value should probably check for nHeight as well,
|
|
// as new tx version will probably only be introduced at certain heights
|
|
int nVersion;
|
|
|
|
// construct a CCoins from a CTransaction, at a given height
|
|
CCoins(const CTransaction &tx, int nHeightIn) : fCoinBase(tx.IsCoinBase()), vout(tx.vout), nHeight(nHeightIn), nVersion(tx.nVersion) { }
|
|
|
|
// empty constructor
|
|
CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { }
|
|
|
|
// remove spent outputs at the end of vout
|
|
void Cleanup() {
|
|
while (vout.size() > 0 && vout.back().IsNull())
|
|
vout.pop_back();
|
|
}
|
|
|
|
// equality test
|
|
friend bool operator==(const CCoins &a, const CCoins &b) {
|
|
return a.fCoinBase == b.fCoinBase &&
|
|
a.nHeight == b.nHeight &&
|
|
a.nVersion == b.nVersion &&
|
|
a.vout == b.vout;
|
|
}
|
|
friend bool operator!=(const CCoins &a, const CCoins &b) {
|
|
return !(a == b);
|
|
}
|
|
|
|
// calculate number of bytes for the bitmask, and its number of non-zero bytes
|
|
// each bit in the bitmask represents the availability of one output, but the
|
|
// availabilities of the first two outputs are encoded separately
|
|
void CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const {
|
|
unsigned int nLastUsedByte = 0;
|
|
for (unsigned int b = 0; 2+b*8 < vout.size(); b++) {
|
|
bool fZero = true;
|
|
for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) {
|
|
if (!vout[2+b*8+i].IsNull()) {
|
|
fZero = false;
|
|
continue;
|
|
}
|
|
}
|
|
if (!fZero) {
|
|
nLastUsedByte = b + 1;
|
|
nNonzeroBytes++;
|
|
}
|
|
}
|
|
nBytes += nLastUsedByte;
|
|
}
|
|
|
|
bool IsCoinBase() const {
|
|
return fCoinBase;
|
|
}
|
|
|
|
unsigned int GetSerializeSize(int nType, int nVersion) const {
|
|
unsigned int nSize = 0;
|
|
unsigned int nMaskSize = 0, nMaskCode = 0;
|
|
CalcMaskSize(nMaskSize, nMaskCode);
|
|
bool fFirst = vout.size() > 0 && !vout[0].IsNull();
|
|
bool fSecond = vout.size() > 1 && !vout[1].IsNull();
|
|
assert(fFirst || fSecond || nMaskCode);
|
|
unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0);
|
|
// version
|
|
nSize += ::GetSerializeSize(VARINT(this->nVersion), nType, nVersion);
|
|
// size of header code
|
|
nSize += ::GetSerializeSize(VARINT(nCode), nType, nVersion);
|
|
// spentness bitmask
|
|
nSize += nMaskSize;
|
|
// txouts themself
|
|
for (unsigned int i = 0; i < vout.size(); i++)
|
|
if (!vout[i].IsNull())
|
|
nSize += ::GetSerializeSize(CTxOutCompressor(REF(vout[i])), nType, nVersion);
|
|
// height
|
|
nSize += ::GetSerializeSize(VARINT(nHeight), nType, nVersion);
|
|
return nSize;
|
|
}
|
|
|
|
template<typename Stream>
|
|
void Serialize(Stream &s, int nType, int nVersion) const {
|
|
unsigned int nMaskSize = 0, nMaskCode = 0;
|
|
CalcMaskSize(nMaskSize, nMaskCode);
|
|
bool fFirst = vout.size() > 0 && !vout[0].IsNull();
|
|
bool fSecond = vout.size() > 1 && !vout[1].IsNull();
|
|
assert(fFirst || fSecond || nMaskCode);
|
|
unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0);
|
|
// version
|
|
::Serialize(s, VARINT(this->nVersion), nType, nVersion);
|
|
// header code
|
|
::Serialize(s, VARINT(nCode), nType, nVersion);
|
|
// spentness bitmask
|
|
for (unsigned int b = 0; b<nMaskSize; b++) {
|
|
unsigned char chAvail = 0;
|
|
for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++)
|
|
if (!vout[2+b*8+i].IsNull())
|
|
chAvail |= (1 << i);
|
|
::Serialize(s, chAvail, nType, nVersion);
|
|
}
|
|
// txouts themself
|
|
for (unsigned int i = 0; i < vout.size(); i++) {
|
|
if (!vout[i].IsNull())
|
|
::Serialize(s, CTxOutCompressor(REF(vout[i])), nType, nVersion);
|
|
}
|
|
// coinbase height
|
|
::Serialize(s, VARINT(nHeight), nType, nVersion);
|
|
}
|
|
|
|
template<typename Stream>
|
|
void Unserialize(Stream &s, int nType, int nVersion) {
|
|
unsigned int nCode = 0;
|
|
// version
|
|
::Unserialize(s, VARINT(this->nVersion), nType, nVersion);
|
|
// header code
|
|
::Unserialize(s, VARINT(nCode), nType, nVersion);
|
|
fCoinBase = nCode & 1;
|
|
std::vector<bool> vAvail(2, false);
|
|
vAvail[0] = nCode & 2;
|
|
vAvail[1] = nCode & 4;
|
|
unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1);
|
|
// spentness bitmask
|
|
while (nMaskCode > 0) {
|
|
unsigned char chAvail = 0;
|
|
::Unserialize(s, chAvail, nType, nVersion);
|
|
for (unsigned int p = 0; p < 8; p++) {
|
|
bool f = (chAvail & (1 << p)) != 0;
|
|
vAvail.push_back(f);
|
|
}
|
|
if (chAvail != 0)
|
|
nMaskCode--;
|
|
}
|
|
// txouts themself
|
|
vout.assign(vAvail.size(), CTxOut());
|
|
for (unsigned int i = 0; i < vAvail.size(); i++) {
|
|
if (vAvail[i])
|
|
::Unserialize(s, REF(CTxOutCompressor(vout[i])), nType, nVersion);
|
|
}
|
|
// coinbase height
|
|
::Unserialize(s, VARINT(nHeight), nType, nVersion);
|
|
Cleanup();
|
|
}
|
|
|
|
// mark an outpoint spent, and construct undo information
|
|
bool Spend(const COutPoint &out, CTxInUndo &undo) {
|
|
if (out.n >= vout.size())
|
|
return false;
|
|
if (vout[out.n].IsNull())
|
|
return false;
|
|
undo = CTxInUndo(vout[out.n]);
|
|
vout[out.n].SetNull();
|
|
Cleanup();
|
|
if (vout.size() == 0) {
|
|
undo.nHeight = nHeight;
|
|
undo.fCoinBase = fCoinBase;
|
|
undo.nVersion = this->nVersion;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// mark a vout spent
|
|
bool Spend(int nPos) {
|
|
CTxInUndo undo;
|
|
COutPoint out(0, nPos);
|
|
return Spend(out, undo);
|
|
}
|
|
|
|
// check whether a particular output is still available
|
|
bool IsAvailable(unsigned int nPos) const {
|
|
return (nPos < vout.size() && !vout[nPos].IsNull());
|
|
}
|
|
|
|
// check whether the entire CCoins is spent
|
|
// note that only !IsPruned() CCoins can be serialized
|
|
bool IsPruned() const {
|
|
BOOST_FOREACH(const CTxOut &out, vout)
|
|
if (!out.IsNull())
|
|
return false;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/** A transaction with a merkle branch linking it to the block chain. */
|
|
class CMerkleTx : public CTransaction
|
|
{
|
|
public:
|
|
uint256 hashBlock;
|
|
std::vector<uint256> vMerkleBranch;
|
|
int nIndex;
|
|
|
|
// memory only
|
|
mutable bool fMerkleVerified;
|
|
|
|
|
|
CMerkleTx()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
CMerkleTx(const CTransaction& txIn) : CTransaction(txIn)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
hashBlock = 0;
|
|
nIndex = -1;
|
|
fMerkleVerified = false;
|
|
}
|
|
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action);
|
|
nVersion = this->nVersion;
|
|
READWRITE(hashBlock);
|
|
READWRITE(vMerkleBranch);
|
|
READWRITE(nIndex);
|
|
)
|
|
|
|
|
|
int SetMerkleBranch(const CBlock* pblock=NULL);
|
|
int GetDepthInMainChain(CBlockIndex* &pindexRet) const;
|
|
int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
|
|
bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
|
|
int GetBlocksToMaturity() const;
|
|
bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true);
|
|
bool AcceptToMemoryPool();
|
|
};
|
|
|
|
|
|
|
|
|
|
/** A txdb record that contains the disk location of a transaction and the
|
|
* locations of transactions that spend its outputs. vSpent is really only
|
|
* used as a flag, but having the location is very helpful for debugging.
|
|
*/
|
|
class CTxIndex
|
|
{
|
|
public:
|
|
CDiskTxPos pos;
|
|
std::vector<CDiskTxPos> vSpent;
|
|
|
|
CTxIndex()
|
|
{
|
|
SetNull();
|
|
}
|
|
|
|
CTxIndex(const CDiskTxPos& posIn, unsigned int nOutputs)
|
|
{
|
|
pos = posIn;
|
|
vSpent.resize(nOutputs);
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
if (!(nType & SER_GETHASH))
|
|
READWRITE(nVersion);
|
|
READWRITE(pos);
|
|
READWRITE(vSpent);
|
|
)
|
|
|
|
void SetNull()
|
|
{
|
|
pos.SetNull();
|
|
vSpent.clear();
|
|
}
|
|
|
|
bool IsNull()
|
|
{
|
|
return pos.IsNull();
|
|
}
|
|
|
|
friend bool operator==(const CTxIndex& a, const CTxIndex& b)
|
|
{
|
|
return (a.pos == b.pos &&
|
|
a.vSpent == b.vSpent);
|
|
}
|
|
|
|
friend bool operator!=(const CTxIndex& a, const CTxIndex& b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
int GetDepthInMainChain() const;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/** Nodes collect new transactions into a block, hash them into a hash tree,
|
|
* and scan through nonce values to make the block's hash satisfy proof-of-work
|
|
* requirements. When they solve the proof-of-work, they broadcast the block
|
|
* to everyone and the block is added to the block chain. The first transaction
|
|
* in the block is a special one that creates a new coin owned by the creator
|
|
* of the block.
|
|
*
|
|
* Blocks are appended to blk0001.dat files on disk. Their location on disk
|
|
* is indexed by CBlockIndex objects in memory.
|
|
*/
|
|
class CBlock
|
|
{
|
|
public:
|
|
// header
|
|
static const int CURRENT_VERSION=2;
|
|
int nVersion;
|
|
uint256 hashPrevBlock;
|
|
uint256 hashMerkleRoot;
|
|
unsigned int nTime;
|
|
unsigned int nBits;
|
|
unsigned int nNonce;
|
|
|
|
// network and disk
|
|
std::vector<CTransaction> vtx;
|
|
|
|
// memory only
|
|
mutable std::vector<uint256> vMerkleTree;
|
|
|
|
// Denial-of-service detection:
|
|
mutable int nDoS;
|
|
bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
|
|
|
|
CBlock()
|
|
{
|
|
SetNull();
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
READWRITE(this->nVersion);
|
|
nVersion = this->nVersion;
|
|
READWRITE(hashPrevBlock);
|
|
READWRITE(hashMerkleRoot);
|
|
READWRITE(nTime);
|
|
READWRITE(nBits);
|
|
READWRITE(nNonce);
|
|
|
|
// ConnectBlock depends on vtx being last so it can calculate offset
|
|
if (!(nType & (SER_GETHASH|SER_BLOCKHEADERONLY)))
|
|
READWRITE(vtx);
|
|
else if (fRead)
|
|
const_cast<CBlock*>(this)->vtx.clear();
|
|
)
|
|
|
|
void SetNull()
|
|
{
|
|
nVersion = CBlock::CURRENT_VERSION;
|
|
hashPrevBlock = 0;
|
|
hashMerkleRoot = 0;
|
|
nTime = 0;
|
|
nBits = 0;
|
|
nNonce = 0;
|
|
vtx.clear();
|
|
vMerkleTree.clear();
|
|
nDoS = 0;
|
|
}
|
|
|
|
bool IsNull() const
|
|
{
|
|
return (nBits == 0);
|
|
}
|
|
|
|
uint256 GetHash() const
|
|
{
|
|
return Hash(BEGIN(nVersion), END(nNonce));
|
|
}
|
|
|
|
int64 GetBlockTime() const
|
|
{
|
|
return (int64)nTime;
|
|
}
|
|
|
|
void UpdateTime(const CBlockIndex* pindexPrev);
|
|
|
|
|
|
uint256 BuildMerkleTree() const
|
|
{
|
|
vMerkleTree.clear();
|
|
BOOST_FOREACH(const CTransaction& tx, vtx)
|
|
vMerkleTree.push_back(tx.GetHash());
|
|
int j = 0;
|
|
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
|
|
{
|
|
for (int i = 0; i < nSize; i += 2)
|
|
{
|
|
int i2 = std::min(i+1, nSize-1);
|
|
vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]),
|
|
BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2])));
|
|
}
|
|
j += nSize;
|
|
}
|
|
return (vMerkleTree.empty() ? 0 : vMerkleTree.back());
|
|
}
|
|
|
|
std::vector<uint256> GetMerkleBranch(int nIndex) const
|
|
{
|
|
if (vMerkleTree.empty())
|
|
BuildMerkleTree();
|
|
std::vector<uint256> vMerkleBranch;
|
|
int j = 0;
|
|
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
|
|
{
|
|
int i = std::min(nIndex^1, nSize-1);
|
|
vMerkleBranch.push_back(vMerkleTree[j+i]);
|
|
nIndex >>= 1;
|
|
j += nSize;
|
|
}
|
|
return vMerkleBranch;
|
|
}
|
|
|
|
static uint256 CheckMerkleBranch(uint256 hash, const std::vector<uint256>& vMerkleBranch, int nIndex)
|
|
{
|
|
if (nIndex == -1)
|
|
return 0;
|
|
BOOST_FOREACH(const uint256& otherside, vMerkleBranch)
|
|
{
|
|
if (nIndex & 1)
|
|
hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash));
|
|
else
|
|
hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside));
|
|
nIndex >>= 1;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
|
|
bool WriteToDisk(CDiskBlockPos &pos)
|
|
{
|
|
// Open history file to append
|
|
pos.MakeUnique(GetDataDir());
|
|
CAutoFile fileout = CAutoFile(OpenBlockFile(pos, "ab"), SER_DISK, CLIENT_VERSION);
|
|
if (!fileout)
|
|
return error("CBlock::WriteToDisk() : AppendBlockFile failed");
|
|
|
|
// Write block
|
|
fileout << *this;
|
|
|
|
// Flush stdio buffers and commit to disk before returning
|
|
fflush(fileout);
|
|
if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0)
|
|
FileCommit(fileout);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ReadFromDisk(const CDiskBlockPos &pos, bool fReadTransactions = true)
|
|
{
|
|
SetNull();
|
|
|
|
// Open history file to read
|
|
CAutoFile filein = CAutoFile(OpenBlockFile(pos, "rb"), SER_DISK, CLIENT_VERSION);
|
|
if (!filein)
|
|
return error("CBlock::ReadFromDisk() : OpenBlockFile failed");
|
|
if (!fReadTransactions)
|
|
filein.nType |= SER_BLOCKHEADERONLY;
|
|
|
|
// Read block
|
|
try {
|
|
filein >> *this;
|
|
}
|
|
catch (std::exception &e) {
|
|
return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
|
|
}
|
|
|
|
// Check the header
|
|
if (!CheckProofOfWork(GetHash(), nBits))
|
|
return error("CBlock::ReadFromDisk() : errors in block header");
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
void print() const
|
|
{
|
|
printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu")\n",
|
|
GetHash().ToString().substr(0,20).c_str(),
|
|
nVersion,
|
|
hashPrevBlock.ToString().substr(0,20).c_str(),
|
|
hashMerkleRoot.ToString().substr(0,10).c_str(),
|
|
nTime, nBits, nNonce,
|
|
vtx.size());
|
|
for (unsigned int i = 0; i < vtx.size(); i++)
|
|
{
|
|
printf(" ");
|
|
vtx[i].print();
|
|
}
|
|
printf(" vMerkleTree: ");
|
|
for (unsigned int i = 0; i < vMerkleTree.size(); i++)
|
|
printf("%s ", vMerkleTree[i].ToString().substr(0,10).c_str());
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex);
|
|
bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck=false);
|
|
bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true);
|
|
bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew);
|
|
bool AddToBlockIndex(const CDiskBlockPos &pos);
|
|
bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const;
|
|
bool AcceptBlock();
|
|
|
|
private:
|
|
bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew);
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** The block chain is a tree shaped structure starting with the
|
|
* genesis block at the root, with each block potentially having multiple
|
|
* candidates to be the next block. pprev and pnext link a path through the
|
|
* main/longest chain. A blockindex may have multiple pprev pointing back
|
|
* to it, but pnext will only point forward to the longest branch, or will
|
|
* be null if the block is not part of the longest chain.
|
|
*/
|
|
class CBlockIndex
|
|
{
|
|
public:
|
|
const uint256* phashBlock;
|
|
CBlockIndex* pprev;
|
|
CBlockIndex* pnext;
|
|
int nHeight;
|
|
unsigned int nAlternative;
|
|
CBigNum bnChainWork;
|
|
|
|
// block header
|
|
int nVersion;
|
|
uint256 hashMerkleRoot;
|
|
unsigned int nTime;
|
|
unsigned int nBits;
|
|
unsigned int nNonce;
|
|
|
|
|
|
CBlockIndex()
|
|
{
|
|
phashBlock = NULL;
|
|
pprev = NULL;
|
|
pnext = NULL;
|
|
nHeight = 0;
|
|
bnChainWork = 0;
|
|
nAlternative = 0;
|
|
|
|
nVersion = 0;
|
|
hashMerkleRoot = 0;
|
|
nTime = 0;
|
|
nBits = 0;
|
|
nNonce = 0;
|
|
}
|
|
|
|
CBlockIndex(CBlock& block)
|
|
{
|
|
phashBlock = NULL;
|
|
pprev = NULL;
|
|
pnext = NULL;
|
|
nHeight = 0;
|
|
bnChainWork = 0;
|
|
nAlternative = 0;
|
|
|
|
nVersion = block.nVersion;
|
|
hashMerkleRoot = block.hashMerkleRoot;
|
|
nTime = block.nTime;
|
|
nBits = block.nBits;
|
|
nNonce = block.nNonce;
|
|
}
|
|
|
|
CDiskBlockPos GetBlockPos() const {
|
|
return CDiskBlockPos(nHeight, nAlternative);
|
|
}
|
|
|
|
CBlock GetBlockHeader() const
|
|
{
|
|
CBlock block;
|
|
block.nVersion = nVersion;
|
|
if (pprev)
|
|
block.hashPrevBlock = pprev->GetBlockHash();
|
|
block.hashMerkleRoot = hashMerkleRoot;
|
|
block.nTime = nTime;
|
|
block.nBits = nBits;
|
|
block.nNonce = nNonce;
|
|
return block;
|
|
}
|
|
|
|
uint256 GetBlockHash() const
|
|
{
|
|
return *phashBlock;
|
|
}
|
|
|
|
int64 GetBlockTime() const
|
|
{
|
|
return (int64)nTime;
|
|
}
|
|
|
|
CBigNum GetBlockWork() const
|
|
{
|
|
CBigNum bnTarget;
|
|
bnTarget.SetCompact(nBits);
|
|
if (bnTarget <= 0)
|
|
return 0;
|
|
return (CBigNum(1)<<256) / (bnTarget+1);
|
|
}
|
|
|
|
bool IsInMainChain() const
|
|
{
|
|
return (pnext || this == pindexBest);
|
|
}
|
|
|
|
bool CheckIndex() const
|
|
{
|
|
return CheckProofOfWork(GetBlockHash(), nBits);
|
|
}
|
|
|
|
enum { nMedianTimeSpan=11 };
|
|
|
|
int64 GetMedianTimePast() const
|
|
{
|
|
int64 pmedian[nMedianTimeSpan];
|
|
int64* pbegin = &pmedian[nMedianTimeSpan];
|
|
int64* pend = &pmedian[nMedianTimeSpan];
|
|
|
|
const CBlockIndex* pindex = this;
|
|
for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev)
|
|
*(--pbegin) = pindex->GetBlockTime();
|
|
|
|
std::sort(pbegin, pend);
|
|
return pbegin[(pend - pbegin)/2];
|
|
}
|
|
|
|
int64 GetMedianTime() const
|
|
{
|
|
const CBlockIndex* pindex = this;
|
|
for (int i = 0; i < nMedianTimeSpan/2; i++)
|
|
{
|
|
if (!pindex->pnext)
|
|
return GetBlockTime();
|
|
pindex = pindex->pnext;
|
|
}
|
|
return pindex->GetMedianTimePast();
|
|
}
|
|
|
|
/**
|
|
* Returns true if there are nRequired or more blocks of minVersion or above
|
|
* in the last nToCheck blocks, starting at pstart and going backwards.
|
|
*/
|
|
static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart,
|
|
unsigned int nRequired, unsigned int nToCheck);
|
|
|
|
std::string ToString() const
|
|
{
|
|
return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)",
|
|
pprev, pnext, nHeight,
|
|
hashMerkleRoot.ToString().substr(0,10).c_str(),
|
|
GetBlockHash().ToString().substr(0,20).c_str());
|
|
}
|
|
|
|
void print() const
|
|
{
|
|
printf("%s\n", ToString().c_str());
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/** Used to marshal pointers into hashes for db storage. */
|
|
class CDiskBlockIndex : public CBlockIndex
|
|
{
|
|
public:
|
|
uint256 hashPrev;
|
|
uint256 hashNext;
|
|
|
|
CDiskBlockIndex()
|
|
{
|
|
hashPrev = 0;
|
|
hashNext = 0;
|
|
}
|
|
|
|
explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex)
|
|
{
|
|
hashPrev = (pprev ? pprev->GetBlockHash() : 0);
|
|
hashNext = (pnext ? pnext->GetBlockHash() : 0);
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
if (!(nType & SER_GETHASH))
|
|
READWRITE(nVersion);
|
|
|
|
READWRITE(hashNext);
|
|
READWRITE(nHeight);
|
|
READWRITE(nAlternative);
|
|
|
|
// block header
|
|
READWRITE(this->nVersion);
|
|
READWRITE(hashPrev);
|
|
READWRITE(hashMerkleRoot);
|
|
READWRITE(nTime);
|
|
READWRITE(nBits);
|
|
READWRITE(nNonce);
|
|
)
|
|
|
|
uint256 GetBlockHash() const
|
|
{
|
|
CBlock block;
|
|
block.nVersion = nVersion;
|
|
block.hashPrevBlock = hashPrev;
|
|
block.hashMerkleRoot = hashMerkleRoot;
|
|
block.nTime = nTime;
|
|
block.nBits = nBits;
|
|
block.nNonce = nNonce;
|
|
return block.GetHash();
|
|
}
|
|
|
|
|
|
std::string ToString() const
|
|
{
|
|
std::string str = "CDiskBlockIndex(";
|
|
str += CBlockIndex::ToString();
|
|
str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)",
|
|
GetBlockHash().ToString().c_str(),
|
|
hashPrev.ToString().substr(0,20).c_str(),
|
|
hashNext.ToString().substr(0,20).c_str());
|
|
return str;
|
|
}
|
|
|
|
void print() const
|
|
{
|
|
printf("%s\n", ToString().c_str());
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Describes a place in the block chain to another node such that if the
|
|
* other node doesn't have the same branch, it can find a recent common trunk.
|
|
* The further back it is, the further before the fork it may be.
|
|
*/
|
|
class CBlockLocator
|
|
{
|
|
protected:
|
|
std::vector<uint256> vHave;
|
|
public:
|
|
|
|
CBlockLocator()
|
|
{
|
|
}
|
|
|
|
explicit CBlockLocator(const CBlockIndex* pindex)
|
|
{
|
|
Set(pindex);
|
|
}
|
|
|
|
explicit CBlockLocator(uint256 hashBlock)
|
|
{
|
|
std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
|
|
if (mi != mapBlockIndex.end())
|
|
Set((*mi).second);
|
|
}
|
|
|
|
CBlockLocator(const std::vector<uint256>& vHaveIn)
|
|
{
|
|
vHave = vHaveIn;
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
if (!(nType & SER_GETHASH))
|
|
READWRITE(nVersion);
|
|
READWRITE(vHave);
|
|
)
|
|
|
|
void SetNull()
|
|
{
|
|
vHave.clear();
|
|
}
|
|
|
|
bool IsNull()
|
|
{
|
|
return vHave.empty();
|
|
}
|
|
|
|
void Set(const CBlockIndex* pindex)
|
|
{
|
|
vHave.clear();
|
|
int nStep = 1;
|
|
while (pindex)
|
|
{
|
|
vHave.push_back(pindex->GetBlockHash());
|
|
|
|
// Exponentially larger steps back
|
|
for (int i = 0; pindex && i < nStep; i++)
|
|
pindex = pindex->pprev;
|
|
if (vHave.size() > 10)
|
|
nStep *= 2;
|
|
}
|
|
vHave.push_back(hashGenesisBlock);
|
|
}
|
|
|
|
int GetDistanceBack()
|
|
{
|
|
// Retrace how far back it was in the sender's branch
|
|
int nDistance = 0;
|
|
int nStep = 1;
|
|
BOOST_FOREACH(const uint256& hash, vHave)
|
|
{
|
|
std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
|
|
if (mi != mapBlockIndex.end())
|
|
{
|
|
CBlockIndex* pindex = (*mi).second;
|
|
if (pindex->IsInMainChain())
|
|
return nDistance;
|
|
}
|
|
nDistance += nStep;
|
|
if (nDistance > 10)
|
|
nStep *= 2;
|
|
}
|
|
return nDistance;
|
|
}
|
|
|
|
CBlockIndex* GetBlockIndex()
|
|
{
|
|
// Find the first block the caller has in the main chain
|
|
BOOST_FOREACH(const uint256& hash, vHave)
|
|
{
|
|
std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
|
|
if (mi != mapBlockIndex.end())
|
|
{
|
|
CBlockIndex* pindex = (*mi).second;
|
|
if (pindex->IsInMainChain())
|
|
return pindex;
|
|
}
|
|
}
|
|
return pindexGenesisBlock;
|
|
}
|
|
|
|
uint256 GetBlockHash()
|
|
{
|
|
// Find the first block the caller has in the main chain
|
|
BOOST_FOREACH(const uint256& hash, vHave)
|
|
{
|
|
std::map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
|
|
if (mi != mapBlockIndex.end())
|
|
{
|
|
CBlockIndex* pindex = (*mi).second;
|
|
if (pindex->IsInMainChain())
|
|
return hash;
|
|
}
|
|
}
|
|
return hashGenesisBlock;
|
|
}
|
|
|
|
int GetHeight()
|
|
{
|
|
CBlockIndex* pindex = GetBlockIndex();
|
|
if (!pindex)
|
|
return 0;
|
|
return pindex->nHeight;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CTxMemPool
|
|
{
|
|
public:
|
|
mutable CCriticalSection cs;
|
|
std::map<uint256, CTransaction> mapTx;
|
|
std::map<COutPoint, CInPoint> mapNextTx;
|
|
|
|
bool accept(CTxDB& txdb, CTransaction &tx,
|
|
bool fCheckInputs, bool* pfMissingInputs);
|
|
bool addUnchecked(const uint256& hash, CTransaction &tx);
|
|
bool remove(CTransaction &tx);
|
|
void clear();
|
|
void queryHashes(std::vector<uint256>& vtxid);
|
|
|
|
unsigned long size()
|
|
{
|
|
LOCK(cs);
|
|
return mapTx.size();
|
|
}
|
|
|
|
bool exists(uint256 hash)
|
|
{
|
|
return (mapTx.count(hash) != 0);
|
|
}
|
|
|
|
CTransaction& lookup(uint256 hash)
|
|
{
|
|
return mapTx[hash];
|
|
}
|
|
};
|
|
|
|
extern CTxMemPool mempool;
|
|
|
|
#endif
|