diff --git a/README.md b/README.md index b4e16d5..87bd2b6 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,12 @@ string). Push cursors are separate from pull cursors and live under `fed/push_cursor//head` (space scoped). +To avoid cursor collisions across multiple mounts to the same peer, pass +`remote_space_id=` on cursor-aware endpoints. When provided, cursor +heads use `fed/cursor///head` and +`fed/push_cursor///head`. When omitted, the legacy v1 +cursor names remain in effect for backward compatibility. + Read the current cursor for a peer: ```sh @@ -90,6 +96,14 @@ curl --unix-socket amduatd.sock \ -H 'X-Amduat-Space: demo' ``` +Scoped to a specific remote space: + +```sh +curl --unix-socket amduatd.sock \ + 'http://localhost/v1/fed/cursor?peer=domain-2&remote_space_id=beta' \ + -H 'X-Amduat-Space: demo' +``` + Write a cursor update (CAS-safe; include `expected_ref` to enforce; omitting it only succeeds when the cursor is absent): @@ -119,6 +133,7 @@ curl --unix-socket amduatd.sock \ The plan does not write artifacts, records, or cursors. It is deterministic and returns only identifiers (logseq/ref), plus the next cursor candidate if the plan were applied successfully. +Append `&remote_space_id=` to use mount-specific cursor keying. Apply a bounded batch of remote records (advances the cursor only after success): @@ -131,6 +146,7 @@ curl --unix-socket amduatd.sock -X POST \ `/v1/fed/pull` requires the index backend and will not advance the cursor on partial failure. +Use `remote_space_id=` to scope the cursor to a mount. ### Federation push plan (sender dry run) @@ -145,6 +161,7 @@ curl --unix-socket amduatd.sock \ `/v1/fed/push/plan` requires the index backend and uses a push cursor separate from the pull cursor. +Append `&remote_space_id=` to use mount-specific cursor keying. ### Federation push (sender apply) @@ -159,6 +176,7 @@ curl --unix-socket amduatd.sock -X POST \ `/v1/fed/push` uses `/v1/fed/ingest` on the peer and only advances the push cursor after the batch completes. It requires the index backend. +Use `remote_space_id=` to scope the cursor to a mount. ### Federation sync until caught up @@ -245,6 +263,10 @@ curl --unix-socket amduatd.sock \ -H 'X-Amduat-Space: demo' ``` +The response groups cursor status per peer and per remote space id: +`peers:[{peer_key, remotes:[{remote_space_id, pull_cursor, push_cursor}]}]`. +`remote_space_id` is `null` for legacy v1 cursor heads. + ## Space manifest `/v1/space/manifest` returns the space manifest rooted at the deterministic @@ -289,6 +311,8 @@ space manifest mounts with their local pull cursor state. It performs no network I/O and does not mutate storage. Track mounts indicate intent; syncing remains a separate concern. If no manifest head is present, the endpoint returns a 404. +Track mounts report `local_tracking.cursor_namespace` (`v2` when using +`remote_space_id`-keyed cursors). ```sh curl --unix-socket amduatd.sock \ @@ -458,14 +482,18 @@ When the daemon uses the `fs` store backend, index-only checks are reported as - `GET /v1/contract` → contract bytes (JSON) (+ `X-Amduat-Contract-Ref` header) - `GET /v1/contract?format=ref` → `{ref}` - `GET /v1/space/doctor` → deterministic space health checks +- `GET /v1/space/manifest` → `{effective_space, manifest_ref, manifest}` +- `PUT /v1/space/manifest` → `{effective_space, manifest_ref, updated, previous_ref?, manifest}` +- `GET /v1/space/mounts/resolve` → `{effective_space, manifest_ref, mounts}` +- `GET /v1/space/sync/status` → `{effective_space, store_backend, federation, peers}` - `GET /v1/ui` → browser UI for authoring/running programs - `GET /v1/fed/records?domain_id=...&from_logseq=...&limit=...` → `{domain_id, snapshot_id, log_prefix, next_logseq, records[]}` (published artifacts + tombstones + PER + TGK edges) -- `GET /v1/fed/cursor?peer=...` → `{peer_key, space_id, last_logseq, last_record_hash, ref}` -- `POST /v1/fed/cursor?peer=...` → `{ref}` (CAS update; `expected_ref` in body) -- `GET /v1/fed/pull/plan?peer=...&limit=...` → `{peer, effective_space, cursor, remote_scan, records, next_cursor_candidate, ...}` -- `GET /v1/fed/push/plan?peer=...&limit=...` → `{peer, domain_id, effective_space, cursor, scan, records, required_artifacts, next_cursor_candidate}` -- `POST /v1/fed/pull?peer=...&limit=...` → `{peer, effective_space, cursor_before, plan_summary, applied, cursor_after, errors}` -- `POST /v1/fed/push?peer=...&limit=...` → `{peer, domain_id, effective_space, cursor_before, plan_summary, sent, cursor_after, errors}` +- `GET /v1/fed/cursor?peer=...&remote_space_id=...` → `{peer_key, space_id, last_logseq, last_record_hash, ref}` (`remote_space_id` optional) +- `POST /v1/fed/cursor?peer=...&remote_space_id=...` → `{ref}` (CAS update; `expected_ref` in body; `remote_space_id` optional) +- `GET /v1/fed/pull/plan?peer=...&limit=...&remote_space_id=...` → `{peer, effective_space, cursor, remote_scan, records, next_cursor_candidate, ...}` +- `GET /v1/fed/push/plan?peer=...&limit=...&remote_space_id=...` → `{peer, domain_id, effective_space, cursor, scan, records, required_artifacts, next_cursor_candidate}` +- `POST /v1/fed/pull?peer=...&limit=...&remote_space_id=...` → `{peer, effective_space, cursor_before, plan_summary, applied, cursor_after, errors}` +- `POST /v1/fed/push?peer=...&limit=...&remote_space_id=...` → `{peer, domain_id, effective_space, cursor_before, plan_summary, sent, cursor_after, errors}` - `GET /v1/fed/artifacts/{ref}` → raw bytes for federation resolve - `GET /v1/fed/status` → `{status, domain_id, registry_ref, last_tick_ms}` - `POST /v1/artifacts` diff --git a/registry/amduatd-api-contract.v1.json b/registry/amduatd-api-contract.v1.json index aaaf862..17843af 100644 --- a/registry/amduatd-api-contract.v1.json +++ b/registry/amduatd-api-contract.v1.json @@ -15,9 +15,16 @@ {"method": "POST", "path": "/v1/capabilities"}, {"method": "GET", "path": "/v1/cap/resolve"}, {"method": "GET", "path": "/v1/fed/records"}, + {"method": "GET", "path": "/v1/fed/cursor"}, + {"method": "POST", "path": "/v1/fed/cursor"}, + {"method": "GET", "path": "/v1/fed/pull/plan"}, + {"method": "GET", "path": "/v1/fed/push/plan"}, + {"method": "POST", "path": "/v1/fed/pull"}, {"method": "GET", "path": "/v1/fed/artifacts/{ref}"}, {"method": "GET", "path": "/v1/fed/status"}, + {"method": "POST", "path": "/v1/fed/ingest"}, {"method": "POST", "path": "/v1/fed/pull/until"}, + {"method": "POST", "path": "/v1/fed/push"}, {"method": "POST", "path": "/v1/fed/push/until"}, {"method": "POST", "path": "/v1/concepts"}, {"method": "GET", "path": "/v1/concepts"}, @@ -241,13 +248,87 @@ "manifest": {"$ref": "#/schemas/space_manifest"} } }, + "space_mounts_pull_cursor": { + "type": "object", + "required": ["present"], + "properties": { + "present": {"type": "boolean"}, + "last_logseq": {"type": "integer"}, + "ref": {"type": "string"} + } + }, + "space_mounts_local_tracking": { + "type": "object", + "required": ["cursor_namespace", "cursor_scope", "remote_space_id", "pull_cursor"], + "properties": { + "cursor_namespace": {"type": "string"}, + "cursor_scope": {"type": "string"}, + "remote_space_id": {"type": "string"}, + "pull_cursor": {"$ref": "#/schemas/space_mounts_pull_cursor"} + } + }, + "space_mounts_resolved_mount": { + "type": "object", + "required": ["name", "peer_key", "space_id", "mode", "local_tracking"], + "properties": { + "name": {"type": "string"}, + "peer_key": {"type": "string"}, + "space_id": {"type": "string"}, + "mode": {"type": "string"}, + "pinned_root_ref": {"type": "string"}, + "local_tracking": {"$ref": "#/schemas/space_mounts_local_tracking"} + } + }, "space_mounts_resolve_response": { "type": "object", "required": ["effective_space", "manifest_ref", "mounts"], "properties": { "effective_space": {"type": "object"}, "manifest_ref": {"type": "string"}, - "mounts": {"type": "array"} + "mounts": {"type": "array", "items": {"$ref": "#/schemas/space_mounts_resolved_mount"}} + } + }, + "space_sync_status_cursor": { + "type": "object", + "required": ["present"], + "properties": { + "present": {"type": "boolean"}, + "last_logseq": {"type": "integer"}, + "ref": {"type": "string"} + } + }, + "space_sync_status_remote": { + "type": "object", + "required": ["remote_space_id", "pull_cursor", "push_cursor"], + "properties": { + "remote_space_id": {"type": ["string", "null"]}, + "pull_cursor": {"$ref": "#/schemas/space_sync_status_cursor"}, + "push_cursor": {"$ref": "#/schemas/space_sync_status_cursor"} + } + }, + "space_sync_status_peer": { + "type": "object", + "required": ["peer_key", "remotes"], + "properties": { + "peer_key": {"type": "string"}, + "remotes": {"type": "array", "items": {"$ref": "#/schemas/space_sync_status_remote"}} + } + }, + "space_sync_status_response": { + "type": "object", + "required": ["effective_space", "store_backend", "federation", "peers"], + "properties": { + "effective_space": {"type": "object"}, + "store_backend": {"type": "string"}, + "federation": { + "type": "object", + "required": ["enabled", "transport"], + "properties": { + "enabled": {"type": "boolean"}, + "transport": {"type": "string"} + } + }, + "peers": {"type": "array", "items": {"$ref": "#/schemas/space_sync_status_peer"}} } } } diff --git a/src/amduatd.c b/src/amduatd.c index 8dcaad1..9766367 100644 --- a/src/amduatd.c +++ b/src/amduatd.c @@ -140,13 +140,12 @@ static const char k_amduatd_contract_v1_json[] = "{\"method\":\"GET\",\"path\":\"/v1/fed/pull/plan\"}," "{\"method\":\"GET\",\"path\":\"/v1/fed/push/plan\"}," "{\"method\":\"POST\",\"path\":\"/v1/fed/pull\"}," + "{\"method\":\"GET\",\"path\":\"/v1/fed/artifacts/{ref}\"}," + "{\"method\":\"GET\",\"path\":\"/v1/fed/status\"}," + "{\"method\":\"POST\",\"path\":\"/v1/fed/ingest\"}," "{\"method\":\"POST\",\"path\":\"/v1/fed/pull/until\"}," "{\"method\":\"POST\",\"path\":\"/v1/fed/push\"}," "{\"method\":\"POST\",\"path\":\"/v1/fed/push/until\"}," - "{\"method\":\"POST\",\"path\":\"/v1/fed/pull\"}," - "{\"method\":\"POST\",\"path\":\"/v1/fed/ingest\"}," - "{\"method\":\"GET\",\"path\":\"/v1/fed/artifacts/{ref}\"}," - "{\"method\":\"GET\",\"path\":\"/v1/fed/status\"}," "{\"method\":\"POST\",\"path\":\"/v1/concepts\"}," "{\"method\":\"GET\",\"path\":\"/v1/concepts\"}," "{\"method\":\"GET\",\"path\":\"/v1/concepts/{name}\"}," @@ -335,13 +334,87 @@ static const char k_amduatd_contract_v1_json[] = "\"manifest\":{\"$ref\":\"#/schemas/space_manifest\"}" "}" "}," + "\"space_mounts_pull_cursor\":{" + "\"type\":\"object\"," + "\"required\":[\"present\"]," + "\"properties\":{" + "\"present\":{\"type\":\"boolean\"}," + "\"last_logseq\":{\"type\":\"integer\"}," + "\"ref\":{\"type\":\"string\"}" + "}" + "}," + "\"space_mounts_local_tracking\":{" + "\"type\":\"object\"," + "\"required\":[\"cursor_namespace\",\"cursor_scope\",\"remote_space_id\",\"pull_cursor\"]," + "\"properties\":{" + "\"cursor_namespace\":{\"type\":\"string\"}," + "\"cursor_scope\":{\"type\":\"string\"}," + "\"remote_space_id\":{\"type\":\"string\"}," + "\"pull_cursor\":{\"$ref\":\"#/schemas/space_mounts_pull_cursor\"}" + "}" + "}," + "\"space_mounts_resolved_mount\":{" + "\"type\":\"object\"," + "\"required\":[\"name\",\"peer_key\",\"space_id\",\"mode\",\"local_tracking\"]," + "\"properties\":{" + "\"name\":{\"type\":\"string\"}," + "\"peer_key\":{\"type\":\"string\"}," + "\"space_id\":{\"type\":\"string\"}," + "\"mode\":{\"type\":\"string\"}," + "\"pinned_root_ref\":{\"type\":\"string\"}," + "\"local_tracking\":{\"$ref\":\"#/schemas/space_mounts_local_tracking\"}" + "}" + "}," "\"space_mounts_resolve_response\":{" "\"type\":\"object\"," "\"required\":[\"effective_space\",\"manifest_ref\",\"mounts\"]," "\"properties\":{" "\"effective_space\":{\"type\":\"object\"}," "\"manifest_ref\":{\"type\":\"string\"}," - "\"mounts\":{\"type\":\"array\"}" + "\"mounts\":{\"type\":\"array\",\"items\":{\"$ref\":\"#/schemas/space_mounts_resolved_mount\"}}" + "}" + "}," + "\"space_sync_status_cursor\":{" + "\"type\":\"object\"," + "\"required\":[\"present\"]," + "\"properties\":{" + "\"present\":{\"type\":\"boolean\"}," + "\"last_logseq\":{\"type\":\"integer\"}," + "\"ref\":{\"type\":\"string\"}" + "}" + "}," + "\"space_sync_status_remote\":{" + "\"type\":\"object\"," + "\"required\":[\"remote_space_id\",\"pull_cursor\",\"push_cursor\"]," + "\"properties\":{" + "\"remote_space_id\":{\"type\":[\"string\",\"null\"]}," + "\"pull_cursor\":{\"$ref\":\"#/schemas/space_sync_status_cursor\"}," + "\"push_cursor\":{\"$ref\":\"#/schemas/space_sync_status_cursor\"}" + "}" + "}," + "\"space_sync_status_peer\":{" + "\"type\":\"object\"," + "\"required\":[\"peer_key\",\"remotes\"]," + "\"properties\":{" + "\"peer_key\":{\"type\":\"string\"}," + "\"remotes\":{\"type\":\"array\",\"items\":{\"$ref\":\"#/schemas/space_sync_status_remote\"}}" + "}" + "}," + "\"space_sync_status_response\":{" + "\"type\":\"object\"," + "\"required\":[\"effective_space\",\"store_backend\",\"federation\",\"peers\"]," + "\"properties\":{" + "\"effective_space\":{\"type\":\"object\"}," + "\"store_backend\":{\"type\":\"string\"}," + "\"federation\":{" + "\"type\":\"object\"," + "\"required\":[\"enabled\",\"transport\"]," + "\"properties\":{" + "\"enabled\":{\"type\":\"boolean\"}," + "\"transport\":{\"type\":\"string\"}" + "}" + "}," + "\"peers\":{\"type\":\"array\",\"items\":{\"$ref\":\"#/schemas/space_sync_status_peer\"}}" "}" "}" "}" @@ -743,6 +816,39 @@ static bool amduatd_trim_header_value(const char *value, return true; } +static bool amduatd_get_remote_space_id(const amduatd_http_req_t *req, + char *buf, + size_t cap, + const char **out_remote, + const char **out_error) { + const char *remote = NULL; + if (out_remote != NULL) { + *out_remote = NULL; + } + if (out_error != NULL) { + *out_error = NULL; + } + if (req == NULL || buf == NULL || cap == 0u || out_remote == NULL) { + if (out_error != NULL) { + *out_error = "invalid remote_space_id"; + } + return false; + } + if (amduatd_query_param(req->path, "remote_space_id", buf, cap) == NULL || + buf[0] == '\0') { + return true; + } + if (!amduatd_space_space_id_is_valid(buf)) { + if (out_error != NULL) { + *out_error = "invalid remote_space_id"; + } + return false; + } + remote = buf; + *out_remote = remote; + return true; +} + static bool amduatd_parse_type_tag_hex(const char *text, bool *out_has_type_tag, amduat_type_tag_t *out_type_tag) { @@ -1661,12 +1767,111 @@ manifest_put_cleanup: return ok; } +typedef struct { + char *peer_key; + char *remote_space_id; +} amduatd_cursor_pair_t; + +static void amduatd_cursor_pair_free(amduatd_cursor_pair_t *pair) { + if (pair == NULL) { + return; + } + free(pair->peer_key); + free(pair->remote_space_id); + pair->peer_key = NULL; + pair->remote_space_id = NULL; +} + +static int amduatd_cursor_pair_cmp(const void *a, const void *b) { + const amduatd_cursor_pair_t *lhs = (const amduatd_cursor_pair_t *)a; + const amduatd_cursor_pair_t *rhs = (const amduatd_cursor_pair_t *)b; + const char *lhs_peer = lhs != NULL && lhs->peer_key != NULL ? lhs->peer_key + : ""; + const char *rhs_peer = rhs != NULL && rhs->peer_key != NULL ? rhs->peer_key + : ""; + const char *lhs_remote = + lhs != NULL && lhs->remote_space_id != NULL ? lhs->remote_space_id : ""; + const char *rhs_remote = + rhs != NULL && rhs->remote_space_id != NULL ? rhs->remote_space_id : ""; + int cmp = strcmp(lhs_peer, rhs_peer); + if (cmp != 0) { + return cmp; + } + return strcmp(lhs_remote, rhs_remote); +} + +static bool amduatd_cursor_pair_add(amduatd_cursor_pair_t **pairs, + size_t *len, + size_t *cap, + const char *peer_key, + const char *remote_space_id) { + size_t next_len; + amduatd_cursor_pair_t *next; + + if (pairs == NULL || len == NULL || cap == NULL || + peer_key == NULL || peer_key[0] == '\0') { + return false; + } + for (size_t i = 0u; i < *len; ++i) { + const char *cur_peer = (*pairs)[i].peer_key; + const char *cur_remote = (*pairs)[i].remote_space_id; + const char *remote = remote_space_id != NULL ? remote_space_id : ""; + if (cur_peer != NULL && strcmp(cur_peer, peer_key) == 0) { + if ((cur_remote == NULL || cur_remote[0] == '\0') && remote[0] == '\0') { + return true; + } + if (cur_remote != NULL && strcmp(cur_remote, remote) == 0) { + return true; + } + } + } + if (*len == *cap) { + size_t next_cap = *cap != 0u ? *cap * 2u : 8u; + next = (amduatd_cursor_pair_t *)realloc(*pairs, + next_cap * sizeof(*next)); + if (next == NULL) { + return false; + } + *pairs = next; + *cap = next_cap; + } + next_len = *len + 1u; + (*pairs)[*len].peer_key = strdup(peer_key); + if ((*pairs)[*len].peer_key == NULL) { + return false; + } + if (remote_space_id != NULL && remote_space_id[0] != '\0') { + (*pairs)[*len].remote_space_id = strdup(remote_space_id); + if ((*pairs)[*len].remote_space_id == NULL) { + free((*pairs)[*len].peer_key); + (*pairs)[*len].peer_key = NULL; + return false; + } + } else { + (*pairs)[*len].remote_space_id = NULL; + } + *len = next_len; + return true; +} + +static void amduatd_cursor_pairs_free(amduatd_cursor_pair_t *pairs, + size_t len) { + if (pairs == NULL) { + return; + } + for (size_t i = 0u; i < len; ++i) { + amduatd_cursor_pair_free(&pairs[i]); + } + free(pairs); +} + static bool amduatd_sync_status_append_cursor( amduatd_strbuf_t *b, amduat_asl_store_t *store, amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, bool push) { amduatd_fed_cursor_record_t cursor; amduat_reference_t ref; @@ -1679,18 +1884,20 @@ static bool amduatd_sync_status_append_cursor( ref = amduat_reference(0u, amduat_octets(NULL, 0u)); status = push - ? amduatd_fed_push_cursor_get(store, - pointer_store, - effective_space, - peer_key, - &cursor, - &ref) - : amduatd_fed_cursor_get(store, - pointer_store, - effective_space, - peer_key, - &cursor, - &ref); + ? amduatd_fed_push_cursor_get_remote(store, + pointer_store, + effective_space, + peer_key, + remote_space_id, + &cursor, + &ref) + : amduatd_fed_cursor_get_remote(store, + pointer_store, + effective_space, + peer_key, + remote_space_id, + &cursor, + &ref); if (status == AMDUATD_FED_CURSOR_OK) { present = true; @@ -1781,11 +1988,18 @@ static bool amduatd_handle_get_space_sync_status( const char *root_path, amduatd_store_backend_t store_backend) { amduat_asl_pointer_store_t pointer_store; - amduatd_space_roots_list_t peers; + amduatd_space_roots_list_t pull_heads; + amduatd_space_roots_list_t push_heads; amduatd_strbuf_t b; + amduatd_cursor_pair_t *pairs = NULL; + size_t pair_len = 0u; + size_t pair_cap = 0u; + amduat_octets_t pull_prefix = amduat_octets(NULL, 0u); + amduat_octets_t push_prefix = amduat_octets(NULL, 0u); bool ok = false; - memset(&peers, 0, sizeof(peers)); + memset(&pull_heads, 0, sizeof(pull_heads)); + memset(&push_heads, 0, sizeof(push_heads)); memset(&b, 0, sizeof(b)); if (store == NULL || req == NULL || dcfg == NULL || fed_cfg == NULL || @@ -1810,12 +2024,66 @@ static bool amduatd_handle_get_space_sync_status( return amduatd_send_json_error(fd, 500, "Internal Server Error", "pointer store error"); } - if (!amduatd_space_roots_list_cursor_peers(root_path, - req->effective_space, - &peers)) { + if (!amduatd_space_scope_name(req->effective_space, + "fed/cursor", + &pull_prefix) || + !amduatd_space_scope_name(req->effective_space, + "fed/push_cursor", + &push_prefix)) { return amduatd_send_json_error(fd, 500, "Internal Server Error", "cursor scan failed"); } + if (!amduatd_space_roots_list_cursor_heads(root_path, + req->effective_space, + false, + &pull_heads) || + !amduatd_space_roots_list_cursor_heads(root_path, + req->effective_space, + true, + &push_heads)) { + amduat_octets_free(&pull_prefix); + amduat_octets_free(&push_prefix); + return amduatd_send_json_error(fd, 500, "Internal Server Error", + "cursor scan failed"); + } + + for (size_t i = 0u; i < pull_heads.len; ++i) { + char *peer = NULL; + char *remote = NULL; + if (amduatd_space_roots_cursor_parse((const char *)pull_prefix.data, + pull_heads.names[i], + &peer, + &remote)) { + if (!amduatd_cursor_pair_add(&pairs, &pair_len, &pair_cap, + peer, remote)) { + free(peer); + free(remote); + goto sync_cleanup; + } + free(peer); + free(remote); + } + } + for (size_t i = 0u; i < push_heads.len; ++i) { + char *peer = NULL; + char *remote = NULL; + if (amduatd_space_roots_cursor_parse((const char *)push_prefix.data, + push_heads.names[i], + &peer, + &remote)) { + if (!amduatd_cursor_pair_add(&pairs, &pair_len, &pair_cap, + peer, remote)) { + free(peer); + free(remote); + goto sync_cleanup; + } + free(peer); + free(remote); + } + } + if (pair_len > 1u) { + qsort(pairs, pair_len, sizeof(*pairs), amduatd_cursor_pair_cmp); + } if (!amduatd_strbuf_append_cstr(&b, "{\"effective_space\":{")) { goto sync_cleanup; @@ -1849,39 +2117,91 @@ static bool amduatd_handle_get_space_sync_status( goto sync_cleanup; } - for (size_t i = 0u; i < peers.len; ++i) { - const char *peer_key = peers.names[i]; - if (i != 0u) { - if (!amduatd_strbuf_append_char(&b, ',')) { + { + const char *current_peer = NULL; + bool first_peer = true; + bool first_remote = true; + + for (size_t i = 0u; i < pair_len; ++i) { + const char *peer_key = pairs[i].peer_key; + const char *remote_space_id = pairs[i].remote_space_id; + if (peer_key == NULL || peer_key[0] == '\0') { + continue; + } + if (current_peer == NULL || strcmp(current_peer, peer_key) != 0) { + if (!first_peer) { + if (!amduatd_strbuf_append_cstr(&b, "]}")) { + goto sync_cleanup; + } + } + if (!first_peer) { + if (!amduatd_strbuf_append_char(&b, ',')) { + goto sync_cleanup; + } + } + if (!amduatd_strbuf_append_cstr(&b, "{\"peer_key\":\"") || + !amduatd_strbuf_append_cstr(&b, peer_key) || + !amduatd_strbuf_append_cstr(&b, "\",\"remotes\":[")) { + goto sync_cleanup; + } + current_peer = peer_key; + first_peer = false; + first_remote = true; + } + + if (!first_remote) { + if (!amduatd_strbuf_append_char(&b, ',')) { + goto sync_cleanup; + } + } + if (!amduatd_strbuf_append_cstr(&b, "{\"remote_space_id\":")) { goto sync_cleanup; } + if (remote_space_id != NULL && remote_space_id[0] != '\0') { + if (!amduatd_strbuf_append_cstr(&b, "\"") || + !amduatd_strbuf_append_cstr(&b, remote_space_id) || + !amduatd_strbuf_append_cstr(&b, "\"")) { + goto sync_cleanup; + } + } else { + if (!amduatd_strbuf_append_cstr(&b, "null")) { + goto sync_cleanup; + } + } + if (!amduatd_strbuf_append_cstr(&b, ",\"pull_cursor\":")) { + goto sync_cleanup; + } + if (!amduatd_sync_status_append_cursor(&b, + store, + &pointer_store, + req->effective_space, + peer_key, + remote_space_id, + false)) { + goto sync_cleanup; + } + if (!amduatd_strbuf_append_cstr(&b, ",\"push_cursor\":")) { + goto sync_cleanup; + } + if (!amduatd_sync_status_append_cursor(&b, + store, + &pointer_store, + req->effective_space, + peer_key, + remote_space_id, + true)) { + goto sync_cleanup; + } + if (!amduatd_strbuf_append_cstr(&b, "}")) { + goto sync_cleanup; + } + first_remote = false; } - if (!amduatd_strbuf_append_cstr(&b, "{\"peer_key\":\"") || - !amduatd_strbuf_append_cstr(&b, peer_key) || - !amduatd_strbuf_append_cstr(&b, "\",\"pull_cursor\":")) { - goto sync_cleanup; - } - if (!amduatd_sync_status_append_cursor(&b, - store, - &pointer_store, - req->effective_space, - peer_key, - false)) { - goto sync_cleanup; - } - if (!amduatd_strbuf_append_cstr(&b, ",\"push_cursor\":")) { - goto sync_cleanup; - } - if (!amduatd_sync_status_append_cursor(&b, - store, - &pointer_store, - req->effective_space, - peer_key, - true)) { - goto sync_cleanup; - } - if (!amduatd_strbuf_append_cstr(&b, "}")) { - goto sync_cleanup; + + if (current_peer != NULL) { + if (!amduatd_strbuf_append_cstr(&b, "]}")) { + goto sync_cleanup; + } } } @@ -1895,7 +2215,11 @@ sync_cleanup: if (!ok) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error"); } - amduatd_space_roots_list_free(&peers); + amduatd_space_roots_list_free(&pull_heads); + amduatd_space_roots_list_free(&push_heads); + amduatd_cursor_pairs_free(pairs, pair_len); + amduat_octets_free(&pull_prefix); + amduat_octets_free(&push_prefix); amduatd_strbuf_free(&b); return ok; } @@ -2586,6 +2910,9 @@ static bool amduatd_handle_get_fed_cursor(int fd, const amduatd_http_req_t *req, const char *root_path) { char peer_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + char remote_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + const char *remote_space_id = NULL; + const char *remote_error = NULL; amduat_asl_pointer_store_t pointer_store; amduatd_fed_cursor_record_t cursor; amduat_reference_t ref; @@ -2627,11 +2954,26 @@ static bool amduatd_handle_get_fed_cursor(int fd, return amduatd_send_json_error(fd, 400, "Bad Request", "missing peer"); } + if (!amduatd_get_remote_space_id(req, + remote_buf, + sizeof(remote_buf), + &remote_space_id, + &remote_error)) { + return amduatd_send_json_error(fd, 400, "Bad Request", + remote_error); + } { amduat_octets_t scoped = amduat_octets(NULL, 0u); - if (!amduatd_fed_cursor_pointer_name(req->effective_space, - peer_buf, - &scoped)) { + if (remote_space_id != NULL) { + if (!amduatd_fed_cursor_pointer_name_v2(req->effective_space, + peer_buf, + remote_space_id, + &scoped)) { + return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); + } + } else if (!amduatd_fed_cursor_pointer_name(req->effective_space, + peer_buf, + &scoped)) { return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); } amduat_octets_free(&scoped); @@ -2644,12 +2986,13 @@ static bool amduatd_handle_get_fed_cursor(int fd, amduatd_fed_cursor_record_init(&cursor); memset(&ref, 0, sizeof(ref)); - status = amduatd_fed_cursor_get(store, - &pointer_store, - req->effective_space, - peer_buf, - &cursor, - &ref); + status = amduatd_fed_cursor_get_remote(store, + &pointer_store, + req->effective_space, + peer_buf, + remote_space_id, + &cursor, + &ref); if (status == AMDUATD_FED_CURSOR_ERR_NOT_FOUND) { return amduatd_send_json_error(fd, 404, "Not Found", "cursor not found"); } @@ -2751,6 +3094,9 @@ static bool amduatd_handle_post_fed_cursor(int fd, const amduatd_http_req_t *req, const char *root_path) { char peer_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + char remote_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + const char *remote_space_id = NULL; + const char *remote_error = NULL; uint8_t *body = NULL; const char *p = NULL; const char *end = NULL; @@ -2804,11 +3150,26 @@ static bool amduatd_handle_post_fed_cursor(int fd, return amduatd_send_json_error(fd, 400, "Bad Request", "missing peer"); } + if (!amduatd_get_remote_space_id(req, + remote_buf, + sizeof(remote_buf), + &remote_space_id, + &remote_error)) { + return amduatd_send_json_error(fd, 400, "Bad Request", + remote_error); + } { amduat_octets_t scoped = amduat_octets(NULL, 0u); - if (!amduatd_fed_cursor_pointer_name(req->effective_space, - peer_buf, - &scoped)) { + if (remote_space_id != NULL) { + if (!amduatd_fed_cursor_pointer_name_v2(req->effective_space, + peer_buf, + remote_space_id, + &scoped)) { + return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); + } + } else if (!amduatd_fed_cursor_pointer_name(req->effective_space, + peer_buf, + &scoped)) { return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); } amduat_octets_free(&scoped); @@ -2971,11 +3332,12 @@ fed_cursor_parse_fail: "pointer store error"); } - status = amduatd_fed_cursor_cas_set( + status = amduatd_fed_cursor_cas_set_remote( store, &pointer_store, req->effective_space, peer_buf, + remote_space_id, have_expected_ref ? &expected_ref : NULL, &cursor, &new_ref); @@ -3713,6 +4075,9 @@ static bool amduatd_handle_get_fed_pull_plan(int fd, const amduatd_http_req_t *req, const char *root_path) { char peer_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + char remote_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + const char *remote_space_id = NULL; + const char *remote_error = NULL; char limit_buf[32]; uint64_t limit = 128u; uint64_t from_logseq = 0u; @@ -3772,11 +4137,26 @@ static bool amduatd_handle_get_fed_pull_plan(int fd, return amduatd_send_json_error(fd, 400, "Bad Request", "missing peer"); } + if (!amduatd_get_remote_space_id(req, + remote_buf, + sizeof(remote_buf), + &remote_space_id, + &remote_error)) { + return amduatd_send_json_error(fd, 400, "Bad Request", + remote_error); + } { amduat_octets_t scoped = amduat_octets(NULL, 0u); - if (!amduatd_fed_cursor_pointer_name(req->effective_space, - peer_buf, - &scoped)) { + if (remote_space_id != NULL) { + if (!amduatd_fed_cursor_pointer_name_v2(req->effective_space, + peer_buf, + remote_space_id, + &scoped)) { + return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); + } + } else if (!amduatd_fed_cursor_pointer_name(req->effective_space, + peer_buf, + &scoped)) { return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); } amduat_octets_free(&scoped); @@ -3802,12 +4182,13 @@ static bool amduatd_handle_get_fed_pull_plan(int fd, memset(&cursor_ref, 0, sizeof(cursor_ref)); { amduatd_fed_cursor_status_t cursor_status; - cursor_status = amduatd_fed_cursor_get(store, - &pointer_store, - req->effective_space, - peer_buf, - &cursor, - &cursor_ref); + cursor_status = amduatd_fed_cursor_get_remote(store, + &pointer_store, + req->effective_space, + peer_buf, + remote_space_id, + &cursor, + &cursor_ref); if (cursor_status == AMDUATD_FED_CURSOR_ERR_NOT_FOUND) { cursor_present = false; } else if (cursor_status == AMDUATD_FED_CURSOR_OK) { @@ -3932,6 +4313,9 @@ static bool amduatd_handle_get_fed_push_plan(int fd, const amduatd_http_req_t *req, const char *root_path) { char peer_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + char remote_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + const char *remote_space_id = NULL; + const char *remote_error = NULL; char limit_buf[32]; uint64_t limit = 128u; uint32_t domain_id = 0u; @@ -3982,11 +4366,26 @@ static bool amduatd_handle_get_fed_push_plan(int fd, return amduatd_send_json_error(fd, 400, "Bad Request", "missing peer"); } + if (!amduatd_get_remote_space_id(req, + remote_buf, + sizeof(remote_buf), + &remote_space_id, + &remote_error)) { + return amduatd_send_json_error(fd, 400, "Bad Request", + remote_error); + } { amduat_octets_t scoped = amduat_octets(NULL, 0u); - if (!amduatd_fed_push_cursor_pointer_name(req->effective_space, - peer_buf, - &scoped)) { + if (remote_space_id != NULL) { + if (!amduatd_fed_push_cursor_pointer_name_v2(req->effective_space, + peer_buf, + remote_space_id, + &scoped)) { + return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); + } + } else if (!amduatd_fed_push_cursor_pointer_name(req->effective_space, + peer_buf, + &scoped)) { return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); } amduat_octets_free(&scoped); @@ -4017,6 +4416,7 @@ static bool amduatd_handle_get_fed_push_plan(int fd, &pointer_store, req->effective_space, peer_buf, + remote_space_id, limit, root_path, &scan); @@ -5544,6 +5944,9 @@ static bool amduatd_handle_post_fed_pull(int fd, const amduatd_http_req_t *req, const char *root_path) { char peer_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + char remote_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + const char *remote_space_id = NULL; + const char *remote_error = NULL; char limit_buf[32]; uint64_t limit = 128u; amduat_asl_pointer_store_t pointer_store; @@ -5592,11 +5995,26 @@ static bool amduatd_handle_post_fed_pull(int fd, return amduatd_send_json_error(fd, 400, "Bad Request", "missing peer"); } + if (!amduatd_get_remote_space_id(req, + remote_buf, + sizeof(remote_buf), + &remote_space_id, + &remote_error)) { + return amduatd_send_json_error(fd, 400, "Bad Request", + remote_error); + } { amduat_octets_t scoped = amduat_octets(NULL, 0u); - if (!amduatd_fed_cursor_pointer_name(req->effective_space, - peer_buf, - &scoped)) { + if (remote_space_id != NULL) { + if (!amduatd_fed_cursor_pointer_name_v2(req->effective_space, + peer_buf, + remote_space_id, + &scoped)) { + return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); + } + } else if (!amduatd_fed_cursor_pointer_name(req->effective_space, + peer_buf, + &scoped)) { return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); } amduat_octets_free(&scoped); @@ -5642,6 +6060,7 @@ static bool amduatd_handle_post_fed_pull(int fd, &pointer_store, req->effective_space, peer_buf, + remote_space_id, limit, fed_cfg, &pull_transport, @@ -5987,6 +6406,9 @@ static bool amduatd_handle_post_fed_pull_until( const amduatd_http_req_t *req, const char *root_path) { char peer_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + char remote_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + const char *remote_space_id = NULL; + const char *remote_error = NULL; char limit_buf[32]; char rounds_buf[32]; uint64_t limit = 128u; @@ -6046,11 +6468,26 @@ static bool amduatd_handle_post_fed_pull_until( return amduatd_send_json_error(fd, 400, "Bad Request", "missing peer"); } + if (!amduatd_get_remote_space_id(req, + remote_buf, + sizeof(remote_buf), + &remote_space_id, + &remote_error)) { + return amduatd_send_json_error(fd, 400, "Bad Request", + remote_error); + } { amduat_octets_t scoped = amduat_octets(NULL, 0u); - if (!amduatd_fed_cursor_pointer_name(req->effective_space, - peer_buf, - &scoped)) { + if (remote_space_id != NULL) { + if (!amduatd_fed_cursor_pointer_name_v2(req->effective_space, + peer_buf, + remote_space_id, + &scoped)) { + return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); + } + } else if (!amduatd_fed_cursor_pointer_name(req->effective_space, + peer_buf, + &scoped)) { return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); } amduat_octets_free(&scoped); @@ -6104,6 +6541,7 @@ static bool amduatd_handle_post_fed_pull_until( &pointer_store, req->effective_space, peer_buf, + remote_space_id, limit, max_rounds, fed_cfg, @@ -6255,6 +6693,9 @@ static bool amduatd_handle_post_fed_push(int fd, const amduatd_http_req_t *req, const char *root_path) { char peer_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + char remote_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + const char *remote_space_id = NULL; + const char *remote_error = NULL; char limit_buf[32]; uint64_t limit = 128u; uint32_t domain_id = 0u; @@ -6310,11 +6751,26 @@ static bool amduatd_handle_post_fed_push(int fd, return amduatd_send_json_error(fd, 400, "Bad Request", "missing peer"); } + if (!amduatd_get_remote_space_id(req, + remote_buf, + sizeof(remote_buf), + &remote_space_id, + &remote_error)) { + return amduatd_send_json_error(fd, 400, "Bad Request", + remote_error); + } { amduat_octets_t scoped = amduat_octets(NULL, 0u); - if (!amduatd_fed_push_cursor_pointer_name(req->effective_space, - peer_buf, - &scoped)) { + if (remote_space_id != NULL) { + if (!amduatd_fed_push_cursor_pointer_name_v2(req->effective_space, + peer_buf, + remote_space_id, + &scoped)) { + return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); + } + } else if (!amduatd_fed_push_cursor_pointer_name(req->effective_space, + peer_buf, + &scoped)) { return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); } amduat_octets_free(&scoped); @@ -6360,6 +6816,7 @@ static bool amduatd_handle_post_fed_push(int fd, &pointer_store, req->effective_space, peer_buf, + remote_space_id, limit, root_path, fed_cfg, @@ -6682,6 +7139,9 @@ static bool amduatd_handle_post_fed_push_until( const amduatd_http_req_t *req, const char *root_path) { char peer_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + char remote_buf[AMDUAT_ASL_POINTER_NAME_MAX + 1u]; + const char *remote_space_id = NULL; + const char *remote_error = NULL; char limit_buf[32]; char rounds_buf[32]; uint64_t limit = 128u; @@ -6740,11 +7200,26 @@ static bool amduatd_handle_post_fed_push_until( return amduatd_send_json_error(fd, 400, "Bad Request", "missing peer"); } + if (!amduatd_get_remote_space_id(req, + remote_buf, + sizeof(remote_buf), + &remote_space_id, + &remote_error)) { + return amduatd_send_json_error(fd, 400, "Bad Request", + remote_error); + } { amduat_octets_t scoped = amduat_octets(NULL, 0u); - if (!amduatd_fed_push_cursor_pointer_name(req->effective_space, - peer_buf, - &scoped)) { + if (remote_space_id != NULL) { + if (!amduatd_fed_push_cursor_pointer_name_v2(req->effective_space, + peer_buf, + remote_space_id, + &scoped)) { + return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); + } + } else if (!amduatd_fed_push_cursor_pointer_name(req->effective_space, + peer_buf, + &scoped)) { return amduatd_send_json_error(fd, 400, "Bad Request", "invalid peer"); } amduat_octets_free(&scoped); @@ -6795,6 +7270,7 @@ static bool amduatd_handle_post_fed_push_until( &pointer_store, req->effective_space, peer_buf, + remote_space_id, limit, max_rounds, root_path, diff --git a/src/amduatd_fed_cursor.c b/src/amduatd_fed_cursor.c index 428ec61..f76aa23 100644 --- a/src/amduatd_fed_cursor.c +++ b/src/amduatd_fed_cursor.c @@ -2,6 +2,7 @@ #include "amduat/asl/record.h" #include "amduat/enc/asl1_core_codec.h" +#include "amduatd_space.h" #include #include @@ -110,6 +111,11 @@ static bool amduatd_fed_cursor_peer_key_is_valid(const char *peer_key) { return amduat_asl_pointer_name_is_valid(peer_key); } +static bool amduatd_fed_cursor_remote_space_id_is_valid( + const char *remote_space_id) { + return amduatd_space_space_id_is_valid(remote_space_id); +} + static bool amduatd_fed_cursor_pointer_name_with_prefix( const amduatd_space_t *space, const char *peer_key, @@ -149,6 +155,56 @@ static bool amduatd_fed_cursor_pointer_name_with_prefix( return ok; } +static bool amduatd_fed_cursor_pointer_name_with_prefix_v2( + const amduatd_space_t *space, + const char *peer_key, + const char *remote_space_id, + const char *prefix, + amduat_octets_t *out_name) { + const char suffix[] = "/head"; + size_t peer_len; + size_t remote_len; + size_t total_len; + size_t prefix_len; + char *base = NULL; + bool ok; + + if (out_name != NULL) { + *out_name = amduat_octets(NULL, 0u); + } + if (out_name == NULL || prefix == NULL || + !amduatd_fed_cursor_peer_key_is_valid(peer_key) || + !amduatd_fed_cursor_remote_space_id_is_valid(remote_space_id)) { + return false; + } + peer_len = strlen(peer_key); + remote_len = strlen(remote_space_id); + prefix_len = strlen(prefix); + if (peer_len > SIZE_MAX - prefix_len - 1u) { + return false; + } + if (remote_len > SIZE_MAX - prefix_len - peer_len - 1u - + (sizeof(suffix) - 1u)) { + return false; + } + total_len = prefix_len + peer_len + 1u + remote_len + (sizeof(suffix) - 1u); + base = (char *)malloc(total_len + 1u); + if (base == NULL) { + return false; + } + memcpy(base, prefix, prefix_len); + memcpy(base + prefix_len, peer_key, peer_len); + base[prefix_len + peer_len] = '/'; + memcpy(base + prefix_len + peer_len + 1u, remote_space_id, remote_len); + memcpy(base + prefix_len + peer_len + 1u + remote_len, suffix, + sizeof(suffix) - 1u); + base[total_len] = '\0'; + + ok = amduatd_space_scope_name(space, base, out_name); + free(base); + return ok; +} + static bool amduatd_fed_cursor_record_encode( const amduatd_fed_cursor_record_t *record, amduat_octets_t *out_payload) { @@ -412,6 +468,28 @@ bool amduatd_fed_push_cursor_pointer_name(const amduatd_space_t *space, out_name); } +bool amduatd_fed_cursor_pointer_name_v2(const amduatd_space_t *space, + const char *peer_key, + const char *remote_space_id, + amduat_octets_t *out_name) { + return amduatd_fed_cursor_pointer_name_with_prefix_v2(space, + peer_key, + remote_space_id, + "fed/cursor/", + out_name); +} + +bool amduatd_fed_push_cursor_pointer_name_v2(const amduatd_space_t *space, + const char *peer_key, + const char *remote_space_id, + amduat_octets_t *out_name) { + return amduatd_fed_cursor_pointer_name_with_prefix_v2(space, + peer_key, + remote_space_id, + "fed/push_cursor/", + out_name); +} + amduatd_fed_cursor_status_t amduatd_fed_cursor_check_enabled( const amduatd_fed_cfg_t *cfg) { if (cfg == NULL) { @@ -428,6 +506,7 @@ static amduatd_fed_cursor_status_t amduatd_fed_cursor_get_with_prefix( amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, const char *prefix, amduatd_fed_cursor_record_t *out_cursor, amduat_reference_t *out_ref) { @@ -446,10 +525,18 @@ static amduatd_fed_cursor_status_t amduatd_fed_cursor_get_with_prefix( return AMDUATD_FED_CURSOR_ERR_INVALID; } - if (!amduatd_fed_cursor_pointer_name_with_prefix(effective_space, - peer_key, - prefix, - &pointer_name)) { + if (remote_space_id != NULL && remote_space_id[0] != '\0') { + if (!amduatd_fed_cursor_pointer_name_with_prefix_v2(effective_space, + peer_key, + remote_space_id, + prefix, + &pointer_name)) { + return AMDUATD_FED_CURSOR_ERR_INVALID; + } + } else if (!amduatd_fed_cursor_pointer_name_with_prefix(effective_space, + peer_key, + prefix, + &pointer_name)) { return AMDUATD_FED_CURSOR_ERR_INVALID; } @@ -529,6 +616,25 @@ amduatd_fed_cursor_status_t amduatd_fed_cursor_get( pointer_store, effective_space, peer_key, + NULL, + "fed/cursor/", + out_cursor, + out_ref); +} + +amduatd_fed_cursor_status_t amduatd_fed_cursor_get_remote( + amduat_asl_store_t *store, + amduat_asl_pointer_store_t *pointer_store, + const amduatd_space_t *effective_space, + const char *peer_key, + const char *remote_space_id, + amduatd_fed_cursor_record_t *out_cursor, + amduat_reference_t *out_ref) { + return amduatd_fed_cursor_get_with_prefix(store, + pointer_store, + effective_space, + peer_key, + remote_space_id, "fed/cursor/", out_cursor, out_ref); @@ -545,6 +651,25 @@ amduatd_fed_cursor_status_t amduatd_fed_push_cursor_get( pointer_store, effective_space, peer_key, + NULL, + "fed/push_cursor/", + out_cursor, + out_ref); +} + +amduatd_fed_cursor_status_t amduatd_fed_push_cursor_get_remote( + amduat_asl_store_t *store, + amduat_asl_pointer_store_t *pointer_store, + const amduatd_space_t *effective_space, + const char *peer_key, + const char *remote_space_id, + amduatd_fed_cursor_record_t *out_cursor, + amduat_reference_t *out_ref) { + return amduatd_fed_cursor_get_with_prefix(store, + pointer_store, + effective_space, + peer_key, + remote_space_id, "fed/push_cursor/", out_cursor, out_ref); @@ -555,6 +680,7 @@ static amduatd_fed_cursor_status_t amduatd_fed_cursor_cas_set_with_prefix( amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, const char *prefix, const amduat_reference_t *expected_ref, const amduatd_fed_cursor_record_t *new_cursor, @@ -591,10 +717,18 @@ static amduatd_fed_cursor_status_t amduatd_fed_cursor_cas_set_with_prefix( return AMDUATD_FED_CURSOR_ERR_INVALID; } - if (!amduatd_fed_cursor_pointer_name_with_prefix(effective_space, - peer_key, - prefix, - &pointer_name)) { + if (remote_space_id != NULL && remote_space_id[0] != '\0') { + if (!amduatd_fed_cursor_pointer_name_with_prefix_v2(effective_space, + peer_key, + remote_space_id, + prefix, + &pointer_name)) { + return AMDUATD_FED_CURSOR_ERR_INVALID; + } + } else if (!amduatd_fed_cursor_pointer_name_with_prefix(effective_space, + peer_key, + prefix, + &pointer_name)) { return AMDUATD_FED_CURSOR_ERR_INVALID; } @@ -653,6 +787,27 @@ amduatd_fed_cursor_status_t amduatd_fed_cursor_cas_set( pointer_store, effective_space, peer_key, + NULL, + "fed/cursor/", + expected_ref, + new_cursor, + out_new_ref); +} + +amduatd_fed_cursor_status_t amduatd_fed_cursor_cas_set_remote( + amduat_asl_store_t *store, + amduat_asl_pointer_store_t *pointer_store, + const amduatd_space_t *effective_space, + const char *peer_key, + const char *remote_space_id, + const amduat_reference_t *expected_ref, + const amduatd_fed_cursor_record_t *new_cursor, + amduat_reference_t *out_new_ref) { + return amduatd_fed_cursor_cas_set_with_prefix(store, + pointer_store, + effective_space, + peer_key, + remote_space_id, "fed/cursor/", expected_ref, new_cursor, @@ -671,6 +826,27 @@ amduatd_fed_cursor_status_t amduatd_fed_push_cursor_cas_set( pointer_store, effective_space, peer_key, + NULL, + "fed/push_cursor/", + expected_ref, + new_cursor, + out_new_ref); +} + +amduatd_fed_cursor_status_t amduatd_fed_push_cursor_cas_set_remote( + amduat_asl_store_t *store, + amduat_asl_pointer_store_t *pointer_store, + const amduatd_space_t *effective_space, + const char *peer_key, + const char *remote_space_id, + const amduat_reference_t *expected_ref, + const amduatd_fed_cursor_record_t *new_cursor, + amduat_reference_t *out_new_ref) { + return amduatd_fed_cursor_cas_set_with_prefix(store, + pointer_store, + effective_space, + peer_key, + remote_space_id, "fed/push_cursor/", expected_ref, new_cursor, diff --git a/src/amduatd_fed_cursor.h b/src/amduatd_fed_cursor.h index 44b53d1..a3d94bc 100644 --- a/src/amduatd_fed_cursor.h +++ b/src/amduatd_fed_cursor.h @@ -46,6 +46,16 @@ bool amduatd_fed_push_cursor_pointer_name(const amduatd_space_t *space, const char *peer_key, amduat_octets_t *out_name); +bool amduatd_fed_cursor_pointer_name_v2(const amduatd_space_t *space, + const char *peer_key, + const char *remote_space_id, + amduat_octets_t *out_name); + +bool amduatd_fed_push_cursor_pointer_name_v2(const amduatd_space_t *space, + const char *peer_key, + const char *remote_space_id, + amduat_octets_t *out_name); + amduatd_fed_cursor_status_t amduatd_fed_cursor_check_enabled( const amduatd_fed_cfg_t *cfg); @@ -57,6 +67,15 @@ amduatd_fed_cursor_status_t amduatd_fed_cursor_get( amduatd_fed_cursor_record_t *out_cursor, amduat_reference_t *out_ref); +amduatd_fed_cursor_status_t amduatd_fed_cursor_get_remote( + amduat_asl_store_t *store, + amduat_asl_pointer_store_t *pointer_store, + const amduatd_space_t *effective_space, + const char *peer_key, + const char *remote_space_id, + amduatd_fed_cursor_record_t *out_cursor, + amduat_reference_t *out_ref); + amduatd_fed_cursor_status_t amduatd_fed_push_cursor_get( amduat_asl_store_t *store, amduat_asl_pointer_store_t *pointer_store, @@ -65,6 +84,15 @@ amduatd_fed_cursor_status_t amduatd_fed_push_cursor_get( amduatd_fed_cursor_record_t *out_cursor, amduat_reference_t *out_ref); +amduatd_fed_cursor_status_t amduatd_fed_push_cursor_get_remote( + amduat_asl_store_t *store, + amduat_asl_pointer_store_t *pointer_store, + const amduatd_space_t *effective_space, + const char *peer_key, + const char *remote_space_id, + amduatd_fed_cursor_record_t *out_cursor, + amduat_reference_t *out_ref); + amduatd_fed_cursor_status_t amduatd_fed_cursor_cas_set( amduat_asl_store_t *store, amduat_asl_pointer_store_t *pointer_store, @@ -74,6 +102,16 @@ amduatd_fed_cursor_status_t amduatd_fed_cursor_cas_set( const amduatd_fed_cursor_record_t *new_cursor, amduat_reference_t *out_new_ref); +amduatd_fed_cursor_status_t amduatd_fed_cursor_cas_set_remote( + amduat_asl_store_t *store, + amduat_asl_pointer_store_t *pointer_store, + const amduatd_space_t *effective_space, + const char *peer_key, + const char *remote_space_id, + const amduat_reference_t *expected_ref, + const amduatd_fed_cursor_record_t *new_cursor, + amduat_reference_t *out_new_ref); + amduatd_fed_cursor_status_t amduatd_fed_push_cursor_cas_set( amduat_asl_store_t *store, amduat_asl_pointer_store_t *pointer_store, @@ -83,6 +121,16 @@ amduatd_fed_cursor_status_t amduatd_fed_push_cursor_cas_set( const amduatd_fed_cursor_record_t *new_cursor, amduat_reference_t *out_new_ref); +amduatd_fed_cursor_status_t amduatd_fed_push_cursor_cas_set_remote( + amduat_asl_store_t *store, + amduat_asl_pointer_store_t *pointer_store, + const amduatd_space_t *effective_space, + const char *peer_key, + const char *remote_space_id, + const amduat_reference_t *expected_ref, + const amduatd_fed_cursor_record_t *new_cursor, + amduat_reference_t *out_new_ref); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/amduatd_fed_pull_apply.c b/src/amduatd_fed_pull_apply.c index e373957..5642ec2 100644 --- a/src/amduatd_fed_pull_apply.c +++ b/src/amduatd_fed_pull_apply.c @@ -189,6 +189,7 @@ amduatd_fed_pull_apply_status_t amduatd_fed_pull_apply( amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, uint64_t limit, const amduatd_fed_cfg_t *fed_cfg, const amduatd_fed_pull_transport_t *transport, @@ -234,9 +235,17 @@ amduatd_fed_pull_apply_status_t amduatd_fed_pull_apply( } { amduat_octets_t scoped = amduat_octets(NULL, 0u); - if (!amduatd_fed_cursor_pointer_name(effective_space, - peer_key, - &scoped)) { + if (remote_space_id != NULL && remote_space_id[0] != '\0') { + if (!amduatd_fed_cursor_pointer_name_v2(effective_space, + peer_key, + remote_space_id, + &scoped)) { + amduatd_fed_pull_report_error(out_report, "invalid peer"); + return AMDUATD_FED_PULL_APPLY_ERR_INVALID; + } + } else if (!amduatd_fed_cursor_pointer_name(effective_space, + peer_key, + &scoped)) { amduatd_fed_pull_report_error(out_report, "invalid peer"); return AMDUATD_FED_PULL_APPLY_ERR_INVALID; } @@ -256,12 +265,13 @@ amduatd_fed_pull_apply_status_t amduatd_fed_pull_apply( memset(&cursor_ref, 0, sizeof(cursor_ref)); { amduatd_fed_cursor_status_t cursor_status; - cursor_status = amduatd_fed_cursor_get(store, - pointer_store, - effective_space, - peer_key, - &cursor, - &cursor_ref); + cursor_status = amduatd_fed_cursor_get_remote(store, + pointer_store, + effective_space, + peer_key, + remote_space_id, + &cursor, + &cursor_ref); if (cursor_status == AMDUATD_FED_CURSOR_ERR_NOT_FOUND) { cursor_present = false; } else if (cursor_status == AMDUATD_FED_CURSOR_OK) { @@ -471,14 +481,16 @@ amduatd_fed_pull_apply_status_t amduatd_fed_pull_apply( memset(&next_ref, 0, sizeof(next_ref)); { amduatd_fed_cursor_status_t cursor_status; - cursor_status = amduatd_fed_cursor_cas_set(store, - pointer_store, - effective_space, - peer_key, - cursor_present ? &cursor_ref - : NULL, - &next_cursor, - &next_ref); + cursor_status = amduatd_fed_cursor_cas_set_remote(store, + pointer_store, + effective_space, + peer_key, + remote_space_id, + cursor_present + ? &cursor_ref + : NULL, + &next_cursor, + &next_ref); if (cursor_status == AMDUATD_FED_CURSOR_ERR_CONFLICT) { transport->free_records(transport->ctx, records, record_len_total); amduatd_fed_cursor_record_free(&cursor); diff --git a/src/amduatd_fed_pull_apply.h b/src/amduatd_fed_pull_apply.h index 6972670..49c3894 100644 --- a/src/amduatd_fed_pull_apply.h +++ b/src/amduatd_fed_pull_apply.h @@ -78,6 +78,7 @@ amduatd_fed_pull_apply_status_t amduatd_fed_pull_apply( amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, uint64_t limit, const amduatd_fed_cfg_t *fed_cfg, const amduatd_fed_pull_transport_t *transport, diff --git a/src/amduatd_fed_push_apply.c b/src/amduatd_fed_push_apply.c index cd173bc..c68b146 100644 --- a/src/amduatd_fed_push_apply.c +++ b/src/amduatd_fed_push_apply.c @@ -95,6 +95,7 @@ amduatd_fed_push_apply_status_t amduatd_fed_push_apply( amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, uint64_t limit, const char *root_path, const amduatd_fed_cfg_t *fed_cfg, @@ -133,9 +134,17 @@ amduatd_fed_push_apply_status_t amduatd_fed_push_apply( } { amduat_octets_t scoped = amduat_octets(NULL, 0u); - if (!amduatd_fed_push_cursor_pointer_name(effective_space, - peer_key, - &scoped)) { + if (remote_space_id != NULL && remote_space_id[0] != '\0') { + if (!amduatd_fed_push_cursor_pointer_name_v2(effective_space, + peer_key, + remote_space_id, + &scoped)) { + amduatd_fed_push_report_error(out_report, "invalid peer"); + return AMDUATD_FED_PUSH_APPLY_ERR_INVALID; + } + } else if (!amduatd_fed_push_cursor_pointer_name(effective_space, + peer_key, + &scoped)) { amduatd_fed_push_report_error(out_report, "invalid peer"); return AMDUATD_FED_PUSH_APPLY_ERR_INVALID; } @@ -153,6 +162,7 @@ amduatd_fed_push_apply_status_t amduatd_fed_push_apply( pointer_store, effective_space, peer_key, + remote_space_id, limit, root_path, &scan) != AMDUATD_FED_PUSH_PLAN_OK) { @@ -303,15 +313,16 @@ amduatd_fed_push_apply_status_t amduatd_fed_push_apply( memset(&next_ref, 0, sizeof(next_ref)); { amduatd_fed_cursor_status_t st; - st = amduatd_fed_push_cursor_cas_set(store, - pointer_store, - effective_space, - peer_key, - scan.cursor_present - ? &scan.cursor_ref - : NULL, - &next_cursor, - &next_ref); + st = amduatd_fed_push_cursor_cas_set_remote(store, + pointer_store, + effective_space, + peer_key, + remote_space_id, + scan.cursor_present + ? &scan.cursor_ref + : NULL, + &next_cursor, + &next_ref); amduatd_fed_cursor_record_free(&next_cursor); if (st == AMDUATD_FED_CURSOR_ERR_CONFLICT) { amduatd_fed_push_plan_scan_free(&scan); diff --git a/src/amduatd_fed_push_apply.h b/src/amduatd_fed_push_apply.h index 0a25dca..7ef8650 100644 --- a/src/amduatd_fed_push_apply.h +++ b/src/amduatd_fed_push_apply.h @@ -76,6 +76,7 @@ amduatd_fed_push_apply_status_t amduatd_fed_push_apply( amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, uint64_t limit, const char *root_path, const amduatd_fed_cfg_t *fed_cfg, diff --git a/src/amduatd_fed_push_plan.c b/src/amduatd_fed_push_plan.c index 0ff5590..374c18a 100644 --- a/src/amduatd_fed_push_plan.c +++ b/src/amduatd_fed_push_plan.c @@ -237,6 +237,7 @@ amduatd_fed_push_plan_status_t amduatd_fed_push_plan_scan( amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, uint64_t limit, const char *root_path, amduatd_fed_push_plan_scan_t *out_scan) { @@ -256,12 +257,13 @@ amduatd_fed_push_plan_status_t amduatd_fed_push_plan_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); + cursor_status = amduatd_fed_push_cursor_get_remote(store, + pointer_store, + effective_space, + peer_key, + remote_space_id, + &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) { diff --git a/src/amduatd_fed_push_plan.h b/src/amduatd_fed_push_plan.h index 00b48b1..6926e76 100644 --- a/src/amduatd_fed_push_plan.h +++ b/src/amduatd_fed_push_plan.h @@ -61,6 +61,7 @@ amduatd_fed_push_plan_status_t amduatd_fed_push_plan_scan( amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, uint64_t limit, const char *root_path, amduatd_fed_push_plan_scan_t *out_scan); diff --git a/src/amduatd_fed_until.c b/src/amduatd_fed_until.c index e234d86..948bee8 100644 --- a/src/amduatd_fed_until.c +++ b/src/amduatd_fed_until.c @@ -71,6 +71,7 @@ amduatd_fed_pull_apply_status_t amduatd_fed_pull_until( amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, uint64_t limit, uint64_t max_rounds, const amduatd_fed_cfg_t *fed_cfg, @@ -100,6 +101,7 @@ amduatd_fed_pull_apply_status_t amduatd_fed_pull_until( pointer_store, effective_space, peer_key, + remote_space_id, limit, fed_cfg, transport, @@ -154,6 +156,7 @@ amduatd_fed_push_apply_status_t amduatd_fed_push_until( amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, uint64_t limit, uint64_t max_rounds, const char *root_path, @@ -185,6 +188,7 @@ amduatd_fed_push_apply_status_t amduatd_fed_push_until( pointer_store, effective_space, peer_key, + remote_space_id, limit, root_path, fed_cfg, diff --git a/src/amduatd_fed_until.h b/src/amduatd_fed_until.h index d1d76fc..bcac87b 100644 --- a/src/amduatd_fed_until.h +++ b/src/amduatd_fed_until.h @@ -38,6 +38,7 @@ amduatd_fed_pull_apply_status_t amduatd_fed_pull_until( amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, uint64_t limit, uint64_t max_rounds, const amduatd_fed_cfg_t *fed_cfg, @@ -49,6 +50,7 @@ amduatd_fed_push_apply_status_t amduatd_fed_push_until( amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *effective_space, const char *peer_key, + const char *remote_space_id, uint64_t limit, uint64_t max_rounds, const char *root_path, diff --git a/src/amduatd_space_mounts.c b/src/amduatd_space_mounts.c index 2f5db23..0415d83 100644 --- a/src/amduatd_space_mounts.c +++ b/src/amduatd_space_mounts.c @@ -99,17 +99,19 @@ static amduatd_space_mounts_status_t amduatd_space_mounts_append_tracking( amduatd_fed_cursor_status_t status; bool cursor_present = false; char *cursor_ref_hex = NULL; + const char *cursor_namespace = "none"; amduatd_fed_cursor_record_init(&cursor); cursor_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); if (mount->mode == AMDUATD_SPACE_MANIFEST_MOUNT_TRACK) { - status = amduatd_fed_cursor_get(store, - pointer_store, - effective_space, - mount->peer_key, - &cursor, - &cursor_ref); + status = amduatd_fed_cursor_get_remote(store, + pointer_store, + effective_space, + mount->peer_key, + mount->space_id, + &cursor, + &cursor_ref); if (status == AMDUATD_FED_CURSOR_ERR_NOT_FOUND) { cursor_present = false; } else if (status == AMDUATD_FED_CURSOR_OK) { @@ -123,12 +125,15 @@ static amduatd_space_mounts_status_t amduatd_space_mounts_append_tracking( amduat_reference_free(&cursor_ref); return AMDUATD_SPACE_MOUNTS_ERR_STORE; } + cursor_namespace = "v2"; } if (!amduatd_mounts_buf_append_cstr(b, ",\"local_tracking\":{") || + !amduatd_mounts_buf_append_cstr(b, "\"cursor_namespace\":\"") || + !amduatd_mounts_buf_append_cstr(b, cursor_namespace) || !amduatd_mounts_buf_append_cstr( b, - "\"cursor_scope\":\"per-peer-per-local-space\"," + "\",\"cursor_scope\":\"per-peer-per-local-space\"," "\"remote_space_id\":\"") || !amduatd_mounts_buf_append_cstr(b, mount->space_id) || !amduatd_mounts_buf_append_cstr(b, "\",\"pull_cursor\":{") || diff --git a/src/amduatd_space_roots.c b/src/amduatd_space_roots.c index 6698b01..60ba444 100644 --- a/src/amduatd_space_roots.c +++ b/src/amduatd_space_roots.c @@ -312,24 +312,40 @@ static bool amduatd_space_roots_append_cursor_heads( return ok; } -static bool amduatd_space_roots_cursor_peer_from_name( - const char *prefix, - const char *pointer_name, - char **out_peer) { +bool amduatd_space_roots_cursor_parse(const char *prefix, + const char *pointer_name, + char **out_peer, + char **out_remote_space_id) { const char suffix[] = "/head"; size_t prefix_len; size_t name_len; + size_t name_len_full; size_t suffix_len = sizeof(suffix) - 1u; size_t peer_len; + size_t remote_len = 0u; char *peer; + char *remote = NULL; + const char *segment = NULL; + const char *remote_start = NULL; + const char *name_end = NULL; + if (out_peer != NULL) { + *out_peer = NULL; + } + if (out_remote_space_id != NULL) { + *out_remote_space_id = NULL; + } if (prefix == NULL || pointer_name == NULL || out_peer == NULL) { return false; } - *out_peer = NULL; prefix_len = strlen(prefix); - name_len = strlen(pointer_name); - if (name_len <= prefix_len + 1u + suffix_len) { + name_len_full = strlen(pointer_name); + name_len = name_len_full; + if (name_len_full >= suffix_len && + strcmp(pointer_name + name_len_full - suffix_len, suffix) == 0) { + name_len = name_len_full - suffix_len; + } + if (name_len <= prefix_len + 1u) { return false; } if (strncmp(pointer_name, prefix, prefix_len) != 0) { @@ -338,10 +354,18 @@ static bool amduatd_space_roots_cursor_peer_from_name( if (pointer_name[prefix_len] != '/') { return false; } - if (strcmp(pointer_name + name_len - suffix_len, suffix) != 0) { - return false; + segment = pointer_name + prefix_len + 1u; + name_end = pointer_name + name_len; + remote_start = memchr(segment, '/', (size_t)(name_end - segment)); + if (remote_start != NULL) { + peer_len = (size_t)(remote_start - segment); + if (remote_start + 1u >= name_end) { + return false; + } + remote_len = (size_t)(name_end - (remote_start + 1u)); + } else { + peer_len = (size_t)(name_end - segment); } - peer_len = name_len - prefix_len - 1u - suffix_len; if (peer_len == 0u) { return false; } @@ -349,8 +373,18 @@ static bool amduatd_space_roots_cursor_peer_from_name( if (peer == NULL) { return false; } - memcpy(peer, pointer_name + prefix_len + 1u, peer_len); + memcpy(peer, segment, peer_len); peer[peer_len] = '\0'; + if (remote_len != 0u && out_remote_space_id != NULL) { + remote = (char *)malloc(remote_len + 1u); + if (remote == NULL) { + free(peer); + return false; + } + memcpy(remote, remote_start + 1u, remote_len); + remote[remote_len] = '\0'; + *out_remote_space_id = remote; + } *out_peer = peer; return true; } @@ -552,10 +586,10 @@ bool amduatd_space_roots_list_cursor_peers( for (size_t i = 0u; i < pull_heads.len; ++i) { char *peer = NULL; - if (amduatd_space_roots_cursor_peer_from_name( - (const char *)pull_prefix.data, - pull_heads.names[i], - &peer)) { + if (amduatd_space_roots_cursor_parse((const char *)pull_prefix.data, + pull_heads.names[i], + &peer, + NULL)) { if (!amduatd_space_roots_list_add(out_list, peer)) { free(peer); goto cleanup; @@ -565,10 +599,10 @@ bool amduatd_space_roots_list_cursor_peers( } for (size_t i = 0u; i < push_heads.len; ++i) { char *peer = NULL; - if (amduatd_space_roots_cursor_peer_from_name( - (const char *)push_prefix.data, - push_heads.names[i], - &peer)) { + if (amduatd_space_roots_cursor_parse((const char *)push_prefix.data, + push_heads.names[i], + &peer, + NULL)) { if (!amduatd_space_roots_list_add(out_list, peer)) { free(peer); goto cleanup; diff --git a/src/amduatd_space_roots.h b/src/amduatd_space_roots.h index d6c4e05..1d85a26 100644 --- a/src/amduatd_space_roots.h +++ b/src/amduatd_space_roots.h @@ -35,6 +35,11 @@ bool amduatd_space_roots_list_cursor_peers( const amduatd_space_t *effective_space, amduatd_space_roots_list_t *out_list); +bool amduatd_space_roots_cursor_parse(const char *prefix, + const char *pointer_name, + char **out_peer, + char **out_remote_space_id); + void amduatd_space_roots_list_free(amduatd_space_roots_list_t *list); #ifdef __cplusplus diff --git a/tests/test_amduatd_fed_cursor.c b/tests/test_amduatd_fed_cursor.c index 5bb2b77..1ea45c9 100644 --- a/tests/test_amduatd_fed_cursor.c +++ b/tests/test_amduatd_fed_cursor.c @@ -199,6 +199,42 @@ int main(void) { amduat_octets_free(&name); } + { + amduat_octets_t name = amduat_octets(NULL, 0u); + bool ok = amduatd_fed_cursor_pointer_name_v2(&space, + "peer-a", + "beta", + &name); + expect(ok, "v2 cursor pointer name ok"); + expect(strcmp((const char *)name.data, + "space/alpha/fed/cursor/peer-a/beta/head") == 0, + "v2 cursor pointer name matches"); + amduat_octets_free(&name); + } + + { + amduat_octets_t name = amduat_octets(NULL, 0u); + bool ok = amduatd_fed_push_cursor_pointer_name_v2(&space, + "peer-a", + "beta", + &name); + expect(ok, "v2 push cursor pointer name ok"); + expect(strcmp((const char *)name.data, + "space/alpha/fed/push_cursor/peer-a/beta/head") == 0, + "v2 push cursor pointer name matches"); + amduat_octets_free(&name); + } + + { + amduat_octets_t name = amduat_octets(NULL, 0u); + bool ok = amduatd_fed_cursor_pointer_name_v2(&space, + "peer-a", + "bad/space", + &name); + expect(!ok, "remote space validation rejects invalid id"); + amduat_octets_free(&name); + } + free(root); return failures == 0 ? 0 : 1; } diff --git a/tests/test_amduatd_fed_pull_apply.c b/tests/test_amduatd_fed_pull_apply.c index a375a60..c49675a 100644 --- a/tests/test_amduatd_fed_pull_apply.c +++ b/tests/test_amduatd_fed_pull_apply.c @@ -375,6 +375,7 @@ int main(void) { &pointer_store, &space, "1", + NULL, 2u, &fed_cfg, &transport, @@ -478,6 +479,7 @@ int main(void) { &pointer_store, &space, "1", + NULL, 2u, &fed_cfg, &transport, @@ -550,6 +552,7 @@ int main(void) { &pointer_store, &space, "1", + NULL, 1u, &fed_cfg, &transport, diff --git a/tests/test_amduatd_fed_pull_until.c b/tests/test_amduatd_fed_pull_until.c index 4292b6f..17a7d28 100644 --- a/tests/test_amduatd_fed_pull_until.c +++ b/tests/test_amduatd_fed_pull_until.c @@ -297,6 +297,7 @@ static int amduatd_test_pull_until_zero(void) { &pointer_store, &space, "1", + NULL, 16u, 3u, &fed_cfg, @@ -391,6 +392,7 @@ static int amduatd_test_pull_until_multi(void) { &pointer_store, &space, "1", + NULL, 2u, 5u, &fed_cfg, @@ -483,6 +485,7 @@ static int amduatd_test_pull_until_error(void) { &pointer_store, &space, "1", + NULL, 1u, 4u, &fed_cfg, diff --git a/tests/test_amduatd_fed_push_apply.c b/tests/test_amduatd_fed_push_apply.c index 654dad6..4c3f241 100644 --- a/tests/test_amduatd_fed_push_apply.c +++ b/tests/test_amduatd_fed_push_apply.c @@ -238,6 +238,7 @@ int main(void) { &pointer_store, &space, "2", + NULL, 16u, root, &fed_cfg, diff --git a/tests/test_amduatd_fed_push_until.c b/tests/test_amduatd_fed_push_until.c index 34ab5b7..2022c6e 100644 --- a/tests/test_amduatd_fed_push_until.c +++ b/tests/test_amduatd_fed_push_until.c @@ -210,6 +210,7 @@ static int amduatd_test_push_until_zero(void) { &pointer_store, &space, "2", + NULL, 8u, 3u, root, @@ -308,6 +309,7 @@ static int amduatd_test_push_until_multi(void) { &pointer_store, &space, "2", + NULL, 1u, 5u, root, @@ -410,6 +412,7 @@ static int amduatd_test_push_until_error(void) { &pointer_store, &space, "2", + NULL, 1u, 4u, root, diff --git a/tests/test_amduatd_space_mounts.c b/tests/test_amduatd_space_mounts.c index 2f423b2..137bf2b 100644 --- a/tests/test_amduatd_space_mounts.c +++ b/tests/test_amduatd_space_mounts.c @@ -274,13 +274,15 @@ static int amduatd_test_mounts_resolve(void) { cursor.has_record_ref = true; cursor.last_record_ref = cursor_last_ref; memset(&cursor_ref, 0, sizeof(cursor_ref)); - if (amduatd_fed_cursor_cas_set(&store, - &pointer_store, - &space, - "peer-1", - NULL, - &cursor, - &cursor_ref) != AMDUATD_FED_CURSOR_OK) { + if (amduatd_fed_cursor_cas_set_remote(&store, + &pointer_store, + &space, + "peer-1", + "beta", + NULL, + &cursor, + &cursor_ref) != + AMDUATD_FED_CURSOR_OK) { fprintf(stderr, "failed to set cursor\n"); amduatd_fed_cursor_record_free(&cursor); amduat_reference_free(&cursor_ref); @@ -322,6 +324,8 @@ static int amduatd_test_mounts_resolve(void) { "pinned mode present"); expect(strstr(mounts_json, "\"pinned_root_ref\":\"") != NULL, "pinned root ref present"); + expect(strstr(mounts_json, "\"cursor_namespace\":\"v2\"") != NULL, + "cursor namespace present"); expect(strstr(mounts_json, "\"cursor_scope\":\"per-peer-per-local-space\"") != NULL, "cursor scope present"); expect(strstr(mounts_json, "\"remote_space_id\":\"beta\"") != NULL, diff --git a/tests/test_amduatd_space_roots.c b/tests/test_amduatd_space_roots.c index f90948f..20d7dd9 100644 --- a/tests/test_amduatd_space_roots.c +++ b/tests/test_amduatd_space_roots.c @@ -184,6 +184,8 @@ static int amduatd_test_cursor_roots(void) { amduat_reference_t none_ref; amduat_octets_t cursor_name = amduat_octets(NULL, 0u); amduat_octets_t push_name = amduat_octets(NULL, 0u); + amduat_octets_t cursor_v2 = amduat_octets(NULL, 0u); + amduat_octets_t push_v2 = amduat_octets(NULL, 0u); amduat_octets_t edges_collection = amduat_octets(NULL, 0u); amduat_octets_t edges_index_head = amduat_octets(NULL, 0u); char *collection_head = NULL; @@ -237,7 +239,15 @@ static int amduatd_test_cursor_roots(void) { amduat_artifact_free(&none_artifact); if (!amduatd_fed_cursor_pointer_name(&space, "peer-a", &cursor_name) || - !amduatd_fed_push_cursor_pointer_name(&space, "peer-b", &push_name)) { + !amduatd_fed_push_cursor_pointer_name(&space, "peer-b", &push_name) || + !amduatd_fed_cursor_pointer_name_v2(&space, + "peer-a", + "beta", + &cursor_v2) || + !amduatd_fed_push_cursor_pointer_name_v2(&space, + "peer-b", + "beta", + &push_v2)) { fprintf(stderr, "failed to build cursor names\n"); free(root); return 1; @@ -254,10 +264,24 @@ static int amduatd_test_cursor_roots(void) { false, NULL, &none_ref, + &swapped) != AMDUAT_ASL_POINTER_OK || !swapped || + amduat_asl_pointer_cas(&pointer_store, + (const char *)cursor_v2.data, + false, + NULL, + &none_ref, + &swapped) != AMDUAT_ASL_POINTER_OK || !swapped || + amduat_asl_pointer_cas(&pointer_store, + (const char *)push_v2.data, + false, + NULL, + &none_ref, &swapped) != AMDUAT_ASL_POINTER_OK || !swapped) { fprintf(stderr, "failed to seed cursor pointers\n"); amduat_octets_free(&cursor_name); amduat_octets_free(&push_name); + amduat_octets_free(&cursor_v2); + amduat_octets_free(&push_v2); free(root); return 1; } @@ -266,6 +290,8 @@ static int amduatd_test_cursor_roots(void) { fprintf(stderr, "roots list failed\n"); amduat_octets_free(&cursor_name); amduat_octets_free(&push_name); + amduat_octets_free(&cursor_v2); + amduat_octets_free(&push_v2); free(root); return 1; } @@ -284,9 +310,11 @@ static int amduatd_test_cursor_roots(void) { return 1; } - if (list.len != 5u || + if (list.len != 7u || !amduatd_list_contains(&list, (const char *)cursor_name.data) || !amduatd_list_contains(&list, (const char *)push_name.data) || + !amduatd_list_contains(&list, (const char *)cursor_v2.data) || + !amduatd_list_contains(&list, (const char *)push_v2.data) || !amduatd_list_contains(&list, (const char *)edges_index_head.data) || !amduatd_list_contains(&list, collection_head) || !amduatd_list_contains(&list, collection_log_head) || @@ -294,6 +322,8 @@ static int amduatd_test_cursor_roots(void) { fprintf(stderr, "unexpected cursor roots list\n"); amduat_octets_free(&cursor_name); amduat_octets_free(&push_name); + amduat_octets_free(&cursor_v2); + amduat_octets_free(&push_v2); amduat_octets_free(&edges_collection); amduat_octets_free(&edges_index_head); free(collection_head); @@ -305,6 +335,8 @@ static int amduatd_test_cursor_roots(void) { amduat_octets_free(&cursor_name); amduat_octets_free(&push_name); + amduat_octets_free(&cursor_v2); + amduat_octets_free(&push_v2); amduat_octets_free(&edges_collection); amduat_octets_free(&edges_index_head); free(collection_head); diff --git a/tests/test_amduatd_space_sync_status.c b/tests/test_amduatd_space_sync_status.c index d0c50c6..e283019 100644 --- a/tests/test_amduatd_space_sync_status.c +++ b/tests/test_amduatd_space_sync_status.c @@ -203,6 +203,9 @@ static int amduatd_test_peer_discovery(void) { !amduatd_list_contains(&peers, "2") || !amduatd_list_sorted(&peers)) { fprintf(stderr, "unexpected peers list\n"); + for (size_t i = 0u; i < peers.len; ++i) { + fprintf(stderr, " peer[%zu]=%s\n", i, peers.names[i]); + } amduatd_space_roots_list_free(&peers); amduat_octets_free(&pull_name); amduat_octets_free(&push_name);