diff --git a/AUDITS.md b/AUDITS.md index 12c26a5..1f40834 100644 --- a/AUDITS.md +++ b/AUDITS.md @@ -73,6 +73,8 @@ Status legend: ✅ implemented, 🟡 planned/in-progress, ⬜ not started. | `ASL/INDEXES/1` | 🟡 | Taxonomy planned. | | `ASL/TGK-EXEC-PLAN/1` | 🟡 | Encoding implemented; executor out of scope. | | `ENC/ASL-TGK-EXEC-PLAN/1` | ✅ | Plan encoding implemented. | +| `ASL/FEDERATION/1` | ✅ | Core federation primitives implemented. | +| `ASL/FEDERATION-REPLAY/1` | ✅ | Deterministic replay and view construction implemented. | | `ASL/SYSTEM/1` | 🟡 | Cross-cutting view planned. | | `TGK/1` | 🟡 | Semantic layer planned. | @@ -348,3 +350,15 @@ Status legend: ✅ completed, ⬜ pending. embedded commit-message appendix; tightened wording throughout; bumped the document version/date. - Tests: N/A (documentation-only change). + +## 2026-02-XX — Federation core (`tier1/asl-federation-1.md`, `tier1/asl-federation-replay-1.md`) +- Scope: core federation registry, ingest validation, deterministic replay, view + construction, and resolve semantics. +- Findings: missing record typing and identity coverage for PER/TGK/tombstones; + policy gating needed explicit per-domain + optional per-record handling; view + build and resolve error reporting needed explicit codes and tests. +- Resolution: added federation registry storage, ingest validation, replay/view + build, resolve APIs, and tests for ordering, tombstone scoping, conflicts, + bounds, and metadata preservation; documented middle-layer boundary and + ref-only remote fetch guidance. +- Tests: user reported “100% tests passed, 0 tests failed out of 29”. diff --git a/include/amduat/fed/registry.h b/include/amduat/fed/registry.h index b102ec6..50fa63b 100644 --- a/include/amduat/fed/registry.h +++ b/include/amduat/fed/registry.h @@ -21,7 +21,7 @@ typedef struct { uint8_t policy_ok; uint8_t reserved[6]; amduat_hash_id_t policy_hash_id; - amduat_octets_t policy_hash; /* Empty when unknown. */ + amduat_octets_t policy_hash; /* Empty when unknown; caller-owned bytes. */ } amduat_fed_domain_state_t; typedef struct { @@ -44,9 +44,11 @@ const amduat_fed_domain_state_t *amduat_fed_registry_value_lookup( void amduat_fed_registry_value_free(amduat_fed_registry_value_t *value); +/* Encode/Decode allocate buffers; caller frees via amduat_octets_free. */ bool amduat_fed_registry_encode(const amduat_fed_registry_value_t *value, amduat_octets_t *out_bytes); +/* Decode allocates policy_hash bytes; free via amduat_fed_registry_value_free. */ bool amduat_fed_registry_decode(amduat_octets_t bytes, amduat_fed_registry_value_t *out_value); diff --git a/include/amduat/fed/replay.h b/include/amduat/fed/replay.h index 19fd6c9..18414e6 100644 --- a/include/amduat/fed/replay.h +++ b/include/amduat/fed/replay.h @@ -28,7 +28,7 @@ typedef struct { typedef struct { amduat_fed_record_type_t type; - amduat_reference_t ref; + amduat_reference_t ref; /* PER/TGK identities are ASL references. */ } amduat_fed_record_id_t; typedef struct { @@ -44,6 +44,7 @@ typedef struct { size_t len; } amduat_fed_replay_view_t; +/* Caller frees record ids with amduat_fed_replay_view_free. */ bool amduat_fed_record_validate(const amduat_fed_record_t *record); bool amduat_fed_replay_domain(const amduat_fed_record_t *records, diff --git a/include/amduat/fed/view.h b/include/amduat/fed/view.h index 7cb8a17..857f097 100644 --- a/include/amduat/fed/view.h +++ b/include/amduat/fed/view.h @@ -39,14 +39,23 @@ typedef struct { size_t denies_len; } amduat_fed_view_t; -bool amduat_fed_view_build(const amduat_fed_record_t *records, - size_t count, - uint32_t local_domain_id, - const amduat_fed_view_bounds_t *bounds, - size_t bounds_len, - const amduat_fed_policy_deny_t *denies, - size_t denies_len, - amduat_fed_view_t *out_view); +typedef enum { + AMDUAT_FED_VIEW_OK = 0, + AMDUAT_FED_VIEW_ERR_INVALID = 1, + AMDUAT_FED_VIEW_ERR_CONFLICT = 2, + AMDUAT_FED_VIEW_ERR_OOM = 3 +} amduat_fed_view_error_t; + +/* Caller frees records/denies with amduat_fed_view_free. */ +amduat_fed_view_error_t amduat_fed_view_build( + const amduat_fed_record_t *records, + size_t count, + uint32_t local_domain_id, + const amduat_fed_view_bounds_t *bounds, + size_t bounds_len, + const amduat_fed_policy_deny_t *denies, + size_t denies_len, + amduat_fed_view_t *out_view); void amduat_fed_view_free(amduat_fed_view_t *view); diff --git a/src/near_core/fed/view.c b/src/near_core/fed/view.c index c7ec511..0098bff 100644 --- a/src/near_core/fed/view.c +++ b/src/near_core/fed/view.c @@ -75,22 +75,23 @@ static void amduat_fed_policy_deny_free(amduat_fed_policy_deny_t *deny) { memset(deny, 0, sizeof(*deny)); } -static bool amduat_fed_view_append(amduat_fed_view_t *view, - const amduat_fed_record_t *records, - size_t count) { +static amduat_fed_view_error_t amduat_fed_view_append( + amduat_fed_view_t *view, + const amduat_fed_record_t *records, + size_t count) { size_t i; size_t base_len; if (count == 0u) { - return true; + return AMDUAT_FED_VIEW_OK; } if (view->len > SIZE_MAX - count) { - return false; + return AMDUAT_FED_VIEW_ERR_OOM; } if (view->records == NULL) { view->records = (amduat_fed_record_t *)calloc(count, sizeof(*view->records)); if (view->records == NULL) { - return false; + return AMDUAT_FED_VIEW_ERR_OOM; } } else { amduat_fed_record_t *next = @@ -98,7 +99,7 @@ static bool amduat_fed_view_append(amduat_fed_view_t *view, (view->len + count) * sizeof(*view->records)); if (next == NULL) { - return false; + return AMDUAT_FED_VIEW_ERR_OOM; } view->records = next; } @@ -115,7 +116,7 @@ static bool amduat_fed_view_append(amduat_fed_view_t *view, amduat_fed_record_free(&view->records[j]); } view->len = base_len; - return false; + return AMDUAT_FED_VIEW_ERR_CONFLICT; } continue; } @@ -127,26 +128,27 @@ static bool amduat_fed_view_append(amduat_fed_view_t *view, amduat_fed_record_free(&view->records[j]); } view->len = base_len; - return false; + return AMDUAT_FED_VIEW_ERR_OOM; } view->len++; } - return true; + return AMDUAT_FED_VIEW_OK; } -bool amduat_fed_view_build(const amduat_fed_record_t *records, - size_t count, - uint32_t local_domain_id, - const amduat_fed_view_bounds_t *bounds, - size_t bounds_len, - const amduat_fed_policy_deny_t *denies, - size_t denies_len, - amduat_fed_view_t *out_view) { +amduat_fed_view_error_t amduat_fed_view_build( + const amduat_fed_record_t *records, + size_t count, + uint32_t local_domain_id, + const amduat_fed_view_bounds_t *bounds, + size_t bounds_len, + const amduat_fed_policy_deny_t *denies, + size_t denies_len, + amduat_fed_view_t *out_view) { size_t i; amduat_fed_view_t view; if (out_view == NULL) { - return false; + return AMDUAT_FED_VIEW_ERR_INVALID; } out_view->records = NULL; out_view->len = 0; @@ -155,7 +157,7 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records, out_view->denies_len = 0; if (records == NULL && count != 0u) { - return false; + return AMDUAT_FED_VIEW_ERR_INVALID; } view.records = NULL; @@ -165,13 +167,13 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records, view.denies_len = 0; if (denies_len != 0u && denies == NULL) { - return false; + return AMDUAT_FED_VIEW_ERR_INVALID; } if (denies_len != 0u) { view.denies = (amduat_fed_policy_deny_t *)calloc(denies_len, sizeof(*view.denies)); if (view.denies == NULL) { - return false; + return AMDUAT_FED_VIEW_ERR_OOM; } for (i = 0; i < denies_len; ++i) { if (!amduat_fed_policy_deny_clone(&denies[i], &view.denies[i])) { @@ -180,7 +182,7 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records, amduat_fed_policy_deny_free(&view.denies[j]); } free(view.denies); - return false; + return AMDUAT_FED_VIEW_ERR_OOM; } } view.denies_len = denies_len; @@ -214,7 +216,7 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records, (amduat_fed_record_t *)calloc(domain_count, sizeof(*domain_records)); if (domain_records == NULL) { amduat_fed_view_free(&view); - return false; + return AMDUAT_FED_VIEW_ERR_OOM; } domain_count = 0; @@ -234,7 +236,7 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records, } free(domain_records); amduat_fed_view_free(&view); - return false; + return AMDUAT_FED_VIEW_ERR_OOM; } domain_count++; } @@ -251,20 +253,25 @@ bool amduat_fed_view_build(const amduat_fed_record_t *records, free(domain_records); if (!ok) { amduat_fed_view_free(&view); - return false; + return AMDUAT_FED_VIEW_ERR_INVALID; } - if (!amduat_fed_view_append(&view, - domain_view.records, - domain_view.len)) { - amduat_fed_replay_view_free(&domain_view); - amduat_fed_view_free(&view); - return false; + { + amduat_fed_view_error_t append_err; + + append_err = amduat_fed_view_append(&view, + domain_view.records, + domain_view.len); + if (append_err != AMDUAT_FED_VIEW_OK) { + amduat_fed_replay_view_free(&domain_view); + amduat_fed_view_free(&view); + return append_err; + } } amduat_fed_replay_view_free(&domain_view); } *out_view = view; - return true; + return AMDUAT_FED_VIEW_OK; } void amduat_fed_view_free(amduat_fed_view_t *view) { diff --git a/tests/fed/test_fed_view.c b/tests/fed/test_fed_view.c index 5a1e0eb..e58e9b1 100644 --- a/tests/fed/test_fed_view.c +++ b/tests/fed/test_fed_view.c @@ -105,7 +105,8 @@ static int test_view_and_resolve(void) { denies[0].id.ref = ref_d; denies[0].reason_code = 0; - if (!amduat_fed_view_build(records, 4, 1, bounds, 2, denies, 1, &view)) { + if (amduat_fed_view_build(records, 4, 1, bounds, 2, denies, 1, &view) != + AMDUAT_FED_VIEW_OK) { fprintf(stderr, "view build failed\n"); return 1; } @@ -167,7 +168,8 @@ static int test_view_conflict(void) { bounds[1].snapshot_id = 1; bounds[1].log_prefix = 10; - if (amduat_fed_view_build(records, 2, 1, bounds, 2, NULL, 0, &view)) { + if (amduat_fed_view_build(records, 2, 1, bounds, 2, NULL, 0, &view) != + AMDUAT_FED_VIEW_ERR_CONFLICT) { fprintf(stderr, "expected conflict\n"); amduat_fed_view_free(&view); return 1; @@ -191,11 +193,13 @@ static int test_view_rebuild_metadata(void) { bounds[0].snapshot_id = 1; bounds[0].log_prefix = 10; - if (!amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_a)) { + if (amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_a) != + AMDUAT_FED_VIEW_OK) { fprintf(stderr, "view rebuild build A failed\n"); return 1; } - if (!amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_b)) { + if (amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_b) != + AMDUAT_FED_VIEW_OK) { fprintf(stderr, "view rebuild build B failed\n"); amduat_fed_view_free(&view_a); return 1;