aboutsummaryrefslogtreecommitdiff
path: root/src/DatabaseStorage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/DatabaseStorage.cpp')
-rw-r--r--src/DatabaseStorage.cpp161
1 files changed, 148 insertions, 13 deletions
diff --git a/src/DatabaseStorage.cpp b/src/DatabaseStorage.cpp
index d7f42bb..6064ee5 100644
--- a/src/DatabaseStorage.cpp
+++ b/src/DatabaseStorage.cpp
@@ -6,6 +6,7 @@
#include "../include/odhtdb/Log.hpp"
#include "../include/odhtdb/Database.hpp"
#include "../include/odhtdb/sql/SqlQuery.hpp"
+#include "../include/odhtdb/sql/SqlExec.hpp"
#include <cstring>
#include <chrono>
#include <boost/filesystem/convenience.hpp>
@@ -101,24 +102,26 @@ namespace odhtdb
sqlite_exec_checked(sqliteDb,
"CREATE TABLE IF NOT EXISTS Node(id INTEGER PRIMARY KEY, nodeHash BLOB UNIQUE NOT NULL, timestamp INTEGER NOT NULL, creatorPublicKey BLOB NOT NULL, adminGroupId BLOB NOT NULL);"
- "CREATE TABLE IF NOT EXISTS NodeUser(node BLOB NOT NULL, publicKey BLOB NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash));"
+ "CREATE TABLE IF NOT EXISTS NodeUser(id INTEGER PRIMARY KEY, node BLOB NOT NULL, publicKey BLOB NOT NULL, latestActionCounter INTEGER NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash));"
"CREATE TABLE IF NOT EXISTS NodeGroup(node BLOB NOT NULL, groupId BLOB UNIQUE NOT NULL, permissionLevel INT NOT NULL, permissionFlags INTEGER NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash));"
- "CREATE TABLE IF NOT EXISTS NodeAddData(id INTEGER PRIMARY KEY, node BLOB NOT NULL, requestHash BLOB UNIQUE NOT NULL, operation INT NOT NULL, timestamp INTEGER NOT NULL, creatorPublicKey BLOB NOT NULL, decrypted INT NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash));"
+ "CREATE TABLE IF NOT EXISTS NodeAddData(id INTEGER PRIMARY KEY, node BLOB NOT NULL, requestHash BLOB UNIQUE NOT NULL, operation INT NOT NULL, timestamp INTEGER NOT NULL, creatorPublicKey BLOB NOT NULL, decrypted INT NOT NULL, userActionCounter INTEGER NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash));"
"CREATE TABLE IF NOT EXISTS NodeAddDataAdditional(id INTEGER PRIMARY KEY, nodeAddDataId INTEGER NOT NULL, data BLOB NOT NULL, FOREIGN KEY(nodeAddDataId) REFERENCES NodeAddData(id));"
"CREATE TABLE IF NOT EXISTS NodeAddUserData(id INTEGER PRIMARY KEY, nodeAddDataId INTEGER NOT NULL, userToAddPublicKey BLOB NOT NULL, groupId BLOB NOT NULL, FOREIGN KEY(nodeAddDataId) REFERENCES NodeAddData(id));"
"CREATE TABLE IF NOT EXISTS NodeDecryptionKey(node BLOB UNIQUE NOT NULL, decryptionKey BLOB NOT NULL);"
"CREATE TABLE IF NOT EXISTS NodeUserGroupAssoc(node BLOB NOT NULL, userPublicKey BLOB NOT NULL, groupId BLOB NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash), FOREIGN KEY(userPublicKey) REFERENCES NodeUser(publicKey), FOREIGN KEY(groupId) REFERENCES NodeGroup(groupId));"
"CREATE TABLE IF NOT EXISTS NodeRaw(node INTEGER NOT NULL, data BLOB NOT NULL, FOREIGN KEY(node) REFERENCES Node(id));"
- "CREATE TABLE IF NOT EXISTS NodeAddDataRaw(node INTEGER NOT NULL, nodeAddData INTEGER NOT NULL, data BLOB NOT NULL, FOREIGN KEY(node) REFERENCES Node(id), FOREIGN KEY(nodeAddData) REFERENCES NodeAddData(id));"
+ "CREATE TABLE IF NOT EXISTS NodeAddDataRaw(nodeId INTEGER NOT NULL, nodeAddDataId INTEGER NOT NULL, data BLOB NOT NULL, FOREIGN KEY(nodeId) REFERENCES Node(id), FOREIGN KEY(nodeAddDataId) REFERENCES NodeAddData(id));"
+
+ "CREATE TABLE IF NOT EXISTS NodeUserActionGap(id INTEGER PRIMARY KEY, nodeUserId INTEGER NOT NULL, start INTEGER NOT NULL, range INTEGER NOT NULL, FOREIGN KEY(nodeUserId) REFERENCES NodeUser(id));"
"CREATE UNIQUE INDEX IF NOT EXISTS UniqueUserInNode ON NodeUser(node, publicKey);"
"CREATE UNIQUE INDEX IF NOT EXISTS UniqueUserGroupAssoc ON NodeUserGroupAssoc(node, userPublicKey, groupId);");
sqlite_prepare_checked(sqliteDb, "INSERT INTO Node(nodeHash, timestamp, creatorPublicKey, adminGroupId) VALUES(?, ?, ?, ?)", &insertNodeStmt);
- sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeUser(node, publicKey) VALUES(?, ?)", &insertUserStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeUser(node, publicKey, latestActionCounter) VALUES(?, ?, 0)", &insertUserStmt);
sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeGroup(node, groupId, permissionLevel, permissionFlags) VALUES(?, ?, ?, ?)", &insertGroupStmt);
- sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddData(node, requestHash, operation, timestamp, creatorPublicKey, decrypted) VALUES(?, ?, ?, ?, ?, ?)", &insertNodeAddDataStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddData(node, requestHash, operation, timestamp, creatorPublicKey, decrypted, userActionCounter) VALUES(?, ?, ?, ?, ?, ?, ?)", &insertNodeAddDataStmt);
sqlite_prepare_checked(sqliteDb, "INSERT OR REPLACE INTO NodeDecryptionKey(node, decryptionKey) VALUES(?, ?)", &setNodeDecryptionKeyStmt);
sqlite_prepare_checked(sqliteDb, "SELECT decryptionKey FROM NodeDecryptionKey WHERE node = ?", &getNodeDecryptionKeyStmt);
sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeUserGroupAssoc(node, userPublicKey, groupId) VALUES(?, ?, ?)", &insertNodeUserGroupAssocStmt);
@@ -128,7 +131,7 @@ namespace odhtdb
sqlite_prepare_checked(sqliteDb, "SELECT id FROM Node WHERE nodeHash = ?", &selectNodeIdStatement);
sqlite_prepare_checked(sqliteDb, "SELECT id FROM NodeAddData WHERE requestHash = ?", &selectNodeAddDataIdStatement);
sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeRaw(node, data) VALUES(?, ?)", &insertNodeRawStmt);
- sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddDataRaw(node, nodeAddData, data) VALUES(?, ?, ?)", &insertNodeAddDataRawStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddDataRaw(nodeId, nodeAddDataId, data) VALUES(?, ?, ?)", &insertNodeAddDataRawStmt);
sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddDataAdditional(nodeAddDataId, data) VALUES(?, ?)", &insertNodeAddDataAdditionalStmt);
sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddUserData(nodeAddDataId, userToAddPublicKey, groupId) VALUES(?, ?, ?)", &insertNodeAddUserDataStmt);
@@ -456,9 +459,97 @@ namespace odhtdb
decryptNodeData(hash, nodeDecryptionKeyResult.second, &adminPublicKey, adminGroupId, timestamp);
}
- void DatabaseStorage::appendStorage(const Hash &nodeHash, const Hash &dataHash, DatabaseOperation operation, const Signature::PublicKey &creatorPublicKey, u64 timestamp, const void *data, usize size, const DataView &additionalDataView)
+ void DatabaseStorage::appendStorage(const Hash &nodeHash, const Hash &dataHash, DatabaseOperation operation, u64 newUserActionCounter, const Signature::PublicKey &creatorPublicKey, u64 timestamp, const void *data, usize size, const DataView &additionalDataView)
{
sqlite3_exec(sqliteDb, "BEGIN", 0, 0, 0);
+
+ {
+ SqlQuery selectUserIdAndActionCounter(sqliteDb, "SELECT id, latestActionCounter FROM NodeUser WHERE node = ? AND publicKey = ?",
+ { DataView(nodeHash.getData(), nodeHash.getSize()), DataView((void*)creatorPublicKey.getData(), creatorPublicKey.getSize()) });
+ if(!selectUserIdAndActionCounter.next())
+ {
+ string errMsg = "User ";
+ errMsg += creatorPublicKey.toString();
+ errMsg += " not found in node ";
+ errMsg += nodeHash.toString();
+ throw DatabaseStorageNotFound(errMsg);
+ }
+
+ i64 nodeUserRowId = selectUserIdAndActionCounter.getInt64(0);
+ u64 userActionCounter = selectUserIdAndActionCounter.getInt64(1);
+
+ if(newUserActionCounter == userActionCounter)
+ {
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException("Got unique package but action counter was equal to users existing one, discarding packet");
+ }
+ else if(newUserActionCounter == userActionCounter + 1)
+ {
+ SqlExec setUserActionCounter(sqliteDb, "UPDATE NodeUser SET latestActionCounter = ? WHERE id = ?");
+ setUserActionCounter.execWithArgs({
+ newUserActionCounter,
+ nodeUserRowId
+ });
+ }
+ else
+ {
+ SqlQuery existingActionGap(sqliteDb, "SELECT id, start, range FROM NodeUserActionGap WHERE nodeUserId = ? AND ? >= start AND ? <= start + range",
+ { nodeUserRowId, newUserActionCounter, newUserActionCounter });
+ if(existingActionGap.next())
+ {
+ i64 actionGapRowId = existingActionGap.getInt64(0);
+ u64 start = existingActionGap.getInt64(1);
+ u64 range = existingActionGap.getInt64(2);
+
+ SqlExec removeRange(sqliteDb, "DELETE FROM NodeUserActionGap WHERE id = ?");
+ removeRange.execWithArgs({ actionGapRowId });
+
+ if(range == 1)
+ {
+ if(start + range > userActionCounter)
+ {
+ SqlExec setUserActionCounter(sqliteDb, "UPDATE NodeUser SET latestActionCounter = ? WHERE id = ?");
+ setUserActionCounter.execWithArgs({
+ start + range,
+ nodeUserRowId
+ });
+ }
+ }
+ else
+ {
+ SqlExec addUserActionGap(sqliteDb, "INSERT INTO NodeUserActionGap(nodeUserId, start, range) VALUES(?, ?, ?)");
+
+ u64 startBefore = start;
+ u64 rangeBefore = newUserActionCounter - start;
+ if(rangeBefore > 0)
+ addUserActionGap.execWithArgs({ nodeUserRowId, startBefore, rangeBefore });
+
+ u64 startAfter = newUserActionCounter + 1;
+ u64 rangeAfter = (start + range) - newUserActionCounter;
+ if(rangeAfter > 0)
+ addUserActionGap.execWithArgs({ nodeUserRowId, startAfter, rangeAfter });
+ }
+ }
+ else
+ {
+ if(newUserActionCounter > userActionCounter + 1)
+ {
+ u64 start = userActionCounter + 1;
+ u64 range = newUserActionCounter - start;
+ SqlExec addUserActionGap(sqliteDb, "INSERT INTO NodeUserActionGap(nodeUserId, start, range) VALUES(?, ?, ?)");
+ addUserActionGap.execWithArgs({ nodeUserRowId, start, range });
+ }
+ else if(newUserActionCounter < userActionCounter)
+ {
+ u64 start = newUserActionCounter;
+ u64 range = userActionCounter - start;
+ SqlExec addUserActionGap(sqliteDb, "INSERT INTO NodeUserActionGap(nodeUserId, start, range) VALUES(?, ?, ?)");
+ addUserActionGap.execWithArgs({ nodeUserRowId, start, range });
+ }
+ }
+ }
+ }
+
{
sqlite3_reset(insertNodeAddDataStmt);
sqlite3_clear_bindings(insertNodeAddDataStmt);
@@ -482,6 +573,9 @@ namespace odhtdb
rc = sqlite3_bind_int(insertNodeAddDataStmt, 6, 0);
bindCheckError(rc);
+ rc = sqlite3_bind_int(insertNodeAddDataStmt, 7, newUserActionCounter);
+ bindCheckError(rc);
+
sqlite_step_rollback_on_failure(sqliteDb, insertNodeAddDataStmt, "insert data into NodeAddData");
}
@@ -537,7 +631,7 @@ namespace odhtdb
else
{
sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
- throw std::runtime_error("Unexpected operation type");
+ throw ("Unexpected operation type");
}
{
@@ -633,18 +727,44 @@ namespace odhtdb
SqlQuery query(sqliteDb, "SELECT data FROM NodeRaw WHERE node = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) });
while(query.next())
{
- const DataView data = query.getBlob(0);
- callbackFunc(data);
+ const DataView rawData = query.getBlob(0);
+ callbackFunc(rawData);
}
}
void DatabaseStorage::fetchNodeAddDataRaw(const Hash &nodeHash, FetchNodeAddDataRawCallbackFunc callbackFunc)
{
- SqlQuery query(sqliteDb, "SELECT data FROM NodeAddDataRaw WHERE node = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ SqlQuery query(sqliteDb, "SELECT rawData.data, nodeAddData.creatorPublicKey, nodeAddData.userActionCounter From NodeAddData AS nodeAddData INNER JOIN NodeAddDataRaw AS rawData ON rawData.nodeAddDataId = nodeAddData.id WHERE nodeAddData.node = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) });
while(query.next())
{
- const DataView data = query.getBlob(0);
- callbackFunc(data);
+ const DataView rawData = query.getBlob(0);
+ const DataView creatorPublicKey = query.getBlob(1);
+ u64 userActionCounter = query.getInt64(2);
+ callbackFunc(rawData, creatorPublicKey, userActionCounter);
+ }
+ }
+
+ void DatabaseStorage::fetchNodeUserActionGaps(const Hash &nodeHash, FetchNodeUserActionGapsCallbackFunc callbackFunc)
+ {
+ SqlQuery query(sqliteDb, "SELECT user.publicKey, actionGap.start, actionGap.range FROM NodeUser AS user INNER JOIN NodeUserActionGap AS actionGap ON actionGap.nodeUserId = user.id WHERE user.node = ?",
+ { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ while(query.next())
+ {
+ const DataView userPublicKey = query.getBlob(0);
+ u64 actionGapStart = query.getInt64(1);
+ u64 actionGapRange = query.getInt64(2);
+ callbackFunc(userPublicKey, actionGapStart, actionGapRange);
+ }
+ }
+
+ void DatabaseStorage::fetchNodeUserLatestActionCounter(const Hash &nodeHash, FetchNodeUserLatestActionCounterCallbackFunc callbackFunc)
+ {
+ SqlQuery query(sqliteDb, "SELECT publicKey, latestActionCounter FROM NodeUser WHERE node = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ while(!query.next())
+ {
+ const DataView userPublicKey = query.getBlob(0);
+ u64 latestActionCounter = query.getInt64(1);
+ callbackFunc(userPublicKey, latestActionCounter);
}
}
@@ -677,6 +797,21 @@ namespace odhtdb
{ DataView(nodeHash.getData(), nodeHash.getSize()), DataView((void*)userPublicKey.getData(), userPublicKey.getSize()), groupToAddPermissionLevel, (i64)PermissionType::ADD_USER_SAME_LEVEL, groupToAddPermissionLevel, (i64)PermissionType::ADD_USER_HIGHER_LEVEL });
return queryCreatorGroupWithRightsToAddUserToGroup.next();
}
+
+ u64 DatabaseStorage::getUserActionCounter(const Hash &nodeHash, const Signature::PublicKey &userPublicKey) const
+ {
+ SqlQuery query(sqliteDb, "SELECT latestActionCounter FROM NodeUser WHERE node = ? AND publicKey = ?",
+ { DataView(nodeHash.getData(), nodeHash.getSize()), DataView((void*)userPublicKey.getData(), userPublicKey.getSize()) });
+ if(!query.next())
+ {
+ string errMsg = "User ";
+ errMsg += userPublicKey.toString();
+ errMsg += " not found in node ";
+ errMsg += nodeHash.toString();
+ throw DatabaseStorageNotFound(errMsg);
+ }
+ return query.getInt64(0);
+ }
#if 0
bool DatabaseStorage::storeLocalUser(const string &username, const Signature::KeyPair &keyPair, const string &password)
{