@ -136,7 +136,7 @@ static void mon(void* arg) {
if ( ce - > conn & & ce - > conn - > links ) { struct ETCP_LINK * l ; for ( l = ce - > conn - > links ; l ; l = l - > next )
if ( l - > initialized & & ce - > conn - > crypto_ctx . initialized ) ok + + ; } e = e - > next ; }
}
if ( ok > = N_NODES * 2 - 2 ) g_connected = 1 ;
if ( ok > = N_NODES * ( N_NODES - 1 ) ) g_connected = 1 ; /* полный mesh: N*(N-1) линков */
if ( ! g_result ) uasync_set_timeout ( g_ua , 10 , NULL , mon , " mdc_mon " ) ;
}
@ -162,6 +162,28 @@ static int wait_pubkeys(void) {
return 0 ;
}
/* ждём, пока BGP CHAT-группы сойдутся: каждый узел имеет маршрут (path) до каждого
* д р у г о г о м е м б е р а в с в о е й CHAT - г р у п п е . Б е з э т о г о media - д о с т а в к а « ч е р е з г р у п п у » н е и м е е т
* м а р ш р у т а ( topo_group_find_conn_for_node = = NULL ) и в с т а ё т с « no route » . */
static int wait_chat_bgp ( void ) {
int a = 0 ;
while ( a < 8000 ) {
int ok = 1 ;
for ( int i = 0 ; i < N_NODES & & ok ; i + + ) {
struct TOPO_GROUP * g = topo_groups_find ( g_inst [ i ] - > topo_groups , g_group_id ) ;
if ( ! g ) { ok = 0 ; break ; }
for ( int m = 0 ; m < N_NODES ; m + + ) {
if ( m = = i ) continue ;
struct TOPO_GROUP_NODE * nq = topo_node_find_by_id ( g , g_nid [ m ] ) ;
if ( ! nq | | ! nq - > paths | | ! nq - > paths - > head ) { ok = 0 ; break ; }
}
}
if ( ok ) return 1 ;
uasync_poll ( g_ua , POLL_MS ) ; a + + ;
}
return 0 ;
}
static void sha256_buf ( const uint8_t * data , size_t len , uint8_t out [ 32 ] ) {
EVP_MD_CTX * ctx = EVP_MD_CTX_new ( ) ;
EVP_DigestInit_ex ( ctx , EVP_sha256 ( ) , NULL ) ;
@ -193,6 +215,16 @@ static void ensure_supernode_type(int idx) {
sqlite3_exec ( g_inst [ idx ] - > topo_sqlite_db , sql , NULL , NULL , NULL ) ;
}
/* прямой send media-пакета через CHAT-группу (доставка идёт по группе канала) */
static int msend ( struct UTUN_INSTANCE * inst , uint64_t dst , const uint8_t * data , size_t len ) {
struct ll_entry * e = queue_entry_new ( 0 ) ; if ( ! e ) return - 1 ;
e - > dgram = u_malloc ( len + 1 ) ; if ( ! e - > dgram ) { queue_entry_free ( e ) ; return - 1 ; }
e - > dgram [ 0 ] = ETCP_RT_ID_MEDIA_DELIVERY ; memcpy ( e - > dgram + 1 , data , len ) ; e - > len = ( uint16_t ) ( len + 1 ) ;
int rc = etcp_route_send ( inst , g_group_id , dst , e , 1 , 0 ) ;
if ( rc ! = 0 ) { u_free ( e - > dgram ) ; queue_entry_free ( e ) ; }
return rc ;
}
/* ── завершение загрузки ── */
static void dl_done_cb ( void * arg , int err ) {
@ -317,6 +349,14 @@ static int author_index_file(void) {
int rc = media_index_commit ( g_inst [ I_N1 ] - > topo_sqlite_db , & result , g_nid [ I_N1 ] ,
g_inst [ I_N1 ] - > my_ed25519_privkey , CH_ID , dst_path , media_base ) ;
/* ui_state.media_base — откуда block_req-хендлер открывает файл на стриминг */
{
sqlite3_exec ( g_inst [ I_N1 ] - > topo_sqlite_db , " CREATE TABLE IF NOT EXISTS ui_state(key TEXT PRIMARY KEY, value TEXT) " , NULL , NULL , NULL ) ;
sqlite3_stmt * us = NULL ;
sqlite3_prepare_v2 ( g_inst [ I_N1 ] - > topo_sqlite_db , " INSERT OR REPLACE INTO ui_state(key,value) VALUES('media_base',?) " , - 1 , & us , NULL ) ;
sqlite3_bind_text ( us , 1 , media_base , - 1 , SQLITE_STATIC ) ; sqlite3_step ( us ) ; sqlite3_finalize ( us ) ;
}
/* сохраняем глобально */
memcpy ( g_media_id , result . media_id , 16 ) ;
for ( int n = 0 ; n < MEDIA_NUM_BLOCKS ; n + + ) {
@ -356,7 +396,13 @@ static int author_publish_message(void) {
if ( sc_ed25519_sign ( g_inst [ I_N1 ] - > my_ed25519_privkey , sig_msg , soff , sig ) ! = SC_OK ) return - 1 ;
DEBUG_INFO ( DEBUG_CATEGORY_GENERAL , " [mdc] publish media message ts=%llu len=%zu " , ( unsigned long long ) ts , jl ) ;
return db_sync_insert_signed ( g_si [ I_N1 ] , json , jl , sig , 64 , ts , NULL ) ;
int rc = db_sync_insert_signed ( g_si [ I_N1 ] , json , jl , sig , 64 , ts , NULL ) ;
if ( rc = = 0 ) {
/* анонсируем блоки суперузлам (как on_media_registered в chat_msg.c) */
ensure_supernode_type ( I_N1 ) ;
media_delivery_announce_media ( g_inst [ I_N1 ] , g_group_id , g_media_id , & g_block_ids [ 0 ] [ 0 ] , MEDIA_NUM_BLOCKS ) ;
}
return rc ;
}
/* ── фазы ── */
@ -431,6 +477,21 @@ static void setup_chat_group(void) {
media_delivery_set_supernode ( g_inst [ I_S2 ] , 1 ) ;
if ( g_inst [ I_S1 ] - > md . is_supernode & & g_inst [ I_S2 ] - > md . is_supernode ) OK ( ) ; else FAIL ( ) ;
}
TEST ( " SUPER_HELLO s1<->s2 (via CHAT) " ) ; {
struct media_pkt_super_hello h ; memset ( & h , 0 , sizeof ( h ) ) ;
h . subcmd = MEDIA_SUBCMD_SUPER_HELLO ; h . group_id = g_group_id ;
msend ( g_inst [ I_S1 ] , g_nid [ I_S2 ] , ( const uint8_t * ) & h , sizeof ( h ) ) ;
msend ( g_inst [ I_S2 ] , g_nid [ I_S1 ] , ( const uint8_t * ) & h , sizeof ( h ) ) ;
int a = 0 , ok = 0 ;
while ( a < 1000 ) {
ok = ( g_inst [ I_S1 ] - > md . super_peers & & g_inst [ I_S1 ] - > md . super_peers - > head )
& & ( g_inst [ I_S2 ] - > md . super_peers & & g_inst [ I_S2 ] - > md . super_peers - > head ) ;
if ( ok ) break ;
uasync_poll ( g_ua , POLL_MS ) ; a + + ;
}
if ( ok ) OK ( ) ; else FAIL ( ) ;
}
}
static void setup_db_sync ( void ) {
@ -455,10 +516,11 @@ static void setup_db_sync(void) {
int main ( void ) {
debug_config_init ( ) ; debug_set_level ( DEBUG_LEVEL_ERROR ) ;
if ( getenv ( " UTUN_TEST_DEBUG " ) ) {
debug_set_category_level_by_name ( " media " , " trace " ) ;
debug_set_category_level_by_name ( " media " , " info " ) ;
debug_set_category_level_by_name ( " general " , " info " ) ;
debug_set_category_level_by_name ( " chatsync " , " info " ) ;
debug_set_category_level_by_name ( " etcproute " , " info " ) ;
debug_set_category_level_by_name ( " chat_sync " , " info " ) ;
debug_set_category_level_by_name ( " etcp_route " , " info " ) ;
debug_set_category_level_by_name ( " bgp " , " trace " ) ;
}
utun_instance_set_tun_init_enabled ( 0 ) ;
srand ( ( unsigned ) time ( NULL ) ) ;
@ -486,19 +548,17 @@ int main(void) {
for ( int i = 0 ; i < N_NODES ; i + + ) {
char * pr = gv ( g_cfg [ i ] , " priv " ) , * pu = gv ( g_cfg [ i ] , " pub " ) ;
if ( i = = 0 ) {
wf ( g_cfg [ i ] , " [global] \n my_private_key=%s \n my_public_key=%s \n tun_ip=10.98.%d.1/24 \n tun_ifname=tun%d0 \n "
" db_path=%s \n db_sync_enabled=1 \n [server: s1] \n addr=127.0.0.1:%d \n type=public \n [allowed_keys] \n allow_all=1 \n " ,
pr , pu , i , i , g_db_dir [ i ] , g_port [ i ] ) ;
} else {
int prev = i - 1 ; char * pv_pu = gv ( g_cfg [ prev ] , " pub " ) ;
char clients [ 4096 ] = " " ;
for ( int j = 0 ; j < i ; j + + ) {
char * pj_pu = gv ( g_cfg [ j ] , " pub " ) ;
char link [ 256 ] ; snprintf ( link , sizeof ( link ) ,
" [client: to_n%d] \n keepalive=1 \n peer_public_key=%s \n link=s1:127.0.0.1:%d \n " , prev , pv_pu , g_port [ prev ] ) ;
wf ( g_cfg [ i ] , " [global] \n my_private_key=%s \n my_public_key=%s \n tun_ip=10.98.%d.1/24 \n tun_ifname=tun%d0 \n "
" db_path=%s \n db_sync_enabled=1 \n [server: s1] \n addr=127.0.0.1:%d \n type=public \n %s[allowed_keys] \n allow_all=1 \n " ,
pr , pu , i , i , g_db_dir [ i ] , g_port [ i ] , link ) ;
u_free ( pv_pu ) ;
" [client: to_n%d] \n keepalive=1 \n peer_public_key=%s \n link=s1:127.0.0.1:%d \n " , j , pj_pu , g_port [ j ] ) ;
strncat ( clients , link , sizeof ( clients ) - strlen ( clients ) - 1 ) ;
u_free ( pj_pu ) ;
}
wf ( g_cfg [ i ] , " [global] \n my_private_key=%s \n my_public_key=%s \n tun_ip=10.98.%d.1/24 \n tun_ifname=tun%d0 \n "
" db_path=%s \n db_sync_enabled=1 \n [server: s1] \n addr=127.0.0.1:%d \n type=public \n %s[allowed_keys] \n allow_all=1 \n " ,
pr , pu , i , i , g_db_dir [ i ] , g_port [ i ] , clients ) ;
u_free ( pr ) ; u_free ( pu ) ;
}
@ -506,6 +566,7 @@ int main(void) {
g_inst [ i ] = utun_instance_create ( g_ua , g_cfg [ i ] ) ;
if ( ! g_inst [ i ] ) { printf ( " FAIL: create n%d \n " , i ) ; goto done ; }
utun_instance_init ( g_inst [ i ] ) ;
member_sync_init ( g_inst [ i ] ) ;
}
mon ( NULL ) ;
@ -516,6 +577,10 @@ int main(void) {
g_group_id = strtoull ( CH_ID , NULL , 10 ) ;
/* media_files-таблица нужна на КАЖДОМ узле (в chat_core_init её создаёт media_index_init) —
и н а ч е у з е л , п р и н я в ш и й BLOCK_REQ , п а д а е т с " no such table: media_files " . */
for ( int i = 0 ; i < N_NODES ; i + + ) media_index_init ( g_inst [ i ] - > topo_sqlite_db ) ;
setup_chat_group ( ) ;
setup_db_sync ( ) ;
@ -523,6 +588,10 @@ int main(void) {
if ( wait_pubkeys ( ) ) OK ( ) ; else FAIL ( ) ;
}
TEST ( " CHAT group BGP converged (routing ready) " ) ; {
if ( wait_chat_bgp ( ) ) OK ( ) ; else FAIL ( ) ;
}
TEST ( " author: index file + media_index_commit " ) ; {
if ( author_index_file ( ) = = 0 & & db_count ( g_inst [ I_N1 ] - > topo_sqlite_db , " media_files " , NULL , 0 ) = = MEDIA_NUM_BLOCKS ) OK ( ) ;
else FAIL ( ) ;
@ -534,12 +603,13 @@ int main(void) {
/* Фаза 1: сообщение распространилось на все узлы */
TEST ( " message propagated to all 6 nodes " ) ; {
int a = 0 , all = 0 ;
while ( a < 4000 ) {
int all = 0 ;
uint64_t start = get_time_tb ( ) ;
while ( ( get_time_tb ( ) - start ) < ( uint64_t ) 60000 ) {
all = 1 ;
for ( int i = 0 ; i < N_NODES ; i + + ) if ( db_sync_count ( g_si [ i ] ) < 1 ) all = 0 ;
if ( all ) break ;
uasync_poll ( g_ua , POLL_MS ) ; a + + ;
uasync_poll ( g_ua , POLL_MS ) ;
}
if ( all ) OK ( ) ; else FAIL ( " counts: %d %d %d %d %d %d " ,
db_sync_count ( g_si [ 0 ] ) , db_sync_count ( g_si [ 1 ] ) , db_sync_count ( g_si [ 2 ] ) ,
@ -550,8 +620,9 @@ int main(void) {
TEST ( " storage st1/st2 downloaded + assembled media " ) ; {
char d1 [ 1024 ] , d2 [ 1024 ] ;
node_dest ( I_ST1 , d1 , sizeof ( d1 ) ) ; node_dest ( I_ST2 , d2 , sizeof ( d2 ) ) ;
int a = 0 ;
while ( a < 8000 & & ( ! g_dl_done [ I_ST1 ] | | ! g_dl_done [ I_ST2 ] ) ) { uasync_poll ( g_ua , POLL_MS ) ; a + + ; }
uint64_t start = get_time_tb ( ) ;
while ( ( get_time_tb ( ) - start ) < ( uint64_t ) 60000 & & ( ! g_dl_done [ I_ST1 ] | | ! g_dl_done [ I_ST2 ] ) )
uasync_poll ( g_ua , POLL_MS ) ;
if ( g_dl_done [ I_ST1 ] & & g_dl_done [ I_ST2 ] & & g_dl_err [ I_ST1 ] = = 0 & & g_dl_err [ I_ST2 ] = = 0
& & file_exists ( d1 ) & & file_exists ( d2 ) ) OK ( ) ;
else FAIL ( " st1 done=%d err=%d exists=%d; st2 done=%d err=%d exists=%d " ,
@ -569,12 +640,13 @@ int main(void) {
/* Фаза 3: суперузлы узнали о блоках + репликация между собой */
TEST ( " supernodes have block_availability (from storage HAVE_BLOCK) " ) ; {
int a = 0 , ok = 0 ;
while ( a < 2000 ) {
int ok = 0 ;
uint64_t start = get_time_tb ( ) ;
while ( ( get_time_tb ( ) - start ) < ( uint64_t ) 60000 ) {
int n1 = db_count ( g_inst [ I_S1 ] - > topo_sqlite_db , " block_availability " , NULL , 0 ) ;
int n2 = db_count ( g_inst [ I_S2 ] - > topo_sqlite_db , " block_availability " , NULL , 0 ) ;
if ( n1 > = 2 & & n2 > = 2 ) { ok = 1 ; break ; }
uasync_poll ( g_ua , POLL_MS ) ; a + + ;
uasync_poll ( g_ua , POLL_MS ) ;
}
if ( ok ) OK ( ) ; else FAIL ( " s1=%d s2=%d " ,
db_count ( g_inst [ I_S1 ] - > topo_sqlite_db , " block_availability " , NULL , 0 ) ,
@ -582,15 +654,16 @@ int main(void) {
}
TEST ( " supernode replication s1↔s2 (super_sync) " ) ; {
int a = 0 , ok = 0 ;
while ( a < 2000 ) {
int ok = 0 ;
uint64_t start = get_time_tb ( ) ;
while ( ( get_time_tb ( ) - start ) < ( uint64_t ) 60000 ) {
sqlite3_stmt * st = NULL ; uint64_t lr = 0 ;
sqlite3_prepare_v2 ( g_inst [ I_S2 ] - > topo_sqlite_db , " SELECT last_recv_id FROM super_sync WHERE peer_node_id=? " , - 1 , & st , NULL ) ;
sqlite3_bind_int64 ( st , 1 , ( sqlite3_int64 ) g_nid [ I_S1 ] ) ;
if ( sqlite3_step ( st ) = = SQLITE_ROW ) lr = ( uint64_t ) sqlite3_column_int64 ( st , 0 ) ;
sqlite3_finalize ( st ) ;
if ( lr > 0 ) { ok = 1 ; break ; }
uasync_poll ( g_ua , POLL_MS ) ; a + + ;
uasync_poll ( g_ua , POLL_MS ) ;
}
if ( ok ) OK ( ) ; else FAIL ( ) ;
}
@ -612,8 +685,9 @@ int main(void) {
media_download_start ( g_inst [ I_N2 ] , g_group_id , & r , dest , mbase , g_nid [ I_N1 ] , dl_done_cb , ( void * ) ( intptr_t ) I_N2 , NULL , NULL ) ;
media_index_result_free ( & r ) ;
int a = 0 ;
while ( a < 8000 & & ! g_dl_done [ I_N2 ] ) { uasync_poll ( g_ua , POLL_MS ) ; a + + ; }
uint64_t start = get_time_tb ( ) ;
while ( ( get_time_tb ( ) - start ) < ( uint64_t ) 60000 & & ! g_dl_done [ I_N2 ] )
uasync_poll ( g_ua , POLL_MS ) ;
if ( g_dl_done [ I_N2 ] & & g_dl_err [ I_N2 ] = = 0 & & file_exists ( dest ) ) OK ( ) ;
else FAIL ( " done=%d err=%d exists=%d " , g_dl_done [ I_N2 ] , g_dl_err [ I_N2 ] , file_exists ( dest ) ) ;
}