From b7e50c5b73a4235f00331195becf57f77048addb Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 3 Oct 2017 13:52:35 -0700 Subject: [PATCH 01/49] First steps --- .../resources/describe-settings.json | 140 +++++++++++++++--- .../qml/hifi/commerce/checkout/Checkout.qml | 4 +- libraries/entities/src/EntityTree.cpp | 56 ++++++- libraries/entities/src/EntityTree.h | 3 + 4 files changed, 179 insertions(+), 24 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 8d0e949ff3..f1180e00c2 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1,5 +1,5 @@ { - "version": 1.8, + "version": 1.9, "settings": [ { "name": "metaverse", @@ -207,7 +207,7 @@ "name": "standard_permissions", "type": "table", "label": "Domain-Wide User Permissions", - "help": "Indicate which types of users can have which domain-wide permissions.", + "help": "Indicate which types of users can have which domain-wide permissions.", "caption": "Standard Permissions", "can_add_new_rows": false, "groups": [ @@ -216,7 +216,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 8 } ], @@ -253,6 +253,20 @@ "editable": true, "default": false }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, { "name": "id_can_write_to_asset_server", "label": "Write Assets", @@ -283,7 +297,7 @@ } ], "non-deletable-row-key": "permissions_id", - "non-deletable-row-values": ["localhost", "anonymous", "logged-in"] + "non-deletable-row-values": [ "localhost", "anonymous", "logged-in" ] }, { "name": "group_permissions", @@ -300,7 +314,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 8 } ], @@ -362,6 +376,20 @@ "editable": true, "default": false }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, { "name": "id_can_write_to_asset_server", "label": "Write Assets", @@ -383,7 +411,7 @@ "editable": true, "default": false }, - { + { "name": "id_can_replace_content", "label": "Replace Content", "type": "checkbox", @@ -407,7 +435,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 8 } ], @@ -466,6 +494,20 @@ "editable": true, "default": false }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, { "name": "id_can_write_to_asset_server", "label": "Write Assets", @@ -487,7 +529,7 @@ "editable": true, "default": false }, - { + { "name": "id_can_replace_content", "label": "Replace Content", "type": "checkbox", @@ -507,7 +549,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 8 } ], @@ -544,6 +586,20 @@ "editable": true, "default": false }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, { "name": "id_can_write_to_asset_server", "label": "Write Assets", @@ -565,7 +621,7 @@ "editable": true, "default": false }, - { + { "name": "id_can_replace_content", "label": "Replace Content", "type": "checkbox", @@ -585,7 +641,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 8 } ], @@ -622,6 +678,20 @@ "editable": true, "default": false }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, { "name": "id_can_write_to_asset_server", "label": "Write Assets", @@ -643,7 +713,7 @@ "editable": true, "default": false }, - { + { "name": "id_can_replace_content", "label": "Replace Content", "type": "checkbox", @@ -663,7 +733,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 8 } ], @@ -700,6 +770,20 @@ "editable": true, "default": false }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, { "name": "id_can_write_to_asset_server", "label": "Write Assets", @@ -721,7 +805,7 @@ "editable": true, "default": false }, - { + { "name": "id_can_replace_content", "label": "Replace Content", "type": "checkbox", @@ -741,7 +825,7 @@ "span": 1 }, { - "label": "Permissions ?", + "label": "Permissions ?", "span": 8 } ], @@ -778,6 +862,20 @@ "editable": true, "default": false }, + { + "name": "id_can_rez_certified", + "label": "Rez Certified", + "type": "checkbox", + "editable": true, + "default": false + }, + { + "name": "id_can_rez_tmp_certified", + "label": "Rez Temporary Certified", + "type": "checkbox", + "editable": true, + "default": false + }, { "name": "id_can_write_to_asset_server", "label": "Write Assets", @@ -799,7 +897,7 @@ "editable": true, "default": false }, - { + { "name": "id_can_replace_content", "label": "Replace Content", "type": "checkbox", @@ -841,7 +939,7 @@ { "name": "asset_server", "label": "Asset Server (ATP)", - "assignment-types": [3], + "assignment-types": [ 3 ], "settings": [ { "name": "enabled", @@ -864,7 +962,7 @@ { "name": "entity_script_server", "label": "Entity Script Server (ESS)", - "assignment-types": [5], + "assignment-types": [ 5 ], "settings": [ { "name": "entity_pps_per_script", @@ -887,7 +985,7 @@ { "name": "avatars", "label": "Avatars", - "assignment-types": [1, 2], + "assignment-types": [ 1, 2 ], "settings": [ { "name": "min_avatar_scale", @@ -926,7 +1024,7 @@ { "name": "audio_threading", "label": "Audio Threading", - "assignment-types": [0], + "assignment-types": [ 0 ], "settings": [ { "name": "auto_threads", @@ -949,7 +1047,7 @@ { "name": "audio_env", "label": "Audio Environment", - "assignment-types": [0], + "assignment-types": [ 0 ], "settings": [ { "name": "attenuation_per_doubling_in_distance", diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 77180f7bab..32f324aea9 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -35,7 +35,7 @@ Rectangle { property string itemHref; property double balanceAfterPurchase; property bool alreadyOwned: false; - property int itemPrice; + property int itemPrice: -1; property bool itemIsJson: true; property bool shouldBuyWithControlledFailure: false; property bool debugCheckoutSuccess: false; @@ -339,7 +339,7 @@ Rectangle { } FiraSansSemiBold { id: itemPriceText; - text: root.itemPrice; + text: (root.itemPrice === -1) ? "--" : root.itemPrice; // Text size size: 26; // Anchors diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index bf37a08386..b01c9b2d26 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -359,6 +359,18 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti qCDebug(entities) << (senderNode ? senderNode->getUUID() : "null") << "physical edits suppressed"; } } + + // Clear Certificate ID if any static certificate property is changed + if (properties.itemNameChanged() || properties.itemDescriptionChanged() || properties.itemCategoriesChanged() || + properties.itemArtistChanged() || properties.itemLicenseChanged() || properties.limitedRunChanged() || + properties.editionNumberChanged() || properties.entityInstanceNumberChanged() || properties.certificateIDChanged()) { + qCDebug(entities) << "A static certificate property on Entity" << entity->getID() << "has changed." + << "Clearing Certificate ID."; + QWriteLocker locker(&_entityCertificateIDMapLock); + _entityCertificateIDMap.remove(entity->getCertificateID()); + properties.setCertificateID(""); + properties.setCertificateIDChanged(true); + } } // else client accepts what the server says @@ -653,6 +665,9 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) if (_simulation) { _simulation->prepareEntityForDelete(theEntity); } + + QWriteLocker locker(&_entityCertificateIDMapLock); + _entityCertificateIDMap.remove(theEntity->getProperties(PROP_CERTIFICATE_ID).getCertificateID()); } } @@ -1257,13 +1272,17 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c _totalUpdates++; } else if (isAdd) { bool failedAdd = !allowed; + bool isCertified = !properties.getCertificateID().isEmpty(); if (!allowed) { qCDebug(entities) << "Filtered entity add. ID:" << entityItemID; } else if (!senderNode->getCanRez() && !senderNode->getCanRezTmp()) { failedAdd = true; qCDebug(entities) << "User without 'rez rights' [" << senderNode->getUUID() << "] attempted to add an entity ID:" << entityItemID; - + } else if (isCertified && !senderNode->getCanRezCertified() && !senderNode->getCanRezTmpCertified()) { + failedAdd = true; + qCDebug(entities) << "User without 'certified rez rights' [" << senderNode->getUUID() + << "] attempted to add a certified entity with ID:" << entityItemID; } else { // this is a new entity... assign a new entityID properties.setCreated(properties.getLastEdited()); @@ -1272,6 +1291,41 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c EntityItemPointer newEntity = addEntity(entityItemID, properties); endCreate = usecTimestampNow(); _totalCreates++; + + if (newEntity && isCertified) { + if (!newEntity->verifyStaticCertificateProperties()) { + qCDebug(entities) << "User" << senderNode->getUUID() + << "attempted to add a certified entity with ID" << entityItemID << "which failed" + << "static certificate verification."; + // Delete the entity we just added if it doesn't pass static certificate verification + deleteEntity(entityItemID, true); + } else { + QString certID = properties.getCertificateID(); + EntityItemID existingEntityItemID; + + { + QReadLocker locker(&_entityCertificateIDMapLock); + existingEntityItemID = _entityCertificateIDMap.value(certID); + } + + // 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()) { + qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" + << existingEntityItemID << ". Deleting existing entity."; + deleteEntity(existingEntityItemID, true); + } + + { + QWriteLocker locker(&_entityCertificateIDMapLock); + _entityCertificateIDMap.insert(certID, entityItemID); + qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; + } + + // Start owner verification + } + } + if (newEntity) { newEntity->markAsChangedOnServer(); notifyNewlyCreatedEntity(*newEntity, senderNode); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index d0448f438a..4168dc3b66 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -316,6 +316,9 @@ protected: mutable QReadWriteLock _entityMapLock; QHash _entityMap; + mutable QReadWriteLock _entityCertificateIDMapLock; + QHash _entityCertificateIDMap; + EntitySimulationPointer _simulation; bool _wantEditLogging = false; From ed40e41b94a5bf1f0a4379ae9e4c99b8a82a8d8b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 3 Oct 2017 16:18:29 -0700 Subject: [PATCH 02/49] Potential progress? Not totally sure --- libraries/entities/src/EntityTree.cpp | 40 ++++++++++++++++++++++++++- libraries/entities/src/EntityTree.h | 9 ++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b01c9b2d26..0f5059d688 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1097,6 +1097,29 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { return false; } +void EntityTree::popStatusSuccess(QNetworkReply& reply) { + QJsonObject jsonObject = QJsonDocument::fromJson(reply.readAll()).object(); + qCDebug(entities) << "ZRF FIXME" << jsonObject; + //if (!jsonObject["invalid_reason"].toString().isEmpty()) { + // qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; + // deleteEntity(entityItemID, true); + //} else if (jsonObject["transfer_status"].toString() == "failed") { + // qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; + // deleteEntity(entityItemID, true); + //} else { + // //auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, NUM_BYTES_RFC4122_UUID + sizeof(encryptedText)); + // //challengeOwnershipPacket->write(senderNode->getUUID()); + // //challengeOwnershipPacket->writePrimitive(KillAvatarReason::TheirAvatarEnteredYourBubble); + //} +} + +void EntityTree::popStatusFailure(QNetworkReply& reply) { + QJsonObject jsonObject = QJsonDocument::fromJson(reply.readAll()).object(); + qCDebug(entities) << "ZRF FIXME" << jsonObject; + //qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; + //deleteEntity(entityItemID, true); +} + int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) { @@ -1322,7 +1345,22 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; } - // Start owner verification + // Start owner verification. + // First, asynchronously hit "proof_of_purchase_status?transaction_type=transfer" endpoint. + const QString endpoint("proof_of_purchase_status?transaction_type=transfer"); + QJsonObject request; + request["certificate_id"] = certID; + request["domain_id"] = DependencyManager::get()->getDomainHandler().getUUID().toString(); + + auto accountManager = DependencyManager::get(); + const QString URL = "/api/v1/commerce/"; + JSONCallbackParameters callbackParams(this, "popStatusSuccess", this, "popStatusFailure"); + qCDebug(entities) << "Sending" << endpoint << QJsonDocument(request).toJson(QJsonDocument::Compact); + accountManager->sendRequest(URL + endpoint, + AccountManagerAuth::None, + QNetworkAccessManager::PostOperation, + callbackParams, + QJsonDocument(request).toJson()); } } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 4168dc3b66..8c26cd0791 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -15,6 +15,11 @@ #include #include +#include "AccountManager.h" +#include +#include +#include + #include #include @@ -277,6 +282,10 @@ signals: void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID); void clearingEntities(); +private slots: + void popStatusSuccess(QNetworkReply& reply); + void popStatusFailure(QNetworkReply& reply); + protected: void processRemovedEntities(const DeleteEntityOperator& theOperator); From 2cdd5c29f315d9269ed8150b13499bb7fcec5aaf Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Oct 2017 09:58:29 -0700 Subject: [PATCH 03/49] Uncertified rez rights --- libraries/entities/src/EntityTree.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 0f5059d688..e067637db8 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1298,10 +1298,10 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c bool isCertified = !properties.getCertificateID().isEmpty(); if (!allowed) { qCDebug(entities) << "Filtered entity add. ID:" << entityItemID; - } else if (!senderNode->getCanRez() && !senderNode->getCanRezTmp()) { + } else if (!isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) { failedAdd = true; - qCDebug(entities) << "User without 'rez rights' [" << senderNode->getUUID() - << "] attempted to add an entity ID:" << entityItemID; + qCDebug(entities) << "User without 'uncertified rez rights' [" << senderNode->getUUID() + << "] attempted to add an uncertified entity with ID:" << entityItemID; } else if (isCertified && !senderNode->getCanRezCertified() && !senderNode->getCanRezTmpCertified()) { failedAdd = true; qCDebug(entities) << "User without 'certified rez rights' [" << senderNode->getUUID() From 23245e2fb9c82cb6f5a58636609318a6170a4ee8 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Oct 2017 10:39:23 -0700 Subject: [PATCH 04/49] Modify context overlay filter --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 39fd4f9377..8cbb214857 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -41,6 +41,7 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_MARKETPLACE_ID; _entityPropertyFlags += PROP_DIMENSIONS; _entityPropertyFlags += PROP_REGISTRATION_POINT; + _entityPropertyFlags += PROP_CERTIFICATE_ID; auto entityTreeRenderer = DependencyManager::get().data(); connect(entityTreeRenderer, SIGNAL(mousePressOnEntity(const EntityItemID&, const PointerEvent&)), this, SLOT(createOrDestroyContextOverlay(const EntityItemID&, const PointerEvent&))); @@ -176,7 +177,12 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& bool ContextOverlayInterface::contextOverlayFilterPassed(const EntityItemID& entityItemID) { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags); - return (entityProperties.getMarketplaceID().length() != 0); + Setting::Handle _settingSwitch{ "commerce", false }; + if (_settingSwitch.get()) { + return (entityProperties.getCertificateID().length() != 0); + } else { + return (entityProperties.getMarketplaceID().length() != 0); + } } bool ContextOverlayInterface::destroyContextOverlay(const EntityItemID& entityItemID, const PointerEvent& event) { From ab08dc6bd6abee1ba54c2bcc0ee3b15af42e3d16 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Oct 2017 12:33:58 -0700 Subject: [PATCH 05/49] Progress? --- .../resources/qml/hifi/commerce/checkout/Checkout.qml | 1 + .../qml/hifi/commerce/purchases/PurchasedItem.qml | 1 + interface/src/commerce/Ledger.cpp | 4 ++-- interface/src/commerce/QmlCommerce.cpp | 6 ++++++ interface/src/commerce/QmlCommerce.h | 2 ++ libraries/entities/src/EntityTree.cpp | 11 +++++++---- libraries/entities/src/EntityTree.h | 1 - 7 files changed, 19 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 32f324aea9..80c9a3ec0f 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -579,6 +579,7 @@ Rectangle { } rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start(); + commerce.updatePopLocation(root.itemId); } } RalewaySemiBold { diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index a026a818c0..361a89d047 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -343,6 +343,7 @@ Item { if (urlHandler.canHandleUrl(root.itemHref)) { urlHandler.handleUrl(root.itemHref); } + commerce.updatePopLocation(root.itemId); rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start(); } diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index a68a6fe929..e926d8918e 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -221,8 +221,8 @@ void Ledger::account() { } // The api/failResponse is called just for the side effect of logging. -void Ledger::updateLocationSuccess(QNetworkReply& reply) { apiResponse("reset", reply); } -void Ledger::updateLocationFailure(QNetworkReply& reply) { failResponse("reset", reply); } +void Ledger::updateLocationSuccess(QNetworkReply& reply) { apiResponse("updateLocation", reply); } +void Ledger::updateLocationFailure(QNetworkReply& reply) { failResponse("updateLocation", reply); } void Ledger::updateLocation(const QString& asset_id, const QString location, const bool controlledFailure) { auto wallet = DependencyManager::get(); QStringList keys = wallet->listPublicKeys(); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index dbd84594bc..8c22542d94 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -149,3 +149,9 @@ void QmlCommerce::account() { auto ledger = DependencyManager::get(); ledger->account(); } + +void QmlCommerce::updatePopLocation(const QString& popId, const bool controlledFailure) { + auto ledger = DependencyManager::get(); + auto nodeList = DependencyManager::get(); + ledger->updateLocation(popId, nodeList->getDomainHandler().getUUID().toString(), controlledFailure); +} diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index 8e6af6da65..b1d179aae8 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -70,6 +70,8 @@ protected: Q_INVOKABLE void generateKeyPair(); Q_INVOKABLE void reset(); Q_INVOKABLE void account(); + + Q_INVOKABLE void updatePopLocation(const QString& popId, const bool controlledFailure = false); }; #endif // hifi_QmlCommerce_h diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index e067637db8..112066a2f8 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1107,9 +1107,6 @@ void EntityTree::popStatusSuccess(QNetworkReply& reply) { // qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; // deleteEntity(entityItemID, true); //} else { - // //auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, NUM_BYTES_RFC4122_UUID + sizeof(encryptedText)); - // //challengeOwnershipPacket->write(senderNode->getUUID()); - // //challengeOwnershipPacket->writePrimitive(KillAvatarReason::TheirAvatarEnteredYourBubble); //} } @@ -1358,9 +1355,15 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c qCDebug(entities) << "Sending" << endpoint << QJsonDocument(request).toJson(QJsonDocument::Compact); accountManager->sendRequest(URL + endpoint, AccountManagerAuth::None, - QNetworkAccessManager::PostOperation, + QNetworkAccessManager::GetOperation, callbackParams, QJsonDocument(request).toJson()); + + // Second, challenge ownership of the PoP cert + //auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, NUM_BYTES_RFC4122_UUID + sizeof(encryptedText)); + //challengeOwnershipPacket->write(senderNode->getUUID()); + //challengeOwnershipPacket->writePrimitive(KillAvatarReason::TheirAvatarEnteredYourBubble); + //// Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time } } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 8c26cd0791..3ea48c113a 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -18,7 +18,6 @@ #include "AccountManager.h" #include #include -#include #include #include From cf7c1934f875d682473c0e5de7b17b1a4da2d28e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Oct 2017 13:12:21 -0700 Subject: [PATCH 06/49] Progress? --- libraries/entities/src/EntityTree.cpp | 75 ++++++++++++++------------- libraries/entities/src/EntityTree.h | 5 +- 2 files changed, 41 insertions(+), 39 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 112066a2f8..a31c5a139e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1097,26 +1097,6 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { return false; } -void EntityTree::popStatusSuccess(QNetworkReply& reply) { - QJsonObject jsonObject = QJsonDocument::fromJson(reply.readAll()).object(); - qCDebug(entities) << "ZRF FIXME" << jsonObject; - //if (!jsonObject["invalid_reason"].toString().isEmpty()) { - // qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; - // deleteEntity(entityItemID, true); - //} else if (jsonObject["transfer_status"].toString() == "failed") { - // qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; - // deleteEntity(entityItemID, true); - //} else { - //} -} - -void EntityTree::popStatusFailure(QNetworkReply& reply) { - QJsonObject jsonObject = QJsonDocument::fromJson(reply.readAll()).object(); - qCDebug(entities) << "ZRF FIXME" << jsonObject; - //qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; - //deleteEntity(entityItemID, true); -} - int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) { @@ -1344,26 +1324,51 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c // Start owner verification. // First, asynchronously hit "proof_of_purchase_status?transaction_type=transfer" endpoint. - const QString endpoint("proof_of_purchase_status?transaction_type=transfer"); + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status?transaction_type=transfer"); QJsonObject request; request["certificate_id"] = certID; request["domain_id"] = DependencyManager::get()->getDomainHandler().getUUID().toString(); + networkRequest.setUrl(requestURL); - auto accountManager = DependencyManager::get(); - const QString URL = "/api/v1/commerce/"; - JSONCallbackParameters callbackParams(this, "popStatusSuccess", this, "popStatusFailure"); - qCDebug(entities) << "Sending" << endpoint << QJsonDocument(request).toJson(QJsonDocument::Compact); - accountManager->sendRequest(URL + endpoint, - AccountManagerAuth::None, - QNetworkAccessManager::GetOperation, - callbackParams, - QJsonDocument(request).toJson()); + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.get(networkRequest); - // Second, challenge ownership of the PoP cert - //auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, NUM_BYTES_RFC4122_UUID + sizeof(encryptedText)); - //challengeOwnershipPacket->write(senderNode->getUUID()); - //challengeOwnershipPacket->writePrimitive(KillAvatarReason::TheirAvatarEnteredYourBubble); - //// Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + QJsonDocument doc(jsonObject); + qCDebug(entities) << "ZRF FIXME" << networkReply->error(); + qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); + + if (networkReply->error() == QNetworkReply::NoError) { + //QJsonObject jsonObject = QJsonDocument::fromJson(reply.readAll()).object(); + //qCDebug(entities) << "ZRF FIXME" << jsonObject; + //if (!jsonObject["invalid_reason"].toString().isEmpty()) { + // qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; + // deleteEntity(entityItemID, true); + //} else if (jsonObject["transfer_status"].toString() == "failed") { + // qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; + // deleteEntity(entityItemID, true); + //} else { + // // Second, challenge ownership of the PoP cert + // // 1. Encrypt a nonce with the owner's public key + // QString ownerKey(jsonObject["owner_public_key"].toString()); + // QString encryptedText(""); + + // // 2. Send the encrypted text to the rezzing avatar's node + // auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, NUM_BYTES_RFC4122_UUID + sizeof(encryptedText)); + // challengeOwnershipPacket->write(senderNode->getUUID()); + // challengeOwnershipPacket->writePrimitive(encryptedText); + // // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time + //} + } else { + //qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; + //deleteEntity(entityItemID, true); + } + }); } } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 3ea48c113a..da202c0df9 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -18,6 +18,7 @@ #include "AccountManager.h" #include #include +#include #include #include @@ -281,10 +282,6 @@ signals: void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID); void clearingEntities(); -private slots: - void popStatusSuccess(QNetworkReply& reply); - void popStatusFailure(QNetworkReply& reply); - protected: void processRemovedEntities(const DeleteEntityOperator& theOperator); From f7ea50ef3bb5787bfdcb21841e3209bf1fc7f266 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Oct 2017 14:02:28 -0700 Subject: [PATCH 07/49] Free memory --- libraries/entities/src/EntityTree.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index a31c5a139e..bb320317c6 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1365,9 +1365,11 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c // // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time //} } else { - //qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; - //deleteEntity(entityItemID, true); + qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; + deleteEntity(entityItemID, true); } + + networkReply->deleteLater(); }); } } From b35eaf2cc80db190487c5072a76d15bb01f9ff66 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Oct 2017 15:06:55 -0700 Subject: [PATCH 08/49] Throw a bunch of code at the wall and hope some sticks --- .../octree/OctreeInboundPacketProcessor.cpp | 4 +- interface/src/commerce/Wallet.cpp | 29 +++-- libraries/entities/src/EntityTree.cpp | 106 ++++++++++++++---- libraries/entities/src/EntityTree.h | 2 + libraries/octree/src/Octree.h | 1 + 5 files changed, 112 insertions(+), 30 deletions(-) diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index 04409b3b21..2723e8acd5 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -92,7 +92,9 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer // Ask our tree subclass if it can handle the incoming packet... PacketType packetType = message->getType(); - if (_myServer->getOctree()->handlesEditPacketType(packetType)) { + if (packetType == PacketType::ChallengeOwnership) { + _myServer->getOctree()->processChallengeOwnershipPacket(*message, sendingNode); + } else if (_myServer->getOctree()->handlesEditPacketType(packetType)) { PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket); _receivedPacketCount++; diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index cc2039da48..93e3f7acda 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -656,16 +656,29 @@ bool Wallet::changePassphrase(const QString& newPassphrase) { void Wallet::handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode) { QString decryptedText; - quint64 encryptedTextSize; - quint64 publicKeySize; + int certIDByteArraySize; + int encryptedTextByteArraySize; + int ownerKeyByteArraySize; - if (verifyOwnerChallenge(packet->read(packet->readPrimitive(&encryptedTextSize)), packet->read(packet->readPrimitive(&publicKeySize)), decryptedText)) { + packet->readPrimitive(&certIDByteArraySize); + packet->readPrimitive(&encryptedTextByteArraySize); + packet->readPrimitive(&ownerKeyByteArraySize); + + QByteArray certID = packet->read(certIDByteArraySize); + + if (verifyOwnerChallenge(packet->read(encryptedTextByteArraySize), packet->read(ownerKeyByteArraySize), decryptedText)) { auto nodeList = DependencyManager::get(); - // setup the packet - auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, NUM_BYTES_RFC4122_UUID + decryptedText.size(), true); - // write the decrypted text to the packet - decryptedTextPacket->write(decryptedText.toUtf8()); + QByteArray decryptedTextByteArray = decryptedText.toUtf8(); + int decryptedTextByteArraySize = decryptedTextByteArray.size(); + int certIDSize = certID.size(); + // setup the packet + auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + decryptedTextByteArraySize + 2*sizeof(int), true); + + decryptedTextPacket->writePrimitive(certIDSize); + decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); + decryptedTextPacket->write(certID); + decryptedTextPacket->write(decryptedTextByteArray); qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text"; @@ -677,6 +690,6 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack bool Wallet::verifyOwnerChallenge(const QByteArray& encryptedText, const QString& publicKey, QString& decryptedText) { // I have no idea how to do this yet, so here's some dummy code that may not even work. - decryptedText = QString("hello"); + decryptedText = QString("fail"); return true; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index bb320317c6..29d0e0cc84 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1097,6 +1097,30 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { return false; } +void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { + int certIDByteArraySize; + int decryptedTextByteArraySize; + + message.readPrimitive(&certIDByteArraySize); + message.readPrimitive(&decryptedTextByteArraySize); + + QString certID(message.read(certIDByteArraySize)); + QString decryptedText(message.read(decryptedTextByteArraySize)); + + emit killChallengeOwnershipTimeoutTimer(certID); + + if (decryptedText == "fail") { + QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); + EntityItemID id = _entityCertificateIDMap.value(certID); + + qCDebug(entities) << "Ownership challenge failed, deleting entity" << id; + deleteEntity(id, true); + QWriteLocker recentlyDeletedLocker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), id); + } else { + } +} + int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) { @@ -1299,6 +1323,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c << "static certificate verification."; // Delete the entity we just added if it doesn't pass static certificate verification deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else { QString certID = properties.getCertificateID(); EntityItemID existingEntityItemID; @@ -1314,6 +1340,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" << existingEntityItemID << ". Deleting existing entity."; deleteEntity(existingEntityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), existingEntityItemID); } { @@ -1323,6 +1351,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } // Start owner verification. + auto nodeList = DependencyManager::get(); // First, asynchronously hit "proof_of_purchase_status?transaction_type=transfer" endpoint. QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest; @@ -1331,7 +1360,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c requestURL.setPath("/api/v1/commerce/proof_of_purchase_status?transaction_type=transfer"); QJsonObject request; request["certificate_id"] = certID; - request["domain_id"] = DependencyManager::get()->getDomainHandler().getUUID().toString(); + request["domain_id"] = nodeList->getDomainHandler().getUUID().toString(); networkRequest.setUrl(requestURL); QNetworkReply* networkReply = NULL; @@ -1340,33 +1369,68 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c connect(networkReply, &QNetworkReply::finished, [=]() { QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); QJsonDocument doc(jsonObject); - qCDebug(entities) << "ZRF FIXME" << networkReply->error(); qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); if (networkReply->error() == QNetworkReply::NoError) { - //QJsonObject jsonObject = QJsonDocument::fromJson(reply.readAll()).object(); - //qCDebug(entities) << "ZRF FIXME" << jsonObject; - //if (!jsonObject["invalid_reason"].toString().isEmpty()) { - // qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; - // deleteEntity(entityItemID, true); - //} else if (jsonObject["transfer_status"].toString() == "failed") { - // qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; - // deleteEntity(entityItemID, true); - //} else { - // // Second, challenge ownership of the PoP cert - // // 1. Encrypt a nonce with the owner's public key - // QString ownerKey(jsonObject["owner_public_key"].toString()); - // QString encryptedText(""); + if (!jsonObject["invalid_reason"].toString().isEmpty()) { + qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; + deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + } else if (jsonObject["transfer_status"].toString() == "failed") { + qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; + deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + } else { + // Second, challenge ownership of the PoP cert + // 1. Encrypt a nonce with the owner's public key + QString ownerKey(jsonObject["owner_public_key"].toString()); + QString encryptedText("test"); - // // 2. Send the encrypted text to the rezzing avatar's node - // auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, NUM_BYTES_RFC4122_UUID + sizeof(encryptedText)); - // challengeOwnershipPacket->write(senderNode->getUUID()); - // challengeOwnershipPacket->writePrimitive(encryptedText); - // // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time - //} + // 2. Send the encrypted text to the rezzing avatar's node + QByteArray certIDByteArray = certID.toUtf8(); + int certIDByteArraySize = certIDByteArray.size(); + QByteArray encryptedTextByteArray = encryptedText.toUtf8(); + int encryptedTextByteArraySize = encryptedTextByteArray.size(); + QByteArray ownerKeyByteArray = ownerKey.toUtf8(); + int ownerKeyByteArraySize = ownerKeyByteArray.size(); + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, + certIDByteArraySize + encryptedTextByteArraySize + ownerKeyByteArraySize + 3*sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); + challengeOwnershipPacket->writePrimitive(ownerKeyByteArraySize); + challengeOwnershipPacket->write(certIDByteArray); + challengeOwnershipPacket->write(encryptedTextByteArray); + challengeOwnershipPacket->write(ownerKeyByteArray); + nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode); + + // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time + QTimer* challengeOwnershipTimeoutTimer = new QTimer(this); + connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [&](const QString& certID) { + QReadLocker locker(&_entityCertificateIDMapLock); + EntityItemID id = _entityCertificateIDMap.value(certID); + if (entityItemID == id) { + challengeOwnershipTimeoutTimer->stop(); + challengeOwnershipTimeoutTimer->deleteLater(); + } + }); + connect(challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { + qCDebug(entities) << "Ownership challenge timed out, deleting entity" << entityItemID; + deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + }); + challengeOwnershipTimeoutTimer->setSingleShot(false); + challengeOwnershipTimeoutTimer->setInterval(10000); + challengeOwnershipTimeoutTimer->start(); + } } else { qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } networkReply->deleteLater(); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index da202c0df9..68f2c605af 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -100,6 +100,7 @@ public: void fixupTerseEditLogging(EntityItemProperties& properties, QList& changedProperties); virtual int processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) override; + virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, QVector entityIdsToInclude, QVector entityIdsToDiscard, @@ -281,6 +282,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); protected: diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index a2df5f44e5..250577f351 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -214,6 +214,7 @@ public: virtual bool handlesEditPacketType(PacketType packetType) const { return false; } virtual int processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& sourceNode) { return 0; } + virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; } virtual bool recurseChildrenWithData() const { return true; } virtual bool rootElementHasData() const { return false; } From f974672e7b31171b8cbe913eb7d675187278b0d2 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Oct 2017 15:19:06 -0700 Subject: [PATCH 09/49] Working way better than I expected --- assignment-client/src/entities/EntityServer.cpp | 2 +- libraries/entities/src/EntityTree.cpp | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 2c8f8a9e37..1c1e02316a 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -36,7 +36,7 @@ EntityServer::EntityServer(ReceivedMessage& message) : DependencyManager::set(); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityEdit, PacketType::EntityErase, PacketType::EntityPhysics }, + packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityEdit, PacketType::EntityErase, PacketType::EntityPhysics, PacketType::ChallengeOwnership }, this, "handleEntityPacket"); } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 29d0e0cc84..64b9c7b1ca 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1107,6 +1107,8 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const QString certID(message.read(certIDByteArraySize)); QString decryptedText(message.read(decryptedTextByteArraySize)); + qCDebug(entities) << "ZRF FIXME" << decryptedText << certID; + emit killChallengeOwnershipTimeoutTimer(certID); if (decryptedText == "fail") { @@ -1371,17 +1373,24 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c QJsonDocument doc(jsonObject); qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); - if (networkReply->error() == QNetworkReply::NoError) { - if (!jsonObject["invalid_reason"].toString().isEmpty()) { + // ZRF FIXME!!! + //if (networkReply->error() == QNetworkReply::NoError) { + if (true) { + // ZRF FIXME!!! + //if (!jsonObject["invalid_reason"].toString().isEmpty()) { + if (false) { qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; deleteEntity(entityItemID, true); QWriteLocker locker(&_recentlyDeletedEntitiesLock); _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); - } else if (jsonObject["transfer_status"].toString() == "failed") { + // ZRF FIXME!!! + //} else if (jsonObject["transfer_status"].toString() == "failed") { + } else if (false) { qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; deleteEntity(entityItemID, true); QWriteLocker locker(&_recentlyDeletedEntitiesLock); _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + // ZRF FIXME!!! } else { // Second, challenge ownership of the PoP cert // 1. Encrypt a nonce with the owner's public key @@ -1422,7 +1431,6 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c QWriteLocker locker(&_recentlyDeletedEntitiesLock); _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); }); - challengeOwnershipTimeoutTimer->setSingleShot(false); challengeOwnershipTimeoutTimer->setInterval(10000); challengeOwnershipTimeoutTimer->start(); } From 4cfb860e23ea75dbfa083b44587993e16ed51842 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Oct 2017 15:45:12 -0700 Subject: [PATCH 10/49] Wow, this actually seems to be working --- libraries/entities/src/EntityTree.cpp | 45 +++++++++++++++++---------- libraries/entities/src/EntityTree.h | 2 ++ 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 64b9c7b1ca..7322378b5f 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1097,6 +1097,28 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { return false; } +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); + if (entityItemID == id) { + _challengeOwnershipTimeoutTimer->stop(); + _challengeOwnershipTimeoutTimer->deleteLater(); + } + }); + connect(_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { + qCDebug(entities) << "Ownership challenge timed out, deleting entity" << entityItemID; + deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + _challengeOwnershipTimeoutTimer->stop(); + _challengeOwnershipTimeoutTimer->deleteLater(); + }); + _challengeOwnershipTimeoutTimer->setInterval(5000); + _challengeOwnershipTimeoutTimer->start(); +} + void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { int certIDByteArraySize; int decryptedTextByteArraySize; @@ -1416,23 +1438,12 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode); // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time - QTimer* challengeOwnershipTimeoutTimer = new QTimer(this); - connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [&](const QString& certID) { - QReadLocker locker(&_entityCertificateIDMapLock); - EntityItemID id = _entityCertificateIDMap.value(certID); - if (entityItemID == id) { - challengeOwnershipTimeoutTimer->stop(); - challengeOwnershipTimeoutTimer->deleteLater(); - } - }); - connect(challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { - qCDebug(entities) << "Ownership challenge timed out, deleting entity" << entityItemID; - deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); - }); - challengeOwnershipTimeoutTimer->setInterval(10000); - challengeOwnershipTimeoutTimer->start(); + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityItemID)); + return; + } else { + startChallengeOwnershipTimer(entityItemID); + } } } else { qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 68f2c605af..210a35d407 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -367,6 +367,8 @@ protected: MovingEntitiesOperator _entityMover; QHash _entitiesToAdd; + + Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID); }; #endif // hifi_EntityTree_h From 1fa9bd8fc6c169454296e97aceb956a76deb5e1d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 6 Oct 2017 10:47:06 -0700 Subject: [PATCH 11/49] Better implementation of location change and bugfixes --- .../qml/hifi/commerce/checkout/Checkout.qml | 1 - .../hifi/commerce/purchases/PurchasedItem.qml | 1 - .../qml/hifi/commerce/wallet/Wallet.qml | 2 +- interface/src/Application.cpp | 6 +++ interface/src/Application.h | 1 + interface/src/commerce/Ledger.cpp | 24 ++++++++---- interface/src/commerce/QmlCommerce.cpp | 38 ++----------------- interface/src/commerce/QmlCommerce.h | 9 ----- interface/src/commerce/Wallet.cpp | 38 +++++++++++++++++++ interface/src/commerce/Wallet.h | 13 +++++++ .../scripting/WalletScriptingInterface.cpp | 5 +++ .../src/scripting/WalletScriptingInterface.h | 3 ++ .../entities/src/EntityEditPacketSender.cpp | 5 ++- .../entities/src/EntityEditPacketSender.h | 3 ++ libraries/entities/src/EntityTree.cpp | 33 ++++++++++------ libraries/entities/src/EntityTree.h | 1 + scripts/system/marketplaces/marketplaces.js | 11 ++++-- scripts/system/notifications.js | 9 ++++- 18 files changed, 131 insertions(+), 72 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index e7bbaec755..09c2f6fa76 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -579,7 +579,6 @@ Rectangle { } rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start(); - commerce.updatePopLocation(root.itemId); } } RalewaySemiBold { diff --git a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml index e343b93117..5eb5516519 100644 --- a/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml +++ b/interface/resources/qml/hifi/commerce/purchases/PurchasedItem.qml @@ -347,7 +347,6 @@ Item { if (urlHandler.canHandleUrl(root.itemHref)) { urlHandler.handleUrl(root.itemHref); } - commerce.updatePopLocation(root.itemId); rezzedNotifContainer.visible = true; rezzedNotifContainerTimer.start(); } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 9056d5bed3..0bc444efb5 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -283,7 +283,7 @@ Rectangle { Connections { onSendSignalToParent: { if (msg.method === "authSuccess") { - root.activeView = "walletHome"; + commerce.getWalletStatus(); } else { sendToScript(msg); } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0fc8c46cdc..6608cfe5fc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1006,6 +1006,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // connect to the packet sent signal of the _entityEditSender connect(&_entityEditSender, &EntityEditPacketSender::packetSent, this, &Application::packetSent); + connect(&_entityEditSender, &EntityEditPacketSender::addingEntityWithCertificate, this, &Application::addingEntityWithCertificate); const char** constArgv = const_cast(argv); QString concurrentDownloadsStr = getCmdOption(argc, constArgv, "--concurrent-downloads"); @@ -5735,6 +5736,11 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer void Application::packetSent(quint64 length) { } +void Application::addingEntityWithCertificate(const QString& certificateID, const QString& domainID) { + auto ledger = DependencyManager::get(); + ledger->updateLocation(certificateID, domainID); +} + void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointer scriptEngine) { scriptEngine->setEmitScriptUpdatesFunction([this]() { diff --git a/interface/src/Application.h b/interface/src/Application.h index 478642e2da..061fae5bcd 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -440,6 +440,7 @@ private slots: void nodeActivated(SharedNodePointer node); void nodeKilled(SharedNodePointer node); static void packetSent(quint64 length); + static void addingEntityWithCertificate(const QString& certificateID, const QString& domainID); void updateDisplayMode(); void domainConnectionRefused(const QString& reasonMessage, int reason, const QString& extraInfo); diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index e926d8918e..2f5e79f28b 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -225,12 +225,20 @@ void Ledger::updateLocationSuccess(QNetworkReply& reply) { apiResponse("updateLo void Ledger::updateLocationFailure(QNetworkReply& reply) { failResponse("updateLocation", reply); } void Ledger::updateLocation(const QString& asset_id, const QString location, const bool controlledFailure) { auto wallet = DependencyManager::get(); - QStringList keys = wallet->listPublicKeys(); - QString key = keys[0]; - QJsonObject transaction; - transaction["asset_id"] = asset_id; - transaction["location"] = location; - QJsonDocument transactionDoc{ transaction }; - auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); - signedSend("transaction", transactionString, key, "location", "updateLocationSuccess", "updateLocationFailure", controlledFailure); + auto walletScriptingInterface = DependencyManager::get(); + uint walletStatus = walletScriptingInterface->getWalletStatus(); + + if (walletStatus != wallet->WALLET_STATUS_READY) { + emit walletScriptingInterface->walletNotSetup(); + qDebug(commerce) << "User attempted to update the location of a certificate, but their wallet wasn't ready. Status:" << walletStatus; + } else { + QStringList keys = wallet->listPublicKeys(); + QString key = keys[0]; + QJsonObject transaction; + transaction["asset_id"] = asset_id; + transaction["location"] = location; + QJsonDocument transactionDoc{ transaction }; + auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); + signedSend("transaction", transactionString, key, "location", "updateLocationSuccess", "updateLocationFailure", controlledFailure); + } } diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 46c73708de..ee75bc59e3 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -15,7 +15,6 @@ #include "Ledger.h" #include "Wallet.h" #include -#include "scripting/WalletScriptingInterface.h" HIFI_QML_DEF(QmlCommerce) @@ -29,37 +28,12 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) { connect(ledger.data(), &Ledger::historyResult, this, &QmlCommerce::historyResult); connect(wallet.data(), &Wallet::keyFilePathIfExistsResult, this, &QmlCommerce::keyFilePathIfExistsResult); connect(ledger.data(), &Ledger::accountResult, this, &QmlCommerce::accountResult); - connect(ledger.data(), &Ledger::accountResult, this, [&]() { - auto wallet = DependencyManager::get(); - auto walletScriptingInterface = DependencyManager::get(); - uint status; - - if (wallet->getKeyFilePath() == "" || !wallet->getSecurityImage()) { - status = (uint)WalletStatus::WALLET_STATUS_NOT_SET_UP; - } else if (!wallet->walletIsAuthenticatedWithPassphrase()) { - status = (uint)WalletStatus::WALLET_STATUS_NOT_AUTHENTICATED; - } else { - status = (uint)WalletStatus::WALLET_STATUS_READY; - } - - walletScriptingInterface->setWalletStatus(status); - emit walletStatusResult(status); - }); + connect(wallet.data(), &Wallet::walletStatusResult, this, &QmlCommerce::walletStatusResult); } void QmlCommerce::getWalletStatus() { - auto walletScriptingInterface = DependencyManager::get(); - uint status; - - if (DependencyManager::get()->isLoggedIn()) { - // This will set account info for the wallet, allowing us to decrypt and display the security image. - account(); - } else { - status = (uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN; - emit walletStatusResult(status); - walletScriptingInterface->setWalletStatus(status); - return; - } + auto wallet = DependencyManager::get(); + wallet->getWalletStatus(); } void QmlCommerce::getLoginStatus() { @@ -151,9 +125,3 @@ void QmlCommerce::account() { auto ledger = DependencyManager::get(); ledger->account(); } - -void QmlCommerce::updatePopLocation(const QString& popId, const bool controlledFailure) { - auto ledger = DependencyManager::get(); - auto nodeList = DependencyManager::get(); - ledger->updateLocation(popId, nodeList->getDomainHandler().getUUID().toString(), controlledFailure); -} diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index b1d179aae8..45a5360680 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -27,13 +27,6 @@ class QmlCommerce : public OffscreenQmlDialog { public: QmlCommerce(QQuickItem* parent = nullptr); - enum WalletStatus { - WALLET_STATUS_NOT_LOGGED_IN = 0, - WALLET_STATUS_NOT_SET_UP, - WALLET_STATUS_NOT_AUTHENTICATED, - WALLET_STATUS_READY - }; - signals: void walletStatusResult(uint walletStatus); @@ -70,8 +63,6 @@ protected: Q_INVOKABLE void generateKeyPair(); Q_INVOKABLE void reset(); Q_INVOKABLE void account(); - - Q_INVOKABLE void updatePopLocation(const QString& popId, const bool controlledFailure = false); }; #endif // hifi_QmlCommerce_h diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 8b6f2785f1..34e266b32c 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -282,9 +282,27 @@ void initializeAESKeys(unsigned char* ivec, unsigned char* ckey, const QByteArra Wallet::Wallet() { auto nodeList = DependencyManager::get(); + auto ledger = DependencyManager::get(); auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket"); + + connect(ledger.data(), &Ledger::accountResult, this, [&]() { + auto wallet = DependencyManager::get(); + auto walletScriptingInterface = DependencyManager::get(); + uint status; + + if (wallet->getKeyFilePath() == "" || !wallet->getSecurityImage()) { + status = (uint)WalletStatus::WALLET_STATUS_NOT_SET_UP; + } else if (!wallet->walletIsAuthenticatedWithPassphrase()) { + status = (uint)WalletStatus::WALLET_STATUS_NOT_AUTHENTICATED; + } else { + status = (uint)WalletStatus::WALLET_STATUS_READY; + } + + walletScriptingInterface->setWalletStatus(status); + emit walletStatusResult(status); + }); } Wallet::~Wallet() { @@ -695,3 +713,23 @@ bool Wallet::verifyOwnerChallenge(const QByteArray& encryptedText, const QString decryptedText = QString("fail"); return true; } + +void Wallet::account() { + auto ledger = DependencyManager::get(); + ledger->account(); +} + +void Wallet::getWalletStatus() { + auto walletScriptingInterface = DependencyManager::get(); + uint status; + + if (DependencyManager::get()->isLoggedIn()) { + // This will set account info for the wallet, allowing us to decrypt and display the security image. + account(); + } else { + status = (uint)WalletStatus::WALLET_STATUS_NOT_LOGGED_IN; + emit walletStatusResult(status); + walletScriptingInterface->setWalletStatus(status); + return; + } +} diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index acf9f8e45e..38c5299810 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -17,6 +17,7 @@ #include #include #include +#include "scripting/WalletScriptingInterface.h" #include @@ -50,10 +51,20 @@ public: void reset(); + void getWalletStatus(); + enum WalletStatus { + WALLET_STATUS_NOT_LOGGED_IN = 0, + WALLET_STATUS_NOT_SET_UP, + WALLET_STATUS_NOT_AUTHENTICATED, + WALLET_STATUS_READY + }; + signals: void securityImageResult(bool exists); void keyFilePathIfExistsResult(const QString& path); + void walletStatusResult(uint walletStatus); + private slots: void handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode); @@ -71,6 +82,8 @@ private: bool readSecurityImage(const QString& inputFilePath, unsigned char** outputBufferPtr, int* outputBufferLen); bool verifyOwnerChallenge(const QByteArray& encryptedText, const QString& publicKey, QString& decryptedText); + + void account(); }; #endif // hifi_Wallet_h diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index 555e9477b0..99fdd5fbde 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -18,6 +18,11 @@ CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(q WalletScriptingInterface::WalletScriptingInterface() { } +void WalletScriptingInterface::refreshWalletStatus() { + auto wallet = DependencyManager::get(); + wallet->getWalletStatus(); +} + static const QString CHECKOUT_QML_PATH = qApp->applicationDirPath() + "../../../qml/hifi/commerce/checkout/Checkout.qml"; void WalletScriptingInterface::buy(const QString& name, const QString& id, const int& price, const QString& href) { if (QThread::currentThread() != thread()) { diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 31b42094cf..038c580197 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -20,6 +20,7 @@ #include #include #include "Application.h" +#include "commerce/Wallet.h" class CheckoutProxy : public QmlWrapper { Q_OBJECT @@ -36,6 +37,7 @@ class WalletScriptingInterface : public QObject, public Dependency { public: WalletScriptingInterface(); + Q_INVOKABLE void refreshWalletStatus(); Q_INVOKABLE uint getWalletStatus() { return _walletStatus; } void setWalletStatus(const uint& status) { _walletStatus = status; } @@ -43,6 +45,7 @@ public: signals: void walletStatusChanged(); + void walletNotSetup(); private: uint _walletStatus; diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index ee0fcf8218..2f8b796c93 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -93,9 +93,9 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0); bool success; + auto nodeList = DependencyManager::get(); if (properties.parentIDChanged() && properties.getParentID() == AVATAR_SELF_ID) { EntityItemProperties propertiesCopy = properties; - auto nodeList = DependencyManager::get(); const QUuid myNodeID = nodeList->getSessionUUID(); propertiesCopy.setParentID(myNodeID); success = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, propertiesCopy, bufferOut); @@ -110,6 +110,9 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, qCDebug(entities) << " properties:" << properties; #endif queueOctreeEditMessage(type, bufferOut); + if (type == PacketType::EntityAdd && !properties.getCertificateID().isEmpty()) { + emit addingEntityWithCertificate(properties.getCertificateID(), nodeList->getDomainHandler().getUUID().toString()); + } } } diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 81efaa865c..4e5b62e206 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -43,6 +43,9 @@ public: virtual char getMyNodeType() const override { return NodeType::EntityServer; } virtual void adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, qint64 clockSkew) override; +signals: + void addingEntityWithCertificate(const QString& certificateID, const QString& domainID); + public slots: void processEntityEditNackPacket(QSharedPointer message, SharedNodePointer sendingNode); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 7322378b5f..35c47e48c9 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1099,10 +1099,10 @@ 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) { + connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const QString& certID) { QReadLocker locker(&_entityCertificateIDMapLock); EntityItemID id = _entityCertificateIDMap.value(certID); - if (entityItemID == id) { + if (entityItemID == id && _challengeOwnershipTimeoutTimer) { _challengeOwnershipTimeoutTimer->stop(); _challengeOwnershipTimeoutTimer->deleteLater(); } @@ -1112,8 +1112,10 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) deleteEntity(entityItemID, true); QWriteLocker locker(&_recentlyDeletedEntitiesLock); _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); - _challengeOwnershipTimeoutTimer->stop(); - _challengeOwnershipTimeoutTimer->deleteLater(); + if (_challengeOwnershipTimeoutTimer) { + _challengeOwnershipTimeoutTimer->stop(); + _challengeOwnershipTimeoutTimer->deleteLater(); + } }); _challengeOwnershipTimeoutTimer->setInterval(5000); _challengeOwnershipTimeoutTimer->start(); @@ -1134,14 +1136,18 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const emit killChallengeOwnershipTimeoutTimer(certID); if (decryptedText == "fail") { - QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); - EntityItemID id = _entityCertificateIDMap.value(certID); - - qCDebug(entities) << "Ownership challenge failed, deleting entity" << id; - deleteEntity(id, true); - QWriteLocker recentlyDeletedLocker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), id); - } else { + EntityItemID id; + { + QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); + id = _entityCertificateIDMap.value(certID); + } + + if (!id.isNull()) { + qCDebug(entities) << "Ownership challenge failed, deleting entity" << id; + deleteEntity(id, true); + QWriteLocker recentlyDeletedLocker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), id); + } } } @@ -2173,3 +2179,6 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const { return entity->getJointNames(); } +void EntityTree::startDynamicDomainVerification() { + +} diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 210a35d407..85ce6a2ed5 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -369,6 +369,7 @@ protected: QHash _entitiesToAdd; Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID); + void startDynamicDomainVerification(); }; #endif // hifi_EntityTree_h diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index e94b227a4a..0bee540a9d 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -129,6 +129,10 @@ } } + function openWallet() { + tablet.pushOntoStack(MARKETPLACE_WALLET_QML_PATH); + } + function setCertificateInfo(currentEntityWithContextOverlay, itemMarketplaceId) { wireEventBridge(true); tablet.sendToQml({ @@ -158,6 +162,7 @@ Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); ContextOverlay.contextOverlayClicked.connect(setCertificateInfo); GlobalServices.myUsernameChanged.connect(onUsernameChanged); + Wallet.refreshWalletStatus(); function onMessage(message) { @@ -214,7 +219,7 @@ } else if (parsedJsonMessage.type === "LOGIN") { openLoginWindow(); } else if (parsedJsonMessage.type === "WALLET_SETUP") { - tablet.pushOntoStack(MARKETPLACE_WALLET_QML_PATH); + openWallet(); } else if (parsedJsonMessage.type === "MY_ITEMS") { referrerURL = MARKETPLACE_URL_INITIAL; filterText = ""; @@ -281,7 +286,7 @@ case 'purchases_openWallet': case 'checkout_openWallet': case 'checkout_setUpClicked': - tablet.pushOntoStack(MARKETPLACE_WALLET_QML_PATH); + openWallet(); break; case 'purchases_walletNotSetUp': case 'checkout_walletNotSetUp': @@ -290,7 +295,7 @@ method: 'updateWalletReferrer', referrer: "purchases" }); - tablet.pushOntoStack(MARKETPLACE_WALLET_QML_PATH); + openWallet(); break; case 'checkout_cancelClicked': tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.params, MARKETPLACES_INJECT_SCRIPT_URL); diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index ffe93d13e8..a23a390e9b 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -94,13 +94,15 @@ EDIT_ERROR: 4, TABLET: 5, CONNECTION: 6, + WALLET: 7, properties: [ { text: "Snapshot" }, { text: "Level of Detail" }, { text: "Connection Refused" }, { text: "Edit error" }, { text: "Tablet" }, - { text: "Connection" } + { text: "Connection" }, + { text: "Wallet" } ], getTypeFromMenuItem: function (menuItemName) { var type; @@ -574,6 +576,10 @@ createNotification(wordWrap("Error trying to make connection: " + error), NotificationType.CONNECTION); } + function walletNotSetup() { + createNotification("The action you performed requires you to set up your Wallet. Open the Wallet app.", NotificationType.SNAPSHOT); + } + // handles mouse clicks on buttons function mousePressEvent(event) { var pickRay, @@ -682,6 +688,7 @@ Window.notifyEditError = onEditError; Window.notify = onNotify; Tablet.tabletNotification.connect(tabletNotification); + Wallet.walletNotSetup.connect(walletNotSetup); setup(); }()); // END LOCAL_SCOPE From 59a6726af6a0153f7cb93e58aa3f4588fb6c06ce Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 6 Oct 2017 15:18:39 -0700 Subject: [PATCH 12/49] Checkpoint; lotsa changes... --- .../src/entities/EntityServer.cpp | 62 +++++- assignment-client/src/entities/EntityServer.h | 7 + .../qml/hifi/commerce/checkout/Checkout.qml | 2 +- .../qml/hifi/commerce/purchases/Purchases.qml | 10 +- .../qml/hifi/commerce/wallet/Wallet.qml | 2 + interface/src/commerce/Wallet.cpp | 2 +- libraries/entities/src/EntityTree.cpp | 182 +++++++++--------- libraries/entities/src/EntityTree.h | 6 +- scripts/system/commerce/wallet.js | 5 + scripts/system/html/js/marketplacesInject.js | 1 - scripts/system/marketplaces/marketplaces.js | 8 +- 11 files changed, 186 insertions(+), 101 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 1c1e02316a..445af3ac34 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -29,7 +29,8 @@ const char* LOCAL_MODELS_PERSIST_FILE = "resources/models.svo"; EntityServer::EntityServer(ReceivedMessage& message) : OctreeServer(message), - _entitySimulation(NULL) + _entitySimulation(NULL), + _dynamicDomainVerificationTimer(this) { DependencyManager::set(); DependencyManager::set(); @@ -38,6 +39,9 @@ EntityServer::EntityServer(ReceivedMessage& message) : auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityEdit, PacketType::EntityErase, PacketType::EntityPhysics, PacketType::ChallengeOwnership }, this, "handleEntityPacket"); + + connect(&_dynamicDomainVerificationTimer, &QTimer::timeout, this, &EntityServer::startDynamicDomainVerification); + _dynamicDomainVerificationTimer.setSingleShot(true); } EntityServer::~EntityServer() { @@ -93,6 +97,8 @@ void EntityServer::beforeRun() { connect(_pruneDeletedEntitiesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntities())); const int PRUNE_DELETED_MODELS_INTERVAL_MSECS = 1 * 1000; // once every second _pruneDeletedEntitiesTimer->start(PRUNE_DELETED_MODELS_INTERVAL_MSECS); + + startDynamicDomainVerification(); } void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) { @@ -410,3 +416,57 @@ QString EntityServer::serverSubclassStats() { return statsString; } + +void EntityServer::startDynamicDomainVerification() { + qCDebug(entities) << "Starting Dynamic Domain Verification..."; + + auto nodeList = DependencyManager::get(); + QString thisDomainID = nodeList->getDomainHandler().getUUID().toString(); + + EntityTreePointer tree = std::static_pointer_cast(_tree); + QHash localMap(tree->getEntityCertificateIDMap()); + + QHashIterator i(localMap); + qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap"; + while (i.hasNext()) { + i.next(); + + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status"); + QJsonObject request; + request["certificate_id"] = i.key(); + networkRequest.setUrl(requestURL); + + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.get(networkRequest); + + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + QJsonDocument doc(jsonObject); + qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); + + // ZRF FIXME!!! + //if (networkReply->error() == QNetworkReply::NoError) { + if (true) { + // ZRF FIXME!!! + //if (jsonObject["location"].toString() != thisDomainID) { + if (false) { + qCDebug(entities) << "invalid_reason not empty, deleting entity" << i.value(); + tree->deleteEntity(i.value(), true); + } + } else { + qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << i.value(); + tree->deleteEntity(i.value(), true); + } + + networkReply->deleteLater(); + }); + } + + 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"; + _dynamicDomainVerificationTimer.start(nextInterval); +} diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index 26c2f149aa..39d15f726e 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -80,6 +80,13 @@ private: QReadWriteLock _viewerSendingStatsLock; QMap> _viewerSendingStats; + + //static const int MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 45 * 60 * 1000; // 45m + //static const int MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 75 * 60 * 1000; // 1h15m + static const int MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 5 * 1000; // 5s + static const int MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 10 * 1000; // 10s + QTimer _dynamicDomainVerificationTimer; + void startDynamicDomainVerification(); }; #endif // hifi_EntityServer_h diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 09c2f6fa76..2ab8e90e9d 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -125,7 +125,7 @@ Rectangle { id: notSetUpTimer; interval: 200; onTriggered: { - sendToScript({method: 'checkout_walletNotSetUp'}); + sendToScript({method: 'checkout_walletNotSetUp', itemId: itemId}); } } diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index 990fd348c6..42708a80c5 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -78,6 +78,10 @@ Rectangle { onInventoryResult: { purchasesReceived = true; + if (root.pendingInventoryReply) { + inventoryTimer.start(); + } + if (result.status !== 'success') { console.log("Failed to get purchases", result.message); } else { @@ -98,10 +102,6 @@ Rectangle { previousPurchasesModel.append(inventoryResult); buildFilteredPurchasesModel(); - - if (root.pendingInventoryReply) { - inventoryTimer.start(); - } } root.pendingInventoryReply = false; @@ -112,7 +112,7 @@ Rectangle { id: notSetUpTimer; interval: 200; onTriggered: { - sendToScript({method: 'checkout_walletNotSetUp'}); + sendToScript({method: 'purchases_walletNotSetUp'}); } } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 0bc444efb5..9beadd3361 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -176,6 +176,8 @@ Rectangle { commerce.getWalletStatus(); } else if (msg.referrer === 'purchases') { sendToScript({method: 'goToPurchases'}); + } else { + sendToScript({method: 'goToMarketplaceItemPage', itemId: msg.referrer}); } } else if (msg.method === 'walletSetup_raiseKeyboard') { root.keyboardRaised = true; diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 34e266b32c..e80475f2cc 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -710,7 +710,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack bool Wallet::verifyOwnerChallenge(const QByteArray& encryptedText, const QString& publicKey, QString& decryptedText) { // I have no idea how to do this yet, so here's some dummy code that may not even work. - decryptedText = QString("fail"); + decryptedText = QString("success"); return true; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 35c47e48c9..8f75a4bdf1 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -147,63 +147,63 @@ int EntityTree::readEntityDataFromBuffer(const unsigned char* data, int bytesLef if (bytesLeftToRead >= (int)(numberOfEntities * expectedBytesPerEntity)) { for (uint16_t i = 0; i < numberOfEntities; i++) { int bytesForThisEntity = 0; - EntityItemID entityItemID = EntityItemID::readEntityItemIDFromBuffer(dataAt, bytesLeftToRead); - EntityItemPointer entity = findEntityByEntityItemID(entityItemID); +EntityItemID entityItemID = EntityItemID::readEntityItemIDFromBuffer(dataAt, bytesLeftToRead); +EntityItemPointer entity = findEntityByEntityItemID(entityItemID); - if (entity) { - QString entityScriptBefore = entity->getScript(); - QUuid parentIDBefore = entity->getParentID(); - QString entityServerScriptsBefore = entity->getServerScripts(); - quint64 entityScriptTimestampBefore = entity->getScriptTimestamp(); +if (entity) { + QString entityScriptBefore = entity->getScript(); + QUuid parentIDBefore = entity->getParentID(); + QString entityServerScriptsBefore = entity->getServerScripts(); + quint64 entityScriptTimestampBefore = entity->getScriptTimestamp(); - bytesForThisEntity = entity->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); - if (entity->getDirtyFlags()) { - entityChanged(entity); - } - _entityMover.addEntityToMoveList(entity, entity->getQueryAACube()); + bytesForThisEntity = entity->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); + if (entity->getDirtyFlags()) { + entityChanged(entity); + } + _entityMover.addEntityToMoveList(entity, entity->getQueryAACube()); - QString entityScriptAfter = entity->getScript(); - QString entityServerScriptsAfter = entity->getServerScripts(); - quint64 entityScriptTimestampAfter = entity->getScriptTimestamp(); - bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter; + QString entityScriptAfter = entity->getScript(); + QString entityServerScriptsAfter = entity->getServerScripts(); + quint64 entityScriptTimestampAfter = entity->getScriptTimestamp(); + bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter; - // If the script value has changed on us, or it's timestamp has changed to force - // a reload then we want to send out a script changing signal... - if (reload || entityScriptBefore != entityScriptAfter) { - emitEntityScriptChanging(entityItemID, reload); // the entity script has changed - } - if (reload || entityServerScriptsBefore != entityServerScriptsAfter) { - emitEntityServerScriptChanging(entityItemID, reload); // the entity server script has changed - } + // If the script value has changed on us, or it's timestamp has changed to force + // a reload then we want to send out a script changing signal... + if (reload || entityScriptBefore != entityScriptAfter) { + emitEntityScriptChanging(entityItemID, reload); // the entity script has changed + } + if (reload || entityServerScriptsBefore != entityServerScriptsAfter) { + emitEntityServerScriptChanging(entityItemID, reload); // the entity server script has changed + } - QUuid parentIDAfter = entity->getParentID(); - if (parentIDBefore != parentIDAfter) { - addToNeedsParentFixupList(entity); - } - } else { - entity = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args); - if (entity) { - bytesForThisEntity = entity->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); + QUuid parentIDAfter = entity->getParentID(); + if (parentIDBefore != parentIDAfter) { + addToNeedsParentFixupList(entity); + } +} else { + entity = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args); + if (entity) { + bytesForThisEntity = entity->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); - // don't add if we've recently deleted.... - if (!isDeletedEntity(entityItemID)) { - _entitiesToAdd.insert(entityItemID, entity); + // don't add if we've recently deleted.... + if (!isDeletedEntity(entityItemID)) { + _entitiesToAdd.insert(entityItemID, entity); - if (entity->getCreated() == UNKNOWN_CREATED_TIME) { - entity->recordCreationTime(); - } - #ifdef WANT_DEBUG - } else { - qCDebug(entities) << "Received packet for previously deleted entity [" << - entityItemID << "] ignoring. (inside " << __FUNCTION__ << ")"; - #endif - } - } - } - // Move the buffer forward to read more entities - dataAt += bytesForThisEntity; - bytesLeftToRead -= bytesForThisEntity; - bytesRead += bytesForThisEntity; + if (entity->getCreated() == UNKNOWN_CREATED_TIME) { + entity->recordCreationTime(); + } +#ifdef WANT_DEBUG + } else { + qCDebug(entities) << "Received packet for previously deleted entity [" << + entityItemID << "] ignoring. (inside " << __FUNCTION__ << ")"; +#endif + } + } +} +// Move the buffer forward to read more entities +dataAt += bytesForThisEntity; +bytesLeftToRead -= bytesForThisEntity; +bytesRead += bytesForThisEntity; } } } @@ -214,13 +214,13 @@ int EntityTree::readEntityDataFromBuffer(const unsigned char* data, int bytesLef bool EntityTree::handlesEditPacketType(PacketType packetType) const { // we handle these types of "edit" packets switch (packetType) { - case PacketType::EntityAdd: - case PacketType::EntityEdit: - case PacketType::EntityErase: - case PacketType::EntityPhysics: - return true; - default: - return false; + case PacketType::EntityAdd: + case PacketType::EntityEdit: + case PacketType::EntityErase: + case PacketType::EntityPhysics: + return true; + default: + return false; } } @@ -241,6 +241,29 @@ void EntityTree::postAddEntity(EntityItemPointer entity) { // find and hook up any entities with this entity as a (previously) missing parent fixupNeedsParentFixups(); + + 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()) { + qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" + << existingEntityItemID << ". Deleting existing entity."; + deleteEntity(existingEntityItemID, true); + } + } } bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) { @@ -654,8 +677,16 @@ 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); + } + } + // set up the deleted entities ID - QWriteLocker locker(&_recentlyDeletedEntitiesLock); + QWriteLocker recentlyDeletedEntitiesLocker(&_recentlyDeletedEntitiesLock); _recentlyDeletedEntityItemIDs.insert(deletedAt, theEntity->getEntityItemID()); } else { // on the client side, we also remember that we deleted this entity, we don't care about the time @@ -665,9 +696,6 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator) if (_simulation) { _simulation->prepareEntityForDelete(theEntity); } - - QWriteLocker locker(&_entityCertificateIDMapLock); - _entityCertificateIDMap.remove(theEntity->getProperties(PROP_CERTIFICATE_ID).getCertificateID()); } } @@ -1117,8 +1145,8 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) _challengeOwnershipTimeoutTimer->deleteLater(); } }); - _challengeOwnershipTimeoutTimer->setInterval(5000); - _challengeOwnershipTimeoutTimer->start(); + _challengeOwnershipTimeoutTimer->setSingleShot(true); + _challengeOwnershipTimeoutTimer->start(5000); } void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { @@ -1346,7 +1374,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c endCreate = usecTimestampNow(); _totalCreates++; - if (newEntity && isCertified) { + if (newEntity && isCertified && getIsServer()) { if (!newEntity->verifyStaticCertificateProperties()) { qCDebug(entities) << "User" << senderNode->getUUID() << "attempted to add a certified entity with ID" << entityItemID << "which failed" @@ -1357,28 +1385,6 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else { QString certID = properties.getCertificateID(); - EntityItemID existingEntityItemID; - - { - QReadLocker locker(&_entityCertificateIDMapLock); - existingEntityItemID = _entityCertificateIDMap.value(certID); - } - - // 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()) { - qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" - << existingEntityItemID << ". Deleting existing entity."; - deleteEntity(existingEntityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), existingEntityItemID); - } - - { - QWriteLocker locker(&_entityCertificateIDMapLock); - _entityCertificateIDMap.insert(certID, entityItemID); - qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID; - } // Start owner verification. auto nodeList = DependencyManager::get(); @@ -2178,7 +2184,3 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const { } return entity->getJointNames(); } - -void EntityTree::startDynamicDomainVerification() { - -} diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 85ce6a2ed5..d6a35ca2aa 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -187,6 +187,11 @@ public: return _recentlyDeletedEntityItemIDs; } + QHash getEntityCertificateIDMap() const { + QReadLocker locker(&_entityCertificateIDMapLock); + return _entityCertificateIDMap; + } + void forgetEntitiesDeletedBefore(quint64 sinceTime); int processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode); @@ -369,7 +374,6 @@ protected: QHash _entitiesToAdd; Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID); - void startDynamicDomainVerification(); }; #endif // hifi_EntityTree_h diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 7553ca4eeb..04b67ec14f 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -16,6 +16,8 @@ (function () { // BEGIN LOCAL_SCOPE Script.include("/~/system/libraries/accountUtils.js"); + var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; + // Function Name: onButtonClicked() // // Description: @@ -88,6 +90,9 @@ case 'goToPurchases': tablet.pushOntoStack(MARKETPLACE_PURCHASES_QML_PATH); break; + case 'goToMarketplaceItemPage': + tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.itemId, MARKETPLACES_INJECT_SCRIPT_URL); + break; default: print('Unrecognized message from QML:', JSON.stringify(message)); } diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js index ded4542c51..41db724c3f 100644 --- a/scripts/system/html/js/marketplacesInject.js +++ b/scripts/system/html/js/marketplacesInject.js @@ -334,7 +334,6 @@ $('body').addClass("code-injected"); maybeAddLogInButton(); - maybeAddSetupWalletButton(); changeDropdownMenu(); var purchaseButton = $('#side-info').find('.btn').first(); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 0bee540a9d..deeae0d299 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -289,13 +289,19 @@ openWallet(); break; case 'purchases_walletNotSetUp': - case 'checkout_walletNotSetUp': wireEventBridge(true); tablet.sendToQml({ method: 'updateWalletReferrer', referrer: "purchases" }); openWallet(); + case 'checkout_walletNotSetUp': + wireEventBridge(true); + tablet.sendToQml({ + method: 'updateWalletReferrer', + referrer: message.itemId + }); + openWallet(); break; case 'checkout_cancelClicked': tablet.gotoWebScreen(MARKETPLACE_URL + '/items/' + message.params, MARKETPLACES_INJECT_SCRIPT_URL); From c70ee6055ffc5de2b4f7d711c5f200ffeecfbbdf Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 6 Oct 2017 17:12:40 -0700 Subject: [PATCH 13/49] Don't clear cert ID if static properties change; updates to DDV --- .../src/entities/EntityServer.cpp | 60 +++++++++++-------- libraries/entities/src/EntityTree.cpp | 12 ---- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 445af3ac34..0342f4acda 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -431,39 +431,47 @@ void EntityServer::startDynamicDomainVerification() { while (i.hasNext()) { i.next(); - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest; - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; - requestURL.setPath("/api/v1/commerce/proof_of_purchase_status"); - QJsonObject request; - request["certificate_id"] = i.key(); - networkRequest.setUrl(requestURL); + if (!tree->findEntityByEntityItemID(i.value())->verifyStaticCertificateProperties()) { + qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed" + << "static certificate verification."; + // Delete the entity if it doesn't pass static certificate verification + tree->deleteEntity(i.value(), true); + } else { - QNetworkReply* networkReply = NULL; - networkReply = networkAccessManager.get(networkRequest); + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status"); + QJsonObject request; + request["certificate_id"] = i.key(); + networkRequest.setUrl(requestURL); - connect(networkReply, &QNetworkReply::finished, [=]() { - QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - QJsonDocument doc(jsonObject); - qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.get(networkRequest); + + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + QJsonDocument doc(jsonObject); + qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); - // ZRF FIXME!!! - //if (networkReply->error() == QNetworkReply::NoError) { - if (true) { // ZRF FIXME!!! - //if (jsonObject["location"].toString() != thisDomainID) { - if (false) { - qCDebug(entities) << "invalid_reason not empty, deleting entity" << i.value(); + //if (networkReply->error() == QNetworkReply::NoError) { + if (true) { + // ZRF FIXME!!! + //if (jsonObject["location"].toString() != thisDomainID) { + if (false) { + qCDebug(entities) << "invalid_reason not empty, deleting entity" << i.value(); + tree->deleteEntity(i.value(), true); + } + } else { + qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << i.value(); tree->deleteEntity(i.value(), true); } - } else { - qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << i.value(); - tree->deleteEntity(i.value(), true); - } - networkReply->deleteLater(); - }); + networkReply->deleteLater(); + }); + } } 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/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 8f75a4bdf1..56a92fb20c 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -382,18 +382,6 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti qCDebug(entities) << (senderNode ? senderNode->getUUID() : "null") << "physical edits suppressed"; } } - - // Clear Certificate ID if any static certificate property is changed - if (properties.itemNameChanged() || properties.itemDescriptionChanged() || properties.itemCategoriesChanged() || - properties.itemArtistChanged() || properties.itemLicenseChanged() || properties.limitedRunChanged() || - properties.editionNumberChanged() || properties.entityInstanceNumberChanged() || properties.certificateIDChanged()) { - qCDebug(entities) << "A static certificate property on Entity" << entity->getID() << "has changed." - << "Clearing Certificate ID."; - QWriteLocker locker(&_entityCertificateIDMapLock); - _entityCertificateIDMap.remove(entity->getCertificateID()); - properties.setCertificateID(""); - properties.setCertificateIDChanged(true); - } } // else client accepts what the server says From 8d887caa5d00bab566d60a521366b6fe3715a456 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 9 Oct 2017 11:37:02 -0700 Subject: [PATCH 14/49] Make min and max dynamic verification times server settings --- .../src/entities/EntityServer.cpp | 26 +++++++++++++++++-- assignment-client/src/entities/EntityServer.h | 9 ++++--- .../resources/describe-settings.json | 16 ++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 0342f4acda..f275c5c44a 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -98,7 +98,8 @@ void EntityServer::beforeRun() { const int PRUNE_DELETED_MODELS_INTERVAL_MSECS = 1 * 1000; // once every second _pruneDeletedEntitiesTimer->start(PRUNE_DELETED_MODELS_INTERVAL_MSECS); - startDynamicDomainVerification(); + DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); + connect(&domainHandler, &DomainHandler::settingsReceiveFail, this, &EntityServer::domainSettingsRequestFailed); } void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) { @@ -302,6 +303,18 @@ void EntityServer::readAdditionalConfiguration(const QJsonObject& settingsSectio tree->setEntityMaxTmpLifetime(EntityTree::DEFAULT_MAX_TMP_ENTITY_LIFETIME); } + int minTime; + if (readOptionInt("dynamicDomainVerificationTimeMin", settingsSectionObject, minTime)) { + _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = minTime * 1000; + } + + int maxTime; + if (readOptionInt("dynamicDomainVerificationTimeMax", settingsSectionObject, maxTime)) { + _MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = maxTime * 1000; + } + + startDynamicDomainVerification(); + tree->setWantEditLogging(wantEditLogging); tree->setWantTerseEditLogging(wantTerseEditLogging); @@ -417,6 +430,15 @@ QString EntityServer::serverSubclassStats() { return statsString; } +void EntityServer::domainSettingsRequestFailed() { + auto nodeList = DependencyManager::get(); + qCDebug(entities) << "The EntityServer couldn't get the Domain Settings. Starting dynamic domain verification with default values..."; + + _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = DEFAULT_MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; + _MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = DEFAULT_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; + startDynamicDomainVerification(); +} + void EntityServer::startDynamicDomainVerification() { qCDebug(entities) << "Starting Dynamic Domain Verification..."; @@ -474,7 +496,7 @@ void EntityServer::startDynamicDomainVerification() { } } - int nextInterval = qrand() % ((MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS + 1) - MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS) + MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; + 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"; _dynamicDomainVerificationTimer.start(nextInterval); } diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index 39d15f726e..5cebd9b751 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -73,6 +73,7 @@ protected: private slots: void handleEntityPacket(QSharedPointer message, SharedNodePointer senderNode); + void domainSettingsRequestFailed(); private: SimpleEntitySimulationPointer _entitySimulation; @@ -81,10 +82,10 @@ private: QReadWriteLock _viewerSendingStatsLock; QMap> _viewerSendingStats; - //static const int MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 45 * 60 * 1000; // 45m - //static const int MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 75 * 60 * 1000; // 1h15m - static const int MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 5 * 1000; // 5s - static const int MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 10 * 1000; // 10s + static const int DEFAULT_MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 45 * 60 * 1000; // 45m + static const int DEFAULT_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 75 * 60 * 1000; // 1h15m + int _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = DEFAULT_MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; // 45m + int _MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = DEFAULT_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; // 1h15m QTimer _dynamicDomainVerificationTimer; void startDynamicDomainVerification(); }; diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index f1180e00c2..2b0d032e3c 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1254,6 +1254,22 @@ "default": "3600", "advanced": true }, + { + "name": "dynamicDomainVerificationTimeMin", + "label": "Dynamic Domain Verification Time (seconds) - Minimum", + "help": "The lower limit on the amount of time that passes before Dynamic Domain Verification on entities occurs. Units are seconds.", + "placeholder": "2700", + "default": "2700", + "advanced": true + }, + { + "name": "dynamicDomainVerificationTimeMax", + "label": "Dynamic Domain Verification Time (seconds) - Maximum", + "help": "The upper limit on the amount of time that passes before Dynamic Domain Verification on entities occurs. Units are seconds.", + "placeholder": "4500", + "default": "4500", + "advanced": true + }, { "name": "entityScriptSourceWhitelist", "label": "Entity Scripts Allowed from:", From 1f8b68c952790b6b4f38ffee167d055a0f91e1b6 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 11 Oct 2017 11:46:35 -0700 Subject: [PATCH 15/49] Updates now that endpoint exists; add pending timer --- libraries/entities/src/EntityTree.cpp | 186 ++++++++++++++------------ libraries/entities/src/EntityTree.h | 4 + 2 files changed, 108 insertions(+), 82 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b8293a117d..7960322383 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1143,6 +1143,109 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) _challengeOwnershipTimeoutTimer->start(5000); } +void EntityTree::startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode) { + qCDebug(entities) << "'transfer_status' is 'pending', checking again in 10 seconds..." << entityItemID; + QTimer* transferStatusRetryTimer = new QTimer(this); + connect(transferStatusRetryTimer, &QTimer::timeout, this, [=]() { + validatePop(certID, entityItemID, senderNode, true); + }); + transferStatusRetryTimer->setSingleShot(true); + transferStatusRetryTimer->start(10000); +} + +void EntityTree::validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation) { + // Start owner verification. + auto nodeList = DependencyManager::get(); + // First, asynchronously hit "proof_of_purchase_status?transaction_type=transfer" endpoint. + 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/transfer"); + QJsonObject request; + request["certificate_id"] = certID; + networkRequest.setUrl(requestURL); + + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); + + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + QJsonDocument doc(jsonObject); + qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); + + if (networkReply->error() == QNetworkReply::NoError) { + if (!jsonObject["invalid_reason"].toString().isEmpty()) { + qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; + deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + } else if (jsonObject["transfer_status"].toString() == "failed") { + qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; + deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + } else if (jsonObject["transfer_status"].toString() == "pending") { + if (isRetryingValidation) { + qCDebug(entities) << "'transfer_status' is 'pending' after retry, deleting entity" << entityItemID; + deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + } else { + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "startPendingTransferStatusTimer", + Q_ARG(const QString&, certID), + Q_ARG(const EntityItemID&, entityItemID), + Q_ARG(const SharedNodePointer&, senderNode)); + return; + } else { + startPendingTransferStatusTimer(certID, entityItemID, senderNode); + } + } + } else { + // Second, challenge ownership of the PoP cert + // 1. Encrypt a nonce with the owner's public key + QString ownerKey(jsonObject["owner_public_key"].toString()); + QString encryptedText("test"); + + // 2. Send the encrypted text to the rezzing avatar's node + QByteArray certIDByteArray = certID.toUtf8(); + int certIDByteArraySize = certIDByteArray.size(); + QByteArray encryptedTextByteArray = encryptedText.toUtf8(); + int encryptedTextByteArraySize = encryptedTextByteArray.size(); + QByteArray ownerKeyByteArray = ownerKey.toUtf8(); + int ownerKeyByteArraySize = ownerKeyByteArray.size(); + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, + certIDByteArraySize + encryptedTextByteArraySize + ownerKeyByteArraySize + 3 * sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); + challengeOwnershipPacket->writePrimitive(ownerKeyByteArraySize); + challengeOwnershipPacket->write(certIDByteArray); + challengeOwnershipPacket->write(encryptedTextByteArray); + challengeOwnershipPacket->write(ownerKeyByteArray); + nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode); + + // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityItemID)); + return; + } else { + startChallengeOwnershipTimer(entityItemID); + } + } + } else { + qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; + deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + } + + networkReply->deleteLater(); + }); +} + void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { int certIDByteArraySize; int decryptedTextByteArraySize; @@ -1378,88 +1481,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c QWriteLocker locker(&_recentlyDeletedEntitiesLock); _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else { - QString certID = properties.getCertificateID(); - - // Start owner verification. - auto nodeList = DependencyManager::get(); - // First, asynchronously hit "proof_of_purchase_status?transaction_type=transfer" endpoint. - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest; - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; - requestURL.setPath("/api/v1/commerce/proof_of_purchase_status?transaction_type=transfer"); - QJsonObject request; - request["certificate_id"] = certID; - request["domain_id"] = nodeList->getDomainHandler().getUUID().toString(); - networkRequest.setUrl(requestURL); - - QNetworkReply* networkReply = NULL; - networkReply = networkAccessManager.get(networkRequest); - - connect(networkReply, &QNetworkReply::finished, [=]() { - QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - QJsonDocument doc(jsonObject); - qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); - - // ZRF FIXME!!! - //if (networkReply->error() == QNetworkReply::NoError) { - if (true) { - // ZRF FIXME!!! - //if (!jsonObject["invalid_reason"].toString().isEmpty()) { - if (false) { - qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; - deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); - // ZRF FIXME!!! - //} else if (jsonObject["transfer_status"].toString() == "failed") { - } else if (false) { - qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; - deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); - // ZRF FIXME!!! - } else { - // Second, challenge ownership of the PoP cert - // 1. Encrypt a nonce with the owner's public key - QString ownerKey(jsonObject["owner_public_key"].toString()); - QString encryptedText("test"); - - // 2. Send the encrypted text to the rezzing avatar's node - QByteArray certIDByteArray = certID.toUtf8(); - int certIDByteArraySize = certIDByteArray.size(); - QByteArray encryptedTextByteArray = encryptedText.toUtf8(); - int encryptedTextByteArraySize = encryptedTextByteArray.size(); - QByteArray ownerKeyByteArray = ownerKey.toUtf8(); - int ownerKeyByteArraySize = ownerKeyByteArray.size(); - auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, - certIDByteArraySize + encryptedTextByteArraySize + ownerKeyByteArraySize + 3*sizeof(int), - true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); - challengeOwnershipPacket->writePrimitive(ownerKeyByteArraySize); - challengeOwnershipPacket->write(certIDByteArray); - challengeOwnershipPacket->write(encryptedTextByteArray); - challengeOwnershipPacket->write(ownerKeyByteArray); - nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode); - - // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityItemID)); - return; - } else { - startChallengeOwnershipTimer(entityItemID); - } - } - } else { - qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; - deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); - } - - networkReply->deleteLater(); - }); + validatePop(properties.getCertificateID(), entityItemID, senderNode, false); } } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 693c8c5b56..4a773f7a15 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -379,6 +379,10 @@ protected: QHash _entitiesToAdd; Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID); + Q_INVOKABLE void startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); + +private: + void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); }; #endif // hifi_EntityTree_h From f05c70969333e577c5b2dc3502e54d869ac8a766 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 11 Oct 2017 12:06:11 -0700 Subject: [PATCH 16/49] Remove static certificate verification for now --- assignment-client/src/entities/EntityServer.cpp | 4 +++- libraries/entities/src/EntityTree.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index f275c5c44a..539a082911 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -453,7 +453,9 @@ void EntityServer::startDynamicDomainVerification() { while (i.hasNext()) { i.next(); - if (!tree->findEntityByEntityItemID(i.value())->verifyStaticCertificateProperties()) { + // ZRF FIXME!!! + //if (!tree->findEntityByEntityItemID(i.value())->verifyStaticCertificateProperties()) { + if (false) { qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed" << "static certificate verification."; // Delete the entity if it doesn't pass static certificate verification diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 7960322383..a479c04459 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1472,7 +1472,9 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c _totalCreates++; if (newEntity && isCertified && getIsServer()) { - if (!newEntity->verifyStaticCertificateProperties()) { + // ZRF FIXME!!! + //if (!newEntity->verifyStaticCertificateProperties()) { + if (false) { qCDebug(entities) << "User" << senderNode->getUUID() << "attempted to add a certified entity with ID" << entityItemID << "which failed" << "static certificate verification."; From 9e570fdba3c76b0eda6ff91758afed890d47e648 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 11 Oct 2017 13:05:46 -0700 Subject: [PATCH 17/49] First pass at encryption/decryption. Exciting! --- interface/src/commerce/Wallet.cpp | 21 +++--- interface/src/commerce/Wallet.h | 2 - libraries/entities/src/EntityTree.cpp | 102 +++++++++++++++++++------- libraries/entities/src/EntityTree.h | 5 ++ 4 files changed, 91 insertions(+), 39 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index dd926d00d4..702a94625a 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -718,15 +718,22 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack QString decryptedText; int certIDByteArraySize; int encryptedTextByteArraySize; - int ownerKeyByteArraySize; packet->readPrimitive(&certIDByteArraySize); packet->readPrimitive(&encryptedTextByteArraySize); - packet->readPrimitive(&ownerKeyByteArraySize); QByteArray certID = packet->read(certIDByteArraySize); + QByteArray encryptedText = packet->read(encryptedTextByteArraySize); - if (verifyOwnerChallenge(packet->read(encryptedTextByteArraySize), packet->read(ownerKeyByteArraySize), decryptedText)) { + const auto text = reinterpret_cast(encryptedText.constData()); + const unsigned int textLength = encryptedText.length(); + + RSA* rsa = readKeys(keyFilePath().toStdString().c_str()); + + const int decryptionStatus = RSA_private_decrypt(textLength, text, reinterpret_cast(encryptedText.data()), rsa, RSA_PKCS1_OAEP_PADDING); + RSA_free(rsa); + + if (decryptionStatus != -1) { auto nodeList = DependencyManager::get(); QByteArray decryptedTextByteArray = decryptedText.toUtf8(); @@ -744,16 +751,10 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); } else { - qCDebug(commerce) << "verifyOwnerChallenge() returned false"; + qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed."; } } -bool Wallet::verifyOwnerChallenge(const QByteArray& encryptedText, const QString& publicKey, QString& decryptedText) { - // I have no idea how to do this yet, so here's some dummy code that may not even work. - decryptedText = QString("success"); - return true; -} - void Wallet::account() { auto ledger = DependencyManager::get(); ledger->account(); diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 38c5299810..16d23c1e5b 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -81,8 +81,6 @@ private: bool writeSecurityImage(const QPixmap* pixmap, const QString& outputFilePath); bool readSecurityImage(const QString& inputFilePath, unsigned char** outputBufferPtr, int* outputBufferLen); - bool verifyOwnerChallenge(const QByteArray& encryptedText, const QString& publicKey, QString& decryptedText); - void account(); }; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index a479c04459..2a8e3e575a 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -13,6 +13,10 @@ #include #include +#include +#include +#include + #include #include @@ -1144,13 +1148,49 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) } void EntityTree::startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode) { - qCDebug(entities) << "'transfer_status' is 'pending', checking again in 10 seconds..." << entityItemID; + qCDebug(entities) << "'transfer_status' is 'pending', checking again in 90 seconds..." << entityItemID; QTimer* transferStatusRetryTimer = new QTimer(this); connect(transferStatusRetryTimer, &QTimer::timeout, this, [=]() { validatePop(certID, entityItemID, senderNode, true); }); transferStatusRetryTimer->setSingleShot(true); - transferStatusRetryTimer->start(10000); + transferStatusRetryTimer->start(90000); +} + +QString EntityTree::computeEncryptedNonce(const QString& certID, const QString& ownerKey) { + QUuid nonce = QUuid::createUuid(); + const auto text = reinterpret_cast(qPrintable(nonce.toString())); + const unsigned int textLength = nonce.toString().length(); + + const auto publicKey = reinterpret_cast(ownerKey.toUtf8().toBase64().constData()); + BIO* bio = BIO_new_mem_buf((void*)publicKey, sizeof(publicKey)); + RSA* rsa = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL); + + QByteArray encryptedText(RSA_size(rsa), 0); + const int encryptStatus = RSA_public_encrypt(textLength, text, reinterpret_cast(encryptedText.data()), rsa, RSA_PKCS1_OAEP_PADDING); + BIO_free(bio); + RSA_free(rsa); + if (encryptStatus == -1) { + qCWarning(entities) << "Unable to compute encrypted nonce for" << certID; + return ""; + } + + { + QWriteLocker locker(&_certNonceMapLock); + _certNonceMap.insert(certID, nonce); + } + + return encryptedText.toBase64(); +} + +bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce) { + QString actualNonce; + { + QWriteLocker locker(&_certNonceMapLock); + actualNonce = _certNonceMap.take(certID).toString(); + } + + return actualNonce == decryptedNonce; } void EntityTree::validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation) { @@ -1172,6 +1212,9 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt connect(networkReply, &QNetworkReply::finished, [=]() { QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); + + // ZRF FIXME Remove these two lines QJsonDocument doc(jsonObject); qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); @@ -1207,32 +1250,37 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt // Second, challenge ownership of the PoP cert // 1. Encrypt a nonce with the owner's public key QString ownerKey(jsonObject["owner_public_key"].toString()); - QString encryptedText("test"); + QString encryptedText = computeEncryptedNonce(certID, ownerKey); - // 2. Send the encrypted text to the rezzing avatar's node - QByteArray certIDByteArray = certID.toUtf8(); - int certIDByteArraySize = certIDByteArray.size(); - QByteArray encryptedTextByteArray = encryptedText.toUtf8(); - int encryptedTextByteArraySize = encryptedTextByteArray.size(); - QByteArray ownerKeyByteArray = ownerKey.toUtf8(); - int ownerKeyByteArraySize = ownerKeyByteArray.size(); - auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, - certIDByteArraySize + encryptedTextByteArraySize + ownerKeyByteArraySize + 3 * sizeof(int), - true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); - challengeOwnershipPacket->writePrimitive(ownerKeyByteArraySize); - challengeOwnershipPacket->write(certIDByteArray); - challengeOwnershipPacket->write(encryptedTextByteArray); - challengeOwnershipPacket->write(ownerKeyByteArray); - nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode); - - // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityItemID)); - return; + if (encryptedText == "") { + qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce. Deleting entity..."; + deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else { - startChallengeOwnershipTimer(entityItemID); + // 2. Send the encrypted text to the rezzing avatar's node + QByteArray certIDByteArray = certID.toUtf8(); + int certIDByteArraySize = certIDByteArray.size(); + QByteArray encryptedTextByteArray = encryptedText.toUtf8(); + int encryptedTextByteArraySize = encryptedTextByteArray.size(); + QByteArray ownerKeyByteArray = ownerKey.toUtf8(); + int ownerKeyByteArraySize = ownerKeyByteArray.size(); + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, + certIDByteArraySize + encryptedTextByteArraySize + 2 * sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); + challengeOwnershipPacket->write(certIDByteArray); + challengeOwnershipPacket->write(encryptedTextByteArray); + nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode); + + // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityItemID)); + return; + } else { + startChallengeOwnershipTimer(entityItemID); + } } } } else { @@ -1260,7 +1308,7 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const emit killChallengeOwnershipTimeoutTimer(certID); - if (decryptedText == "fail") { + if (!verifyDecryptedNonce(certID, decryptedText)) { EntityItemID id; { QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 4a773f7a15..fc6a913ffe 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -336,6 +336,9 @@ protected: mutable QReadWriteLock _entityCertificateIDMapLock; QHash _entityCertificateIDMap; + mutable QReadWriteLock _certNonceMapLock; + QHash _certNonceMap; + EntitySimulationPointer _simulation; bool _wantEditLogging = false; @@ -382,6 +385,8 @@ protected: Q_INVOKABLE void startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); private: + QString computeEncryptedNonce(const QString& certID, const QString& ownerKey); + bool verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); }; From 1aaf1a19cdba36c972746c6ab2756cf4004193df Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 11 Oct 2017 13:20:17 -0700 Subject: [PATCH 18/49] Quick fix for printing --- assignment-client/src/entities/EntityServer.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 539a082911..4a4315dd34 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -465,17 +465,21 @@ void EntityServer::startDynamicDomainVerification() { 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"); + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); QJsonObject request; request["certificate_id"] = i.key(); networkRequest.setUrl(requestURL); QNetworkReply* networkReply = NULL; - networkReply = networkAccessManager.get(networkRequest); + networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); connect(networkReply, &QNetworkReply::finished, [=]() { QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); + + // ZRF FIXME Remove these two lines QJsonDocument doc(jsonObject); qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); From d8b84e687533150f6d1f6e5af93e57ebbfca725c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 11 Oct 2017 13:26:32 -0700 Subject: [PATCH 19/49] Minor cleanup --- assignment-client/src/entities/EntityServer.cpp | 4 +--- libraries/entities/src/EntityTree.cpp | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 4a4315dd34..1dc1eef593 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -483,9 +483,7 @@ void EntityServer::startDynamicDomainVerification() { QJsonDocument doc(jsonObject); qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); - // ZRF FIXME!!! - //if (networkReply->error() == QNetworkReply::NoError) { - if (true) { + if (networkReply->error() == QNetworkReply::NoError) { // ZRF FIXME!!! //if (jsonObject["location"].toString() != thisDomainID) { if (false) { diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 2a8e3e575a..b4eef7ebcc 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1249,7 +1249,7 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt } else { // Second, challenge ownership of the PoP cert // 1. Encrypt a nonce with the owner's public key - QString ownerKey(jsonObject["owner_public_key"].toString()); + QString ownerKey(jsonObject["transfer_recipient_key"].toString()); QString encryptedText = computeEncryptedNonce(certID, ownerKey); if (encryptedText == "") { From fa1bfe0d19597de88392cdcb02d11059772a109e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 11 Oct 2017 14:10:33 -0700 Subject: [PATCH 20/49] Maybe fixes? --- libraries/entities/src/EntityTree.cpp | 145 +++++++++++++------------- 1 file changed, 74 insertions(+), 71 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index b4eef7ebcc..a68e36b7ed 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -151,63 +151,63 @@ int EntityTree::readEntityDataFromBuffer(const unsigned char* data, int bytesLef if (bytesLeftToRead >= (int)(numberOfEntities * expectedBytesPerEntity)) { for (uint16_t i = 0; i < numberOfEntities; i++) { int bytesForThisEntity = 0; -EntityItemID entityItemID = EntityItemID::readEntityItemIDFromBuffer(dataAt, bytesLeftToRead); -EntityItemPointer entity = findEntityByEntityItemID(entityItemID); + EntityItemID entityItemID = EntityItemID::readEntityItemIDFromBuffer(dataAt, bytesLeftToRead); + EntityItemPointer entity = findEntityByEntityItemID(entityItemID); -if (entity) { - QString entityScriptBefore = entity->getScript(); - QUuid parentIDBefore = entity->getParentID(); - QString entityServerScriptsBefore = entity->getServerScripts(); - quint64 entityScriptTimestampBefore = entity->getScriptTimestamp(); + if (entity) { + QString entityScriptBefore = entity->getScript(); + QUuid parentIDBefore = entity->getParentID(); + QString entityServerScriptsBefore = entity->getServerScripts(); + quint64 entityScriptTimestampBefore = entity->getScriptTimestamp(); - bytesForThisEntity = entity->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); - if (entity->getDirtyFlags()) { - entityChanged(entity); - } - _entityMover.addEntityToMoveList(entity, entity->getQueryAACube()); + bytesForThisEntity = entity->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); + if (entity->getDirtyFlags()) { + entityChanged(entity); + } + _entityMover.addEntityToMoveList(entity, entity->getQueryAACube()); - QString entityScriptAfter = entity->getScript(); - QString entityServerScriptsAfter = entity->getServerScripts(); - quint64 entityScriptTimestampAfter = entity->getScriptTimestamp(); - bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter; + QString entityScriptAfter = entity->getScript(); + QString entityServerScriptsAfter = entity->getServerScripts(); + quint64 entityScriptTimestampAfter = entity->getScriptTimestamp(); + bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter; - // If the script value has changed on us, or it's timestamp has changed to force - // a reload then we want to send out a script changing signal... - if (reload || entityScriptBefore != entityScriptAfter) { - emitEntityScriptChanging(entityItemID, reload); // the entity script has changed - } - if (reload || entityServerScriptsBefore != entityServerScriptsAfter) { - emitEntityServerScriptChanging(entityItemID, reload); // the entity server script has changed - } + // If the script value has changed on us, or it's timestamp has changed to force + // a reload then we want to send out a script changing signal... + if (reload || entityScriptBefore != entityScriptAfter) { + emitEntityScriptChanging(entityItemID, reload); // the entity script has changed + } + if (reload || entityServerScriptsBefore != entityServerScriptsAfter) { + emitEntityServerScriptChanging(entityItemID, reload); // the entity server script has changed + } - QUuid parentIDAfter = entity->getParentID(); - if (parentIDBefore != parentIDAfter) { - addToNeedsParentFixupList(entity); - } -} else { - entity = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args); - if (entity) { - bytesForThisEntity = entity->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); + QUuid parentIDAfter = entity->getParentID(); + if (parentIDBefore != parentIDAfter) { + addToNeedsParentFixupList(entity); + } + } else { + entity = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args); + if (entity) { + bytesForThisEntity = entity->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args); - // don't add if we've recently deleted.... - if (!isDeletedEntity(entityItemID)) { - _entitiesToAdd.insert(entityItemID, entity); + // don't add if we've recently deleted.... + if (!isDeletedEntity(entityItemID)) { + _entitiesToAdd.insert(entityItemID, entity); - if (entity->getCreated() == UNKNOWN_CREATED_TIME) { - entity->recordCreationTime(); - } -#ifdef WANT_DEBUG - } else { - qCDebug(entities) << "Received packet for previously deleted entity [" << - entityItemID << "] ignoring. (inside " << __FUNCTION__ << ")"; -#endif - } - } -} -// Move the buffer forward to read more entities -dataAt += bytesForThisEntity; -bytesLeftToRead -= bytesForThisEntity; -bytesRead += bytesForThisEntity; + if (entity->getCreated() == UNKNOWN_CREATED_TIME) { + entity->recordCreationTime(); + } + #ifdef WANT_DEBUG + } else { + qCDebug(entities) << "Received packet for previously deleted entity [" << + entityItemID << "] ignoring. (inside " << __FUNCTION__ << ")"; + #endif + } + } + } + // Move the buffer forward to read more entities + dataAt += bytesForThisEntity; + bytesLeftToRead -= bytesForThisEntity; + bytesRead += bytesForThisEntity; } } } @@ -218,33 +218,19 @@ bytesRead += bytesForThisEntity; bool EntityTree::handlesEditPacketType(PacketType packetType) const { // we handle these types of "edit" packets switch (packetType) { - case PacketType::EntityAdd: - case PacketType::EntityEdit: - case PacketType::EntityErase: - case PacketType::EntityPhysics: - return true; - default: - return false; + case PacketType::EntityAdd: + case PacketType::EntityEdit: + case PacketType::EntityErase: + case PacketType::EntityPhysics: + return true; + default: + return false; } } /// Adds a new entity item to the tree void EntityTree::postAddEntity(EntityItemPointer entity) { assert(entity); - // check to see if we need to simulate this entity.. - if (_simulation) { - _simulation->addEntity(entity); - } - - if (!entity->getParentID().isNull()) { - addToNeedsParentFixupList(entity); - } - - _isDirty = true; - emit addingEntity(entity->getEntityItemID()); - - // find and hook up any entities with this entity as a (previously) missing parent - fixupNeedsParentFixups(); if (getIsServer()) { QString certID(entity->getCertificateID()); @@ -266,8 +252,24 @@ void EntityTree::postAddEntity(EntityItemPointer entity) { qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" << existingEntityItemID << ". Deleting existing entity."; deleteEntity(existingEntityItemID, true); + return; } } + + // check to see if we need to simulate this entity.. + if (_simulation) { + _simulation->addEntity(entity); + } + + if (!entity->getParentID().isNull()) { + addToNeedsParentFixupList(entity); + } + + _isDirty = true; + emit addingEntity(entity->getEntityItemID()); + + // find and hook up any entities with this entity as a (previously) missing parent + fixupNeedsParentFixups(); } bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) { @@ -2250,3 +2252,4 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const { } return entity->getJointNames(); } + From 5a3a3c493785dc18ff4a0668c7ff517aa5eb3483 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 11 Oct 2017 15:05:45 -0700 Subject: [PATCH 21/49] What is going on --- libraries/entities/src/EntityTree.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index a68e36b7ed..48c148906e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1177,20 +1177,15 @@ QString EntityTree::computeEncryptedNonce(const QString& certID, const QString& return ""; } - { - QWriteLocker locker(&_certNonceMapLock); - _certNonceMap.insert(certID, nonce); - } + QWriteLocker locker(&_certNonceMapLock); + _certNonceMap.insert(certID, nonce); return encryptedText.toBase64(); } bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce) { - QString actualNonce; - { - QWriteLocker locker(&_certNonceMapLock); - actualNonce = _certNonceMap.take(certID).toString(); - } + QWriteLocker locker(&_certNonceMapLock); + QString actualNonce = _certNonceMap.take(certID).toString(); return actualNonce == decryptedNonce; } From c3e66c95814b9ed08fbf273fdd4273c9381e5817 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 11 Oct 2017 16:38:48 -0700 Subject: [PATCH 22/49] Fixes --- interface/src/commerce/Wallet.cpp | 36 ++++++++++++++------------ libraries/entities/src/EntityTree.cpp | 37 ++++++++++++++------------- libraries/entities/src/EntityTree.h | 2 +- 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 702a94625a..16800c6ad3 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -730,28 +730,32 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack RSA* rsa = readKeys(keyFilePath().toStdString().c_str()); - const int decryptionStatus = RSA_private_decrypt(textLength, text, reinterpret_cast(encryptedText.data()), rsa, RSA_PKCS1_OAEP_PADDING); - RSA_free(rsa); + if (rsa) { + const int decryptionStatus = RSA_private_decrypt(textLength, text, reinterpret_cast(encryptedText.data()), rsa, RSA_PKCS1_OAEP_PADDING); + RSA_free(rsa); - if (decryptionStatus != -1) { - auto nodeList = DependencyManager::get(); + if (decryptionStatus != -1) { + auto nodeList = DependencyManager::get(); - QByteArray decryptedTextByteArray = decryptedText.toUtf8(); - int decryptedTextByteArraySize = decryptedTextByteArray.size(); - int certIDSize = certID.size(); - // setup the packet - auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + decryptedTextByteArraySize + 2*sizeof(int), true); + QByteArray decryptedTextByteArray = decryptedText.toUtf8(); + int decryptedTextByteArraySize = decryptedTextByteArray.size(); + int certIDSize = certID.size(); + // setup the packet + auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + decryptedTextByteArraySize + 2 * sizeof(int), true); - decryptedTextPacket->writePrimitive(certIDSize); - decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); - decryptedTextPacket->write(certID); - decryptedTextPacket->write(decryptedTextByteArray); + decryptedTextPacket->writePrimitive(certIDSize); + decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); + decryptedTextPacket->write(certID); + decryptedTextPacket->write(decryptedTextByteArray); - qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text"; + qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text"; - nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); + nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); + } else { + qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed."; + } } else { - qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed."; + qCDebug(commerce) << "During entity ownership challenge, creating the RSA object failed."; } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 48c148906e..7995a7894e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1159,28 +1159,32 @@ void EntityTree::startPendingTransferStatusTimer(const QString& certID, const En transferStatusRetryTimer->start(90000); } -QString EntityTree::computeEncryptedNonce(const QString& certID, const QString& ownerKey) { +QString EntityTree::computeEncryptedNonce(const QString& certID, const QString ownerKey) { QUuid nonce = QUuid::createUuid(); const auto text = reinterpret_cast(qPrintable(nonce.toString())); const unsigned int textLength = nonce.toString().length(); - const auto publicKey = reinterpret_cast(ownerKey.toUtf8().toBase64().constData()); - BIO* bio = BIO_new_mem_buf((void*)publicKey, sizeof(publicKey)); + BIO* bio = BIO_new_mem_buf((void*)ownerKey.toUtf8().constData(), -1); + BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // NO NEWLINE RSA* rsa = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL); - QByteArray encryptedText(RSA_size(rsa), 0); - const int encryptStatus = RSA_public_encrypt(textLength, text, reinterpret_cast(encryptedText.data()), rsa, RSA_PKCS1_OAEP_PADDING); - BIO_free(bio); - RSA_free(rsa); - if (encryptStatus == -1) { - qCWarning(entities) << "Unable to compute encrypted nonce for" << certID; - return ""; - } + //if (rsa) { + QByteArray encryptedText(RSA_size(rsa), 0); + const int encryptStatus = RSA_public_encrypt(textLength, text, reinterpret_cast(encryptedText.data()), rsa, RSA_PKCS1_OAEP_PADDING); + BIO_free(bio); + RSA_free(rsa); + if (encryptStatus == -1) { + qCWarning(entities) << "Unable to compute encrypted nonce for" << certID; + return ""; + } - QWriteLocker locker(&_certNonceMapLock); - _certNonceMap.insert(certID, nonce); + QWriteLocker locker(&_certNonceMapLock); + _certNonceMap.insert(certID, nonce); - return encryptedText.toBase64(); + return encryptedText.toBase64(); + //} else { + // return ""; + //} } bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce) { @@ -1246,8 +1250,7 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt } else { // Second, challenge ownership of the PoP cert // 1. Encrypt a nonce with the owner's public key - QString ownerKey(jsonObject["transfer_recipient_key"].toString()); - QString encryptedText = computeEncryptedNonce(certID, ownerKey); + QString encryptedText = computeEncryptedNonce(certID, jsonObject["transfer_recipient_key"].toString()); if (encryptedText == "") { qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce. Deleting entity..."; @@ -1260,8 +1263,6 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt int certIDByteArraySize = certIDByteArray.size(); QByteArray encryptedTextByteArray = encryptedText.toUtf8(); int encryptedTextByteArraySize = encryptedTextByteArray.size(); - QByteArray ownerKeyByteArray = ownerKey.toUtf8(); - int ownerKeyByteArraySize = ownerKeyByteArray.size(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDByteArraySize + encryptedTextByteArraySize + 2 * sizeof(int), true); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index fc6a913ffe..cffe0f6bb1 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -385,7 +385,7 @@ protected: Q_INVOKABLE void startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); private: - QString computeEncryptedNonce(const QString& certID, const QString& ownerKey); + QString computeEncryptedNonce(const QString& certID, const QString ownerKey); bool verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); }; From 5efa9207120562acf07bdb68d2b58df9fe1d556f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Oct 2017 11:13:23 -0700 Subject: [PATCH 23/49] Getting closer --- interface/src/Application.cpp | 4 ++-- interface/src/Application.h | 2 +- interface/src/commerce/Ledger.cpp | 4 ++-- interface/src/commerce/Wallet.cpp | 15 ++++++++++----- .../entities/src/EntityEditPacketSender.cpp | 2 +- libraries/entities/src/EntityEditPacketSender.h | 4 +++- libraries/entities/src/EntityTree.cpp | 17 +++++++++-------- 7 files changed, 28 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ed43c04d04..7589b85fd3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5758,9 +5758,9 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer void Application::packetSent(quint64 length) { } -void Application::addingEntityWithCertificate(const QString& certificateID, const QString& domainID) { +void Application::addingEntityWithCertificate(const QString& certificateID, const QString& placeName) { auto ledger = DependencyManager::get(); - ledger->updateLocation(certificateID, domainID); + ledger->updateLocation(certificateID, placeName); } void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointer scriptEngine) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 84d9eb9feb..bd1de68d71 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -440,7 +440,7 @@ private slots: void nodeActivated(SharedNodePointer node); void nodeKilled(SharedNodePointer node); static void packetSent(quint64 length); - static void addingEntityWithCertificate(const QString& certificateID, const QString& domainID); + static void addingEntityWithCertificate(const QString& certificateID, const QString& placeName); void updateDisplayMode(); void domainConnectionRefused(const QString& reasonMessage, int reason, const QString& extraInfo); diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index a1a709da98..7b48c98e54 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -234,8 +234,8 @@ void Ledger::updateLocation(const QString& asset_id, const QString location, con QStringList keys = wallet->listPublicKeys(); QString key = keys[0]; QJsonObject transaction; - transaction["asset_id"] = asset_id; - transaction["location"] = location; + transaction["certificate_id"] = asset_id; + transaction["place_name"] = location; QJsonDocument transactionDoc{ transaction }; auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); signedSend("transaction", transactionString, key, "location", "updateLocationSuccess", "updateLocationFailure", controlledFailure); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 16800c6ad3..6952f3b327 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -715,7 +715,7 @@ bool Wallet::changePassphrase(const QString& newPassphrase) { } void Wallet::handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode) { - QString decryptedText; + unsigned char decryptedText[64]; int certIDByteArraySize; int encryptedTextByteArraySize; @@ -725,19 +725,24 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack QByteArray certID = packet->read(certIDByteArraySize); QByteArray encryptedText = packet->read(encryptedTextByteArraySize); - const auto text = reinterpret_cast(encryptedText.constData()); - const unsigned int textLength = encryptedText.length(); + const auto encryptedTextBuf = reinterpret_cast(encryptedText.constData()); + const unsigned int textLength = (int)strlen((char*)encryptedTextBuf); RSA* rsa = readKeys(keyFilePath().toStdString().c_str()); if (rsa) { - const int decryptionStatus = RSA_private_decrypt(textLength, text, reinterpret_cast(encryptedText.data()), rsa, RSA_PKCS1_OAEP_PADDING); + const int decryptionStatus = RSA_private_decrypt(textLength, encryptedTextBuf, decryptedText, rsa, RSA_PKCS1_OAEP_PADDING); + + long error = ERR_get_error(); + const char* error_str = ERR_error_string(error, NULL); + qDebug() << "ZRF HERE\n\nEncrypted Text:" << encryptedTextBuf << "\nEncrypted Text Length:" << textLength << "\nDecrypted Text:" << decryptedText << "\nError:" << error_str; + RSA_free(rsa); if (decryptionStatus != -1) { auto nodeList = DependencyManager::get(); - QByteArray decryptedTextByteArray = decryptedText.toUtf8(); + QByteArray decryptedTextByteArray = QByteArray(reinterpret_cast(decryptedText), (int)strlen((char*)decryptedText)); int decryptedTextByteArraySize = decryptedTextByteArray.size(); int certIDSize = certID.size(); // setup the packet diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 2f8b796c93..aa045acbcc 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -111,7 +111,7 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, #endif queueOctreeEditMessage(type, bufferOut); if (type == PacketType::EntityAdd && !properties.getCertificateID().isEmpty()) { - emit addingEntityWithCertificate(properties.getCertificateID(), nodeList->getDomainHandler().getUUID().toString()); + emit addingEntityWithCertificate(properties.getCertificateID(), DependencyManager::get()->currentAddress().authority()); } } } diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 4e5b62e206..4e8a4be13d 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -19,6 +19,8 @@ #include "EntityItem.h" #include "AvatarData.h" +#include + /// Utility for processing, packing, queueing and sending of outbound edit voxel messages. class EntityEditPacketSender : public OctreeEditPacketSender { Q_OBJECT @@ -44,7 +46,7 @@ public: virtual void adjustEditPacketForClockSkew(PacketType type, QByteArray& buffer, qint64 clockSkew) override; signals: - void addingEntityWithCertificate(const QString& certificateID, const QString& domainID); + void addingEntityWithCertificate(const QString& certificateID, const QString& placeName); public slots: void processEntityEditNackPacket(QSharedPointer message, SharedNodePointer sendingNode); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 7995a7894e..d96cdaaa3e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -1164,14 +1165,14 @@ QString EntityTree::computeEncryptedNonce(const QString& certID, const QString o const auto text = reinterpret_cast(qPrintable(nonce.toString())); const unsigned int textLength = nonce.toString().length(); - BIO* bio = BIO_new_mem_buf((void*)ownerKey.toUtf8().constData(), -1); + QString ownerKeyWithHeaders = ("-----BEGIN RSA PUBLIC KEY-----\n" + ownerKey + "\n-----END RSA PUBLIC KEY-----"); + BIO* bio = BIO_new_mem_buf((void*)ownerKeyWithHeaders.toUtf8().constData(), -1); BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // NO NEWLINE RSA* rsa = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL); - //if (rsa) { + if (rsa) { QByteArray encryptedText(RSA_size(rsa), 0); const int encryptStatus = RSA_public_encrypt(textLength, text, reinterpret_cast(encryptedText.data()), rsa, RSA_PKCS1_OAEP_PADDING); - BIO_free(bio); RSA_free(rsa); if (encryptStatus == -1) { qCWarning(entities) << "Unable to compute encrypted nonce for" << certID; @@ -1181,10 +1182,10 @@ QString EntityTree::computeEncryptedNonce(const QString& certID, const QString o QWriteLocker locker(&_certNonceMapLock); _certNonceMap.insert(certID, nonce); - return encryptedText.toBase64(); - //} else { - // return ""; - //} + return encryptedText; + } else { + return ""; + } } bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce) { @@ -1302,7 +1303,7 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const QString certID(message.read(certIDByteArraySize)); QString decryptedText(message.read(decryptedTextByteArraySize)); - qCDebug(entities) << "ZRF FIXME" << decryptedText << certID; + qCDebug(entities) << "ZRF FIXME FJAOPISEJFPAOISEJFOA" << decryptedText << certID; emit killChallengeOwnershipTimeoutTimer(certID); From 0e96fc5caba3ce1b3291cbf7d27f5c82ba7f7bee Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Oct 2017 11:37:26 -0700 Subject: [PATCH 24/49] It's working! --- interface/src/commerce/Wallet.cpp | 11 ++++++----- libraries/entities/src/EntityTree.cpp | 16 +++++++--------- libraries/entities/src/EntityTree.h | 2 +- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 6952f3b327..fcb287c2d6 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -725,17 +725,18 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack QByteArray certID = packet->read(certIDByteArraySize); QByteArray encryptedText = packet->read(encryptedTextByteArraySize); - const auto encryptedTextBuf = reinterpret_cast(encryptedText.constData()); - const unsigned int textLength = (int)strlen((char*)encryptedTextBuf); - RSA* rsa = readKeys(keyFilePath().toStdString().c_str()); if (rsa) { - const int decryptionStatus = RSA_private_decrypt(textLength, encryptedTextBuf, decryptedText, rsa, RSA_PKCS1_OAEP_PADDING); + const int decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize, + reinterpret_cast(encryptedText.constData()), + decryptedText, + rsa, + RSA_PKCS1_OAEP_PADDING); long error = ERR_get_error(); const char* error_str = ERR_error_string(error, NULL); - qDebug() << "ZRF HERE\n\nEncrypted Text:" << encryptedTextBuf << "\nEncrypted Text Length:" << textLength << "\nDecrypted Text:" << decryptedText << "\nError:" << error_str; + qDebug() << "ZRF HERE\n\nEncrypted Text:" << encryptedText << "\nEncrypted Text ByteArray Size:" << encryptedTextByteArraySize << "\nEncrypted Text Length:" << encryptedText.length() << "\nDecrypted Text:" << decryptedText << "\nError:" << error_str; RSA_free(rsa); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index d96cdaaa3e..9a2048211f 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1160,7 +1160,7 @@ void EntityTree::startPendingTransferStatusTimer(const QString& certID, const En transferStatusRetryTimer->start(90000); } -QString EntityTree::computeEncryptedNonce(const QString& certID, const QString ownerKey) { +QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QString ownerKey) { QUuid nonce = QUuid::createUuid(); const auto text = reinterpret_cast(qPrintable(nonce.toString())); const unsigned int textLength = nonce.toString().length(); @@ -1182,6 +1182,8 @@ QString EntityTree::computeEncryptedNonce(const QString& certID, const QString o QWriteLocker locker(&_certNonceMapLock); _certNonceMap.insert(certID, nonce); + qDebug() << "ZRF HERE\n\nEncrypted Text:" << encryptedText << "\nEncrypted Text Length:" << encryptedText.length(); + return encryptedText; } else { return ""; @@ -1251,26 +1253,22 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt } else { // Second, challenge ownership of the PoP cert // 1. Encrypt a nonce with the owner's public key - QString encryptedText = computeEncryptedNonce(certID, jsonObject["transfer_recipient_key"].toString()); + QByteArray encryptedText = computeEncryptedNonce(certID, jsonObject["transfer_recipient_key"].toString()); if (encryptedText == "") { qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce. Deleting entity..."; deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else { // 2. Send the encrypted text to the rezzing avatar's node QByteArray certIDByteArray = certID.toUtf8(); int certIDByteArraySize = certIDByteArray.size(); - QByteArray encryptedTextByteArray = encryptedText.toUtf8(); - int encryptedTextByteArraySize = encryptedTextByteArray.size(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, - certIDByteArraySize + encryptedTextByteArraySize + 2 * sizeof(int), + certIDByteArraySize + encryptedText.length() + 2 * sizeof(int), true); challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); + challengeOwnershipPacket->writePrimitive(encryptedText.length()); challengeOwnershipPacket->write(certIDByteArray); - challengeOwnershipPacket->write(encryptedTextByteArray); + challengeOwnershipPacket->write(encryptedText); nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode); // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index cffe0f6bb1..00a601d684 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -385,7 +385,7 @@ protected: Q_INVOKABLE void startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); private: - QString computeEncryptedNonce(const QString& certID, const QString ownerKey); + QByteArray computeEncryptedNonce(const QString& certID, const QString ownerKey); bool verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); }; From abe1cd1b51bba38d6a5e5ed7a5015b15dc77c0b9 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Oct 2017 11:40:47 -0700 Subject: [PATCH 25/49] Minor cleanup --- assignment-client/src/entities/EntityServer.cpp | 7 +++---- interface/src/commerce/Wallet.cpp | 4 ---- libraries/entities/src/EntityTree.cpp | 4 ---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 1dc1eef593..0b27cfa408 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -442,8 +442,7 @@ void EntityServer::domainSettingsRequestFailed() { void EntityServer::startDynamicDomainVerification() { qCDebug(entities) << "Starting Dynamic Domain Verification..."; - auto nodeList = DependencyManager::get(); - QString thisDomainID = nodeList->getDomainHandler().getUUID().toString(); + QString thisPlaceName = DependencyManager::get()->currentAddress().authority(); EntityTreePointer tree = std::static_pointer_cast(_tree); QHash localMap(tree->getEntityCertificateIDMap()); @@ -485,9 +484,9 @@ void EntityServer::startDynamicDomainVerification() { if (networkReply->error() == QNetworkReply::NoError) { // ZRF FIXME!!! - //if (jsonObject["location"].toString() != thisDomainID) { + //if (jsonObject["place_name"].toString() != thisPlaceName) { if (false) { - qCDebug(entities) << "invalid_reason not empty, deleting entity" << i.value(); + qCDebug(entities) << "Entity's cert's place name isn't the current place name; deleting entity" << i.value(); tree->deleteEntity(i.value(), true); } } else { diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index fcb287c2d6..74f5e700f6 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -734,10 +734,6 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack rsa, RSA_PKCS1_OAEP_PADDING); - long error = ERR_get_error(); - const char* error_str = ERR_error_string(error, NULL); - qDebug() << "ZRF HERE\n\nEncrypted Text:" << encryptedText << "\nEncrypted Text ByteArray Size:" << encryptedTextByteArraySize << "\nEncrypted Text Length:" << encryptedText.length() << "\nDecrypted Text:" << decryptedText << "\nError:" << error_str; - RSA_free(rsa); if (decryptionStatus != -1) { diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 9a2048211f..c4a22569d7 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1182,8 +1182,6 @@ QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QStrin QWriteLocker locker(&_certNonceMapLock); _certNonceMap.insert(certID, nonce); - qDebug() << "ZRF HERE\n\nEncrypted Text:" << encryptedText << "\nEncrypted Text Length:" << encryptedText.length(); - return encryptedText; } else { return ""; @@ -1301,8 +1299,6 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const QString certID(message.read(certIDByteArraySize)); QString decryptedText(message.read(decryptedTextByteArraySize)); - qCDebug(entities) << "ZRF FIXME FJAOPISEJFPAOISEJFOA" << decryptedText << certID; - emit killChallengeOwnershipTimeoutTimer(certID); if (!verifyDecryptedNonce(certID, decryptedText)) { From 50446619f5b5d39ed2551b0ea6f272a754decb1c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Oct 2017 11:59:46 -0700 Subject: [PATCH 26/49] Remove unnecessary (?) references to _recentlyDeletedEntityItemIDs --- libraries/entities/src/EntityTree.cpp | 30 ++++++++------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index c4a22569d7..202d9f74b7 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1139,8 +1139,6 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) connect(_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { qCDebug(entities) << "Ownership challenge timed out, deleting entity" << entityItemID; deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); if (_challengeOwnershipTimeoutTimer) { _challengeOwnershipTimeoutTimer->stop(); _challengeOwnershipTimeoutTimer->deleteLater(); @@ -1224,19 +1222,13 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt if (!jsonObject["invalid_reason"].toString().isEmpty()) { qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else if (jsonObject["transfer_status"].toString() == "failed") { qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else if (jsonObject["transfer_status"].toString() == "pending") { if (isRetryingValidation) { qCDebug(entities) << "'transfer_status' is 'pending' after retry, deleting entity" << entityItemID; deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else { if (thread() != QThread::currentThread()) { QMetaObject::invokeMethod(this, "startPendingTransferStatusTimer", @@ -1281,8 +1273,6 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt } else { qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } networkReply->deleteLater(); @@ -1299,21 +1289,21 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const QString certID(message.read(certIDByteArraySize)); QString decryptedText(message.read(decryptedTextByteArraySize)); + EntityItemID id; + { + QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); + id = _entityCertificateIDMap.value(certID); + } + emit killChallengeOwnershipTimeoutTimer(certID); if (!verifyDecryptedNonce(certID, decryptedText)) { - EntityItemID id; - { - QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); - id = _entityCertificateIDMap.value(certID); - } - if (!id.isNull()) { - qCDebug(entities) << "Ownership challenge failed, deleting entity" << id; + qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed; deleting entity" << id; deleteEntity(id, true); - QWriteLocker recentlyDeletedLocker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), id); } + } else { + qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded; keeping entity" << id; } } @@ -1521,8 +1511,6 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c << "static certificate verification."; // Delete the entity we just added if it doesn't pass static certificate verification deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else { validatePop(properties.getCertificateID(), entityItemID, senderNode, false); } From 6a47884fcf1b3dc9a2ee4be7fdee3479cae5465b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Oct 2017 13:41:19 -0700 Subject: [PATCH 27/49] Cleanup and fix --- .../src/entities/EntityServer.cpp | 9 ++++-- interface/src/commerce/Wallet.cpp | 2 +- .../entities/src/EntityEditPacketSender.cpp | 2 +- libraries/entities/src/EntityTree.cpp | 32 ++++++++++--------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 0b27cfa408..475c911bb8 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -442,7 +442,7 @@ void EntityServer::domainSettingsRequestFailed() { void EntityServer::startDynamicDomainVerification() { qCDebug(entities) << "Starting Dynamic Domain Verification..."; - QString thisPlaceName = DependencyManager::get()->currentAddress().authority(); + QString thisPlaceName = DependencyManager::get()->getPlaceName(); EntityTreePointer tree = std::static_pointer_cast(_tree); QHash localMap(tree->getEntityCertificateIDMap()); @@ -466,7 +466,7 @@ void EntityServer::startDynamicDomainVerification() { 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/transfer"); + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); QJsonObject request; request["certificate_id"] = i.key(); networkRequest.setUrl(requestURL); @@ -486,8 +486,11 @@ void EntityServer::startDynamicDomainVerification() { // ZRF FIXME!!! //if (jsonObject["place_name"].toString() != thisPlaceName) { if (false) { - qCDebug(entities) << "Entity's cert's place name isn't the current place name; deleting entity" << i.value(); + qCDebug(entities) << "Entity's cert's place name" << jsonObject["place_name"].toString() + << "isn't the current place name" << thisPlaceName << "; deleting entity" << i.value(); tree->deleteEntity(i.value(), true); + } else { + qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value(); } } else { qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << i.value(); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 74f5e700f6..0bdb3c03ec 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -750,7 +750,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack decryptedTextPacket->write(certID); decryptedTextPacket->write(decryptedTextByteArray); - qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text"; + qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID; nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); } else { diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index aa045acbcc..5527cbc5ba 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -111,7 +111,7 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, #endif queueOctreeEditMessage(type, bufferOut); if (type == PacketType::EntityAdd && !properties.getCertificateID().isEmpty()) { - emit addingEntityWithCertificate(properties.getCertificateID(), DependencyManager::get()->currentAddress().authority()); + emit addingEntityWithCertificate(properties.getCertificateID(), DependencyManager::get()->getPlaceName()); } } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 202d9f74b7..69b7e5ab33 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1187,10 +1187,25 @@ QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QStrin } bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce) { + + QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); + EntityItemID id = _entityCertificateIDMap.value(certID); + QWriteLocker locker(&_certNonceMapLock); QString actualNonce = _certNonceMap.take(certID).toString(); - return actualNonce == decryptedNonce; + bool verificationSuccess = (actualNonce == decryptedNonce); + if (!verificationSuccess) { + if (!id.isNull()) { + qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed; deleting entity" << id + << "\nActual nonce:" << actualNonce << "\nDecrypted nonce:" << decryptedNonce; + deleteEntity(id, true); + } + } else { + qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded; keeping entity" << id; + } + + return verificationSuccess; } void EntityTree::validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation) { @@ -1289,22 +1304,9 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const QString certID(message.read(certIDByteArraySize)); QString decryptedText(message.read(decryptedTextByteArraySize)); - EntityItemID id; - { - QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); - id = _entityCertificateIDMap.value(certID); - } - emit killChallengeOwnershipTimeoutTimer(certID); - if (!verifyDecryptedNonce(certID, decryptedText)) { - if (!id.isNull()) { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed; deleting entity" << id; - deleteEntity(id, true); - } - } else { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded; keeping entity" << id; - } + verifyDecryptedNonce(certID, decryptedText); } int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, From d75c0a00bb97bfee9cad7f9c4f3af6a601ceba29 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Oct 2017 16:29:05 -0700 Subject: [PATCH 28/49] Closer than ever --- assignment-client/src/entities/EntityServer.cpp | 8 +------- assignment-client/src/entities/EntityServer.h | 1 + interface/src/commerce/Wallet.cpp | 2 +- libraries/entities/src/EntityTree.cpp | 8 ++------ libraries/entities/src/EntityTree.h | 1 + 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 475c911bb8..47d51e8d77 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -478,14 +478,8 @@ void EntityServer::startDynamicDomainVerification() { QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); jsonObject = jsonObject["data"].toObject(); - // ZRF FIXME Remove these two lines - QJsonDocument doc(jsonObject); - qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); - if (networkReply->error() == QNetworkReply::NoError) { - // ZRF FIXME!!! - //if (jsonObject["place_name"].toString() != thisPlaceName) { - if (false) { + if (jsonObject["location"].toArray().first().toString() != thisPlaceName) { qCDebug(entities) << "Entity's cert's place name" << jsonObject["place_name"].toString() << "isn't the current place name" << thisPlaceName << "; deleting entity" << i.value(); tree->deleteEntity(i.value(), true); diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index 5cebd9b751..408fad4494 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -19,6 +19,7 @@ #include "EntityItem.h" #include "EntityServerConsts.h" #include "EntityTree.h" +#include /// Handles assignments of type EntityServer - sending entities to various clients. diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 0bdb3c03ec..64b9922022 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -739,7 +739,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack if (decryptionStatus != -1) { auto nodeList = DependencyManager::get(); - QByteArray decryptedTextByteArray = QByteArray(reinterpret_cast(decryptedText), (int)strlen((char*)decryptedText)); + QByteArray decryptedTextByteArray = QByteArray(reinterpret_cast(decryptedText), decryptionStatus); int decryptedTextByteArraySize = decryptedTextByteArray.size(); int certIDSize = certID.size(); // setup the packet diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 69b7e5ab33..341637606f 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1229,18 +1229,14 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); jsonObject = jsonObject["data"].toObject(); - // ZRF FIXME Remove these two lines - QJsonDocument doc(jsonObject); - qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); - if (networkReply->error() == QNetworkReply::NoError) { if (!jsonObject["invalid_reason"].toString().isEmpty()) { qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; deleteEntity(entityItemID, true); - } else if (jsonObject["transfer_status"].toString() == "failed") { + } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; deleteEntity(entityItemID, true); - } else if (jsonObject["transfer_status"].toString() == "pending") { + } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { if (isRetryingValidation) { qCDebug(entities) << "'transfer_status' is 'pending' after retry, deleting entity" << entityItemID; deleteEntity(entityItemID, true); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 00a601d684..8d939159ce 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -18,6 +18,7 @@ #include "AccountManager.h" #include #include +#include #include #include From b56a38ca15aa109ee96047fa8907d91631b4197c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Oct 2017 16:32:09 -0700 Subject: [PATCH 29/49] Re-add references to recentlyDeletedEntityItemIDs --- libraries/entities/src/EntityTree.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 341637606f..f36b6ef168 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1139,6 +1139,8 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) connect(_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { qCDebug(entities) << "Ownership challenge timed out, deleting entity" << entityItemID; deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); if (_challengeOwnershipTimeoutTimer) { _challengeOwnershipTimeoutTimer->stop(); _challengeOwnershipTimeoutTimer->deleteLater(); @@ -1200,6 +1202,8 @@ bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decr qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed; deleting entity" << id << "\nActual nonce:" << actualNonce << "\nDecrypted nonce:" << decryptedNonce; deleteEntity(id, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), id); } } else { qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded; keeping entity" << id; @@ -1233,13 +1237,19 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt if (!jsonObject["invalid_reason"].toString().isEmpty()) { qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { if (isRetryingValidation) { qCDebug(entities) << "'transfer_status' is 'pending' after retry, deleting entity" << entityItemID; deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else { if (thread() != QThread::currentThread()) { QMetaObject::invokeMethod(this, "startPendingTransferStatusTimer", @@ -1284,6 +1294,8 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt } else { qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } networkReply->deleteLater(); @@ -1509,6 +1521,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c << "static certificate verification."; // Delete the entity we just added if it doesn't pass static certificate verification deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else { validatePop(properties.getCertificateID(), entityItemID, senderNode, false); } From 1400c66506d70c5341d58cbcc0f7fa3d9154a76d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Oct 2017 16:58:48 -0700 Subject: [PATCH 30/49] Be a bit more thorough --- .../src/entities/EntityServer.cpp | 3 +++ libraries/entities/src/EntityTree.cpp | 22 +++++++------------ libraries/entities/src/EntityTree.h | 5 +++++ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 47d51e8d77..ba76bf7f53 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -459,6 +459,7 @@ void EntityServer::startDynamicDomainVerification() { << "static certificate verification."; // Delete the entity if it doesn't pass static certificate verification tree->deleteEntity(i.value(), true); + tree->insertRecentlyDeletedEntityIDs(i.value()); } else { QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); @@ -483,12 +484,14 @@ void EntityServer::startDynamicDomainVerification() { qCDebug(entities) << "Entity's cert's place name" << jsonObject["place_name"].toString() << "isn't the current place name" << thisPlaceName << "; deleting entity" << i.value(); tree->deleteEntity(i.value(), true); + tree->insertRecentlyDeletedEntityIDs(i.value()); } else { qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value(); } } else { qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << i.value(); tree->deleteEntity(i.value(), true); + tree->insertRecentlyDeletedEntityIDs(i.value()); } networkReply->deleteLater(); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index f36b6ef168..d0ec1ffda7 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1139,8 +1139,7 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) connect(_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { qCDebug(entities) << "Ownership challenge timed out, deleting entity" << entityItemID; deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + insertRecentlyDeletedEntityIDs(entityItemID); if (_challengeOwnershipTimeoutTimer) { _challengeOwnershipTimeoutTimer->stop(); _challengeOwnershipTimeoutTimer->deleteLater(); @@ -1202,8 +1201,7 @@ bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decr qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed; deleting entity" << id << "\nActual nonce:" << actualNonce << "\nDecrypted nonce:" << decryptedNonce; deleteEntity(id, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), id); + insertRecentlyDeletedEntityIDs(id); } } else { qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded; keeping entity" << id; @@ -1237,19 +1235,16 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt if (!jsonObject["invalid_reason"].toString().isEmpty()) { qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + insertRecentlyDeletedEntityIDs(entityItemID); } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + insertRecentlyDeletedEntityIDs(entityItemID); } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { if (isRetryingValidation) { qCDebug(entities) << "'transfer_status' is 'pending' after retry, deleting entity" << entityItemID; deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + insertRecentlyDeletedEntityIDs(entityItemID); } else { if (thread() != QThread::currentThread()) { QMetaObject::invokeMethod(this, "startPendingTransferStatusTimer", @@ -1269,6 +1264,7 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt if (encryptedText == "") { qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce. Deleting entity..."; deleteEntity(entityItemID, true); + insertRecentlyDeletedEntityIDs(entityItemID); } else { // 2. Send the encrypted text to the rezzing avatar's node QByteArray certIDByteArray = certID.toUtf8(); @@ -1294,8 +1290,7 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt } else { qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + insertRecentlyDeletedEntityIDs(entityItemID); } networkReply->deleteLater(); @@ -1521,8 +1516,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c << "static certificate verification."; // Delete the entity we just added if it doesn't pass static certificate verification deleteEntity(entityItemID, true); - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + insertRecentlyDeletedEntityIDs(entityItemID); } else { validatePop(properties.getCertificateID(), entityItemID, senderNode, false); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 8d939159ce..b939ff51c1 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -193,6 +193,11 @@ public: return _recentlyDeletedEntityItemIDs; } + void insertRecentlyDeletedEntityIDs(const EntityItemID& id) { + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), id); + } + QHash getEntityCertificateIDMap() const { QReadLocker locker(&_entityCertificateIDMapLock); return _entityCertificateIDMap; From bcbddb7106f83bf9f4176af584b4b6b93a6ebc1e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Sat, 14 Oct 2017 12:26:21 -0700 Subject: [PATCH 31/49] Fix unix warnings --- interface/src/commerce/Ledger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 7b48c98e54..904847cb5f 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -227,7 +227,7 @@ void Ledger::updateLocation(const QString& asset_id, const QString location, con auto walletScriptingInterface = DependencyManager::get(); uint walletStatus = walletScriptingInterface->getWalletStatus(); - if (walletStatus != wallet->WALLET_STATUS_READY) { + if (walletStatus != (uint)wallet->WALLET_STATUS_READY) { emit walletScriptingInterface->walletNotSetup(); qDebug(commerce) << "User attempted to update the location of a certificate, but their wallet wasn't ready. Status:" << walletStatus; } else { From 552defb418febefe887f12d65f75b9a1e4394f8c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 16 Oct 2017 11:23:34 -0700 Subject: [PATCH 32/49] Change protocol version --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 2f3f65c90b..1a9f93d255 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return VERSION_ENTITIES_HAS_CERTIFICATE_PROPERTIES; + return VERSION_ENTITIES_HAS_DYNAMIC_OWNERSHIP_TESTS; case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::JSONFilterWithFamilyTree); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 122be60870..da29372a40 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -268,6 +268,7 @@ const PacketVersion VERSION_ENTITIES_HAS_SHOULD_HIGHLIGHT = 71; const PacketVersion VERSION_ENTITIES_HAS_HIGHLIGHT_SCRIPTING_INTERFACE = 72; const PacketVersion VERSION_ENTITIES_ANIMATION_ALLOW_TRANSLATION_PROPERTIES = 73; const PacketVersion VERSION_ENTITIES_HAS_CERTIFICATE_PROPERTIES = 74; +const PacketVersion VERSION_ENTITIES_HAS_DYNAMIC_OWNERSHIP_TESTS = 75; enum class EntityQueryPacketVersion: PacketVersion { JSONFilter = 18, From 960f1fbfe49bbef2f562ed1cd7cf6d6910360f7a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 16 Oct 2017 13:28:14 -0700 Subject: [PATCH 33/49] Still broken, but safer --- .../src/entities/EntityServer.cpp | 77 ++++++++++--------- .../octree/OctreeInboundPacketProcessor.cpp | 4 +- libraries/entities/src/EntityTree.cpp | 13 +--- libraries/entities/src/EntityTree.h | 5 -- 4 files changed, 47 insertions(+), 52 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index ba76bf7f53..d2585aae49 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -452,50 +452,53 @@ void EntityServer::startDynamicDomainVerification() { while (i.hasNext()) { i.next(); - // ZRF FIXME!!! - //if (!tree->findEntityByEntityItemID(i.value())->verifyStaticCertificateProperties()) { - if (false) { - qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed" - << "static certificate verification."; - // Delete the entity if it doesn't pass static certificate verification - tree->deleteEntity(i.value(), true); - tree->insertRecentlyDeletedEntityIDs(i.value()); - } else { + EntityItemPointer entity = tree->findEntityByEntityItemID(i.value()); - 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"] = i.key(); - networkRequest.setUrl(requestURL); + if (entity) { + // ZRF FIXME!!! + //if (!entity->verifyStaticCertificateProperties()) { + if (false) { + qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed" + << "static certificate verification."; + // Delete the entity if it doesn't pass static certificate verification + tree->deleteEntity(i.value(), true); + } else { - QNetworkReply* networkReply = NULL; - networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); + 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"] = i.key(); + networkRequest.setUrl(requestURL); - connect(networkReply, &QNetworkReply::finished, [=]() { - QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - jsonObject = jsonObject["data"].toObject(); + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - if (networkReply->error() == QNetworkReply::NoError) { - if (jsonObject["location"].toArray().first().toString() != thisPlaceName) { - qCDebug(entities) << "Entity's cert's place name" << jsonObject["place_name"].toString() - << "isn't the current place name" << thisPlaceName << "; deleting entity" << i.value(); - tree->deleteEntity(i.value(), true); - tree->insertRecentlyDeletedEntityIDs(i.value()); + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); + + if (networkReply->error() == QNetworkReply::NoError) { + if (jsonObject["location"].toArray().first().toString() != thisPlaceName) { + qCDebug(entities) << "Entity's cert's place name" << jsonObject["place_name"].toString() + << "isn't the current place name" << thisPlaceName << "; deleting entity" << i.value(); + tree->deleteEntity(i.value(), true); + } else { + qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value(); + } } else { - qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value(); + qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << i.value(); + tree->deleteEntity(i.value(), true); } - } else { - qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << i.value(); - tree->deleteEntity(i.value(), true); - tree->insertRecentlyDeletedEntityIDs(i.value()); - } - networkReply->deleteLater(); - }); + networkReply->deleteLater(); + }); + } + } else { + qCWarning(entities) << "During DDV, an entity with ID" << i.value() << "was NOT found in the Entity Tree!"; } } diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index 2723e8acd5..3f835678ac 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -93,7 +93,9 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer PacketType packetType = message->getType(); if (packetType == PacketType::ChallengeOwnership) { - _myServer->getOctree()->processChallengeOwnershipPacket(*message, sendingNode); + _myServer->getOctree()->withWriteLock([&] { + _myServer->getOctree()->processChallengeOwnershipPacket(*message, sendingNode); + }); } else if (_myServer->getOctree()->handlesEditPacketType(packetType)) { PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket); _receivedPacketCount++; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 3e63c94c87..5bd56eba50 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1139,7 +1139,6 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) connect(_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { qCDebug(entities) << "Ownership challenge timed out, deleting entity" << entityItemID; deleteEntity(entityItemID, true); - insertRecentlyDeletedEntityIDs(entityItemID); if (_challengeOwnershipTimeoutTimer) { _challengeOwnershipTimeoutTimer->stop(); _challengeOwnershipTimeoutTimer->deleteLater(); @@ -1174,13 +1173,16 @@ QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QStrin const int encryptStatus = RSA_public_encrypt(textLength, text, reinterpret_cast(encryptedText.data()), rsa, RSA_PKCS1_OAEP_PADDING); RSA_free(rsa); if (encryptStatus == -1) { - qCWarning(entities) << "Unable to compute encrypted nonce for" << certID; + long error = ERR_get_error(); + const char* error_str = ERR_error_string(error, NULL); + qCWarning(entities) << "Unable to compute encrypted nonce for" << certID << "\nRSA error:" << error_str; return ""; } QWriteLocker locker(&_certNonceMapLock); _certNonceMap.insert(certID, nonce); + qCDebug(entities) << "Challenging ownership of Cert ID" << certID << "by encrypting and sending nonce" << nonce << "to owner."; return encryptedText; } else { return ""; @@ -1201,7 +1203,6 @@ bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decr qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed; deleting entity" << id << "\nActual nonce:" << actualNonce << "\nDecrypted nonce:" << decryptedNonce; deleteEntity(id, true); - insertRecentlyDeletedEntityIDs(id); } } else { qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded; keeping entity" << id; @@ -1235,16 +1236,13 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt if (!jsonObject["invalid_reason"].toString().isEmpty()) { qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; deleteEntity(entityItemID, true); - insertRecentlyDeletedEntityIDs(entityItemID); } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; deleteEntity(entityItemID, true); - insertRecentlyDeletedEntityIDs(entityItemID); } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { if (isRetryingValidation) { qCDebug(entities) << "'transfer_status' is 'pending' after retry, deleting entity" << entityItemID; deleteEntity(entityItemID, true); - insertRecentlyDeletedEntityIDs(entityItemID); } else { if (thread() != QThread::currentThread()) { QMetaObject::invokeMethod(this, "startPendingTransferStatusTimer", @@ -1264,7 +1262,6 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt if (encryptedText == "") { qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce. Deleting entity..."; deleteEntity(entityItemID, true); - insertRecentlyDeletedEntityIDs(entityItemID); } else { // 2. Send the encrypted text to the rezzing avatar's node QByteArray certIDByteArray = certID.toUtf8(); @@ -1290,7 +1287,6 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt } else { qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; deleteEntity(entityItemID, true); - insertRecentlyDeletedEntityIDs(entityItemID); } networkReply->deleteLater(); @@ -1516,7 +1512,6 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c << "static certificate verification."; // Delete the entity we just added if it doesn't pass static certificate verification deleteEntity(entityItemID, true); - insertRecentlyDeletedEntityIDs(entityItemID); } else { validatePop(properties.getCertificateID(), entityItemID, senderNode, false); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index b939ff51c1..8d939159ce 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -193,11 +193,6 @@ public: return _recentlyDeletedEntityItemIDs; } - void insertRecentlyDeletedEntityIDs(const EntityItemID& id) { - QWriteLocker locker(&_recentlyDeletedEntitiesLock); - _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), id); - } - QHash getEntityCertificateIDMap() const { QReadLocker locker(&_entityCertificateIDMapLock); return _entityCertificateIDMap; From 3c572b0f7a1d5eeda5654ed9a2a7aef0e644c2ca Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 16 Oct 2017 13:41:39 -0700 Subject: [PATCH 34/49] Fix deadlock --- libraries/entities/src/EntityTree.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 5bd56eba50..6ccff83fb4 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1191,11 +1191,17 @@ QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QStrin bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce) { - QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); - EntityItemID id = _entityCertificateIDMap.value(certID); + EntityItemID id; + { + QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); + id = _entityCertificateIDMap.value(certID); + } - QWriteLocker locker(&_certNonceMapLock); - QString actualNonce = _certNonceMap.take(certID).toString(); + QString actualNonce; + { + QWriteLocker locker(&_certNonceMapLock); + actualNonce = _certNonceMap.take(certID).toString(); + } bool verificationSuccess = (actualNonce == decryptedNonce); if (!verificationSuccess) { From a4b8bf0be60b52ce8ae85d3d4fa2cf5e67e1a8f8 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 16 Oct 2017 15:33:10 -0700 Subject: [PATCH 35/49] Bugfixes and CR --- .../src/entities/EntityServer.cpp | 1 + assignment-client/src/entities/EntityServer.h | 4 ++-- .../resources/describe-settings.json | 4 ++-- interface/src/commerce/Wallet.cpp | 2 +- libraries/entities/src/EntityTree.cpp | 21 +++++++++++++------ libraries/entities/src/EntityTree.h | 1 - 6 files changed, 21 insertions(+), 12 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index d2585aae49..d99187adee 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "AssignmentParentFinder.h" #include "EntityNodeData.h" diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index 408fad4494..bcfeb3485d 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -84,9 +84,9 @@ private: QMap> _viewerSendingStats; static const int DEFAULT_MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 45 * 60 * 1000; // 45m - static const int DEFAULT_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 75 * 60 * 1000; // 1h15m + static const int DEFAULT_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = 60 * 60 * 1000; // 1h int _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = DEFAULT_MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; // 45m - int _MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = DEFAULT_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; // 1h15m + int _MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS = DEFAULT_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS; // 1h QTimer _dynamicDomainVerificationTimer; void startDynamicDomainVerification(); }; diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 2b0d032e3c..d8c91f4ce3 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1266,8 +1266,8 @@ "name": "dynamicDomainVerificationTimeMax", "label": "Dynamic Domain Verification Time (seconds) - Maximum", "help": "The upper limit on the amount of time that passes before Dynamic Domain Verification on entities occurs. Units are seconds.", - "placeholder": "4500", - "default": "4500", + "placeholder": "3600", + "default": "3600", "advanced": true }, { diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index f7cd0e5919..c7c09d8b03 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -741,7 +741,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack if (decryptionStatus != -1) { auto nodeList = DependencyManager::get(); - QByteArray decryptedTextByteArray = QByteArray(reinterpret_cast(decryptedText), decryptionStatus); + QByteArray decryptedTextByteArray = QByteArray(reinterpret_cast(decryptedText), decryptionStatus); int decryptedTextByteArraySize = decryptedTextByteArray.size(); int certIDSize = certID.size(); // setup the packet diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 6ccff83fb4..57b4c9acc1 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -1159,18 +1160,23 @@ void EntityTree::startPendingTransferStatusTimer(const QString& certID, const En } QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QString ownerKey) { - QUuid nonce = QUuid::createUuid(); - const auto text = reinterpret_cast(qPrintable(nonce.toString())); - const unsigned int textLength = nonce.toString().length(); - QString ownerKeyWithHeaders = ("-----BEGIN RSA PUBLIC KEY-----\n" + ownerKey + "\n-----END RSA PUBLIC KEY-----"); BIO* bio = BIO_new_mem_buf((void*)ownerKeyWithHeaders.toUtf8().constData(), -1); BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // NO NEWLINE RSA* rsa = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL); if (rsa) { + QUuid nonce = QUuid::createUuid(); + const unsigned int textLength = nonce.toString().length(); QByteArray encryptedText(RSA_size(rsa), 0); - const int encryptStatus = RSA_public_encrypt(textLength, text, reinterpret_cast(encryptedText.data()), rsa, RSA_PKCS1_OAEP_PADDING); + const int encryptStatus = RSA_public_encrypt(textLength, + reinterpret_cast(qPrintable(nonce.toString())), + reinterpret_cast(encryptedText.data()), + rsa, + RSA_PKCS1_OAEP_PADDING); + if (bio) { + BIO_free(bio); + } RSA_free(rsa); if (encryptStatus == -1) { long error = ERR_get_error(); @@ -1181,10 +1187,13 @@ QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QStrin QWriteLocker locker(&_certNonceMapLock); _certNonceMap.insert(certID, nonce); - qCDebug(entities) << "Challenging ownership of Cert ID" << certID << "by encrypting and sending nonce" << nonce << "to owner."; + return encryptedText; } else { + if (bio) { + BIO_free(bio); + } return ""; } } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 8d939159ce..d6539babe7 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -19,7 +19,6 @@ #include #include #include -#include #include #include From e1b0e5a2dd2a8ec10d005cc23198f7f437c3c825 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 16 Oct 2017 16:00:32 -0700 Subject: [PATCH 36/49] Move some include files around --- assignment-client/src/entities/EntityServer.cpp | 3 +++ assignment-client/src/entities/EntityServer.h | 1 - libraries/entities/src/EntityEditPacketSender.cpp | 1 + libraries/entities/src/EntityEditPacketSender.h | 2 -- libraries/entities/src/EntityTree.cpp | 4 ++++ libraries/entities/src/EntityTree.h | 5 ----- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index d99187adee..d23cdd593e 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -17,6 +17,9 @@ #include #include #include +#include +#include +#include #include "AssignmentParentFinder.h" #include "EntityNodeData.h" diff --git a/assignment-client/src/entities/EntityServer.h b/assignment-client/src/entities/EntityServer.h index bcfeb3485d..05404b28c8 100644 --- a/assignment-client/src/entities/EntityServer.h +++ b/assignment-client/src/entities/EntityServer.h @@ -19,7 +19,6 @@ #include "EntityItem.h" #include "EntityServerConsts.h" #include "EntityTree.h" -#include /// Handles assignments of type EntityServer - sending entities to various clients. diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index a629e7908a..e82ed82093 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -18,6 +18,7 @@ #include "EntitiesLogging.h" #include "EntityItem.h" #include "EntityItemProperties.h" +#include EntityEditPacketSender::EntityEditPacketSender() { auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 4e8a4be13d..bf79bb9203 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -19,8 +19,6 @@ #include "EntityItem.h" #include "AvatarData.h" -#include - /// Utility for processing, packing, queueing and sending of outbound edit voxel messages. class EntityEditPacketSender : public OctreeEditPacketSender { Q_OBJECT diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 57b4c9acc1..ba92947a2e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -18,6 +18,10 @@ #include #include #include +#include "AccountManager.h" +#include +#include +#include #include diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index d6539babe7..922d60f132 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -15,11 +15,6 @@ #include #include -#include "AccountManager.h" -#include -#include -#include - #include #include From eceb0dcc950c4a80783beb42cf472bd5b4de94aa Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 17 Oct 2017 11:29:43 -0700 Subject: [PATCH 37/49] Fix appearance of domain settings page --- domain-server/resources/describe-settings.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index d8c91f4ce3..9dadc6df03 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -217,7 +217,7 @@ }, { "label": "Permissions ?", - "span": 8 + "span": 10 } ], "columns": [ @@ -315,7 +315,7 @@ }, { "label": "Permissions ?", - "span": 8 + "span": 10 } ], "columns": [ @@ -436,7 +436,7 @@ }, { "label": "Permissions ?", - "span": 8 + "span": 10 } ], "columns": [ @@ -550,7 +550,7 @@ }, { "label": "Permissions ?", - "span": 8 + "span": 10 } ], "columns": [ @@ -642,7 +642,7 @@ }, { "label": "Permissions ?", - "span": 8 + "span": 10 } ], "columns": [ @@ -734,7 +734,7 @@ }, { "label": "Permissions ?", - "span": 8 + "span": 10 } ], "columns": [ @@ -826,7 +826,7 @@ }, { "label": "Permissions ?", - "span": 8 + "span": 10 } ], "columns": [ From f3b641a8335a6bf1b9fe8c2fce56a63c64c7476e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 17 Oct 2017 11:07:40 -0700 Subject: [PATCH 38/49] Edit.js caused a testing fail :( --- scripts/system/edit.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index d6d4de2a4b..c8e93b229b 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1561,7 +1561,8 @@ function getPositionToCreateEntity(extra) { } function importSVO(importURL) { - if (!Entities.canRez() && !Entities.canRezTmp()) { + if (!Entities.canRez() && !Entities.canRezTmp() && + !Entities.canRezCertified() && !Entities.canRezTmpCertified()) { Window.notifyEditError(INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG); return; } From 952160874c576ada782bf25c826ea75537e38836 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 18 Oct 2017 11:21:03 -0700 Subject: [PATCH 39/49] Stringmatch Domain ID instead of placename for DVV --- assignment-client/src/entities/EntityServer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index d23cdd593e..75bdb469a4 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -446,7 +446,7 @@ void EntityServer::domainSettingsRequestFailed() { void EntityServer::startDynamicDomainVerification() { qCDebug(entities) << "Starting Dynamic Domain Verification..."; - QString thisPlaceName = DependencyManager::get()->getPlaceName(); + QString thisDomainID = DependencyManager::get()->getDomainId(); EntityTreePointer tree = std::static_pointer_cast(_tree); QHash localMap(tree->getEntityCertificateIDMap()); @@ -486,9 +486,9 @@ void EntityServer::startDynamicDomainVerification() { jsonObject = jsonObject["data"].toObject(); if (networkReply->error() == QNetworkReply::NoError) { - if (jsonObject["location"].toArray().first().toString() != thisPlaceName) { - qCDebug(entities) << "Entity's cert's place name" << jsonObject["place_name"].toString() - << "isn't the current place name" << thisPlaceName << "; deleting entity" << i.value(); + if (jsonObject["domain_id"].toString() != thisDomainID) { + qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString() + << "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << i.value(); tree->deleteEntity(i.value(), true); } else { qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value(); From 74180bc4cd3026eb331c751014578e1ba2a29f3c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 19 Oct 2017 15:30:49 -0700 Subject: [PATCH 40/49] Implement static cert verification correctly --- .../src/entities/EntityServer.cpp | 4 +- libraries/entities/src/EntityItem.cpp | 86 +++++++++---------- libraries/entities/src/EntityItem.h | 10 +-- .../entities/src/EntityScriptingInterface.cpp | 15 ---- .../entities/src/EntityScriptingInterface.h | 3 - libraries/entities/src/EntityTree.cpp | 6 +- 6 files changed, 49 insertions(+), 75 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 75bdb469a4..1351220714 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -459,9 +459,7 @@ void EntityServer::startDynamicDomainVerification() { EntityItemPointer entity = tree->findEntityByEntityItemID(i.value()); if (entity) { - // ZRF FIXME!!! - //if (!entity->verifyStaticCertificateProperties()) { - if (false) { + if (!entity->verifyStaticCertificateProperties()) { qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed" << "static certificate verification."; // Delete the entity if it doesn't pass static certificate verification diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 58b8dd22bf..3074bd275b 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -14,9 +14,13 @@ #include #include #include -#include // see comments for DEBUG_CERT +#include #include #include +#include +#include +#include +#include #include @@ -41,6 +45,7 @@ int entityItemPointernMetaTypeId = qRegisterMetaType(); int EntityItem::_maxActionsDataSize = 800; quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND; +QString EntityItem::_marketplacePublicKey; EntityItem::EntityItem(const EntityItemID& entityItemID) : SpatiallyNestable(NestableType::Entity, entityItemID) @@ -1588,16 +1593,16 @@ QByteArray EntityItem::getStaticCertificateJSON() const { // It is important that this be reproducible in the same order each time. Since we also generate these on the server, we do it alphabetically // to help maintainence in two different code bases. if (!propertySet.getAnimation().getURL().isEmpty()) { - json["animation.url"] = propertySet.getAnimation().getURL(); + json["animationURL"] = propertySet.getAnimation().getURL(); } ADD_STRING_PROPERTY(collisionSoundURL, CollisionSoundURL); ADD_STRING_PROPERTY(compoundShapeURL, CompoundShapeURL); ADD_INT_PROPERTY(editionNumber, EditionNumber); - ADD_INT_PROPERTY(entityInstanceNumber, EntityInstanceNumber); + ADD_INT_PROPERTY(instanceNumber, EntityInstanceNumber); ADD_STRING_PROPERTY(itemArtist, ItemArtist); ADD_STRING_PROPERTY(itemCategories, ItemCategories); ADD_STRING_PROPERTY(itemDescription, ItemDescription); - ADD_STRING_PROPERTY(itemLicense, ItemLicense); + ADD_STRING_PROPERTY(itemLicenseUrl, ItemLicense); ADD_STRING_PROPERTY(itemName, ItemName); ADD_INT_PROPERTY(limitedRun, LimitedRun); ADD_STRING_PROPERTY(marketplaceID, MarketplaceID); @@ -1612,39 +1617,6 @@ QByteArray EntityItem::getStaticCertificateHash() const { return QCryptographicHash::hash(getStaticCertificateJSON(), QCryptographicHash::Sha256); } -#ifdef DEBUG_CERT -QString EntityItem::computeCertificateID() { - // Until the marketplace generates it, compute and answer the certificateID here. - // Does not set it, as that will have to be done from script engine in order to update server, etc. - const auto hash = getStaticCertificateHash(); - const auto text = reinterpret_cast(hash.constData()); - const unsigned int textLength = hash.length(); - - const char privateKey[] = "-----BEGIN RSA PRIVATE KEY-----\n\ -MIIBOQIBAAJBALCoBiDAZOClO26tC5pd7JikBL61WIgpAqbcNnrV/TcG6LPI7Zbi\n\ -MjdUixmTNvYMRZH3Wlqtl2IKG1W68y3stKECAwEAAQJABvOlwhYwIhL+gr12jm2R\n\ -yPPzZ9nVEQ6kFxLlZfIT09119fd6OU1X5d4sHWfMfSIEgjwQIDS3ZU1kY3XKo87X\n\ -zQIhAOPHlYa1OC7BLhaTouy68qIU2vCKLP8mt4S31/TT0UOnAiEAxor6gU6yupTQ\n\ -yuyV3yHvr5LkZKBGqhjmOTmDfgtX7ncCIChGbgX3nQuHVOLhD/nTxHssPNozVGl5\n\ -KxHof+LmYSYZAiB4U+yEh9SsXdq40W/3fpLMPuNq1PRezJ5jGidGMcvF+wIgUNec\n\ -3Kg2U+CVZr8/bDT/vXRrsKj1zfobYuvbfVH02QY=\n\ ------END RSA PRIVATE KEY-----"; - BIO* bio = BIO_new_mem_buf((void*)privateKey, sizeof(privateKey)); - RSA* rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL); - - QByteArray signature(RSA_size(rsa), 0); - unsigned int signatureLength = 0; - const int signOK = RSA_sign(NID_sha256, text, textLength, reinterpret_cast(signature.data()), &signatureLength, rsa); - BIO_free(bio); - RSA_free(rsa); - if (!signOK) { - qCWarning(entities) << "Unable to compute signature for" << getName() << getEntityItemID(); - return ""; - } - return signature.toBase64(); -#endif -} - bool EntityItem::verifyStaticCertificateProperties() { // True IIF a non-empty certificateID matches the static certificate json. // I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash. @@ -1659,13 +1631,8 @@ bool EntityItem::verifyStaticCertificateProperties() { const auto hash = getStaticCertificateHash(); const auto text = reinterpret_cast(hash.constData()); const unsigned int textLength = hash.length(); - - // After DEBUG_CERT ends, we will get/cache this once from the marketplace when needed, and it likely won't be RSA. - const char publicKey[] = "-----BEGIN PUBLIC KEY-----\n\ -MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALCoBiDAZOClO26tC5pd7JikBL61WIgp\n\ -AqbcNnrV/TcG6LPI7ZbiMjdUixmTNvYMRZH3Wlqtl2IKG1W68y3stKECAwEAAQ==\n\ ------END PUBLIC KEY-----"; - BIO *bio = BIO_new_mem_buf((void*)publicKey, sizeof(publicKey)); + + BIO *bio = BIO_new_mem_buf((void*)qPrintable(EntityItem::_marketplacePublicKey), -1); EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); RSA* rsa = EVP_PKEY_get1_RSA(evp_key); bool answer = RSA_verify(NID_sha256, text, textLength, signature, signatureLength, rsa); @@ -3006,3 +2973,34 @@ void EntityItem::somethingChangedNotification() { } }); } + +void EntityItem::retrieveMarketplacePublicKey() { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + requestURL.setPath("/api/v1/commerce/marketplace_key"); + QJsonObject request; + networkRequest.setUrl(requestURL); + + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.get(networkRequest); + + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); + + if (networkReply->error() == QNetworkReply::NoError) { + if (!jsonObject["public_key"].toString().isEmpty()) { + EntityItem::_marketplacePublicKey = jsonObject["public_key"].toString(); + qCWarning(entities) << "Marketplace public key has been set to" << _marketplacePublicKey; + } else { + qCWarning(entities) << "Marketplace public key is empty!"; + } + } else { + qCWarning(entities) << "Call to" << networkRequest.url() << "failed! Error:" << networkReply->error(); + } + + networkReply->deleteLater(); + }); +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index c26f1694a9..ce39bbce8e 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -36,9 +36,6 @@ #include "SimulationFlags.h" #include "EntityDynamicInterface.h" -// FIXME: The server-side marketplace will soon create the certificateID. At that point, all of the DEBUG_CERT stuff will go away. -#define DEBUG_CERT 1 - class EntitySimulation; class EntityTreeElement; class EntityTreeElementExtraEncodeData; @@ -331,9 +328,6 @@ public: QByteArray getStaticCertificateJSON() const; QByteArray getStaticCertificateHash() const; bool verifyStaticCertificateProperties(); -#ifdef DEBUG_CERT - QString computeCertificateID(); -#endif // TODO: get rid of users of getRadius()... float getRadius() const; @@ -484,6 +478,9 @@ public: ChangeHandlerId registerChangeHandler(const ChangeHandlerCallback& handler); void deregisterChangeHandler(const ChangeHandlerId& changeHandlerId); + static QString _marketplacePublicKey; + static void retrieveMarketplacePublicKey(); + protected: QHash _changeHandlers; @@ -635,7 +632,6 @@ protected: quint64 _lastUpdatedVelocityTimestamp { 0 }; quint64 _lastUpdatedAngularVelocityTimestamp { 0 }; quint64 _lastUpdatedAccelerationTimestamp { 0 }; - }; #endif // hifi_EntityItem_h diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index f5117dddc0..66a64db15e 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1778,18 +1778,3 @@ bool EntityScriptingInterface::verifyStaticCertificateProperties(const QUuid& en } return result; } - -#ifdef DEBUG_CERT -QString EntityScriptingInterface::computeCertificateID(const QUuid& entityID) { - QString result { "" }; - if (_entityTree) { - _entityTree->withReadLock([&] { - EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID)); - if (entity) { - result = entity->computeCertificateID(); - } - }); - } - return result; -} -#endif \ No newline at end of file diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 989d3dd89d..2dc31dbe7d 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -387,9 +387,6 @@ public slots: Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID); Q_INVOKABLE bool verifyStaticCertificateProperties(const QUuid& entityID); -#ifdef DEBUG_CERT - Q_INVOKABLE QString computeCertificateID(const QUuid& entityID); -#endif signals: void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ba92947a2e..f8db876728 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -71,6 +71,8 @@ EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage) { resetClientEditStats(); + + EntityItem::retrieveMarketplacePublicKey(); } EntityTree::~EntityTree() { @@ -1523,9 +1525,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c _totalCreates++; if (newEntity && isCertified && getIsServer()) { - // ZRF FIXME!!! - //if (!newEntity->verifyStaticCertificateProperties()) { - if (false) { + if (!newEntity->verifyStaticCertificateProperties()) { qCDebug(entities) << "User" << senderNode->getUUID() << "attempted to add a certified entity with ID" << entityItemID << "which failed" << "static certificate verification."; From c99e803ab707020d0f2fb74cbc7b581405a9bc1b Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 19 Oct 2017 17:17:54 -0700 Subject: [PATCH 41/49] Error handling --- libraries/entities/src/EntityItem.cpp | 100 ++++++++++++++++++-------- 1 file changed, 69 insertions(+), 31 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 3074bd275b..94d9a08252 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -1624,6 +1625,7 @@ bool EntityItem::verifyStaticCertificateProperties() { if (getCertificateID().isEmpty()) { return false; } + const auto signatureBytes = QByteArray::fromBase64(getCertificateID().toLatin1()); const auto signature = reinterpret_cast(signatureBytes.constData()); const unsigned int signatureLength = signatureBytes.length(); @@ -1632,14 +1634,41 @@ bool EntityItem::verifyStaticCertificateProperties() { const auto text = reinterpret_cast(hash.constData()); const unsigned int textLength = hash.length(); - BIO *bio = BIO_new_mem_buf((void*)qPrintable(EntityItem::_marketplacePublicKey), -1); + BIO *bio = BIO_new_mem_buf((void*)EntityItem::_marketplacePublicKey.toUtf8().constData(), -1); EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); - RSA* rsa = EVP_PKEY_get1_RSA(evp_key); - bool answer = RSA_verify(NID_sha256, text, textLength, signature, signatureLength, rsa); - BIO_free(bio); - RSA_free(rsa); - EVP_PKEY_free(evp_key); - return answer; + if (evp_key) { + RSA* rsa = EVP_PKEY_get1_RSA(evp_key); + if (rsa) { + bool answer = RSA_verify(NID_sha256, text, textLength, signature, signatureLength, rsa); + RSA_free(rsa); + if (bio) { + BIO_free(bio); + } + if (evp_key) { + EVP_PKEY_free(evp_key); + } + return answer; + } else { + if (bio) { + BIO_free(bio); + } + if (evp_key) { + EVP_PKEY_free(evp_key); + } + long error = ERR_get_error(); + const char* error_str = ERR_error_string(error, NULL); + qCWarning(entities) << "Failed to verify static certificate properties! RSA error:" << error_str; + return false; + } + } else { + if (bio) { + BIO_free(bio); + } + long error = ERR_get_error(); + const char* error_str = ERR_error_string(error, NULL); + qCWarning(entities) << "Failed to verify static certificate properties! RSA error:" << error_str; + return false; + } } void EntityItem::adjustShapeInfoByRegistration(ShapeInfo& info) const { @@ -2975,32 +3004,41 @@ void EntityItem::somethingChangedNotification() { } void EntityItem::retrieveMarketplacePublicKey() { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest; - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; - requestURL.setPath("/api/v1/commerce/marketplace_key"); - QJsonObject request; - networkRequest.setUrl(requestURL); + EntityItem::_marketplacePublicKey = "-----BEGIN PUBLIC KEY-----\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyNNpKuspPe9D8jbzrX5k\n" + "dyl7HvEGHzbXS2ydi0qUApyVZoPsmdx4vtpx6XgwxY8+9X/CDBIIWT2DnfOSzeOQ\n" + "D3zdcK2ro0HRiWuCzGKp9BM2GppkoiCZaJjpCiM7XOrBuI5OHp+5csb21nJs/Djo\n" + "a6eCj3NlkJEjR2SQepPU89dKbS13g6B5uxH7IgerPmJmsCTEmst87AMGJU0SWyiA\n" + "0DSzom/QDODGYAwmC9+++l+xD7pm/zT2NHRom0tbr6Il51PSAcnmxHOcdxuJeRN7\n" + "9ep9dg0aTpKBvVbef9WGWj2QgdQ8qR+b9zoiWDq5vlgeLH2WH/AcDAIyyTr/ydeo\n" + "CQIDAQAB\n" + "-----END PUBLIC KEY-----\n"; + //QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + //QNetworkRequest networkRequest; + //networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + //QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + //requestURL.setPath("/api/v1/commerce/marketplace_key"); + //QJsonObject request; + //networkRequest.setUrl(requestURL); - QNetworkReply* networkReply = NULL; - networkReply = networkAccessManager.get(networkRequest); + //QNetworkReply* networkReply = NULL; + //networkReply = networkAccessManager.get(networkRequest); - connect(networkReply, &QNetworkReply::finished, [=]() { - QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - jsonObject = jsonObject["data"].toObject(); + //connect(networkReply, &QNetworkReply::finished, [=]() { + // QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + // jsonObject = jsonObject["data"].toObject(); - if (networkReply->error() == QNetworkReply::NoError) { - if (!jsonObject["public_key"].toString().isEmpty()) { - EntityItem::_marketplacePublicKey = jsonObject["public_key"].toString(); - qCWarning(entities) << "Marketplace public key has been set to" << _marketplacePublicKey; - } else { - qCWarning(entities) << "Marketplace public key is empty!"; - } - } else { - qCWarning(entities) << "Call to" << networkRequest.url() << "failed! Error:" << networkReply->error(); - } + // if (networkReply->error() == QNetworkReply::NoError) { + // if (!jsonObject["public_key"].toString().isEmpty()) { + // EntityItem::_marketplacePublicKey = jsonObject["public_key"].toString(); + // qCWarning(entities) << "Marketplace public key has been set to" << _marketplacePublicKey; + // } else { + // qCWarning(entities) << "Marketplace public key is empty!"; + // } + // } else { + // qCWarning(entities) << "Call to" << networkRequest.url() << "failed! Error:" << networkReply->error(); + // } - networkReply->deleteLater(); - }); + // networkReply->deleteLater(); + //}); } From 9e68e805a0401d744563e4f48c319b677bd7e9a2 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 20 Oct 2017 10:38:59 -0700 Subject: [PATCH 42/49] Get marketplace public key from backend --- libraries/entities/src/EntityItem.cpp | 57 +++++++++++---------------- 1 file changed, 24 insertions(+), 33 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 94d9a08252..15ef5295e0 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -3004,41 +3004,32 @@ void EntityItem::somethingChangedNotification() { } void EntityItem::retrieveMarketplacePublicKey() { - EntityItem::_marketplacePublicKey = "-----BEGIN PUBLIC KEY-----\n" - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyNNpKuspPe9D8jbzrX5k\n" - "dyl7HvEGHzbXS2ydi0qUApyVZoPsmdx4vtpx6XgwxY8+9X/CDBIIWT2DnfOSzeOQ\n" - "D3zdcK2ro0HRiWuCzGKp9BM2GppkoiCZaJjpCiM7XOrBuI5OHp+5csb21nJs/Djo\n" - "a6eCj3NlkJEjR2SQepPU89dKbS13g6B5uxH7IgerPmJmsCTEmst87AMGJU0SWyiA\n" - "0DSzom/QDODGYAwmC9+++l+xD7pm/zT2NHRom0tbr6Il51PSAcnmxHOcdxuJeRN7\n" - "9ep9dg0aTpKBvVbef9WGWj2QgdQ8qR+b9zoiWDq5vlgeLH2WH/AcDAIyyTr/ydeo\n" - "CQIDAQAB\n" - "-----END PUBLIC KEY-----\n"; - //QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - //QNetworkRequest networkRequest; - //networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - //QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; - //requestURL.setPath("/api/v1/commerce/marketplace_key"); - //QJsonObject request; - //networkRequest.setUrl(requestURL); + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; + requestURL.setPath("/api/v1/commerce/marketplace_key"); + QJsonObject request; + networkRequest.setUrl(requestURL); - //QNetworkReply* networkReply = NULL; - //networkReply = networkAccessManager.get(networkRequest); + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.get(networkRequest); - //connect(networkReply, &QNetworkReply::finished, [=]() { - // QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - // jsonObject = jsonObject["data"].toObject(); + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); - // if (networkReply->error() == QNetworkReply::NoError) { - // if (!jsonObject["public_key"].toString().isEmpty()) { - // EntityItem::_marketplacePublicKey = jsonObject["public_key"].toString(); - // qCWarning(entities) << "Marketplace public key has been set to" << _marketplacePublicKey; - // } else { - // qCWarning(entities) << "Marketplace public key is empty!"; - // } - // } else { - // qCWarning(entities) << "Call to" << networkRequest.url() << "failed! Error:" << networkReply->error(); - // } + if (networkReply->error() == QNetworkReply::NoError) { + if (!jsonObject["public_key"].toString().isEmpty()) { + EntityItem::_marketplacePublicKey = jsonObject["public_key"].toString(); + qCWarning(entities) << "Marketplace public key has been set to" << _marketplacePublicKey; + } else { + qCWarning(entities) << "Marketplace public key is empty!"; + } + } else { + qCWarning(entities) << "Call to" << networkRequest.url() << "failed! Error:" << networkReply->error(); + } - // networkReply->deleteLater(); - //}); + networkReply->deleteLater(); + }); } From fbd393d918072cfeefe66330a83591225b146232 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 20 Oct 2017 15:10:24 -0700 Subject: [PATCH 43/49] Add newline to cert ID when updating location --- interface/src/commerce/Ledger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 904847cb5f..856ba68a2f 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -234,7 +234,7 @@ void Ledger::updateLocation(const QString& asset_id, const QString location, con QStringList keys = wallet->listPublicKeys(); QString key = keys[0]; QJsonObject transaction; - transaction["certificate_id"] = asset_id; + transaction["certificate_id"] = asset_id + "\n"; transaction["place_name"] = location; QJsonDocument transactionDoc{ transaction }; auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); From 53081b51a6b0e530a57e73f7ec477a9368759885 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 23 Oct 2017 13:42:25 -0700 Subject: [PATCH 44/49] Clean up as much as possible --- .../src/entities/EntityServer.cpp | 2 +- libraries/entities/src/EntityItem.cpp | 37 ++++++++++++++----- libraries/entities/src/EntityTree.cpp | 2 +- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 1351220714..129c3600c7 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -492,7 +492,7 @@ void EntityServer::startDynamicDomainVerification() { qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value(); } } else { - qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << i.value(); + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << i.value(); tree->deleteEntity(i.value(), true); } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 15ef5295e0..2239ee03b6 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1626,20 +1626,39 @@ bool EntityItem::verifyStaticCertificateProperties() { return false; } - const auto signatureBytes = QByteArray::fromBase64(getCertificateID().toLatin1()); - const auto signature = reinterpret_cast(signatureBytes.constData()); - const unsigned int signatureLength = signatureBytes.length(); + const QByteArray marketplacePublicKeyByteArray = EntityItem::_marketplacePublicKey.toUtf8(); + const unsigned char* marketplacePublicKey = reinterpret_cast(marketplacePublicKeyByteArray.constData()); + int marketplacePublicKeyLength = marketplacePublicKeyByteArray.length(); - const auto hash = getStaticCertificateHash(); - const auto text = reinterpret_cast(hash.constData()); - const unsigned int textLength = hash.length(); - - BIO *bio = BIO_new_mem_buf((void*)EntityItem::_marketplacePublicKey.toUtf8().constData(), -1); + BIO *bio = BIO_new_mem_buf((void*)marketplacePublicKey, marketplacePublicKeyLength); EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); if (evp_key) { RSA* rsa = EVP_PKEY_get1_RSA(evp_key); if (rsa) { - bool answer = RSA_verify(NID_sha256, text, textLength, signature, signatureLength, rsa); + const QByteArray digestByteArray = getStaticCertificateHash(); + const unsigned char* digest = reinterpret_cast(digestByteArray.constData()); + int digestLength = digestByteArray.length(); + + const QByteArray signatureByteArray = QByteArray::fromBase64(getCertificateID().toUtf8()); + const unsigned char* signature = reinterpret_cast(signatureByteArray.constData()); + int signatureLength = signatureByteArray.length(); + + ERR_clear_error(); + bool answer = RSA_verify(NID_sha256, + digest, + digestLength, + signature, + signatureLength, + rsa); + long error = ERR_get_error(); + if (error != 0) { + const char* error_str = ERR_error_string(error, NULL); + qCWarning(entities) << "ERROR while verifying static certificate properties! RSA error:" << error_str + << "\nStatic Cert JSON:" << getStaticCertificateJSON() + << "\nKey:" << EntityItem::_marketplacePublicKey << "\nKey Length:" << marketplacePublicKeyLength + << "\nDigest:" << digest << "\nDigest Length:" << digestLength + << "\nSignature:" << signature << "\nSignature Length:" << signatureLength; + } RSA_free(rsa); if (bio) { BIO_free(bio); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index bca921fe0f..463eae7fd0 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1307,7 +1307,7 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt } } } else { - qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityItemID; deleteEntity(entityItemID, true); } From 97d44e62c87747e173786a98e429f242e2ee3255 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 23 Oct 2017 16:52:32 -0700 Subject: [PATCH 45/49] Fix bugs --- assignment-client/src/entities/EntityServer.cpp | 7 ++++--- libraries/entities/src/EntityTree.cpp | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 129c3600c7..7032668d05 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -446,7 +446,7 @@ void EntityServer::domainSettingsRequestFailed() { void EntityServer::startDynamicDomainVerification() { qCDebug(entities) << "Starting Dynamic Domain Verification..."; - QString thisDomainID = DependencyManager::get()->getDomainId(); + QString thisDomainID = DependencyManager::get()->getDomainId().remove(QRegExp("\\{|\\}")); EntityTreePointer tree = std::static_pointer_cast(_tree); QHash localMap(tree->getEntityCertificateIDMap()); @@ -473,7 +473,7 @@ void EntityServer::startDynamicDomainVerification() { QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); QJsonObject request; - request["certificate_id"] = i.key(); + request["certificate_id"] = i.key() + "\n"; networkRequest.setUrl(requestURL); QNetworkReply* networkReply = NULL; @@ -492,7 +492,8 @@ void EntityServer::startDynamicDomainVerification() { qCDebug(entities) << "Entity passed dynamic domain verification:" << i.value(); } } else { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << i.value(); + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << i.value() + << "More info:" << jsonObject; tree->deleteEntity(i.value(), true); } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 463eae7fd0..510512b72c 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1244,7 +1244,7 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); QJsonObject request; - request["certificate_id"] = certID; + request["certificate_id"] = certID + "\n"; networkRequest.setUrl(requestURL); QNetworkReply* networkReply = NULL; @@ -1307,7 +1307,8 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt } } } else { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityItemID; + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityItemID + << "More info:" << jsonObject; deleteEntity(entityItemID, true); } From 3e139283ba03247fbd5d2a11e83fa60b070b20dc Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 23 Oct 2017 17:31:21 -0700 Subject: [PATCH 46/49] Remove unnecessary newlines --- assignment-client/src/entities/EntityServer.cpp | 2 +- interface/src/commerce/Ledger.cpp | 2 +- libraries/entities/src/EntityTree.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 7032668d05..fa9c73b12d 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -473,7 +473,7 @@ void EntityServer::startDynamicDomainVerification() { QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location"); QJsonObject request; - request["certificate_id"] = i.key() + "\n"; + request["certificate_id"] = i.key(); networkRequest.setUrl(requestURL); QNetworkReply* networkReply = NULL; diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 856ba68a2f..904847cb5f 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -234,7 +234,7 @@ void Ledger::updateLocation(const QString& asset_id, const QString location, con QStringList keys = wallet->listPublicKeys(); QString key = keys[0]; QJsonObject transaction; - transaction["certificate_id"] = asset_id + "\n"; + transaction["certificate_id"] = asset_id; transaction["place_name"] = location; QJsonDocument transactionDoc{ transaction }; auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 510512b72c..483e2bfc46 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1244,7 +1244,7 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL; requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); QJsonObject request; - request["certificate_id"] = certID + "\n"; + request["certificate_id"] = certID; networkRequest.setUrl(requestURL); QNetworkReply* networkReply = NULL; From 2f28cf1443cceef77cac4178514871f6fd57622a Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 25 Oct 2017 15:20:31 -0700 Subject: [PATCH 47/49] Remove unnecessary newline --- scripts/system/marketplaces/marketplaces.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index 3bf6435a25..7dc41abcd3 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -135,7 +135,7 @@ function setCertificateInfo(currentEntityWithContextOverlay, itemCertificateId) { wireEventBridge(true); - var certificateId = itemCertificateId || (Entities.getEntityProperties(currentEntityWithContextOverlay, ['certificateID']).certificateID + "\n"); + var certificateId = itemCertificateId || (Entities.getEntityProperties(currentEntityWithContextOverlay, ['certificateID']).certificateID); tablet.sendToQml({ method: 'inspectionCertificate_setCertificateId', certificateId: certificateId From 8cb60fc62c259b3605446c9ae0ec47f2ab6b58c5 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 26 Oct 2017 16:44:21 -0700 Subject: [PATCH 48/49] Small edit.js changes --- scripts/system/edit.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index ef1f2049bb..a3ca1bc5dc 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -415,7 +415,7 @@ var toolBar = (function () { } }); - var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp()); + var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp() || Entities.canRezCertified() || Entities.canRezTmpCertified()); var createButtonIconRsrc = (hasRezPermissions ? CREATE_ENABLED_ICON : CREATE_DISABLED_ICON); tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); activeButton = tablet.addButton({ @@ -434,7 +434,7 @@ var toolBar = (function () { tablet.fromQml.connect(fromQml); createButton.clicked.connect(function() { - if ( ! (Entities.canRez() || Entities.canRezTmp()) ) { + if ( ! (Entities.canRez() || Entities.canRezTmp() || Entities.canRezCertified() || Entities.canRezTmpCertified()) ) { Window.notifyEditError(INSUFFICIENT_PERMISSIONS_ERROR_MSG); return; } @@ -634,7 +634,7 @@ var toolBar = (function () { if (active === isActive) { return; } - if (active && !Entities.canRez() && !Entities.canRezTmp()) { + if (active && !Entities.canRez() && !Entities.canRezTmp() && !Entities.canRezCertified() && !Entities.canRezTmpCertified()) { Window.notifyEditError(INSUFFICIENT_PERMISSIONS_ERROR_MSG); return; } @@ -789,7 +789,7 @@ function handleDomainChange() { return; } - var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp()); + var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp() || Entities.canRezCertified() || Entities.canRezTmpCertified()); createButton.editProperties({ icon: (hasRezPermissions ? CREATE_ENABLED_ICON : CREATE_DISABLED_ICON), captionColorOverride: (hasRezPermissions ? "" : "#888888"), @@ -1491,7 +1491,7 @@ function onFileOpenChanged(filename) { } } if (importURL) { - if (!isActive && (Entities.canRez() && Entities.canRezTmp())) { + if (!isActive && (Entities.canRez() && Entities.canRezTmp() && Entities.canRezCertified() && Entities.canRezTmpCertified())) { toolBar.toggle(); } importSVO(importURL); @@ -1501,7 +1501,7 @@ function onFileOpenChanged(filename) { function onPromptTextChanged(prompt) { Window.promptTextChanged.disconnect(onPromptTextChanged); if (prompt !== "") { - if (!isActive && (Entities.canRez() && Entities.canRezTmp())) { + if (!isActive && (Entities.canRez() && Entities.canRezTmp() && Entities.canRezCertified() && Entities.canRezTmpCertified())) { toolBar.toggle(); } importSVO(prompt); From 9481824d0ab11cd35b8f9da991e99af367730e0e Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 26 Oct 2017 17:02:09 -0700 Subject: [PATCH 49/49] Fix enabled status of wear button --- interface/resources/qml/hifi/commerce/checkout/Checkout.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml index 8ea9ce494c..dfe0c319e5 100644 --- a/interface/resources/qml/hifi/commerce/checkout/Checkout.qml +++ b/interface/resources/qml/hifi/commerce/checkout/Checkout.qml @@ -585,7 +585,7 @@ Rectangle { // "Rez" button HifiControlsUit.Button { id: rezNowButton; - enabled: root.canRezCertifiedItems; + enabled: root.canRezCertifiedItems || root.isWearable; buttonGlyph: hifi.glyphs.lightning; color: hifi.buttons.red; colorScheme: hifi.colorSchemes.light; @@ -634,7 +634,7 @@ Rectangle { } RalewaySemiBold { id: explainRezText; - //visible: !root.isWearable; + visible: !root.isWearable; text: 'What does "Rez" mean?' // Text size size: 16;