I guess we support sub-millisecond timestamps. Who knew?

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3835 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
Cory McWilliams 2022-02-12 01:44:11 +00:00
parent 50bef73200
commit 483638a7e6
3 changed files with 76 additions and 36 deletions

View File

@ -9,49 +9,56 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
static void _tf_ssb_db_exec(sqlite3* db, const char* statement)
{
char* error = NULL;
int result = sqlite3_exec(db, statement, NULL, NULL, &error);
if (result != SQLITE_OK)
{
printf("Error running '%s': %s.\n", statement, error);
abort();
}
}
void tf_ssb_db_init(tf_ssb_t* ssb) void tf_ssb_db_init(tf_ssb_t* ssb)
{ {
sqlite3* db = tf_ssb_get_db(ssb); sqlite3* db = tf_ssb_get_db(ssb);
sqlite3_exec(db, "PRAGMA journal_mode = WAL", NULL, NULL, NULL); _tf_ssb_db_exec(db, "PRAGMA journal_mode = WAL");
sqlite3_exec(db, "PRAGMA synchronous = NORMAL", NULL, NULL, NULL); _tf_ssb_db_exec(db, "PRAGMA synchronous = NORMAL");
sqlite3_exec(db, _tf_ssb_db_exec(db,
"CREATE TABLE IF NOT EXISTS messages (" "CREATE TABLE IF NOT EXISTS messages ("
" author TEXT," " author TEXT,"
" id TEXT PRIMARY KEY," " id TEXT PRIMARY KEY,"
" sequence INTEGER," " sequence INTEGER,"
" timestamp INTEGER," " timestamp REAL,"
" previous TEXT," " previous TEXT,"
" hash TEXT," " hash TEXT,"
" content TEXT," " content TEXT,"
" signature TEXT," " signature TEXT,"
" sequence_before_author INTEGER,"
" UNIQUE(author, sequence)" " UNIQUE(author, sequence)"
")", ")");
NULL, NULL, NULL); _tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_id_index ON messages (author, id)");
sqlite3_exec(db, "ALTER TABLE messages ADD COLUMN sequence_before_author INTEGER", NULL, NULL, NULL); _tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_sequence_index ON messages (author, sequence)");
sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_id_index ON messages (author, id)", NULL, NULL, NULL); _tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp)");
sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_sequence_index ON messages (author, sequence)", NULL, NULL, NULL); _tf_ssb_db_exec(db,
sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp)", NULL, NULL, NULL);
sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS blobs (" "CREATE TABLE IF NOT EXISTS blobs ("
" id TEXT PRIMARY KEY," " id TEXT PRIMARY KEY,"
" content BLOB," " content BLOB,"
" created INTEGER" " created INTEGER"
")", ")");
NULL, NULL, NULL); _tf_ssb_db_exec(db,
sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS blob_wants (" "CREATE TABLE IF NOT EXISTS blob_wants ("
" id TEXT PRIMARY KEY" " id TEXT PRIMARY KEY"
")", ")");
NULL, NULL, NULL); _tf_ssb_db_exec(db,
sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS properties (" "CREATE TABLE IF NOT EXISTS properties ("
" id TEXT," " id TEXT,"
" key TEXT," " key TEXT,"
" value TEXT," " value TEXT,"
" UNIQUE(id, key)" " UNIQUE(id, key)"
")", ")");
NULL, NULL, NULL); _tf_ssb_db_exec(db,
sqlite3_exec(db,
"CREATE TABLE IF NOT EXISTS connections (" "CREATE TABLE IF NOT EXISTS connections ("
" host TEXT," " host TEXT,"
" port INTEGER," " port INTEGER,"
@ -59,8 +66,41 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
" last_attempt INTEGER," " last_attempt INTEGER,"
" last_success INTEGER," " last_success INTEGER,"
" UNIQUE(host, port, key)" " UNIQUE(host, port, key)"
")", ")");
NULL, NULL, NULL);
bool need_add_sequence_before_author = true;
bool need_convert_timestamp_to_real = false;
sqlite3_stmt* statement = NULL;
if (sqlite3_prepare(db, "PRAGMA table_info(messages)", -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
const char* name = (const char*)sqlite3_column_text(statement, 1);
const char* type = (const char*)sqlite3_column_text(statement, 1);
if (name && type && strcmp(name, "timestamp") == 0 && strcmp(type, "INTEGER") == 0)
{
need_convert_timestamp_to_real = true;
}
if (name && strcmp(name, "sequence_before_author") == 0)
{
need_add_sequence_before_author = false;
}
}
sqlite3_finalize(statement);
}
if (need_convert_timestamp_to_real)
{
_tf_ssb_db_exec(db, "ALTER TABLE messages ADD COLUMN timestamp_real REAL");
_tf_ssb_db_exec(db, "UPDATE messages SET timestamp_real = timestamp");
_tf_ssb_db_exec(db, "ALTER TABLE messages DROP COLUMN timestamp REAL");
_tf_ssb_db_exec(db, "ALTER TABLE messages RENAME COLUMN timestamp_real TO timestamp");
}
if (need_add_sequence_before_author)
{
_tf_ssb_db_exec(db, "ALTER TABLE messages ADD COLUMN sequence_before_author INTEGER");
}
} }
static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author, int64_t sequence, const char* previous) static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author, int64_t sequence, const char* previous)
@ -97,8 +137,8 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id,
const char* author = JS_ToCString(context, authorval); const char* author = JS_ToCString(context, authorval);
int64_t sequence = -1; int64_t sequence = -1;
JS_ToInt64(context, &sequence, JS_GetPropertyStr(context, val, "sequence")); JS_ToInt64(context, &sequence, JS_GetPropertyStr(context, val, "sequence"));
int64_t timestamp = -1; double timestamp = -1.0;
JS_ToInt64(context, &timestamp, JS_GetPropertyStr(context, val, "timestamp")); JS_ToFloat64(context, &timestamp, JS_GetPropertyStr(context, val, "timestamp"));
JSValue contentval = JS_GetPropertyStr(context, val, "content"); JSValue contentval = JS_GetPropertyStr(context, val, "content");
JSValue content = JS_JSONStringify(context, contentval, JS_NULL, JS_NULL); JSValue content = JS_JSONStringify(context, contentval, JS_NULL, JS_NULL);
@ -119,7 +159,7 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id,
(previous ? sqlite3_bind_text(statement, 2, previous, -1, NULL) : sqlite3_bind_null(statement, 2)) == SQLITE_OK && (previous ? sqlite3_bind_text(statement, 2, previous, -1, NULL) : sqlite3_bind_null(statement, 2)) == SQLITE_OK &&
sqlite3_bind_text(statement, 3, author, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 3, author, -1, NULL) == SQLITE_OK &&
sqlite3_bind_int64(statement, 4, sequence) == SQLITE_OK && sqlite3_bind_int64(statement, 4, sequence) == SQLITE_OK &&
sqlite3_bind_int64(statement, 5, timestamp) == SQLITE_OK && sqlite3_bind_double(statement, 5, timestamp) == SQLITE_OK &&
sqlite3_bind_text(statement, 6, contentstr, content_len, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 6, contentstr, content_len, NULL) == SQLITE_OK &&
sqlite3_bind_text(statement, 7, "sha256", 6, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 7, "sha256", 6, NULL) == SQLITE_OK &&
sqlite3_bind_text(statement, 8, signature, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 8, signature, -1, NULL) == SQLITE_OK &&
@ -304,7 +344,7 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char*
return result; return result;
} }
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, int64_t* out_timestamp, char** out_content) bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, double* out_timestamp, char** out_content)
{ {
bool found = false; bool found = false;
sqlite3_stmt* statement; sqlite3_stmt* statement;
@ -321,7 +361,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
} }
if (out_timestamp) if (out_timestamp)
{ {
*out_timestamp = sqlite3_column_int64(statement, 1); *out_timestamp = sqlite3_column_double(statement, 1);
} }
if (out_content) if (out_content)
{ {
@ -508,7 +548,7 @@ void tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue binds
sqlite3_set_authorizer(db, NULL, NULL); sqlite3_set_authorizer(db, NULL, NULL);
} }
static JSValue _tf_ssb_format_message(JSContext* context, const char* previous, const char* author, int64_t sequence, int64_t timestamp, const char* hash, const char* content, const char* signature, bool sequence_before_author) static JSValue _tf_ssb_format_message(JSContext* context, const char* previous, const char* author, int64_t sequence, double timestamp, const char* hash, const char* content, const char* signature, bool sequence_before_author)
{ {
JSValue value = JS_NewObject(context); JSValue value = JS_NewObject(context);
JS_SetPropertyStr(context, value, "previous", previous ? JS_NewString(context, previous) : JS_NULL); JS_SetPropertyStr(context, value, "previous", previous ? JS_NewString(context, previous) : JS_NULL);
@ -522,7 +562,7 @@ static JSValue _tf_ssb_format_message(JSContext* context, const char* previous,
JS_SetPropertyStr(context, value, "author", JS_NewString(context, author)); JS_SetPropertyStr(context, value, "author", JS_NewString(context, author));
JS_SetPropertyStr(context, value, "sequence", JS_NewInt64(context, sequence)); JS_SetPropertyStr(context, value, "sequence", JS_NewInt64(context, sequence));
} }
JS_SetPropertyStr(context, value, "timestamp", JS_NewInt64(context, timestamp)); JS_SetPropertyStr(context, value, "timestamp", JS_NewFloat64(context, timestamp));
JS_SetPropertyStr(context, value, "hash", JS_NewString(context, hash)); JS_SetPropertyStr(context, value, "hash", JS_NewString(context, hash));
JS_SetPropertyStr(context, value, "content", JS_ParseJSON(context, content, strlen(content), NULL)); JS_SetPropertyStr(context, value, "content", JS_ParseJSON(context, content, strlen(content), NULL));
JS_SetPropertyStr(context, value, "signature", JS_NewString(context, signature)); JS_SetPropertyStr(context, value, "signature", JS_NewString(context, signature));
@ -567,7 +607,7 @@ bool tf_ssb_db_check(sqlite3* db, const char* check_author)
const char* previous = (const char*)sqlite3_column_text(statement, 1); const char* previous = (const char*)sqlite3_column_text(statement, 1);
const char* author = (const char*)sqlite3_column_text(statement, 2); const char* author = (const char*)sqlite3_column_text(statement, 2);
int64_t sequence = sqlite3_column_int64(statement, 3); int64_t sequence = sqlite3_column_int64(statement, 3);
int64_t timestamp = sqlite3_column_int64(statement, 4); double timestamp = sqlite3_column_double(statement, 4);
const char* hash = (const char*)sqlite3_column_text(statement, 5); const char* hash = (const char*)sqlite3_column_text(statement, 5);
const char* content = (const char*)sqlite3_column_text(statement, 6); const char* content = (const char*)sqlite3_column_text(statement, 6);
const char* signature = (const char*)sqlite3_column_text(statement, 7); const char* signature = (const char*)sqlite3_column_text(statement, 7);
@ -580,10 +620,10 @@ bool tf_ssb_db_check(sqlite3* db, const char* check_author)
const char* jv = JS_ToCString(context, j); const char* jv = JS_ToCString(context, j);
if (tf_ssb_verify_and_strip_signature(context, message, actual_id, sizeof(actual_id), out_signature, sizeof(out_signature), &actual_sequence_before_author)) if (tf_ssb_verify_and_strip_signature(context, message, actual_id, sizeof(actual_id), out_signature, sizeof(out_signature), &actual_sequence_before_author))
{ {
/*if (previous && strcmp(previous, previous_id)) if (previous && strcmp(previous, previous_id))
{ {
printf("%s:%d previous was %s should be %s\n", id, (int)sequence, previous_id, previous); printf("%s:%d previous was %s should be %s\n", id, (int)sequence, previous_id, previous);
}*/ }
if (strcmp(id, actual_id)) if (strcmp(id, actual_id))
{ {
if (_tf_ssb_update_message_id(db, id, actual_id)) if (_tf_ssb_update_message_id(db, id, actual_id))
@ -598,7 +638,7 @@ bool tf_ssb_db_check(sqlite3* db, const char* check_author)
} }
else else
{ {
printf("unable to verify signature for %s\n", id); printf("%s sequence=%" PRId64 " unable to verify signature for %s sequence_before_author=%d message=[%.*s]\n", author, sequence, id, sequence_before_author, (int)strlen(jv), jv);
} }
JS_FreeCString(context, jv); JS_FreeCString(context, jv);
JS_FreeValue(context, j); JS_FreeValue(context, j);

View File

@ -11,7 +11,7 @@ bool tf_ssb_db_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_
bool tf_ssb_db_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_t* out_size); bool tf_ssb_db_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_t* out_size);
bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char* out_id, size_t out_id_size); bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char* out_id, size_t out_id_size);
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, int64_t* out_timestamp, char** out_content); bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, double* out_timestamp, char** out_content);
bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, int64_t* out_sequence, char* out_message_id, size_t out_message_id_size); bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, int64_t* out_sequence, char* out_message_id, size_t out_message_id_size);
void tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue binds, void (*callback)(JSValue row, void* user_data), void* user_data); void tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue binds, void (*callback)(JSValue row, void* user_data), void* user_data);

View File

@ -41,12 +41,12 @@ static JSValue _tf_ssb_getMessage(JSContext* context, JSValueConst this_val, int
const char* id = JS_ToCString(context, argv[0]); const char* id = JS_ToCString(context, argv[0]);
int64_t sequence = 0; int64_t sequence = 0;
JS_ToInt64(context, &sequence, argv[1]); JS_ToInt64(context, &sequence, argv[1]);
int64_t timestamp = -1; double timestamp = -1.0;
char* contents = NULL; char* contents = NULL;
if (tf_ssb_db_get_message_by_author_and_sequence(ssb, id, sequence, NULL, 0, &timestamp, &contents)) if (tf_ssb_db_get_message_by_author_and_sequence(ssb, id, sequence, NULL, 0, &timestamp, &contents))
{ {
result = JS_NewObject(context); result = JS_NewObject(context);
JS_SetPropertyStr(context, result, "timestamp", JS_NewInt64(context, timestamp)); JS_SetPropertyStr(context, result, "timestamp", JS_NewFloat64(context, timestamp));
JS_SetPropertyStr(context, result, "content", JS_NewString(context, contents)); JS_SetPropertyStr(context, result, "content", JS_NewString(context, contents));
free(contents); free(contents);
} }