diff --git a/src/db.cpp b/src/db.cpp index acc0cc5a5..1b21a467a 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -157,7 +157,7 @@ void CDB::Close() --mapFileUseCount[strFile]; } -void CDB::CloseDb(const string& strFile) +static void CloseDb(const string& strFile) { CRITICAL_BLOCK(cs_db) { @@ -291,7 +291,7 @@ void DBFlush(bool fShutdown) if (nRefCount == 0) { // Move log data to the dat file - CDB::CloseDb(strFile); + CloseDb(strFile); dbenv.txn_checkpoint(0, 0, 0); printf("%s flush\n", strFile.c_str()); dbenv.lsn_reset(strFile.c_str(), 0); @@ -753,7 +753,7 @@ void ThreadFlushWalletDB(void* parg) int64 nStart = GetTimeMillis(); // Flush wallet.dat so it's self contained - CDB::CloseDb(strFile); + CloseDb(strFile); dbenv.txn_checkpoint(0, 0, 0); dbenv.lsn_reset(strFile.c_str(), 0); @@ -777,7 +777,7 @@ bool BackupWallet(const CWallet& wallet, const string& strDest) if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0) { // Flush log data to the dat file - CDB::CloseDb(wallet.strWalletFile); + CloseDb(wallet.strWalletFile); dbenv.txn_checkpoint(0, 0, 0); dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0); mapFileUseCount.erase(wallet.strWalletFile); diff --git a/src/db.h b/src/db.h index 24bdd84d1..4b06d4423 100644 --- a/src/db.h +++ b/src/db.h @@ -43,12 +43,10 @@ class CDB std::vector vTxn; bool fReadOnly; -public: // FIXME: making consturctor/destructor public, so namedb can be loaded from init.cpp without dependency on namecoin.h explicit CDB(const char* pszFile, const char* pszMode="r+"); ~CDB() { Close(); } public: void Close(); - static void CloseDb(const std::string& strFile); private: CDB(const CDB&); void operator=(const CDB&); diff --git a/src/init.cpp b/src/init.cpp index 9b23fdd1c..92b8f1bc5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -529,27 +529,20 @@ bool AppInit2(int argc, char* argv[]) RandAddSeedPerfmon(); - const char *name_db_file = "nameindexfull.dat"; - filesystem::path nameindexfile = filesystem::path(GetDataDir()) / name_db_file; - if (!filesystem::exists(nameindexfile)) - { - //PrintConsole("Scanning blockchain for names to create fast index..."); - rescanfornames(); - //PrintConsole("\n"); - } - else // Name bug workaround + filesystem::path nameindexfile_old = filesystem::path(GetDataDir()) / "nameindexfull.dat"; + filesystem::path nameindexfile = filesystem::path(GetDataDir()) / "nameindex.dat"; + + if (filesystem::exists(nameindexfile_old)) { - CDB dbName(name_db_file, "r"); - int nVersion; - if (!dbName.ReadVersion(nVersion) || nVersion < 37200) - { - dbName.Close(); - CDB::CloseDb(name_db_file); + // If old file exists - delete it and recan + filesystem::remove(nameindexfile_old); + // Also delete new file if it exists together with the old one, as it could be the one from a much older version + if (filesystem::exists(nameindexfile)) filesystem::remove(nameindexfile); - printf("Name DB is of old version containing a bug. Forcing rescan.\n"); - rescanfornames(); - } + rescanfornames(); } + else if (!filesystem::exists(nameindexfile)) + rescanfornames(); if (!CreateThread(StartNode, NULL)) wxMessageBox("Error: CreateThread(StartNode) failed", "Namecoin"); diff --git a/src/namecoin.cpp b/src/namecoin.cpp index 0ff200844..89a086f8a 100644 --- a/src/namecoin.cpp +++ b/src/namecoin.cpp @@ -1497,7 +1497,7 @@ bool CNameDB::ScanNames( } // true - accept, false - reject -bool NameBugWorkaround(const CTransaction& tx, CTxDB &txdb) +bool NameBugWorkaround(const CTransaction& tx, CTxDB &txdb, CTransaction *pTxPrev = NULL) { // Find previous name tx bool found = false; @@ -1526,6 +1526,8 @@ bool NameBugWorkaround(const CTransaction& tx, CTxDB &txdb) if (found) return error("NameBugWorkaround WARNING: multiple previous name transactions"); found = true; + if (pTxPrev) + *pTxPrev = txPrev; } } @@ -1561,11 +1563,30 @@ bool NameBugWorkaround(const CTransaction& tx, CTxDB &txdb) return true; } +// Checks that the last transaction in vtxPos is equal to tx +// The test is optimistic, i.e. it is assumed that tx is correct, +// unless name bug is in action (so outside of the buggy block range +// the test is skipped). This function must be called only if op == OP_NAME_UPDATE, +// becauseduring firstupdate there is no entry in vtxPos +bool CheckNameTxPos(const vector &vtxPos, const CTransaction& tx) +{ + if (vtxPos.empty()) + return false; + if (vtxPos.back().nHeight < BUG_WORKAROUND_BLOCK_START || vtxPos.back().nHeight >= BUG_WORKAROUND_BLOCK) + return true; + + // During the name bug period we must check that tx was really used, + // as it could have been rejected - in this case do not disconnect + const CDiskTxPos &txPos = vtxPos.back().txPos; + CTransaction prevTx; + if (prevTx.ReadFromDisk(txPos) && prevTx == tx) + return true; + return false; +} + bool CNameDB::ReconstructNameIndex() { CTxDB txdb("r"); - vector vchName; - vector vchValue; int nHeight; CTxIndex txindex; CBlockIndex* pindex = pindexGenesisBlock; @@ -1582,20 +1603,27 @@ bool CNameDB::ReconstructNameIndex() { if (tx.nVersion != NAMECOIN_TX_VERSION) continue; + + vector > vvchArgs; + int op; + int nOut; - if(!GetNameOfTx(tx, vchName)) + if (!DecodeNameTx(tx, op, nOut, vvchArgs)) continue; - if(!GetValueOfNameTx(tx, vchValue)) + if (op == OP_NAME_NEW) continue; + const vector &vchName = vvchArgs[0]; + const vector &vchValue = vvchArgs[op == OP_NAME_FIRSTUPDATE ? 2 : 1]; if(!txdb.ReadDiskTx(tx.GetHash(), tx, txindex)) continue; nHeight = GetTxPosHeight(txindex.pos); // Bug workaround + CTransaction txPrev; if (nHeight >= BUG_WORKAROUND_BLOCK_START && nHeight < BUG_WORKAROUND_BLOCK) - if (!NameBugWorkaround(tx, txdb)) + if (!NameBugWorkaround(tx, txdb, &txPrev)) { printf("NameBugWorkaround rejected tx %s at height %d (name %s)\n", tx.GetHash().ToString().c_str(), nHeight, stringFromVch(vchName).c_str()); continue; @@ -1603,11 +1631,17 @@ bool CNameDB::ReconstructNameIndex() vector vtxPos; if (ExistsName(vchName)) - { + { if (!ReadName(vchName, vtxPos)) return error("Rescanfornames() : failed to read from name DB"); } + if (op == OP_NAME_UPDATE && nHeight >= BUG_WORKAROUND_BLOCK_START && nHeight < BUG_WORKAROUND_BLOCK && !CheckNameTxPos(vtxPos, txPrev)) + { + printf("NameBugWorkaround rejected tx %s at height %d (name %s), because previous tx %s was also rejected\n", tx.GetHash().ToString().c_str(), nHeight, stringFromVch(vchName).c_str(), txPrev.GetHash().ToString().c_str()); + continue; + } + CNameIndex txPos2; txPos2.nHeight = nHeight; txPos2.vValue = vchValue; @@ -1814,7 +1848,7 @@ bool CNamecoinHooks::IsMine(const CTransaction& tx, const CTxOut& txout, bool ig if (!DecodeNameScript(txout.scriptPubKey, op, vvch)) return false; - + if (ignore_name_new && op == OP_NAME_NEW) return false; @@ -1925,13 +1959,14 @@ bool CNamecoinHooks::ConnectInputs(CTxDB& txdb, bool fBlock, bool fMiner) { - bool nInput; + int nInput; bool found = false; int prevOp; vector > vvchPrevArgs; - for (int i = 0 ; i < tx.vin.size() ; i++) { + for (int i = 0; i < tx.vin.size(); i++) + { CTxOut& out = vTxPrev[i].vout[tx.vin[i].prevout.n]; if (DecodeNameScript(out.scriptPubKey, prevOp, vvchPrevArgs)) { @@ -2063,12 +2098,24 @@ bool CNamecoinHooks::ConnectInputs(CTxDB& txdb, if (!fBugWorkaround) { - if (fBlock) - { - CNameDB dbName("cr+", txdb); + CNameDB dbName("cr+", txdb); + dbName.TxnBegin(); - dbName.TxnBegin(); + if (!fBlock && op == OP_NAME_UPDATE) + { + vector vtxPos; + if (dbName.ExistsName(vvchArgs[0])) + { + if (!dbName.ReadName(vvchArgs[0], vtxPos)) + return error("ConnectInputsHook() : failed to read from name DB"); + } + // Valid tx on top of buggy tx: if not in block, reject + if (!CheckNameTxPos(vtxPos, vTxPrev[nInput])) + return error("ConnectInputsHook() : Name bug workaround: tx %s rejected, since previous tx (%s) is not in the name DB\n", tx.GetHash().ToString().c_str(), vTxPrev[nInput].GetHash().ToString().c_str()); + } + if (fBlock) + { if (op == OP_NAME_FIRSTUPDATE || op == OP_NAME_UPDATE) { //vector vtxPos; @@ -2078,32 +2125,45 @@ bool CNamecoinHooks::ConnectInputs(CTxDB& txdb, if (!dbName.ReadName(vvchArgs[0], vtxPos)) return error("ConnectInputsHook() : failed to read from name DB"); } - vector vchValue; // add - int nHeight; - uint256 hash; - GetValueOfTxPos(txPos, vchValue, hash, nHeight); - CNameIndex txPos2; - txPos2.nHeight = pindexBlock->nHeight; - txPos2.vValue = vchValue; - txPos2.txPos = txPos; - vtxPos.push_back(txPos2); // fin add - //vtxPos.push_back(txPos); - if (!dbName.WriteName(vvchArgs[0], vtxPos)) + if (op == OP_NAME_UPDATE) { - return error("ConnectInputsHook() : failed to write to name DB"); + if (CheckNameTxPos(vtxPos, vTxPrev[nInput])) + { + vector vchValue; // add + int nHeight; + uint256 hash; + GetValueOfTxPos(txPos, vchValue, hash, nHeight); + CNameIndex txPos2; + txPos2.nHeight = pindexBlock->nHeight; + txPos2.vValue = vchValue; + txPos2.txPos = txPos; + vtxPos.push_back(txPos2); // fin add + //vtxPos.push_back(txPos); + if (!dbName.WriteName(vvchArgs[0], vtxPos)) + { + return error("ConnectInputsHook() : failed to write to name DB"); + } + } + else + { + printf("ConnectInputsHook() : Name bug workaround: tx %s rejected, since previous tx (%s) is not in the name DB\n", tx.GetHash().ToString().c_str(), vTxPrev[nInput].GetHash().ToString().c_str()); + // Valid tx on top of buggy tx: reject only after hard-fork + if (pindexBlock->nHeight >= BUG_WORKAROUND_BLOCK) + return false; + } } } - dbName.TxnCommit(); - } - if (fBlock && op != OP_NAME_NEW) - CRITICAL_BLOCK(cs_main) - { - std::map, std::set >::iterator mi = mapNamePending.find(vvchArgs[0]); - if (mi != mapNamePending.end()) - mi->second.erase(tx.GetHash()); - } + if (op != OP_NAME_NEW) + CRITICAL_BLOCK(cs_main) + { + std::map, std::set >::iterator mi = mapNamePending.find(vvchArgs[0]); + if (mi != mapNamePending.end()) + mi->second.erase(tx.GetHash()); + } + } + dbName.TxnCommit(); } return true; @@ -2137,7 +2197,9 @@ bool CNamecoinHooks::DisconnectInputs(CTxDB& txdb, // be empty, since a reorg cannot go that far back. Be safe anyway and do not try to pop if empty. if (vtxPos.size()) { - vtxPos.pop_back(); + if (op == OP_NAME_UPDATE && CheckNameTxPos(vtxPos, tx)) + vtxPos.pop_back(); + // TODO validate that the first pos is the current tx pos } if (!dbName.WriteName(vvchArgs[0], vtxPos)) diff --git a/src/namecoin.h b/src/namecoin.h index b0cb5482b..f9a8fdf33 100644 --- a/src/namecoin.h +++ b/src/namecoin.h @@ -6,11 +6,11 @@ class CNameDB : public CDB protected: bool fHaveParent; public: - CNameDB(const char* pszMode="r+") : CDB("nameindexfull.dat", pszMode) { + CNameDB(const char* pszMode="r+") : CDB("nameindex.dat", pszMode) { fHaveParent = false; } - CNameDB(const char* pszMode, CDB& parent) : CDB("nameindexfull.dat", pszMode) { + CNameDB(const char* pszMode, CDB& parent) : CDB("nameindex.dat", pszMode) { vTxn.push_back(parent.GetTxn()); fHaveParent = true; }