#include "amduatd_fed_push_plan.h" #include "amduat/asl/log_store.h" #include "amduat/asl/artifact_io.h" #include "amduat/asl/ref_text.h" #include "amduat/asl/store.h" #include "amduat/enc/fer1_receipt.h" #include "amduat/enc/tgk1_edge.h" #include #include #include typedef struct { char *data; size_t len; size_t cap; } amduatd_fed_push_plan_strbuf_t; static void amduatd_fed_push_plan_strbuf_free( amduatd_fed_push_plan_strbuf_t *b) { if (b == NULL) { return; } free(b->data); b->data = NULL; b->len = 0; b->cap = 0; } static bool amduatd_fed_push_plan_strbuf_reserve( amduatd_fed_push_plan_strbuf_t *b, size_t extra) { size_t need; size_t next_cap; char *next; if (b == NULL) { return false; } if (extra > (SIZE_MAX - b->len)) { return false; } need = b->len + extra; if (need <= b->cap) { return true; } next_cap = b->cap != 0 ? b->cap : 256u; while (next_cap < need) { if (next_cap > (SIZE_MAX / 2u)) { next_cap = need; break; } next_cap *= 2u; } next = (char *)realloc(b->data, next_cap); if (next == NULL) { return false; } b->data = next; b->cap = next_cap; return true; } static bool amduatd_fed_push_plan_strbuf_append( amduatd_fed_push_plan_strbuf_t *b, const char *s, size_t n) { if (b == NULL) { return false; } if (n == 0u) { return true; } if (s == NULL) { return false; } if (!amduatd_fed_push_plan_strbuf_reserve(b, n + 1u)) { return false; } memcpy(b->data + b->len, s, n); b->len += n; b->data[b->len] = '\0'; return true; } static bool amduatd_fed_push_plan_strbuf_append_cstr( amduatd_fed_push_plan_strbuf_t *b, const char *s) { return amduatd_fed_push_plan_strbuf_append( b, s != NULL ? s : "", s != NULL ? strlen(s) : 0u); } static const char *amduatd_fed_push_plan_record_type_name( amduat_fed_record_type_t type) { switch (type) { case AMDUAT_FED_REC_ARTIFACT: return "artifact"; case AMDUAT_FED_REC_PER: return "per"; case AMDUAT_FED_REC_TGK_EDGE: return "tgk_edge"; case AMDUAT_FED_REC_TOMBSTONE: return "tombstone"; default: return "unknown"; } } void amduatd_fed_push_plan_candidate_init( amduatd_fed_push_cursor_candidate_t *candidate) { if (candidate == NULL) { return; } memset(candidate, 0, sizeof(*candidate)); candidate->ref = amduat_reference(0u, amduat_octets(NULL, 0u)); } void amduatd_fed_push_plan_candidate_free( amduatd_fed_push_cursor_candidate_t *candidate) { if (candidate == NULL) { return; } if (candidate->has_ref) { amduat_reference_free(&candidate->ref); } memset(candidate, 0, sizeof(*candidate)); } bool amduatd_fed_push_plan_next_cursor_candidate( const amduatd_fed_cursor_record_t *cursor, const amduat_fed_record_t *records, size_t record_count, amduatd_fed_push_cursor_candidate_t *out_candidate) { if (out_candidate == NULL) { return false; } amduatd_fed_push_plan_candidate_init(out_candidate); if (record_count > 0u && records != NULL) { const amduat_fed_record_t *last = &records[record_count - 1u]; out_candidate->has_logseq = true; out_candidate->logseq = last->logseq; out_candidate->has_ref = true; if (!amduat_reference_clone(last->id.ref, &out_candidate->ref)) { amduatd_fed_push_plan_candidate_free(out_candidate); return false; } return true; } (void)cursor; return true; } amduatd_fed_push_plan_status_t amduatd_fed_push_plan_check( const amduatd_fed_cfg_t *cfg, const amduat_asl_store_t *store) { if (cfg == NULL || store == NULL) { return AMDUATD_FED_PUSH_PLAN_ERR_INVALID; } if (!cfg->enabled) { return AMDUATD_FED_PUSH_PLAN_ERR_DISABLED; } if (store->ops.log_scan == NULL || store->ops.current_state == NULL) { return AMDUATD_FED_PUSH_PLAN_ERR_UNSUPPORTED; } return AMDUATD_FED_PUSH_PLAN_OK; } static void amduatd_fed_push_plan_records_free(amduat_fed_record_t *records, size_t record_count) { size_t i; if (records == NULL) { return; } for (i = 0; i < record_count; ++i) { amduat_reference_free(&records[i].id.ref); } free(records); } static bool amduatd_fed_push_entry_record_type( amduat_asl_store_t *store, const amduat_asl_log_entry_t *entry, amduat_fed_record_type_t *out_type) { amduat_fed_record_type_t rec_type = AMDUAT_FED_REC_ARTIFACT; if (store == NULL || entry == NULL || out_type == NULL) { return false; } if (entry->kind == AMDUATD_FED_LOG_KIND_ARTIFACT) { amduat_artifact_t artifact; amduat_asl_store_error_t store_err; memset(&artifact, 0, sizeof(artifact)); store_err = amduat_asl_store_get(store, entry->payload_ref, &artifact); if (store_err == AMDUAT_ASL_STORE_OK && artifact.has_type_tag) { if (artifact.type_tag.tag_id == AMDUAT_TYPE_TAG_TGK1_EDGE_V1) { rec_type = AMDUAT_FED_REC_TGK_EDGE; } else if (artifact.type_tag.tag_id == AMDUAT_TYPE_TAG_FER1_RECEIPT_1) { rec_type = AMDUAT_FED_REC_PER; } } if (store_err == AMDUAT_ASL_STORE_OK) { amduat_asl_artifact_free(&artifact); } } else if (entry->kind == AMDUATD_FED_LOG_KIND_TOMBSTONE) { rec_type = AMDUAT_FED_REC_TOMBSTONE; } else { return false; } *out_type = rec_type; return true; } void amduatd_fed_push_plan_scan_init(amduatd_fed_push_plan_scan_t *scan) { if (scan == NULL) { return; } memset(scan, 0, sizeof(*scan)); amduatd_fed_cursor_record_init(&scan->cursor); scan->cursor_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); } void amduatd_fed_push_plan_scan_free(amduatd_fed_push_plan_scan_t *scan) { if (scan == NULL) { return; } if (scan->cursor_present) { amduatd_fed_cursor_record_free(&scan->cursor); amduat_reference_free(&scan->cursor_ref); } amduatd_fed_push_plan_records_free(scan->records, scan->record_count); memset(scan, 0, sizeof(*scan)); } amduatd_fed_push_plan_status_t amduatd_fed_push_plan_scan( amduat_asl_store_t *store, amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, uint64_t limit, const char *root_path, amduatd_fed_push_plan_scan_t *out_scan) { amduat_asl_log_store_t log_store; amduat_asl_log_entry_t *entries = NULL; size_t entry_count = 0u; uint64_t next_offset = 0u; bool end = false; amduat_octets_t log_name = amduat_octets(NULL, 0u); uint64_t from_logseq = 0u; if (store == NULL || pointer_store == NULL || peer_key == NULL || root_path == NULL || out_scan == NULL) { return AMDUATD_FED_PUSH_PLAN_ERR_INVALID; } amduatd_fed_push_plan_scan_init(out_scan); { amduatd_fed_cursor_status_t cursor_status; cursor_status = amduatd_fed_push_cursor_get(store, pointer_store, effective_space, peer_key, &out_scan->cursor, &out_scan->cursor_ref); if (cursor_status == AMDUATD_FED_CURSOR_ERR_NOT_FOUND) { out_scan->cursor_present = false; } else if (cursor_status == AMDUATD_FED_CURSOR_OK) { out_scan->cursor_present = true; if (out_scan->cursor.has_logseq) { if (out_scan->cursor.last_logseq == UINT64_MAX) { amduatd_fed_push_plan_scan_free(out_scan); return AMDUATD_FED_PUSH_PLAN_ERR_INVALID; } from_logseq = out_scan->cursor.last_logseq + 1u; } } else { amduatd_fed_push_plan_scan_free(out_scan); return AMDUATD_FED_PUSH_PLAN_ERR_INVALID; } } if (!amduat_asl_log_store_init(&log_store, root_path, store, pointer_store)) { amduatd_fed_push_plan_scan_free(out_scan); return AMDUATD_FED_PUSH_PLAN_ERR_INVALID; } if (!amduatd_space_scope_name(effective_space, "fed/records", &log_name)) { amduatd_fed_push_plan_scan_free(out_scan); return AMDUATD_FED_PUSH_PLAN_ERR_INVALID; } { amduat_asl_store_error_t read_err = amduat_asl_log_read(&log_store, (const char *)log_name.data, from_logseq, (size_t)limit, &entries, &entry_count, &next_offset, &end); amduat_octets_free(&log_name); if (read_err != AMDUAT_ASL_STORE_OK) { amduatd_fed_push_plan_scan_free(out_scan); return AMDUATD_FED_PUSH_PLAN_ERR_INVALID; } } (void)next_offset; (void)end; if (entry_count != 0u) { out_scan->records = (amduat_fed_record_t *)calloc(entry_count, sizeof(*out_scan->records)); if (out_scan->records == NULL) { amduat_asl_log_entries_free(entries, entry_count); amduatd_fed_push_plan_scan_free(out_scan); return AMDUATD_FED_PUSH_PLAN_ERR_OOM; } } { size_t i; for (i = 0; i < entry_count; ++i) { const amduat_asl_log_entry_t *entry = &entries[i]; amduat_fed_record_type_t rec_type = AMDUAT_FED_REC_ARTIFACT; uint64_t logseq; if (entry->payload_ref.digest.data == NULL || entry->payload_ref.digest.len == 0u) { continue; } if (from_logseq > UINT64_MAX - (uint64_t)i) { amduat_asl_log_entries_free(entries, entry_count); amduatd_fed_push_plan_scan_free(out_scan); return AMDUATD_FED_PUSH_PLAN_ERR_INVALID; } logseq = from_logseq + (uint64_t)i; if (!amduatd_fed_push_entry_record_type(store, entry, &rec_type)) { continue; } memset(&out_scan->records[out_scan->record_count], 0, sizeof(out_scan->records[out_scan->record_count])); out_scan->records[out_scan->record_count].id.type = rec_type; out_scan->records[out_scan->record_count].logseq = logseq; if (!amduat_reference_clone(entry->payload_ref, &out_scan->records[out_scan->record_count] .id.ref)) { amduat_asl_log_entries_free(entries, entry_count); amduatd_fed_push_plan_scan_free(out_scan); return AMDUATD_FED_PUSH_PLAN_ERR_OOM; } out_scan->record_count++; } } amduat_asl_log_entries_free(entries, entry_count); return AMDUATD_FED_PUSH_PLAN_OK; } amduatd_fed_push_plan_status_t amduatd_fed_push_plan_json( const amduatd_fed_push_plan_input_t *input, char **out_json) { amduatd_fed_push_plan_strbuf_t b; size_t i; const amduat_fed_record_t *first = NULL; const amduat_fed_record_t *last = NULL; amduatd_fed_push_cursor_candidate_t candidate; char *ref_hex = NULL; char *cursor_ref_hex = NULL; char tmp[64]; if (out_json != NULL) { *out_json = NULL; } if (input == NULL || out_json == NULL || input->peer_key == NULL) { return AMDUATD_FED_PUSH_PLAN_ERR_INVALID; } if (input->record_count > 0u && input->records == NULL) { return AMDUATD_FED_PUSH_PLAN_ERR_INVALID; } if (input->cursor_present && input->cursor == NULL) { return AMDUATD_FED_PUSH_PLAN_ERR_INVALID; } if (input->record_count > 0u && input->records != NULL) { first = &input->records[0]; last = &input->records[input->record_count - 1u]; } if (!amduatd_fed_push_plan_next_cursor_candidate( input->cursor_present ? input->cursor : NULL, input->records, input->record_count, &candidate)) { return AMDUATD_FED_PUSH_PLAN_ERR_OOM; } if (input->cursor_present && input->cursor_ref != NULL && input->cursor_ref->digest.data != NULL) { if (!amduat_asl_ref_encode_hex(*input->cursor_ref, &cursor_ref_hex)) { amduatd_fed_push_plan_candidate_free(&candidate); return AMDUATD_FED_PUSH_PLAN_ERR_OOM; } } memset(&b, 0, sizeof(b)); if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "{")) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"peer\":\"") || !amduatd_fed_push_plan_strbuf_append_cstr(&b, input->peer_key) || !amduatd_fed_push_plan_strbuf_append_cstr(&b, "\",")) { goto plan_oom; } snprintf(tmp, sizeof(tmp), "%u", (unsigned int)input->domain_id); if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"domain_id\":") || !amduatd_fed_push_plan_strbuf_append_cstr(&b, tmp) || !amduatd_fed_push_plan_strbuf_append_cstr(&b, ",")) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"effective_space\":{")) { goto plan_oom; } if (input->effective_space != NULL && input->effective_space->enabled && input->effective_space->space_id.data != NULL) { const char *space_id = (const char *)input->effective_space->space_id.data; if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"mode\":\"scoped\",") || !amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"space_id\":\"") || !amduatd_fed_push_plan_strbuf_append_cstr(&b, space_id) || !amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"")) { goto plan_oom; } } else { if (!amduatd_fed_push_plan_strbuf_append_cstr( &b, "\"mode\":\"unscoped\",") || !amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"space_id\":null")) { goto plan_oom; } } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "},")) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"cursor\":{")) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"present\":") || !amduatd_fed_push_plan_strbuf_append_cstr(&b, input->cursor_present ? "true" : "false")) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, ",\"last_logseq\":")) { goto plan_oom; } if (input->cursor_present && input->cursor != NULL && input->cursor->has_logseq) { snprintf(tmp, sizeof(tmp), "%llu", (unsigned long long)input->cursor->last_logseq); if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, tmp)) { goto plan_oom; } } else { if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "null")) { goto plan_oom; } } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, ",\"ref\":")) { goto plan_oom; } if (cursor_ref_hex != NULL) { if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"") || !amduatd_fed_push_plan_strbuf_append_cstr(&b, cursor_ref_hex) || !amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"")) { goto plan_oom; } } else { if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "null")) { goto plan_oom; } } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "},")) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"scan\":{")) { goto plan_oom; } snprintf(tmp, sizeof(tmp), "%zu", input->record_count); if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"record_count\":") || !amduatd_fed_push_plan_strbuf_append_cstr(&b, tmp)) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, ",\"first_logseq\":")) { goto plan_oom; } if (first != NULL) { snprintf(tmp, sizeof(tmp), "%llu", (unsigned long long)first->logseq); if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, tmp)) { goto plan_oom; } } else { if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "null")) { goto plan_oom; } } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, ",\"last_logseq\":")) { goto plan_oom; } if (last != NULL) { snprintf(tmp, sizeof(tmp), "%llu", (unsigned long long)last->logseq); if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, tmp)) { goto plan_oom; } } else { if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "null")) { goto plan_oom; } } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "},")) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"records\":[")) { goto plan_oom; } for (i = 0; i < input->record_count; ++i) { const amduat_fed_record_t *rec = &input->records[i]; const char *type_name = amduatd_fed_push_plan_record_type_name(rec->id.type); if (i > 0) { if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, ",")) { goto plan_oom; } } if (!amduat_asl_ref_encode_hex(rec->id.ref, &ref_hex)) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "{\"logseq\":")) { goto plan_oom; } snprintf(tmp, sizeof(tmp), "%llu", (unsigned long long)rec->logseq); if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, tmp) || !amduatd_fed_push_plan_strbuf_append_cstr(&b, ",\"record_type\":\"") || !amduatd_fed_push_plan_strbuf_append_cstr(&b, type_name) || !amduatd_fed_push_plan_strbuf_append_cstr(&b, "\",\"ref\":\"") || !amduatd_fed_push_plan_strbuf_append_cstr(&b, ref_hex) || !amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"}")) { goto plan_oom; } free(ref_hex); ref_hex = NULL; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "],")) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"required_artifacts\":[")) { goto plan_oom; } { bool first_artifact = true; for (i = 0; i < input->record_count; ++i) { const amduat_fed_record_t *rec = &input->records[i]; if (rec->id.type == AMDUAT_FED_REC_TOMBSTONE) { continue; } if (!amduat_asl_ref_encode_hex(rec->id.ref, &ref_hex)) { goto plan_oom; } if (!first_artifact) { if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, ",")) { goto plan_oom; } } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"") || !amduatd_fed_push_plan_strbuf_append_cstr(&b, ref_hex) || !amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"")) { goto plan_oom; } first_artifact = false; free(ref_hex); ref_hex = NULL; } } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "],")) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr( &b, "\"next_cursor_candidate\":{")) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"last_logseq\":")) { goto plan_oom; } if (candidate.has_logseq) { snprintf(tmp, sizeof(tmp), "%llu", (unsigned long long)candidate.logseq); if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, tmp)) { goto plan_oom; } } else { if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "null")) { goto plan_oom; } } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, ",\"ref\":")) { goto plan_oom; } if (candidate.has_ref) { if (!amduat_asl_ref_encode_hex(candidate.ref, &ref_hex)) { goto plan_oom; } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"") || !amduatd_fed_push_plan_strbuf_append_cstr(&b, ref_hex) || !amduatd_fed_push_plan_strbuf_append_cstr(&b, "\"")) { goto plan_oom; } free(ref_hex); ref_hex = NULL; } else { if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "null")) { goto plan_oom; } } if (!amduatd_fed_push_plan_strbuf_append_cstr(&b, "}}\n")) { goto plan_oom; } amduatd_fed_push_plan_candidate_free(&candidate); free(cursor_ref_hex); *out_json = b.data; return AMDUATD_FED_PUSH_PLAN_OK; plan_oom: free(ref_hex); amduatd_fed_push_plan_candidate_free(&candidate); free(cursor_ref_hex); amduatd_fed_push_plan_strbuf_free(&b); return AMDUATD_FED_PUSH_PLAN_ERR_OOM; }