From 8a1fdf3486a65e554f0cf692b75c073ffd4339fd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 May 2014 14:38:51 -0700 Subject: [PATCH 01/14] pass wallet UUID from AC to DS, cleanup noisy timer --- assignment-client/src/AssignmentClient.cpp | 11 ++++++++ domain-server/src/DomainServer.cpp | 32 ++++++++++++---------- libraries/networking/src/Assignment.cpp | 16 +++++++++-- libraries/networking/src/Assignment.h | 4 +++ 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index b3a92f2f54..009bd42e88 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -67,9 +67,20 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) : if (argumentIndex != -1) { assignmentPool = argumentList[argumentIndex + 1]; } + // setup our _requestAssignment member variable from the passed arguments _requestAssignment = Assignment(Assignment::RequestCommand, requestAssignmentType, assignmentPool); + // check if we were passed a wallet UUID on the command line + // this would represent where the user running AC wants funds sent to + + const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "--wallet"; + if ((argumentIndex = argumentList.indexOf(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION)) != -1) { + QUuid walletUUID = QString(argumentList[argumentIndex + 1]); + qDebug() << "The destination wallet UUID for credits is" << uuidStringWithoutCurlyBraces(walletUUID); + _requestAssignment.setWalletUUID(walletUUID); + } + // create a NodeList as an unassigned client NodeList* nodeList = NodeList::createInstance(NodeType::Unassigned); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index f6af63fd7a..bcce081d94 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -564,14 +564,21 @@ void DomainServer::readAvailableDatagrams() { Assignment requestAssignment(receivedPacket); // Suppress these for Assignment::AgentType to once per 5 seconds - static quint64 lastNoisyMessage = usecTimestampNow(); - quint64 timeNow = usecTimestampNow(); - const quint64 NOISY_TIME_ELAPSED = 5 * USECS_PER_SECOND; - bool noisyMessage = false; - if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastNoisyMessage) > NOISY_TIME_ELAPSED) { + static QElapsedTimer noisyMessageTimer; + static bool wasNoisyTimerStarted = false; + + if (!wasNoisyTimerStarted) { + noisyMessageTimer.start(); + wasNoisyTimerStarted = true; + } + + const quint64 NOISY_MESSAGE_INTERVAL_MSECS = 5 * 1000; + + if (requestAssignment.getType() != Assignment::AgentType + || noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) { qDebug() << "Received a request for assignment type" << requestAssignment.getType() - << "from" << senderSockAddr; - noisyMessage = true; + << "from" << senderSockAddr; + noisyMessageTimer.restart(); } SharedAssignmentPointer assignmentToDeploy = deployableAssignmentForRequest(requestAssignment); @@ -589,16 +596,13 @@ void DomainServer::readAvailableDatagrams() { nodeList->getNodeSocket().writeDatagram(assignmentPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); } else { - if (requestAssignment.getType() != Assignment::AgentType || (timeNow - lastNoisyMessage) > NOISY_TIME_ELAPSED) { + if (requestAssignment.getType() != Assignment::AgentType + || noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) { qDebug() << "Unable to fulfill assignment request of type" << requestAssignment.getType() - << "from" << senderSockAddr; - noisyMessage = true; + << "from" << senderSockAddr; + noisyMessageTimer.restart(); } } - - if (noisyMessage) { - lastNoisyMessage = timeNow; - } } else if (!_isUsingDTLS) { // not using DTLS, process datagram normally processDatagram(receivedPacket, senderSockAddr); diff --git a/libraries/networking/src/Assignment.cpp b/libraries/networking/src/Assignment.cpp index 925ed2930f..fd6ecd4602 100644 --- a/libraries/networking/src/Assignment.cpp +++ b/libraries/networking/src/Assignment.cpp @@ -63,7 +63,8 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const _pool(pool), _location(location), _payload(), - _isStatic(false) + _isStatic(false), + _walletUUID() { if (_command == Assignment::CreateCommand) { // this is a newly created assignment, generate a random UUID @@ -74,7 +75,8 @@ Assignment::Assignment(Assignment::Command command, Assignment::Type type, const Assignment::Assignment(const QByteArray& packet) : _pool(), _location(GlobalLocation), - _payload() + _payload(), + _walletUUID() { PacketType packetType = packetTypeForPacket(packet); @@ -104,6 +106,7 @@ Assignment::Assignment(const Assignment& otherAssignment) { _location = otherAssignment._location; _pool = otherAssignment._pool; _payload = otherAssignment._payload; + _walletUUID = otherAssignment._walletUUID; } Assignment& Assignment::operator=(const Assignment& rhsAssignment) { @@ -121,6 +124,7 @@ void Assignment::swap(Assignment& otherAssignment) { swap(_location, otherAssignment._location); swap(_pool, otherAssignment._pool); swap(_payload, otherAssignment._payload); + swap(_walletUUID, otherAssignment._walletUUID); } const char* Assignment::getTypeName() const { @@ -156,6 +160,10 @@ QDebug operator<<(QDebug debug, const Assignment &assignment) { QDataStream& operator<<(QDataStream &out, const Assignment& assignment) { out << (quint8) assignment._type << assignment._uuid << assignment._pool << assignment._payload; + if (assignment._command == Assignment::RequestCommand) { + out << assignment._walletUUID; + } + return out; } @@ -166,5 +174,9 @@ QDataStream& operator>>(QDataStream &in, Assignment& assignment) { in >> assignment._uuid >> assignment._pool >> assignment._payload; + if (assignment._command == Assignment::RequestCommand) { + in >> assignment._walletUUID; + } + return in; } diff --git a/libraries/networking/src/Assignment.h b/libraries/networking/src/Assignment.h index 1d97b08bb8..3898b84787 100644 --- a/libraries/networking/src/Assignment.h +++ b/libraries/networking/src/Assignment.h @@ -81,6 +81,9 @@ public: void setIsStatic(bool isStatic) { _isStatic = isStatic; } bool isStatic() const { return _isStatic; } + void setWalletUUID(const QUuid& walletUUID) { _walletUUID = walletUUID; } + const QUuid& getWalletUUID() const { return _walletUUID; } + const char* getTypeName() const; // implement parseData to return 0 so we can be a subclass of NodeData @@ -98,6 +101,7 @@ protected: Assignment::Location _location; /// the location of the assignment, allows a domain to preferentially use local ACs QByteArray _payload; /// an optional payload attached to this assignment, a maximum for 1024 bytes will be packed bool _isStatic; /// defines if this assignment needs to be re-queued in the domain-server if it stops being fulfilled + QUuid _walletUUID; /// the UUID for the wallet that should be paid for this assignment }; #endif // hifi_Assignment_h From b42e005cdbc3b205e460967493fd93e438d4555b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 May 2014 15:28:45 -0700 Subject: [PATCH 02/14] add pending assignment purgatory for assignees --- domain-server/src/DomainServer.cpp | 48 ++++++++++++++----- domain-server/src/DomainServer.h | 3 ++ domain-server/src/PendingAssignedNodeData.cpp | 19 ++++++++ domain-server/src/PendingAssignedNodeData.h | 33 +++++++++++++ 4 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 domain-server/src/PendingAssignedNodeData.cpp create mode 100644 domain-server/src/PendingAssignedNodeData.h diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index bcce081d94..816e014c23 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -35,6 +35,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _httpsManager(NULL), _allAssignments(), _unfulfilledAssignments(), + _pendingAssignedNodes(), _isUsingDTLS(false), _oauthProviderURL(), _oauthClientID(), @@ -339,9 +340,9 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock QUuid packetUUID = uuidFromPacketHeader(packet); // check if this connect request matches an assignment in the queue - bool isFulfilledOrUnfulfilledAssignment = _allAssignments.contains(packetUUID); + bool isAssignment = _pendingAssignedNodes.contains(packetUUID); SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer(); - if (isFulfilledOrUnfulfilledAssignment) { + if (isAssignment) { matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(packetUUID, nodeType); } @@ -371,8 +372,8 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock } } - if ((!isFulfilledOrUnfulfilledAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) - || (isFulfilledOrUnfulfilledAssignment && matchingQueuedAssignment)) { + if ((!isAssignment && !STATICALLY_ASSIGNED_NODES.contains(nodeType)) + || (isAssignment && matchingQueuedAssignment)) { // this was either not a static assignment or it was and we had a matching one in the queue // create a new session UUID for this node @@ -384,8 +385,8 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock // if this was a static assignment set the UUID, set the sendingSockAddr DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); - if (isFulfilledOrUnfulfilledAssignment) { - nodeData->setAssignmentUUID(packetUUID); + if (isAssignment) { + nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID()); } nodeData->setSendingSockAddr(senderSockAddr); @@ -589,12 +590,21 @@ void DomainServer::readAvailableDatagrams() { // give this assignment out, either the type matches or the requestor said they will take any assignmentPacket.resize(numAssignmentPacketHeaderBytes); + // setup a copy of this assignment that will have a unique UUID, for packaging purposes + Assignment uniqueAssignment(*assignmentToDeploy.data()); + uniqueAssignment.setUUID(QUuid::createUuid()); + QDataStream assignmentStream(&assignmentPacket, QIODevice::Append); - assignmentStream << *assignmentToDeploy.data(); + assignmentStream << uniqueAssignment; nodeList->getNodeSocket().writeDatagram(assignmentPacket, senderSockAddr.getAddress(), senderSockAddr.getPort()); + + // add the information for that deployed assignment to the hash of pending assigned nodes + PendingAssignedNodeData* pendingNodeData = new PendingAssignedNodeData(assignmentToDeploy->getUUID(), + requestAssignment.getWalletUUID()); + _pendingAssignedNodes.insert(uniqueAssignment.getUUID(), pendingNodeData); } else { if (requestAssignment.getType() != Assignment::AgentType || noisyMessageTimer.elapsed() > NOISY_MESSAGE_INTERVAL_MSECS) { @@ -1069,11 +1079,25 @@ void DomainServer::nodeKilled(SharedNodePointer node) { SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType) { QQueue::iterator i = _unfulfilledAssignments.begin(); - while (i != _unfulfilledAssignments.end()) { - if (i->data()->getType() == Assignment::typeForNodeType(nodeType) && i->data()->getUUID() == checkInUUID) { - return _unfulfilledAssignments.takeAt(i - _unfulfilledAssignments.begin()); - } else { - ++i; + PendingAssignedNodeData* pendingAssigneeData = _pendingAssignedNodes.take(checkInUUID); + + if (pendingAssigneeData) { + while (i != _unfulfilledAssignments.end()) { + if (i->data()->getType() == Assignment::typeForNodeType(nodeType) + && i->data()->getUUID() == pendingAssigneeData->getAssignmentUUID()) { + // we have an unfulfilled assignment to return + qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(checkInUUID) + << "matches unfulfilled assignment" + << uuidStringWithoutCurlyBraces(pendingAssigneeData->getAssignmentUUID()); + + // first clear the pending assignee data + delete pendingAssigneeData; + + // return the matching assignment + return _unfulfilledAssignments.takeAt(i - _unfulfilledAssignments.begin()); + } else { + ++i; + } } } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 71809d9e16..63a3afa13d 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -24,6 +24,8 @@ #include #include +#include "PendingAssignedNodeData.h" + typedef QSharedPointer SharedAssignmentPointer; class DomainServer : public QCoreApplication, public HTTPSRequestHandler { @@ -85,6 +87,7 @@ private: QHash _allAssignments; QQueue _unfulfilledAssignments; + QHash _pendingAssignedNodes; QVariantMap _argumentVariantMap; diff --git a/domain-server/src/PendingAssignedNodeData.cpp b/domain-server/src/PendingAssignedNodeData.cpp new file mode 100644 index 0000000000..21b3aa4ca4 --- /dev/null +++ b/domain-server/src/PendingAssignedNodeData.cpp @@ -0,0 +1,19 @@ +// +// PendingAssignedNodeData.cpp +// domain-server/src +// +// Created by Stephen Birarda on 2014-05-20. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "PendingAssignedNodeData.h" + +PendingAssignedNodeData::PendingAssignedNodeData(const QUuid& assignmentUUID, const QUuid& walletUUID) : + _assignmentUUID(assignmentUUID), + _walletUUID(walletUUID) +{ + +} \ No newline at end of file diff --git a/domain-server/src/PendingAssignedNodeData.h b/domain-server/src/PendingAssignedNodeData.h new file mode 100644 index 0000000000..93d99a6f8f --- /dev/null +++ b/domain-server/src/PendingAssignedNodeData.h @@ -0,0 +1,33 @@ +// +// PendingAssignedNodeData.h +// domain-server/src +// +// Created by Stephen Birarda on 2014-05-20. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_PendingAssignedNodeData_h +#define hifi_PendingAssignedNodeData_h + +#include +#include + +class PendingAssignedNodeData : public QObject { + Q_OBJECT +public: + PendingAssignedNodeData(const QUuid& assignmentUUID, const QUuid& walletUUID); + + void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } + const QUuid& getAssignmentUUID() const { return _assignmentUUID; } + + void setWalletUUID(const QUuid& walletUUID) { _walletUUID = walletUUID; } + const QUuid& getWalletUUID() const { return _walletUUID; } +private: + QUuid _assignmentUUID; + QUuid _walletUUID; +}; + +#endif // hifi_PendingAssignedNodeData_h \ No newline at end of file From 13077e48f85f5aad79368db6ba6ab590461e6c2f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 May 2014 15:45:08 -0700 Subject: [PATCH 03/14] handle types larger than 255 for packetVersionAndHashMatch --- libraries/networking/src/LimitedNodeList.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 5692023ab1..b5a23f6b99 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -152,10 +152,11 @@ void LimitedNodeList::changeSendSocketBufferSize(int numSendBytes) { bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) { PacketType checkType = packetTypeForPacket(packet); - if (packet[1] != versionForPacketType(checkType) + int numPacketTypeBytes = numBytesArithmeticCodingFromBuffer(packet.data()); + + if (packet[numPacketTypeBytes] != versionForPacketType(checkType) && checkType != PacketTypeStunResponse) { PacketType mismatchType = packetTypeForPacket(packet); - int numPacketTypeBytes = numBytesArithmeticCodingFromBuffer(packet.data()); static QMultiMap versionDebugSuppressMap; From 072343ce790dab3c362492bb27dc14b268ec4b8b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 May 2014 15:50:09 -0700 Subject: [PATCH 04/14] associate assignment wallet UUID with node via DomainServerNodeData --- domain-server/src/DomainServer.cpp | 47 ++++++++++++---------- domain-server/src/DomainServerNodeData.cpp | 1 + domain-server/src/DomainServerNodeData.h | 4 ++ 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 816e014c23..56d645ed24 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -342,8 +342,19 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock // check if this connect request matches an assignment in the queue bool isAssignment = _pendingAssignedNodes.contains(packetUUID); SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer(); + PendingAssignedNodeData* pendingAssigneeData = NULL; if (isAssignment) { - matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(packetUUID, nodeType); + pendingAssigneeData = _pendingAssignedNodes.take(packetUUID); + if (pendingAssigneeData) { + matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(packetUUID, nodeType); + + if (matchingQueuedAssignment) { + qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(packetUUID) + << "matches unfulfilled assignment" + << uuidStringWithoutCurlyBraces(matchingQueuedAssignment->getUUID()); + } + } + } if (!matchingQueuedAssignment && !_oauthProviderURL.isEmpty() && _argumentVariantMap.contains(ALLOWED_ROLES_CONFIG_KEY)) { @@ -387,6 +398,10 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock if (isAssignment) { nodeData->setAssignmentUUID(matchingQueuedAssignment->getUUID()); + nodeData->setWalletUUID(pendingAssigneeData->getWalletUUID()); + + // now that we've pulled the wallet UUID and added the node to our list, delete the pending assignee data + delete pendingAssigneeData; } nodeData->setSendingSockAddr(senderSockAddr); @@ -1076,28 +1091,18 @@ void DomainServer::nodeKilled(SharedNodePointer node) { } } -SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const QUuid& checkInUUID, NodeType_t nodeType) { +SharedAssignmentPointer DomainServer::matchingQueuedAssignmentForCheckIn(const QUuid& assignmentUUID, NodeType_t nodeType) { QQueue::iterator i = _unfulfilledAssignments.begin(); - PendingAssignedNodeData* pendingAssigneeData = _pendingAssignedNodes.take(checkInUUID); - - if (pendingAssigneeData) { - while (i != _unfulfilledAssignments.end()) { - if (i->data()->getType() == Assignment::typeForNodeType(nodeType) - && i->data()->getUUID() == pendingAssigneeData->getAssignmentUUID()) { - // we have an unfulfilled assignment to return - qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(checkInUUID) - << "matches unfulfilled assignment" - << uuidStringWithoutCurlyBraces(pendingAssigneeData->getAssignmentUUID()); - - // first clear the pending assignee data - delete pendingAssigneeData; - - // return the matching assignment - return _unfulfilledAssignments.takeAt(i - _unfulfilledAssignments.begin()); - } else { - ++i; - } + while (i != _unfulfilledAssignments.end()) { + if (i->data()->getType() == Assignment::typeForNodeType(nodeType) + && i->data()->getUUID() == assignmentUUID) { + // we have an unfulfilled assignment to return + + // return the matching assignment + return _unfulfilledAssignments.takeAt(i - _unfulfilledAssignments.begin()); + } else { + ++i; } } diff --git a/domain-server/src/DomainServerNodeData.cpp b/domain-server/src/DomainServerNodeData.cpp index a43e17ae60..bdf088c19e 100644 --- a/domain-server/src/DomainServerNodeData.cpp +++ b/domain-server/src/DomainServerNodeData.cpp @@ -20,6 +20,7 @@ DomainServerNodeData::DomainServerNodeData() : _sessionSecretHash(), _assignmentUUID(), + _walletUUID(), _statsJSONObject(), _sendingSockAddr(), _isAuthenticated(true) diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h index 011bee57c1..a8935bea00 100644 --- a/domain-server/src/DomainServerNodeData.h +++ b/domain-server/src/DomainServerNodeData.h @@ -30,6 +30,9 @@ public: void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } const QUuid& getAssignmentUUID() const { return _assignmentUUID; } + void setWalletUUID(const QUuid& walletUUID) { _walletUUID = walletUUID; } + const QUuid& getWalletUUID() const { return _walletUUID; } + void setSendingSockAddr(const HifiSockAddr& sendingSockAddr) { _sendingSockAddr = sendingSockAddr; } const HifiSockAddr& getSendingSockAddr() { return _sendingSockAddr; } @@ -42,6 +45,7 @@ private: QHash _sessionSecretHash; QUuid _assignmentUUID; + QUuid _walletUUID; QJsonObject _statsJSONObject; HifiSockAddr _sendingSockAddr; bool _isAuthenticated; From 8fb85110ac407d46720a2e72afd9c7169e2e5d78 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 May 2014 15:58:57 -0700 Subject: [PATCH 05/14] add a timer to initiate payment to assigned nodes --- domain-server/src/DomainServer.cpp | 23 ++++++++++++++++++++++- domain-server/src/DomainServer.h | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 56d645ed24..df946b8dcb 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -57,6 +57,13 @@ DomainServer::DomainServer(int argc, char* argv[]) : setupNodeListAndAssignments(); _networkAccessManager = new QNetworkAccessManager(this); + + // setup a timer to send transactions to pay assigned nodes every 30 seconds + QTimer* nodePaymentTimer = new QTimer(this); + connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::payAssignedNodes); + + const qint64 NODE_PAYMENT_INTERVAL_MSECS = 30 * 1000; + nodePaymentTimer->start(NODE_PAYMENT_INTERVAL_MSECS); } } @@ -343,10 +350,12 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock bool isAssignment = _pendingAssignedNodes.contains(packetUUID); SharedAssignmentPointer matchingQueuedAssignment = SharedAssignmentPointer(); PendingAssignedNodeData* pendingAssigneeData = NULL; + if (isAssignment) { pendingAssigneeData = _pendingAssignedNodes.take(packetUUID); + if (pendingAssigneeData) { - matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(packetUUID, nodeType); + matchingQueuedAssignment = matchingQueuedAssignmentForCheckIn(pendingAssigneeData->getAssignmentUUID(), nodeType); if (matchingQueuedAssignment) { qDebug() << "Assignment deployed with" << uuidStringWithoutCurlyBraces(packetUUID) @@ -647,6 +656,18 @@ void DomainServer::readAvailableDatagrams() { } } +void DomainServer::payAssignedNodes() { + // enumerate the NodeList to find the assigned nodes + foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) { + DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); + + if (!nodeData->getAssignmentUUID().isNull() && !nodeData->getWalletUUID().isNull()) { + // add a pending transaction for this node or increase the amount for the existing transaction + + } + } +} + void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) { LimitedNodeList* nodeList = LimitedNodeList::getInstance(); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 63a3afa13d..281a10d88b 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -45,8 +45,8 @@ public slots: void nodeKilled(SharedNodePointer node); private slots: - void readAvailableDatagrams(); + void payAssignedNodes(); private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); bool optionallySetupOAuth(); From 99a3fde8fa7a2a0d76012e1d1e9ab5198ec2a66f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 May 2014 16:57:19 -0700 Subject: [PATCH 06/14] accumulate credits to pay nodes, return in assignments JSON --- domain-server/src/DomainServer.cpp | 49 +++++++++++++++++++--- domain-server/src/DomainServer.h | 6 ++- domain-server/src/DomainServerNodeData.cpp | 3 +- domain-server/src/DomainServerNodeData.h | 5 +++ domain-server/src/WalletTransaction.cpp | 43 +++++++++++++++++++ domain-server/src/WalletTransaction.h | 42 +++++++++++++++++++ 6 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 domain-server/src/WalletTransaction.cpp create mode 100644 domain-server/src/WalletTransaction.h diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index df946b8dcb..5f9864f45b 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -60,10 +60,10 @@ DomainServer::DomainServer(int argc, char* argv[]) : // setup a timer to send transactions to pay assigned nodes every 30 seconds QTimer* nodePaymentTimer = new QTimer(this); - connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::payAssignedNodes); + connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::setupPendingAssignmentCredits); - const qint64 NODE_PAYMENT_INTERVAL_MSECS = 30 * 1000; - nodePaymentTimer->start(NODE_PAYMENT_INTERVAL_MSECS); + const qint64 CREDIT_CHECK_INTERVAL = 5 * 1000; + nodePaymentTimer->start(CREDIT_CHECK_INTERVAL); } } @@ -656,14 +656,40 @@ void DomainServer::readAvailableDatagrams() { } } -void DomainServer::payAssignedNodes() { +void DomainServer::setupPendingAssignmentCredits() { // enumerate the NodeList to find the assigned nodes foreach (const SharedNodePointer& node, LimitedNodeList::getInstance()->getNodeHash()) { DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); if (!nodeData->getAssignmentUUID().isNull() && !nodeData->getWalletUUID().isNull()) { - // add a pending transaction for this node or increase the amount for the existing transaction + // check if we have a non-finalized transaction for this node to add this amount to + TransactionHash::iterator i = _pendingAssignmentCredits.find(nodeData->getWalletUUID()); + WalletTransaction* existingTransaction = NULL; + while (i != _pendingAssignmentCredits.end() && i.key() == nodeData->getWalletUUID()) { + if (!i.value()->isFinalized()) { + existingTransaction = i.value(); + break; + } else { + ++i; + } + } + + qint64 elapsedMsecsSinceLastPayment = nodeData->getPaymentIntervalTimer().elapsed(); + nodeData->getPaymentIntervalTimer().restart(); + + const float CREDITS_PER_HOUR = 3; + const float CREDITS_PER_MSEC = CREDITS_PER_HOUR / (60 * 60 * 1000); + + float pendingCredits = elapsedMsecsSinceLastPayment * CREDITS_PER_MSEC; + + if (existingTransaction) { + existingTransaction->incrementAmount(pendingCredits); + } else { + // create a fresh transaction to pay this node, there is no transaction to append to + WalletTransaction* freshTransaction = new WalletTransaction(nodeData->getWalletUUID(), pendingCredits); + _pendingAssignmentCredits.insert(nodeData->getWalletUUID(), freshTransaction); + } } } } @@ -717,6 +743,7 @@ const char JSON_KEY_TYPE[] = "type"; const char JSON_KEY_PUBLIC_SOCKET[] = "public"; const char JSON_KEY_LOCAL_SOCKET[] = "local"; const char JSON_KEY_POOL[] = "pool"; +const char JSON_KEY_PENDING_CREDITS[] = "pending_credits"; const char JSON_KEY_WAKE_TIMESTAMP[] = "wake_timestamp"; QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { @@ -745,6 +772,18 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) { SharedAssignmentPointer matchingAssignment = _allAssignments.value(nodeData->getAssignmentUUID()); if (matchingAssignment) { nodeJson[JSON_KEY_POOL] = matchingAssignment->getPool(); + + if (!nodeData->getWalletUUID().isNull()) { + TransactionHash::iterator i = _pendingAssignmentCredits.find(nodeData->getWalletUUID()); + double pendingCreditAmount = 0; + + while (i != _pendingAssignmentCredits.end() && i.key() == nodeData->getWalletUUID()) { + pendingCreditAmount += i.value()->getAmount(); + ++i; + } + + nodeJson[JSON_KEY_PENDING_CREDITS] = pendingCreditAmount; + } } return nodeJson; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 281a10d88b..cb011be16b 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -24,9 +24,12 @@ #include #include +#include "WalletTransaction.h" + #include "PendingAssignedNodeData.h" typedef QSharedPointer SharedAssignmentPointer; +typedef QMultiHash TransactionHash; class DomainServer : public QCoreApplication, public HTTPSRequestHandler { Q_OBJECT @@ -46,7 +49,7 @@ public slots: private slots: void readAvailableDatagrams(); - void payAssignedNodes(); + void setupPendingAssignmentCredits(); private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); bool optionallySetupOAuth(); @@ -88,6 +91,7 @@ private: QHash _allAssignments; QQueue _unfulfilledAssignments; QHash _pendingAssignedNodes; + TransactionHash _pendingAssignmentCredits; QVariantMap _argumentVariantMap; diff --git a/domain-server/src/DomainServerNodeData.cpp b/domain-server/src/DomainServerNodeData.cpp index bdf088c19e..68903cc106 100644 --- a/domain-server/src/DomainServerNodeData.cpp +++ b/domain-server/src/DomainServerNodeData.cpp @@ -21,11 +21,12 @@ DomainServerNodeData::DomainServerNodeData() : _sessionSecretHash(), _assignmentUUID(), _walletUUID(), + _paymentIntervalTimer(), _statsJSONObject(), _sendingSockAddr(), _isAuthenticated(true) { - + _paymentIntervalTimer.start(); } void DomainServerNodeData::parseJSONStatsPacket(const QByteArray& statsPacket) { diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h index a8935bea00..a7d7233874 100644 --- a/domain-server/src/DomainServerNodeData.h +++ b/domain-server/src/DomainServerNodeData.h @@ -12,6 +12,8 @@ #ifndef hifi_DomainServerNodeData_h #define hifi_DomainServerNodeData_h + +#include #include #include @@ -33,6 +35,8 @@ public: void setWalletUUID(const QUuid& walletUUID) { _walletUUID = walletUUID; } const QUuid& getWalletUUID() const { return _walletUUID; } + QElapsedTimer& getPaymentIntervalTimer() { return _paymentIntervalTimer; } + void setSendingSockAddr(const HifiSockAddr& sendingSockAddr) { _sendingSockAddr = sendingSockAddr; } const HifiSockAddr& getSendingSockAddr() { return _sendingSockAddr; } @@ -46,6 +50,7 @@ private: QHash _sessionSecretHash; QUuid _assignmentUUID; QUuid _walletUUID; + QElapsedTimer _paymentIntervalTimer; QJsonObject _statsJSONObject; HifiSockAddr _sendingSockAddr; bool _isAuthenticated; diff --git a/domain-server/src/WalletTransaction.cpp b/domain-server/src/WalletTransaction.cpp new file mode 100644 index 0000000000..400eeb0688 --- /dev/null +++ b/domain-server/src/WalletTransaction.cpp @@ -0,0 +1,43 @@ +// +// WalletTransaction.cpp +// domain-server/src +// +// Created by Stephen Birarda on 2014-05-20. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include + +#include "WalletTransaction.h" + +WalletTransaction::WalletTransaction(const QUuid& destinationUUID, double amount) : + _uuid(QUuid::createUuid()), + _destinationUUID(destinationUUID), + _amount(amount), + _isFinalized(false) +{ + +} + +QJsonDocument WalletTransaction::postJson() { + QJsonObject rootObject; + QJsonObject transactionObject; + + const QString TRANSCATION_ID_KEY = "id"; + const QString TRANSACTION_DESTINATION_WALLET_ID_KEY = "destination_wallet_id"; + const QString TRANSACTION_AMOUNT_KEY = "amount"; + + transactionObject.insert(TRANSCATION_ID_KEY, uuidStringWithoutCurlyBraces(_uuid)); + transactionObject.insert(TRANSACTION_DESTINATION_WALLET_ID_KEY, uuidStringWithoutCurlyBraces(_destinationUUID)); + transactionObject.insert(TRANSACTION_AMOUNT_KEY, _amount); + + const QString ROOT_OBJECT_TRANSACTION_KEY = "transaction"; + rootObject.insert(ROOT_OBJECT_TRANSACTION_KEY, transactionObject); + + return QJsonDocument(rootObject); +} \ No newline at end of file diff --git a/domain-server/src/WalletTransaction.h b/domain-server/src/WalletTransaction.h new file mode 100644 index 0000000000..f704042804 --- /dev/null +++ b/domain-server/src/WalletTransaction.h @@ -0,0 +1,42 @@ +// +// WalletTransaction.h +// domain-server/src +// +// Created by Stephen Birarda on 2014-05-20. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_WalletTransaction_h +#define hifi_WalletTransaction_h + +#include +#include +#include + +class WalletTransaction : public QObject { +public: + WalletTransaction(const QUuid& destinationUUID, double amount); + + const QUuid& getUUID() const { return _uuid; } + + void setDestinationUUID(const QUuid& destinationUUID) { _destinationUUID = destinationUUID; } + const QUuid& getDestinationUUID() const { return _destinationUUID; } + + double getAmount() const { return _amount; } + void setAmount(double amount) { _amount = amount; } + void incrementAmount(double increment) { _amount += increment; } + + bool isFinalized() const { return _isFinalized; } + + QJsonDocument postJson(); +private: + QUuid _uuid; + QUuid _destinationUUID; + double _amount; + bool _isFinalized; +}; + +#endif // hifi_WalletTransaction_h \ No newline at end of file From 5b3b19011ee87876f886e43d351322d8479ef333 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 May 2014 17:04:41 -0700 Subject: [PATCH 07/14] display pending credits in DS web page --- domain-server/resources/web/index.shtml | 1 + domain-server/resources/web/js/tables.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/domain-server/resources/web/index.shtml b/domain-server/resources/web/index.shtml index afd0af1679..b6ba8f67db 100644 --- a/domain-server/resources/web/index.shtml +++ b/domain-server/resources/web/index.shtml @@ -13,6 +13,7 @@ Public Local Uptime (s) + Pending Credits Kill? diff --git a/domain-server/resources/web/js/tables.js b/domain-server/resources/web/js/tables.js index a4884486c3..b564d9392f 100644 --- a/domain-server/resources/web/js/tables.js +++ b/domain-server/resources/web/js/tables.js @@ -42,6 +42,8 @@ $(document).ready(function(){ var uptimeSeconds = (Date.now() - data.wake_timestamp) / 1000; nodesTableBody += "" + uptimeSeconds.toLocaleString() + ""; + nodesTableBody += "" + (typeof data.pending_credits == 'number' ? data.pending_credits.toLocaleString() : 'N/A') + ""; + nodesTableBody += ""; nodesTableBody += ""; }); From ee67c64b766b91777ee990ff97928f255c1f4405 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 21 May 2014 09:26:17 -0700 Subject: [PATCH 08/14] stub sending of transactions to server --- domain-server/src/DomainServer.cpp | 24 ++++++++++++++++++++---- domain-server/src/DomainServer.h | 1 + domain-server/src/WalletTransaction.h | 1 + 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 5f9864f45b..ab1430fc29 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -59,11 +59,17 @@ DomainServer::DomainServer(int argc, char* argv[]) : _networkAccessManager = new QNetworkAccessManager(this); // setup a timer to send transactions to pay assigned nodes every 30 seconds - QTimer* nodePaymentTimer = new QTimer(this); - connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::setupPendingAssignmentCredits); + QTimer* creditSetupTimer = new QTimer(this); + connect(creditSetupTimer, &QTimer::timeout, this, &DomainServer::setupPendingAssignmentCredits); - const qint64 CREDIT_CHECK_INTERVAL = 5 * 1000; - nodePaymentTimer->start(CREDIT_CHECK_INTERVAL); + const qint64 CREDIT_CHECK_INTERVAL_MSECS = 5 * 1000; + creditSetupTimer->start(CREDIT_CHECK_INTERVAL_MSECS); + + QTimer* nodePaymentTimer = new QTimer(this); + connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::sendPendingTransactionsToServer); + + const qint64 TRANSACTION_SEND_INTERVAL_MSECS = 30 * 1000; + nodePaymentTimer->start(TRANSACTION_SEND_INTERVAL_MSECS); } } @@ -694,6 +700,16 @@ void DomainServer::setupPendingAssignmentCredits() { } } +void DomainServer::sendPendingTransactionsToServer() { + // enumerate the pending transactions and send them to the server to complete payment + TransactionHash::iterator i = _pendingAssignmentCredits.begin(); + + while (i != _pendingAssignmentCredits.end()) { + + ++i; + } +} + void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr) { LimitedNodeList* nodeList = LimitedNodeList::getInstance(); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index cb011be16b..fc8806d53e 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -50,6 +50,7 @@ public slots: private slots: void readAvailableDatagrams(); void setupPendingAssignmentCredits(); + void sendPendingTransactionsToServer(); private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); bool optionallySetupOAuth(); diff --git a/domain-server/src/WalletTransaction.h b/domain-server/src/WalletTransaction.h index f704042804..6c9708906d 100644 --- a/domain-server/src/WalletTransaction.h +++ b/domain-server/src/WalletTransaction.h @@ -30,6 +30,7 @@ public: void incrementAmount(double increment) { _amount += increment; } bool isFinalized() const { return _isFinalized; } + void setIsFinalized(bool isFinalized) { _isFinalized = isFinalized; } QJsonDocument postJson(); private: From 981f9df6b387c832363b53f0ba090e25bf43eca6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 21 May 2014 09:53:09 -0700 Subject: [PATCH 09/14] add option to pass data server username and password to DS --- domain-server/src/DomainServer.cpp | 74 ++++++++++++++----- domain-server/src/DomainServer.h | 2 + libraries/shared/src/HifiConfigVariantMap.cpp | 6 +- 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index ab1430fc29..7b06acc9c1 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -50,26 +50,14 @@ DomainServer::DomainServer(int argc, char* argv[]) : _argumentVariantMap = HifiConfigVariantMap::mergeCLParametersWithJSONConfig(arguments()); - if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth()) { - // we either read a certificate and private key or were not passed one, good to load assignments - // and set up the node list + _networkAccessManager = new QNetworkAccessManager(this); + + if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallyLoginAndSetupAssignmentPayment()) { + // we either read a certificate and private key or were not passed one + // and completed login or did not need to + qDebug() << "Setting up LimitedNodeList and assignments."; setupNodeListAndAssignments(); - - _networkAccessManager = new QNetworkAccessManager(this); - - // setup a timer to send transactions to pay assigned nodes every 30 seconds - QTimer* creditSetupTimer = new QTimer(this); - connect(creditSetupTimer, &QTimer::timeout, this, &DomainServer::setupPendingAssignmentCredits); - - const qint64 CREDIT_CHECK_INTERVAL_MSECS = 5 * 1000; - creditSetupTimer->start(CREDIT_CHECK_INTERVAL_MSECS); - - QTimer* nodePaymentTimer = new QTimer(this); - connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::sendPendingTransactionsToServer); - - const qint64 TRANSACTION_SEND_INTERVAL_MSECS = 30 * 1000; - nodePaymentTimer->start(TRANSACTION_SEND_INTERVAL_MSECS); } } @@ -201,6 +189,56 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { addStaticAssignmentsToQueue(); } +bool DomainServer::optionallyLoginAndSetupAssignmentPayment() { + // check if we have a username and password set via env + const QString HIFI_AUTH_ENABLED_OPTION = "hifi-auth"; + const QString HIFI_USERNAME_ENV_KEY = "DOMAIN_SERVER_USERNAME"; + const QString HIFI_PASSWORD_ENV_KEY = "DOMAIN_SERVER_PASSWORD"; + + if (_argumentVariantMap.contains(HIFI_AUTH_ENABLED_OPTION)) { + AccountManager& accountManager = AccountManager::getInstance(); + accountManager.setAuthURL(DEFAULT_NODE_AUTH_URL); + + if (!accountManager.hasValidAccessToken()) { + // we don't have a valid access token so we need to get one + QString username = QProcessEnvironment::systemEnvironment().value(HIFI_USERNAME_ENV_KEY); + QString password = QProcessEnvironment::systemEnvironment().value(HIFI_PASSWORD_ENV_KEY); + + if (!username.isEmpty() && !password.isEmpty()) { + accountManager.requestAccessToken(username, password); + + // connect to loginFailed signal from AccountManager so we can quit if that is the case + connect(&accountManager, &AccountManager::loginFailed, this, &DomainServer::loginFailed); + } else { + qDebug() << "Missing access-token or username and password combination. domain-server will now quit."; + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + return false; + } + } + + // assume that the fact we are authing against HF data server means we will pay for assignments + // setup a timer to send transactions to pay assigned nodes every 30 seconds + QTimer* creditSetupTimer = new QTimer(this); + connect(creditSetupTimer, &QTimer::timeout, this, &DomainServer::setupPendingAssignmentCredits); + + const qint64 CREDIT_CHECK_INTERVAL_MSECS = 5 * 1000; + creditSetupTimer->start(CREDIT_CHECK_INTERVAL_MSECS); + + QTimer* nodePaymentTimer = new QTimer(this); + connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::sendPendingTransactionsToServer); + + const qint64 TRANSACTION_SEND_INTERVAL_MSECS = 30 * 1000; + nodePaymentTimer->start(TRANSACTION_SEND_INTERVAL_MSECS); + } + + return true; +} + +void DomainServer::loginFailed() { + qDebug() << "Login to data server has failed. domain-server will now quit"; + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); +} + void DomainServer::parseAssignmentConfigs(QSet& excludedTypes) { // check for configs from the command line, these take precedence const QString ASSIGNMENT_CONFIG_REGEX_STRING = "config-([\\d]+)"; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index fc8806d53e..645b023c78 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -48,6 +48,7 @@ public slots: void nodeKilled(SharedNodePointer node); private slots: + void loginFailed(); void readAvailableDatagrams(); void setupPendingAssignmentCredits(); void sendPendingTransactionsToServer(); @@ -55,6 +56,7 @@ private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); bool optionallySetupOAuth(); bool optionallyReadX509KeyAndCertificate(); + bool optionallyLoginAndSetupAssignmentPayment(); void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index d20f4276f1..e8ab59ce2d 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -41,19 +41,19 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL nextKeyIndex = argumentList.indexOf(dashedKeyRegex, keyIndex + 1); - if (nextKeyIndex == keyIndex + 1) { + if (nextKeyIndex == keyIndex + 1 || keyIndex == argumentList.size() - 1) { // there's no value associated with this option, it's a boolean // so add it to the variant map with NULL as value mergedMap.insertMulti(key, QVariant()); } else { - int maxIndex = (nextKeyIndex == -1) ? argumentList.size() : nextKeyIndex; + int maxIndex = (nextKeyIndex == -1) ? argumentList.size() - 1: nextKeyIndex; // there's at least one value associated with the option // pull the first value to start QString value = argumentList[keyIndex + 1]; // for any extra values, append them, with a space, to the value string - for (int i = keyIndex + 2; i < maxIndex; i++) { + for (int i = keyIndex + 2; i <= maxIndex; i++) { value += " " + argumentList[i]; } From 908eb5cc4f285af6d7ae30e54ef010a5699379b2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 21 May 2014 10:04:48 -0700 Subject: [PATCH 10/14] use OAuth provider URL for transaction login --- domain-server/src/DomainServer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 7b06acc9c1..cdb88ec7a4 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -131,7 +131,7 @@ bool DomainServer::optionallySetupOAuth() { _oauthClientSecret = QProcessEnvironment::systemEnvironment().value(OAUTH_CLIENT_SECRET_ENV); _hostname = _argumentVariantMap.value(REDIRECT_HOSTNAME_OPTION).toString(); - if (!_oauthProviderURL.isEmpty() || !_hostname.isEmpty() || !_oauthClientID.isEmpty()) { + if (!_oauthClientID.isEmpty()) { if (_oauthProviderURL.isEmpty() || _hostname.isEmpty() || _oauthClientID.isEmpty() @@ -191,13 +191,13 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { bool DomainServer::optionallyLoginAndSetupAssignmentPayment() { // check if we have a username and password set via env - const QString HIFI_AUTH_ENABLED_OPTION = "hifi-auth"; const QString HIFI_USERNAME_ENV_KEY = "DOMAIN_SERVER_USERNAME"; const QString HIFI_PASSWORD_ENV_KEY = "DOMAIN_SERVER_PASSWORD"; - if (_argumentVariantMap.contains(HIFI_AUTH_ENABLED_OPTION)) { + if (!_oauthProviderURL.isEmpty()) { + AccountManager& accountManager = AccountManager::getInstance(); - accountManager.setAuthURL(DEFAULT_NODE_AUTH_URL); + accountManager.setAuthURL(_oauthProviderURL); if (!accountManager.hasValidAccessToken()) { // we don't have a valid access token so we need to get one From 1dddabb6915f74e209fa42a111b05c69583cea36 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 21 May 2014 10:58:41 -0700 Subject: [PATCH 11/14] send and confirm transactions to assigned nodes from DS --- domain-server/src/DomainServer.cpp | 69 +++++++++++++++++++-- domain-server/src/DomainServer.h | 2 + domain-server/src/WalletTransaction.cpp | 40 +++++++++--- domain-server/src/WalletTransaction.h | 3 + libraries/networking/src/AccountManager.cpp | 8 ++- 5 files changed, 109 insertions(+), 13 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index cdb88ec7a4..15b720a388 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -739,12 +739,55 @@ void DomainServer::setupPendingAssignmentCredits() { } void DomainServer::sendPendingTransactionsToServer() { - // enumerate the pending transactions and send them to the server to complete payment - TransactionHash::iterator i = _pendingAssignmentCredits.begin(); - while (i != _pendingAssignmentCredits.end()) { + AccountManager& accountManager = AccountManager::getInstance(); + + if (accountManager.hasValidAccessToken()) { - ++i; + // enumerate the pending transactions and send them to the server to complete payment + TransactionHash::iterator i = _pendingAssignmentCredits.begin(); + + JSONCallbackParameters transactionCallbackParams; + + transactionCallbackParams.jsonCallbackReceiver = this; + transactionCallbackParams.jsonCallbackMethod = "transactionJSONCallback"; + + while (i != _pendingAssignmentCredits.end()) { + accountManager.authenticatedRequest("api/v1/transactions", QNetworkAccessManager::PostOperation, + transactionCallbackParams, i.value()->postJson().toJson()); + + // set this transaction to finalized so we don't add additional credits to it + i.value()->setIsFinalized(true); + + ++i; + } + } + +} + +void DomainServer::transactionJSONCallback(const QJsonObject& data) { + // check if this was successful - if so we can remove it from our list of pending + if (data.value("status").toString() == "success") { + // create a dummy wallet transaction to unpack the JSON to + WalletTransaction dummyTransaction; + dummyTransaction.loadFromJson(data); + + TransactionHash::iterator i = _pendingAssignmentCredits.find(dummyTransaction.getDestinationUUID()); + + while (i != _pendingAssignmentCredits.end() && i.key() == dummyTransaction.getDestinationUUID()) { + if (i.value()->getUUID() == dummyTransaction.getUUID()) { + // we have a match - we can remove this from the hash of pending credits + // and delete it for clean up + + WalletTransaction* matchingTransaction = i.value(); + _pendingAssignmentCredits.erase(i); + delete matchingTransaction; + + break; + } else { + ++i; + } + } } } @@ -907,6 +950,24 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url connection->respond(HTTPConnection::StatusCode200, assignmentDocument.toJson(), qPrintable(JSON_MIME_TYPE)); // we've processed this request + return true; + } else if (url.path() == "/transactions.json") { + // enumerate our pending transactions and display them in an array + QJsonObject rootObject; + QJsonArray transactionArray; + + TransactionHash::iterator i = _pendingAssignmentCredits.begin(); + while (i != _pendingAssignmentCredits.end()) { + transactionArray.push_back(i.value()->toJson()); + ++i; + } + + rootObject["pending_transactions"] = transactionArray; + + // print out the created JSON + QJsonDocument transactionsDocument(rootObject); + connection->respond(HTTPConnection::StatusCode200, transactionsDocument.toJson(), qPrintable(JSON_MIME_TYPE)); + return true; } else if (url.path() == QString("%1.json").arg(URI_NODES)) { // setup the JSON diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 645b023c78..aa55e07086 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -47,6 +47,8 @@ public slots: /// Called by NodeList to inform us a node has been killed void nodeKilled(SharedNodePointer node); + void transactionJSONCallback(const QJsonObject& data); + private slots: void loginFailed(); void readAvailableDatagrams(); diff --git a/domain-server/src/WalletTransaction.cpp b/domain-server/src/WalletTransaction.cpp index 400eeb0688..6ff57f063c 100644 --- a/domain-server/src/WalletTransaction.cpp +++ b/domain-server/src/WalletTransaction.cpp @@ -15,6 +15,15 @@ #include "WalletTransaction.h" +WalletTransaction::WalletTransaction() : + _uuid(), + _destinationUUID(), + _amount(), + _isFinalized(false) +{ + +} + WalletTransaction::WalletTransaction(const QUuid& destinationUUID, double amount) : _uuid(QUuid::createUuid()), _destinationUUID(destinationUUID), @@ -24,20 +33,35 @@ WalletTransaction::WalletTransaction(const QUuid& destinationUUID, double amount } +const QString TRANSACTION_ID_KEY = "id"; +const QString TRANSACTION_DESTINATION_WALLET_ID_KEY = "destination_wallet_id"; +const QString TRANSACTION_AMOUNT_KEY = "amount"; + +const QString ROOT_OBJECT_TRANSACTION_KEY = "transaction"; + QJsonDocument WalletTransaction::postJson() { QJsonObject rootObject; + + rootObject.insert(ROOT_OBJECT_TRANSACTION_KEY, toJson()); + + return QJsonDocument(rootObject); +} + +QJsonObject WalletTransaction::toJson() { QJsonObject transactionObject; - const QString TRANSCATION_ID_KEY = "id"; - const QString TRANSACTION_DESTINATION_WALLET_ID_KEY = "destination_wallet_id"; - const QString TRANSACTION_AMOUNT_KEY = "amount"; - - transactionObject.insert(TRANSCATION_ID_KEY, uuidStringWithoutCurlyBraces(_uuid)); + transactionObject.insert(TRANSACTION_ID_KEY, uuidStringWithoutCurlyBraces(_uuid)); transactionObject.insert(TRANSACTION_DESTINATION_WALLET_ID_KEY, uuidStringWithoutCurlyBraces(_destinationUUID)); transactionObject.insert(TRANSACTION_AMOUNT_KEY, _amount); - const QString ROOT_OBJECT_TRANSACTION_KEY = "transaction"; - rootObject.insert(ROOT_OBJECT_TRANSACTION_KEY, transactionObject); + return transactionObject; +} + +void WalletTransaction::loadFromJson(const QJsonObject& jsonObject) { + // pull the destination wallet and ID of the transaction to match it + QJsonObject transactionObject = jsonObject.value("data").toObject().value(ROOT_OBJECT_TRANSACTION_KEY).toObject(); - return QJsonDocument(rootObject); + _uuid = QUuid(transactionObject.value(TRANSACTION_ID_KEY).toString()); + _destinationUUID = QUuid(transactionObject.value(TRANSACTION_DESTINATION_WALLET_ID_KEY).toString()); + _amount = transactionObject.value(TRANSACTION_AMOUNT_KEY).toDouble(); } \ No newline at end of file diff --git a/domain-server/src/WalletTransaction.h b/domain-server/src/WalletTransaction.h index 6c9708906d..8f36d10302 100644 --- a/domain-server/src/WalletTransaction.h +++ b/domain-server/src/WalletTransaction.h @@ -18,6 +18,7 @@ class WalletTransaction : public QObject { public: + WalletTransaction(); WalletTransaction(const QUuid& destinationUUID, double amount); const QUuid& getUUID() const { return _uuid; } @@ -33,6 +34,8 @@ public: void setIsFinalized(bool isFinalized) { _isFinalized = isFinalized; } QJsonDocument postJson(); + QJsonObject toJson(); + void loadFromJson(const QJsonObject& jsonObject); private: QUuid _uuid; QUuid _destinationUUID; diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp index 547768ec48..aad2cfb386 100644 --- a/libraries/networking/src/AccountManager.cpp +++ b/libraries/networking/src/AccountManager.cpp @@ -136,7 +136,13 @@ void AccountManager::invokedRequest(const QString& path, QNetworkAccessManager:: QNetworkRequest authenticatedRequest; QUrl requestURL = _authURL; - requestURL.setPath(path); + + if (path.startsWith("/")) { + requestURL.setPath(path); + } else { + requestURL.setPath("/" + path); + } + requestURL.setQuery("access_token=" + _accountInfo.getAccessToken().token); authenticatedRequest.setUrl(requestURL); From b7727f0b16f6625bfdb399617a406332c388c5ee Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 21 May 2014 11:02:02 -0700 Subject: [PATCH 12/14] require a flag to enable payment to assigned nodes --- domain-server/src/DomainServer.cpp | 71 +++++++++++++++++------------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 15b720a388..6fca13f3c8 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -191,44 +191,53 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { bool DomainServer::optionallyLoginAndSetupAssignmentPayment() { // check if we have a username and password set via env + const QString ASSIGNED_NODE_PAYMENT_OPTION = "pay-nodes"; const QString HIFI_USERNAME_ENV_KEY = "DOMAIN_SERVER_USERNAME"; const QString HIFI_PASSWORD_ENV_KEY = "DOMAIN_SERVER_PASSWORD"; - if (!_oauthProviderURL.isEmpty()) { - - AccountManager& accountManager = AccountManager::getInstance(); - accountManager.setAuthURL(_oauthProviderURL); - - if (!accountManager.hasValidAccessToken()) { - // we don't have a valid access token so we need to get one - QString username = QProcessEnvironment::systemEnvironment().value(HIFI_USERNAME_ENV_KEY); - QString password = QProcessEnvironment::systemEnvironment().value(HIFI_PASSWORD_ENV_KEY); + if (_argumentVariantMap.contains(ASSIGNED_NODE_PAYMENT_OPTION)) { + if (!_oauthProviderURL.isEmpty()) { - if (!username.isEmpty() && !password.isEmpty()) { - accountManager.requestAccessToken(username, password); + AccountManager& accountManager = AccountManager::getInstance(); + accountManager.setAuthURL(_oauthProviderURL); + + if (!accountManager.hasValidAccessToken()) { + // we don't have a valid access token so we need to get one + QString username = QProcessEnvironment::systemEnvironment().value(HIFI_USERNAME_ENV_KEY); + QString password = QProcessEnvironment::systemEnvironment().value(HIFI_PASSWORD_ENV_KEY); - // connect to loginFailed signal from AccountManager so we can quit if that is the case - connect(&accountManager, &AccountManager::loginFailed, this, &DomainServer::loginFailed); - } else { - qDebug() << "Missing access-token or username and password combination. domain-server will now quit."; - QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); - return false; + if (!username.isEmpty() && !password.isEmpty()) { + accountManager.requestAccessToken(username, password); + + // connect to loginFailed signal from AccountManager so we can quit if that is the case + connect(&accountManager, &AccountManager::loginFailed, this, &DomainServer::loginFailed); + } else { + qDebug() << "Missing access-token or username and password combination. domain-server will now quit."; + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + return false; + } } + + // assume that the fact we are authing against HF data server means we will pay for assignments + // setup a timer to send transactions to pay assigned nodes every 30 seconds + QTimer* creditSetupTimer = new QTimer(this); + connect(creditSetupTimer, &QTimer::timeout, this, &DomainServer::setupPendingAssignmentCredits); + + const qint64 CREDIT_CHECK_INTERVAL_MSECS = 5 * 1000; + creditSetupTimer->start(CREDIT_CHECK_INTERVAL_MSECS); + + QTimer* nodePaymentTimer = new QTimer(this); + connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::sendPendingTransactionsToServer); + + const qint64 TRANSACTION_SEND_INTERVAL_MSECS = 30 * 1000; + nodePaymentTimer->start(TRANSACTION_SEND_INTERVAL_MSECS); + + } else { + qDebug() << "Missing OAuth provider URL, but assigned node payment was enabled. domain-server will now quit."; + QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + + return false; } - - // assume that the fact we are authing against HF data server means we will pay for assignments - // setup a timer to send transactions to pay assigned nodes every 30 seconds - QTimer* creditSetupTimer = new QTimer(this); - connect(creditSetupTimer, &QTimer::timeout, this, &DomainServer::setupPendingAssignmentCredits); - - const qint64 CREDIT_CHECK_INTERVAL_MSECS = 5 * 1000; - creditSetupTimer->start(CREDIT_CHECK_INTERVAL_MSECS); - - QTimer* nodePaymentTimer = new QTimer(this); - connect(nodePaymentTimer, &QTimer::timeout, this, &DomainServer::sendPendingTransactionsToServer); - - const qint64 TRANSACTION_SEND_INTERVAL_MSECS = 30 * 1000; - nodePaymentTimer->start(TRANSACTION_SEND_INTERVAL_MSECS); } return true; From 98d1146e90158d16f63b5491858515dcfb94cec9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 21 May 2014 11:04:00 -0700 Subject: [PATCH 13/14] rename the pay for assignments option --- domain-server/src/DomainServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 6fca13f3c8..54cfa12130 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -191,11 +191,11 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { bool DomainServer::optionallyLoginAndSetupAssignmentPayment() { // check if we have a username and password set via env - const QString ASSIGNED_NODE_PAYMENT_OPTION = "pay-nodes"; + const QString PAY_FOR_ASSIGNMENTS_OPTION = "pay-for-assignments"; const QString HIFI_USERNAME_ENV_KEY = "DOMAIN_SERVER_USERNAME"; const QString HIFI_PASSWORD_ENV_KEY = "DOMAIN_SERVER_PASSWORD"; - if (_argumentVariantMap.contains(ASSIGNED_NODE_PAYMENT_OPTION)) { + if (_argumentVariantMap.contains(PAY_FOR_ASSIGNMENTS_OPTION)) { if (!_oauthProviderURL.isEmpty()) { AccountManager& accountManager = AccountManager::getInstance(); From e3ac7c5eecb84cb1166fe6d53836340e789e80b1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 21 May 2014 12:07:23 -0700 Subject: [PATCH 14/14] rename assignment payment method for clarity --- domain-server/src/DomainServer.cpp | 4 ++-- domain-server/src/DomainServer.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 54cfa12130..9ad36e6956 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -52,7 +52,7 @@ DomainServer::DomainServer(int argc, char* argv[]) : _networkAccessManager = new QNetworkAccessManager(this); - if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallyLoginAndSetupAssignmentPayment()) { + if (optionallyReadX509KeyAndCertificate() && optionallySetupOAuth() && optionallySetupAssignmentPayment()) { // we either read a certificate and private key or were not passed one // and completed login or did not need to @@ -189,7 +189,7 @@ void DomainServer::setupNodeListAndAssignments(const QUuid& sessionUUID) { addStaticAssignmentsToQueue(); } -bool DomainServer::optionallyLoginAndSetupAssignmentPayment() { +bool DomainServer::optionallySetupAssignmentPayment() { // check if we have a username and password set via env const QString PAY_FOR_ASSIGNMENTS_OPTION = "pay-for-assignments"; const QString HIFI_USERNAME_ENV_KEY = "DOMAIN_SERVER_USERNAME"; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index aa55e07086..b038850b3d 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -58,7 +58,7 @@ private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); bool optionallySetupOAuth(); bool optionallyReadX509KeyAndCertificate(); - bool optionallyLoginAndSetupAssignmentPayment(); + bool optionallySetupAssignmentPayment(); void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr);