/* undo is set if the change is due to disconnecting blocks / going back in time. The ordinary case (!undo) means that we update the name normally, going forward in time. This is important for keeping track of the name history. */ void CCoinsViewCache::SetName(const valtype &name, const CNameData& data, bool undo) { CNameData oldData; if (GetName(name, oldData)) { cacheNames.removeExpireIndex(name, oldData.getHeight()); /* Update the name history. If we are undoing, we expect that the top history item matches the data being set now. If we are not undoing, push the overwritten data onto the history stack. Note that we only have to do this if the name already existed in the database. Otherwise, no special action is required for the name history. */ if (fNameHistory) { CNameHistory history; if (!GetNameHistory(name, history)) { /* Ensure that the history stack is indeed (still) empty and was not modified by the failing GetNameHistory call. */ assert(history.empty()); } if (undo) history.pop(data); else history.push(oldData); cacheNames.setHistory(name, history); } } else assert (!undo); cacheNames.set(name, data); cacheNames.addExpireIndex(name, data.getHeight()); }
void refreshName(const std::vector<unsigned char> &inName) { LOCK(cs_main); NameTableEntry nameObj(ValtypeToString(inName), std::string(""), NameTableEntry::NAME_NON_EXISTING); CNameData data; { LOCK (cs_main); if (!pcoinsTip->GetName (inName, data)) { LogPrintf ("name not found: '%s'\n", ValtypeToString (inName).c_str()); return; } nameObj = NameTableEntry(ValtypeToString(inName), ValtypeToString(data.getValue ()), data.getHeight ()); } // Find name in model QList<NameTableEntry>::iterator lower = qLowerBound( cachedNameTable.begin(), cachedNameTable.end(), nameObj.name, NameTableEntryLessThan()); QList<NameTableEntry>::iterator upper = qUpperBound( cachedNameTable.begin(), cachedNameTable.end(), nameObj.name, NameTableEntryLessThan()); bool inModel = (lower != upper); if (inModel) { // In model - update or delete if (nameObj.nHeight != NameTableEntry::NAME_NON_EXISTING) { LogPrintf ("refreshName result : %s - refreshed in the table\n", qPrintable(nameObj.name)); updateEntry(nameObj.name, nameObj.value, nameObj.nHeight, CT_UPDATED); } else { LogPrintf("refreshName result : %s - deleted from the table\n", qPrintable(nameObj.name)); updateEntry(nameObj.name, nameObj.value, nameObj.nHeight, CT_DELETED); } } else { // Not in model - add or do nothing if (nameObj.nHeight != NameTableEntry::NAME_NON_EXISTING) { LogPrintf("refreshName result : %s - added to the table\n", qPrintable(nameObj.name)); updateEntry(nameObj.name, nameObj.value, nameObj.nHeight, CT_NEW); } else { LogPrintf("refreshName result : %s - ignored (not in the table)\n", qPrintable(nameObj.name)); } } }
void CCoinsViewCache::DeleteName(const valtype &name) { CNameData oldData; if (GetName(name, oldData)) cacheNames.removeExpireIndex(name, oldData.getHeight()); else assert(false); if (fNameHistory) { /* When deleting a name, the history should already be clean. */ CNameHistory history; assert (!GetNameHistory(name, history) || history.empty()); } cacheNames.remove(name); }
/** * Return name info object for a CNameData object. * @param name The name. * @param data The name's data. * @return A JSON object to return. */ UniValue getNameInfo (const valtype& name, const CNameData& data) { return getNameInfo (name, data.getValue (), data.getUpdateOutpoint (), data.getAddress (), data.getHeight ()); }
UniValue name_filter (const UniValue& params, bool fHelp) { if (fHelp || params.size () > 5) throw std::runtime_error ( "name_filter (\"regexp\" (\"maxage\" (\"from\" (\"nb\" (\"stat\")))))\n" "\nScan and list names matching a regular expression.\n" "\nArguments:\n" "1. \"regexp\" (string, optional) filter names with this regexp\n" "2. \"maxage\" (numeric, optional, default=36000) only consider names updated in the last \"maxage\" blocks; 0 means all names\n" "3. \"from\" (numeric, optional, default=0) return from this position onward; index starts at 0\n" "4. \"nb\" (numeric, optional, default=0) return only \"nb\" entries; 0 means all\n" "5. \"stat\" (string, optional) if set to the string \"stat\", print statistics instead of returning the names\n" "\nResult:\n" "[\n" + getNameInfoHelp (" ", ",") + " ...\n" "]\n" "\nExamples:\n" + HelpExampleCli ("name_filter", "\"\" 5") + HelpExampleCli ("name_filter", "\"^id/\"") + HelpExampleCli ("name_filter", "\"^id/\" 36000 0 0 \"stat\"") + HelpExampleRpc ("name_scan", "\"^d/\"") ); if (IsInitialBlockDownload ()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Namecoin is downloading blocks..."); /* ********************** */ /* Interpret parameters. */ bool haveRegexp(false); boost::xpressive::sregex regexp; int maxage(36000), from(0), nb(0); bool stats(false); if (params.size () >= 1) { haveRegexp = true; regexp = boost::xpressive::sregex::compile (params[0].get_str ()); } if (params.size () >= 2) maxage = params[1].get_int (); if (maxage < 0) throw JSONRPCError (RPC_INVALID_PARAMETER, "'maxage' should be non-negative"); if (params.size () >= 3) from = params[2].get_int (); if (from < 0) throw JSONRPCError (RPC_INVALID_PARAMETER, "'from' should be non-negative"); if (params.size () >= 4) nb = params[3].get_int (); if (nb < 0) throw JSONRPCError (RPC_INVALID_PARAMETER, "'nb' should be non-negative"); if (params.size () >= 5) { if (params[4].get_str () != "stat") throw JSONRPCError (RPC_INVALID_PARAMETER, "fifth argument must be the literal string 'stat'"); stats = true; } /* ******************************************* */ /* Iterate over names to build up the result. */ UniValue names(UniValue::VARR); unsigned count(0); LOCK (cs_main); valtype name; CNameData data; std::unique_ptr<CNameIterator> iter(pcoinsTip->IterateNames ()); while (iter->next (name, data)) { const int age = chainActive.Height () - data.getHeight (); assert (age >= 0); if (maxage != 0 && age >= maxage) continue; if (haveRegexp) { const std::string nameStr = ValtypeToString (name); boost::xpressive::smatch matches; if (!boost::xpressive::regex_search (nameStr, matches, regexp)) continue; } if (from > 0) { --from; continue; } assert (from == 0); if (stats) ++count; else names.push_back (getNameInfo (name, data)); if (nb > 0) { --nb; if (nb == 0) break; } } /* ********************************************************** */ /* Return the correct result (take stats mode into account). */ if (stats) { UniValue res(UniValue::VOBJ); res.push_back (Pair ("blocks", chainActive.Height ())); res.push_back (Pair ("count", static_cast<int> (count))); return res; } return names; }
bool CCoinsViewDB::ValidateNameDB() const { const uint256 blockHash = GetBestBlock(); int nHeight; if (blockHash.IsNull()) nHeight = 0; else nHeight = mapBlockIndex.find(blockHash)->second->nHeight; /* It seems that there are no "const iterators" for LevelDB. Since we only need read operations on it, use a const-cast to get around that restriction. */ boost::scoped_ptr<CDBIterator> pcursor(const_cast<CDBWrapper*>(&db)->NewIterator()); pcursor->SeekToFirst(); /* Loop over the total database and read interesting things to memory. We later use that to check everything against each other. */ std::map<valtype, unsigned> nameHeightsIndex; std::map<valtype, unsigned> nameHeightsData; std::set<valtype> namesInDB; std::set<valtype> namesInUTXO; std::set<valtype> namesWithHistory; for (; pcursor->Valid(); pcursor->Next()) { boost::this_thread::interruption_point(); char chType; if (!pcursor->GetKey(chType)) continue; switch (chType) { case DB_COINS: { CCoins coins; if (!pcursor->GetValue(coins)) return error("%s : failed to read coins", __func__); BOOST_FOREACH(const CTxOut& txout, coins.vout) if (!txout.IsNull()) { const CNameScript nameOp(txout.scriptPubKey); if (nameOp.isNameOp() && nameOp.isAnyUpdate()) { const valtype& name = nameOp.getOpName(); if (namesInUTXO.count(name) > 0) return error("%s : name %s duplicated in UTXO set", __func__, ValtypeToString(name).c_str()); namesInUTXO.insert(nameOp.getOpName()); } } break; } case DB_NAME: { std::pair<char, valtype> key; if (!pcursor->GetKey(key) || key.first != DB_NAME) return error("%s : failed to read DB_NAME key", __func__); const valtype& name = key.second; CNameData data; if (!pcursor->GetValue(data)) return error("%s : failed to read name value", __func__); if (nameHeightsData.count(name) > 0) return error("%s : name %s duplicated in name index", __func__, ValtypeToString(name).c_str()); nameHeightsData.insert(std::make_pair(name, data.getHeight())); /* Expiration is checked at height+1, because that matches how the UTXO set is cleared in ExpireNames. */ assert(namesInDB.count(name) == 0); if (!data.isExpired(nHeight + 1)) namesInDB.insert(name); break; } case DB_NAME_HISTORY: { std::pair<char, valtype> key; if (!pcursor->GetKey(key) || key.first != DB_NAME_HISTORY) return error("%s : failed to read DB_NAME_HISTORY key", __func__); const valtype& name = key.second; if (namesWithHistory.count(name) > 0) return error("%s : name %s has duplicate history", __func__, ValtypeToString(name).c_str()); namesWithHistory.insert(name); break; } case DB_NAME_EXPIRY: { std::pair<char, CNameCache::ExpireEntry> key; if (!pcursor->GetKey(key) || key.first != DB_NAME_EXPIRY) return error("%s : failed to read DB_NAME_EXPIRY key", __func__); const CNameCache::ExpireEntry& entry = key.second; const valtype& name = entry.name; if (nameHeightsIndex.count(name) > 0) return error("%s : name %s duplicated in expire idnex", __func__, ValtypeToString(name).c_str()); nameHeightsIndex.insert(std::make_pair(name, entry.nHeight)); break; } default: break; } } /* Now verify the collected data. */ assert (nameHeightsData.size() >= namesInDB.size()); if (nameHeightsIndex != nameHeightsData) return error("%s : name height data mismatch", __func__); BOOST_FOREACH(const valtype& name, namesInDB) if (namesInUTXO.count(name) == 0) return error("%s : name '%s' in DB but not UTXO set", __func__, ValtypeToString(name).c_str()); BOOST_FOREACH(const valtype& name, namesInUTXO) if (namesInDB.count(name) == 0) return error("%s : name '%s' in UTXO set but not DB", __func__, ValtypeToString(name).c_str()); if (fNameHistory) { BOOST_FOREACH(const valtype& name, namesWithHistory) if (nameHeightsData.count(name) == 0) return error("%s : history entry for name '%s' not in main DB", __func__, ValtypeToString(name).c_str()); } else if (!namesWithHistory.empty ())