From 8daf7b3165c65a932a7d8eae1f0a640199892ca9 Mon Sep 17 00:00:00 2001 From: dec05eba <0xdec05eba@gmail.com> Date: Mon, 14 May 2018 20:13:24 +0200 Subject: Only download nodes that we are missing --- src/DatabaseStorage.cpp | 161 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 148 insertions(+), 13 deletions(-) (limited to 'src/DatabaseStorage.cpp') 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 #include #include @@ -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) { -- cgit v1.2.3