diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml index 2082d41beec7e51a37315d3c80ffd51b59a626eb..c72e04f7c3f5224df5f03d1a3c077907b637e873 100644 --- a/.idea/androidTestResultsUserPreferences.xml +++ b/.idea/androidTestResultsUserPreferences.xml @@ -56,6 +56,20 @@ </AndroidTestResultsTableState> </value> </entry> + <entry key="-1834143290"> + <value> + <AndroidTestResultsTableState> + <option name="preferredColumnWidths"> + <map> + <entry key="29211JEGR13699" value="120" /> + <entry key="Duration" value="90" /> + <entry key="Google Pixel 6a" value="120" /> + <entry key="Tests" value="360" /> + </map> + </option> + </AndroidTestResultsTableState> + </value> + </entry> <entry key="-1766545009"> <value> <AndroidTestResultsTableState> @@ -113,6 +127,34 @@ </AndroidTestResultsTableState> </value> </entry> + <entry key="-1531674397"> + <value> + <AndroidTestResultsTableState> + <option name="preferredColumnWidths"> + <map> + <entry key="29211JEGR13699" value="120" /> + <entry key="Duration" value="90" /> + <entry key="Google Pixel 6a" value="120" /> + <entry key="Tests" value="360" /> + </map> + </option> + </AndroidTestResultsTableState> + </value> + </entry> + <entry key="-1528278223"> + <value> + <AndroidTestResultsTableState> + <option name="preferredColumnWidths"> + <map> + <entry key="29211JEGR13699" value="120" /> + <entry key="Duration" value="90" /> + <entry key="Google Pixel 6a" value="120" /> + <entry key="Tests" value="360" /> + </map> + </option> + </AndroidTestResultsTableState> + </value> + </entry> <entry key="-1495363119"> <value> <AndroidTestResultsTableState> @@ -230,6 +272,20 @@ </AndroidTestResultsTableState> </value> </entry> + <entry key="-991781541"> + <value> + <AndroidTestResultsTableState> + <option name="preferredColumnWidths"> + <map> + <entry key="29211JEGR13699" value="120" /> + <entry key="Duration" value="90" /> + <entry key="Google Pixel 6a" value="120" /> + <entry key="Tests" value="360" /> + </map> + </option> + </AndroidTestResultsTableState> + </value> + </entry> <entry key="-693989101"> <value> <AndroidTestResultsTableState> @@ -290,6 +346,20 @@ </AndroidTestResultsTableState> </value> </entry> + <entry key="-91332955"> + <value> + <AndroidTestResultsTableState> + <option name="preferredColumnWidths"> + <map> + <entry key="29211JEGR13699" value="120" /> + <entry key="Duration" value="90" /> + <entry key="Google Pixel 6a" value="120" /> + <entry key="Tests" value="360" /> + </map> + </option> + </AndroidTestResultsTableState> + </value> + </entry> <entry key="-43042216"> <value> <AndroidTestResultsTableState> diff --git a/app/src/androidTest/java/com/futo/polycentric/core/DatabaseTests.kt b/app/src/androidTest/java/com/futo/polycentric/core/DatabaseTests.kt index ace1e20bc8f5030ed461daec5e0e5042d76cf941..6e7f0ded01b7a05f5650d0e3ef6a37e4082e79a3 100644 --- a/app/src/androidTest/java/com/futo/polycentric/core/DatabaseTests.kt +++ b/app/src/androidTest/java/com/futo/polycentric/core/DatabaseTests.kt @@ -1,10 +1,12 @@ package com.futo.polycentric.core import androidx.test.platform.app.InstrumentationRegistry +import kotlinx.coroutines.runBlocking import org.junit.After import org.junit.Assert import org.junit.Before import org.junit.Test +import userpackage.Protocol class DatabaseTests { private lateinit var _db: SqlLiteDbHelper @@ -41,6 +43,83 @@ class DatabaseTests { Assert.assertArrayEquals(handle.processSecret.system.secretKey, secrets[0].system.secretKey) } + @Test + fun deletePost() { + _db.recreate() + + Store.initializeSqlLiteStore(_db) + + //Create handle and post + val processHandle = ProcessHandle.create() + processHandle.addServer(TestConstants.SERVER) + + val ev = processHandle.post("TEST") + runBlocking { + processHandle.fullyBackfillServers() + } + + //Query local for event existing + val sev = Store.instance.getSignedEvent(ev)!! + Assert.assertEquals(ContentType.POST.value, sev.event.contentType) + + var count = 0 + Store.instance.enumerateSignedEvents(ev.system, ContentType.POST) { + count++ + } + Assert.assertEquals(1, count) + + //Query remote for event existing + runBlocking { + val events = ApiMethods.getEvents(TestConstants.SERVER, processHandle.system.toProto(), Protocol.RangesForSystem.newBuilder() + .addRangesForProcesses( + Protocol.RangesForProcess.newBuilder() + .setProcess(processHandle.processSecret.process.toProto()) + .addRanges( + Protocol.Range.newBuilder() + .setLow(ev.logicalClock) + .setHigh(ev.logicalClock) + .build()) + .build()) + .build()) + + Assert.assertEquals(1, events.eventsList.size) + val sevRemote = SignedEvent.fromProto(events.eventsList.first()) + Assert.assertEquals(ContentType.POST.value, sevRemote.event.contentType) + } + + //Delete the event + processHandle.delete(ev.process, ev.logicalClock) + runBlocking { + processHandle.fullyBackfillServers() + } + + //Query local for non existence + Assert.assertNull(null, Store.instance.getSignedEvent(ev)) + + count = 0 + Store.instance.enumerateSignedEvents(ev.system, ContentType.POST) { + count++ + } + Assert.assertEquals(0, count) + + //Query remotely for non existence + runBlocking { + val events = ApiMethods.getEvents(TestConstants.SERVER, processHandle.system.toProto(), Protocol.RangesForSystem.newBuilder() + .addRangesForProcesses( + Protocol.RangesForProcess.newBuilder() + .setProcess(processHandle.processSecret.process.toProto()) + .addRanges( + Protocol.Range.newBuilder() + .setLow(ev.logicalClock) + .setHigh(ev.logicalClock) + .build()) + .build()) + .build()) + + Assert.assertEquals(0, events.eventsList.size) + } + } + @After fun cleanup() { _db.close() diff --git a/app/src/androidTest/java/com/futo/polycentric/core/ProcessHandleTests.kt b/app/src/androidTest/java/com/futo/polycentric/core/ProcessHandleTests.kt index eddd088ab647e2fa33c8eb8991d0841900caf14b..694d91faa29fbccd7eb49c82d9ca2d19bb97c236 100644 --- a/app/src/androidTest/java/com/futo/polycentric/core/ProcessHandleTests.kt +++ b/app/src/androidTest/java/com/futo/polycentric/core/ProcessHandleTests.kt @@ -9,6 +9,7 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import com.google.protobuf.ByteString +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.junit.Assert.* import org.junit.Test @@ -235,6 +236,77 @@ class ProcessHandleTests { assertEquals("alice", serverState2.username) } + @Test + fun deletePost() { + Store.initializeMemoryStore() + + //Create handle and post + val processHandle = ProcessHandle.create() + processHandle.addServer(TestConstants.SERVER) + + val ev = processHandle.post("TEST") + runBlocking { + processHandle.fullyBackfillServers() + } + + //Query local for event existing + val sev = Store.instance.getSignedEvent(ev)!! + assertEquals(ContentType.POST.value, sev.event.contentType) + + var count = 0 + Store.instance.enumerateSignedEvents(ev.system, ContentType.POST) { + count++ + } + assertEquals(1, count) + + //Query remote for event existing + runBlocking { + val events = ApiMethods.getEvents(TestConstants.SERVER, processHandle.system.toProto(), Protocol.RangesForSystem.newBuilder() + .addRangesForProcesses(Protocol.RangesForProcess.newBuilder() + .setProcess(processHandle.processSecret.process.toProto()) + .addRanges(Protocol.Range.newBuilder() + .setLow(ev.logicalClock) + .setHigh(ev.logicalClock) + .build()) + .build()) + .build()) + + assertEquals(1, events.eventsList.size) + val sevRemote = SignedEvent.fromProto(events.eventsList.first()) + assertEquals(ContentType.POST.value, sevRemote.event.contentType) + } + + //Delete the event + processHandle.delete(ev.process, ev.logicalClock) + runBlocking { + processHandle.fullyBackfillServers() + } + + //Query local for non existence + count = 0 + Store.instance.enumerateSignedEvents(ev.system, ContentType.POST) { + count++ + } + assertEquals(0, count) + + assertNull(null, Store.instance.getSignedEvent(ev)) + + //Query remotely for non existence + runBlocking { + val events = ApiMethods.getEvents(TestConstants.SERVER, processHandle.system.toProto(), Protocol.RangesForSystem.newBuilder() + .addRangesForProcesses(Protocol.RangesForProcess.newBuilder() + .setProcess(processHandle.processSecret.process.toProto()) + .addRanges(Protocol.Range.newBuilder() + .setLow(ev.logicalClock) + .setHigh(ev.logicalClock) + .build()) + .build()) + .build()) + + assertEquals(0, events.eventsList.size) + } + } + @Test fun comment() = runTest { Store.initializeMemoryStore() diff --git a/app/src/main/java/com/futo/polycentric/core/MemoryStore.kt b/app/src/main/java/com/futo/polycentric/core/MemoryStore.kt index 54755cd276cf77b90e8d7849c11fc061e4a90cc1..830eba36d809b925e4ce93c12f1a32f25603b5ae 100644 --- a/app/src/main/java/com/futo/polycentric/core/MemoryStore.kt +++ b/app/src/main/java/com/futo/polycentric/core/MemoryStore.kt @@ -56,12 +56,20 @@ class MemoryStore : Store() { override fun updateProcessState(system: PublicKey, process: ByteArray, storageTypeProcessState: StorageTypeProcessState) { } override fun getSignedEvent(system: PublicKey, process: Process, logicalClock: Long): SignedEvent? { - val e = _signedEvents[EventKey(system, process.process, logicalClock)]?.event ?: return null - return SignedEvent.fromProto(e) + val e = _signedEvents[EventKey(system, process.process, logicalClock)] ?: return null + if (!e.hasEvent()) { + return null + } + + return SignedEvent.fromProto(e.event) } override fun enumerateSignedEvents(system: PublicKey, contentType: ContentType?, handler: (SignedEvent) -> Unit) { for (pair in _signedEvents) { + if (!pair.value.hasEvent()) { + continue + } + if (pair.key.system != system) { continue } diff --git a/app/src/main/java/com/futo/polycentric/core/ProcessHandle.kt b/app/src/main/java/com/futo/polycentric/core/ProcessHandle.kt index 05f56d3bb75a36c8fe46703aceb5009751dda8c9..4b4b8c5c798958f4d781d905491d91b38af7a4ee 100644 --- a/app/src/main/java/com/futo/polycentric/core/ProcessHandle.kt +++ b/app/src/main/java/com/futo/polycentric/core/ProcessHandle.kt @@ -252,24 +252,6 @@ class ProcessHandle constructor( Ranges.insert(deleteProcessState.ranges, deleteProto.logicalClock) Store.instance.putTombstone(event.system, deleteProcess.process, deleteProto.logicalClock, signedEvent.toPointer()) - - /* TODO - actions.push( - this._store.deleteIndexClaim( - event.system, - event.process, - event.logicalClock, - ), - )*/ - } else if (event.contentType == ContentType.CLAIM.value) { - /* TODO - actions.push( - this._store.putIndexClaim( - event.system, - event.process, - event.logicalClock, - ), - )*/ } systemState.update(event) diff --git a/app/src/main/java/com/futo/polycentric/core/SqlLiteDbHelper.kt b/app/src/main/java/com/futo/polycentric/core/SqlLiteDbHelper.kt index 70cd702e7bcadfaafe5fc0be25ededcbcf79e149..230484544111c4f54e9d184a916a21078d285205 100644 --- a/app/src/main/java/com/futo/polycentric/core/SqlLiteDbHelper.kt +++ b/app/src/main/java/com/futo/polycentric/core/SqlLiteDbHelper.kt @@ -59,13 +59,38 @@ class SqlLiteDbHelper : SQLiteOpenHelper { currentVersion = 3 } + + if (currentVersion == 3 && newVersion == 4) { + db.execSQL(""" + CREATE TABLE new_signed_events ( + id INTEGER PRIMARY KEY, + public_key VARCHAR, + process VARCHAR, + logical_clock INTEGER, + content_type INTEGER, + value BLOB, + UNIQUE(public_key, process, logical_clock) + ) + """) + + db.execSQL(""" + INSERT INTO new_signed_events (id, public_key, process, logical_clock, content_type, value) + SELECT MAX(id) as id, public_key, process, logical_clock, content_type, value + FROM signed_events + GROUP BY public_key, process, logical_clock + """) + + db.execSQL("DROP TABLE signed_events") + db.execSQL("ALTER TABLE new_signed_events RENAME TO signed_events") + db.execSQL("CREATE INDEX idx_event_pointer ON signed_events (public_key, process, logical_clock)") + } } private fun createTables(db: SQLiteDatabase) { db.execSQL("CREATE TABLE system_states (public_key VARCHAR PRIMARY KEY, value BLOB)") db.execSQL("CREATE TABLE process_states (public_key_process VARCHAR PRIMARY KEY, value BLOB)") db.execSQL("CREATE TABLE process_secrets (public_key VARCHAR PRIMARY KEY, value BLOB)") - db.execSQL("CREATE TABLE signed_events (id INTEGER PRIMARY KEY, public_key VARCHAR, process VARCHAR, logical_clock INTEGER, content_type INTEGER, value BLOB)") + db.execSQL("CREATE TABLE signed_events (id INTEGER PRIMARY KEY, public_key VARCHAR, process VARCHAR, logical_clock INTEGER, content_type INTEGER, value BLOB, UNIQUE(public_key, process, logical_clock))") db.execSQL("CREATE INDEX idx_event_pointer ON signed_events (public_key, process, logical_clock)") } @@ -88,6 +113,6 @@ class SqlLiteDbHelper : SQLiteOpenHelper { companion object { private const val TAG = "SqlLiteDbHelper" private const val DATABASE_NAME = "polycentric_core.db" - private const val DATABASE_VERSION = 3 + private const val DATABASE_VERSION = 4 } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/polycentric/core/SqlLiteStore.kt b/app/src/main/java/com/futo/polycentric/core/SqlLiteStore.kt index 859284937c2a896d910dc44ecc564daa32782e11..624636df294e66c8135a114b73625ffbc3898890 100644 --- a/app/src/main/java/com/futo/polycentric/core/SqlLiteStore.kt +++ b/app/src/main/java/com/futo/polycentric/core/SqlLiteStore.kt @@ -58,7 +58,7 @@ class SqlLiteStore(private val _db: SqlLiteDbHelper) : Store() { .build() _db.writableDatabase.execSQL("REPLACE INTO signed_events(public_key, process, logical_clock, content_type, value) VALUES(?, ?, ?, 0, ?)", - arrayOf(system.key.toBase64(), process.toBase64(), logicalClock.toString(), storageTypeEvent.toByteArray())) + arrayOf(system.key.toBase64(), process.toBase64(), logicalClock.toString(), storageTypeEvent.toByteArray())) } override fun getSignedEvent(system: PublicKey, process: Process, logicalClock: Long): SignedEvent? {