From 02129e0543919831cbfa26b44008f7ccac1a3035 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 25 Mar 2019 16:14:48 -0700 Subject: [PATCH 01/10] no-op refactor in prep for multiple entities per cert --- .../src/entities/EntityServer.cpp | 99 ++++++++----------- .../ui/overlays/ContextOverlayInterface.cpp | 3 +- libraries/entities/src/EntityTree.cpp | 21 ++-- libraries/entities/src/EntityTree.h | 4 +- 4 files changed, 54 insertions(+), 73 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 581d854909..f2cad1e400 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -474,73 +474,58 @@ void EntityServer::startDynamicDomainVerification() { QHash localMap(tree->getEntityCertificateIDMap()); QHashIterator i(localMap); - qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap"; + qCDebug(entities) << localMap.size() << "certificates present."; while (i.hasNext()) { i.next(); const auto& certificateID = i.key(); const auto& entityID = i.value(); - EntityItemPointer entity = tree->findEntityByEntityItemID(entityID); + // Examine each cert: + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); + QJsonObject request; + request["certificate_id"] = certificateID; + networkRequest.setUrl(requestURL); - if (entity) { - if (!entity->getProperties().verifyStaticCertificateProperties()) { - qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << entityID << "failed" - << "static certificate verification."; - // Delete the entity if it doesn't pass static certificate verification - tree->withWriteLock([&] { - tree->deleteEntity(entityID, true); - }); - } else { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest; - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); - requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); - QJsonObject request; - request["certificate_id"] = certificateID; - networkRequest.setUrl(requestURL); + QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); + connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply] { + EntityTreePointer tree = std::static_pointer_cast(_tree); - connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply] { - EntityTreePointer tree = std::static_pointer_cast(_tree); + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); + networkReply->deleteLater(); - QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - jsonObject = jsonObject["data"].toObject(); - - if (networkReply->error() == QNetworkReply::NoError) { - QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); - if (jsonObject["domain_id"].toString() != thisDomainID) { - EntityItemPointer entity = tree->findEntityByEntityItemID(entityID); - if (!entity) { - qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; - networkReply->deleteLater(); - return; - } - if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) { - qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() - << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; - tree->withWriteLock([&] { - tree->deleteEntity(entityID, true); - }); - } else { - qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; - } - } else { - qCDebug(entities) << "Entity passed dynamic domain verification:" << entityID; - } - } else { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID - << "More info:" << jsonObject; - } - - networkReply->deleteLater(); - }); + if (networkReply->error() != QNetworkReply::NoError) { + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID + << "More info:" << jsonObject; + return; } - } else { - qCWarning(entities) << "During DDV, an entity with ID" << entityID << "was NOT found in the Entity Tree!"; - } + QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); + if (jsonObject["domain_id"].toString() == thisDomainID) { + // Entity belongs here. Nothing to do. + return; + } + // Entity does not belong here: + EntityItemPointer entity = tree->findEntityByEntityItemID(entityID); + if (!entity) { + qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; + return; + } + if (entity->getAge() <= (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS / MSECS_PER_SECOND)) { + qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; + return; + } + qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() + << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; + tree->withWriteLock([&] { + tree->deleteEntity(entityID, true); + }); + }); } int nextInterval = qrand() % ((_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS + 1) - _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS) + _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e5cec70f64..794feddd8a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -422,8 +422,7 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer QString certID(packet->read(certIDByteArraySize)); QString text(packet->read(textByteArraySize)); - EntityItemID id; - bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(certID, text, id); + bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(certID, text); if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 8bf7c92b1f..11e392f590 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1423,9 +1423,7 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) { QTimer* _challengeOwnershipTimeoutTimer = new QTimer(this); - connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const QString& certID) { - QReadLocker locker(&_entityCertificateIDMapLock); - EntityItemID id = _entityCertificateIDMap.value(certID); + connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const EntityItemID& id) { if (entityItemID == id && _challengeOwnershipTimeoutTimer) { _challengeOwnershipTimeoutTimer->stop(); _challengeOwnershipTimeoutTimer->deleteLater(); @@ -1455,12 +1453,7 @@ QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKe return nonceBytes; } -bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id) { - { - QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); - id = _entityCertificateIDMap.value(certID); - } - +bool EntityTree::verifyNonce(const QString& certID, const QString& nonce) { QString actualNonce, key; { QWriteLocker locker(&_certNonceMapLock); @@ -1645,10 +1638,14 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const QString certID(message.read(certIDByteArraySize)); QString text(message.read(textByteArraySize)); - emit killChallengeOwnershipTimeoutTimer(certID); + EntityItemID id; + { + QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); + id = _entityCertificateIDMap.value(certID); + } + emit killChallengeOwnershipTimeoutTimer(id); - EntityItemID id; - if (!verifyNonce(certID, text, id)) { + if (!verifyNonce(certID, text)) { if (!id.isNull()) { deleteEntity(id, true); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index e627a07d13..63e1197970 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -253,7 +253,7 @@ public: static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME; QByteArray computeNonce(const QString& certID, const QString ownerKey); - bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id); + bool verifyNonce(const QString& certID, const QString& nonce); QUuid getMyAvatarSessionUUID() { return _myAvatar ? _myAvatar->getSessionUUID() : QUuid(); } void setMyAvatar(std::shared_ptr myAvatar) { _myAvatar = myAvatar; } @@ -290,7 +290,7 @@ signals: void entityServerScriptChanging(const EntityItemID& entityItemID, const bool reload); void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID); void clearingEntities(); - void killChallengeOwnershipTimeoutTimer(const QString& certID); + void killChallengeOwnershipTimeoutTimer(const EntityItemID& certID); protected: From 91a165b4c3d92e958f318057ea67b67207f022a1 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 26 Mar 2019 11:49:08 -0700 Subject: [PATCH 02/10] separate out the certified entity map stuff (no-op refactor) --- .../src/entities/EntityServer.cpp | 58 +-------- libraries/entities/src/EntityTree.cpp | 119 +++++++++++++----- libraries/entities/src/EntityTree.h | 3 + 3 files changed, 95 insertions(+), 85 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index f2cad1e400..06632dabb0 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "../AssignmentDynamicFactory.h" @@ -471,62 +470,7 @@ void EntityServer::startDynamicDomainVerification() { qCDebug(entities) << "Starting Dynamic Domain Verification..."; EntityTreePointer tree = std::static_pointer_cast(_tree); - QHash localMap(tree->getEntityCertificateIDMap()); - - QHashIterator i(localMap); - qCDebug(entities) << localMap.size() << "certificates present."; - while (i.hasNext()) { - i.next(); - const auto& certificateID = i.key(); - const auto& entityID = i.value(); - - // Examine each cert: - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest; - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); - requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); - QJsonObject request; - request["certificate_id"] = certificateID; - networkRequest.setUrl(requestURL); - - QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - - connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply] { - EntityTreePointer tree = std::static_pointer_cast(_tree); - - QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - jsonObject = jsonObject["data"].toObject(); - networkReply->deleteLater(); - - if (networkReply->error() != QNetworkReply::NoError) { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID - << "More info:" << jsonObject; - return; - } - QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); - if (jsonObject["domain_id"].toString() == thisDomainID) { - // Entity belongs here. Nothing to do. - return; - } - // Entity does not belong here: - EntityItemPointer entity = tree->findEntityByEntityItemID(entityID); - if (!entity) { - qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; - return; - } - if (entity->getAge() <= (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS / MSECS_PER_SECOND)) { - qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; - return; - } - qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() - << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; - tree->withWriteLock([&] { - tree->deleteEntity(entityID, true); - }); - }); - } + tree->startDynamicDomainVerificationOnServer((float) _MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS / MSECS_PER_SECOND); int nextInterval = qrand() % ((_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS + 1) - _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS) + _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; qCDebug(entities) << "Restarting Dynamic Domain Verification timer for" << nextInterval / 1000 << "seconds"; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 11e392f590..55a8c42261 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "EntitySimulation.h" #include "VariantMapToScriptValue.h" @@ -286,27 +287,7 @@ void EntityTree::postAddEntity(EntityItemPointer entity) { assert(entity); if (getIsServer()) { - QString certID(entity->getCertificateID()); - EntityItemID entityItemID = entity->getEntityItemID(); - EntityItemID existingEntityItemID; - - { - QWriteLocker locker(&_entityCertificateIDMapLock); - existingEntityItemID = _entityCertificateIDMap.value(certID); - if (!certID.isEmpty()) { - _entityCertificateIDMap.insert(certID, entityItemID); - qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; - } - } - - // Delete an already-existing entity from the tree if it has the same - // CertificateID as the entity we're trying to add. - if (!existingEntityItemID.isNull() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { - qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" - << existingEntityItemID << ". Deleting existing entity."; - deleteEntity(existingEntityItemID, true); - return; - } + addCertifiedEntityOnServer(entity); } // check to see if we need to simulate this entity.. @@ -764,13 +745,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) theEntity->die(); if (getIsServer()) { - { - QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); - QString certID = theEntity->getCertificateID(); - if (theEntity->getEntityItemID() == _entityCertificateIDMap.value(certID)) { - _entityCertificateIDMap.remove(certID); - } - } + removeCertifiedEntityOnServer(theEntity); // set up the deleted entities ID QWriteLocker recentlyDeletedEntitiesLocker(&_recentlyDeletedEntitiesLock); @@ -1421,6 +1396,94 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { return false; } +void EntityTree::addCertifiedEntityOnServer(EntityItemPointer entity) { + QString certID(entity->getCertificateID()); + EntityItemID entityItemID = entity->getEntityItemID(); + EntityItemID existingEntityItemID; + + { + QWriteLocker locker(&_entityCertificateIDMapLock); + existingEntityItemID = _entityCertificateIDMap.value(certID); + if (!certID.isEmpty()) { + _entityCertificateIDMap.insert(certID, entityItemID); + qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; + } + } + + // Delete an already-existing entity from the tree if it has the same + // CertificateID as the entity we're trying to add. + if (!existingEntityItemID.isNull() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { + qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" + << existingEntityItemID << ". Deleting existing entity."; + deleteEntity(existingEntityItemID, true); + } +} + +void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) { + QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); + QString certID = entity->getCertificateID(); + if (entity->getEntityItemID() == _entityCertificateIDMap.value(certID)) { + _entityCertificateIDMap.remove(certID); + } +} + +void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove) { + QHash localMap(getEntityCertificateIDMap()); + QHashIterator i(localMap); + qCDebug(entities) << localMap.size() << "certificates present."; + while (i.hasNext()) { + i.next(); + const auto& certificateID = i.key(); + const auto& entityID = i.value(); + + // Examine each cert: + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); + QJsonObject request; + request["certificate_id"] = certificateID; + networkRequest.setUrl(requestURL); + + QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); + + connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply, minimumAgeToRemove] { + + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); + networkReply->deleteLater(); + + if (networkReply->error() != QNetworkReply::NoError) { + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID + << "More info:" << jsonObject; + return; + } + QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); + if (jsonObject["domain_id"].toString() == thisDomainID) { + // Entity belongs here. Nothing to do. + return; + } + // Entity does not belong here: + EntityItemPointer entity = findEntityByEntityItemID(entityID); + if (!entity) { + qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; + return; + } + if (entity->getAge() <= minimumAgeToRemove) { + qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; + return; + } + qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() + << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; + withWriteLock([&] { + deleteEntity(entityID, true); + }); + }); + } +} + void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) { QTimer* _challengeOwnershipTimeoutTimer = new QTimer(this); connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const EntityItemID& id) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 63e1197970..10809747b3 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -279,6 +279,7 @@ public: void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, bool force, bool tellServer); + void startDynamicDomainVerificationOnServer(float minimumAgeToRemove); signals: void deletingEntity(const EntityItemID& entityID); @@ -377,6 +378,8 @@ protected: Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID); private: + void addCertifiedEntityOnServer(EntityItemPointer entity); + void removeCertifiedEntityOnServer(EntityItemPointer entity); void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); From be74218d931ec3adef8c885e935d3b1aebc55c72 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 26 Mar 2019 12:05:10 -0700 Subject: [PATCH 03/10] eliminate copy of hash table --- libraries/entities/src/EntityTree.cpp | 6 +++--- libraries/entities/src/EntityTree.h | 5 ----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 55a8c42261..63259e2c58 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1428,9 +1428,9 @@ void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) { } void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove) { - QHash localMap(getEntityCertificateIDMap()); - QHashIterator i(localMap); - qCDebug(entities) << localMap.size() << "certificates present."; + QReadLocker locker(&_entityCertificateIDMapLock); + QHashIterator i(_entityCertificateIDMap); + qCDebug(entities) << _entityCertificateIDMap.size() << "certificates present."; while (i.hasNext()) { i.next(); const auto& certificateID = i.key(); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 10809747b3..fe6045f6f7 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -157,11 +157,6 @@ public: return _recentlyDeletedEntityItemIDs; } - QHash getEntityCertificateIDMap() const { - QReadLocker locker(&_entityCertificateIDMapLock); - return _entityCertificateIDMap; - } - void forgetEntitiesDeletedBefore(quint64 sinceTime); int processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode); From 4dfd0fbda3fa7771437c557fe17e4692ac048053 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 26 Mar 2019 15:02:13 -0700 Subject: [PATCH 04/10] challenge ownership packet has an id that gets reflected back. It doesn't have to be a cert. --- interface/src/commerce/Wallet.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 0e9ad7d79a..37f28960e5 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -816,18 +816,18 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest; int status; - int certIDByteArraySize; + int idByteArraySize; int textByteArraySize; int challengingNodeUUIDByteArraySize; - packet->readPrimitive(&certIDByteArraySize); + packet->readPrimitive(&idByteArraySize); packet->readPrimitive(&textByteArraySize); // returns a cast char*, size if (challengeOriginatedFromClient) { packet->readPrimitive(&challengingNodeUUIDByteArraySize); } // "encryptedText" is now a series of random bytes, a nonce - QByteArray certID = packet->read(certIDByteArraySize); + QByteArray id = packet->read(idByteArraySize); QByteArray text = packet->read(textByteArraySize); QByteArray challengingNodeUUID; if (challengeOriginatedFromClient) { @@ -853,32 +853,32 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack textByteArray = sig.toUtf8(); } textByteArraySize = textByteArray.size(); - int certIDSize = certID.size(); + int idSize = id.size(); // setup the packet if (challengeOriginatedFromClient) { auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, - certIDSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), + idSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), true); - textPacket->writePrimitive(certIDSize); + textPacket->writePrimitive(idSize); textPacket->writePrimitive(textByteArraySize); textPacket->writePrimitive(challengingNodeUUIDByteArraySize); - textPacket->write(certID); + textPacket->write(id); textPacket->write(textByteArray); textPacket->write(challengingNodeUUID); - qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for id" << id; nodeList->sendPacket(std::move(textPacket), *sendingNode); } else { - auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + textByteArraySize + 2 * sizeof(int), true); + auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, idSize + textByteArraySize + 2 * sizeof(int), true); - textPacket->writePrimitive(certIDSize); + textPacket->writePrimitive(idSize); textPacket->writePrimitive(textByteArraySize); - textPacket->write(certID); + textPacket->write(id); textPacket->write(textByteArray); - qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for id" << id; nodeList->sendPacket(std::move(textPacket), *sendingNode); } From c9b79b24e3fc033949e21cd24eebd8d720090144 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 26 Mar 2019 15:23:13 -0700 Subject: [PATCH 05/10] track nonces by entity id instead of by cert --- .../ui/overlays/ContextOverlayInterface.cpp | 4 ++-- libraries/entities/src/EntityTree.cpp | 20 +++++++++---------- libraries/entities/src/EntityTree.h | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 794feddd8a..5a4ff52a84 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -329,7 +329,7 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID QString ownerKey = jsonObject["transfer_recipient_key"].toString(); QByteArray certID = entityProperties.getCertificateID().toUtf8(); - QByteArray text = DependencyManager::get()->getTree()->computeNonce(certID, ownerKey); + QByteArray text = DependencyManager::get()->getTree()->computeNonce(entityID, ownerKey); QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); int certIDByteArraySize = certID.length(); @@ -422,7 +422,7 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer QString certID(packet->read(certIDByteArraySize)); QString text(packet->read(textByteArraySize)); - bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(certID, text); + bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(_lastInspectedEntity, text); if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 63259e2c58..41e0cbafc4 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1506,21 +1506,21 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) _challengeOwnershipTimeoutTimer->start(5000); } -QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKey) { +QByteArray EntityTree::computeNonce(const EntityItemID& entityID, const QString ownerKey) { QUuid nonce = QUuid::createUuid(); //random, 5-hex value, separated by "-" QByteArray nonceBytes = nonce.toByteArray(); - QWriteLocker locker(&_certNonceMapLock); - _certNonceMap.insert(certID, QPair(nonce, ownerKey)); + QWriteLocker locker(&_entityNonceMapLock); + _entityNonceMap.insert(entityID, QPair(nonce, ownerKey)); return nonceBytes; } -bool EntityTree::verifyNonce(const QString& certID, const QString& nonce) { +bool EntityTree::verifyNonce(const EntityItemID& entityID, const QString& nonce) { QString actualNonce, key; { - QWriteLocker locker(&_certNonceMapLock); - QPair sent = _certNonceMap.take(certID); + QWriteLocker locker(&_entityNonceMapLock); + QPair sent = _entityNonceMap.take(entityID); actualNonce = sent.first.toString(); key = sent.second; } @@ -1530,9 +1530,9 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce) { bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), hashedActualNonce, QByteArray::fromBase64(nonce.toUtf8())); if (verificationSuccess) { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded."; + qCDebug(entities) << "Ownership challenge for Entity ID" << entityID << "succeeded."; } else { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed. Actual nonce:" << actualNonce << + qCDebug(entities) << "Ownership challenge for Entity ID" << entityID << "failed. Actual nonce:" << actualNonce << "\nHashed actual nonce (digest):" << hashedActualNonce << "\nSent nonce (signature)" << nonce << "\nKey" << key; } @@ -1585,7 +1585,7 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri // 1. Obtain a nonce auto nodeList = DependencyManager::get(); - QByteArray text = computeNonce(certID, ownerKey); + QByteArray text = computeNonce(entityItemID, ownerKey); if (text == "") { qCDebug(entities) << "CRITICAL ERROR: Couldn't compute nonce. Deleting entity..."; @@ -1708,7 +1708,7 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const } emit killChallengeOwnershipTimeoutTimer(id); - if (!verifyNonce(certID, text)) { + if (!verifyNonce(id, text)) { if (!id.isNull()) { deleteEntity(id, true); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index fe6045f6f7..327f06164e 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -247,8 +247,8 @@ public: static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME; - QByteArray computeNonce(const QString& certID, const QString ownerKey); - bool verifyNonce(const QString& certID, const QString& nonce); + QByteArray computeNonce(const EntityItemID& entityID, const QString ownerKey); + bool verifyNonce(const EntityItemID& entityID, const QString& nonce); QUuid getMyAvatarSessionUUID() { return _myAvatar ? _myAvatar->getSessionUUID() : QUuid(); } void setMyAvatar(std::shared_ptr myAvatar) { _myAvatar = myAvatar; } @@ -325,8 +325,8 @@ protected: mutable QReadWriteLock _entityCertificateIDMapLock; QHash _entityCertificateIDMap; - mutable QReadWriteLock _certNonceMapLock; - QHash> _certNonceMap; + mutable QReadWriteLock _entityNonceMapLock; + QHash> _entityNonceMap; EntitySimulationPointer _simulation; From 57da21cec2beac3d62b25deca50bb3df849f20a7 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 26 Mar 2019 17:12:28 -0700 Subject: [PATCH 06/10] more about challenges use entity id as id, not cert id --- interface/src/commerce/Ledger.h | 3 +- interface/src/commerce/QmlCommerce.h | 3 +- .../ui/overlays/ContextOverlayInterface.cpp | 26 ++++----- libraries/entities/src/EntityTree.cpp | 55 ++++++++----------- libraries/entities/src/EntityTree.h | 2 +- 5 files changed, 42 insertions(+), 47 deletions(-) diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 2e18f34c8d..64528e617d 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "AccountManager.h" @@ -65,7 +66,7 @@ signals: void availableUpdatesResult(QJsonObject result); void updateItemResult(QJsonObject result); - void updateCertificateStatus(const QString& certID, uint certStatus); + void updateCertificateStatus(const EntityItemID& entityID, uint certStatus); public slots: void buySuccess(QNetworkReply* reply); diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index 3217b8a1f9..99b3e32e8b 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -19,6 +19,7 @@ #include +#include #include class QmlCommerce : public QObject, public Dependency { @@ -49,7 +50,7 @@ signals: void availableUpdatesResult(QJsonObject result); void updateItemResult(QJsonObject result); - void updateCertificateStatus(const QString& certID, uint certStatus); + void updateCertificateStatus(const EntityItemID& entityID, uint certStatus); void transferAssetToNodeResult(QJsonObject result); void transferAssetToUsernameResult(QJsonObject result); diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 5a4ff52a84..0e4fa796d8 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -328,21 +328,21 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID } else { QString ownerKey = jsonObject["transfer_recipient_key"].toString(); - QByteArray certID = entityProperties.getCertificateID().toUtf8(); + QByteArray id = entityID.toByteArray(); QByteArray text = DependencyManager::get()->getTree()->computeNonce(entityID, ownerKey); QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); - int certIDByteArraySize = certID.length(); + int idByteArraySize = id.length(); int textByteArraySize = text.length(); int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), + idByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(idByteArraySize); challengeOwnershipPacket->writePrimitive(textByteArraySize); challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); - challengeOwnershipPacket->write(certID); + challengeOwnershipPacket->write(id); challengeOwnershipPacket->write(text); challengeOwnershipPacket->write(nodeToChallengeByteArray); nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer); @@ -370,12 +370,12 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID // so they always pass Ownership Verification. It's necessary to emit this signal // so that the Inspection Certificate can continue its information-grabbing process. auto ledger = DependencyManager::get(); - emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); + emit ledger->updateCertificateStatus(entityID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); } } else { auto ledger = DependencyManager::get(); _challengeOwnershipTimeoutTimer.stop(); - emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); + emit ledger->updateCertificateStatus(entityID, (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; } @@ -401,7 +401,7 @@ void ContextOverlayInterface::startChallengeOwnershipTimer() { connect(&_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { qCDebug(entities) << "Ownership challenge timed out for" << _lastInspectedEntity; - emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT)); + emit ledger->updateCertificateStatus(_lastInspectedEntity, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT)); emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); }); @@ -413,22 +413,22 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer _challengeOwnershipTimeoutTimer.stop(); - int certIDByteArraySize; + int idByteArraySize; int textByteArraySize; - packet->readPrimitive(&certIDByteArraySize); + packet->readPrimitive(&idByteArraySize); packet->readPrimitive(&textByteArraySize); - QString certID(packet->read(certIDByteArraySize)); + EntityItemID id(packet->read(idByteArraySize)); QString text(packet->read(textByteArraySize)); bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(_lastInspectedEntity, text); if (verificationSuccess) { - emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); + emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); emit DependencyManager::get()->ownershipVerificationSuccess(_lastInspectedEntity); } else { - emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED)); + emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED)); emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 41e0cbafc4..4fc234122b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1540,42 +1540,42 @@ bool EntityTree::verifyNonce(const EntityItemID& entityID, const QString& nonce) } void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { - int certIDByteArraySize; + int idByteArraySize; int textByteArraySize; int nodeToChallengeByteArraySize; - message.readPrimitive(&certIDByteArraySize); + message.readPrimitive(&idByteArraySize); message.readPrimitive(&textByteArraySize); message.readPrimitive(&nodeToChallengeByteArraySize); - QByteArray certID(message.read(certIDByteArraySize)); + QByteArray id(message.read(idByteArraySize)); QByteArray text(message.read(textByteArraySize)); QByteArray nodeToChallenge(message.read(nodeToChallengeByteArraySize)); - sendChallengeOwnershipRequestPacket(certID, text, nodeToChallenge, sourceNode); + sendChallengeOwnershipRequestPacket(id, text, nodeToChallenge, sourceNode); } void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { auto nodeList = DependencyManager::get(); - int certIDByteArraySize; + int idByteArraySize; int textByteArraySize; int challengingNodeUUIDByteArraySize; - message.readPrimitive(&certIDByteArraySize); + message.readPrimitive(&idByteArraySize); message.readPrimitive(&textByteArraySize); message.readPrimitive(&challengingNodeUUIDByteArraySize); - QByteArray certID(message.read(certIDByteArraySize)); + QByteArray id(message.read(idByteArraySize)); QByteArray text(message.read(textByteArraySize)); QUuid challengingNode = QUuid::fromRfc4122(message.read(challengingNodeUUIDByteArraySize)); auto challengeOwnershipReplyPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, - certIDByteArraySize + text.length() + 2 * sizeof(int), + idByteArraySize + text.length() + 2 * sizeof(int), true); - challengeOwnershipReplyPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipReplyPacket->writePrimitive(idByteArraySize); challengeOwnershipReplyPacket->writePrimitive(text.length()); - challengeOwnershipReplyPacket->write(certID); + challengeOwnershipReplyPacket->write(id); challengeOwnershipReplyPacket->write(text); nodeList->sendPacket(std::move(challengeOwnershipReplyPacket), *(nodeList->nodeWithUUID(challengingNode))); @@ -1595,14 +1595,14 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri } else { qCDebug(entities) << "Challenging ownership of Cert ID" << certID; // 2. Send the nonce to the rezzing avatar's node - QByteArray certIDByteArray = certID.toUtf8(); - int certIDByteArraySize = certIDByteArray.size(); + QByteArray idByteArray = entityItemID.toByteArray(); + int idByteArraySize = idByteArray.size(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, - certIDByteArraySize + text.length() + 2 * sizeof(int), + idByteArraySize + text.length() + 2 * sizeof(int), true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(idByteArraySize); challengeOwnershipPacket->writePrimitive(text.length()); - challengeOwnershipPacket->write(certIDByteArray); + challengeOwnershipPacket->write(idByteArray); challengeOwnershipPacket->write(text); nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode); @@ -1616,24 +1616,24 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri } } -void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) { +void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& id, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) { auto nodeList = DependencyManager::get(); // In this case, Client A is challenging Client B. Client A is inspecting a certified entity that it wants // to make sure belongs to Avatar B. QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122(); - int certIDByteArraySize = certID.length(); + int idByteArraySize = id.length(); int TextByteArraySize = text.length(); int senderNodeUUIDSize = senderNodeUUID.length(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + TextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int), + idByteArraySize + TextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int), true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(idByteArraySize); challengeOwnershipPacket->writePrimitive(TextByteArraySize); challengeOwnershipPacket->writePrimitive(senderNodeUUIDSize); - challengeOwnershipPacket->write(certID); + challengeOwnershipPacket->write(id); challengeOwnershipPacket->write(text); challengeOwnershipPacket->write(senderNodeUUID); @@ -1692,26 +1692,19 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt } void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { - int certIDByteArraySize; + int idByteArraySize; int textByteArraySize; - message.readPrimitive(&certIDByteArraySize); + message.readPrimitive(&idByteArraySize); message.readPrimitive(&textByteArraySize); - QString certID(message.read(certIDByteArraySize)); + EntityItemID id(message.read(idByteArraySize)); QString text(message.read(textByteArraySize)); - EntityItemID id; - { - QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); - id = _entityCertificateIDMap.value(certID); - } emit killChallengeOwnershipTimeoutTimer(id); if (!verifyNonce(id, text)) { - if (!id.isNull()) { - deleteEntity(id, true); - } + deleteEntity(id, true); } } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 327f06164e..2b3ee93ab7 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -376,7 +376,7 @@ private: void addCertifiedEntityOnServer(EntityItemPointer entity); void removeCertifiedEntityOnServer(EntityItemPointer entity); void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); - void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); + void sendChallengeOwnershipRequestPacket(const QByteArray& id, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); std::shared_ptr _myAvatar{ nullptr }; From bf1982564c702c0f91ce27318ee2ae5a089b2108 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Tue, 26 Mar 2019 19:26:38 -0700 Subject: [PATCH 07/10] pass id around instead of relying on latest ivar value in a singleton --- .../ui/overlays/ContextOverlayInterface.cpp | 26 +++++++++---------- .../src/ui/overlays/ContextOverlayInterface.h | 5 ++-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 0e4fa796d8..1c8a9019ea 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -292,7 +292,7 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID setLastInspectedEntity(entityID); - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags); + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityID, _entityPropertyFlags); auto nodeList = DependencyManager::get(); @@ -349,10 +349,10 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID // Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer"); + QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityID)); return; } else { - startChallengeOwnershipTimer(); + startChallengeOwnershipTimer(entityID); } } } else { @@ -376,8 +376,8 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID auto ledger = DependencyManager::get(); _challengeOwnershipTimeoutTimer.stop(); emit ledger->updateCertificateStatus(entityID, (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); - emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); - qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; + emit DependencyManager::get()->ownershipVerificationFailed(entityID); + qCDebug(context_overlay) << "Entity" << entityID << "failed static certificate verification!"; } } @@ -395,14 +395,14 @@ void ContextOverlayInterface::deletingEntity(const EntityItemID& entityID) { } } -void ContextOverlayInterface::startChallengeOwnershipTimer() { +void ContextOverlayInterface::startChallengeOwnershipTimer(const EntityItemID& entityItemID) { auto ledger = DependencyManager::get(); - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags); + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); connect(&_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { - qCDebug(entities) << "Ownership challenge timed out for" << _lastInspectedEntity; - emit ledger->updateCertificateStatus(_lastInspectedEntity, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT)); - emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); + qCDebug(entities) << "Ownership challenge timed out for" << entityItemID; + emit ledger->updateCertificateStatus(entityItemID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT)); + emit DependencyManager::get()->ownershipVerificationFailed(entityItemID); }); _challengeOwnershipTimeoutTimer.start(5000); @@ -422,13 +422,13 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer EntityItemID id(packet->read(idByteArraySize)); QString text(packet->read(textByteArraySize)); - bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(_lastInspectedEntity, text); + bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(id, text); if (verificationSuccess) { emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); - emit DependencyManager::get()->ownershipVerificationSuccess(_lastInspectedEntity); + emit DependencyManager::get()->ownershipVerificationSuccess(id); } else { emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED)); - emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); + emit DependencyManager::get()->ownershipVerificationFailed(id); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index b1b62aa0c4..e688d1c115 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -46,7 +46,7 @@ public: ContextOverlayInterface(); Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } - void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; } + void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); } void setEnabled(bool enabled); bool getEnabled() { return _enabled; } bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; } @@ -83,7 +83,6 @@ private: EntityItemID _mouseDownEntity; quint64 _mouseDownEntityTimestamp; EntityItemID _currentEntityWithContextOverlay; - EntityItemID _lastInspectedEntity; QString _entityMarketplaceID; bool _contextOverlayJustClicked { false }; @@ -94,7 +93,7 @@ private: void deletingEntity(const EntityItemID& entityItemID); - Q_INVOKABLE void startChallengeOwnershipTimer(); + Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID); QTimer _challengeOwnershipTimeoutTimer; }; From 71111936a2d1eb38d1d1c2c6453ae9fe2403ddfb Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 27 Mar 2019 11:42:16 -0700 Subject: [PATCH 08/10] prep for entity lists --- libraries/entities/src/EntityTree.cpp | 69 +++++++++++++++------------ 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 4fc234122b..f4b82ad4b1 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1398,32 +1398,32 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { void EntityTree::addCertifiedEntityOnServer(EntityItemPointer entity) { QString certID(entity->getCertificateID()); - EntityItemID entityItemID = entity->getEntityItemID(); EntityItemID existingEntityItemID; - - { + if (!certID.isEmpty()) { + EntityItemID entityItemID = entity->getEntityItemID(); QWriteLocker locker(&_entityCertificateIDMapLock); existingEntityItemID = _entityCertificateIDMap.value(certID); - if (!certID.isEmpty()) { - _entityCertificateIDMap.insert(certID, entityItemID); - qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; - } + _entityCertificateIDMap.insert(certID, entityItemID); + qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; } - // Delete an already-existing entity from the tree if it has the same // CertificateID as the entity we're trying to add. if (!existingEntityItemID.isNull() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" << existingEntityItemID << ". Deleting existing entity."; - deleteEntity(existingEntityItemID, true); + withWriteLock([&] { + deleteEntity(existingEntityItemID, true); + }); } } void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) { - QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); QString certID = entity->getCertificateID(); - if (entity->getEntityItemID() == _entityCertificateIDMap.value(certID)) { - _entityCertificateIDMap.remove(certID); + if (!certID.isEmpty()) { + QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); + if (entity->getEntityItemID() == _entityCertificateIDMap.value(certID)) { + _entityCertificateIDMap.remove(certID); + } } } @@ -1449,15 +1449,16 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply, minimumAgeToRemove] { + connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply, minimumAgeToRemove, &certificateID] { QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); jsonObject = jsonObject["data"].toObject(); + bool failure = networkReply->error() != QNetworkReply::NoError; + auto failureReason = networkReply->error(); networkReply->deleteLater(); - - if (networkReply->error() != QNetworkReply::NoError) { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID - << "More info:" << jsonObject; + if (failure) { + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << failureReason + << "; NOT deleting entity" << entityID << "More info:" << jsonObject; return; } QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); @@ -1466,20 +1467,26 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove return; } // Entity does not belong here: - EntityItemPointer entity = findEntityByEntityItemID(entityID); - if (!entity) { - qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; - return; + { + EntityItemPointer entity = findEntityByEntityItemID(entityID); + if (!entity) { + qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; + return; + } + if (entity->getAge() <= minimumAgeToRemove) { + qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; + return; + } + qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() + << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; + withWriteLock([&] { + deleteEntity(entityID, true); + }); } - if (entity->getAge() <= minimumAgeToRemove) { - qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; - return; + { + QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); + _entityCertificateIDMap.remove(certificateID); } - qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() - << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; - withWriteLock([&] { - deleteEntity(entityID, true); - }); }); } } @@ -1704,7 +1711,9 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const emit killChallengeOwnershipTimeoutTimer(id); if (!verifyNonce(id, text)) { - deleteEntity(id, true); + withWriteLock([&] { + deleteEntity(id, true); + }); } } From f6ee77a6ae5b31881efcbe56d67bbaff1e36fe70 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 27 Mar 2019 12:23:02 -0700 Subject: [PATCH 09/10] an entity list of one --- libraries/entities/src/EntityTree.cpp | 27 ++++++++++++++++++--------- libraries/entities/src/EntityTree.h | 2 +- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index f4b82ad4b1..01c00b82e5 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1398,17 +1398,20 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { void EntityTree::addCertifiedEntityOnServer(EntityItemPointer entity) { QString certID(entity->getCertificateID()); - EntityItemID existingEntityItemID; + QList entityList; if (!certID.isEmpty()) { EntityItemID entityItemID = entity->getEntityItemID(); QWriteLocker locker(&_entityCertificateIDMapLock); - existingEntityItemID = _entityCertificateIDMap.value(certID); - _entityCertificateIDMap.insert(certID, entityItemID); + entityList = _entityCertificateIDMap.value(certID); + QList newList; + newList << entityItemID; + _entityCertificateIDMap.insert(certID, newList); qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; } // Delete an already-existing entity from the tree if it has the same // CertificateID as the entity we're trying to add. - if (!existingEntityItemID.isNull() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { + if (!entityList.isEmpty() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { + EntityItemID existingEntityItemID = entityList.first(); qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" << existingEntityItemID << ". Deleting existing entity."; withWriteLock([&] { @@ -1421,7 +1424,8 @@ void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) { QString certID = entity->getCertificateID(); if (!certID.isEmpty()) { QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); - if (entity->getEntityItemID() == _entityCertificateIDMap.value(certID)) { + QList entityList = _entityCertificateIDMap.value(certID); + if (!entityList.isEmpty() && (entity->getEntityItemID() == entityList.first())) { _entityCertificateIDMap.remove(certID); } } @@ -1429,12 +1433,16 @@ void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) { void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove) { QReadLocker locker(&_entityCertificateIDMapLock); - QHashIterator i(_entityCertificateIDMap); + QHashIterator> i(_entityCertificateIDMap); qCDebug(entities) << _entityCertificateIDMap.size() << "certificates present."; while (i.hasNext()) { i.next(); const auto& certificateID = i.key(); - const auto& entityID = i.value(); + const auto& entityIDs = i.value(); + + if (entityIDs.isEmpty()) { + continue; + } // Examine each cert: QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); @@ -1449,7 +1457,7 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply, minimumAgeToRemove, &certificateID] { + connect(networkReply, &QNetworkReply::finished, this, [this, entityIDs, networkReply, minimumAgeToRemove, &certificateID] { QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); jsonObject = jsonObject["data"].toObject(); @@ -1458,7 +1466,7 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove networkReply->deleteLater(); if (failure) { qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << failureReason - << "; NOT deleting entity" << entityID << "More info:" << jsonObject; + << "; NOT deleting cert" << certificateID << "More info:" << jsonObject; return; } QString thisDomainID = DependencyManager::get()->getDomainID().remove(QRegExp("\\{|\\}")); @@ -1468,6 +1476,7 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove } // Entity does not belong here: { + EntityItemID entityID = entityIDs.first(); EntityItemPointer entity = findEntityByEntityItemID(entityID); if (!entity) { qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 2b3ee93ab7..c80517c82b 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -323,7 +323,7 @@ protected: QHash _entityMap; mutable QReadWriteLock _entityCertificateIDMapLock; - QHash _entityCertificateIDMap; + QHash> _entityCertificateIDMap; mutable QReadWriteLock _entityNonceMapLock; QHash> _entityNonceMap; From 489fadda6ea3e406fbfd145125ef01119fe88bbc Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 27 Mar 2019 14:58:58 -0700 Subject: [PATCH 10/10] and now it's a list that cleans up after itself --- libraries/entities/src/EntityTree.cpp | 42 +++++++++++++++++---------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 01c00b82e5..1ccf3fcfa2 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1398,20 +1398,21 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { void EntityTree::addCertifiedEntityOnServer(EntityItemPointer entity) { QString certID(entity->getCertificateID()); - QList entityList; + EntityItemID existingEntityItemID; if (!certID.isEmpty()) { EntityItemID entityItemID = entity->getEntityItemID(); QWriteLocker locker(&_entityCertificateIDMapLock); - entityList = _entityCertificateIDMap.value(certID); - QList newList; - newList << entityItemID; - _entityCertificateIDMap.insert(certID, newList); - qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; + QList& entityList = _entityCertificateIDMap[certID]; // inserts it if needed. + if (!entityList.isEmpty() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { + existingEntityItemID = entityList.first(); // we will only care about the first, if any, below. + entityList.removeOne(existingEntityItemID); + } + entityList << entityItemID; // adds to list within hash because entityList is a reference. + qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID << "total" << entityList.size() << "entities."; } // Delete an already-existing entity from the tree if it has the same // CertificateID as the entity we're trying to add. - if (!entityList.isEmpty() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) { - EntityItemID existingEntityItemID = entityList.first(); + if (!existingEntityItemID.isNull()) { qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" << existingEntityItemID << ". Deleting existing entity."; withWriteLock([&] { @@ -1424,8 +1425,10 @@ void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) { QString certID = entity->getCertificateID(); if (!certID.isEmpty()) { QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); - QList entityList = _entityCertificateIDMap.value(certID); - if (!entityList.isEmpty() && (entity->getEntityItemID() == entityList.first())) { + QList& entityList = _entityCertificateIDMap[certID]; + entityList.removeOne(entity->getEntityItemID()); + if (entityList.isEmpty()) { + // hmmm, do we to make it be a hash instead of a list, so that this is faster if you stamp out 1000 of a domainUnlimited? _entityCertificateIDMap.remove(certID); } } @@ -1439,7 +1442,6 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove i.next(); const auto& certificateID = i.key(); const auto& entityIDs = i.value(); - if (entityIDs.isEmpty()) { continue; } @@ -1475,16 +1477,18 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove return; } // Entity does not belong here: - { - EntityItemID entityID = entityIDs.first(); + QList retained; + for (int i = 0; i < entityIDs.size(); i++) { + EntityItemID entityID = entityIDs.at(i); EntityItemPointer entity = findEntityByEntityItemID(entityID); if (!entity) { qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID; - return; + continue; } if (entity->getAge() <= minimumAgeToRemove) { qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID; - return; + retained << entityID; + continue; } qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID; @@ -1494,7 +1498,13 @@ void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove } { QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock); - _entityCertificateIDMap.remove(certificateID); + if (retained.isEmpty()) { + qCDebug(entities) << "Removed" << certificateID; + _entityCertificateIDMap.remove(certificateID); + } else { + qCDebug(entities) << "Retained" << retained.size() << "young entities for" << certificateID; + _entityCertificateIDMap[certificateID] = retained; + } } }); }