|
|
|
|
@ -54,10 +54,17 @@ static int automatic_drop_relay;
|
|
|
|
|
static int automatic_drop_tail; |
|
|
|
|
static etcp_recv_fn automatic_handlers[4]; |
|
|
|
|
static uint8_t automatic_request[STREAM_WIRE_HEADER + 1]; |
|
|
|
|
static etcp_recv_fn relay_raw_handlers[3], relay_route_handler; |
|
|
|
|
static etcp_recv_fn relay_raw_handlers[4], relay_route_handler; |
|
|
|
|
static unsigned relay_data[3], relay_ends[3], relay_requests_seen; |
|
|
|
|
static int relay_drop_source; |
|
|
|
|
static uint8_t relay_request_wire[STREAM_WIRE_HEADER]; |
|
|
|
|
static struct { |
|
|
|
|
int hold_info, hold_request, check_starts; |
|
|
|
|
uint8_t info[STREAM_WIRE_HEADER + STREAM_INFO_SIZE + STREAM_PATH_MAX * 8]; |
|
|
|
|
size_t info_length; |
|
|
|
|
unsigned source_subscribes, helper_subscribes, accepted; |
|
|
|
|
uint64_t request_tb, start; |
|
|
|
|
} relay_race; |
|
|
|
|
static struct { |
|
|
|
|
unsigned data, reinits, responses; |
|
|
|
|
int hold_info; |
|
|
|
|
@ -1009,7 +1016,27 @@ static void relay_role(unsigned member, int enabled, unsigned version) {
|
|
|
|
|
|
|
|
|
|
/* Прямой stream-вход источника блокируем только у зрителей; Merkle/BGP и получение суперузла остаются настоящими. */ |
|
|
|
|
static void relay_test_raw(struct ETCP_CONN *conn, struct ll_entry *entry) { |
|
|
|
|
unsigned index = 0; while (index < 3 && conn->instance != nodes[index]) index++; CHECK(index < 3); |
|
|
|
|
unsigned index = 0; while (index < 4 && conn->instance != nodes[index]) index++; CHECK(index < 4); |
|
|
|
|
if (entry->len >= STREAM_WIRE_HEADER) { |
|
|
|
|
unsigned command = entry->dgram[2]; |
|
|
|
|
if (command == STREAM_CMD_SUBSCRIBE && conn->peer_node_id == nodes[2]->node_id) { |
|
|
|
|
if (index == 3) relay_race.source_subscribes++; |
|
|
|
|
if (index == 1) relay_race.helper_subscribes++; |
|
|
|
|
} |
|
|
|
|
if (index == 2 && conn->peer_node_id == nodes[3]->node_id && command == STREAM_CMD_INFO_RESP && relay_race.hold_info) { |
|
|
|
|
if (!relay_race.info_length) { |
|
|
|
|
CHECK(entry->len <= sizeof(relay_race.info)); relay_race.info_length = entry->len; |
|
|
|
|
memcpy(relay_race.info, entry->dgram, entry->len); |
|
|
|
|
} |
|
|
|
|
queue_dgram_free(entry); queue_entry_free(entry); return; |
|
|
|
|
} |
|
|
|
|
if (relay_race.check_starts && index == 2 && conn->peer_node_id == nodes[1]->node_id && command == STREAM_CMD_ACCEPT) { |
|
|
|
|
struct stream_message m; CHECK(stream_wire_parse(entry->dgram, entry->len, &m) == STREAM_OK && m.length >= 16); |
|
|
|
|
uint64_t start = stream_read_be(m.payload + 8, 8); |
|
|
|
|
if (!relay_race.accepted) relay_race.start = start; |
|
|
|
|
CHECK(start == relay_race.start && m.subblock < 8); relay_race.accepted |= 1u << m.subblock; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (relay_drop_source && index != 1 && conn->peer_node_id == nodes[3]->node_id) { |
|
|
|
|
queue_dgram_free(entry); queue_entry_free(entry); return; |
|
|
|
|
} |
|
|
|
|
@ -1021,6 +1048,8 @@ static void relay_test_routed(struct ETCP_CONN *conn, struct ll_entry *entry) {
|
|
|
|
|
if (entry->len >= ROUTER_SVC_PAYLOAD_OFF + STREAM_WIRE_HEADER |
|
|
|
|
&& entry->dgram[ROUTER_SVC_PAYLOAD_OFF + 2] == STREAM_CMD_RELAY_REQUEST) { |
|
|
|
|
memcpy(relay_request_wire, entry->dgram + ROUTER_SVC_PAYLOAD_OFF, STREAM_WIRE_HEADER); relay_requests_seen++; |
|
|
|
|
if (!relay_race.request_tb) relay_race.request_tb = get_time_tb(); |
|
|
|
|
if (relay_race.hold_request) { queue_dgram_free(entry); queue_entry_free(entry); return; } |
|
|
|
|
} |
|
|
|
|
relay_route_handler(conn, entry); |
|
|
|
|
} |
|
|
|
|
@ -1041,6 +1070,15 @@ static void relay_test_control(const struct stream_message *message) {
|
|
|
|
|
CHECK(etcp_route_send(nodes[2], GROUP2, nodes[1]->node_id, entry, 0, ROUTE_CRYPTO_SIGN | ROUTE_CRYPTO_ENCRYPT) == 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Запоздалый настоящий INFO подаётся тому же raw dispatcher; старый probe nonce не должен менять победителя. */ |
|
|
|
|
static void relay_release_info(void) { |
|
|
|
|
CHECK(relay_race.info_length); |
|
|
|
|
struct ETCP_CONN *conn = instance_find_conn(nodes[2], nodes[3]->node_id); CHECK(conn && conn->links_up); |
|
|
|
|
struct ll_entry *entry = ll_alloc_lldgram((uint16_t)relay_race.info_length); CHECK(entry); |
|
|
|
|
memcpy(entry->dgram, relay_race.info, relay_race.info_length); entry->len = relay_race.info_length; |
|
|
|
|
relay_raw_handlers[2](conn, entry); relay_race.info_length = 0; relay_race.hold_info = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Все проверки помощи используют подписанное управление и настоящий raw ETCP, не прямой ingest. */ |
|
|
|
|
static void test_supernode_relay(void) { |
|
|
|
|
int route_level = g_debug_config.category_levels[DEBUG_CATEGORY_ETCPROUTE]; |
|
|
|
|
@ -1055,14 +1093,14 @@ static void test_supernode_relay(void) {
|
|
|
|
|
CHECK(member_sync_start(nodes[i], nodes[3]->node_id, "710002", NULL, NULL) == 0); |
|
|
|
|
} |
|
|
|
|
relay_role(1, 1, 100); |
|
|
|
|
for (unsigned i = 0; i < 3; i++) { |
|
|
|
|
for (unsigned i = 0; i < 4; i++) { |
|
|
|
|
relay_raw_handlers[i] = nodes[i]->api_bindings.callbacks[ETCP_ID_STREAM]; |
|
|
|
|
CHECK(etcp_bind(nodes[i], ETCP_ID_STREAM, relay_test_raw) == 0); |
|
|
|
|
stream_set_event_callback(nodes[i], relay_test_event, NULL); |
|
|
|
|
if (i < 3) stream_set_event_callback(nodes[i], relay_test_event, NULL); |
|
|
|
|
} |
|
|
|
|
relay_route_handler = nodes[1]->router_bindings.callbacks[ETCP_RT_ID_STREAM]; |
|
|
|
|
CHECK(etcp_router_bind(nodes[1], ETCP_RT_ID_STREAM, relay_test_routed) == 0); |
|
|
|
|
struct stream_parameters parameters = {.n = 2, .r = 1, .block_size = STREAM_RS_DEFAULT_SIZE}; |
|
|
|
|
struct stream_parameters parameters = {.n = 8, .r = 2, .block_size = STREAM_RS_DEFAULT_SIZE}; |
|
|
|
|
struct STREAM_HANDLE *publisher = NULL, *viewers[3] = {0}; |
|
|
|
|
CHECK(stream_publish_open(nodes[3], GROUP2, ¶meters, &publisher) == STREAM_OK); |
|
|
|
|
struct stream_info origin; CHECK(stream_get_info(publisher, &origin) == STREAM_OK); |
|
|
|
|
@ -1074,11 +1112,45 @@ static void test_supernode_relay(void) {
|
|
|
|
|
CHECK(stream_local_get(nodes[1], &origin.descriptor, &local) == STREAM_WAIT && !relay_requests_seen); |
|
|
|
|
stream_close(viewers[2]); viewers[2] = NULL; |
|
|
|
|
test_clock_run_for(ua, 10000); |
|
|
|
|
/* Помощь начинается через 100 мс, но прямой INFO выигрывает до ответа помощника и отменяет его запрос. */ |
|
|
|
|
relay_race.hold_info = relay_race.hold_request = 1; |
|
|
|
|
uint64_t started = get_time_tb(); |
|
|
|
|
CHECK(stream_subscribe_open(nodes[2], GROUP2, nodes[3]->node_id, &viewers[2]) == STREAM_OK); |
|
|
|
|
test_clock_run_for(ua, 900); CHECK(!relay_requests_seen); |
|
|
|
|
deadline = get_time_tb() + 10000; |
|
|
|
|
while (!relay_requests_seen && get_time_tb() < deadline) test_clock_poll(ua, 100); |
|
|
|
|
CHECK(relay_requests_seen && relay_race.info_length); |
|
|
|
|
CHECK(relay_race.request_tb - started >= 990 && relay_race.request_tb - started < 1500); |
|
|
|
|
relay_release_info(); run_until(&relay_data[2], 2); test_clock_run_for(ua, 2000); |
|
|
|
|
CHECK(relay_race.source_subscribes && !relay_race.helper_subscribes); |
|
|
|
|
CHECK(stream_local_get(nodes[1], &origin.descriptor, &local) == STREAM_WAIT); |
|
|
|
|
struct stream_message cancelled; CHECK(stream_wire_parse(relay_request_wire, sizeof(relay_request_wire), &cancelled) == STREAM_OK); |
|
|
|
|
relay_race.hold_request = 0; relay_test_control(&cancelled); test_clock_run_for(ua, 2000); |
|
|
|
|
CHECK(stream_local_get(nodes[1], &origin.descriptor, &local) == STREAM_WAIT); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "[PASS] bootstrap direct wins after helper request: request_delay_ms=%llu, late REQUEST cancelled", |
|
|
|
|
(unsigned long long)((relay_race.request_tb - started) / 10)); |
|
|
|
|
stream_close(viewers[2]); viewers[2] = NULL; test_clock_run_for(ua, 1000); |
|
|
|
|
/* Закрытие reader до дедлайна отменяет и таймер, и объединённую работу сети. */ |
|
|
|
|
unsigned requests_before = relay_requests_seen; |
|
|
|
|
relay_race.hold_info = 1; |
|
|
|
|
CHECK(stream_subscribe_open(nodes[2], GROUP2, nodes[3]->node_id, &viewers[2]) == STREAM_OK); |
|
|
|
|
test_clock_run_for(ua, 500); stream_close(viewers[2]); viewers[2] = NULL; |
|
|
|
|
test_clock_run_for(ua, 2000); CHECK(relay_requests_seen == requests_before); |
|
|
|
|
memset(&relay_race, 0, sizeof(relay_race)); relay_race.hold_info = relay_race.check_starts = 1; |
|
|
|
|
relay_drop_source = 1; |
|
|
|
|
started = get_time_tb(); |
|
|
|
|
CHECK(stream_subscribe_open(nodes[2], GROUP2, nodes[3]->node_id, &viewers[2]) == STREAM_OK); |
|
|
|
|
deadline = get_time_tb() + 300000; |
|
|
|
|
while (relay_data[2] < 2 && get_time_tb() < deadline) test_clock_poll(ua, 100); |
|
|
|
|
CHECK(relay_data[2] == 2 && relay_requests_seen && !relay_data[1]); |
|
|
|
|
while (relay_data[2] < 3 && get_time_tb() < deadline) test_clock_poll(ua, 100); |
|
|
|
|
CHECK(relay_data[2] == 3 && relay_requests_seen && !relay_data[1]); |
|
|
|
|
CHECK(get_time_tb() - started < 10000 && relay_race.request_tb - started >= 990); |
|
|
|
|
test_clock_run_for(ua, 1000); CHECK(relay_race.accepted == 255); |
|
|
|
|
CHECK(relay_race.helper_subscribes && !relay_race.source_subscribes); |
|
|
|
|
relay_release_info(); test_clock_run_for(ua, 1000); |
|
|
|
|
CHECK(!relay_race.source_subscribes && relay_data[2] == 3); |
|
|
|
|
relay_race.check_starts = 0; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "[PASS] bootstrap helper wins: elapsed_ms=%llu, n=8 r=2, common_start=%llu, stale direct INFO ignored", |
|
|
|
|
(unsigned long long)((get_time_tb() - started) / 10), (unsigned long long)relay_race.start); |
|
|
|
|
CHECK(stream_local_get(nodes[1], &origin.descriptor, &local) == STREAM_OK && local.relay_only); |
|
|
|
|
/* Поздний обычный подписчик также удерживает раздачу, даже без собственного запроса помощи. */ |
|
|
|
|
CHECK(stream_subscribe_open(nodes[0], GROUP2, nodes[3]->node_id, &viewers[0]) == STREAM_OK); |
|
|
|
|
@ -1163,9 +1235,9 @@ static void test_supernode_relay(void) {
|
|
|
|
|
stream_close(viewers[2]); stream_close(publisher); |
|
|
|
|
relay_drop_source = 0; relay_role(1, 0, 101); |
|
|
|
|
CHECK(etcp_router_bind(nodes[1], ETCP_RT_ID_STREAM, relay_route_handler) == 0); |
|
|
|
|
for (unsigned i = 0; i < 3; i++) { |
|
|
|
|
for (unsigned i = 0; i < 4; i++) { |
|
|
|
|
CHECK(etcp_bind(nodes[i], ETCP_ID_STREAM, relay_raw_handlers[i]) == 0); |
|
|
|
|
stream_set_event_callback(nodes[i], i < 2 ? on_stream : NULL, NULL); |
|
|
|
|
if (i < 3) stream_set_event_callback(nodes[i], i < 2 ? on_stream : NULL, NULL); |
|
|
|
|
} |
|
|
|
|
for (unsigned i = 0; i < 3; i++) topo_group_peer_close(connections[i]); |
|
|
|
|
debug_set_category_level(DEBUG_CATEGORY_ETCPROUTE, route_level); |
|
|
|
|
|