6ff4388ffa
with live update of default address in main window, New... button on main window for creating new receiving address, made receiving address labels more visible, ask user before paying transaction fee, when sending to bitcoin address also use a bitcoin address for the change, added some event.Skip() to fix UI glitches -- version 0.2.4
1416 lines
36 KiB
C++
1416 lines
36 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_SIZE = 0x02000000;
|
|
static const int64 COIN = 100000000;
|
|
static const int64 CENT = 1000000;
|
|
static const int COINBASE_MATURITY = 100;
|
|
|
|
static const CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern CCriticalSection cs_main;
|
|
extern map<uint256, CBlockIndex*> mapBlockIndex;
|
|
extern const uint256 hashGenesisBlock;
|
|
extern CBlockIndex* pindexGenesisBlock;
|
|
extern int nBestHeight;
|
|
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;
|
|
|
|
// Settings
|
|
extern int fGenerateBitcoins;
|
|
extern int64 nTransactionFee;
|
|
extern CAddress addrIncoming;
|
|
extern int fLimitProcessors;
|
|
extern int nLimitProcessors;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool CheckDiskSpace(int64 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);
|
|
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);
|
|
int64 GetBalance();
|
|
bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CKey& keyRet, int64& nFeeRequiredRet);
|
|
bool CommitTransaction(CWalletTx& wtxNew, const CKey& key);
|
|
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);
|
|
void BitcoinMiner();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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,6).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.begin(), scriptSig.end(), false).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;
|
|
|
|
public:
|
|
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 (IsMine())
|
|
return nValue;
|
|
return 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,24).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(int64 nBlockTime=0) const
|
|
{
|
|
// Time based nLockTime implemented in 0.1.6,
|
|
// do not use time based until most 0.1.5 nodes have upgraded.
|
|
if (nLockTime == 0)
|
|
return true;
|
|
if (nBlockTime == 0)
|
|
nBlockTime = GetAdjustedTime();
|
|
if (nLockTime < (nLockTime < 500000000 ? nBestHeight : 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());
|
|
}
|
|
|
|
bool CheckTransaction() const
|
|
{
|
|
// Basic checks that don't depend on any context
|
|
if (vin.empty() || vout.empty())
|
|
return error("CTransaction::CheckTransaction() : vin or vout empty");
|
|
|
|
// Check for negative values
|
|
foreach(const CTxOut& txout, vout)
|
|
if (txout.nValue < 0)
|
|
return error("CTransaction::CheckTransaction() : txout.nValue negative");
|
|
|
|
if (IsCoinBase())
|
|
{
|
|
if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100)
|
|
return error("CTransaction::CheckTransaction() : coinbase script size");
|
|
}
|
|
else
|
|
{
|
|
foreach(const CTxIn& txin, vin)
|
|
if (txin.prevout.IsNull())
|
|
return error("CTransaction::CheckTransaction() : prevout is null");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsMine() const
|
|
{
|
|
foreach(const CTxOut& txout, vout)
|
|
if (txout.IsMine())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
int64 GetDebit() const
|
|
{
|
|
int64 nDebit = 0;
|
|
foreach(const CTxIn& txin, vin)
|
|
nDebit += txin.GetDebit();
|
|
return nDebit;
|
|
}
|
|
|
|
int64 GetCredit() const
|
|
{
|
|
int64 nCredit = 0;
|
|
foreach(const CTxOut& txout, vout)
|
|
nCredit += txout.GetCredit();
|
|
return nCredit;
|
|
}
|
|
|
|
int64 GetValueOut() const
|
|
{
|
|
int64 nValueOut = 0;
|
|
foreach(const CTxOut& txout, vout)
|
|
{
|
|
if (txout.nValue < 0)
|
|
throw runtime_error("CTransaction::GetValueOut() : negative value");
|
|
nValueOut += txout.nValue;
|
|
}
|
|
return nValueOut;
|
|
}
|
|
|
|
int64 GetMinFee(unsigned int nBlockSize=1) const
|
|
{
|
|
// Base fee is 1 cent per kilobyte
|
|
unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK);
|
|
int64 nMinFee = (1 + (int64)nBytes / 1000) * CENT;
|
|
|
|
// Transactions under 60K are free as long as block size is under 80K
|
|
// (about 27,000bc if made of 50bc inputs)
|
|
if (nBytes < 60000 && nBlockSize < 80000)
|
|
nMinFee = 0;
|
|
|
|
// Transactions under 3K are free as long as block size is under 200K
|
|
if (nBytes < 3000 && nBlockSize < 200000)
|
|
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;
|
|
|
|
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,6).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 DisconnectInputs(CTxDB& txdb);
|
|
bool ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx, int nHeight, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0);
|
|
bool ClientConnectInputs();
|
|
|
|
bool AcceptTransaction(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
|
|
|
|
bool AcceptTransaction(bool fCheckInputs=true, bool* pfMissingInputs=NULL)
|
|
{
|
|
CTxDB txdb("r");
|
|
return AcceptTransaction(txdb, fCheckInputs, pfMissingInputs);
|
|
}
|
|
|
|
protected:
|
|
bool AddToMemoryPool();
|
|
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 bool fMerkleVerified;
|
|
mutable bool fGetCreditCached;
|
|
mutable int64 nGetCreditCached;
|
|
|
|
|
|
CMerkleTx()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
CMerkleTx(const CTransaction& txIn) : CTransaction(txIn)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
hashBlock = 0;
|
|
nIndex = -1;
|
|
fMerkleVerified = false;
|
|
fGetCreditCached = false;
|
|
nGetCreditCached = 0;
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action);
|
|
nVersion = this->nVersion;
|
|
READWRITE(hashBlock);
|
|
READWRITE(vMerkleBranch);
|
|
READWRITE(nIndex);
|
|
)
|
|
|
|
int64 GetCredit(bool fUseCache=false) 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 && fGetCreditCached)
|
|
return nGetCreditCached;
|
|
nGetCreditCached = CTransaction::GetCredit();
|
|
fGetCreditCached = true;
|
|
return nGetCreditCached;
|
|
}
|
|
|
|
|
|
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 AcceptTransaction(CTxDB& txdb, bool fCheckInputs=true);
|
|
bool AcceptTransaction() { CTxDB txdb("r"); return AcceptTransaction(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;
|
|
char fSpent;
|
|
//// probably need to sign the order info so know it came from payer
|
|
|
|
// memory only UI hints
|
|
mutable unsigned int nTimeDisplayed;
|
|
mutable int nLinesDisplayed;
|
|
|
|
|
|
CWalletTx()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
CWalletTx(const CMerkleTx& txIn) : CMerkleTx(txIn)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
CWalletTx(const CTransaction& txIn) : CMerkleTx(txIn)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
fTimeReceivedIsTxTime = false;
|
|
nTimeReceived = 0;
|
|
fFromMe = false;
|
|
fSpent = false;
|
|
nTimeDisplayed = 0;
|
|
nLinesDisplayed = 0;
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion, ser_action);
|
|
nVersion = this->nVersion;
|
|
READWRITE(vtxPrev);
|
|
READWRITE(mapValue);
|
|
READWRITE(vOrderForm);
|
|
READWRITE(fTimeReceivedIsTxTime);
|
|
READWRITE(nTimeReceived);
|
|
READWRITE(fFromMe);
|
|
READWRITE(fSpent);
|
|
)
|
|
|
|
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)
|
|
{
|
|
if (a.pos != b.pos || a.vSpent.size() != b.vSpent.size())
|
|
return false;
|
|
for (int i = 0; i < a.vSpent.size(); i++)
|
|
if (a.vSpent[i] != b.vSpent[i])
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
friend bool operator!=(const CTxIndex& a, const CTxIndex& b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// 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));
|
|
}
|
|
|
|
|
|
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(bool fWriteTransactions, unsigned int& nFileRet, unsigned int& nBlockPosRet)
|
|
{
|
|
// Open history file to append
|
|
CAutoFile fileout = AppendBlockFile(nFileRet);
|
|
if (!fileout)
|
|
return error("CBlock::WriteToDisk() : AppendBlockFile failed");
|
|
if (!fWriteTransactions)
|
|
fileout.nType |= SER_BLOCKHEADERONLY;
|
|
|
|
// 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);
|
|
#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 (CBigNum().SetCompact(nBits) > bnProofOfWorkLimit)
|
|
return error("CBlock::ReadFromDisk() : nBits errors in block header");
|
|
if (GetHash() > CBigNum().SetCompact(nBits).getuint256())
|
|
return error("CBlock::ReadFromDisk() : GetHash() 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,16).c_str(),
|
|
nVersion,
|
|
hashPrevBlock.ToString().substr(0,16).c_str(),
|
|
hashMerkleRoot.ToString().substr(0,6).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,6).c_str());
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
int64 GetBlockValue(int64 nFees) const;
|
|
bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex);
|
|
bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex);
|
|
bool ReadFromDisk(const CBlockIndex* blockindex, bool fReadTransactions=true);
|
|
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;
|
|
|
|
// 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;
|
|
|
|
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;
|
|
|
|
nVersion = block.nVersion;
|
|
hashMerkleRoot = block.hashMerkleRoot;
|
|
nTime = block.nTime;
|
|
nBits = block.nBits;
|
|
nNonce = block.nNonce;
|
|
}
|
|
|
|
uint256 GetBlockHash() const
|
|
{
|
|
return *phashBlock;
|
|
}
|
|
|
|
bool IsInMainChain() const
|
|
{
|
|
return (pnext || this == pindexBest);
|
|
}
|
|
|
|
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
|
|
{
|
|
unsigned int pmedian[nMedianTimeSpan];
|
|
unsigned int* pbegin = &pmedian[nMedianTimeSpan];
|
|
unsigned int* pend = &pmedian[nMedianTimeSpan];
|
|
|
|
const CBlockIndex* pindex = this;
|
|
for (int i = 0; i < nMedianTimeSpan && pindex; i++, pindex = pindex->pprev)
|
|
*(--pbegin) = pindex->nTime;
|
|
|
|
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 nTime;
|
|
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,6).c_str(),
|
|
GetBlockHash().ToString().substr(0,16).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,16).c_str(),
|
|
hashNext.ToString().substr(0,16).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 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;
|
|
|
|
CWalletKey(int64 nTimeExpiresIn=0)
|
|
{
|
|
nTimeCreated = (nTimeExpiresIn ? GetTime() : 0);
|
|
nTimeExpires = nTimeExpiresIn;
|
|
}
|
|
|
|
IMPLEMENT_SERIALIZE
|
|
(
|
|
if (!(nType & SER_GETHASH))
|
|
READWRITE(nVersion);
|
|
READWRITE(vchPrivKey);
|
|
READWRITE(nTimeCreated);
|
|
READWRITE(nTimeExpires);
|
|
)
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|