Permalink
Browse files

Name bug fix.

Hard-fork is set to happen at block 150000 (~76 days from now). Before that bad name_* transactions are allowed, but won't be processed (i.e. won't be saved to name DB). After that they will be rejected, as well as blocks containing them.

At first start nameindexfull.dat will be rescanned (slow - be patient). If something goes wrong, delete it to force another rescan.
nameindexfull.dat is checked by version (rescanned if below 0.3.72).
  • Loading branch information...
1 parent 34b38c5 commit d70d01ff77310d3755891cb43cb2700b553019f4 @namecoin-qt namecoin-qt committed Oct 15, 2013
Showing with 175 additions and 39 deletions.
  1. +1 −1 namecoin-qt.pro
  2. +1 −1 src/db.cpp
  3. +1 −0 src/db.h
  4. +15 −1 src/init.cpp
  5. +156 −35 src/namecoin.cpp
  6. +1 −1 src/serialize.h
View
@@ -1,7 +1,7 @@
TEMPLATE = app
TARGET = namecoin-qt
macx:TARGET = "Namecoin-Qt"
-VERSION = 0.3.71
+VERSION = 0.3.72
INCLUDEPATH += src src/json src/qt
QT += network
DEFINES += GUI QT_GUI BOOST_THREAD_USE_LIB BOOST_SPIRIT_THREADSAFE
View
@@ -157,7 +157,7 @@ void CDB::Close()
--mapFileUseCount[strFile];
}
-void static CloseDb(const string& strFile)
+void CDB::CloseDb(const string& strFile)
{
CRITICAL_BLOCK(cs_db)
{
View
@@ -47,6 +47,7 @@ class CDB
~CDB() { Close(); }
public:
void Close();
+ static void CloseDb(const std::string& strFile);
private:
CDB(const CDB&);
void operator=(const CDB&);
View
@@ -529,13 +529,27 @@ bool AppInit2(int argc, char* argv[])
RandAddSeedPerfmon();
- filesystem::path nameindexfile = filesystem::path(GetDataDir()) / "nameindexfull.dat";
+ 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
+ {
+ CDB dbName(name_db_file, "r");
+ int nVersion;
+ if (!dbName.ReadVersion(nVersion) || nVersion < 37200)
+ {
+ dbName.Close();
+ CDB::CloseDb(name_db_file);
+ filesystem::remove(nameindexfile);
+ printf("Name DB is of old version containing a bug. Forcing rescan.\n");
+ rescanfornames();
+ }
+ }
if (!CreateThread(StartNode, NULL))
wxMessageBox("Error: CreateThread(StartNode) failed", "Namecoin");
View
@@ -27,6 +27,9 @@ extern int64 AmountFromValue(const Value& value);
extern Object JSONRPCError(int code, const string& message);
template<typename T> void ConvertTo(Value& value, bool fAllowNull=false);
+static const int BUG_WORKAROUND_BLOCK_START = 139750; // Bug was not exploited before block 139872, so skip checking earlier blocks
+static const int BUG_WORKAROUND_BLOCK = 150000; // Point of hard fork
+
map<vector<unsigned char>, uint256> mapMyNames;
map<vector<unsigned char>, set<uint256> > mapNamePending;
@@ -1493,6 +1496,71 @@ bool CNameDB::ScanNames(
return true;
}
+// true - accept, false - reject
+bool NameBugWorkaround(const CTransaction& tx, CTxDB &txdb)
+{
+ // Find previous name tx
+ bool found = false;
+ int prevOp;
+ vector<vector<unsigned char> > vvchPrevArgs;
+
+ for (int i = 0; i < tx.vin.size(); i++)
+ {
+ COutPoint prevout = tx.vin[i].prevout;
+ CTxIndex txindex;
+ if (!txdb.ReadTxIndex(prevout.hash, txindex))
+ {
+ printf("NameBugWorkaround WARNING: cannot read tx index of previous tx (hash: %s)\n", prevout.hash.ToString().c_str());
+ continue;
+ }
+ CTransaction txPrev;
+ if (!txPrev.ReadFromDisk(txindex.pos))
+ {
+ printf("NameBugWorkaround WARNING: cannot read previous tx from disk (hash: %s)\n", prevout.hash.ToString().c_str());
+ continue;
+ }
+
+ CTxOut& out = txPrev.vout[tx.vin[i].prevout.n];
+ if (DecodeNameScript(out.scriptPubKey, prevOp, vvchPrevArgs))
+ {
+ if (found)
+ return error("NameBugWorkaround WARNING: multiple previous name transactions");
+ found = true;
+ }
+ }
+
+ if (!found)
+ return error("NameBugWorkaround WARNING: prev tx not found");
+
+ vector<vector<unsigned char> > vvchArgs;
+ int op;
+ int nOut;
+
+ if (!DecodeNameTx(tx, op, nOut, vvchArgs))
+ return error("NameBugWorkaround WARNING: cannot decode name tx\n");
+
+ if (op == OP_NAME_FIRSTUPDATE)
+ {
+ // Check hash
+ const vector<unsigned char> &vchHash = vvchPrevArgs[0];
+ const vector<unsigned char> &vchName = vvchArgs[0];
+ const vector<unsigned char> &vchRand = vvchArgs[1];
+ vector<unsigned char> vchToHash(vchRand);
+ vchToHash.insert(vchToHash.end(), vchName.begin(), vchName.end());
+ uint160 hash = Hash160(vchToHash);
+ if (uint160(vchHash) != hash)
+ return false;
+ }
+ else if (op == OP_NAME_UPDATE)
+ {
+ // Check name
+ if (vvchPrevArgs[0] != vvchArgs[0])
+ return false;
+ }
+
+ return true;
+}
+
bool CNameDB::ReconstructNameIndex()
{
CTxDB txdb("r");
@@ -1525,13 +1593,21 @@ bool CNameDB::ReconstructNameIndex()
continue;
nHeight = GetTxPosHeight(txindex.pos);
+ // Bug workaround
+ if (nHeight >= BUG_WORKAROUND_BLOCK_START && nHeight < BUG_WORKAROUND_BLOCK)
+ if (!NameBugWorkaround(tx, txdb))
+ {
+ printf("NameBugWorkaround rejected tx %s at height %d (name %s)\n", tx.GetHash().ToString().c_str(), nHeight, stringFromVch(vchName).c_str());
+ continue;
+ }
- vector<CNameIndex> vtxPos;
+ vector<CNameIndex> vtxPos;
if (ExistsName(vchName))
{
if (!ReadName(vchName, vtxPos))
return error("Rescanfornames() : failed to read from name DB");
}
+
CNameIndex txPos2;
txPos2.nHeight = nHeight;
txPos2.vValue = vchValue;
@@ -1887,6 +1963,8 @@ bool CNamecoinHooks::ConnectInputs(CTxDB& txdb,
int nDepth;
int64 nNetFee;
+ bool fBugWorkaround = false;
+
switch (op)
{
case OP_NAME_NEW:
@@ -1899,6 +1977,28 @@ bool CNamecoinHooks::ConnectInputs(CTxDB& txdb,
return error("ConnectInputsHook() : got tx %s with fee too low %d", tx.GetHash().GetHex().c_str(), nNetFee);
if (!found || prevOp != OP_NAME_NEW)
return error("ConnectInputsHook() : name_firstupdate tx without previous name_new tx");
+
+ {
+ // Check hash
+ const vector<unsigned char> &vchHash = vvchPrevArgs[0];
+ const vector<unsigned char> &vchName = vvchArgs[0];
+ const vector<unsigned char> &vchRand = vvchArgs[1];
+ vector<unsigned char> vchToHash(vchRand);
+ vchToHash.insert(vchToHash.end(), vchName.begin(), vchName.end());
+ uint160 hash = Hash160(vchToHash);
+ if (uint160(vchHash) != hash)
+ {
+ if (pindexBlock->nHeight >= BUG_WORKAROUND_BLOCK)
+ return error("ConnectInputsHook() : name_firstupdate hash mismatch");
+ else
+ {
+ // Accept bad transactions before the hard-fork point, but do not write them to name DB
+ printf("ConnectInputsHook() : name_firstupdate mismatch bug workaround");
+ fBugWorkaround = true;
+ }
+ }
+ }
+
nPrevHeight = GetNameHeight(txdb, vvchArgs[0]);
if (nPrevHeight >= 0 && pindexBlock->nHeight - nPrevHeight < GetExpirationDepth(pindexBlock->nHeight))
return error("ConnectInputsHook() : name_firstupdate on an unexpired name");
@@ -1932,6 +2032,20 @@ bool CNamecoinHooks::ConnectInputs(CTxDB& txdb,
case OP_NAME_UPDATE:
if (!found || (prevOp != OP_NAME_FIRSTUPDATE && prevOp != OP_NAME_UPDATE))
return error("name_update tx without previous update tx");
+
+ // Check name
+ if (vvchPrevArgs[0] != vvchArgs[0])
+ {
+ if (pindexBlock->nHeight >= BUG_WORKAROUND_BLOCK)
+ return error("ConnectInputsHook() : name_update name mismatch");
+ else
+ {
+ // Accept bad transactions before the hard-fork point, but do not write them to name DB
+ printf("ConnectInputsHook() : name_update mismatch bug workaround");
+ fBugWorkaround = true;
+ }
+ }
+
// TODO CPU intensive
nDepth = CheckTransactionAtRelativeDepth(pindexBlock, vTxindex[nInput], GetExpirationDepth(pindexBlock->nHeight));
if ((fBlock || fMiner) && nDepth < 0)
@@ -1941,48 +2055,55 @@ bool CNamecoinHooks::ConnectInputs(CTxDB& txdb,
return error("ConnectInputsHook() : name transaction has unknown op");
}
- if (fBlock)
- {
- CNameDB dbName("cr+", txdb);
+ // If fBugWorkaround is in action, do not update name DB (i.e. just silently accept bad tx to avoid early hard-fork)
+ // Also disallow mining bad txes
- dbName.TxnBegin();
+ if (fMiner && fBugWorkaround)
+ return error("ConnectInputsHook(): mismatch bug workaround - should not mine this tx");
- if (op == OP_NAME_FIRSTUPDATE || op == OP_NAME_UPDATE)
+ if (!fBugWorkaround)
+ {
+ if (fBlock)
{
- //vector<CDiskTxPos> vtxPos;
- vector<CNameIndex> vtxPos;
- if (dbName.ExistsName(vvchArgs[0]))
- {
- if (!dbName.ReadName(vvchArgs[0], vtxPos))
- return error("ConnectInputsHook() : failed to read from name DB");
- }
- vector<unsigned char> 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))
+ CNameDB dbName("cr+", txdb);
+
+ dbName.TxnBegin();
+
+ if (op == OP_NAME_FIRSTUPDATE || op == OP_NAME_UPDATE)
{
- return error("ConnectInputsHook() : failed to write to name DB");
+ //vector<CDiskTxPos> vtxPos;
+ vector<CNameIndex> vtxPos;
+ if (dbName.ExistsName(vvchArgs[0]))
+ {
+ if (!dbName.ReadName(vvchArgs[0], vtxPos))
+ return error("ConnectInputsHook() : failed to read from name DB");
+ }
+ vector<unsigned char> 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");
+ }
}
- }
- dbName.TxnCommit();
- }
+ dbName.TxnCommit();
+ }
- CRITICAL_BLOCK(cs_main)
- {
if (fBlock && op != OP_NAME_NEW)
- {
- std::map<std::vector<unsigned char>, std::set<uint256> >::iterator mi = mapNamePending.find(vvchArgs[0]);
- if (mi != mapNamePending.end())
- mi->second.erase(tx.GetHash());
- }
+ CRITICAL_BLOCK(cs_main)
+ {
+ std::map<std::vector<unsigned char>, std::set<uint256> >::iterator mi = mapNamePending.find(vvchArgs[0]);
+ if (mi != mapNamePending.end())
+ mi->second.erase(tx.GetHash());
+ }
}
return true;
View
@@ -35,7 +35,7 @@ class CDataStream;
class CAutoFile;
static const unsigned int MAX_SIZE = 0x02000000;
-static const int VERSION = 37100;
+static const int VERSION = 37200;
static const char* pszSubVer = "";
static const bool VERSION_IS_BETA = false;

0 comments on commit d70d01f

Please sign in to comment.