mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 12:32:48 +01:00
be2fa90b94
When one single dependency of an unconfirmed transaction is already verified, this does not mean the parent is confirmed. We can skip checking its own dependencies though.
2051 lines
51 KiB
C++
2051 lines
51 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
class COutPoint;
|
|
class CInPoint;
|
|
class CDiskTxPos;
|
|
class CCoinBase;
|
|
class CTxIn;
|
|
class CTxOut;
|
|
class CTransaction;
|
|
class CBlock;
|
|
class CBlockIndex;
|
|
class CWalletTx;
|
|
class CKeyItem;
|
|
|
|
static const unsigned int MAX_BLOCK_SIZE = 1000000;
|
|
static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
|
|
static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
|
|
static const int64 COIN = 100000000;
|
|
static const int64 CENT = 1000000;
|
|
static const int64 MAX_MONEY = 21000000 * COIN;
|
|
inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONEY); }
|
|
static const int COINBASE_MATURITY = 100;
|
|
#ifdef USE_UPNP
|
|
static const int fHaveUPnP = true;
|
|
#else
|
|
static const int fHaveUPnP = false;
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern CCriticalSection cs_main;
|
|
extern map<uint256, CBlockIndex*> mapBlockIndex;
|
|
extern uint256 hashGenesisBlock;
|
|
extern CBigNum bnProofOfWorkLimit;
|
|
extern CBlockIndex* pindexGenesisBlock;
|
|
extern int nBestHeight;
|
|
extern CBigNum bnBestChainWork;
|
|
extern CBigNum bnBestInvalidWork;
|
|
extern uint256 hashBestChain;
|
|
extern CBlockIndex* pindexBest;
|
|
extern unsigned int nTransactionsUpdated;
|
|
extern map<uint256, int> mapRequestCount;
|
|
extern CCriticalSection cs_mapRequestCount;
|
|
extern map<string, string> mapAddressBook;
|
|
extern CCriticalSection cs_mapAddressBook;
|
|
extern vector<unsigned char> vchDefaultKey;
|
|
extern double dHashesPerSec;
|
|
extern int64 nHPSTimerStart;
|
|
|
|
// Settings
|
|
extern int fGenerateBitcoins;
|
|
extern int64 nTransactionFee;
|
|
extern CAddress addrIncoming;
|
|
extern int fLimitProcessors;
|
|
extern int nLimitProcessors;
|
|
extern int fMinimizeToTray;
|
|
extern int fMinimizeOnClose;
|
|
extern int fUseUPnP;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool CheckDiskSpace(uint64 nAdditionalBytes=0);
|
|
FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb");
|
|
FILE* AppendBlockFile(unsigned int& nFileRet);
|
|
bool AddKey(const CKey& key);
|
|
vector<unsigned char> GenerateNewKey();
|
|
bool AddToWallet(const CWalletTx& wtxIn);
|
|
void WalletUpdateSpent(const COutPoint& prevout);
|
|
int ScanForWalletTransactions(CBlockIndex* pindexStart);
|
|
void ReacceptWalletTransactions();
|
|
bool LoadBlockIndex(bool fAllowNew=true);
|
|
void PrintBlockTree();
|
|
bool ProcessMessages(CNode* pfrom);
|
|
bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv);
|
|
bool SendMessages(CNode* pto, bool fSendTrickle);
|
|
int64 GetBalance();
|
|
bool CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet);
|
|
bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet);
|
|
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
|
|
bool BroadcastTransaction(CWalletTx& wtxNew);
|
|
string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
|
|
string SendMoneyToBitcoinAddress(string strAddress, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false);
|
|
void GenerateBitcoins(bool fGenerate);
|
|
void ThreadBitcoinMiner(void* parg);
|
|
CBlock* CreateNewBlock(CReserveKey& reservekey);
|
|
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce, int64& nPrevTime);
|
|
void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1);
|
|
bool CheckWork(CBlock* pblock, CReserveKey& reservekey);
|
|
void BitcoinMiner();
|
|
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
|
|
bool IsInitialBlockDownload();
|
|
string GetWarnings(string strFor);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CDiskTxPos
|
|
{
|
|
public:
|
|
unsigned int nFile;
|
|
unsigned int nBlockPos;
|
|
unsigned int nTxPos;
|
|
|
|
CDiskTxPos()
|
|
{
|
|
SetNull();
|
|
}
|
|
|
|
CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn)
|
|
{
|
|
nFile = nFileIn;
|
|
nBlockPos = nBlockPosIn;
|
|
nTxPos = nTxPosIn;
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); )
|
|
void SetNull() { nFile = -1; nBlockPos = 0; nTxPos = 0; }
|
|
bool IsNull() const { return (nFile == -1); }
|
|
|
|
friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b)
|
|
{
|
|
return (a.nFile == b.nFile &&
|
|
a.nBlockPos == b.nBlockPos &&
|
|
a.nTxPos == b.nTxPos);
|
|
}
|
|
|
|
friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
string ToString() const
|
|
{
|
|
if (IsNull())
|
|
return strprintf("null");
|
|
else
|
|
return strprintf("(nFile=%d, nBlockPos=%d, nTxPos=%d)", nFile, nBlockPos, nTxPos);
|
|
}
|
|
|
|
void print() const
|
|
{
|
|
printf("%s", ToString().c_str());
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
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 = -1; }
|
|
bool IsNull() const { return (ptx == NULL && n == -1); }
|
|
};
|
|
|
|
|
|
|
|
|
|
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 = -1; }
|
|
bool IsNull() const { return (hash == 0 && n == -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);
|
|
}
|
|
|
|
string ToString() const
|
|
{
|
|
return strprintf("COutPoint(%s, %d)", 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 = UINT_MAX;
|
|
}
|
|
|
|
explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX)
|
|
{
|
|
prevout = prevoutIn;
|
|
scriptSig = scriptSigIn;
|
|
nSequence = nSequenceIn;
|
|
}
|
|
|
|
CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX)
|
|
{
|
|
prevout = COutPoint(hashPrevTx, nOut);
|
|
scriptSig = scriptSigIn;
|
|
nSequence = nSequenceIn;
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
READWRITE(prevout);
|
|
READWRITE(scriptSig);
|
|
READWRITE(nSequence);
|
|
)
|
|
|
|
bool IsFinal() const
|
|
{
|
|
return (nSequence == UINT_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);
|
|
}
|
|
|
|
string ToString() const
|
|
{
|
|
string str;
|
|
str += strprintf("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 != UINT_MAX)
|
|
str += strprintf(", nSequence=%u", nSequence);
|
|
str += ")";
|
|
return str;
|
|
}
|
|
|
|
void print() const
|
|
{
|
|
printf("%s\n", ToString().c_str());
|
|
}
|
|
|
|
bool IsMine() const;
|
|
int64 GetDebit() const;
|
|
};
|
|
|
|
|
|
|
|
|
|
//
|
|
// 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()
|
|
{
|
|
return (nValue == -1);
|
|
}
|
|
|
|
uint256 GetHash() const
|
|
{
|
|
return SerializeHash(*this);
|
|
}
|
|
|
|
bool IsMine() const
|
|
{
|
|
return ::IsMine(scriptPubKey);
|
|
}
|
|
|
|
int64 GetCredit() const
|
|
{
|
|
if (!MoneyRange(nValue))
|
|
throw runtime_error("CTxOut::GetCredit() : value out of range");
|
|
return (IsMine() ? nValue : 0);
|
|
}
|
|
|
|
bool IsChange() const
|
|
{
|
|
// On a debit transaction, a txout that's mine but isn't in the address book is change
|
|
vector<unsigned char> vchPubKey;
|
|
if (ExtractPubKey(scriptPubKey, true, vchPubKey))
|
|
CRITICAL_BLOCK(cs_mapAddressBook)
|
|
if (!mapAddressBook.count(PubKeyToAddress(vchPubKey)))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
int64 GetChange() const
|
|
{
|
|
if (!MoneyRange(nValue))
|
|
throw runtime_error("CTxOut::GetChange() : value out of range");
|
|
return (IsChange() ? nValue : 0);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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());
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
//
|
|
// The basic transaction that is broadcasted on the network and contained in
|
|
// blocks. A transaction can contain multiple inputs and outputs.
|
|
//
|
|
class CTransaction
|
|
{
|
|
public:
|
|
int nVersion;
|
|
vector<CTxIn> vin;
|
|
vector<CTxOut> vout;
|
|
unsigned int nLockTime;
|
|
|
|
|
|
CTransaction()
|
|
{
|
|
SetNull();
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
READWRITE(this->nVersion);
|
|
nVersion = this->nVersion;
|
|
READWRITE(vin);
|
|
READWRITE(vout);
|
|
READWRITE(nLockTime);
|
|
)
|
|
|
|
void SetNull()
|
|
{
|
|
nVersion = 1;
|
|
vin.clear();
|
|
vout.clear();
|
|
nLockTime = 0;
|
|
}
|
|
|
|
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 < (nLockTime < 500000000 ? (int64)nBlockHeight : nBlockTime))
|
|
return true;
|
|
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 (int i = 0; i < vin.size(); i++)
|
|
if (vin[i].prevout != old.vin[i].prevout)
|
|
return false;
|
|
|
|
bool fNewer = false;
|
|
unsigned int nLowest = UINT_MAX;
|
|
for (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());
|
|
}
|
|
|
|
int GetSigOpCount() const
|
|
{
|
|
int n = 0;
|
|
foreach(const CTxIn& txin, vin)
|
|
n += txin.scriptSig.GetSigOpCount();
|
|
foreach(const CTxOut& txout, vout)
|
|
n += txout.scriptPubKey.GetSigOpCount();
|
|
return n;
|
|
}
|
|
|
|
bool IsStandard() const
|
|
{
|
|
foreach(const CTxIn& txin, vin)
|
|
if (!txin.scriptSig.IsPushOnly())
|
|
return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str());
|
|
foreach(const CTxOut& txout, vout)
|
|
if (!::IsStandard(txout.scriptPubKey))
|
|
return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str());
|
|
return true;
|
|
}
|
|
|
|
bool IsMine() const
|
|
{
|
|
foreach(const CTxOut& txout, vout)
|
|
if (txout.IsMine())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool IsFromMe() const
|
|
{
|
|
return (GetDebit() > 0);
|
|
}
|
|
|
|
int64 GetDebit() const
|
|
{
|
|
int64 nDebit = 0;
|
|
foreach(const CTxIn& txin, vin)
|
|
{
|
|
nDebit += txin.GetDebit();
|
|
if (!MoneyRange(nDebit))
|
|
throw runtime_error("CTransaction::GetDebit() : value out of range");
|
|
}
|
|
return nDebit;
|
|
}
|
|
|
|
int64 GetCredit() const
|
|
{
|
|
int64 nCredit = 0;
|
|
foreach(const CTxOut& txout, vout)
|
|
{
|
|
nCredit += txout.GetCredit();
|
|
if (!MoneyRange(nCredit))
|
|
throw runtime_error("CTransaction::GetCredit() : value out of range");
|
|
}
|
|
return nCredit;
|
|
}
|
|
|
|
int64 GetChange() const
|
|
{
|
|
if (IsCoinBase())
|
|
return 0;
|
|
int64 nChange = 0;
|
|
foreach(const CTxOut& txout, vout)
|
|
{
|
|
nChange += txout.GetChange();
|
|
if (!MoneyRange(nChange))
|
|
throw runtime_error("CTransaction::GetChange() : value out of range");
|
|
}
|
|
return nChange;
|
|
}
|
|
|
|
int64 GetValueOut() const
|
|
{
|
|
int64 nValueOut = 0;
|
|
foreach(const CTxOut& txout, vout)
|
|
{
|
|
nValueOut += txout.nValue;
|
|
if (!MoneyRange(txout.nValue) || !MoneyRange(nValueOut))
|
|
throw runtime_error("CTransaction::GetValueOut() : value out of range");
|
|
}
|
|
return nValueOut;
|
|
}
|
|
|
|
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) const
|
|
{
|
|
// Base fee is 1 cent per kilobyte
|
|
unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK);
|
|
unsigned int nNewBlockSize = nBlockSize + nBytes;
|
|
int64 nMinFee = (1 + (int64)nBytes / 1000) * CENT;
|
|
|
|
if (fAllowFree)
|
|
{
|
|
if (nBlockSize == 1)
|
|
{
|
|
// Transactions under 10K are free
|
|
// (about 4500bc if made of 50bc inputs)
|
|
if (nBytes < 10000)
|
|
nMinFee = 0;
|
|
}
|
|
else
|
|
{
|
|
// Free transaction area
|
|
if (nNewBlockSize < 27000)
|
|
nMinFee = 0;
|
|
}
|
|
}
|
|
|
|
// To limit dust spam, require a 0.01 fee if any output is less than 0.01
|
|
if (nMinFee < CENT)
|
|
foreach(const CTxOut& txout, vout)
|
|
if (txout.nValue < CENT)
|
|
nMinFee = CENT;
|
|
|
|
// Raise the price as the block approaches full
|
|
if (nBlockSize != 1 && nNewBlockSize >= MAX_BLOCK_SIZE_GEN/2)
|
|
{
|
|
if (nNewBlockSize >= MAX_BLOCK_SIZE_GEN)
|
|
return MAX_MONEY;
|
|
nMinFee *= MAX_BLOCK_SIZE_GEN / (MAX_BLOCK_SIZE_GEN - nNewBlockSize);
|
|
}
|
|
|
|
if (!MoneyRange(nMinFee))
|
|
nMinFee = MAX_MONEY;
|
|
return nMinFee;
|
|
}
|
|
|
|
|
|
bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL)
|
|
{
|
|
CAutoFile filein = OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb");
|
|
if (!filein)
|
|
return error("CTransaction::ReadFromDisk() : OpenBlockFile failed");
|
|
|
|
// Read transaction
|
|
if (fseek(filein, pos.nTxPos, SEEK_SET) != 0)
|
|
return error("CTransaction::ReadFromDisk() : fseek failed");
|
|
filein >> *this;
|
|
|
|
// 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);
|
|
}
|
|
|
|
|
|
string ToString() const
|
|
{
|
|
string str;
|
|
str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%d, vout.size=%d, nLockTime=%d)\n",
|
|
GetHash().ToString().substr(0,10).c_str(),
|
|
nVersion,
|
|
vin.size(),
|
|
vout.size(),
|
|
nLockTime);
|
|
for (int i = 0; i < vin.size(); i++)
|
|
str += " " + vin[i].ToString() + "\n";
|
|
for (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);
|
|
bool ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
|
|
CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0);
|
|
bool ClientConnectInputs();
|
|
bool CheckTransaction() const;
|
|
bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
|
|
bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL)
|
|
{
|
|
CTxDB txdb("r");
|
|
return AcceptToMemoryPool(txdb, fCheckInputs, pfMissingInputs);
|
|
}
|
|
protected:
|
|
bool AddToMemoryPoolUnchecked();
|
|
public:
|
|
bool RemoveFromMemoryPool();
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// A transaction with a merkle branch linking it to the block chain
|
|
//
|
|
class CMerkleTx : public CTransaction
|
|
{
|
|
public:
|
|
uint256 hashBlock;
|
|
vector<uint256> vMerkleBranch;
|
|
int nIndex;
|
|
|
|
// memory only
|
|
mutable char 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(int& nHeightRet) const;
|
|
int GetDepthInMainChain() const { int nHeight; return GetDepthInMainChain(nHeight); }
|
|
bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
|
|
int GetBlocksToMaturity() const;
|
|
bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true);
|
|
bool AcceptToMemoryPool() { CTxDB txdb("r"); return AcceptToMemoryPool(txdb); }
|
|
};
|
|
|
|
|
|
|
|
|
|
//
|
|
// A transaction with a bunch of additional info that only the owner cares
|
|
// about. It includes any unrecorded transactions needed to link it back
|
|
// to the block chain.
|
|
//
|
|
class CWalletTx : public CMerkleTx
|
|
{
|
|
public:
|
|
vector<CMerkleTx> vtxPrev;
|
|
map<string, string> mapValue;
|
|
vector<pair<string, string> > vOrderForm;
|
|
unsigned int fTimeReceivedIsTxTime;
|
|
unsigned int nTimeReceived; // time received by this node
|
|
char fFromMe;
|
|
string strFromAccount;
|
|
vector<char> vfSpent;
|
|
|
|
// memory only
|
|
mutable char fDebitCached;
|
|
mutable char fCreditCached;
|
|
mutable char fAvailableCreditCached;
|
|
mutable char fChangeCached;
|
|
mutable int64 nDebitCached;
|
|
mutable int64 nCreditCached;
|
|
mutable int64 nAvailableCreditCached;
|
|
mutable int64 nChangeCached;
|
|
|
|
// memory only UI hints
|
|
mutable unsigned int nTimeDisplayed;
|
|
mutable int nLinesDisplayed;
|
|
mutable char fConfirmedDisplayed;
|
|
|
|
|
|
CWalletTx()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
CWalletTx(const CMerkleTx& txIn) : CMerkleTx(txIn)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
CWalletTx(const CTransaction& txIn) : CMerkleTx(txIn)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
vtxPrev.clear();
|
|
mapValue.clear();
|
|
vOrderForm.clear();
|
|
fTimeReceivedIsTxTime = false;
|
|
nTimeReceived = 0;
|
|
fFromMe = false;
|
|
strFromAccount.clear();
|
|
vfSpent.clear();
|
|
fDebitCached = false;
|
|
fCreditCached = false;
|
|
fAvailableCreditCached = false;
|
|
fChangeCached = false;
|
|
nDebitCached = 0;
|
|
nCreditCached = 0;
|
|
nAvailableCreditCached = 0;
|
|
nChangeCached = 0;
|
|
nTimeDisplayed = 0;
|
|
nLinesDisplayed = 0;
|
|
fConfirmedDisplayed = false;
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
CWalletTx* pthis = const_cast<CWalletTx*>(this);
|
|
if (fRead)
|
|
pthis->Init();
|
|
char fSpent = false;
|
|
|
|
if (!fRead)
|
|
{
|
|
pthis->mapValue["fromaccount"] = pthis->strFromAccount;
|
|
|
|
string str;
|
|
foreach(char f, vfSpent)
|
|
{
|
|
str += (f ? '1' : '0');
|
|
if (f)
|
|
fSpent = true;
|
|
}
|
|
pthis->mapValue["spent"] = str;
|
|
}
|
|
|
|
nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action);
|
|
READWRITE(vtxPrev);
|
|
READWRITE(mapValue);
|
|
READWRITE(vOrderForm);
|
|
READWRITE(fTimeReceivedIsTxTime);
|
|
READWRITE(nTimeReceived);
|
|
READWRITE(fFromMe);
|
|
READWRITE(fSpent);
|
|
|
|
if (fRead)
|
|
{
|
|
pthis->strFromAccount = pthis->mapValue["fromaccount"];
|
|
|
|
if (mapValue.count("spent"))
|
|
foreach(char c, pthis->mapValue["spent"])
|
|
pthis->vfSpent.push_back(c != '0');
|
|
else
|
|
pthis->vfSpent.assign(vout.size(), fSpent);
|
|
}
|
|
|
|
pthis->mapValue.erase("fromaccount");
|
|
pthis->mapValue.erase("version");
|
|
pthis->mapValue.erase("spent");
|
|
)
|
|
|
|
// marks certain txout's as spent
|
|
// returns true if any update took place
|
|
bool UpdateSpent(const vector<char>& vfNewSpent)
|
|
{
|
|
bool fReturn = false;
|
|
for (int i=0; i < vfNewSpent.size(); i++)
|
|
{
|
|
if (i == vfSpent.size())
|
|
break;
|
|
|
|
if (vfNewSpent[i] && !vfSpent[i])
|
|
{
|
|
vfSpent[i] = true;
|
|
fReturn = true;
|
|
fAvailableCreditCached = false;
|
|
}
|
|
}
|
|
return fReturn;
|
|
}
|
|
|
|
void MarkDirty()
|
|
{
|
|
fCreditCached = false;
|
|
fAvailableCreditCached = false;
|
|
fDebitCached = false;
|
|
fChangeCached = false;
|
|
}
|
|
|
|
void MarkSpent(unsigned int nOut)
|
|
{
|
|
if (nOut >= vout.size())
|
|
throw runtime_error("CWalletTx::MarkSpent() : nOut out of range");
|
|
vfSpent.resize(vout.size());
|
|
if (!vfSpent[nOut])
|
|
{
|
|
vfSpent[nOut] = true;
|
|
fAvailableCreditCached = false;
|
|
}
|
|
}
|
|
|
|
bool IsSpent(unsigned int nOut) const
|
|
{
|
|
if (nOut >= vout.size())
|
|
throw runtime_error("CWalletTx::IsSpent() : nOut out of range");
|
|
if (nOut >= vfSpent.size())
|
|
return false;
|
|
return (!!vfSpent[nOut]);
|
|
}
|
|
|
|
int64 GetDebit() const
|
|
{
|
|
if (vin.empty())
|
|
return 0;
|
|
if (fDebitCached)
|
|
return nDebitCached;
|
|
nDebitCached = CTransaction::GetDebit();
|
|
fDebitCached = true;
|
|
return nDebitCached;
|
|
}
|
|
|
|
int64 GetCredit(bool fUseCache=true) const
|
|
{
|
|
// Must wait until coinbase is safely deep enough in the chain before valuing it
|
|
if (IsCoinBase() && GetBlocksToMaturity() > 0)
|
|
return 0;
|
|
|
|
// GetBalance can assume transactions in mapWallet won't change
|
|
if (fUseCache && fCreditCached)
|
|
return nCreditCached;
|
|
nCreditCached = CTransaction::GetCredit();
|
|
fCreditCached = true;
|
|
return nCreditCached;
|
|
}
|
|
|
|
int64 GetAvailableCredit(bool fUseCache=true) const
|
|
{
|
|
// Must wait until coinbase is safely deep enough in the chain before valuing it
|
|
if (IsCoinBase() && GetBlocksToMaturity() > 0)
|
|
return 0;
|
|
|
|
if (fUseCache && fAvailableCreditCached)
|
|
return nAvailableCreditCached;
|
|
|
|
int64 nCredit = 0;
|
|
for (int i = 0; i < vout.size(); i++)
|
|
{
|
|
if (!IsSpent(i))
|
|
{
|
|
const CTxOut &txout = vout[i];
|
|
nCredit += txout.GetCredit();
|
|
if (!MoneyRange(nCredit))
|
|
throw runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
|
|
}
|
|
}
|
|
|
|
nAvailableCreditCached = nCredit;
|
|
fAvailableCreditCached = true;
|
|
return nCredit;
|
|
}
|
|
|
|
|
|
int64 GetChange() const
|
|
{
|
|
if (fChangeCached)
|
|
return nChangeCached;
|
|
nChangeCached = CTransaction::GetChange();
|
|
fChangeCached = true;
|
|
return nChangeCached;
|
|
}
|
|
|
|
void GetAmounts(int64& nGeneratedImmature, int64& nGeneratedMature, list<pair<string /* address */, int64> >& listReceived,
|
|
list<pair<string /* address */, int64> >& listSent, int64& nFee, string& strSentAccount) const;
|
|
|
|
void GetAccountAmounts(const string& strAccount, int64& nGenerated, int64& nReceived,
|
|
int64& nSent, int64& nFee) const;
|
|
|
|
bool IsFromMe() const
|
|
{
|
|
return (GetDebit() > 0);
|
|
}
|
|
|
|
bool IsConfirmed() const
|
|
{
|
|
// Quick answer in most cases
|
|
if (!IsFinal())
|
|
return false;
|
|
if (GetDepthInMainChain() >= 1)
|
|
return true;
|
|
if (!IsFromMe()) // using wtx's cached debit
|
|
return false;
|
|
|
|
// If no confirmations but it's from us, we can still
|
|
// consider it confirmed if all dependencies are confirmed
|
|
map<uint256, const CMerkleTx*> mapPrev;
|
|
vector<const CMerkleTx*> vWorkQueue;
|
|
vWorkQueue.reserve(vtxPrev.size()+1);
|
|
vWorkQueue.push_back(this);
|
|
for (int i = 0; i < vWorkQueue.size(); i++)
|
|
{
|
|
const CMerkleTx* ptx = vWorkQueue[i];
|
|
|
|
if (!ptx->IsFinal())
|
|
return false;
|
|
if (ptx->GetDepthInMainChain() >= 1)
|
|
continue;
|
|
if (!ptx->IsFromMe())
|
|
return false;
|
|
|
|
if (mapPrev.empty())
|
|
foreach(const CMerkleTx& tx, vtxPrev)
|
|
mapPrev[tx.GetHash()] = &tx;
|
|
|
|
foreach(const CTxIn& txin, ptx->vin)
|
|
{
|
|
if (!mapPrev.count(txin.prevout.hash))
|
|
return false;
|
|
vWorkQueue.push_back(mapPrev[txin.prevout.hash]);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool WriteToDisk()
|
|
{
|
|
return CWalletDB().WriteTx(GetHash(), *this);
|
|
}
|
|
|
|
|
|
int64 GetTxTime() const;
|
|
int GetRequestCount() const;
|
|
|
|
void AddSupportingTransactions(CTxDB& txdb);
|
|
|
|
bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true);
|
|
bool AcceptWalletTransaction() { CTxDB txdb("r"); return AcceptWalletTransaction(txdb); }
|
|
|
|
void RelayWalletTransaction(CTxDB& txdb);
|
|
void RelayWalletTransaction() { CTxDB txdb("r"); RelayWalletTransaction(txdb); }
|
|
};
|
|
|
|
|
|
|
|
|
|
//
|
|
// 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;
|
|
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
|
|
int nVersion;
|
|
uint256 hashPrevBlock;
|
|
uint256 hashMerkleRoot;
|
|
unsigned int nTime;
|
|
unsigned int nBits;
|
|
unsigned int nNonce;
|
|
|
|
// network and disk
|
|
vector<CTransaction> vtx;
|
|
|
|
// memory only
|
|
mutable vector<uint256> vMerkleTree;
|
|
|
|
|
|
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 = 1;
|
|
hashPrevBlock = 0;
|
|
hashMerkleRoot = 0;
|
|
nTime = 0;
|
|
nBits = 0;
|
|
nNonce = 0;
|
|
vtx.clear();
|
|
vMerkleTree.clear();
|
|
}
|
|
|
|
bool IsNull() const
|
|
{
|
|
return (nBits == 0);
|
|
}
|
|
|
|
uint256 GetHash() const
|
|
{
|
|
return Hash(BEGIN(nVersion), END(nNonce));
|
|
}
|
|
|
|
int64 GetBlockTime() const
|
|
{
|
|
return (int64)nTime;
|
|
}
|
|
|
|
int GetSigOpCount() const
|
|
{
|
|
int n = 0;
|
|
foreach(const CTransaction& tx, vtx)
|
|
n += tx.GetSigOpCount();
|
|
return n;
|
|
}
|
|
|
|
|
|
uint256 BuildMerkleTree() const
|
|
{
|
|
vMerkleTree.clear();
|
|
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 = 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());
|
|
}
|
|
|
|
vector<uint256> GetMerkleBranch(int nIndex) const
|
|
{
|
|
if (vMerkleTree.empty())
|
|
BuildMerkleTree();
|
|
vector<uint256> vMerkleBranch;
|
|
int j = 0;
|
|
for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
|
|
{
|
|
int i = min(nIndex^1, nSize-1);
|
|
vMerkleBranch.push_back(vMerkleTree[j+i]);
|
|
nIndex >>= 1;
|
|
j += nSize;
|
|
}
|
|
return vMerkleBranch;
|
|
}
|
|
|
|
static uint256 CheckMerkleBranch(uint256 hash, const vector<uint256>& vMerkleBranch, int nIndex)
|
|
{
|
|
if (nIndex == -1)
|
|
return 0;
|
|
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(unsigned int& nFileRet, unsigned int& nBlockPosRet)
|
|
{
|
|
// Open history file to append
|
|
CAutoFile fileout = AppendBlockFile(nFileRet);
|
|
if (!fileout)
|
|
return error("CBlock::WriteToDisk() : AppendBlockFile failed");
|
|
|
|
// Write index header
|
|
unsigned int nSize = fileout.GetSerializeSize(*this);
|
|
fileout << FLATDATA(pchMessageStart) << nSize;
|
|
|
|
// Write block
|
|
nBlockPosRet = ftell(fileout);
|
|
if (nBlockPosRet == -1)
|
|
return error("CBlock::WriteToDisk() : ftell failed");
|
|
fileout << *this;
|
|
|
|
// Flush stdio buffers and commit to disk before returning
|
|
fflush(fileout);
|
|
if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0)
|
|
{
|
|
#ifdef __WXMSW__
|
|
_commit(_fileno(fileout));
|
|
#else
|
|
fsync(fileno(fileout));
|
|
#endif
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true)
|
|
{
|
|
SetNull();
|
|
|
|
// Open history file to read
|
|
CAutoFile filein = OpenBlockFile(nFile, nBlockPos, "rb");
|
|
if (!filein)
|
|
return error("CBlock::ReadFromDisk() : OpenBlockFile failed");
|
|
if (!fReadTransactions)
|
|
filein.nType |= SER_BLOCKHEADERONLY;
|
|
|
|
// Read block
|
|
filein >> *this;
|
|
|
|
// 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=%d)\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 (int i = 0; i < vtx.size(); i++)
|
|
{
|
|
printf(" ");
|
|
vtx[i].print();
|
|
}
|
|
printf(" vMerkleTree: ");
|
|
for (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 ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true);
|
|
bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew);
|
|
bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos);
|
|
bool CheckBlock() const;
|
|
bool AcceptBlock();
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// 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;
|
|
unsigned int nFile;
|
|
unsigned int nBlockPos;
|
|
int nHeight;
|
|
CBigNum bnChainWork;
|
|
|
|
// block header
|
|
int nVersion;
|
|
uint256 hashMerkleRoot;
|
|
unsigned int nTime;
|
|
unsigned int nBits;
|
|
unsigned int nNonce;
|
|
|
|
|
|
CBlockIndex()
|
|
{
|
|
phashBlock = NULL;
|
|
pprev = NULL;
|
|
pnext = NULL;
|
|
nFile = 0;
|
|
nBlockPos = 0;
|
|
nHeight = 0;
|
|
bnChainWork = 0;
|
|
|
|
nVersion = 0;
|
|
hashMerkleRoot = 0;
|
|
nTime = 0;
|
|
nBits = 0;
|
|
nNonce = 0;
|
|
}
|
|
|
|
CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block)
|
|
{
|
|
phashBlock = NULL;
|
|
pprev = NULL;
|
|
pnext = NULL;
|
|
nFile = nFileIn;
|
|
nBlockPos = nBlockPosIn;
|
|
nHeight = 0;
|
|
bnChainWork = 0;
|
|
|
|
nVersion = block.nVersion;
|
|
hashMerkleRoot = block.hashMerkleRoot;
|
|
nTime = block.nTime;
|
|
nBits = block.nBits;
|
|
nNonce = block.nNonce;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
bool EraseBlockFromDisk()
|
|
{
|
|
// Open history file
|
|
CAutoFile fileout = OpenBlockFile(nFile, nBlockPos, "rb+");
|
|
if (!fileout)
|
|
return false;
|
|
|
|
// Overwrite with empty null block
|
|
CBlock block;
|
|
block.SetNull();
|
|
fileout << block;
|
|
|
|
return true;
|
|
}
|
|
|
|
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();
|
|
|
|
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();
|
|
}
|
|
|
|
|
|
|
|
string ToString() const
|
|
{
|
|
return strprintf("CBlockIndex(nprev=%08x, pnext=%08x, nFile=%d, nBlockPos=%-6d nHeight=%d, merkle=%s, hashBlock=%s)",
|
|
pprev, pnext, nFile, nBlockPos, 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(nFile);
|
|
READWRITE(nBlockPos);
|
|
READWRITE(nHeight);
|
|
|
|
// 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();
|
|
}
|
|
|
|
|
|
string ToString() const
|
|
{
|
|
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:
|
|
vector<uint256> vHave;
|
|
public:
|
|
|
|
CBlockLocator()
|
|
{
|
|
}
|
|
|
|
explicit CBlockLocator(const CBlockIndex* pindex)
|
|
{
|
|
Set(pindex);
|
|
}
|
|
|
|
explicit CBlockLocator(uint256 hashBlock)
|
|
{
|
|
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
|
|
if (mi != mapBlockIndex.end())
|
|
Set((*mi).second);
|
|
}
|
|
|
|
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;
|
|
foreach(const uint256& hash, vHave)
|
|
{
|
|
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
|
|
foreach(const uint256& hash, vHave)
|
|
{
|
|
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
|
|
foreach(const uint256& hash, vHave)
|
|
{
|
|
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;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Private key that includes an expiration date in case it never gets used.
|
|
//
|
|
class CWalletKey
|
|
{
|
|
public:
|
|
CPrivKey vchPrivKey;
|
|
int64 nTimeCreated;
|
|
int64 nTimeExpires;
|
|
string strComment;
|
|
//// todo: add something to note what created it (user, getnewaddress, change)
|
|
//// maybe should have a map<string, string> property map
|
|
|
|
CWalletKey(int64 nExpires=0)
|
|
{
|
|
nTimeCreated = (nExpires ? GetTime() : 0);
|
|
nTimeExpires = nExpires;
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
if (!(nType & SER_GETHASH))
|
|
READWRITE(nVersion);
|
|
READWRITE(vchPrivKey);
|
|
READWRITE(nTimeCreated);
|
|
READWRITE(nTimeExpires);
|
|
READWRITE(strComment);
|
|
)
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Account information.
|
|
// Stored in wallet with key "acc"+string account name
|
|
//
|
|
class CAccount
|
|
{
|
|
public:
|
|
vector<unsigned char> vchPubKey;
|
|
|
|
CAccount()
|
|
{
|
|
SetNull();
|
|
}
|
|
|
|
void SetNull()
|
|
{
|
|
vchPubKey.clear();
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
if (!(nType & SER_GETHASH))
|
|
READWRITE(nVersion);
|
|
READWRITE(vchPubKey);
|
|
)
|
|
};
|
|
|
|
|
|
|
|
//
|
|
// Internal transfers.
|
|
// Database key is acentry<account><counter>
|
|
//
|
|
class CAccountingEntry
|
|
{
|
|
public:
|
|
string strAccount;
|
|
int64 nCreditDebit;
|
|
int64 nTime;
|
|
string strOtherAccount;
|
|
string strComment;
|
|
|
|
CAccountingEntry()
|
|
{
|
|
SetNull();
|
|
}
|
|
|
|
void SetNull()
|
|
{
|
|
nCreditDebit = 0;
|
|
nTime = 0;
|
|
strAccount.clear();
|
|
strOtherAccount.clear();
|
|
strComment.clear();
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
if (!(nType & SER_GETHASH))
|
|
READWRITE(nVersion);
|
|
// Note: strAccount is serialized as part of the key, not here.
|
|
READWRITE(nCreditDebit);
|
|
READWRITE(nTime);
|
|
READWRITE(strOtherAccount);
|
|
READWRITE(strComment);
|
|
)
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// Alerts are for notifying old versions if they become too obsolete and
|
|
// need to upgrade. The message is displayed in the status bar.
|
|
// Alert messages are broadcast as a vector of signed data. Unserializing may
|
|
// not read the entire buffer if the alert is for a newer version, but older
|
|
// versions can still relay the original data.
|
|
//
|
|
class CUnsignedAlert
|
|
{
|
|
public:
|
|
int nVersion;
|
|
int64 nRelayUntil; // when newer nodes stop relaying to newer nodes
|
|
int64 nExpiration;
|
|
int nID;
|
|
int nCancel;
|
|
set<int> setCancel;
|
|
int nMinVer; // lowest version inclusive
|
|
int nMaxVer; // highest version inclusive
|
|
set<string> setSubVer; // empty matches all
|
|
int nPriority;
|
|
|
|
// Actions
|
|
string strComment;
|
|
string strStatusBar;
|
|
string strReserved;
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
READWRITE(this->nVersion);
|
|
nVersion = this->nVersion;
|
|
READWRITE(nRelayUntil);
|
|
READWRITE(nExpiration);
|
|
READWRITE(nID);
|
|
READWRITE(nCancel);
|
|
READWRITE(setCancel);
|
|
READWRITE(nMinVer);
|
|
READWRITE(nMaxVer);
|
|
READWRITE(setSubVer);
|
|
READWRITE(nPriority);
|
|
|
|
READWRITE(strComment);
|
|
READWRITE(strStatusBar);
|
|
READWRITE(strReserved);
|
|
)
|
|
|
|
void SetNull()
|
|
{
|
|
nVersion = 1;
|
|
nRelayUntil = 0;
|
|
nExpiration = 0;
|
|
nID = 0;
|
|
nCancel = 0;
|
|
setCancel.clear();
|
|
nMinVer = 0;
|
|
nMaxVer = 0;
|
|
setSubVer.clear();
|
|
nPriority = 0;
|
|
|
|
strComment.clear();
|
|
strStatusBar.clear();
|
|
strReserved.clear();
|
|
}
|
|
|
|
string ToString() const
|
|
{
|
|
string strSetCancel;
|
|
foreach(int n, setCancel)
|
|
strSetCancel += strprintf("%d ", n);
|
|
string strSetSubVer;
|
|
foreach(string str, setSubVer)
|
|
strSetSubVer += "\"" + str + "\" ";
|
|
return strprintf(
|
|
"CAlert(\n"
|
|
" nVersion = %d\n"
|
|
" nRelayUntil = %"PRI64d"\n"
|
|
" nExpiration = %"PRI64d"\n"
|
|
" nID = %d\n"
|
|
" nCancel = %d\n"
|
|
" setCancel = %s\n"
|
|
" nMinVer = %d\n"
|
|
" nMaxVer = %d\n"
|
|
" setSubVer = %s\n"
|
|
" nPriority = %d\n"
|
|
" strComment = \"%s\"\n"
|
|
" strStatusBar = \"%s\"\n"
|
|
")\n",
|
|
nVersion,
|
|
nRelayUntil,
|
|
nExpiration,
|
|
nID,
|
|
nCancel,
|
|
strSetCancel.c_str(),
|
|
nMinVer,
|
|
nMaxVer,
|
|
strSetSubVer.c_str(),
|
|
nPriority,
|
|
strComment.c_str(),
|
|
strStatusBar.c_str());
|
|
}
|
|
|
|
void print() const
|
|
{
|
|
printf("%s", ToString().c_str());
|
|
}
|
|
};
|
|
|
|
class CAlert : public CUnsignedAlert
|
|
{
|
|
public:
|
|
vector<unsigned char> vchMsg;
|
|
vector<unsigned char> vchSig;
|
|
|
|
CAlert()
|
|
{
|
|
SetNull();
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
READWRITE(vchMsg);
|
|
READWRITE(vchSig);
|
|
)
|
|
|
|
void SetNull()
|
|
{
|
|
CUnsignedAlert::SetNull();
|
|
vchMsg.clear();
|
|
vchSig.clear();
|
|
}
|
|
|
|
bool IsNull() const
|
|
{
|
|
return (nExpiration == 0);
|
|
}
|
|
|
|
uint256 GetHash() const
|
|
{
|
|
return SerializeHash(*this);
|
|
}
|
|
|
|
bool IsInEffect() const
|
|
{
|
|
return (GetAdjustedTime() < nExpiration);
|
|
}
|
|
|
|
bool Cancels(const CAlert& alert) const
|
|
{
|
|
if (!IsInEffect())
|
|
return false; // this was a no-op before 31403
|
|
return (alert.nID <= nCancel || setCancel.count(alert.nID));
|
|
}
|
|
|
|
bool AppliesTo(int nVersion, string strSubVerIn) const
|
|
{
|
|
return (IsInEffect() &&
|
|
nMinVer <= nVersion && nVersion <= nMaxVer &&
|
|
(setSubVer.empty() || setSubVer.count(strSubVerIn)));
|
|
}
|
|
|
|
bool AppliesToMe() const
|
|
{
|
|
return AppliesTo(VERSION, ::pszSubVer);
|
|
}
|
|
|
|
bool RelayTo(CNode* pnode) const
|
|
{
|
|
if (!IsInEffect())
|
|
return false;
|
|
// returns true if wasn't already contained in the set
|
|
if (pnode->setKnown.insert(GetHash()).second)
|
|
{
|
|
if (AppliesTo(pnode->nVersion, pnode->strSubVer) ||
|
|
AppliesToMe() ||
|
|
GetAdjustedTime() < nRelayUntil)
|
|
{
|
|
pnode->PushMessage("alert", *this);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CheckSignature()
|
|
{
|
|
CKey key;
|
|
if (!key.SetPubKey(ParseHex("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284")))
|
|
return error("CAlert::CheckSignature() : SetPubKey failed");
|
|
if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
|
|
return error("CAlert::CheckSignature() : verify signature failed");
|
|
|
|
// Now unserialize the data
|
|
CDataStream sMsg(vchMsg);
|
|
sMsg >> *(CUnsignedAlert*)this;
|
|
return true;
|
|
}
|
|
|
|
bool ProcessAlert();
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern map<uint256, CTransaction> mapTransactions;
|
|
extern map<uint256, CWalletTx> mapWallet;
|
|
extern vector<uint256> vWalletUpdated;
|
|
extern CCriticalSection cs_mapWallet;
|
|
extern map<vector<unsigned char>, CPrivKey> mapKeys;
|
|
extern map<uint160, vector<unsigned char> > mapPubKeys;
|
|
extern CCriticalSection cs_mapKeys;
|
|
extern CKey keyUser;
|