Add -enforcecanonical and -confchange options, rename IsConfirmed() to IsTrusted().
[novacoin.git] / src / main.cpp
index 437db1a..1f62d80 100644 (file)
@@ -6,6 +6,7 @@
 #include "alert.h"
 #include "checkpoints.h"
 #include "db.h"
+#include "txdb.h"
 #include "net.h"
 #include "init.h"
 #include "ui_interface.h"
@@ -58,6 +59,7 @@ uint256 nBestInvalidTrust = 0;
 uint256 hashBestChain = 0;
 CBlockIndex* pindexBest = NULL;
 int64 nTimeBestReceived = 0;
+set<CBlockIndex*, CBlockIndexTrustComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed
 
 CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have
 
@@ -130,7 +132,7 @@ void static EraseFromWallets(uint256 hash)
 }
 
 // make sure all wallets know about the given transaction, in the given block
-void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fConnect)
+void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fConnect)
 {
     if (!fConnect)
     {
@@ -145,7 +147,7 @@ void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate,
     }
 
     BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
-        pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
+        pwallet->AddToWalletIfInvolvingMe(hash, tx, pblock, fUpdate);
 }
 
 // notify wallets about a new best chain
@@ -177,10 +179,10 @@ void static Inventory(const uint256& hash)
 }
 
 // ask wallets to resend their transactions
-void ResendWalletTransactions()
+void ResendWalletTransactions(bool fForce)
 {
     BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
-        pwallet->ResendWalletTransactions();
+        pwallet->ResendWalletTransactions(fForce);
 }
 
 
@@ -194,6 +196,8 @@ 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; }
+bool CCoinsView::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return false; }
+bool CCoinsView::GetStats(CCoinsStats &stats) { return false; }
 
 CCoinsViewBacked::CCoinsViewBacked(CCoinsView &viewIn) : base(&viewIn) { }
 bool CCoinsViewBacked::GetCoins(uint256 txid, CCoins &coins) { return base->GetCoins(txid, coins); }
@@ -202,13 +206,9 @@ 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; }
+bool CCoinsViewBacked::GetStats(CCoinsStats &stats) { return base->GetStats(stats); }
 
-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()); }
+bool CCoinsViewBacked::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { return base->BatchWrite(mapCoins, pindex); }
 
 CCoinsViewCache::CCoinsViewCache(CCoinsView &baseIn, bool fDummy) : CCoinsViewBacked(baseIn), pindexTip(NULL) { }
 
@@ -224,13 +224,47 @@ bool CCoinsViewCache::GetCoins(uint256 txid, CCoins &coins) {
     return false;
 }
 
+// Select coins from read-only cache or database
+bool CCoinsViewCache::GetCoinsReadOnly(uint256 txid, CCoins &coins) {
+    if (cacheCoins.count(txid)) {
+        coins = cacheCoins[txid]; // get from cache
+        return true;
+    }
+    if (cacheCoinsReadOnly.count(txid)) {
+        coins = cacheCoinsReadOnly[txid]; // get from read-only cache
+        return true;
+    }
+    if (base->GetCoins(txid, coins)) {
+        cacheCoinsReadOnly[txid] = coins; // save to read-only cache
+        return true;
+    }
+    return false;
+}
+
+std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(uint256 txid) {
+    std::map<uint256,CCoins>::iterator it = cacheCoins.find(txid);
+    if (it != cacheCoins.end())
+        return it;
+    CCoins tmp;
+    if (!base->GetCoins(txid,tmp))
+        return it;
+    std::pair<std::map<uint256,CCoins>::iterator,bool> ret = cacheCoins.insert(std::make_pair(txid, tmp));
+    return ret.first;
+}
+
+CCoins &CCoinsViewCache::GetCoins(uint256 txid) {
+    std::map<uint256,CCoins>::iterator it = FetchCoins(txid);
+    assert(it != cacheCoins.end());
+    return it->second;
+}
+
 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);
+    return FetchCoins(txid) != cacheCoins.end();
 }
 
 CBlockIndex *CCoinsViewCache::GetBestBlock() {
@@ -244,18 +278,26 @@ bool CCoinsViewCache::SetBestBlock(CBlockIndex *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;
+bool CCoinsViewCache::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) {
+    for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++)
+        cacheCoins[it->first] = it->second;
+    pindexTip = pindex;
     return true;
 }
 
+bool CCoinsViewCache::Flush() {
+    cacheCoinsReadOnly.clear(); // purge read-only cache
+
+    bool fOk = base->BatchWrite(cacheCoins, pindexTip);
+    if (fOk)
+        cacheCoins.clear();
+    return fOk;
+}
+
+unsigned int CCoinsViewCache::GetCacheSize() {
+    return cacheCoins.size();
+}
+
 /** 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) { }
@@ -275,6 +317,8 @@ bool CCoinsViewMemPool::HaveCoins(uint256 txid) {
     return mempool.exists(txid) || base->HaveCoins(txid);
 }
 
+CCoinsViewCache *pcoinsTip = NULL;
+CBlockTreeDB *pblocktree = NULL;
 
 //////////////////////////////////////////////////////////////////////////////
 //
@@ -355,25 +399,38 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
 
 bool CTransaction::IsStandard() const
 {
-    if (nVersion > CTransaction::CURRENT_VERSION)
+    if (nVersion > CTransaction::CURRENT_VERSION) {
         return false;
+    }
 
     BOOST_FOREACH(const CTxIn& txin, vin)
     {
         // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG
         // pay-to-script-hash, which is 3 ~80-byte signatures, 3
         // ~65-byte public keys, plus a few script ops.
-        if (txin.scriptSig.size() > 500)
+        if (txin.scriptSig.size() > 500) {
             return false;
-        if (!txin.scriptSig.IsPushOnly())
+        }
+        if (!txin.scriptSig.IsPushOnly()) {
+            return false;
+        }
+        if (fEnforceCanonical && !txin.scriptSig.HasCanonicalPushes()) {
             return false;
+        }
     }
+
     BOOST_FOREACH(const CTxOut& txout, vout) {
-        if (!::IsStandard(txout.scriptPubKey))
+        if (!::IsStandard(txout.scriptPubKey)) {
+            return false;
+        }
+        if (txout.nValue == 0) {
             return false;
-        if (txout.nValue == 0)
+        }
+        if (fEnforceCanonical && !txout.scriptPubKey.HasCanonicalPushes()) {
             return false;
+        }
     }
+
     return true;
 }
 
@@ -388,7 +445,7 @@ bool CTransaction::IsStandard() const
 // expensive-to-check-upon-redemption script like:
 //   DUP CHECKSIG DROP ... repeated 100 times... OP_1
 //
-bool CTransaction::AreInputsStandard(CCoinsView& mapInputs) const
+bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const
 {
     if (IsCoinBase())
         return true; // Coinbases don't use vin normally
@@ -468,11 +525,9 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
     else
     {
         CBlock blockTmp;
-
         if (pblock == NULL) {
-            CCoinsDB coinsdb("r");
             CCoins coins;
-            if (coinsdb.ReadCoins(GetHash(), coins)) {
+            if (pcoinsTip->GetCoins(GetHash(), coins)) {
                 CBlockIndex *pindex = FindBlockByHeight(coins.nHeight);
                 if (pindex) {
                     if (!blockTmp.ReadFromDisk(pindex))
@@ -533,7 +588,7 @@ bool CTransaction::CheckTransaction() const
         if (txout.IsEmpty() && !IsCoinBase() && !IsCoinStake())
             return DoS(100, error("CTransaction::CheckTransaction() : txout empty for user transaction"));
 
-        // NovaCoin: enforce minimum output amount for user transactions until 1 May 2014 04:00:00 GMT
+        // Enforce minimum output amount for user transactions until 1 May 2014 04:00:00 GMT
         if (!fTestNet && !IsCoinBase() && !txout.IsEmpty() && nTime < OUTPUT_SWITCH_TIME && txout.nValue < MIN_TXOUT_AMOUNT)
             return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue below minimum"));
 
@@ -611,7 +666,7 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
     }
 }
 
-bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs)
+bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInputs)
 {
     if (pfMissingInputs)
         *pfMissingInputs = false;
@@ -673,9 +728,7 @@ bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs,
 
     if (fCheckInputs)
     {
-        CCoinsViewDB viewDB(coinsdb);
-        CCoinsViewMemPool viewMemPool(viewDB, mempool);
-        CCoinsViewCache view(viewMemPool);
+        CCoinsViewCache &view = *pcoinsTip;
 
         // do we already have it?
         if (view.HaveCoins(hash))
@@ -690,6 +743,9 @@ bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs,
             }
         }
 
+        if (!tx.HaveInputs(view))
+            return error("CTxMemPool::accept() : inputs already spent");
+
         // Check for non-standard pay-to-script-hash in inputs
         if (!tx.AreInputsStandard(view) && !fTestNet)
             return error("CTxMemPool::accept() : nonstandard transaction input");
@@ -764,9 +820,9 @@ bool CTxMemPool::accept(CCoinsDB& coinsdb, CTransaction &tx, bool fCheckInputs,
     return true;
 }
 
-bool CTransaction::AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs, bool* pfMissingInputs)
+bool CTransaction::AcceptToMemoryPool(bool fCheckInputs, bool* pfMissingInputs)
 {
-    return mempool.accept(coinsdb, *this, fCheckInputs, pfMissingInputs);
+    return mempool.accept(*this, fCheckInputs, pfMissingInputs);
 }
 
 bool CTxMemPool::addUnchecked(const uint256& hash, CTransaction &tx)
@@ -854,27 +910,21 @@ int CMerkleTx::GetBlocksToMaturity() const
 }
 
 
-bool CMerkleTx::AcceptToMemoryPool(CCoinsDB& coinsdb, bool fCheckInputs)
+bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs)
 {
     if (fClient)
     {
         if (!IsInMainChain() && !ClientCheckInputs())
             return false;
-        return CTransaction::AcceptToMemoryPool(coinsdb, false);
+        return CTransaction::AcceptToMemoryPool(false);
     }
     else
     {
-        return CTransaction::AcceptToMemoryPool(coinsdb, fCheckInputs);
+        return CTransaction::AcceptToMemoryPool(fCheckInputs);
     }
 }
 
-bool CMerkleTx::AcceptToMemoryPool()
-{
-    CCoinsDB coinsdb("r");
-    return AcceptToMemoryPool(coinsdb);
-}
-
-bool CWalletTx::AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs)
+bool CWalletTx::AcceptWalletTransaction(bool fCheckInputs)
 {
 
     {
@@ -885,21 +935,15 @@ bool CWalletTx::AcceptWalletTransaction(CCoinsDB& coinsdb, bool fCheckInputs)
             if (!(tx.IsCoinBase() || tx.IsCoinStake()))
             {
                 uint256 hash = tx.GetHash();
-                if (!mempool.exists(hash) && !coinsdb.HaveCoins(hash))
-                    tx.AcceptToMemoryPool(coinsdb, fCheckInputs);
+                if (!mempool.exists(hash) && pcoinsTip->HaveCoins(hash))
+                    tx.AcceptToMemoryPool(fCheckInputs);
             }
         }
-        return AcceptToMemoryPool(coinsdb, fCheckInputs);
+        return AcceptToMemoryPool(fCheckInputs);
     }
     return false;
 }
 
-bool CWalletTx::AcceptWalletTransaction()
-{
-    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 &txOut, uint256 &hashBlock, bool fAllowSlow)
 {
@@ -918,8 +962,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock
         if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
             int nHeight = -1;
             {
-                CCoinsDB coindb("r");
-                CCoinsViewDB view(coindb);
+                CCoinsViewCache &view = *pcoinsTip;
                 CCoins coins;
                 if (view.GetCoins(hash, coins))
                     nHeight = coins.nHeight;
@@ -1277,7 +1320,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
     if (pindexNew->nChainTrust > nBestInvalidTrust)
     {
         nBestInvalidTrust = pindexNew->nChainTrust;
-        CChainDB().WriteBestInvalidTrust(CBigNum(nBestInvalidTrust));
+        pblocktree->WriteBestInvalidTrust(CBigNum(nBestInvalidTrust));
         uiInterface.NotifyBlocksChanged();
     }
 
@@ -1295,43 +1338,86 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
       DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
 }
 
+void static InvalidBlockFound(CBlockIndex *pindex) {
+    pindex->nStatus |= BLOCK_FAILED_VALID;
+    pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
+    setBlockIndexValid.erase(pindex);
+    InvalidChainFound(pindex);
+    if (pindex->pnext)
+        ConnectBestBlock(); // reorganise away from the failed block
+}
+
+bool ConnectBestBlock() {
+    do {
+        CBlockIndex *pindexNewBest;
+
+        {
+            std::set<CBlockIndex*,CBlockIndexTrustComparator>::reverse_iterator it = setBlockIndexValid.rbegin();
+            if (it == setBlockIndexValid.rend())
+                return true;
+            pindexNewBest = *it;
+        }
+
+        if (pindexNewBest == pindexBest)
+            return true; // nothing to do
+
+        // check ancestry
+        CBlockIndex *pindexTest = pindexNewBest;
+        std::vector<CBlockIndex*> vAttach;
+        do {
+            if (pindexTest->nStatus & BLOCK_FAILED_MASK) {
+                // mark descendants failed
+                CBlockIndex *pindexFailed = pindexNewBest;
+                while (pindexTest != pindexFailed) {
+                    pindexFailed->nStatus |= BLOCK_FAILED_CHILD;
+                    setBlockIndexValid.erase(pindexFailed);
+                    pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexFailed));
+                    pindexFailed = pindexFailed->pprev;
+                }
+                InvalidChainFound(pindexNewBest);
+                break;
+            }
+
+            if (pindexBest == NULL || pindexTest->nChainTrust > pindexBest->nChainTrust)
+                vAttach.push_back(pindexTest);
+
+            if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) {
+                reverse(vAttach.begin(), vAttach.end());
+                BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach)
+                    if (!SetBestChain(pindexSwitch))
+                        return false;
+                return true;
+            }
+            pindexTest = pindexTest->pprev;
+        } while(true);
+    } while(true);
+}
 
 void CBlock::UpdateTime(const CBlockIndex* pindexPrev)
 {
     nTime = max(GetBlockTime(), GetAdjustedTime());
 }
 
-
-CTxOut CTransaction::GetOutputFor(const CTxIn& input, CCoinsView& view)
+const CTxOut &CTransaction::GetOutputFor(const CTxIn& input, CCoinsViewCache& view)
 {
-    CCoins coins;
-    if (!view.GetCoins(input.prevout.hash, coins))
-        throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found");
-
-    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 out;
+    const CCoins &coins = view.GetCoins(input.prevout.hash);
+    assert(coins.IsAvailable(input.prevout.n));
+    return coins.vout[input.prevout.n];
 }
 
-int64 CTransaction::GetValueIn(CCoinsView& inputs) const
+int64 CTransaction::GetValueIn(CCoinsViewCache& 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;
 }
 
-unsigned int CTransaction::GetP2SHSigOpCount(CCoinsView& inputs) const
+unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const
 {
     if (IsCoinBase())
         return 0;
@@ -1339,44 +1425,36 @@ unsigned int CTransaction::GetP2SHSigOpCount(CCoinsView& inputs) const
     unsigned int nSigOps = 0;
     for (unsigned int i = 0; i < vin.size(); i++)
     {
-        CTxOut prevout = GetOutputFor(vin[i], inputs);
+        const CTxOut &prevout = GetOutputFor(vin[i], inputs);
         if (prevout.scriptPubKey.IsPayToScriptHash())
             nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig);
     }
     return nSigOps;
 }
 
-bool CTransaction::UpdateCoins(CCoinsView &inputs, CTxUndo &txundo, int nHeight, unsigned int nTimeStamp) const
+bool CTransaction::UpdateCoins(CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, unsigned int nTimeStamp, const uint256 &txhash) 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");
-
+            CCoins &coins = inputs.GetCoins(txin.prevout.hash);
             if (coins.nTime > nTimeStamp)
                 return error("UpdateCoins() : timestamp violation");
-
             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, nTimeStamp)))
+    if (!inputs.SetCoins(txhash, CCoins(*this, nHeight, nTimeStamp)))
         return error("UpdateCoins() : cannot update output");
 
     return true;
 }
 
-bool CTransaction::HaveInputs(CCoinsView &inputs) const
+bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const
 {
     if (!IsCoinBase()) { 
         // first check whether information about the prevout hash is available
@@ -1389,8 +1467,7 @@ bool CTransaction::HaveInputs(CCoinsView &inputs) const
         // 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);
+            const CCoins &coins = inputs.GetCoins(prevout.hash);
             if (!coins.IsAvailable(prevout.n))
                 return false;
         }
@@ -1398,28 +1475,25 @@ bool CTransaction::HaveInputs(CCoinsView &inputs) const
     return true;
 }
 
-bool CTransaction::CheckInputs(CCoinsView &inputs, enum CheckSig_mode csmode, bool fStrictPayToScriptHash, bool fStrictEncodings, CBlock *pblock) const
+bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, bool fStrictPayToScriptHash, bool fStrictEncodings, CBlock *pblock) const
 {
     if (!IsCoinBase())
     {
+        // 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 (!HaveInputs(inputs))
+            return error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str());
+
+        CBlockIndex *pindexBlock = inputs.GetBestBlock();
         int64 nValueIn = 0;
         int64 nFees = 0;
         for (unsigned int i = 0; i < vin.size(); i++)
         {
             const COutPoint &prevout = vin[i].prevout;
-            CCoins coins;
-            if (!inputs.GetCoins(prevout.hash, coins))
-                return error("CheckInputs() : cannot find prevout tx");
-
-            // 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());
+            const CCoins &coins = inputs.GetCoins(prevout.hash);
 
             // If prev is coinbase or coinstake, check that it's matured
             if (coins.IsCoinBase() || coins.IsCoinStake()) {
-                CBlockIndex *pindexBlock = inputs.GetBestBlock();
                 if (pindexBlock->nHeight - coins.nHeight < nCoinbaseMaturity)
                     return error("CheckInputs() : tried to spend %s at depth %d", coins.IsCoinBase() ? "coinbase" : "coinstake", pindexBlock->nHeight - coins.nHeight);
             }
@@ -1441,7 +1515,7 @@ bool CTransaction::CheckInputs(CCoinsView &inputs, enum CheckSig_mode csmode, bo
 
             // Coin stake tx earns reward instead of paying fee
             uint64 nCoinAge;
-            if (!GetCoinAge(inputs, nCoinAge))
+            if (!GetCoinAge(nCoinAge))
                 return error("CheckInputs() : %s unable to get coin age for coinstake", GetHash().ToString().substr(0,10).c_str());
 
             int64 nStakeReward = GetValueOut() - nValueIn;
@@ -1479,8 +1553,7 @@ bool CTransaction::CheckInputs(CCoinsView &inputs, enum CheckSig_mode csmode, bo
             (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);
+                const CCoins &coins = inputs.GetCoins(prevout.hash);
 
                 // Verify signature
                 if (!VerifySignature(coins, *this, i, fStrictPayToScriptHash, fStrictEncodings, 0)) {
@@ -1493,8 +1566,6 @@ bool CTransaction::CheckInputs(CCoinsView &inputs, enum CheckSig_mode csmode, bo
                 }
             }
         }
-
-
     }
 
     return true;
@@ -1547,7 +1618,7 @@ bool CTransaction::ClientCheckInputs() const
     return true;
 }
 
-bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
+bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view)
 {
     assert(pindex == view.GetBestBlock());
 
@@ -1570,18 +1641,21 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
         const CTransaction &tx = vtx[i];
         uint256 hash = tx.GetHash();
 
+        // don't check coinbase coins for proof-of-stake block
+        if(IsProofOfStake() && tx.IsCoinBase())
+            continue;
+
         // check that all outputs are available
-        CCoins outs;
-        if (!view.GetCoins(hash, outs))
+        if (!view.HaveCoins(hash))
             return error("DisconnectBlock() : outputs still spent? database corrupted");
+        CCoins &outs = view.GetCoins(hash);
 
         CCoins outsBlock = CCoins(tx, pindex->nHeight, pindex->nTime);
         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");
+        outs = CCoins();
 
         // restore inputs
         if (i > 0) { // not coinbases
@@ -1596,7 +1670,10 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
                     if (undo.nHeight == 0)
                         return error("DisconnectBlock() : undo data doesn't contain tx metadata? database corrupted");
                     coins.fCoinBase = undo.fCoinBase;
+                    coins.fCoinStake = undo.fCoinStake;
                     coins.nHeight = undo.nHeight;
+                    coins.nTime = undo.nTime;
+                    coins.nBlockTime = undo.nBlockTime;
                     coins.nVersion = undo.nVersion;
                 } else {
                     if (undo.nHeight != 0)
@@ -1611,6 +1688,9 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
                     return error("DisconnectBlock() : cannot restore coin inputs");
             }
         }
+
+        // clean up wallet after disconnecting coinstake
+        SyncWithWallets(vtx[i].GetHash(), vtx[i], this, false, false);
     }
 
     // move best block pointer to prevout block
@@ -1619,9 +1699,9 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
     return true;
 }
 
-bool FindUndoPos(CChainDB &chaindb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
+bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
 
-bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck)
+bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck)
 {
     // Check it again in case a previous version let a bad block in
     if (!CheckBlock(!fJustCheck, !fJustCheck))
@@ -1645,10 +1725,9 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck
     bool fEnforceBIP30 = true;
 
     if (fEnforceBIP30) {
-        BOOST_FOREACH(CTransaction& tx, vtx) {
-            uint256 hash = tx.GetHash();
-            CCoins coins;
-            if (view.GetCoins(hash, coins) && !coins.IsPruned())
+        for (unsigned int i=0; i<vtx.size(); i++) {
+            uint256 hash = GetTxHash(i);
+            if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned())
                 return error("ConnectBlock() : tried to overwrite transaction");
         }
     }
@@ -1660,8 +1739,9 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck
 
     int64 nFees = 0, nValueIn = 0, nValueOut = 0;
     unsigned int nSigOps = 0;
-    BOOST_FOREACH(CTransaction& tx, vtx)
+    for (unsigned int i=0; i<vtx.size(); i++)
     {
+        const CTransaction &tx = vtx[i];
         nSigOps += tx.GetLegacySigOpCount();
         if (nSigOps > MAX_BLOCK_SIGOPS)
             return DoS(100, error("ConnectBlock() : too many sigops"));
@@ -1698,8 +1778,12 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck
             nValueOut += tx.GetValueOut();
         }
 
+        // don't create coinbase coins for proof-of-stake block
+        if(IsProofOfStake() && tx.IsCoinBase())
+            continue;
+
         CTxUndo txundo;
-        if (!tx.UpdateCoins(view, txundo, pindex->nHeight, pindex->nTime))
+        if (!tx.UpdateCoins(view, txundo, pindex->nHeight, pindex->nTime, GetTxHash(i)))
             return error("ConnectBlock() : UpdateInputs failed");
         if (!tx.IsCoinBase())
             blockundo.vtxundo.push_back(txundo);
@@ -1711,24 +1795,27 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck
     if (fJustCheck)
         return true;
 
-    CChainDB chaindb;
-    CDiskBlockPos pos;
-
     // Write undo information to disk
-    if (pindex->GetUndoPos().IsNull())
+    if (pindex->GetUndoPos().IsNull() || (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS)
     {
-        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");
+        if (pindex->GetUndoPos().IsNull()) {
+            CDiskBlockPos pos;
+            if (!FindUndoPos(pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 8))
+                return error("ConnectBlock() : FindUndoPos failed");
+            if (!blockundo.WriteToDisk(pos))
+                return error("ConnectBlock() : CBlockUndo::WriteToDisk failed");
+
+            // update nUndoPos in block index
+            pindex->nUndoPos = pos.nPos;
+            pindex->nStatus |= BLOCK_HAVE_UNDO;
+        }
 
-        // update nUndoPos in block index
-        pindex->nUndoPos = pos.nPos + 1;
-    }
+        pindex->nStatus = (pindex->nStatus & ~BLOCK_VALID_MASK) | BLOCK_VALID_SCRIPTS;
 
-    CDiskBlockIndex blockindex(pindex);
-    if (!chaindb.WriteBlockIndex(blockindex))
-        return error("ConnectBlock() : WriteBlockIndex failed");
+        CDiskBlockIndex blockindex(pindex);
+        if (!pblocktree->WriteBlockIndex(blockindex))
+            return error("ConnectBlock() : WriteBlockIndex failed");
+    }
 
     // add this block to the view's blockchain
     if (!view.SetBestBlock(pindex))
@@ -1739,26 +1826,23 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck
         printf("ConnectBlock() : destroy=%s nFees=%"PRI64d"\n", FormatMoney(nFees).c_str(), nFees);
 
     // Watch for transactions paying to me
-    BOOST_FOREACH(CTransaction& tx, vtx)
-        SyncWithWallets(tx, this, true);
+    for (unsigned int i=0; i<vtx.size(); i++)
+        SyncWithWallets(GetTxHash(i), vtx[i], this, true);
 
     return true;
 }
 
-bool CBlock::SetBestChain(CBlockIndex* pindexNew)
+bool SetBestChain(CBlockIndex* pindexNew)
 {
-    // if this functions exits prematurely, the transaction is aborted
-    CCoinsDB coinsdb;
-    if (!coinsdb.TxnBegin())
-        return error("SetBestChain() : TxnBegin failed");
+    CCoinsViewCache &view = *pcoinsTip;
 
     // 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() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
     {
-        coinsdb.WriteHashBestChain(pindexNew->GetBlockHash());
-        if (!coinsdb.TxnCommit())
-            return error("SetBestChain() : TxnCommit failed");
+        view.SetBestBlock(pindexNew);
+        if (!view.Flush())
+            return false;
         pindexGenesisBlock = pindexNew;
         pindexBest = pindexNew;
         hashBestChain = pindexNew->GetBlockHash();
@@ -1767,10 +1851,6 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew)
         return true;
     }
 
-    // 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;
@@ -1806,13 +1886,16 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew)
     BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) {
         CBlock block;
         if (!block.ReadFromDisk(pindex))
-            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());
+            return error("SetBestChain() : ReadFromDisk for disconnect failed");
+        CCoinsViewCache viewTemp(view, true);
+        if (!block.DisconnectBlock(pindex, viewTemp))
+            return error("SetBestChain() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
+        if (!viewTemp.Flush())
+            return error("SetBestChain() : Cache flush failed after disconnect");
 
         // Queue memory transactions to resurrect
         BOOST_FOREACH(const CTransaction& tx, block.vtx)
-            if (!tx.IsCoinBase())
+            if (!tx.IsCoinBase() && !tx.IsCoinStake())
                 vResurrect.push_back(tx);
     }
 
@@ -1820,30 +1903,27 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew)
     vector<CTransaction> vDelete;
     BOOST_FOREACH(CBlockIndex *pindex, vConnect) {
         CBlock block;
-        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)) {
+        if (!block.ReadFromDisk(pindex))
+            return error("SetBestChain() : ReadFromDisk for connect failed");
+        CCoinsViewCache viewTemp(view, true);
+        if (!block.ConnectBlock(pindex, viewTemp)) {
             InvalidChainFound(pindexNew);
-            return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
+            InvalidBlockFound(pindex);
+            return error("SetBestChain() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
         }
+        if (!viewTemp.Flush())
+            return error("SetBestChain() : Cache flush failed after connect");
 
         // Queue memory transactions to delete
-        BOOST_FOREACH(const CTransaction& tx, pblock->vtx)
+        BOOST_FOREACH(const CTransaction& tx, block.vtx)
             vDelete.push_back(tx);
     }
 
     // Make sure it's successfully written to disk before changing memory structure
-    if (!view.Flush())
-        return error("SetBestBlock() : failed to write coin changes");
-    if (!coinsdb.TxnCommit())
-        return error("SetBestBlock() : TxnCommit failed");
-    coinsdb.Close();
+    bool fIsInitialDownload = IsInitialBlockDownload();
+    if (!fIsInitialDownload || view.GetCacheSize()>5000)
+        if (!view.Flush())
+            return false;
 
     // At this point, all changes have been done to the database.
     // Proceed by updating the memory structures.
@@ -1860,14 +1940,13 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew)
 
     // Resurrect memory transactions that were in the disconnected branch
     BOOST_FOREACH(CTransaction& tx, vResurrect)
-        tx.AcceptToMemoryPool(coinsdb, false);
+        tx.AcceptToMemoryPool(false);
 
     // Delete redundant memory transactions that are in the connected branch
     BOOST_FOREACH(CTransaction& tx, vDelete)
         mempool.remove(tx);
 
     // Update best block in wallet (so we can detect restored wallets)
-    bool fIsInitialDownload = IsInitialBlockDownload();
     if (!fIsInitialDownload)
     {
         const CBlockLocator locator(pindexNew);
@@ -1885,10 +1964,8 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew)
 
     uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust;
 
-    printf("SetBestChain: new best=%s  height=%d  trust=%s  blocktrust=%"PRI64d"  date=%s\n",
-      hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
-      CBigNum(nBestChainTrust).ToString().c_str(),
-      nBestBlockTrust.Get64(),
+    printf("SetBestChain: new best=%s  height=%d  trust=%s blocktrust=%s  tx=%lu  date=%s\n",
+      hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str(), CBigNum(nBestBlockTrust).ToString().c_str(), (unsigned long)pindexNew->nChainTx,
       DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
 
     // Check the version of the last 100 blocks to see if we need to upgrade:
@@ -1927,8 +2004,10 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew)
 // guaranteed to be in main chain by sync-checkpoint. This rule is
 // introduced to help nodes establish a consistent view of the coin
 // age (trust score) of competing branches.
-bool CTransaction::GetCoinAge(CCoinsView& inputs, uint64& nCoinAge) const
+bool CTransaction::GetCoinAge(uint64& nCoinAge) const
 {
+    CCoinsViewCache &inputs = *pcoinsTip;
+
     CBigNum bnCentSecond = 0;  // coin age in the unit of cent-seconds
     nCoinAge = 0;
 
@@ -1965,13 +2044,10 @@ bool CBlock::GetCoinAge(uint64& nCoinAge) const
 {
     nCoinAge = 0;
 
-    CCoinsDB coindb("r");
-    CCoinsViewDB view(coindb);
-
     BOOST_FOREACH(const CTransaction& tx, vtx)
     {
         uint64 nTxCoinAge;
-        if (tx.GetCoinAge(view, nTxCoinAge))
+        if (tx.GetCoinAge(nTxCoinAge))
             nCoinAge += nTxCoinAge;
         else
             return false;
@@ -2003,9 +2079,13 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
         pindexNew->pprev = (*miPrev).second;
         pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
     }
+    pindexNew->nTx = vtx.size();
     pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust();
-    pindexNew->pos = pos;
+    pindexNew->nChainTx = (pindexNew->pprev ? pindexNew->pprev->nChainTx : 0) + pindexNew->nTx;
+    pindexNew->nFile = pos.nFile;
+    pindexNew->nDataPos = pos.nPos;
     pindexNew->nUndoPos = 0;
+    pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA;
 
     // Compute stake entropy bit for stake modifier
     if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nTime)))
@@ -2029,33 +2109,29 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
     if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum))
         return error("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=0x%016"PRI64x, pindexNew->nHeight, nStakeModifier);
 
-    CChainDB chaindb;
-    if (!chaindb.TxnBegin())
-        return false;
-    chaindb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
-    if (!chaindb.TxnCommit())
-        return false;
+    setBlockIndexValid.insert(pindexNew);
 
-    // New best
-    if (pindexNew->nChainTrust > nBestChainTrust) {
-        if (!IsInitialBlockDownload() || (pindexNew->nHeight % 1) == 0)
-            if (!SetBestChain(pindexNew))
-                return false;
-    }
+    pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew));
+
+    // New best?
+    if (!ConnectBestBlock())
+        return false;
 
     if (pindexNew == pindexBest)
     {
         // Notify UI to display prev block's coinbase if it was ours
         static uint256 hashPrevBestCoinBase;
         UpdatedTransaction(hashPrevBestCoinBase);
-        hashPrevBestCoinBase = vtx[0].GetHash();
+        hashPrevBestCoinBase = GetTxHash(0);
     }
 
+    pblocktree->Flush();
+
     uiInterface.NotifyBlocksChanged();
     return true;
 }
 
-bool FindBlockPos(CChainDB &chaindb, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime)
+bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime)
 {
     bool fUpdatedLast = false;
 
@@ -2071,7 +2147,7 @@ bool FindBlockPos(CChainDB &chaindb, CDiskBlockPos &pos, unsigned int nAddSize,
         fclose(file);
         nLastBlockFile++;
         infoLastBlockFile.SetNull();
-        chaindb.ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine
+        pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine
         fUpdatedLast = true;
     }
 
@@ -2091,15 +2167,15 @@ bool FindBlockPos(CChainDB &chaindb, CDiskBlockPos &pos, unsigned int nAddSize,
         fclose(file);
     }
 
-    if (!chaindb.WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
+    if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
         return error("FindBlockPos() : cannot write updated block info");
     if (fUpdatedLast)
-        chaindb.WriteLastBlockFile(nLastBlockFile);
+        pblocktree->WriteLastBlockFile(nLastBlockFile);
 
     return true;
 }
 
-bool FindUndoPos(CChainDB &chaindb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
+bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
 {
     pos.nFile = nFile;
 
@@ -2109,15 +2185,15 @@ bool FindUndoPos(CChainDB &chaindb, int nFile, CDiskBlockPos &pos, unsigned int
     if (nFile == nLastBlockFile) {
         pos.nPos = infoLastBlockFile.nUndoSize;
         nNewSize = (infoLastBlockFile.nUndoSize += nAddSize);
-        if (!chaindb.WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
+        if (!pblocktree->WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
             return error("FindUndoPos() : cannot write updated block info");
     } else {
         CBlockFileInfo info;
-        if (!chaindb.ReadBlockFileInfo(nFile, info))
+        if (!pblocktree->ReadBlockFileInfo(nFile, info))
             return error("FindUndoPos() : cannot read block info");
         pos.nPos = info.nUndoSize;
         nNewSize = (info.nUndoSize += nAddSize);
-        if (!chaindb.WriteBlockFileInfo(nFile, info))
+        if (!pblocktree->WriteBlockFileInfo(nFile, info))
             return error("FindUndoPos() : cannot write updated block info");
     }
 
@@ -2179,10 +2255,6 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c
         // Check coinstake timestamp
         if (!CheckCoinStakeTimestamp(GetBlockTime(), (int64)vtx[1].nTime))
             return DoS(50, error("CheckBlock() : coinstake timestamp violation nTimeBlock=%"PRI64d" nTimeTx=%u", GetBlockTime(), vtx[1].nTime));
-
-        // NovaCoin: check proof-of-stake block signature
-        if (fCheckSig && !CheckBlockSignature(true))
-            return DoS(100, error("CheckBlock() : bad proof-of-stake block signature"));
     }
     else
     {
@@ -2203,8 +2275,8 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c
         {
             bool checkEntropySig = (GetBlockTime() < ENTROPY_SWITCH_TIME);
 
-            // NovaCoin: check proof-of-work block signature
-            if (checkEntropySig && !CheckBlockSignature(false))
+            // check legacy proof-of-work block signature
+            if (checkEntropySig && !CheckLegacySignature())
                 return DoS(100, error("CheckBlock() : bad proof-of-work block signature"));
         }
     }
@@ -2223,10 +2295,10 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c
 
     // Check for duplicate txids. This is caught by ConnectInputs(),
     // but catching it earlier avoids a potential DoS attack:
+    BuildMerkleTree();
     set<uint256> uniqueTx;
-    BOOST_FOREACH(const CTransaction& tx, vtx)
-    {
-        uniqueTx.insert(tx.GetHash());
+    for (unsigned int i=0; i<vtx.size(); i++) {
+        uniqueTx.insert(GetTxHash(i));
     }
     if (uniqueTx.size() != vtx.size())
         return DoS(100, error("CheckBlock() : duplicate transaction"));
@@ -2263,7 +2335,7 @@ bool CBlock::AcceptBlock()
 
     // Check proof-of-work or proof-of-stake
     if (nBits != GetNextTargetRequired(pindexPrev, IsProofOfStake()))
-        return DoS(100, error("AcceptBlock() : incorrect %s", IsProofOfWork() ? "proof-of-work" : "proof-of-stake"));
+        return DoS(100, error("AcceptBlock() : incorrect proof-of-%s amount", IsProofOfWork() ? "work" : "stake"));
 
     // Check timestamp against prev
     if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || FutureDrift(GetBlockTime()) < pindexPrev->GetBlockTime())
@@ -2299,8 +2371,7 @@ bool CBlock::AcceptBlock()
         return error("AcceptBlock() : out of disk space");
     CDiskBlockPos blockPos;
     {
-        CChainDB chaindb;
-        if (!FindBlockPos(chaindb, blockPos, nBlockSize+8, nHeight, nTime))
+        if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, nTime))
             return error("AcceptBlock() : FindBlockPos failed");
     }
     if (!WriteToDisk(blockPos))
@@ -2429,25 +2500,44 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
     if (mapOrphanBlocks.count(hash))
         return error("ProcessBlock() : already have block (orphan) %s", hash.ToString().substr(0,20).c_str());
 
-    // ppcoin: check proof-of-stake
-    // Limited duplicity on stake: prevents block flood attack
-    // Duplicate stake allowed only when there is orphan child block
-    if (pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
-        return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str());
-
     // Preliminary checks
     if (!pblock->CheckBlock())
         return error("ProcessBlock() : CheckBlock FAILED");
 
-    // ppcoin: verify hash target and signature of coinstake tx
     if (pblock->IsProofOfStake())
     {
-        uint256 hashProofOfStake = 0, targetProofOfStake = 0;
-        if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, hashProofOfStake, targetProofOfStake))
+        // Limited duplicity on stake: prevents block flood attack
+        // Duplicate stake allowed only when there is orphan child block
+        if (setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash) && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
+            return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString().c_str(), pblock->GetProofOfStake().second, hash.ToString().c_str());
+
+        bool fFatal = false;
+        uint256 hashProofOfStake;
+
+        // Verify proof-of-stake script, hash target and signature
+        if (!pblock->CheckSignature(fFatal, hashProofOfStake))
         {
-            printf("WARNING: ProcessBlock(): check proof-of-stake failed for block %s\n", hash.ToString().c_str());
-            return false; // do not error here as we expect this during initial block download
+            if (fFatal)
+            {
+                // Invalid coinstake script, blockhash signature or no generator defined, nothing to do here
+                // This also may occur when supplied proof-of-stake doesn't satisfy required target
+                if (pfrom)
+                    pfrom->Misbehaving(100);
+                return error("ProcessBlock() : invalid signatures found in proof-of-stake block %s", hash.ToString().c_str());
+            }
+            else
+            {
+                // Blockhash and coinstake signatures are OK but target checkings failed
+                // This may occur during initial block download
+
+                if (pfrom)
+                    pfrom->Misbehaving(1); // Small DoS penalty
+
+                printf("WARNING: ProcessBlock(): proof-of-stake target checkings failed for block %s, we'll try again later\n", hash.ToString().c_str());
+                return false;
+            }
         }
+
         if (!mapProofOfStake.count(hash)) // add to mapProofOfStake
             mapProofOfStake.insert(make_pair(hash, hashProofOfStake));
     }
@@ -2470,11 +2560,11 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
         {
             if (pfrom)
                 pfrom->Misbehaving(100);
-            return error("ProcessBlock() : block with too little %s", pblock->IsProofOfStake()? "proof-of-stake" : "proof-of-work");
+            return error("ProcessBlock() : block with too little proof-of-%s", pblock->IsProofOfStake() ? "stake" : "work");
         }
     }
 
-    // ppcoin: ask for pending sync-checkpoint if any
+    // Ask for pending sync-checkpoint if any
     if (!IsInitialBlockDownload())
         Checkpoints::AskForPendingSyncCheckpoint(pfrom);
 
@@ -2483,7 +2573,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
     {
         printf("ProcessBlock: ORPHAN BLOCK, prev=%s\n", pblock->hashPrevBlock.ToString().substr(0,20).c_str());
         CBlock* pblock2 = new CBlock(*pblock);
-        // ppcoin: check proof-of-stake
+
         if (pblock2->IsProofOfStake())
         {
             // Limited duplicity on stake: prevents block flood attack
@@ -2493,6 +2583,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
             else
                 setStakeSeenOrphan.insert(pblock2->GetProofOfStake());
         }
+
         mapOrphanBlocks.insert(make_pair(hash, pblock2));
         mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2));
 
@@ -2541,7 +2632,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
     return true;
 }
 
-// novacoin: attempt to generate suitable proof-of-stake
+// attempt to generate suitable proof-of-stake
 bool CBlock::SignBlock(CWallet& wallet)
 {
     // if we are trying to sign
@@ -2591,57 +2682,78 @@ bool CBlock::SignBlock(CWallet& wallet)
     return false;
 }
 
-// ppcoin: check block signature
-bool CBlock::CheckBlockSignature(bool fProofOfStake) const
+// get generation key
+bool CBlock::GetGenerator(CKey& GeneratorKey) const
+{
+    if(!IsProofOfStake())
+        return false;
+
+    vector<valtype> vSolutions;
+    txnouttype whichType;
+
+    const CTxOut& txout = vtx[1].vout[1];
+
+    if (!Solver(txout.scriptPubKey, whichType, vSolutions))
+        return false;
+    if (whichType == TX_PUBKEY)
+    {
+        valtype& vchPubKey = vSolutions[0];
+        CKey key;
+        return GeneratorKey.SetPubKey(vchPubKey);
+    }
+
+    return false;
+}
+
+// verify proof-of-stake signatures
+bool CBlock::CheckSignature(bool& fFatal, uint256& hashProofOfStake) const
 {
-    if (GetHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet))
-        return vchBlockSig.empty();
+    CKey key;
+
+    // no generator or invalid hash signature means fatal error
+    fFatal = !GetGenerator(key) || !key.Verify(GetHash(), vchBlockSig);
+
+    if (fFatal)
+        return false;
+
+    uint256 hashTarget = 0;
+    if (!CheckProofOfStake(vtx[1], nBits, hashProofOfStake, hashTarget, fFatal))
+        return false; // hash target mismatch or invalid coinstake signature
+
+    return true;
+}
+
+// verify legacy proof-of-work signature
+bool CBlock::CheckLegacySignature() const
+{
+    if (IsProofOfStake())
+        return false;
 
     vector<valtype> vSolutions;
     txnouttype whichType;
 
-    if(fProofOfStake)
+    for(unsigned int i = 0; i < vtx[0].vout.size(); i++)
     {
-        const CTxOut& txout = vtx[1].vout[1];
+        const CTxOut& txout = vtx[0].vout[i];
 
         if (!Solver(txout.scriptPubKey, whichType, vSolutions))
             return false;
+
         if (whichType == TX_PUBKEY)
         {
+            // Verify
             valtype& vchPubKey = vSolutions[0];
             CKey key;
             if (!key.SetPubKey(vchPubKey))
-                return false;
+                continue;
             if (vchBlockSig.empty())
-                return false;
-            return key.Verify(GetHash(), vchBlockSig);
+                continue;
+            if(!key.Verify(GetHash(), vchBlockSig))
+                continue;
+            return true;
         }
     }
-    else
-    {
-        for(unsigned int i = 0; i < vtx[0].vout.size(); i++)
-        {
-            const CTxOut& txout = vtx[0].vout[i];
 
-            if (!Solver(txout.scriptPubKey, whichType, vSolutions))
-                return false;
-
-            if (whichType == TX_PUBKEY)
-            {
-                // Verify
-                valtype& vchPubKey = vSolutions[0];
-                CKey key;
-                if (!key.SetPubKey(vchPubKey))
-                    continue;
-                if (vchBlockSig.empty())
-                    continue;
-                if(!key.Verify(GetHash(), vchBlockSig))
-                    continue;
-
-                return true;
-            }
-        }
-    }
     return false;
 }
 
@@ -2699,6 +2811,130 @@ FILE *OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
     return OpenDiskFile(pos, "rev", fReadOnly);
 }
 
+CBlockIndex * InsertBlockIndex(uint256 hash)
+{
+    if (hash == 0)
+        return NULL;
+
+    // Return existing
+    map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
+    if (mi != mapBlockIndex.end())
+        return (*mi).second;
+
+    // Create new
+    CBlockIndex* pindexNew = new CBlockIndex();
+    if (!pindexNew)
+        throw runtime_error("InsertBlockIndex() : new CBlockIndex failed");
+    mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
+    pindexNew->phashBlock = &((*mi).first);
+
+    return pindexNew;
+}
+
+bool static LoadBlockIndexDB()
+{
+    if (!pblocktree->LoadBlockIndexGuts())
+        return false;
+
+    if (fRequestShutdown)
+        return true;
+
+    // Calculate nChainTrust
+    vector<pair<int, CBlockIndex*> > vSortedByHeight;
+    vSortedByHeight.reserve(mapBlockIndex.size());
+    BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
+    {
+        CBlockIndex* pindex = item.second;
+        vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
+    }
+    sort(vSortedByHeight.begin(), vSortedByHeight.end());
+    BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
+    {
+        CBlockIndex* pindex = item.second;
+        pindex->nChainTrust = (pindex->pprev ? pindex->pprev->nChainTrust : 0) + pindex->GetBlockTrust();
+        pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx;
+        if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK))
+            setBlockIndexValid.insert(pindex);
+
+        // Calculate stake modifier checksum
+        pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex);
+        if (!CheckStakeModifierCheckpoints(pindex->nHeight, pindex->nStakeModifierChecksum))
+            return error("LoadBlockIndexDB() : Failed stake modifier checkpoint height=%d, modifier=0x%016"PRI64x, pindex->nHeight, pindex->nStakeModifier);
+    }
+
+    // Load block file info
+    pblocktree->ReadLastBlockFile(nLastBlockFile);
+    printf("LoadBlockIndexDB(): last block file = %i\n", nLastBlockFile);
+    if (pblocktree->ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile))
+        printf("LoadBlockIndexDB(): last block file: %s\n", infoLastBlockFile.ToString().c_str());
+
+    // Load hashBestChain pointer to end of best chain
+    pindexBest = pcoinsTip->GetBestBlock();
+    if (pindexBest == NULL)
+    {
+        if (pindexGenesisBlock == NULL)
+            return true;
+        return error("LoadBlockIndexDB() : hashBestChain not loaded");
+    }
+    hashBestChain = pindexBest->GetBlockHash();
+    nBestHeight = pindexBest->nHeight;
+    nBestChainTrust = pindexBest->nChainTrust;
+
+    // set 'next' pointers in best chain
+    CBlockIndex *pindex = pindexBest;
+    while(pindex != NULL && pindex->pprev != NULL) {
+         CBlockIndex *pindexPrev = pindex->pprev;
+         pindexPrev->pnext = pindex;
+         pindex = pindexPrev;
+    }
+    printf("LoadBlockIndexDB(): 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 sync-checkpoint
+    if (!pblocktree->ReadSyncCheckpoint(Checkpoints::hashSyncCheckpoint))
+        return error("LoadBlockIndexDB() : hashSyncCheckpoint not loaded");
+    printf("LoadBlockIndexDB(): synchronized checkpoint %s\n", Checkpoints::hashSyncCheckpoint.ToString().c_str());
+
+    // Load bnBestInvalidTrust, OK if it doesn't exist
+    CBigNum bnBestInvalidTrust;
+    pblocktree->ReadBestInvalidTrust(bnBestInvalidTrust);
+    nBestInvalidTrust = bnBestInvalidTrust.getuint256();
+
+    // Verify blocks in the best chain
+    int nCheckLevel = GetArg("-checklevel", 1);
+    int nCheckDepth = GetArg( "-checkblocks", 2500);
+    if (nCheckDepth == 0)
+        nCheckDepth = 1000000000; // suffices until the year 19000
+    if (nCheckDepth > nBestHeight)
+        nCheckDepth = nBestHeight;
+    printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
+    CBlockIndex* pindexFork = NULL;
+    for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
+    {
+        if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth)
+            break;
+        CBlock block;
+        if (!block.ReadFromDisk(pindex))
+            return error("LoadBlockIndexDB() : block.ReadFromDisk failed");
+        // check level 1: verify block validity
+        if (nCheckLevel>0 && !block.CheckBlock())
+        {
+            printf("LoadBlockIndexDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
+            pindexFork = pindex->pprev;
+        }
+        // TODO: stronger verifications
+    }
+    if (pindexFork && !fRequestShutdown)
+    {
+        // TODO: reorg back
+        return error("LoadBlockIndexDB(): chain database corrupted");
+    }
+
+    return true;
+}
+
+
 bool LoadBlockIndex(bool fAllowNew)
 {
     CBigNum bnTrustedModulus;
@@ -2726,14 +2962,10 @@ bool LoadBlockIndex(bool fAllowNew)
     ZCParams = new libzerocoin::Params(bnTrustedModulus);
 
     //
-    // Load block index
+    // Load block index from databases
     //
-    CChainDB chaindb("cr");
-    CCoinsDB coinsdb("cr");
-    if (!LoadBlockIndex(coinsdb, chaindb))
+    if (!LoadBlockIndexDB())
         return false;
-    chaindb.Close();
-    coinsdb.Close();
 
     //
     // Init with genesis block
@@ -2788,11 +3020,8 @@ bool LoadBlockIndex(bool fAllowNew)
         // Start new block file
         unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
         CDiskBlockPos blockPos;
-        {
-            CChainDB chaindb;
-            if (!FindBlockPos(chaindb, blockPos, nBlockSize+8, 0, block.nTime))
-                return error("AcceptBlock() : FindBlockPos failed");
-        }
+        if (!FindBlockPos(blockPos, nBlockSize+8, 0, block.nTime))
+            return error("AcceptBlock() : FindBlockPos failed");
         if (!block.WriteToDisk(blockPos))
             return error("LoadBlockIndex() : writing genesis block to disk failed");
         if (!block.AddToBlockIndex(blockPos))
@@ -2804,20 +3033,18 @@ bool LoadBlockIndex(bool fAllowNew)
     }
 
     string strPubKey = "";
+    // if checkpoint master key changed must reset sync-checkpoint
+    if (!pblocktree->ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey)
     {
-        CChainDB chaindb;
-        // if checkpoint master key changed must reset sync-checkpoint
-        if (!chaindb.ReadCheckpointPubKey(strPubKey) || strPubKey != CSyncCheckpoint::strMasterPubKey)
         {
+            LOCK(Checkpoints::cs_hashSyncCheckpoint);
             // write checkpoint master key to db
-            chaindb.TxnBegin();
-            if (!chaindb.WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey))
+            if (!pblocktree->WriteCheckpointPubKey(CSyncCheckpoint::strMasterPubKey))
                 return error("LoadBlockIndex() : failed to write new checkpoint master key to db");
-            if (!chaindb.TxnCommit())
-                return error("LoadBlockIndex() : failed to commit new checkpoint master key to db");
-            if ((!fTestNet) && !Checkpoints::ResetSyncCheckpoint())
-                return error("LoadBlockIndex() : failed to reset sync-checkpoint");
         }
+
+        if ((!fTestNet) && !Checkpoints::ResetSyncCheckpoint())
+            return error("LoadBlockIndex() : failed to reset sync-checkpoint");
     }
 
     return true;
@@ -3041,7 +3268,7 @@ string GetWarnings(string strFor)
 //
 
 
-bool static AlreadyHave(CCoinsDB &coinsdb, const CInv& inv)
+bool static AlreadyHave(const CInv& inv)
 {
     switch (inv.type)
     {
@@ -3053,7 +3280,7 @@ bool static AlreadyHave(CCoinsDB &coinsdb, const CInv& inv)
                 txInMap = mempool.exists(inv.hash);
             }
             return txInMap || mapOrphanTransactions.count(inv.hash) ||
-                coinsdb.HaveCoins(inv.hash);
+                pcoinsTip->HaveCoins(inv.hash);
         }
     case MSG_BLOCK:
         return mapBlockIndex.count(inv.hash) ||
@@ -3129,6 +3356,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
             return true;
         }
 
+        if (pfrom->nVersion < 60010)
+        {
+            printf("partner %s using a buggy client %d, disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion);
+            pfrom->fDisconnect = true;
+            return true;
+        }
+
         // record my external IP reported by peer
         if (addrFrom.IsRoutable() && addrMe.IsRoutable())
             addrSeenByPeer = addrMe;
@@ -3306,7 +3540,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
                 break;
             }
         }
-        CCoinsDB coinsdb("r");
         for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
         {
             const CInv &inv = vInv[nInv];
@@ -3315,7 +3548,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
                 return true;
             pfrom->AddInventoryKnown(inv);
 
-            bool fAlreadyHave = AlreadyHave(coinsdb, inv);
+            bool fAlreadyHave = AlreadyHave(inv);
             if (fDebug)
                 printf("  got inventory: %s  %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new");
 
@@ -3503,7 +3736,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
         vector<uint256> vWorkQueue;
         vector<uint256> vEraseQueue;
         CDataStream vMsg(vRecv);
-        CCoinsDB coinsdb("r");
         CTransaction tx;
         vRecv >> tx;
 
@@ -3511,9 +3743,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
         pfrom->AddInventoryKnown(inv);
 
         bool fMissingInputs = false;
-        if (tx.AcceptToMemoryPool(coinsdb, true, &fMissingInputs))
+        if (tx.AcceptToMemoryPool(true, &fMissingInputs))
         {
-            SyncWithWallets(tx, NULL, true);
+            SyncWithWallets(inv.hash, tx, NULL, true);
             RelayTransaction(tx, inv.hash);
             mapAlreadyAskedFor.erase(inv);
             vWorkQueue.push_back(inv.hash);
@@ -3531,10 +3763,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
                     CTransaction& orphanTx = mapOrphanTransactions[orphanTxHash];
                     bool fMissingInputs2 = false;
 
-                    if (orphanTx.AcceptToMemoryPool(coinsdb, true, &fMissingInputs2))
+                    if (orphanTx.AcceptToMemoryPool(true, &fMissingInputs2))
                     {
                         printf("   accepted orphan tx %s\n", orphanTxHash.ToString().substr(0,10).c_str());
-                        SyncWithWallets(tx, NULL, true);
+                        SyncWithWallets(inv.hash, tx, NULL, true);
                         RelayTransaction(orphanTx, orphanTxHash);
                         mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanTxHash));
                         vWorkQueue.push_back(orphanTxHash);
@@ -3983,11 +4215,10 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
         //
         vector<CInv> vGetData;
         int64 nNow = GetTime() * 1000000;
-        CCoinsDB coinsdb("r");
         while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
         {
             const CInv& inv = (*pto->mapAskFor.begin()).second;
-            if (!AlreadyHave(coinsdb, inv))
+            if (!AlreadyHave(inv))
             {
                 if (fDebugNet)
                     printf("sending getdata: %s\n", inv.ToString().c_str());