Skip to content

Commit

Permalink
An improved patch.
Browse files Browse the repository at this point in the history
1. Added check that previous tx was good. Otherwise, while bad tx is rejected, bad on top of bad would be accepted, since the name matches.
This however degrades the performance slightly (additional access to name db).

2. Renamed nameindexfull.dat to nameindex.dat. Hopefully this would fix the rescan crash issue of the previous commit.
nameindex.dat was used in some older version though. When we agree the patch is final, we should rename to e.g. namedb.dat and make another commit.
  • Loading branch information
namecoin-qt committed Oct 16, 2013
1 parent 2b90295 commit 753336a
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 62 deletions.
8 changes: 4 additions & 4 deletions 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 CDB::CloseDb(const string& strFile)
static void CloseDb(const string& strFile)
{
CRITICAL_BLOCK(cs_db)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand All @@ -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);
Expand Down
2 changes: 0 additions & 2 deletions src/db.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,10 @@ class CDB
std::vector<DbTxn*> 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&);
Expand Down
29 changes: 11 additions & 18 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
134 changes: 98 additions & 36 deletions src/namecoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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<CNameIndex> &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<unsigned char> vchName;
vector<unsigned char> vchValue;
int nHeight;
CTxIndex txindex;
CBlockIndex* pindex = pindexGenesisBlock;
Expand All @@ -1582,32 +1603,45 @@ bool CNameDB::ReconstructNameIndex()
{
if (tx.nVersion != NAMECOIN_TX_VERSION)
continue;

vector<vector<unsigned char> > 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<unsigned char> &vchName = vvchArgs[0];
const vector<unsigned char> &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;
}

vector<CNameIndex> 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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -1925,13 +1959,14 @@ bool CNamecoinHooks::ConnectInputs(CTxDB& txdb,
bool fBlock,
bool fMiner)
{
bool nInput;
int nInput;
bool found = false;

int prevOp;
vector<vector<unsigned char> > 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))
{
Expand Down Expand Up @@ -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<CNameIndex> 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<CDiskTxPos> vtxPos;
Expand All @@ -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<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))
if (op == OP_NAME_UPDATE)
{
return error("ConnectInputsHook() : failed to write to name DB");
if (CheckNameTxPos(vtxPos, vTxPrev[nInput]))
{
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");
}
}
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::vector<unsigned char>, std::set<uint256> >::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::vector<unsigned char>, std::set<uint256> >::iterator mi = mapNamePending.find(vvchArgs[0]);
if (mi != mapNamePending.end())
mi->second.erase(tx.GetHash());
}
}
dbName.TxnCommit();
}

return true;
Expand Down Expand Up @@ -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))
Expand Down
4 changes: 2 additions & 2 deletions src/namecoin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down

0 comments on commit 753336a

Please sign in to comment.