I think I fixed following calculations, again. Revived the test, though it's still very not thorough.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4662 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
f0984b19f2
commit
b1714cf554
97
src/ssb.db.c
97
src/ssb.db.c
@ -1301,6 +1301,7 @@ typedef struct _following_t
|
|||||||
int depth;
|
int depth;
|
||||||
int ref_count;
|
int ref_count;
|
||||||
int block_ref_count;
|
int block_ref_count;
|
||||||
|
bool populated;
|
||||||
} following_t;
|
} following_t;
|
||||||
|
|
||||||
static int _following_compare(const void* a, const void* b)
|
static int _following_compare(const void* a, const void* b)
|
||||||
@ -1310,6 +1311,11 @@ static int _following_compare(const void* a, const void* b)
|
|||||||
return strcmp(ida, (*followingb)->id);
|
return strcmp(ida, (*followingb)->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool _has_following_entry(const char* id, following_t** list, int count)
|
||||||
|
{
|
||||||
|
return bsearch(id, list, count, sizeof(following_t*), _following_compare) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
static bool _add_following_entry(following_t*** list, int* count, following_t* add)
|
static bool _add_following_entry(following_t*** list, int* count, following_t* add)
|
||||||
{
|
{
|
||||||
int index = tf_util_insert_index(add->id, *list, *count, sizeof(following_t*), _following_compare);
|
int index = tf_util_insert_index(add->id, *list, *count, sizeof(following_t*), _following_compare);
|
||||||
@ -1343,19 +1349,37 @@ static bool _remove_following_entry(following_t*** list, int* count, following_t
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static following_t* _get_following(tf_ssb_t* ssb, const char* id, following_t*** following, int* following_count, int depth, int max_depth)
|
typedef struct _block_node_t block_node_t;
|
||||||
|
|
||||||
|
typedef struct _block_node_t
|
||||||
{
|
{
|
||||||
|
following_t* entry;
|
||||||
|
block_node_t* parent;
|
||||||
|
} block_node_t;
|
||||||
|
|
||||||
|
static bool _is_blocked_by_active_blocks(const char* id, const block_node_t* blocks)
|
||||||
|
{
|
||||||
|
for (const block_node_t* b = blocks; b; b = b->parent)
|
||||||
|
{
|
||||||
|
if (_has_following_entry(id, b->entry->blocking, b->entry->blocking_count))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static following_t* _make_following_node(const char* id, following_t*** following, int* following_count, block_node_t* blocks)
|
||||||
|
{
|
||||||
|
if (_is_blocked_by_active_blocks(id, blocks))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
int index = tf_util_insert_index(id, *following, *following_count, sizeof(following_t*), _following_compare);
|
int index = tf_util_insert_index(id, *following, *following_count, sizeof(following_t*), _following_compare);
|
||||||
following_t* entry = NULL;
|
following_t* entry = NULL;
|
||||||
bool already_populated = false;
|
|
||||||
if (index < *following_count && strcmp(id, (*following)[index]->id) == 0)
|
if (index < *following_count && strcmp(id, (*following)[index]->id) == 0)
|
||||||
{
|
{
|
||||||
entry = (*following)[index];
|
entry = (*following)[index];
|
||||||
already_populated = entry->depth < max_depth;
|
|
||||||
if (depth < entry->depth)
|
|
||||||
{
|
|
||||||
entry->depth = depth;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1368,29 +1392,34 @@ static following_t* _get_following(tf_ssb_t* ssb, const char* id, following_t***
|
|||||||
(*following)[index] = entry;
|
(*following)[index] = entry;
|
||||||
(*following_count)++;
|
(*following_count)++;
|
||||||
memset(entry, 0, sizeof(*entry));
|
memset(entry, 0, sizeof(*entry));
|
||||||
|
entry->depth = INT_MAX;
|
||||||
snprintf(entry->id, sizeof(entry->id), "%s", id);
|
snprintf(entry->id, sizeof(entry->id), "%s", id);
|
||||||
entry->depth = depth;
|
|
||||||
}
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
if (depth < max_depth && !already_populated)
|
static void _populate_follows_and_blocks(tf_ssb_t* ssb, following_t* entry, following_t*** following, int* following_count, block_node_t* active_blocks)
|
||||||
{
|
{
|
||||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
sqlite3_stmt* statement = NULL;
|
sqlite3_stmt* statement = NULL;
|
||||||
if (sqlite3_prepare(db, "SELECT json_extract(content, '$.contact'), json_extract(content, '$.following'), json_extract(content, '$.blocking') FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'contact' ORDER BY sequence", -1, &statement, NULL) == SQLITE_OK)
|
if (sqlite3_prepare(db,
|
||||||
|
"SELECT json_extract(content, '$.contact') AS contact, json_extract(content, '$.following'), json_extract(content, '$.blocking') "
|
||||||
|
"FROM messages "
|
||||||
|
"WHERE contact IS NOT NULL AND author = ? AND json_extract(content, '$.type') = 'contact' "
|
||||||
|
"ORDER BY sequence",
|
||||||
|
-1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK)
|
if (sqlite3_bind_text(statement, 1, entry->id, -1, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
while (sqlite3_step(statement) == SQLITE_ROW)
|
while (sqlite3_step(statement) == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
const char* contact = (const char*)sqlite3_column_text(statement, 0);
|
const char* contact = (const char*)sqlite3_column_text(statement, 0);
|
||||||
if (!contact)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (sqlite3_column_type(statement, 1) != SQLITE_NULL)
|
if (sqlite3_column_type(statement, 1) != SQLITE_NULL)
|
||||||
{
|
{
|
||||||
bool is_following = sqlite3_column_int(statement, 1) != 0;
|
bool is_following = sqlite3_column_int(statement, 1) != 0;
|
||||||
following_t* next = _get_following(ssb, contact, following, following_count, depth + 1, max_depth);
|
following_t* next = _make_following_node(contact, following, following_count, active_blocks);
|
||||||
|
if (next)
|
||||||
|
{
|
||||||
if (is_following)
|
if (is_following)
|
||||||
{
|
{
|
||||||
if (_add_following_entry(&entry->following, &entry->following_count, next))
|
if (_add_following_entry(&entry->following, &entry->following_count, next))
|
||||||
@ -1406,10 +1435,13 @@ static following_t* _get_following(tf_ssb_t* ssb, const char* id, following_t***
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (sqlite3_column_type(statement, 2) != SQLITE_NULL)
|
if (sqlite3_column_type(statement, 2) != SQLITE_NULL)
|
||||||
{
|
{
|
||||||
bool is_blocking = sqlite3_column_int(statement, 2) != 0;
|
bool is_blocking = sqlite3_column_int(statement, 2) != 0;
|
||||||
following_t* next = _get_following(ssb, contact, following, following_count, depth + 1, 0 /* don't dig deeper into blocked users */);
|
following_t* next = _make_following_node(contact, following, following_count, active_blocks);
|
||||||
|
if (next)
|
||||||
|
{
|
||||||
if (is_blocking)
|
if (is_blocking)
|
||||||
{
|
{
|
||||||
if (_add_following_entry(&entry->blocking, &entry->blocking_count, next))
|
if (_add_following_entry(&entry->blocking, &entry->blocking_count, next))
|
||||||
@ -1427,11 +1459,32 @@ static following_t* _get_following(tf_ssb_t* ssb, const char* id, following_t***
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
sqlite3_finalize(statement);
|
sqlite3_finalize(statement);
|
||||||
}
|
}
|
||||||
tf_ssb_release_db_reader(ssb, db);
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _get_following(tf_ssb_t* ssb, following_t* entry, following_t*** following, int* following_count, int depth, int max_depth, block_node_t* active_blocks)
|
||||||
|
{
|
||||||
|
entry->depth = tf_min(depth, entry->depth);
|
||||||
|
if (depth < max_depth && !entry->populated && !_is_blocked_by_active_blocks(entry->id, active_blocks))
|
||||||
|
{
|
||||||
|
entry->populated = true;
|
||||||
|
_populate_follows_and_blocks(ssb, entry, following, following_count, active_blocks);
|
||||||
|
|
||||||
|
if (depth < max_depth)
|
||||||
|
{
|
||||||
|
block_node_t blocks = { .entry = entry, .parent = active_blocks };
|
||||||
|
for (int i = 0; i < entry->following_count; i++)
|
||||||
|
{
|
||||||
|
if (!_has_following_entry(entry->following[i]->id, entry->blocking, entry->blocking_count))
|
||||||
|
{
|
||||||
|
_get_following(ssb, entry->following[i], following, following_count, depth + 1, max_depth, &blocks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return entry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tf_ssb_following_t* tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, int count, int depth)
|
tf_ssb_following_t* tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, int count, int depth)
|
||||||
@ -1440,7 +1493,8 @@ tf_ssb_following_t* tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, in
|
|||||||
int following_count = 0;
|
int following_count = 0;
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
following_t* entry = _get_following(ssb, ids[i], &following, &following_count, 0, depth);
|
following_t* entry = _make_following_node(ids[i], &following, &following_count, NULL);
|
||||||
|
_get_following(ssb, entry, &following, &following_count, 0, depth, NULL);
|
||||||
entry->ref_count++;
|
entry->ref_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1487,7 +1541,8 @@ const char** tf_ssb_db_following_deep_ids(tf_ssb_t* ssb, const char** ids, int c
|
|||||||
int following_count = 0;
|
int following_count = 0;
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
following_t* entry = _get_following(ssb, ids[i], &following, &following_count, 0, depth);
|
following_t* entry = _make_following_node(ids[i], &following, &following_count, NULL);
|
||||||
|
_get_following(ssb, entry, &following, &following_count, 0, depth, NULL);
|
||||||
entry->ref_count++;
|
entry->ref_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
119
src/ssb.tests.c
119
src/ssb.tests.c
@ -496,6 +496,22 @@ void tf_ssb_test_rooms(const tf_test_options_t* options)
|
|||||||
uv_loop_close(&loop);
|
uv_loop_close(&loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _assert_visible(tf_ssb_t* ssb, const char* id, const char* contact, bool visible)
|
||||||
|
{
|
||||||
|
const char** ids = tf_ssb_db_following_deep_ids(ssb, &id, 1, 2);
|
||||||
|
bool found = false;
|
||||||
|
for (int i = 0; ids[i]; i++)
|
||||||
|
{
|
||||||
|
if (strcmp(ids[i], contact) == 0)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tf_free(ids);
|
||||||
|
assert(found == visible);
|
||||||
|
}
|
||||||
|
|
||||||
void tf_ssb_test_following(const tf_test_options_t* options)
|
void tf_ssb_test_following(const tf_test_options_t* options)
|
||||||
{
|
{
|
||||||
tf_printf("Testing following.\n");
|
tf_printf("Testing following.\n");
|
||||||
@ -503,91 +519,68 @@ void tf_ssb_test_following(const tf_test_options_t* options)
|
|||||||
uv_loop_t loop = { 0 };
|
uv_loop_t loop = { 0 };
|
||||||
uv_loop_init(&loop);
|
uv_loop_init(&loop);
|
||||||
|
|
||||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, ":memory:");
|
unlink("out/test_db0.sqlite");
|
||||||
|
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite");
|
||||||
tf_ssb_generate_keys(ssb0);
|
tf_ssb_generate_keys(ssb0);
|
||||||
|
|
||||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, ":memory:");
|
char id0[k_id_base64_len] = { "@" };
|
||||||
tf_ssb_generate_keys(ssb1);
|
char id1[k_id_base64_len] = { "@" };
|
||||||
|
char id2[k_id_base64_len] = { "@" };
|
||||||
tf_ssb_t* ssb2 = tf_ssb_create(&loop, NULL, ":memory:");
|
char id3[k_id_base64_len] = { "@" };
|
||||||
tf_ssb_generate_keys(ssb2);
|
char priv0b[512] = { 0 };
|
||||||
|
char priv1b[512] = { 0 };
|
||||||
char id0[k_id_base64_len] = { 0 };
|
char priv2b[512] = { 0 };
|
||||||
char id1[k_id_base64_len] = { 0 };
|
char priv3b[512] = { 0 };
|
||||||
char id2[k_id_base64_len] = { 0 };
|
|
||||||
tf_ssb_whoami(ssb0, id0, sizeof(id0));
|
|
||||||
tf_ssb_whoami(ssb1, id1, sizeof(id1));
|
|
||||||
tf_ssb_whoami(ssb2, id2, sizeof(id2));
|
|
||||||
|
|
||||||
uint8_t priv0[512] = { 0 };
|
uint8_t priv0[512] = { 0 };
|
||||||
uint8_t priv1[512] = { 0 };
|
uint8_t priv1[512] = { 0 };
|
||||||
uint8_t priv2[512] = { 0 };
|
uint8_t priv2[512] = { 0 };
|
||||||
tf_ssb_get_private_key(ssb0, priv0, sizeof(priv0));
|
uint8_t priv3[512] = { 0 };
|
||||||
tf_ssb_get_private_key(ssb1, priv1, sizeof(priv1));
|
|
||||||
tf_ssb_get_private_key(ssb2, priv2, sizeof(priv2));
|
|
||||||
|
|
||||||
JSContext* context = NULL;
|
tf_ssb_generate_keys_buffer(id0 + 1, sizeof(id0) - 1, priv0b, sizeof(priv0b));
|
||||||
|
tf_ssb_generate_keys_buffer(id1 + 1, sizeof(id1) - 1, priv1b, sizeof(priv1b));
|
||||||
|
tf_ssb_generate_keys_buffer(id2 + 1, sizeof(id2) - 1, priv2b, sizeof(priv2b));
|
||||||
|
tf_ssb_generate_keys_buffer(id3 + 1, sizeof(id3) - 1, priv3b, sizeof(priv3b));
|
||||||
|
tf_base64_decode(priv0b, strlen(priv0b), priv0, sizeof(priv0));
|
||||||
|
tf_base64_decode(priv1b, strlen(priv1b), priv1, sizeof(priv1));
|
||||||
|
tf_base64_decode(priv2b, strlen(priv2b), priv2, sizeof(priv2));
|
||||||
|
tf_base64_decode(priv3b, strlen(priv3b), priv3, sizeof(priv3));
|
||||||
|
|
||||||
|
JSContext* context = tf_ssb_get_context(ssb0);
|
||||||
JSValue message;
|
JSValue message;
|
||||||
JSValue signed_message;
|
JSValue signed_message;
|
||||||
bool stored;
|
bool stored;
|
||||||
|
|
||||||
#define FOLLOW(ssb, id, priv, follow) \
|
#define FOLLOW_BLOCK(id, priv, contact, follow, block) \
|
||||||
context = tf_ssb_get_context(ssb); \
|
|
||||||
message = JS_NewObject(context); \
|
message = JS_NewObject(context); \
|
||||||
JS_SetPropertyStr(context, message, "type", JS_NewString(context, "contact")); \
|
JS_SetPropertyStr(context, message, "type", JS_NewString(context, "contact")); \
|
||||||
JS_SetPropertyStr(context, message, "contact", JS_NewString(context, id)); \
|
JS_SetPropertyStr(context, message, "contact", JS_NewString(context, contact)); \
|
||||||
JS_SetPropertyStr(context, message, "following", follow ? JS_TRUE : JS_FALSE); \
|
JS_SetPropertyStr(context, message, "following", follow ? JS_TRUE : JS_FALSE); \
|
||||||
signed_message = tf_ssb_sign_message(ssb, id, priv, message); \
|
JS_SetPropertyStr(context, message, "blocking", block ? JS_TRUE : JS_FALSE); \
|
||||||
|
signed_message = tf_ssb_sign_message(ssb0, id, priv, message); \
|
||||||
stored = false; \
|
stored = false; \
|
||||||
tf_ssb_verify_strip_and_store_message(ssb, signed_message, _message_stored, &stored); \
|
tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored); \
|
||||||
_wait_stored(ssb, &stored); \
|
_wait_stored(ssb0, &stored); \
|
||||||
JS_FreeValue(context, signed_message); \
|
JS_FreeValue(context, signed_message); \
|
||||||
JS_FreeValue(context, message); \
|
JS_FreeValue(context, message); \
|
||||||
context = NULL
|
|
||||||
|
|
||||||
#if 1
|
FOLLOW_BLOCK(id0, priv0, id1, true, false);
|
||||||
/* TODO: This test doesn't actually really test anything anymore. */
|
FOLLOW_BLOCK(id1, priv1, id2, true, false);
|
||||||
#define DUMP(id, depth)
|
FOLLOW_BLOCK(id1, priv1, id3, true, false);
|
||||||
#else
|
_assert_visible(ssb0, id0, id0, true);
|
||||||
#define DUMP(id, depth) \
|
_assert_visible(ssb0, id0, id1, true);
|
||||||
do \
|
_assert_visible(ssb0, id0, id2, true);
|
||||||
{ \
|
_assert_visible(ssb0, id0, id3, true);
|
||||||
tf_printf("following %d:\n", depth); \
|
FOLLOW_BLOCK(id0, priv0, id3, false, true);
|
||||||
const char** tf_ssb_get_following_deep(tf_ssb_t* ssb_param, const char** ids, int depth_param); \
|
_assert_visible(ssb0, id0, id0, true);
|
||||||
const char** f = tf_ssb_get_following_deep(ssb0, (const char*[]) { id, NULL }, depth); \
|
_assert_visible(ssb0, id0, id1, true);
|
||||||
for (const char** p = f; p && *p; p++) \
|
_assert_visible(ssb0, id0, id2, true);
|
||||||
{ \
|
_assert_visible(ssb0, id0, id3, false);
|
||||||
tf_printf("* %s\n", *p); \
|
|
||||||
} \
|
|
||||||
tf_printf("\n"); \
|
|
||||||
tf_free(f); \
|
|
||||||
} \
|
|
||||||
while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
FOLLOW(ssb0, id1, priv1, true);
|
#undef FOLLOW_BLOCK
|
||||||
FOLLOW(ssb1, id2, priv2, true);
|
|
||||||
FOLLOW(ssb2, id0, priv0, true);
|
|
||||||
DUMP(id0, 2);
|
|
||||||
DUMP(id1, 2);
|
|
||||||
DUMP(id2, 2);
|
|
||||||
FOLLOW(ssb0, id1, priv1, false);
|
|
||||||
//FOLLOW(ssb0, id1, priv1, true);
|
|
||||||
//FOLLOW(ssb0, id1, priv1, true);
|
|
||||||
DUMP(id0, 1);
|
|
||||||
DUMP(id1, 2);
|
|
||||||
//FOLLOW(ssb0, id1, priv1, false);
|
|
||||||
//DUMP(1);
|
|
||||||
//DUMP(1);
|
|
||||||
|
|
||||||
#undef FOLLOW
|
|
||||||
#undef DUMP
|
|
||||||
|
|
||||||
uv_run(&loop, UV_RUN_DEFAULT);
|
uv_run(&loop, UV_RUN_DEFAULT);
|
||||||
|
|
||||||
tf_ssb_destroy(ssb0);
|
tf_ssb_destroy(ssb0);
|
||||||
tf_ssb_destroy(ssb1);
|
|
||||||
tf_ssb_destroy(ssb2);
|
|
||||||
|
|
||||||
uv_loop_close(&loop);
|
uv_loop_close(&loop);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user