Skip to content

Commit

Permalink
Name bug fix.
Browse files Browse the repository at this point in the history
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
namecoin-qt committed Oct 15, 2013
1 parent 34b38c5 commit d70d01f
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 39 deletions.
2 changes: 1 addition & 1 deletion namecoin-qt.pro
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
1 change: 1 addition & 0 deletions src/db.h
Original file line number Diff line number Diff line change
Expand Up @@ -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&);
Expand Down
16 changes: 15 additions & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
191 changes: 156 additions & 35 deletions src/namecoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1887,6 +1963,8 @@ bool CNamecoinHooks::ConnectInputs(CTxDB& txdb,
int nDepth;
int64 nNetFee;

bool fBugWorkaround = false;

switch (op)
{
case OP_NAME_NEW:
Expand All @@ -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");
Expand Down Expand Up @@ -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)
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/serialize.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

1 comment on commit d70d01f

@namecoin-qt
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a small bug with compiling (protected constructor/destructor in CDB) - hurried up a bit. Fixed in the next commit 2b90295

Please sign in to comment.