Example #1
0
/* 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());
}
Example #2
0
    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));
            }
        }
    }
Example #3
0
CNameData
NameIterationTester::getNextData ()
{
  const CScript addr = getTestAddress ();
  const valtype name = ValtypeFromString ("dummy");
  const valtype value = ValtypeFromString ("abc");
  const CScript updateScript = CNameScript::buildNameUpdate (addr, name, value);
  const CNameScript nameOp(updateScript);

  CNameData res;
  res.fromScript (++counter, COutPoint (uint256 (), 0), nameOp);

  return res;
}
Example #4
0
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);
}
Example #5
0
void
ApplyGameTransactions (const std::vector<CTransaction>& vGameTx,
                       const StepResult& stepResult, unsigned nHeight,
                       CValidationState& state, CCoinsViewCache& view,
                       CBlockUndo& undo)
{
  for (unsigned i = 0; i < vGameTx.size (); ++i)
    {
      undo.vtxundo.push_back (CTxUndo ());
      UpdateCoins (vGameTx[i], state, view, undo.vtxundo.back (), nHeight);
    }

  /* Update name db for killed players.  */
  const PlayerSet& victims = stepResult.GetKilledPlayers ();
  if (!victims.empty ())
    {
      assert (!vGameTx.empty ());
      const CTransaction& txKills = vGameTx.front ();
      assert (txKills.vout.empty ());
      assert (txKills.vin.size () == victims.size ());

      BOOST_FOREACH(const PlayerID& name, victims)
        {
          const valtype& vchName = ValtypeFromString (name);
          LogPrint ("names", "Killing player at height %d: %s\n",
                    nHeight, name.c_str ());

          CNameTxUndo opUndo;
          opUndo.fromOldState (vchName, view);
          undo.vnameundo.push_back (opUndo);

          CNameData data;
          data.setDead (nHeight, txKills.GetHash ());
          view.SetName (vchName, data, false);
        }
    }
Example #6
0
/**
 * 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 ());
}
Example #7
0
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;
}
Example #8
0
bool CCoinsViewDB::ValidateNameDB(CGameDB& gameDb) const
{
    /* Skip for genesis block, since there is no game state available yet
       (test would fail below).  There's not really anything to verify
       for the genesis block anyway.  */
    const uint256 blockHash = GetBestBlock();
    if (blockHash.IsNull())
        return true;

    /* 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::set<valtype> namesTotal;
    std::set<valtype> namesInDB;
    std::set<valtype> namesWithHistory;
    std::map<valtype, CAmount> namesInUTXO;

    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(std::make_pair(nameOp.getOpName(),
                                                          txout.nValue));
                    }
                }
            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 (namesTotal.count(name) > 0)
                return error("%s : name %s duplicated in name index",
                             __func__, ValtypeToString(name).c_str());
            namesTotal.insert(name);
            
            assert(namesInDB.count(name) == 0);
            if (!data.isDead ())
                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;
        }

        default:
            break;
        }
    }

    std::map<valtype, CAmount> namesInGame;
    GameState state(Params().GetConsensus());
    if (!gameDb.get(blockHash, state))
        return error("%s : failed to read game state", __func__);
    for (PlayerStateMap::const_iterator mi = state.players.begin();
         mi != state.players.end(); ++mi)
    {
        const valtype cur = ValtypeFromString(mi->first);
        if (namesInGame.count(cur) > 0)
            return error("%s : name %s is duplicate in the game state",
                         __func__, mi->first.c_str());
        namesInGame.insert(std::make_pair(cur, mi->second.lockedCoins));
    }

    /* Now verify the collected data.  */

    assert (namesTotal.size() >= namesInDB.size());

    if (namesInGame != namesInUTXO)
        return error("%s : game state and name DB 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 PAIRTYPE(valtype, CAmount)& pair, namesInUTXO)
        if (namesInDB.count(pair.first) == 0)
            return error("%s : name '%s' in UTXO set but not DB",
                         __func__, ValtypeToString(pair.first).c_str());

    if (fNameHistory)
    {
        BOOST_FOREACH(const valtype& name, namesWithHistory)
            if (namesTotal.count(name) == 0)
                return error("%s : history entry for name '%s' not in main DB",
                             __func__, ValtypeToString(name).c_str());
    } else if (!namesWithHistory.empty ())
Example #9
0
bool
CreateGameTransactions (const CCoinsView& view, unsigned nHeight,
                        const StepResult& stepResult,
                        std::vector<CTransaction>& vGameTx)
{
  vGameTx.clear ();

  /* Destroy name-coins of killed players.  */

  bool haveTxKills = false;
  CMutableTransaction txKills;
  txKills.SetGameTx ();

  const PlayerSet& killedPlayers = stepResult.GetKilledPlayers ();
  const KilledByMap& killedBy = stepResult.GetKilledBy ();
  txKills.vin.reserve (killedPlayers.size ());
  BOOST_FOREACH(const PlayerID &victim, killedPlayers)
    {
      const valtype vchName = ValtypeFromString (victim);
      CNameData data;
      if (!view.GetName (vchName, data))
        return error ("Game engine killed a non-existing player %s",
                      victim.c_str ());

      CTxIn txin(data.getUpdateOutpoint ());

      /* List all killers, if player was simultaneously killed by several
         other players.  If the reason was not KILLED_DESTRUCT, handle
         it also.  If multiple reasons apply, the game tx is constructed
         for the first reason according to the ordering inside of KilledByMap.
         (Which in turn is determined by the enum values for KILLED_*.)  */

      typedef KilledByMap::const_iterator Iter;
      const std::pair<Iter, Iter> iters = killedBy.equal_range (victim);
      if (iters.first == iters.second)
        return error ("No reason for killed player %s", victim.c_str ());
      const KilledByInfo::Reason reason = iters.first->second.reason;

      /* Unless we have destruct, there should be exactly one entry with
         the "first" reason.  There may be multiple entries for different
         reasons, for instance, killed by poison and staying in spawn
         area at the same time.  */
      {
        Iter it = iters.first;
        ++it;
        if (reason != KilledByInfo::KILLED_DESTRUCT && it != iters.second
            && reason == it->second.reason)
          return error ("Multiple same-reason, non-destruct killed-by"
                        " entries for %s", victim.c_str ());
      }

      switch (reason)
        {
        case KilledByInfo::KILLED_DESTRUCT:
          txin.scriptSig << vchName << GAMEOP_KILLED_BY;
          for (Iter it = iters.first; it != iters.second; ++it)
            {
              if (it->second.reason != KilledByInfo::KILLED_DESTRUCT)
                {
                  assert (it != iters.first);
                  break;
                }
              txin.scriptSig
                << ValtypeFromString (it->second.killer.ToString ());
            }
          break;

        case KilledByInfo::KILLED_SPAWN:
          txin.scriptSig << vchName << GAMEOP_KILLED_BY;
          break;

        case KilledByInfo::KILLED_POISON:
          txin.scriptSig << vchName << GAMEOP_KILLED_POISON;
          break;

        default:
          assert (false);
        }

      txKills.vin.push_back (txin);
      haveTxKills = true;
    }
  if (haveTxKills)
    {
      const CTransaction tx(txKills);
      assert (tx.IsGameTx () && !tx.IsBountyTx ());
      vGameTx.push_back (tx);
    }

  /* Pay bounties to the players who collected them.  The transaction
     inputs are just "dummy" containing informational messages.  */

  bool haveTxBounties = false;
  CMutableTransaction txBounties;
  txBounties.SetGameTx ();

  txBounties.vin.reserve (stepResult.bounties.size ());
  txBounties.vout.reserve (stepResult.bounties.size ());

  BOOST_FOREACH(const CollectedBounty& bounty, stepResult.bounties)
    {
      const valtype vchName = ValtypeFromString (bounty.character.player);
      CNameData data;
      if (!view.GetName (vchName, data))
        return error ("Game engine created bounty for non-existing player");

      CTxOut txout;
      txout.nValue = bounty.loot.nAmount;

      if (!bounty.address.empty ())
        {
          /* Player-provided addresses are validated before accepting them,
             so failing here is ok.  */
          CBitcoinAddress addr(bounty.address);
          if (!addr.IsValid ())
            return error ("Failed to set player-provided address for bounty");
          txout.scriptPubKey = GetScriptForDestination (addr.Get ());
        }
      else
        txout.scriptPubKey = data.getAddress ();

      txBounties.vout.push_back (txout);

      CTxIn txin;
      if (bounty.loot.IsRefund ())
        txin.scriptSig
          << vchName << GAMEOP_REFUND
          << bounty.character.index << bounty.loot.GetRefundHeight ();
      else
        txin.scriptSig
          << vchName << GAMEOP_COLLECTED_BOUNTY
          << bounty.character.index
          << bounty.loot.firstBlock
          << bounty.loot.lastBlock
          << bounty.loot.collectedFirstBlock
          << bounty.loot.collectedLastBlock;
      txBounties.vin.push_back (txin);

      haveTxBounties = true;
    }
  if (haveTxBounties)
    {
      const CTransaction tx(txBounties);
      assert (tx.IsGameTx () && tx.IsBountyTx ());
      vGameTx.push_back (tx);
    }

  /* Print log chatter.  */
  if (haveTxKills || haveTxBounties)
    {
      LogPrint ("game", "Game transactions @%d:\n", nHeight);
      if (haveTxKills)
        LogPrint ("game", "  kills:    %s\n", txKills.GetHash ().ToString ());
      if (haveTxBounties)
        LogPrint ("game", "  bounties: %s\n",
                  txBounties.GetHash ().ToString ());
    }

  return true;
}
Example #10
0
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 ())