Persist in checking owner and sending challenges for failures

This commit is contained in:
Simon Walton 2019-10-17 17:29:14 -07:00
parent 95f00e2caa
commit 6bcb9f64f7
2 changed files with 69 additions and 30 deletions

View file

@ -32,24 +32,41 @@ MixerAvatar::MixerAvatar() {
_challengeTimer.setSingleShot(true); _challengeTimer.setSingleShot(true);
_challengeTimer.setInterval(CHALLENGE_TIMEOUT_MS); _challengeTimer.setInterval(CHALLENGE_TIMEOUT_MS);
_challengeTimer.callOnTimeout(this, &MixerAvatar::challengeTimeout);
_challengeTimer.callOnTimeout(this, [this]() { // QTimer::start is a set of overloaded functions.
if (_verifyState == challengeClient) { connect(this, &MixerAvatar::startChallengeTimer, &_challengeTimer, static_cast<void(QTimer::*)()>(&QTimer::start));
_pendingEvent = false;
_verifyState = verificationFailed;
_needsIdentityUpdate = true;
qCDebug(avatars) << "Dynamic verification TIMED-OUT for" << getDisplayName() << getSessionUUID();
} else {
qCDebug(avatars) << "Ignoring timeout of avatar challenge";
}
});
} }
const char* MixerAvatar::stateToName(VerifyState state) { const char* MixerAvatar::stateToName(VerifyState state) {
return QMetaEnum::fromType<VerifyState>().valueToKey(state); return QMetaEnum::fromType<VerifyState>().valueToKey(state);
} }
void MixerAvatar::challengeTimeout() {
switch (_verifyState) {
case challengeClient:
_verifyState = staticValidation;
_pendingEvent = true;
if (++_numberChallenges < NUM_CHALLENGES_BEFORE_FAIL) {
qCDebug(avatars) << "Retrying (" << _numberChallenges << ") timed-out challenge for" << getDisplayName()
<< getSessionUUID();
} else {
_certifyFailed = true;
_needsIdentityUpdate = true;
qCDebug(avatars) << "Dynamic verification TIMED-OUT for" << getDisplayName() << getSessionUUID();
}
break;
case verificationFailed:
qCDebug(avatars) << "Retrying failed challenge for" << getDisplayName() << getSessionUUID();
_verifyState = staticValidation;
_pendingEvent = true;
break;
default:
qCDebug(avatars) << "Ignoring timeout of avatar challenge";
}
}
void MixerAvatar::fetchAvatarFST() { void MixerAvatar::fetchAvatarFST() {
if (_verifyState >= requestingFST && _verifyState <= challengeClient) { if (_verifyState >= requestingFST && _verifyState <= challengeClient) {
qCDebug(avatars) << "WARNING: Avatar verification restarted; old state:" << stateToName(_verifyState); qCDebug(avatars) << "WARNING: Avatar verification restarted; old state:" << stateToName(_verifyState);
@ -210,6 +227,23 @@ void MixerAvatar::ownerRequestComplete() {
networkReply->deleteLater(); networkReply->deleteLater();
} }
void MixerAvatar::requestCurrentOwnership() {
// Get registered owner's public key from metaverse.
static const QString POP_MARKETPLACE_API { "/api/v1/commerce/proof_of_purchase_status/transfer" };
auto& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
requestURL.setPath(POP_MARKETPLACE_API);
networkRequest.setUrl(requestURL);
QJsonObject request;
request["certificate_id"] = _certificateIdFromFST;
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, this, &MixerAvatar::ownerRequestComplete);
}
void MixerAvatar::processCertifyEvents() { void MixerAvatar::processCertifyEvents() {
if (!_pendingEvent) { if (!_pendingEvent) {
return; return;
@ -221,26 +255,15 @@ void MixerAvatar::processCertifyEvents() {
case receivedFST: case receivedFST:
{ {
generateFSTHash(); generateFSTHash();
_numberChallenges = 0;
if (_certificateIdFromFST.length() != 0) { if (_certificateIdFromFST.length() != 0) {
QString& marketplacePublicKey = EntityItem::_marketplacePublicKey; QString& marketplacePublicKey = EntityItem::_marketplacePublicKey;
bool staticVerification = validateFSTHash(marketplacePublicKey); bool staticVerification = validateFSTHash(marketplacePublicKey);
_verifyState = staticVerification ? staticValidation : verificationFailed; _verifyState = staticVerification ? staticValidation : verificationFailed;
if (_verifyState == staticValidation) { if (_verifyState == staticValidation) {
static const QString POP_MARKETPLACE_API { "/api/v1/commerce/proof_of_purchase_status/transfer" }; requestCurrentOwnership();
auto& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
requestURL.setPath(POP_MARKETPLACE_API);
networkRequest.setUrl(requestURL);
QJsonObject request;
request["certificate_id"] = _certificateIdFromFST;
_verifyState = requestingOwner; _verifyState = requestingOwner;
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, this, &MixerAvatar::ownerRequestComplete);
} else { } else {
_needsIdentityUpdate = true; _needsIdentityUpdate = true;
_pendingEvent = false; _pendingEvent = false;
@ -253,6 +276,13 @@ void MixerAvatar::processCertifyEvents() {
break; break;
} }
case staticValidation:
{
requestCurrentOwnership();
_verifyState = requestingOwner;
break;
}
case ownerResponse: case ownerResponse:
{ {
QJsonDocument responseJson = QJsonDocument::fromJson(_dynamicMarketResponse.toUtf8()); QJsonDocument responseJson = QJsonDocument::fromJson(_dynamicMarketResponse.toUtf8());
@ -325,8 +355,7 @@ void MixerAvatar::sendOwnerChallenge() {
_challengeNonceHash = nonceHash.result(); _challengeNonceHash = nonceHash.result();
_pendingEvent = false; _pendingEvent = false;
// QTimer::start is a set of overloaded functions. emit startChallengeTimer();
QMetaObject::invokeMethod(&_challengeTimer, static_cast<void(QTimer::*)()>(&QTimer::start));
} }
void MixerAvatar::processChallengeResponse(ReceivedMessage& response) { void MixerAvatar::processChallengeResponse(ReceivedMessage& response) {
@ -337,7 +366,7 @@ void MixerAvatar::processChallengeResponse(ReceivedMessage& response) {
QByteArray responseData = response.readAll(); QByteArray responseData = response.readAll();
if (responseData.length() < 8) { if (responseData.length() < 8) {
_verifyState = error; _verifyState = error;
qCDebug(avatars) << "Avatar challenge response packet too small, length:" << responseData.length(); qCWarning(avatars) << "ALERT: Avatar challenge response packet too small, length:" << responseData.length();
return; return;
} }
@ -354,9 +383,11 @@ void MixerAvatar::processChallengeResponse(ReceivedMessage& response) {
bool challengeResult = EntityItemProperties::verifySignature(_ownerPublicKey, _challengeNonceHash, bool challengeResult = EntityItemProperties::verifySignature(_ownerPublicKey, _challengeNonceHash,
QByteArray::fromBase64(signedNonce)); QByteArray::fromBase64(signedNonce));
_verifyState = challengeResult ? verificationSucceeded : verificationFailed; _verifyState = challengeResult ? verificationSucceeded : verificationFailed;
_certifyFailed = !challengeResult;
_needsIdentityUpdate = true; _needsIdentityUpdate = true;
if (_verifyState == verificationFailed) { if (_certifyFailed) {
qCDebug(avatars) << "Dynamic verification FAILED for" << getDisplayName() << getSessionUUID(); qCDebug(avatars) << "Dynamic verification FAILED for" << getDisplayName() << getSessionUUID();
emit startChallengeTimer();
} else { } else {
qCDebug(avatars) << "Dynamic verification SUCCEEDED for" << getDisplayName() << getSessionUUID(); qCDebug(avatars) << "Dynamic verification SUCCEEDED for" << getDisplayName() << getSessionUUID();
} }

View file

@ -27,7 +27,7 @@ public:
void setNeedsHeroCheck(bool needsHeroCheck = true) { _needsHeroCheck = needsHeroCheck; } void setNeedsHeroCheck(bool needsHeroCheck = true) { _needsHeroCheck = needsHeroCheck; }
void fetchAvatarFST(); void fetchAvatarFST();
virtual bool isCertifyFailed() const override { return _verifyState == verificationFailed; } virtual bool isCertifyFailed() const override { return _certifyFailed; }
bool needsIdentityUpdate() const { return _needsIdentityUpdate; } bool needsIdentityUpdate() const { return _needsIdentityUpdate; }
void setNeedsIdentityUpdate(bool value = true) { _needsIdentityUpdate = value; } void setNeedsIdentityUpdate(bool value = true) { _needsIdentityUpdate = value; }
@ -60,11 +60,15 @@ private:
QString _ownerPublicKey; QString _ownerPublicKey;
QByteArray _challengeNonceHash; QByteArray _challengeNonceHash;
QTimer _challengeTimer; QTimer _challengeTimer;
static constexpr int NUM_CHALLENGES_BEFORE_FAIL = 1;
int _numberChallenges { 0 };
bool _certifyFailed { false };
bool _needsIdentityUpdate { false }; bool _needsIdentityUpdate { false };
bool generateFSTHash(); bool generateFSTHash();
bool validateFSTHash(const QString& publicKey) const; bool validateFSTHash(const QString& publicKey) const;
QByteArray canonicalJson(const QString fstFile); QByteArray canonicalJson(const QString fstFile);
void requestCurrentOwnership();
void sendOwnerChallenge(); void sendOwnerChallenge();
static const QString VERIFY_FAIL_MODEL; static const QString VERIFY_FAIL_MODEL;
@ -72,6 +76,10 @@ private:
private slots: private slots:
void fstRequestComplete(); void fstRequestComplete();
void ownerRequestComplete(); void ownerRequestComplete();
void challengeTimeout();
signals:
void startChallengeTimer();
}; };
using MixerAvatarSharedPointer = std::shared_ptr<MixerAvatar>; using MixerAvatarSharedPointer = std::shared_ptr<MixerAvatar>;