2059 lines
51 KiB
C++
2059 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.
|
|
#ifndef BITCOIN_MAIN_H
|
|
#define BITCOIN_MAIN_H
|
|
|
|
#include "bignum.h"
|
|
#include "net.h"
|
|
#include "key.h"
|
|
|
|
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 MIN_TX_FEE = 50000;
|
|
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 std::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 std::map<uint256, int> mapRequestCount;
|
|
extern CCriticalSection cs_mapRequestCount;
|
|
extern std::map<std::string, std::string> mapAddressBook;
|
|
extern CCriticalSection cs_mapAddressBook;
|
|
extern std::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) * MIN_TX_FEE;
|
|
|
|
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 MIN_TX_FEE if any output is less than 0.01
|
|
if (nMinFee < MIN_TX_FEE)
|
|
foreach(const CTxOut& txout, vout)
|
|
if (txout.nValue < CENT)
|
|
nMinFee = MIN_TX_FEE;
|
|
|
|
// 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;
|
|
|
|
#endif
|