aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/db.cpp245
-rw-r--r--src/db.h40
-rw-r--r--src/init.cpp8
-rw-r--r--src/main.cpp1091
-rw-r--r--src/main.h348
-rw-r--r--src/qt/transactiondesc.cpp7
-rw-r--r--src/rpcmining.cpp41
-rw-r--r--src/rpcrawtransaction.cpp87
-rw-r--r--src/script.cpp5
-rw-r--r--src/script.h3
-rw-r--r--src/test/DoS_tests.cpp10
-rw-r--r--src/test/script_P2SH_tests.cpp26
-rw-r--r--src/test/transaction_tests.cpp30
-rw-r--r--src/wallet.cpp63
-rw-r--r--src/wallet.h7
15 files changed, 887 insertions, 1124 deletions
diff --git a/src/db.cpp b/src/db.cpp
index 53be48cb0f..0a6ba3fb39 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -244,7 +244,7 @@ CDB::CDB(const char *pszFile, const char* pszMode) :
ret = pdb->open(NULL, // Txn pointer
fMockDb ? NULL : pszFile, // Filename
- "main", // Logical db name
+ fMockDb ? pszFile : "main", // Logical db name
DB_BTREE, // Database type
nFlags, // Flags
0);
@@ -273,7 +273,7 @@ CDB::CDB(const char *pszFile, const char* pszMode) :
static bool IsChainFile(std::string strFile)
{
- if (strFile == "blkindex.dat")
+ if (strFile == "coins.dat" || strFile == "chain.dat")
return true;
return false;
@@ -475,111 +475,66 @@ void CDBEnv::Flush(bool fShutdown)
//
-// CTxDB
+// CChainDB and CCoinsDB
//
-bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex)
-{
+bool CCoinsDB::HaveCoins(uint256 hash) {
assert(!fClient);
- txindex.SetNull();
- return Read(make_pair(string("tx"), hash), txindex);
+ return Exists(make_pair('c', hash));
}
-bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex)
-{
+bool CCoinsDB::ReadCoins(uint256 hash, CCoins &coins) {
assert(!fClient);
- return Write(make_pair(string("tx"), hash), txindex);
-}
-
-bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight)
-{
- assert(!fClient);
-
- // Add to tx index
- uint256 hash = tx.GetHash();
- CTxIndex txindex(pos, tx.vout.size());
- return Write(make_pair(string("tx"), hash), txindex);
+ return Read(make_pair('c', hash), coins);
}
-bool CTxDB::EraseTxIndex(const CTransaction& tx)
-{
- assert(!fClient);
- uint256 hash = tx.GetHash();
-
- return Erase(make_pair(string("tx"), hash));
-}
-
-bool CTxDB::ContainsTx(uint256 hash)
-{
+bool CCoinsDB::WriteCoins(uint256 hash, const CCoins &coins) {
assert(!fClient);
- return Exists(make_pair(string("tx"), hash));
+ if (coins.IsPruned())
+ return Erase(make_pair('c', hash));
+ else
+ return Write(make_pair('c', hash), coins);
}
-bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex)
+bool CChainDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
{
- assert(!fClient);
- tx.SetNull();
- if (!ReadTxIndex(hash, txindex))
- return false;
- return (tx.ReadFromDisk(txindex.pos));
+ return Write(make_pair('b', blockindex.GetBlockHash()), blockindex);
}
-bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx)
+bool CCoinsDB::ReadHashBestChain(uint256& hashBestChain)
{
- CTxIndex txindex;
- return ReadDiskTx(hash, tx, txindex);
+ return Read('B', hashBestChain);
}
-bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex)
+bool CCoinsDB::WriteHashBestChain(uint256 hashBestChain)
{
- return ReadDiskTx(outpoint.hash, tx, txindex);
+ return Write('B', hashBestChain);
}
-bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx)
+bool CChainDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork)
{
- CTxIndex txindex;
- return ReadDiskTx(outpoint.hash, tx, txindex);
+ return Read('I', bnBestInvalidWork);
}
-bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
+bool CChainDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork)
{
- return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);
+ return Write('I', bnBestInvalidWork);
}
-bool CTxDB::WriteBlockFileInfo(int nFile, const CBlockFileInfo &info) {
- return Write(make_pair(string("blockfile"), nFile), info);
+bool CChainDB::WriteBlockFileInfo(int nFile, const CBlockFileInfo &info) {
+ return Write(make_pair('f', nFile), info);
}
-bool CTxDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
- return Read(make_pair(string("blockfile"), nFile), info);
+bool CChainDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
+ return Read(make_pair('f', nFile), info);
}
-bool CTxDB::WriteLastBlockFile(int nFile) {
- return Write(string("lastblockfile"), nFile);
+bool CChainDB::WriteLastBlockFile(int nFile) {
+ return Write('l', nFile);
}
-bool CTxDB::ReadLastBlockFile(int &nFile) {
- return Read(string("lastblockfile"), nFile);
-}
-
-bool CTxDB::ReadHashBestChain(uint256& hashBestChain)
-{
- return Read(string("hashBestChain"), hashBestChain);
-}
-
-bool CTxDB::WriteHashBestChain(uint256 hashBestChain)
-{
- return Write(string("hashBestChain"), hashBestChain);
-}
-
-bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork)
-{
- return Read(string("bnBestInvalidWork"), bnBestInvalidWork);
-}
-
-bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork)
-{
- return Write(string("bnBestInvalidWork"), bnBestInvalidWork);
+bool CChainDB::ReadLastBlockFile(int &nFile) {
+ return Read('l', nFile);
}
CBlockIndex static * InsertBlockIndex(uint256 hash)
@@ -602,9 +557,9 @@ CBlockIndex static * InsertBlockIndex(uint256 hash)
return pindexNew;
}
-bool CTxDB::LoadBlockIndex()
+bool LoadBlockIndex(CCoinsDB &coindb, CChainDB &chaindb)
{
- if (!LoadBlockIndexGuts())
+ if (!chaindb.LoadBlockIndexGuts())
return false;
if (fRequestShutdown)
@@ -626,29 +581,39 @@ bool CTxDB::LoadBlockIndex()
}
// Load block file info
- ReadLastBlockFile(nLastBlockFile);
+ chaindb.ReadLastBlockFile(nLastBlockFile);
printf("LoadBlockIndex(): last block file = %i\n", nLastBlockFile);
- if (ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile))
+ if (chaindb.ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile))
printf("LoadBlockIndex(): last block file: %s\n", infoLastBlockFile.ToString().c_str());
// Load hashBestChain pointer to end of best chain
- if (!ReadHashBestChain(hashBestChain))
+ if (!coindb.ReadHashBestChain(hashBestChain))
{
if (pindexGenesisBlock == NULL)
return true;
return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded");
}
- if (!mapBlockIndex.count(hashBestChain))
+ std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hashBestChain);
+ if (it == mapBlockIndex.end()) {
return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index");
- pindexBest = mapBlockIndex[hashBestChain];
- nBestHeight = pindexBest->nHeight;
- bnBestChainWork = pindexBest->bnChainWork;
- printf("LoadBlockIndex(): hashBestChain=%s height=%d date=%s\n",
- hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
- DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
+ } else {
+ // set 'next' pointers in best chain
+ CBlockIndex *pindex = it->second;
+ while(pindex != NULL && pindex->pprev != NULL) {
+ CBlockIndex *pindexPrev = pindex->pprev;
+ pindexPrev->pnext = pindex;
+ pindex = pindexPrev;
+ }
+ pindexBest = it->second;
+ nBestHeight = pindexBest->nHeight;
+ bnBestChainWork = pindexBest->bnChainWork;
+ }
+ printf("LoadBlockIndex(): hashBestChain=%s height=%d date=%s\n",
+ hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
+ DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
// Load bnBestInvalidWork, OK if it doesn't exist
- ReadBestInvalidWork(bnBestInvalidWork);
+ chaindb.ReadBestInvalidWork(bnBestInvalidWork);
// Verify blocks in the best chain
int nCheckLevel = GetArg("-checklevel", 1);
@@ -664,7 +629,6 @@ bool CTxDB::LoadBlockIndex()
if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth)
break;
CBlock block;
- CDiskBlockPos blockPos = pindex->GetBlockPos();
if (!block.ReadFromDisk(pindex))
return error("LoadBlockIndex() : block.ReadFromDisk failed");
// check level 1: verify block validity
@@ -673,98 +637,12 @@ bool CTxDB::LoadBlockIndex()
printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
pindexFork = pindex->pprev;
}
- // check level 2: verify transaction index validity
- if (nCheckLevel>1)
- {
- BOOST_FOREACH(const CTransaction &tx, block.vtx)
- {
- uint256 hashTx = tx.GetHash();
- CTxIndex txindex;
- if (ReadTxIndex(hashTx, txindex))
- {
- // check level 3: checker transaction hashes
- if (nCheckLevel>2 || blockPos != txindex.pos.blockPos)
- {
- // either an error or a duplicate transaction
- CTransaction txFound;
- if (!txFound.ReadFromDisk(txindex.pos))
- {
- printf("LoadBlockIndex() : *** cannot read mislocated transaction %s\n", hashTx.ToString().c_str());
- pindexFork = pindex->pprev;
- }
- else
- if (txFound.GetHash() != hashTx) // not a duplicate tx
- {
- printf("LoadBlockIndex(): *** invalid tx position for %s\n", hashTx.ToString().c_str());
- pindexFork = pindex->pprev;
- }
- }
- // check level 4: check whether spent txouts were spent within the main chain
- unsigned int nOutput = 0;
- if (nCheckLevel>3)
- {
- BOOST_FOREACH(const CDiskTxPos &txpos, txindex.vSpent)
- {
- if (!txpos.IsNull())
- {
- // check level 6: check whether spent txouts were spent by a valid transaction that consume them
- if (nCheckLevel>5)
- {
- CTransaction txSpend;
- if (!txSpend.ReadFromDisk(txpos))
- {
- printf("LoadBlockIndex(): *** cannot read spending transaction of %s:%i from disk\n", hashTx.ToString().c_str(), nOutput);
- pindexFork = pindex->pprev;
- }
- else if (!txSpend.CheckTransaction())
- {
- printf("LoadBlockIndex(): *** spending transaction of %s:%i is invalid\n", hashTx.ToString().c_str(), nOutput);
- pindexFork = pindex->pprev;
- }
- else
- {
- bool fFound = false;
- BOOST_FOREACH(const CTxIn &txin, txSpend.vin)
- if (txin.prevout.hash == hashTx && txin.prevout.n == nOutput)
- fFound = true;
- if (!fFound)
- {
- printf("LoadBlockIndex(): *** spending transaction of %s:%i does not spend it\n", hashTx.ToString().c_str(), nOutput);
- pindexFork = pindex->pprev;
- }
- }
- }
- }
- nOutput++;
- }
- }
- }
- // check level 5: check whether all prevouts are marked spent
- if (nCheckLevel>4)
- {
- BOOST_FOREACH(const CTxIn &txin, tx.vin)
- {
- CTxIndex txindex;
- if (ReadTxIndex(txin.prevout.hash, txindex))
- if (txindex.vSpent.size()-1 < txin.prevout.n || txindex.vSpent[txin.prevout.n].IsNull())
- {
- printf("LoadBlockIndex(): *** found unspent prevout %s:%i in %s\n", txin.prevout.hash.ToString().c_str(), txin.prevout.n, hashTx.ToString().c_str());
- pindexFork = pindex->pprev;
- }
- }
- }
- }
- }
+ // TODO: stronger verifications
}
if (pindexFork && !fRequestShutdown)
{
- // Reorg back to the fork
- printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight);
- CBlock block;
- if (!block.ReadFromDisk(pindexFork))
- return error("LoadBlockIndex() : block.ReadFromDisk failed");
- CTxDB txdb;
- block.SetBestChain(txdb, pindexFork);
+ // TODO: reorg back
+ return error("LoadBlockIndex(): chain database corrupted");
}
return true;
@@ -772,7 +650,7 @@ bool CTxDB::LoadBlockIndex()
-bool CTxDB::LoadBlockIndexGuts()
+bool CChainDB::LoadBlockIndexGuts()
{
// Get database cursor
Dbc* pcursor = GetCursor();
@@ -786,7 +664,7 @@ bool CTxDB::LoadBlockIndexGuts()
// Read next record
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
if (fFlags == DB_SET_RANGE)
- ssKey << make_pair(string("blockindex"), uint256(0));
+ ssKey << make_pair('b', uint256(0));
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
fFlags = DB_NEXT;
@@ -798,9 +676,9 @@ bool CTxDB::LoadBlockIndexGuts()
// Unserialize
try {
- string strType;
- ssKey >> strType;
- if (strType == "blockindex" && !fRequestShutdown)
+ char chType;
+ ssKey >> chType;
+ if (chType == 'b' && !fRequestShutdown)
{
CDiskBlockIndex diskindex;
ssValue >> diskindex;
@@ -808,7 +686,6 @@ bool CTxDB::LoadBlockIndexGuts()
// Construct block index object
CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
- pindexNew->pnext = InsertBlockIndex(diskindex.hashNext);
pindexNew->nHeight = diskindex.nHeight;
pindexNew->pos = diskindex.pos;
pindexNew->nUndoPos = diskindex.nUndoPos;
diff --git a/src/db.h b/src/db.h
index 61b59060a0..dd8993d56d 100644
--- a/src/db.h
+++ b/src/db.h
@@ -17,10 +17,8 @@ class CAddress;
class CAddrMan;
class CBlockLocator;
class CDiskBlockIndex;
-class CDiskTxPos;
class CMasterKey;
class COutPoint;
-class CTxIndex;
class CWallet;
class CWalletTx;
@@ -316,39 +314,43 @@ public:
-/** Access to the transaction database (blkindex.dat) */
-class CTxDB : public CDB
+/** Access to the transaction database (coins.dat) */
+class CCoinsDB : public CDB
{
public:
- CTxDB(const char* pszMode="r+") : CDB("blkindex.dat", pszMode) { }
+ CCoinsDB(const char* pszMode="r+") : CDB("coins.dat", pszMode) { }
private:
- CTxDB(const CTxDB&);
- void operator=(const CTxDB&);
+ CCoinsDB(const CCoinsDB&);
+ void operator=(const CCoinsDB&);
public:
- bool ReadTxIndex(uint256 hash, CTxIndex& txindex);
- bool UpdateTxIndex(uint256 hash, const CTxIndex& txindex);
- bool AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight);
- bool EraseTxIndex(const CTransaction& tx);
- bool ContainsTx(uint256 hash);
- bool ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex);
- bool ReadDiskTx(uint256 hash, CTransaction& tx);
- bool ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex);
- bool ReadDiskTx(COutPoint outpoint, CTransaction& tx);
- bool WriteBlockIndex(const CDiskBlockIndex& blockindex);
+ bool ReadCoins(uint256 hash, CCoins &coins);
+ bool WriteCoins(uint256 hash, const CCoins& coins);
+ bool HaveCoins(uint256 hash);
bool ReadHashBestChain(uint256& hashBestChain);
bool WriteHashBestChain(uint256 hashBestChain);
+};
+
+/** Access to the block database (chain.dat) */
+class CChainDB : public CDB
+{
+public:
+ CChainDB(const char* pszMode="r+") : CDB("chain.dat", pszMode) { }
+private:
+ CChainDB(const CChainDB&);
+ void operator=(const CChainDB&);
+public:
+ bool WriteBlockIndex(const CDiskBlockIndex& blockindex);
bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork);
bool WriteBestInvalidWork(CBigNum bnBestInvalidWork);
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo);
bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo);
bool ReadLastBlockFile(int &nFile);
bool WriteLastBlockFile(int nFile);
- bool LoadBlockIndex();
-private:
bool LoadBlockIndexGuts();
};
+bool LoadBlockIndex(CCoinsDB &coinsdb, CChainDB &chaindb);
/** Access to the (IP) address database (peers.dat) */
diff --git a/src/init.cpp b/src/init.cpp
index 92c752a8f5..56108cecee 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -638,14 +638,6 @@ bool AppInit2()
return InitError(msg);
}
- if (GetBoolArg("-loadblockindextest"))
- {
- CTxDB txdb("r");
- txdb.LoadBlockIndex();
- PrintBlockTree();
- return false;
- }
-
uiInterface.InitMessage(_("Loading block index..."));
printf("Loading block index...\n");
nStart = GetTimeMillis();
diff --git a/src/main.cpp b/src/main.cpp
index 616845e929..ef5f627d94 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -160,6 +160,99 @@ void static ResendWalletTransactions()
//////////////////////////////////////////////////////////////////////////////
//
+// CCoinsView implementations
+//
+
+bool CCoinsView::GetCoins(uint256 txid, CCoins &coins) { return false; }
+bool CCoinsView::SetCoins(uint256 txid, const CCoins &coins) { return false; }
+bool CCoinsView::HaveCoins(uint256 txid) { return false; }
+CBlockIndex *CCoinsView::GetBestBlock() { return NULL; }
+bool CCoinsView::SetBestBlock(CBlockIndex *pindex) { return false; }
+
+CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { }
+bool CCoinsViewBacked::GetCoins(uint256 txid, CCoins &coins) { return base->GetCoins(txid, coins); }
+bool CCoinsViewBacked::SetCoins(uint256 txid, const CCoins &coins) { return base->SetCoins(txid, coins); }
+bool CCoinsViewBacked::HaveCoins(uint256 txid) { return base->HaveCoins(txid); }
+CBlockIndex *CCoinsViewBacked::GetBestBlock() { return base->GetBestBlock(); }
+bool CCoinsViewBacked::SetBestBlock(CBlockIndex *pindex) { return base->SetBestBlock(pindex); }
+void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
+
+CCoinsViewDB::CCoinsViewDB(CCoinsDB &dbIn) : db(dbIn) {}
+bool CCoinsViewDB::GetCoins(uint256 txid, CCoins &coins) { return db.ReadCoins(txid, coins); }
+bool CCoinsViewDB::SetCoins(uint256 txid, const CCoins &coins) { return db.WriteCoins(txid, coins); }
+bool CCoinsViewDB::HaveCoins(uint256 txid) { return db.HaveCoins(txid); }
+CBlockIndex *CCoinsViewDB::GetBestBlock() { return pindexBest; }
+bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { return db.WriteHashBestChain(pindex->GetBlockHash()); }
+
+CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { }
+
+bool CCoinsViewCache::GetCoins(uint256 txid, CCoins &coins) {
+ if (cacheCoins.count(txid)) {
+ coins = cacheCoins[txid];
+ return true;
+ }
+ if (base->GetCoins(txid, coins)) {
+ cacheCoins[txid] = coins;
+ return true;
+ }
+ return false;
+}
+
+bool CCoinsViewCache::SetCoins(uint256 txid, const CCoins &coins) {
+ cacheCoins[txid] = coins;
+ return true;
+}
+
+bool CCoinsViewCache::HaveCoins(uint256 txid) {
+ return cacheCoins.count(txid) || base->HaveCoins(txid);
+}
+
+CBlockIndex *CCoinsViewCache::GetBestBlock() {
+ if (pindexTip == NULL)
+ pindexTip = base->GetBestBlock();
+ return pindexTip;
+}
+
+bool CCoinsViewCache::SetBestBlock(CBlockIndex *pindex) {
+ pindexTip = pindex;
+ return true;
+}
+
+bool CCoinsViewCache::Flush() {
+ for (std::map<uint256,CCoins>::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
+ if (!base->SetCoins(it->first, it->second))
+ return false;
+ }
+ if (!base->SetBestBlock(pindexTip))
+ return false;
+ cacheCoins.clear();
+ pindexTip = NULL;
+ return true;
+}
+
+/** CCoinsView that brings transactions from a memorypool into view.
+ It does not check for spendings by memory pool transactions. */
+CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
+
+bool CCoinsViewMemPool::GetCoins(uint256 txid, CCoins &coins) {
+ if (base->GetCoins(txid, coins))
+ return true;
+ if (mempool.exists(txid)) {
+ const CTransaction &tx = mempool.lookup(txid);
+ coins = CCoins(tx, MEMPOOL_HEIGHT);
+ return true;
+ }
+ return false;
+}
+
+bool CCoinsViewMemPool::HaveCoins(uint256 txid) {
+ return mempool.exists(txid) || base->HaveCoins(txid);
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
// mapOrphanTransactions
//
@@ -237,37 +330,9 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
//////////////////////////////////////////////////////////////////////////////
//
-// CTransaction and CTxIndex
+// CTransaction
//
-bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet)
-{
- SetNull();
- if (!txdb.ReadTxIndex(prevout.hash, txindexRet))
- return false;
- if (!ReadFromDisk(txindexRet.pos))
- return false;
- if (prevout.n >= vout.size())
- {
- SetNull();
- return false;
- }
- return true;
-}
-
-bool CTransaction::ReadFromDisk(CTxDB& txdb, COutPoint prevout)
-{
- CTxIndex txindex;
- return ReadFromDisk(txdb, prevout, txindex);
-}
-
-bool CTransaction::ReadFromDisk(COutPoint prevout)
-{
- CTxDB txdb("r");
- CTxIndex txindex;
- return ReadFromDisk(txdb, prevout, txindex);
-}
-
bool CTransaction::IsStandard() const
{
if (nVersion > CTransaction::CURRENT_VERSION)
@@ -303,7 +368,7 @@ bool CTransaction::IsStandard() const
// expensive-to-check-upon-redemption script like:
// DUP CHECKSIG DROP ... repeated 100 times... OP_1
//
-bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const
+bool CTransaction::AreInputsStandard(CCoinsView& mapInputs) const
{
if (IsCoinBase())
return true; // Coinbases don't use vin normally
@@ -383,17 +448,21 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
else
{
CBlock blockTmp;
- if (pblock == NULL)
- {
- // Load the block this tx is in
- CTxIndex txindex;
- if (!CTxDB("r").ReadTxIndex(GetHash(), txindex))
- return 0;
- if (!blockTmp.ReadFromDisk(txindex.pos.blockPos))
- return 0;
- pblock = &blockTmp;
+
+ if (pblock == NULL) {
+ CCoinsDB coinsdb("r");
+ CCoins coins;
+ if (coinsdb.ReadCoins(GetHash(), coins)) {
+ CBlockIndex *pindex = FindBlockByHeight(coins.nHeight);
+ if (pindex) {
+ if (!blockTmp.ReadFromDisk(pindex))
+ return 0;
+ pblock = &blockTmp;
+ }
+ }
}
+ if (pblock) {
// Update the tx's hashBlock
hashBlock = pblock->GetHash();
@@ -411,6 +480,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
// Fill in merkle branch
vMerkleBranch = pblock->GetMerkleBranch(nIndex);
+ }
}
// Is the tx in a block that's in the main chain
@@ -526,8 +596,20 @@ int64 CTransaction::GetMinFee(unsigned int nBlockSize, bool fAllowFree,
return nMinFee;
}
+void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
+{
+ LOCK(cs);
+
+ std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0));
-bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
+ // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx
+ while (it != mapNextTx.end() && it->first.hash == hashTx) {
+ coins.Spend(it->first.n); // and remove those outputs from coins
+ it++;
+ }
+}
+
+bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs,
bool* pfMissingInputs)
{
if (pfMissingInputs)
@@ -548,16 +630,13 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
if (!fTestNet && !tx.IsStandard())
return error("CTxMemPool::accept() : nonstandard transaction type");
- // Do we already have it?
+ // is it already in the memory pool?
uint256 hash = tx.GetHash();
{
LOCK(cs);
if (mapTx.count(hash))
return false;
}
- if (fCheckInputs)
- if (txdb.ContainsTx(hash))
- return false;
// Check for conflicts with in-memory transactions
CTransaction* ptxOld = NULL;
@@ -589,27 +668,32 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
if (fCheckInputs)
{
- MapPrevTx mapInputs;
- map<uint256, CTxIndex> mapUnused;
- bool fInvalid = false;
- if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid))
- {
- if (fInvalid)
- return error("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str());
- if (pfMissingInputs)
- *pfMissingInputs = true;
+ CCoinsViewDB viewDB(coinsdb);
+ CCoinsViewMemPool viewMemPool(viewDB, mempool);
+ CCoinsViewCache view(viewMemPool);
+
+ // do we already have it?
+ if (view.HaveCoins(hash))
return false;
+
+ // do all inputs exist?
+ BOOST_FOREACH(const CTxIn txin, tx.vin) {
+ if (!view.HaveCoins(txin.prevout.hash)) {
+ if (pfMissingInputs)
+ *pfMissingInputs = true;
+ return false;
+ }
}
// Check for non-standard pay-to-script-hash in inputs
- if (!tx.AreInputsStandard(mapInputs) && !fTestNet)
+ if (!tx.AreInputsStandard(view) && !fTestNet)
return error("CTxMemPool::accept() : nonstandard transaction input");
// Note: if you modify this code to accept non-standard transactions, then
// you should add code here to check that the transaction does a
// reasonable number of ECDSA signature verifications.
- int64 nFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
+ int64 nFees = tx.GetValueIn(view)-tx.GetValueOut();
unsigned int nSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
// Don't accept it if it can't get into a block
@@ -646,7 +730,7 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
- if (!tx.ConnectInputs(mapInputs, mapUnused, CDiskTxPos(true), pindexBest, false, false))
+ if (!tx.CheckInputs(view, CS_ALWAYS, true, false))
{
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
}
@@ -674,9 +758,9 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
return true;
}
-bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMissingInputs)
+bool CTransaction::AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs, bool* pfMissingInputs)
{
- return mempool.accept(txdb, *this, fCheckInputs, pfMissingInputs);
+ return mempool.accept(coinsdb, *this, fCheckInputs, pfMissingInputs);
}
bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
@@ -765,29 +849,29 @@ int CMerkleTx::GetBlocksToMaturity() const
}
-bool CMerkleTx::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs)
+bool CMerkleTx::AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs)
{
if (fClient)
{
- if (!IsInMainChain() && !ClientConnectInputs())
+ if (!IsInMainChain() && !ClientCheckInputs())
return false;
- return CTransaction::AcceptToMemoryPool(txdb, false);
+ return CTransaction::AcceptToMemoryPool(coinsdb, false);
}
else
{
- return CTransaction::AcceptToMemoryPool(txdb, fCheckInputs);
+ return CTransaction::AcceptToMemoryPool(coinsdb, fCheckInputs);
}
}
bool CMerkleTx::AcceptToMemoryPool()
{
- CTxDB txdb("r");
- return AcceptToMemoryPool(txdb);
+ CCoinsDB coinsdb("r");
+ return AcceptToMemoryPool(coinsdb);
}
-bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs)
+bool CWalletTx::AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs)
{
{
@@ -798,64 +882,65 @@ bool CWalletTx::AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs)
if (!tx.IsCoinBase())
{
uint256 hash = tx.GetHash();
- if (!mempool.exists(hash) && !txdb.ContainsTx(hash))
- tx.AcceptToMemoryPool(txdb, fCheckInputs);
+ if (!mempool.exists(hash) && !coinsdb.HaveCoins(hash))
+ tx.AcceptToMemoryPool(coinsdb, fCheckInputs);
}
}
- return AcceptToMemoryPool(txdb, fCheckInputs);
+ return AcceptToMemoryPool(coinsdb, fCheckInputs);
}
return false;
}
bool CWalletTx::AcceptWalletTransaction()
{
- CTxDB txdb("r");
- return AcceptWalletTransaction(txdb);
-}
-
-int CTxIndex::GetDepthInMainChain() const
-{
- // Read block header
- CBlock block;
- if (!block.ReadFromDisk(pos.blockPos, false))
- return 0;
- // Find the block in the index
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.GetHash());
- if (mi == mapBlockIndex.end())
- return 0;
- CBlockIndex* pindex = (*mi).second;
- if (!pindex || !pindex->IsInMainChain())
- return 0;
- return 1 + nBestHeight - pindex->nHeight;
+ CCoinsDB coinsdb("r");
+ return AcceptWalletTransaction(coinsdb);
}
// Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock
-bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock)
+bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow)
{
+ CBlockIndex *pindexSlow = NULL;
{
LOCK(cs_main);
{
LOCK(mempool.cs);
if (mempool.exists(hash))
{
- tx = mempool.lookup(hash);
+ txOut = mempool.lookup(hash);
return true;
}
}
- CTxDB txdb("r");
- CTxIndex txindex;
- if (tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex))
- {
- CBlock block;
- if (block.ReadFromDisk(txindex.pos.blockPos, false))
- hashBlock = block.GetHash();
- return true;
+
+ if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
+ int nHeight = -1;
+ {
+ CCoinsDB coindb("r");
+ CCoinsViewDB view(coindb);
+ CCoins coins;
+ if (view.GetCoins(hash, coins))
+ nHeight = coins.nHeight;
+ }
+ if (nHeight > 0)
+ pindexSlow = FindBlockByHeight(nHeight);
}
}
- return false;
-}
+ if (pindexSlow) {
+ CBlock block;
+ if (block.ReadFromDisk(pindexSlow)) {
+ BOOST_FOREACH(const CTransaction &tx, block.vtx) {
+ if (tx.GetHash() == hash) {
+ txOut = tx;
+ hashBlock = pindexSlow->GetBlockHash();
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
@@ -1051,7 +1136,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
if (pindexNew->bnChainWork > bnBestInvalidWork)
{
bnBestInvalidWork = pindexNew->bnChainWork;
- CTxDB().WriteBestInvalidWork(bnBestInvalidWork);
+ CChainDB().WriteBestInvalidWork(bnBestInvalidWork);
uiInterface.NotifyBlocksChanged();
}
printf("InvalidChainFound: invalid block=%s height=%d work=%s date=%s\n",
@@ -1084,145 +1169,35 @@ void CBlock::UpdateTime(const CBlockIndex* pindexPrev)
-bool CTransaction::DisconnectInputs(CTxDB& txdb)
-{
- // Relinquish previous transactions' spent pointers
- if (!IsCoinBase())
- {
- BOOST_FOREACH(const CTxIn& txin, vin)
- {
- COutPoint prevout = txin.prevout;
-
- // Get prev txindex from disk
- CTxIndex txindex;
- if (!txdb.ReadTxIndex(prevout.hash, txindex))
- return error("DisconnectInputs() : ReadTxIndex failed");
-
- if (prevout.n >= txindex.vSpent.size())
- return error("DisconnectInputs() : prevout.n out of range");
-
- // Mark outpoint as not spent
- txindex.vSpent[prevout.n].SetNull();
-
- // Write back
- if (!txdb.UpdateTxIndex(prevout.hash, txindex))
- return error("DisconnectInputs() : UpdateTxIndex failed");
- }
- }
-
- // Remove transaction from index
- // This can fail if a duplicate of this transaction was in a chain that got
- // reorganized away. This is only possible if this transaction was completely
- // spent, so erasing it would be a no-op anyway.
- txdb.EraseTxIndex(*this);
-
- return true;
-}
-
-
-bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool,
- bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid)
+CTxOut CTransaction::GetOutputFor(const CTxIn& input, CCoinsView& view)
{
- // FetchInputs can return false either because we just haven't seen some inputs
- // (in which case the transaction should be stored as an orphan)
- // or because the transaction is malformed (in which case the transaction should
- // be dropped). If tx is definitely invalid, fInvalid will be set to true.
- fInvalid = false;
-
- if (IsCoinBase())
- return true; // Coinbase transactions have no inputs to fetch.
-
- for (unsigned int i = 0; i < vin.size(); i++)
- {
- COutPoint prevout = vin[i].prevout;
- if (inputsRet.count(prevout.hash))
- continue; // Got it already
-
- // Read txindex
- CTxIndex& txindex = inputsRet[prevout.hash].first;
- bool fFound = true;
- if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
- {
- // Get txindex from current proposed changes
- txindex = mapTestPool.find(prevout.hash)->second;
- }
- else
- {
- // Read txindex from txdb
- fFound = txdb.ReadTxIndex(prevout.hash, txindex);
- }
- if (!fFound && (fBlock || fMiner))
- return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
-
- // Read txPrev
- CTransaction& txPrev = inputsRet[prevout.hash].second;
- if (!fFound || txindex.pos.IsMemPool())
- {
- // Get prev tx from single transactions in memory
- {
- LOCK(mempool.cs);
- if (!mempool.exists(prevout.hash))
- return error("FetchInputs() : %s mempool Tx prev not found %s", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
- txPrev = mempool.lookup(prevout.hash);
- }
- if (!fFound)
- txindex.vSpent.resize(txPrev.vout.size());
- }
- else
- {
- // Get prev tx from disk
- if (!txPrev.ReadFromDisk(txindex.pos))
- return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(), prevout.hash.ToString().substr(0,10).c_str());
- }
- }
-
- // Make sure all prevout.n indexes are valid:
- for (unsigned int i = 0; i < vin.size(); i++)
- {
- const COutPoint prevout = vin[i].prevout;
- assert(inputsRet.count(prevout.hash) != 0);
- const CTxIndex& txindex = inputsRet[prevout.hash].first;
- const CTransaction& txPrev = inputsRet[prevout.hash].second;
- if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
- {
- // Revisit this if/when transaction replacement is implemented and allows
- // adding inputs:
- fInvalid = true;
- return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %"PRIszu" %"PRIszu" prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
- }
- }
-
- return true;
-}
-
-const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const
-{
- MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash);
- if (mi == inputs.end())
+ CCoins coins;
+ if (!view.GetCoins(input.prevout.hash, coins))
throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found");
- const CTransaction& txPrev = (mi->second).second;
- if (input.prevout.n >= txPrev.vout.size())
- throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range");
+ if (input.prevout.n >= coins.vout.size())
+ throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range or already spent");
+
+ const CTxOut &out = coins.vout[input.prevout.n];
+ if (out.IsNull())
+ throw std::runtime_error("CTransaction::GetOutputFor() : already spent");
- return txPrev.vout[input.prevout.n];
+ return out;
}
-int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const
+int64 CTransaction::GetValueIn(CCoinsView& inputs) const
{
if (IsCoinBase())
return 0;
int64 nResult = 0;
for (unsigned int i = 0; i < vin.size(); i++)
- {
nResult += GetOutputFor(vin[i], inputs).nValue;
- }
- return nResult;
+ return nResult;
}
-unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const
+unsigned int CTransaction::GetP2SHSigOpCount(CCoinsView& inputs) const
{
if (IsCoinBase())
return 0;
@@ -1230,107 +1205,137 @@ unsigned int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const
unsigned int nSigOps = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
- const CTxOut& prevout = GetOutputFor(vin[i], inputs);
+ CTxOut prevout = GetOutputFor(vin[i], inputs);
if (prevout.scriptPubKey.IsPayToScriptHash())
nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig);
}
return nSigOps;
}
-bool CTransaction::ConnectInputs(MapPrevTx inputs,
- map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
- const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash)
+bool CTransaction::UpdateCoins(CCoinsView &inputs, CTxUndo &txundo, int nHeight) const
+{
+ uint256 hash = GetHash();
+
+ // mark inputs spent
+ if (!IsCoinBase()) {
+ BOOST_FOREACH(const CTxIn &txin, vin) {
+ CCoins coins;
+ if (!inputs.GetCoins(txin.prevout.hash, coins))
+ return error("UpdateCoins() : cannot find prevtx");
+ CTxInUndo undo;
+ if (!coins.Spend(txin.prevout, undo))
+ return error("UpdateCoins() : cannot spend input");
+ txundo.vprevout.push_back(undo);
+ if (!inputs.SetCoins(txin.prevout.hash, coins))
+ return error("UpdateCoins() : cannot update input");
+ }
+ }
+
+ // add outputs
+ if (!inputs.SetCoins(hash, CCoins(*this, nHeight)))
+ return error("UpdateCoins() : cannot update output");
+
+ return true;
+}
+
+bool CTransaction::HaveInputs(CCoinsView &inputs) const
+{
+ if (!IsCoinBase()) {
+ // first check whether information about the prevout hash is available
+ for (unsigned int i = 0; i < vin.size(); i++) {
+ const COutPoint &prevout = vin[i].prevout;
+ if (!inputs.HaveCoins(prevout.hash))
+ return false;
+ }
+
+ // then check whether the actual outputs are available
+ for (unsigned int i = 0; i < vin.size(); i++) {
+ const COutPoint &prevout = vin[i].prevout;
+ CCoins coins;
+ inputs.GetCoins(prevout.hash, coins);
+ if (!coins.IsAvailable(prevout.n))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool CTransaction::CheckInputs(CCoinsView &inputs, enum CheckSig_mode csmode, bool fStrictPayToScriptHash, bool fStrictEncodings) const
{
- // Take over previous transactions' spent pointers
- // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
- // fMiner is true when called from the internal bitcoin miner
- // ... both are false when called from CTransaction::AcceptToMemoryPool
if (!IsCoinBase())
{
int64 nValueIn = 0;
int64 nFees = 0;
for (unsigned int i = 0; i < vin.size(); i++)
{
- COutPoint prevout = vin[i].prevout;
- assert(inputs.count(prevout.hash) > 0);
- CTxIndex& txindex = inputs[prevout.hash].first;
- CTransaction& txPrev = inputs[prevout.hash].second;
+ const COutPoint &prevout = vin[i].prevout;
+ CCoins coins;
+ if (!inputs.GetCoins(prevout.hash, coins))
+ return error("CheckInputs() : cannot find prevout tx");
- if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
- return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %"PRIszu" %"PRIszu" prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
+ // Check for conflicts (double-spend)
+ // This doesn't trigger the DoS code on purpose; if it did, it would make it easier
+ // for an attacker to attempt to split the network.
+ if (!coins.IsAvailable(prevout.n))
+ return error("CheckInputs() : %s prev tx already used", GetHash().ToString().substr(0,10).c_str());
// If prev is coinbase, check that it's matured
- if (txPrev.IsCoinBase())
- for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev)
- if (pindex->GetBlockPos() == txindex.pos.blockPos)
- return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight);
+ if (coins.IsCoinBase()) {
+ CBlockIndex *pindexBlock = inputs.GetBestBlock();
+ if (pindexBlock->nHeight - coins.nHeight < COINBASE_MATURITY)
+ return error("CheckInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - coins.nHeight);
+ }
// Check for negative or overflow input values
- nValueIn += txPrev.vout[prevout.n].nValue;
- if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
- return DoS(100, error("ConnectInputs() : txin values out of range"));
+ nValueIn += coins.vout[prevout.n].nValue;
+ if (!MoneyRange(coins.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
+ return DoS(100, error("CheckInputs() : txin values out of range"));
}
+
+ if (nValueIn < GetValueOut())
+ return DoS(100, error("ChecktInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));
+
+ // Tally transaction fees
+ int64 nTxFee = nValueIn - GetValueOut();
+ if (nTxFee < 0)
+ return DoS(100, error("CheckInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
+ nFees += nTxFee;
+ if (!MoneyRange(nFees))
+ return DoS(100, error("CheckInputs() : nFees out of range"));
+
// The first loop above does all the inexpensive checks.
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
// Helps prevent CPU exhaustion attacks.
- for (unsigned int i = 0; i < vin.size(); i++)
- {
- COutPoint prevout = vin[i].prevout;
- assert(inputs.count(prevout.hash) > 0);
- CTxIndex& txindex = inputs[prevout.hash].first;
- CTransaction& txPrev = inputs[prevout.hash].second;
- // Check for conflicts (double-spend)
- // This doesn't trigger the DoS code on purpose; if it did, it would make it easier
- // for an attacker to attempt to split the network.
- if (!txindex.vSpent[prevout.n].IsNull())
- return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str());
+ // Skip ECDSA signature verification when connecting blocks
+ // before the last blockchain checkpoint. This is safe because block merkle hashes are
+ // still computed and checked, and any change will be caught at the next checkpoint.
+ if (csmode == CS_ALWAYS ||
+ (csmode == CS_AFTER_CHECKPOINT && inputs.GetBestBlock()->nHeight >= Checkpoints::GetTotalBlocksEstimate())) {
+ for (unsigned int i = 0; i < vin.size(); i++) {
+ const COutPoint &prevout = vin[i].prevout;
+ CCoins coins;
+ inputs.GetCoins(prevout.hash, coins);
- // Skip ECDSA signature verification when connecting blocks (fBlock=true)
- // before the last blockchain checkpoint. This is safe because block merkle hashes are
- // still computed and checked, and any change will be caught at the next checkpoint.
- if (!(fBlock && (nBestHeight < Checkpoints::GetTotalBlocksEstimate())))
- {
// Verify signature
- if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, false, 0))
- {
+ if (!VerifySignature(coins, *this, i, fStrictPayToScriptHash, fStrictEncodings, 0)) {
// only during transition phase for P2SH: do not invoke anti-DoS code for
// potentially old clients relaying bad P2SH transactions
- if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, false, 0))
- return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
+ if (fStrictPayToScriptHash && VerifySignature(coins, *this, i, false, fStrictEncodings, 0))
+ return error("CheckInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
- return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
+ return DoS(100,error("CheckInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
}
}
-
- // Mark outpoints as spent
- txindex.vSpent[prevout.n] = posThisTx;
-
- // Write back
- if (fBlock || fMiner)
- {
- mapTestPool[prevout.hash] = txindex;
- }
}
-
- if (nValueIn < GetValueOut())
- return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));
-
- // Tally transaction fees
- int64 nTxFee = nValueIn - GetValueOut();
- if (nTxFee < 0)
- return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
- nFees += nTxFee;
- if (!MoneyRange(nFees))
- return DoS(100, error("ConnectInputs() : nFees out of range"));
}
return true;
}
-bool CTransaction::ClientConnectInputs()
+bool CTransaction::ClientCheckInputs() const
{
if (IsCoinBase())
return false;
@@ -1351,7 +1356,7 @@ bool CTransaction::ClientConnectInputs()
return false;
// Verify signature
- if (!VerifySignature(txPrev, *this, i, true, false, 0))
+ if (!VerifySignature(CCoins(txPrev, -1), *this, i, true, false, 0))
return error("ConnectInputs() : VerifySignature failed");
///// this is redundant with the mempool.mapNextTx stuff,
@@ -1379,34 +1384,89 @@ bool CTransaction::ClientConnectInputs()
-bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex)
+bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
{
- // Disconnect in reverse order
- for (int i = vtx.size()-1; i >= 0; i--)
- if (!vtx[i].DisconnectInputs(txdb))
- return false;
+ assert(pindex == view.GetBestBlock());
- // Update block index on disk without changing it in memory.
- // The memory index structure will be changed after the db commits.
- if (pindex->pprev)
+ CBlockUndo blockUndo;
{
- CDiskBlockIndex blockindexPrev(pindex->pprev);
- blockindexPrev.hashNext = 0;
- if (!txdb.WriteBlockIndex(blockindexPrev))
- return error("DisconnectBlock() : WriteBlockIndex failed");
+ CDiskBlockPos pos = pindex->GetUndoPos();
+ if (pos.IsNull())
+ return error("DisconnectBlock() : no undo data available");
+ FILE *file = OpenUndoFile(pos, true);
+ if (file == NULL)
+ return error("DisconnectBlock() : undo file not available");
+ CAutoFile fileUndo(file, SER_DISK, CLIENT_VERSION);
+ fileUndo >> blockUndo;
}
+ assert(blockUndo.vtxundo.size() + 1 == vtx.size());
+
+ // undo transactions in reverse order
+ for (int i = vtx.size() - 1; i >= 0; i--) {
+ const CTransaction &tx = vtx[i];
+ uint256 hash = tx.GetHash();
+
+ // check that all outputs are available
+ CCoins outs;
+ if (!view.GetCoins(hash, outs))
+ return error("DisconnectBlock() : outputs still spent? database corrupted");
+
+ CCoins outsBlock = CCoins(tx, pindex->nHeight);
+ if (outs != outsBlock)
+ return error("DisconnectBlock() : added transaction mismatch? database corrupted");
+
+ // remove outputs
+ if (!view.SetCoins(hash, CCoins()))
+ return error("DisconnectBlock() : cannot delete coin outputs");
+
+ // restore inputs
+ if (i > 0) { // not coinbases
+ const CTxUndo &txundo = blockUndo.vtxundo[i-1];
+ assert(txundo.vprevout.size() == tx.vin.size());
+ for (unsigned int j = tx.vin.size(); j-- > 0;) {
+ const COutPoint &out = tx.vin[j].prevout;
+ const CTxInUndo &undo = txundo.vprevout[j];
+ CCoins coins;
+ view.GetCoins(out.hash, coins); // this can fail if the prevout was already entirely spent
+ if (coins.IsPruned()) {
+ if (undo.nHeight == 0)
+ return error("DisconnectBlock() : undo data doesn't contain tx metadata? database corrupted");
+ coins.fCoinBase = undo.fCoinBase;
+ coins.nHeight = undo.nHeight;
+ coins.nVersion = undo.nVersion;
+ } else {
+ if (undo.nHeight != 0)
+ return error("DisconnectBlock() : undo data contains unneeded tx metadata? database corrupted");
+ }
+ if (coins.IsAvailable(out.n))
+ return error("DisconnectBlock() : prevout output not spent? database corrupted");
+ if (coins.vout.size() < out.n+1)
+ coins.vout.resize(out.n+1);
+ coins.vout[out.n] = undo.txout;
+ if (!view.SetCoins(out.hash, coins))
+ return error("DisconnectBlock() : cannot restore coin inputs");
+ }
+ }
+ }
+
+ // move best block pointer to prevout block
+ view.SetBestBlock(pindex->pprev);
+
return true;
}
-bool FindUndoPos(CTxDB &txdb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
+bool FindUndoPos(CChainDB &chaindb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
-bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
+bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck)
{
// Check it again in case a previous version let a bad block in
if (!CheckBlock(!fJustCheck, !fJustCheck))
return false;
+ // verify that the view's current state corresponds to the previous block
+ assert(pindex->pprev == view.GetBestBlock());
+
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// unless those are already completely spent.
// If such overwrites are allowed, coinbases and transactions depending upon those
@@ -1421,115 +1481,81 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
// initial block download.
bool fEnforceBIP30 = !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) ||
(pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")));
+ if (fEnforceBIP30) {
+ BOOST_FOREACH(CTransaction& tx, vtx) {
+ uint256 hash = tx.GetHash();
+ CCoins coins;
+ if (view.GetCoins(hash, coins) && !coins.IsPruned())
+ return error("ConnectBlock() : tried to overwrite transaction");
+ }
+ }
// BIP16 didn't become active until Apr 1 2012
int64 nBIP16SwitchTime = 1333238400;
bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime);
- //// issue here: it doesn't know the version
- unsigned int nTxPos;
- if (fJustCheck)
- // Since we're just checking the block and not actually connecting it, it might not (and probably shouldn't) be on the disk to get the transaction from
- nTxPos = 1;
- else
- nTxPos = pindex->pos.nPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - 1 + GetSizeOfCompactSize(vtx.size());
-
CBlockUndo blockundo;
- map<uint256, CTxIndex> mapQueuedChanges;
int64 nFees = 0;
unsigned int nSigOps = 0;
BOOST_FOREACH(CTransaction& tx, vtx)
{
- uint256 hashTx = tx.GetHash();
-
- if (fEnforceBIP30) {
- CTxIndex txindexOld;
- if (txdb.ReadTxIndex(hashTx, txindexOld)) {
- BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent)
- if (pos.IsNull())
- return false;
- }
- }
-
nSigOps += tx.GetLegacySigOpCount();
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops"));
- CDiskTxPos posThisTx(pindex->GetBlockPos(), nTxPos);
- if (!fJustCheck)
- nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
- else
- posThisTx = CDiskTxPos(true);
-
- MapPrevTx mapInputs;
if (!tx.IsCoinBase())
{
- CTxUndo undo;
-
- bool fInvalid;
- if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid))
- return false;
+ if (!tx.HaveInputs(view))
+ return DoS(100, error("ConnectBlock() : inputs missing/spent"));
if (fStrictPayToScriptHash)
{
// Add in sigops done by pay-to-script-hash inputs;
// this is to prevent a "rogue miner" from creating
// an incredibly-expensive-to-validate block.
- nSigOps += tx.GetP2SHSigOpCount(mapInputs);
+ nSigOps += tx.GetP2SHSigOpCount(view);
if (nSigOps > MAX_BLOCK_SIGOPS)
- return DoS(100, error("ConnectBlock() : too many sigops"));
+ return DoS(100, error("ConnectBlock() : too many sigops"));
}
- nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut();
+ nFees += tx.GetValueIn(view)-tx.GetValueOut();
- BOOST_FOREACH(const CTxIn &in, tx.vin) {
- undo.vprevout.push_back(CTxInUndo(mapInputs[in.prevout.hash].second.vout[in.prevout.n], pindex->nHeight));
- }
-
- if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash))
+ if (!tx.CheckInputs(view, CS_AFTER_CHECKPOINT, fStrictPayToScriptHash, false))
return false;
-
- blockundo.vtxundo.push_back(undo);
}
- mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size());
+ CTxUndo txundo;
+ if (!tx.UpdateCoins(view, txundo, pindex->nHeight))
+ return error("ConnectBlock() : UpdateInputs failed");
+ if (!tx.IsCoinBase())
+ blockundo.vtxundo.push_back(txundo);
}
- if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees))
- return false;
-
if (fJustCheck)
return true;
- // Write queued txindex changes
- for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
- {
- if (!txdb.UpdateTxIndex((*mi).first, (*mi).second))
- return error("ConnectBlock() : UpdateTxIndex failed");
- }
-
// Write undo information to disk
- if (pindex->GetUndoPos().IsNull() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate())
+ if (pindex->GetUndoPos().IsNull())
{
+ CChainDB chaindb;
CDiskBlockPos pos;
- if (!FindUndoPos(txdb, pindex->pos.nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 8))
+ if (!FindUndoPos(chaindb, pindex->pos.nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 8))
return error("ConnectBlock() : FindUndoPos failed");
if (!blockundo.WriteToDisk(pos))
return error("ConnectBlock() : CBlockUndo::WriteToDisk failed");
- pindex->nUndoPos = pos.nPos;
- }
- // Update block index on disk without changing it in memory.
- // The memory index structure will be changed after the db commits.
- if (pindex->pprev)
- {
- CDiskBlockIndex blockindexPrev(pindex->pprev);
- blockindexPrev.hashNext = pindex->GetBlockHash();
- if (!txdb.WriteBlockIndex(blockindexPrev))
+ // update nUndoPos in block index
+ pindex->nUndoPos = pos.nPos + 1;
+ CDiskBlockIndex blockindex(pindex);
+ if (!chaindb.WriteBlockIndex(blockindex))
return error("ConnectBlock() : WriteBlockIndex failed");
}
+ // add this block to the view's blockchain
+ if (!view.SetBestBlock(pindex))
+ return false;
+
// Watch for transactions paying to me
BOOST_FOREACH(CTransaction& tx, vtx)
SyncWithWallets(tx, this, true);
@@ -1537,47 +1563,70 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
return true;
}
-bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
+bool CBlock::SetBestChain(CBlockIndex* pindexNew)
{
- printf("REORGANIZE\n");
+ // if this functions exits prematurely, the transaction is aborted
+ CCoinsDB coinsdb;
+ if (!coinsdb.TxnBegin())
+ return error("SetBestChain() : TxnBegin failed");
+
+ // special case for attaching the genesis block
+ // note that no ConnectBlock is called, so its coinbase output is non-spendable
+ if (pindexGenesisBlock == NULL && pindexNew->GetBlockHash() == hashGenesisBlock)
+ {
+ coinsdb.WriteHashBestChain(pindexNew->GetBlockHash());
+ if (!coinsdb.TxnCommit())
+ return error("SetBestChain() : TxnCommit failed");
+ pindexGenesisBlock = pindexNew;
+ pindexBest = pindexNew;
+ hashBestChain = pindexNew->GetBlockHash();
+ nBestHeight = pindexBest->nHeight;
+ bnBestChainWork = pindexNew->bnChainWork;
+ return true;
+ }
- // Find the fork
- CBlockIndex* pfork = pindexBest;
+ // create cached view to the coins database
+ CCoinsViewDB viewDB(coinsdb);
+ CCoinsViewCache view(viewDB);
+
+ // Find the fork (typically, there is none)
+ CBlockIndex* pfork = view.GetBestBlock();
CBlockIndex* plonger = pindexNew;
while (pfork != plonger)
{
while (plonger->nHeight > pfork->nHeight)
if (!(plonger = plonger->pprev))
- return error("Reorganize() : plonger->pprev is null");
+ return error("SetBestChain() : plonger->pprev is null");
if (pfork == plonger)
break;
if (!(pfork = pfork->pprev))
- return error("Reorganize() : pfork->pprev is null");
+ return error("SetBestChain() : pfork->pprev is null");
}
- // List of what to disconnect
+ // List of what to disconnect (typically nothing)
vector<CBlockIndex*> vDisconnect;
- for (CBlockIndex* pindex = pindexBest; pindex != pfork; pindex = pindex->pprev)
+ for (CBlockIndex* pindex = view.GetBestBlock(); pindex != pfork; pindex = pindex->pprev)
vDisconnect.push_back(pindex);
- // List of what to connect
+ // List of what to connect (typically only pindexNew)
vector<CBlockIndex*> vConnect;
for (CBlockIndex* pindex = pindexNew; pindex != pfork; pindex = pindex->pprev)
vConnect.push_back(pindex);
reverse(vConnect.begin(), vConnect.end());
- printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str());
- printf("REORGANIZE: Connect %"PRIszu" blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str());
+ if (vDisconnect.size() > 0) {
+ printf("REORGANIZE: Disconnect %"PRIszu" blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str());
+ printf("REORGANIZE: Connect %"PRIszu" blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str());
+ }
// Disconnect shorter branch
vector<CTransaction> vResurrect;
- BOOST_FOREACH(CBlockIndex* pindex, vDisconnect)
- {
+ BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) {
CBlock block;
if (!block.ReadFromDisk(pindex))
- return error("Reorganize() : ReadFromDisk for disconnect failed");
- if (!block.DisconnectBlock(txdb, pindex))
- return error("Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
+ return error("SetBestBlock() : ReadFromDisk for disconnect failed");
+ if (!block.DisconnectBlock(pindex, view))
+ return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
// Queue memory transactions to resurrect
BOOST_FOREACH(const CTransaction& tx, block.vtx)
@@ -1587,28 +1636,35 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
// Connect longer branch
vector<CTransaction> vDelete;
- for (unsigned int i = 0; i < vConnect.size(); i++)
- {
- CBlockIndex* pindex = vConnect[i];
+ BOOST_FOREACH(CBlockIndex *pindex, vConnect) {
CBlock block;
- if (!block.ReadFromDisk(pindex))
- return error("Reorganize() : ReadFromDisk for connect failed");
- if (!block.ConnectBlock(txdb, pindex))
- {
- // Invalid block
- return error("Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
+ CBlock *pblock;
+ if (pindex == pindexNew) // connecting *this block
+ pblock = this;
+ else { // other block; read it from disk
+ if (!block.ReadFromDisk(pindex))
+ return error("SetBestBlock() : ReadFromDisk for connect failed");
+ pblock = &block;
+ }
+ if (!pblock->ConnectBlock(pindex, view)) {
+ InvalidChainFound(pindexNew);
+ return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
}
// Queue memory transactions to delete
- BOOST_FOREACH(const CTransaction& tx, block.vtx)
+ BOOST_FOREACH(const CTransaction& tx, pblock->vtx)
vDelete.push_back(tx);
}
- if (!txdb.WriteHashBestChain(pindexNew->GetBlockHash()))
- return error("Reorganize() : WriteHashBestChain failed");
// Make sure it's successfully written to disk before changing memory structure
- if (!txdb.TxnCommit())
- return error("Reorganize() : TxnCommit failed");
+ if (!view.Flush())
+ return error("SetBestBlock() : failed to write coin changes");
+ if (!coinsdb.TxnCommit())
+ return error("SetBestBlock() : TxnCommit failed");
+ coinsdb.Close();
+
+ // At this point, all changes have been done to the database.
+ // Proceed by updating the memory structures.
// Disconnect shorter branch
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect)
@@ -1622,108 +1678,12 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew)
// Resurrect memory transactions that were in the disconnected branch
BOOST_FOREACH(CTransaction& tx, vResurrect)
- tx.AcceptToMemoryPool(txdb, false);
+ tx.AcceptToMemoryPool(coinsdb, false);
// Delete redundant memory transactions that are in the connected branch
BOOST_FOREACH(CTransaction& tx, vDelete)
mempool.remove(tx);
- printf("REORGANIZE: done\n");
-
- return true;
-}
-
-
-// Called from inside SetBestChain: attaches a block to the new best chain being built
-bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew)
-{
- uint256 hash = GetHash();
-
- // Adding to current best branch
- if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
- {
- txdb.TxnAbort();
- InvalidChainFound(pindexNew);
- return false;
- }
- if (!txdb.TxnCommit())
- return error("SetBestChain() : TxnCommit failed");
-
- // Add to current best branch
- pindexNew->pprev->pnext = pindexNew;
-
- // Delete redundant memory transactions
- BOOST_FOREACH(CTransaction& tx, vtx)
- mempool.remove(tx);
-
- return true;
-}
-
-bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
-{
- uint256 hash = GetHash();
-
- if (!txdb.TxnBegin())
- return error("SetBestChain() : TxnBegin failed");
-
- if (pindexGenesisBlock == NULL && hash == hashGenesisBlock)
- {
- txdb.WriteHashBestChain(hash);
- if (!txdb.TxnCommit())
- return error("SetBestChain() : TxnCommit failed");
- pindexGenesisBlock = pindexNew;
- }
- else if (hashPrevBlock == hashBestChain)
- {
- if (!SetBestChainInner(txdb, pindexNew))
- return error("SetBestChain() : SetBestChainInner failed");
- }
- else
- {
- // the first block in the new chain that will cause it to become the new best chain
- CBlockIndex *pindexIntermediate = pindexNew;
-
- // list of blocks that need to be connected afterwards
- std::vector<CBlockIndex*> vpindexSecondary;
-
- // Reorganize is costly in terms of db load, as it works in a single db transaction.
- // Try to limit how much needs to be done inside
- while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainWork > pindexBest->bnChainWork)
- {
- vpindexSecondary.push_back(pindexIntermediate);
- pindexIntermediate = pindexIntermediate->pprev;
- }
-
- if (!vpindexSecondary.empty())
- printf("Postponing %"PRIszu" reconnects\n", vpindexSecondary.size());
-
- // Switch to new best branch
- if (!Reorganize(txdb, pindexIntermediate))
- {
- txdb.TxnAbort();
- InvalidChainFound(pindexNew);
- return error("SetBestChain() : Reorganize failed");
- }
-
- // Connect further blocks
- BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary)
- {
- CBlock block;
- if (!block.ReadFromDisk(pindex))
- {
- printf("SetBestChain() : ReadFromDisk failed\n");
- break;
- }
- if (!txdb.TxnBegin()) {
- printf("SetBestChain() : TxnBegin 2 failed\n");
- break;
- }
- // errors now are not fatal, we still did a reorganisation to a new chain in a valid way
- if (!block.SetBestChainInner(txdb, pindex))
- break;
- }
- }
-
// Update best block in wallet (so we can detect restored wallets)
bool fIsInitialDownload = IsInitialBlockDownload();
if (!fIsInitialDownload)
@@ -1733,7 +1693,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
}
// New best block
- hashBestChain = hash;
+ hashBestChain = pindexNew->GetBlockHash();
pindexBest = pindexNew;
pblockindexFBBHLast = NULL;
nBestHeight = pindexBest->nHeight;
@@ -1797,19 +1757,19 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
pindexNew->pos = pos;
pindexNew->nUndoPos = 0;
- CTxDB txdb;
- if (!txdb.TxnBegin())
+ CChainDB chaindb;
+ if (!chaindb.TxnBegin())
return false;
- txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
- if (!txdb.TxnCommit())
+ chaindb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
+ if (!chaindb.TxnCommit())
return false;
// New best
- if (pindexNew->bnChainWork > bnBestChainWork)
- if (!SetBestChain(txdb, pindexNew))
- return false;
-
- txdb.Close();
+ if (pindexNew->bnChainWork > bnBestChainWork) {
+ if (!IsInitialBlockDownload() || (pindexNew->nHeight % 1) == 0)
+ if (!SetBestChain(pindexNew))
+ return false;
+ }
if (pindexNew == pindexBest)
{
@@ -1825,7 +1785,7 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
-bool FindBlockPos(CTxDB &txdb, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime)
+bool FindBlockPos(CChainDB &chaindb, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime)
{
bool fUpdatedLast = false;
@@ -1833,9 +1793,15 @@ bool FindBlockPos(CTxDB &txdb, CDiskBlockPos &pos, unsigned int nAddSize, unsign
while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str());
+ FILE *file = OpenBlockFile(pos);
+ FileCommit(file);
+ fclose(file);
+ file = OpenUndoFile(pos);
+ FileCommit(file);
+ fclose(file);
nLastBlockFile++;
infoLastBlockFile.SetNull();
- txdb.ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine
+ chaindb.ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine
fUpdatedLast = true;
}
@@ -1855,15 +1821,15 @@ bool FindBlockPos(CTxDB &txdb, CDiskBlockPos &pos, unsigned int nAddSize, unsign
fclose(file);
}
- if (!txdb.WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
+ if (!chaindb.WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
return error("FindBlockPos() : cannot write updated block info");
if (fUpdatedLast)
- txdb.WriteLastBlockFile(nLastBlockFile);
+ chaindb.WriteLastBlockFile(nLastBlockFile);
return true;
}
-bool FindUndoPos(CTxDB &txdb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
+bool FindUndoPos(CChainDB &chaindb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
{
pos.nFile = nFile;
@@ -1873,15 +1839,15 @@ bool FindUndoPos(CTxDB &txdb, int nFile, CDiskBlockPos &pos, unsigned int nAddSi
if (nFile == nLastBlockFile) {
pos.nPos = infoLastBlockFile.nUndoSize;
nNewSize = (infoLastBlockFile.nUndoSize += nAddSize);
- if (!txdb.WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
+ if (!chaindb.WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
return error("FindUndoPos() : cannot write updated block info");
} else {
CBlockFileInfo info;
- if (!txdb.ReadBlockFileInfo(nFile, info))
+ if (!chaindb.ReadBlockFileInfo(nFile, info))
return error("FindUndoPos() : cannot read block info");
pos.nPos = info.nUndoSize;
nNewSize = (info.nUndoSize += nAddSize);
- if (!txdb.WriteBlockFileInfo(nFile, info))
+ if (!chaindb.WriteBlockFileInfo(nFile, info))
return error("FindUndoPos() : cannot write updated block info");
}
@@ -2013,8 +1979,8 @@ bool CBlock::AcceptBlock()
return error("AcceptBlock() : out of disk space");
CDiskBlockPos blockPos;
{
- CTxDB txdb;
- if (!FindBlockPos(txdb, blockPos, nBlockSize+8, nHeight, nTime))
+ CChainDB chaindb;
+ if (!FindBlockPos(chaindb, blockPos, nBlockSize+8, nHeight, nTime))
return error("AcceptBlock() : FindBlockPos failed");
}
if (!WriteToDisk(blockPos))
@@ -2159,15 +2125,17 @@ int nLastBlockFile = 0;
FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
{
- if (pos.IsNull() || pos.IsMemPool())
+ if (pos.IsNull())
return NULL;
boost::filesystem::path path = GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile);
boost::filesystem::create_directories(path.parent_path());
FILE* file = fopen(path.string().c_str(), "rb+");
if (!file && !fReadOnly)
file = fopen(path.string().c_str(), "wb+");
- if (!file)
+ if (!file) {
+ printf("Unable to open file %s\n", path.string().c_str());
return NULL;
+ }
if (pos.nPos) {
if (fseek(file, pos.nPos, SEEK_SET)) {
printf("Unable to seek to position %u of %s\n", pos.nPos, path.string().c_str());
@@ -2200,10 +2168,12 @@ bool LoadBlockIndex(bool fAllowNew)
//
// Load block index
//
- CTxDB txdb("cr");
- if (!txdb.LoadBlockIndex())
+ CChainDB chaindb("cr");
+ CCoinsDB coinsdb("cr");
+ if (!LoadBlockIndex(coinsdb, chaindb))
return false;
- txdb.Close();
+ chaindb.Close();
+ coinsdb.Close();
//
// Init with genesis block
@@ -2256,8 +2226,8 @@ bool LoadBlockIndex(bool fAllowNew)
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
{
- CTxDB txdb;
- if (!FindBlockPos(txdb, blockPos, nBlockSize+8, 0, block.nTime))
+ CChainDB chaindb;
+ if (!FindBlockPos(chaindb, blockPos, nBlockSize+8, 0, block.nTime))
return error("AcceptBlock() : FindBlockPos failed");
}
if (!block.WriteToDisk(blockPos))
@@ -2316,7 +2286,7 @@ void PrintBlockTree()
// print item
CBlock block;
block.ReadFromDisk(pindex);
- printf("%d (blk%05u.dat:0x%lx) %s tx %"PRIszu"",
+ printf("%d (blk%05u.dat:0x%x) %s tx %"PRIszu"",
pindex->nHeight,
pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos,
DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(),
@@ -2522,22 +2492,20 @@ string GetWarnings(string strFor)
//
-bool static AlreadyHave(CTxDB& txdb, const CInv& inv)
+bool static AlreadyHave(CCoinsDB &coinsdb, const CInv& inv)
{
switch (inv.type)
{
case MSG_TX:
{
- bool txInMap = false;
+ bool txInMap = false;
{
- LOCK(mempool.cs);
- txInMap = (mempool.exists(inv.hash));
+ LOCK(mempool.cs);
+ txInMap = mempool.exists(inv.hash);
}
- return txInMap ||
- mapOrphanTransactions.count(inv.hash) ||
- txdb.ContainsTx(inv.hash);
+ return txInMap || mapOrphanTransactions.count(inv.hash) ||
+ coinsdb.HaveCoins(inv.hash);
}
-
case MSG_BLOCK:
return mapBlockIndex.count(inv.hash) ||
mapOrphanBlocks.count(inv.hash);
@@ -2780,7 +2748,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
break;
}
}
- CTxDB txdb("r");
+ CCoinsDB coinsdb("r");
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
{
const CInv &inv = vInv[nInv];
@@ -2789,7 +2757,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
return true;
pfrom->AddInventoryKnown(inv);
- bool fAlreadyHave = AlreadyHave(txdb, inv);
+ bool fAlreadyHave = AlreadyHave(coinsdb, inv);
if (fDebug)
printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new");
@@ -2961,7 +2929,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
vector<uint256> vWorkQueue;
vector<uint256> vEraseQueue;
CDataStream vMsg(vRecv);
- CTxDB txdb("r");
+ CCoinsDB coinsdb("r");
CTransaction tx;
vRecv >> tx;
@@ -2969,7 +2937,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
pfrom->AddInventoryKnown(inv);
bool fMissingInputs = false;
- if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs))
+ if (tx.AcceptToMemoryPool(coinsdb, true, &fMissingInputs))
{
SyncWithWallets(tx, NULL, true);
RelayMessage(inv, vMsg);
@@ -2991,7 +2959,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
CInv inv(MSG_TX, tx.GetHash());
bool fMissingInputs2 = false;
- if (tx.AcceptToMemoryPool(txdb, true, &fMissingInputs2))
+ if (tx.AcceptToMemoryPool(coinsdb, true, &fMissingInputs2))
{
printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
SyncWithWallets(tx, NULL, true);
@@ -3439,11 +3407,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
//
vector<CInv> vGetData;
int64 nNow = GetTime() * 1000000;
- CTxDB txdb("r");
+ CCoinsDB coinsdb("r");
while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
{
const CInv& inv = (*pto->mapAskFor.begin()).second;
- if (!AlreadyHave(txdb, inv))
+ if (!AlreadyHave(coinsdb, inv))
{
if (fDebugNet)
printf("sending getdata: %s\n", inv.ToString().c_str());
@@ -3653,7 +3621,9 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
int64 nFees = 0;
{
LOCK2(cs_main, mempool.cs);
- CTxDB txdb("r");
+ CCoinsDB coinsdb("r");
+ CCoinsViewDB viewdb(coinsdb);
+ CCoinsViewCache view(viewdb);
// Priority order to process transactions
list<COrphan> vOrphan; // list memory doesn't move
@@ -3675,9 +3645,8 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
// Read prev transaction
- CTransaction txPrev;
- CTxIndex txindex;
- if (!txPrev.ReadFromDisk(txdb, txin.prevout, txindex))
+ CCoins coins;
+ if (!view.GetCoins(txin.prevout.hash, coins))
{
// This should never happen; all transactions in the memory
// pool should connect to either transactions in the chain
@@ -3704,10 +3673,12 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue;
continue;
}
- int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
+
+ int64 nValueIn = coins.vout[txin.prevout.n].nValue;
nTotalIn += nValueIn;
- int nConf = txindex.GetDepthInMainChain();
+ int nConf = pindexPrev->nHeight - coins.nHeight;
+
dPriority += (double)nValueIn * nConf;
}
if (fMissingInputs) continue;
@@ -3731,7 +3702,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
}
// Collect transactions into block
- map<uint256, CTxIndex> mapTestPool;
uint64 nBlockSize = 1000;
uint64 nBlockTx = 0;
int nBlockSigOps = 100;
@@ -3750,6 +3720,9 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
vecPriority.pop_back();
+ // second layer cached modifications just for this transaction
+ CCoinsViewCache viewTemp(view, true);
+
// Size limits
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
if (nBlockSize + nTxSize >= nBlockMaxSize)
@@ -3774,24 +3747,21 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
}
- // Connecting shouldn't fail due to dependency on other memory pool transactions
- // because we're already processing them in order of dependency
- map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
- MapPrevTx mapInputs;
- bool fInvalid;
- if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid))
+ if (!tx.CheckInputs(viewTemp, CS_ALWAYS, true, false))
continue;
- int64 nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
+ int64 nTxFees = tx.GetValueIn(viewTemp)-tx.GetValueOut();
- nTxSigOps += tx.GetP2SHSigOpCount(mapInputs);
+ nTxSigOps += tx.GetP2SHSigOpCount(viewTemp);
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
- if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(true), pindexPrev, false, true))
+ CTxUndo txundo;
+ if (!tx.UpdateCoins(viewTemp, txundo, pindexPrev->nHeight+1))
continue;
- mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(true), tx.vout.size());
- swap(mapTestPool, mapTestPoolTmp);
+
+ // push changes from the second layer cache to the first one
+ viewTemp.Flush();
// Added
pblock->vtx.push_back(tx);
@@ -3829,19 +3799,20 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
nLastBlockSize = nBlockSize;
printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize);
- pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
-
- // Fill in header
- pblock->hashPrevBlock = pindexPrev->GetBlockHash();
- pblock->UpdateTime(pindexPrev);
- pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get());
- pblock->nNonce = 0;
+ pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
+ // Fill in header
+ pblock->hashPrevBlock = pindexPrev->GetBlockHash();
+ pblock->UpdateTime(pindexPrev);
+ pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get());
+ pblock->nNonce = 0;
pblock->vtx[0].vin[0].scriptSig = scriptDummy;
+
CBlockIndex indexDummy(*pblock);
indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1;
- if (!pblock->ConnectBlock(txdb, &indexDummy, true))
+ CCoinsViewCache viewNew(viewdb);
+ if (!pblock->ConnectBlock(&indexDummy, viewNew, true))
throw std::runtime_error("CreateNewBlock() : ConnectBlock failed");
}
diff --git a/src/main.h b/src/main.h
index 936a4e184d..a57fadac78 100644
--- a/src/main.h
+++ b/src/main.h
@@ -31,6 +31,7 @@ static const unsigned int MAX_INV_SZ = 50000;
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB
static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB
+static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF;
static const int64 MIN_TX_FEE = 50000;
static const int64 MIN_RELAY_TX_FEE = 10000;
static const int64 MAX_MONEY = 21000000 * COIN;
@@ -81,9 +82,12 @@ static const uint64 nMinDiskSpace = 52428800;
class CReserveKey;
-class CTxDB;
-class CTxIndex;
+class CCoinsDB;
+class CChainDB;
class CDiskBlockPos;
+class CCoins;
+class CTxUndo;
+class CCoinsView;
void RegisterWallet(CWallet* pwalletIn);
void UnregisterWallet(CWallet* pwalletIn);
@@ -108,8 +112,7 @@ unsigned int ComputeMinWork(unsigned int nBase, int64 nTime);
int GetNumBlocksOfPeers();
bool IsInitialBlockDownload();
std::string GetWarnings(std::string strFor);
-bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock);
-
+bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false);
@@ -143,62 +146,8 @@ public:
void SetNull() { nFile = -1; nPos = 0; }
bool IsNull() const { return (nFile == -1); }
-
- void SetMemPool() { nFile = -2; nPos = 0; }
- bool IsMemPool() const { return (nFile == -2); }
};
-/** Position on disk for a particular transaction. */
-class CDiskTxPos
-{
-public:
- CDiskBlockPos blockPos;
- unsigned int nTxPos;
-
- CDiskTxPos(bool fInMemPool = false)
- {
- SetNull();
- if (fInMemPool)
- blockPos.SetMemPool();
- }
-
- CDiskTxPos(const CDiskBlockPos &block, unsigned int nTxPosIn) : blockPos(block), nTxPos(nTxPosIn) { }
-
- IMPLEMENT_SERIALIZE(
- READWRITE(blockPos);
- READWRITE(VARINT(nTxPos));
- )
-
- void SetNull() { blockPos.SetNull(); nTxPos = 0; }
- bool IsNull() const { return (nTxPos == 0); }
- bool IsMemPool() const { return blockPos.IsMemPool(); }
-
- friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b)
- {
- return (a.blockPos == b.blockPos &&
- a.nTxPos == b.nTxPos);
- }
-
- friend bool operator!=(const CDiskTxPos& a, const CDiskTxPos& b)
- {
- return !(a == b);
- }
-
- std::string ToString() const
- {
- if (IsNull())
- return "null";
- else if (blockPos.IsMemPool())
- return "mempool";
- else
- return strprintf("\"blk%05i.dat:0x%x\"", blockPos.nFile, nTxPos);
- }
-
- void print() const
- {
- printf("%s", ToString().c_str());
- }
-};
@@ -413,7 +362,13 @@ enum GetMinFee_mode
GMF_SEND,
};
-typedef std::map<uint256, std::pair<CTxIndex, CTransaction> > MapPrevTx;
+// Modes for script/signature checking
+enum CheckSig_mode
+{
+ CS_NEVER, // never validate scripts
+ CS_AFTER_CHECKPOINT, // validate scripts after the last checkpoint
+ CS_ALWAYS // always validate scripts
+};
/** The basic transaction that is broadcasted on the network and contained in
* blocks. A transaction can contain multiple inputs and outputs.
@@ -525,7 +480,7 @@ public:
@return True if all inputs (scriptSigs) use only standard transaction forms
@see CTransaction::FetchInputs
*/
- bool AreInputsStandard(const MapPrevTx& mapInputs) const;
+ bool AreInputsStandard(CCoinsView& mapInputs) const;
/** Count ECDSA signature operations the old-fashioned (pre-0.6) way
@return number of sigops this transaction's outputs will produce when spent
@@ -539,7 +494,7 @@ public:
@return maximum number of sigops required to validate this transaction's inputs
@see CTransaction::FetchInputs
*/
- unsigned int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const;
+ unsigned int GetP2SHSigOpCount(CCoinsView& mapInputs) const;
/** Amount of bitcoins spent by this transaction.
@return sum of all outputs (note: does not include fees)
@@ -564,7 +519,7 @@ public:
@return Sum of value of all inputs (scriptSigs)
@see CTransaction::FetchInputs
*/
- int64 GetValueIn(const MapPrevTx& mapInputs) const;
+ int64 GetValueIn(CCoinsView& mapInputs) const;
static bool AllowFree(double dPriority)
{
@@ -575,33 +530,6 @@ public:
int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const;
- bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL)
- {
- CAutoFile filein = CAutoFile(OpenBlockFile(pos.blockPos, pfileRet==NULL), SER_DISK, CLIENT_VERSION);
- if (!filein)
- return error("CTransaction::ReadFromDisk() : OpenBlockFile failed");
-
- // Read transaction
- if (fseek(filein, pos.nTxPos, SEEK_SET) != 0)
- return error("CTransaction::ReadFromDisk() : fseek failed");
-
- try {
- filein >> *this;
- }
- catch (std::exception &e) {
- return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
- }
-
- // Return file pointer
- if (pfileRet)
- {
- if (fseek(filein, pos.nTxPos, SEEK_SET) != 0)
- return error("CTransaction::ReadFromDisk() : second fseek failed");
- *pfileRet = filein.release();
- }
- return true;
- }
-
friend bool operator==(const CTransaction& a, const CTransaction& b)
{
return (a.nVersion == b.nVersion &&
@@ -638,45 +566,27 @@ public:
}
- bool ReadFromDisk(CTxDB& txdb, COutPoint prevout, CTxIndex& txindexRet);
- bool ReadFromDisk(CTxDB& txdb, COutPoint prevout);
- bool ReadFromDisk(COutPoint prevout);
- bool DisconnectInputs(CTxDB& txdb);
+ // Do all possible client-mode checks
+ bool ClientCheckInputs() const;
- /** Fetch from memory and/or disk. inputsRet keys are transaction hashes.
+ // Check whether all prevouts of this transaction are present in the UTXO set represented by view
+ bool HaveInputs(CCoinsView &view) const;
- @param[in] txdb Transaction database
- @param[in] mapTestPool List of pending changes to the transaction index database
- @param[in] fBlock True if being called to add a new best-block to the chain
- @param[in] fMiner True if being called by CreateNewBlock
- @param[out] inputsRet Pointers to this transaction's inputs
- @param[out] fInvalid returns true if transaction is invalid
- @return Returns true if all inputs are in txdb or mapTestPool
- */
- bool FetchInputs(CTxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool,
- bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid);
-
- /** Sanity check previous transactions, then, if all checks succeed,
- mark them as spent by this transaction.
-
- @param[in] inputs Previous transactions (from FetchInputs)
- @param[out] mapTestPool Keeps track of inputs that need to be updated on disk
- @param[in] posThisTx Position of this transaction on disk
- @param[in] pindexBlock
- @param[in] fBlock true if called from ConnectBlock
- @param[in] fMiner true if called from CreateNewBlock
- @param[in] fStrictPayToScriptHash true if fully validating p2sh transactions
- @return Returns true if all checks succeed
- */
- bool ConnectInputs(MapPrevTx inputs,
- std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
- const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true);
- bool ClientConnectInputs();
+ // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
+ // This does not modify the UTXO set
+ bool CheckInputs(CCoinsView &view, enum CheckSig_mode csmode, bool fStrictPayToScriptHash=true, bool fStrictEncodings=true) const;
+
+ // Apply the effects of this transaction on the UTXO set represented by view
+ bool UpdateCoins(CCoinsView &view, CTxUndo &txundo, int nHeight) const;
+
+ // Context-independent validity checks
bool CheckTransaction() const;
- bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
+
+ // Try to accept this transaction into the memory pool
+ bool AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
protected:
- const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const;
+ static CTxOut GetOutputFor(const CTxIn& input, CCoinsView& mapInputs);
};
/** wrapper for CTxOut that provides a more compact serialization */
@@ -752,6 +662,7 @@ public:
class CTxUndo
{
public:
+ // undo information for all txins
std::vector<CTxInUndo> vprevout;
IMPLEMENT_SERIALIZE(
@@ -763,7 +674,7 @@ public:
class CBlockUndo
{
public:
- std::vector<CTxUndo> vtxundo;
+ std::vector<CTxUndo> vtxundo; // for all but the coinbase
IMPLEMENT_SERIALIZE(
READWRITE(vtxundo);
@@ -789,7 +700,7 @@ public:
// Flush stdio buffers and commit to disk before returning
fflush(fileout);
- if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0)
+ if (!IsInitialBlockDownload())
FileCommit(fileout);
return true;
@@ -1084,66 +995,16 @@ public:
int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
int GetBlocksToMaturity() const;
- bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true);
+ bool AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs=true);
bool AcceptToMemoryPool();
};
-/** A txdb record that contains the disk location of a transaction and the
- * locations of transactions that spend its outputs. vSpent is really only
- * used as a flag, but having the location is very helpful for debugging.
- */
-class CTxIndex
-{
-public:
- CDiskTxPos pos;
- std::vector<CDiskTxPos> vSpent;
- CTxIndex()
- {
- SetNull();
- }
- CTxIndex(const CDiskTxPos& posIn, unsigned int nOutputs)
- {
- pos = posIn;
- vSpent.resize(nOutputs);
- }
- IMPLEMENT_SERIALIZE
- (
- if (!(nType & SER_GETHASH))
- READWRITE(nVersion);
- READWRITE(pos);
- READWRITE(vSpent);
- )
-
- void SetNull()
- {
- pos.SetNull();
- vSpent.clear();
- }
-
- bool IsNull()
- {
- return pos.IsNull();
- }
-
- friend bool operator==(const CTxIndex& a, const CTxIndex& b)
- {
- return (a.pos == b.pos &&
- a.vSpent == b.vSpent);
- }
-
- friend bool operator!=(const CTxIndex& a, const CTxIndex& b)
- {
- return !(a == b);
- }
- int GetDepthInMainChain() const;
-
-};
@@ -1155,9 +1016,6 @@ public:
* 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
{
@@ -1305,7 +1163,7 @@ public:
// Flush stdio buffers and commit to disk before returning
fflush(fileout);
- if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0)
+ if (!IsInitialBlockDownload())
FileCommit(fileout);
return true;
@@ -1360,16 +1218,26 @@ public:
}
- bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex);
- bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck=false);
+ // Undo the effects of this block (with given index) on the UTXO set represented by coins
+ bool DisconnectBlock(CBlockIndex *pindex, CCoinsView &coins);
+
+ // Apply the effects of this block (with given index) on the UTXO set represented by coins
+ bool ConnectBlock(CBlockIndex *pindex, CCoinsView &coins, bool fJustCheck=false);
+
+ // Read a block from disk
bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true);
- bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew);
+
+ // Make this block (with given index) the new tip of the active block chain
+ bool SetBestChain(CBlockIndex* pindexNew);
+
+ // Add this block to the block index, and if necessary, switch the active block chain to this
bool AddToBlockIndex(const CDiskBlockPos &pos);
+
+ // Context-independent validity checks
bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const;
- bool AcceptBlock();
-private:
- bool SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew);
+ // Store block on disk
+ bool AcceptBlock();
};
@@ -1412,7 +1280,7 @@ public:
}
std::string ToString() const {
- return strprintf("CBlockFileInfo(blocks=%u, size=%lu, heights=%u..%u, time=%s..%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst).c_str(), DateTimeStrFormat("%Y-%m-%d", nTimeLast).c_str());
+ return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u..%u, time=%s..%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst).c_str(), DateTimeStrFormat("%Y-%m-%d", nTimeLast).c_str());
}
// update statistics (does not update nSize)
@@ -1466,7 +1334,7 @@ public:
pnext = NULL;
nHeight = 0;
pos.SetNull();
- nUndoPos = (unsigned int)(-1);
+ nUndoPos = 0;
bnChainWork = 0;
nVersion = 0;
@@ -1499,10 +1367,10 @@ public:
CDiskBlockPos GetUndoPos() const {
CDiskBlockPos ret = pos;
- if (nUndoPos == (unsigned int)(-1))
+ if (nUndoPos == 0)
ret.SetNull();
else
- ret.nPos = nUndoPos;
+ ret.nPos = nUndoPos - 1;
return ret;
}
@@ -1604,18 +1472,13 @@ class CDiskBlockIndex : public CBlockIndex
{
public:
uint256 hashPrev;
- uint256 hashNext;
- CDiskBlockIndex()
- {
+ CDiskBlockIndex() {
hashPrev = 0;
- hashNext = 0;
}
- explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex)
- {
+ explicit CDiskBlockIndex(CBlockIndex* pindex) : CBlockIndex(*pindex) {
hashPrev = (pprev ? pprev->GetBlockHash() : 0);
- hashNext = (pnext ? pnext->GetBlockHash() : 0);
}
IMPLEMENT_SERIALIZE
@@ -1623,7 +1486,6 @@ public:
if (!(nType & SER_GETHASH))
READWRITE(nVersion);
- READWRITE(hashNext);
READWRITE(nHeight);
READWRITE(pos);
READWRITE(nUndoPos);
@@ -1654,10 +1516,9 @@ public:
{
std::string str = "CDiskBlockIndex(";
str += CBlockIndex::ToString();
- str += strprintf("\n hashBlock=%s, hashPrev=%s, hashNext=%s)",
+ str += strprintf("\n hashBlock=%s, hashPrev=%s)",
GetBlockHash().ToString().c_str(),
- hashPrev.ToString().substr(0,20).c_str(),
- hashNext.ToString().substr(0,20).c_str());
+ hashPrev.ToString().substr(0,20).c_str());
return str;
}
@@ -1815,12 +1676,13 @@ public:
std::map<uint256, CTransaction> mapTx;
std::map<COutPoint, CInPoint> mapNextTx;
- bool accept(CTxDB& txdb, CTransaction &tx,
+ bool accept(CCoinsDB& coinsdb, CTransaction &tx,
bool fCheckInputs, bool* pfMissingInputs);
bool addUnchecked(const uint256& hash, CTransaction &tx);
bool remove(CTransaction &tx);
void clear();
void queryHashes(std::vector<uint256>& vtxid);
+ void pruneSpent(const uint256& hash, CCoins &coins);
unsigned long size()
{
@@ -1841,4 +1703,86 @@ public:
extern CTxMemPool mempool;
+/** Abstract view on the open txout dataset. */
+class CCoinsView
+{
+public:
+ // Retrieve the CCoins (unspent transaction outputs) for a given txid
+ virtual bool GetCoins(uint256 txid, CCoins &coins);
+
+ // Modify the CCoins for a given txid
+ virtual bool SetCoins(uint256 txid, const CCoins &coins);
+
+ // Just check whether we have data for a given txid.
+ // This may (but cannot always) return true for fully spent transactions
+ virtual bool HaveCoins(uint256 txid);
+
+ // Retrieve the block index whose state this CCoinsView currently represents
+ virtual CBlockIndex *GetBestBlock();
+
+ // Modify the currently active block index
+ virtual bool SetBestBlock(CBlockIndex *pindex);
+};
+
+/** CCoinsView backed by another CCoinsView */
+class CCoinsViewBacked : public CCoinsView
+{
+protected:
+ CCoinsView *base;
+
+public:
+ CCoinsViewBacked(CCoinsView &viewIn);
+ bool GetCoins(uint256 txid, CCoins &coins);
+ bool SetCoins(uint256 txid, const CCoins &coins);
+ bool HaveCoins(uint256 txid);
+ CBlockIndex *GetBestBlock();
+ bool SetBestBlock(CBlockIndex *pindex);
+ void SetBackend(CCoinsView &viewIn);
+};
+
+
+/** CCoinsView backed by a CCoinsDB */
+class CCoinsViewDB : public CCoinsView
+{
+protected:
+ CCoinsDB &db;
+public:
+ CCoinsViewDB(CCoinsDB &dbIn);
+ bool GetCoins(uint256 txid, CCoins &coins);
+ bool SetCoins(uint256 txid, const CCoins &coins);
+ bool HaveCoins(uint256 txid);
+ CBlockIndex *GetBestBlock();
+ bool SetBestBlock(CBlockIndex *pindex);
+};
+
+/** CCoinsView that adds a memory cache for transactions to another CCoinsView */
+class CCoinsViewCache : public CCoinsViewBacked
+{
+protected:
+ CBlockIndex *pindexTip;
+ std::map<uint256,CCoins> cacheCoins;
+
+public:
+ CCoinsViewCache(CCoinsView &baseIn, bool fDummy = false);
+ bool GetCoins(uint256 txid, CCoins &coins);
+ bool SetCoins(uint256 txid, const CCoins &coins);
+ bool HaveCoins(uint256 txid);
+ CBlockIndex *GetBestBlock();
+ bool SetBestBlock(CBlockIndex *pindex);
+ bool Flush();
+};
+
+/** CCoinsView that brings transactions from a memorypool into view.
+ It does not check for spendings by memory pool transactions. */
+class CCoinsViewMemPool : public CCoinsViewBacked
+{
+protected:
+ CTxMemPool &mempool;
+
+public:
+ CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn);
+ bool GetCoins(uint256 txid, CCoins &coins);
+ bool HaveCoins(uint256 txid);
+};
+
#endif
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index efc77e190e..dc840b9f8d 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -234,7 +234,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
- CTxDB txdb("r"); // To fetch source txouts
+ CCoinsDB coindb("r"); // To fetch source txouts
+ CCoinsViewDB coins(coindb);
strHTML += "<br><b>" + tr("Inputs") + ":</b>";
strHTML += "<ul>";
@@ -245,8 +246,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx)
{
COutPoint prevout = txin.prevout;
- CTransaction prev;
- if(txdb.ReadDiskTx(prevout.hash, prev))
+ CCoins prev;
+ if(coins.GetCoins(prevout.hash, prev))
{
if (prevout.n < prev.vout.size())
{
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index 96712482d5..96518c6d8c 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -198,7 +198,7 @@ Value getwork(const Array& params, bool fHelp)
Value getblocktemplate(const Array& params, bool fHelp)
{
- if (fHelp || params.size() > 1)
+ if (fHelp || params.size() != 1)
throw runtime_error(
"getblocktemplate [params]\n"
"Returns data needed to construct a block to work on:\n"
@@ -281,7 +281,9 @@ Value getblocktemplate(const Array& params, bool fHelp)
Array transactions;
map<uint256, int64_t> setTxIndex;
int i = 0;
- CTxDB txdb("r");
+ CCoinsDB coindb("r");
+ CCoinsViewDB viewdb(coindb);
+ CCoinsViewCache view(viewdb);
BOOST_FOREACH (CTransaction& tx, pblock->vtx)
{
uint256 txHash = tx.GetHash();
@@ -298,25 +300,21 @@ Value getblocktemplate(const Array& params, bool fHelp)
entry.push_back(Pair("hash", txHash.GetHex()));
- MapPrevTx mapInputs;
- map<uint256, CTxIndex> mapUnused;
- bool fInvalid = false;
- if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid))
+ Array deps;
+ BOOST_FOREACH (const CTxIn &in, tx.vin)
{
- entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut())));
-
- Array deps;
- BOOST_FOREACH (MapPrevTx::value_type& inp, mapInputs)
- {
- if (setTxIndex.count(inp.first))
- deps.push_back(setTxIndex[inp.first]);
- }
- entry.push_back(Pair("depends", deps));
+ if (setTxIndex.count(in.prevout.hash))
+ deps.push_back(setTxIndex[in.prevout.hash]);
+ }
+ entry.push_back(Pair("depends", deps));
- int64_t nSigOps = tx.GetLegacySigOpCount();
- nSigOps += tx.GetP2SHSigOpCount(mapInputs);
- entry.push_back(Pair("sigops", nSigOps));
+ int64_t nSigOps = tx.GetLegacySigOpCount();
+ if (tx.HaveInputs(view))
+ {
+ entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(view) - tx.GetValueOut())));
+ nSigOps += tx.GetP2SHSigOpCount(view);
}
+ entry.push_back(Pair("sigops", nSigOps));
transactions.push_back(entry);
}
@@ -364,18 +362,17 @@ Value submitblock(const Array& params, bool fHelp)
vector<unsigned char> blockData(ParseHex(params[0].get_str()));
CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION);
- CBlock block;
+ CBlock pblock;
try {
- ssBlock >> block;
+ ssBlock >> pblock;
}
catch (std::exception &e) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
}
- bool fAccepted = ProcessBlock(NULL, &block);
+ bool fAccepted = ProcessBlock(NULL, &pblock);
if (!fAccepted)
return "rejected";
return Value::null;
}
-
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index cb5bae62da..c62898316c 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -118,7 +118,7 @@ Value getrawtransaction(const Array& params, bool fHelp)
CTransaction tx;
uint256 hashBlock = 0;
- if (!GetTransaction(hash, tx, hashBlock))
+ if (!GetTransaction(hash, tx, hashBlock, true))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
@@ -335,26 +335,22 @@ Value signrawtransaction(const Array& params, bool fHelp)
bool fComplete = true;
// Fetch previous transactions (inputs):
- map<COutPoint, CScript> mapPrevOut;
- for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
+ CCoinsView viewDummy;
+ CCoinsViewCache view(viewDummy);
{
- CTransaction tempTx;
- MapPrevTx mapPrevTx;
- CTxDB txdb("r");
- map<uint256, CTxIndex> unused;
- bool fInvalid;
-
- // FetchInputs aborts on failure, so we go one at a time.
- tempTx.vin.push_back(mergedTx.vin[i]);
- tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
-
- // Copy results into mapPrevOut:
- BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
- {
+ LOCK(mempool.cs);
+ CCoinsDB coinsdb("r");
+ CCoinsViewDB viewDB(coinsdb);
+ CCoinsViewMemPool viewMempool(viewDB, mempool);
+ view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
+
+ BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
const uint256& prevHash = txin.prevout.hash;
- if (mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size()>txin.prevout.n)
- mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
+ CCoins coins;
+ view.GetCoins(prevHash, coins); // this is certainly allowed to fail
}
+
+ view.SetBackend(viewDummy); // switch back to avoid locking db/mempool too long
}
// Add previous txouts given in the RPC call:
@@ -386,20 +382,19 @@ Value signrawtransaction(const Array& params, bool fHelp)
vector<unsigned char> pkData(ParseHex(pkHex));
CScript scriptPubKey(pkData.begin(), pkData.end());
- COutPoint outpoint(txid, nOut);
- if (mapPrevOut.count(outpoint))
- {
- // Complain if scriptPubKey doesn't match
- if (mapPrevOut[outpoint] != scriptPubKey)
- {
+ CCoins coins;
+ if (view.GetCoins(txid, coins)) {
+ if (coins.IsAvailable(nOut) && coins.vout[nOut].scriptPubKey != scriptPubKey) {
string err("Previous output scriptPubKey mismatch:\n");
- err = err + mapPrevOut[outpoint].ToString() + "\nvs:\n"+
+ err = err + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
scriptPubKey.ToString();
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
}
+ // what todo if txid is known, but the actual output isn't?
}
- else
- mapPrevOut[outpoint] = scriptPubKey;
+ coins.vout[nOut].scriptPubKey = scriptPubKey;
+ coins.vout[nOut].nValue = 0; // we don't know the actual output value
+ view.SetCoins(txid, coins);
}
}
@@ -452,12 +447,13 @@ Value signrawtransaction(const Array& params, bool fHelp)
for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
{
CTxIn& txin = mergedTx.vin[i];
- if (mapPrevOut.count(txin.prevout) == 0)
+ CCoins coins;
+ if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n))
{
fComplete = false;
continue;
}
- const CScript& prevPubKey = mapPrevOut[txin.prevout];
+ const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey;
txin.scriptSig.clear();
// Only sign SIGHASH_SINGLE if there's a corresponding output:
@@ -505,24 +501,27 @@ Value sendrawtransaction(const Array& params, bool fHelp)
}
uint256 hashTx = tx.GetHash();
- // See if the transaction is already in a block
- // or in the memory pool:
- CTransaction existingTx;
- uint256 hashBlock = 0;
- if (GetTransaction(hashTx, existingTx, hashBlock))
+ bool fHave = false;
+ CCoins existingCoins;
{
- if (hashBlock != 0)
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("transaction already in block ")+hashBlock.GetHex());
+ CCoinsDB coinsdb("r");
+ {
+ CCoinsViewDB coinsviewDB(coinsdb);
+ CCoinsViewMemPool coinsview(coinsviewDB, mempool);
+ fHave = coinsview.GetCoins(hashTx, existingCoins);
+ }
+ if (!fHave) {
+ // push to local node
+ if (!tx.AcceptToMemoryPool(coinsdb))
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
+ }
+ }
+ if (fHave) {
+ if (existingCoins.nHeight < 1000000000)
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "transaction already in block chain");
// Not in block, but already in the memory pool; will drop
// through to re-relay it.
- }
- else
- {
- // push to local node
- CTxDB txdb("r");
- if (!tx.AcceptToMemoryPool(txdb))
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
-
+ } else {
SyncWithWallets(tx, NULL, true);
}
RelayMessage(CInv(MSG_TX, hashTx), tx);
diff --git a/src/script.cpp b/src/script.cpp
index a840bb1c0d..61112b17c0 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -1719,7 +1719,7 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans
return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType);
}
-bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType)
+bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType)
{
assert(nIn < txTo.vin.size());
const CTxIn& txin = txTo.vin[nIn];
@@ -1727,9 +1727,6 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig
return false;
const CTxOut& txout = txFrom.vout[txin.prevout.n];
- if (txin.prevout.hash != txFrom.GetHash())
- return false;
-
return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, fStrictEncodings, nHashType);
}
diff --git a/src/script.h b/src/script.h
index e7b52d95e1..f9df587ca3 100644
--- a/src/script.h
+++ b/src/script.h
@@ -14,6 +14,7 @@
#include "keystore.h"
#include "bignum.h"
+class CCoins;
class CTransaction;
/** Signature hash types/flags */
@@ -667,7 +668,7 @@ bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransa
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType);
-bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType);
+bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, bool fStrictEncodings, int nHashType);
// Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders,
// combine them intelligently and return the result.
diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp
index d469593120..8235b0bda7 100644
--- a/src/test/DoS_tests.cpp
+++ b/src/test/DoS_tests.cpp
@@ -278,7 +278,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig)
mst1 = boost::posix_time::microsec_clock::local_time();
for (unsigned int i = 0; i < 5; i++)
for (unsigned int j = 0; j < tx.vin.size(); j++)
- BOOST_CHECK(VerifySignature(orphans[j], tx, j, true, true, SIGHASH_ALL));
+ BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, true, true, SIGHASH_ALL));
mst2 = boost::posix_time::microsec_clock::local_time();
msdiff = mst2 - mst1;
long nManyValidate = msdiff.total_milliseconds();
@@ -289,13 +289,13 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig)
// Empty a signature, validation should fail:
CScript save = tx.vin[0].scriptSig;
tx.vin[0].scriptSig = CScript();
- BOOST_CHECK(!VerifySignature(orphans[0], tx, 0, true, true, SIGHASH_ALL));
+ BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, true, true, SIGHASH_ALL));
tx.vin[0].scriptSig = save;
// Swap signatures, validation should fail:
std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig);
- BOOST_CHECK(!VerifySignature(orphans[0], tx, 0, true, true, SIGHASH_ALL));
- BOOST_CHECK(!VerifySignature(orphans[1], tx, 1, true, true, SIGHASH_ALL));
+ BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, true, true, SIGHASH_ALL));
+ BOOST_CHECK(!VerifySignature(CCoins(orphans[1], MEMPOOL_HEIGHT), tx, 1, true, true, SIGHASH_ALL));
std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig);
// Exercise -maxsigcachesize code:
@@ -305,7 +305,7 @@ BOOST_AUTO_TEST_CASE(DoS_checkSig)
BOOST_CHECK(SignSignature(keystore, orphans[0], tx, 0));
BOOST_CHECK(tx.vin[0].scriptSig != oldSig);
for (unsigned int j = 0; j < tx.vin.size(); j++)
- BOOST_CHECK(VerifySignature(orphans[j], tx, j, true, true, SIGHASH_ALL));
+ BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, true, true, SIGHASH_ALL));
mapArgs.erase("-maxsigcachesize");
LimitOrphanTxSize(0);
diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp
index 9db584c147..35069a3fdd 100644
--- a/src/test/script_P2SH_tests.cpp
+++ b/src/test/script_P2SH_tests.cpp
@@ -105,7 +105,7 @@ BOOST_AUTO_TEST_CASE(sign)
{
CScript sigSave = txTo[i].vin[0].scriptSig;
txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
- bool sigOK = VerifySignature(txFrom, txTo[i], 0, true, true, 0);
+ bool sigOK = VerifySignature(CCoins(txFrom, 0), txTo[i], 0, true, true, 0);
if (i == j)
BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
else
@@ -243,7 +243,8 @@ BOOST_AUTO_TEST_CASE(switchover)
BOOST_AUTO_TEST_CASE(AreInputsStandard)
{
- std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs;
+ CCoinsView coinsDummy;
+ CCoinsViewCache coins(coinsDummy);
CBasicKeyStore keystore;
CKey key[3];
vector<CKey> keys;
@@ -264,23 +265,29 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
CScript pay1of3; pay1of3.SetMultisig(1, keys);
txFrom.vout[0].scriptPubKey = payScriptHash1;
+ txFrom.vout[0].nValue = 1000;
txFrom.vout[1].scriptPubKey = pay1;
+ txFrom.vout[1].nValue = 2000;
txFrom.vout[2].scriptPubKey = pay1of3;
+ txFrom.vout[2].nValue = 3000;
// Last three non-standard:
CScript empty;
keystore.AddCScript(empty);
txFrom.vout[3].scriptPubKey = empty;
+ txFrom.vout[3].nValue = 4000;
// Can't use SetPayToScriptHash, it checks for the empty Script. So:
txFrom.vout[4].scriptPubKey << OP_HASH160 << Hash160(empty) << OP_EQUAL;
+ txFrom.vout[4].nValue = 5000;
CScript oneOfEleven;
oneOfEleven << OP_1;
for (int i = 0; i < 11; i++)
oneOfEleven << key[0].GetPubKey();
oneOfEleven << OP_11 << OP_CHECKMULTISIG;
txFrom.vout[5].scriptPubKey.SetDestination(oneOfEleven.GetID());
+ txFrom.vout[5].nValue = 6000;
- mapInputs[txFrom.GetHash()] = make_pair(CTxIndex(), txFrom);
+ coins.SetCoins(txFrom.GetHash(), CCoins(txFrom, 0));
CTransaction txTo;
txTo.vout.resize(1);
@@ -297,21 +304,22 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
txTo.vin[2].prevout.hash = txFrom.GetHash();
BOOST_CHECK(SignSignature(keystore, txFrom, txTo, 2));
- BOOST_CHECK(txTo.AreInputsStandard(mapInputs));
- BOOST_CHECK_EQUAL(txTo.GetP2SHSigOpCount(mapInputs), 1);
+ BOOST_CHECK(txTo.AreInputsStandard(coins));
+ BOOST_CHECK_EQUAL(txTo.GetP2SHSigOpCount(coins), 1);
// Make sure adding crap to the scriptSigs makes them non-standard:
for (int i = 0; i < 3; i++)
{
CScript t = txTo.vin[i].scriptSig;
txTo.vin[i].scriptSig = (CScript() << 11) + t;
- BOOST_CHECK(!txTo.AreInputsStandard(mapInputs));
+ BOOST_CHECK(!txTo.AreInputsStandard(coins));
txTo.vin[i].scriptSig = t;
}
CTransaction txToNonStd;
txToNonStd.vout.resize(1);
txToNonStd.vout[0].scriptPubKey.SetDestination(key[1].GetPubKey().GetID());
+ txToNonStd.vout[0].nValue = 1000;
txToNonStd.vin.resize(2);
txToNonStd.vin[0].prevout.n = 4;
txToNonStd.vin[0].prevout.hash = txFrom.GetHash();
@@ -320,11 +328,11 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
txToNonStd.vin[1].prevout.hash = txFrom.GetHash();
txToNonStd.vin[1].scriptSig << OP_0 << Serialize(oneOfEleven);
- BOOST_CHECK(!txToNonStd.AreInputsStandard(mapInputs));
- BOOST_CHECK_EQUAL(txToNonStd.GetP2SHSigOpCount(mapInputs), 11);
+ BOOST_CHECK(!txToNonStd.AreInputsStandard(coins));
+ BOOST_CHECK_EQUAL(txToNonStd.GetP2SHSigOpCount(coins), 11);
txToNonStd.vin[0].scriptSig.clear();
- BOOST_CHECK(!txToNonStd.AreInputsStandard(mapInputs));
+ BOOST_CHECK(!txToNonStd.AreInputsStandard(coins));
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 1dacdeaa23..17b925c340 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests)
// paid to a TX_PUBKEYHASH.
//
static std::vector<CTransaction>
-SetupDummyInputs(CBasicKeyStore& keystoreRet, MapPrevTx& inputsRet)
+SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsView & coinsRet)
{
std::vector<CTransaction> dummyTransactions;
dummyTransactions.resize(2);
@@ -192,14 +192,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, MapPrevTx& inputsRet)
dummyTransactions[0].vout[0].scriptPubKey << key[0].GetPubKey() << OP_CHECKSIG;
dummyTransactions[0].vout[1].nValue = 50*CENT;
dummyTransactions[0].vout[1].scriptPubKey << key[1].GetPubKey() << OP_CHECKSIG;
- inputsRet[dummyTransactions[0].GetHash()] = make_pair(CTxIndex(), dummyTransactions[0]);
+ coinsRet.SetCoins(dummyTransactions[0].GetHash(), CCoins(dummyTransactions[0], 0));
dummyTransactions[1].vout.resize(2);
dummyTransactions[1].vout[0].nValue = 21*CENT;
dummyTransactions[1].vout[0].scriptPubKey.SetDestination(key[2].GetPubKey().GetID());
dummyTransactions[1].vout[1].nValue = 22*CENT;
dummyTransactions[1].vout[1].scriptPubKey.SetDestination(key[3].GetPubKey().GetID());
- inputsRet[dummyTransactions[1].GetHash()] = make_pair(CTxIndex(), dummyTransactions[1]);
+ coinsRet.SetCoins(dummyTransactions[1].GetHash(), CCoins(dummyTransactions[1], 0));
return dummyTransactions;
}
@@ -207,8 +207,9 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, MapPrevTx& inputsRet)
BOOST_AUTO_TEST_CASE(test_Get)
{
CBasicKeyStore keystore;
- MapPrevTx dummyInputs;
- std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, dummyInputs);
+ CCoinsView coinsDummy;
+ CCoinsViewCache coins(coinsDummy);
+ std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
CTransaction t1;
t1.vin.resize(3);
@@ -225,25 +226,24 @@ BOOST_AUTO_TEST_CASE(test_Get)
t1.vout[0].nValue = 90*CENT;
t1.vout[0].scriptPubKey << OP_1;
- BOOST_CHECK(t1.AreInputsStandard(dummyInputs));
- BOOST_CHECK_EQUAL(t1.GetValueIn(dummyInputs), (50+21+22)*CENT);
+ BOOST_CHECK(t1.AreInputsStandard(coins));
+ BOOST_CHECK_EQUAL(t1.GetValueIn(coins), (50+21+22)*CENT);
// Adding extra junk to the scriptSig should make it non-standard:
t1.vin[0].scriptSig << OP_11;
- BOOST_CHECK(!t1.AreInputsStandard(dummyInputs));
+ BOOST_CHECK(!t1.AreInputsStandard(coins));
// ... as should not having enough:
t1.vin[0].scriptSig = CScript();
- BOOST_CHECK(!t1.AreInputsStandard(dummyInputs));
+ BOOST_CHECK(!t1.AreInputsStandard(coins));
}
BOOST_AUTO_TEST_CASE(test_GetThrow)
{
CBasicKeyStore keystore;
- MapPrevTx dummyInputs;
- std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, dummyInputs);
-
- MapPrevTx missingInputs;
+ CCoinsView coinsDummy;
+ CCoinsViewCache coins(coinsDummy);
+ std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
CTransaction t1;
t1.vin.resize(3);
@@ -257,8 +257,8 @@ BOOST_AUTO_TEST_CASE(test_GetThrow)
t1.vout[0].nValue = 90*CENT;
t1.vout[0].scriptPubKey << OP_1;
- BOOST_CHECK_THROW(t1.AreInputsStandard(missingInputs), runtime_error);
- BOOST_CHECK_THROW(t1.GetValueIn(missingInputs), runtime_error);
+ BOOST_CHECK_THROW(t1.AreInputsStandard(coinsDummy), runtime_error);
+ BOOST_CHECK_THROW(t1.GetValueIn(coinsDummy), runtime_error);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet.cpp b/src/wallet.cpp
index 1a6a1082b1..9b2960f64e 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -685,7 +685,7 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64& nReceived,
}
}
-void CWalletTx::AddSupportingTransactions(CTxDB& txdb)
+void CWalletTx::AddSupportingTransactions()
{
vtxPrev.clear();
@@ -696,7 +696,6 @@ void CWalletTx::AddSupportingTransactions(CTxDB& txdb)
BOOST_FOREACH(const CTxIn& txin, vin)
vWorkQueue.push_back(txin.prevout.hash);
- // This critsect is OK because txdb is already open
{
LOCK(pwallet->cs_wallet);
map<uint256, const CMerkleTx*> mapWalletPrev;
@@ -720,15 +719,6 @@ void CWalletTx::AddSupportingTransactions(CTxDB& txdb)
{
tx = *mapWalletPrev[hash];
}
- else if (!fClient && txdb.ReadDiskTx(hash, tx))
- {
- ;
- }
- else
- {
- printf("ERROR: AddSupportingTransactions() : unsupported transaction\n");
- continue;
- }
int nDepth = tx.SetMerkleBranch();
vtxPrev.push_back(tx);
@@ -775,49 +765,36 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
return ret;
}
-int CWallet::ScanForWalletTransaction(const uint256& hashTx)
-{
- CTransaction tx;
- tx.ReadFromDisk(COutPoint(hashTx, 0));
- if (AddToWalletIfInvolvingMe(tx, NULL, true, true))
- return 1;
- return 0;
-}
-
void CWallet::ReacceptWalletTransactions()
{
- CTxDB txdb("r");
+ CCoinsDB coinsdb("r");
bool fRepeat = true;
while (fRepeat)
{
LOCK(cs_wallet);
fRepeat = false;
- vector<CDiskTxPos> vMissingTx;
+ bool fMissing = false;
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
CWalletTx& wtx = item.second;
if (wtx.IsCoinBase() && wtx.IsSpent(0))
continue;
- CTxIndex txindex;
+ CCoins coins;
bool fUpdated = false;
- if (txdb.ReadTxIndex(wtx.GetHash(), txindex))
+ bool fNotFound = coinsdb.ReadCoins(wtx.GetHash(), coins);
+ if (!fNotFound || wtx.GetDepthInMainChain() > 0)
{
// Update fSpent if a tx got spent somewhere else by a copy of wallet.dat
- if (txindex.vSpent.size() != wtx.vout.size())
- {
- printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %"PRIszu" != wtx.vout.size() %"PRIszu"\n", txindex.vSpent.size(), wtx.vout.size());
- continue;
- }
- for (unsigned int i = 0; i < txindex.vSpent.size(); i++)
+ for (unsigned int i = 0; i < wtx.vout.size(); i++)
{
if (wtx.IsSpent(i))
continue;
- if (!txindex.vSpent[i].IsNull() && IsMine(wtx.vout[i]))
+ if ((i >= coins.vout.size() || coins.vout[i].IsNull()) && IsMine(wtx.vout[i]))
{
wtx.MarkSpent(i);
fUpdated = true;
- vMissingTx.push_back(txindex.vSpent[i]);
+ fMissing = true;
}
}
if (fUpdated)
@@ -831,10 +808,10 @@ void CWallet::ReacceptWalletTransactions()
{
// Re-accept any txes of ours that aren't already in a block
if (!wtx.IsCoinBase())
- wtx.AcceptWalletTransaction(txdb, false);
+ wtx.AcceptWalletTransaction(coinsdb, false);
}
}
- if (!vMissingTx.empty())
+ if (fMissing)
{
// TODO: optimize this to scan just part of the block chain?
if (ScanForWalletTransactions(pindexGenesisBlock))
@@ -843,21 +820,21 @@ void CWallet::ReacceptWalletTransactions()
}
}
-void CWalletTx::RelayWalletTransaction(CTxDB& txdb)
+void CWalletTx::RelayWalletTransaction(CCoinsDB& coinsdb)
{
BOOST_FOREACH(const CMerkleTx& tx, vtxPrev)
{
if (!tx.IsCoinBase())
{
uint256 hash = tx.GetHash();
- if (!txdb.ContainsTx(hash))
+ if (!coinsdb.HaveCoins(hash))
RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx);
}
}
if (!IsCoinBase())
{
uint256 hash = GetHash();
- if (!txdb.ContainsTx(hash))
+ if (!coinsdb.HaveCoins(hash))
{
printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str());
RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this);
@@ -867,8 +844,8 @@ void CWalletTx::RelayWalletTransaction(CTxDB& txdb)
void CWalletTx::RelayWalletTransaction()
{
- CTxDB txdb("r");
- RelayWalletTransaction(txdb);
+ CCoinsDB coinsdb("r");
+ RelayWalletTransaction(coinsdb);
}
void CWallet::ResendWalletTransactions()
@@ -891,7 +868,7 @@ void CWallet::ResendWalletTransactions()
// Rebroadcast any of our txes that aren't in a block yet
printf("ResendWalletTransactions()\n");
- CTxDB txdb("r");
+ CCoinsDB coinsdb("r");
{
LOCK(cs_wallet);
// Sort them in chronological order
@@ -907,7 +884,7 @@ void CWallet::ResendWalletTransactions()
BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted)
{
CWalletTx& wtx = *item.second;
- wtx.RelayWalletTransaction(txdb);
+ wtx.RelayWalletTransaction(coinsdb);
}
}
}
@@ -1162,8 +1139,6 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
{
LOCK2(cs_main, cs_wallet);
- // txdb must be opened before the mapWallet lock
- CTxDB txdb("r");
{
nFeeRet = nTransactionFee;
loop
@@ -1253,7 +1228,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
}
// Fill vtxPrev by copying from previous transactions vtxPrev
- wtxNew.AddSupportingTransactions(txdb);
+ wtxNew.AddSupportingTransactions();
wtxNew.fTimeReceivedIsTxTime = true;
break;
diff --git a/src/wallet.h b/src/wallet.h
index c5f1243907..e0aa077972 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -166,7 +166,6 @@ public:
bool EraseFromWallet(uint256 hash);
void WalletUpdateSpent(const CTransaction& prevout);
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
- int ScanForWalletTransaction(const uint256& hashTx);
void ReacceptWalletTransactions();
void ResendWalletTransactions();
int64 GetBalance() const;
@@ -659,12 +658,12 @@ public:
int64 GetTxTime() const;
int GetRequestCount() const;
- void AddSupportingTransactions(CTxDB& txdb);
+ void AddSupportingTransactions();
- bool AcceptWalletTransaction(CTxDB& txdb, bool fCheckInputs=true);
+ bool AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs=true);
bool AcceptWalletTransaction();
- void RelayWalletTransaction(CTxDB& txdb);
+ void RelayWalletTransaction(CCoinsDB& coinsdb);
void RelayWalletTransaction();
};