2026-01-18 11:17:35 +01:00
|
|
|
#include "amduat/fed/view.h"
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
amduat_reference_t ref;
|
|
|
|
|
amduat_artifact_t artifact;
|
|
|
|
|
} stub_store_entry_t;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
amduat_asl_store_config_t config;
|
|
|
|
|
stub_store_entry_t entries[4];
|
|
|
|
|
size_t entries_len;
|
|
|
|
|
} stub_store_t;
|
|
|
|
|
|
|
|
|
|
static void stub_store_init(stub_store_t *store) {
|
|
|
|
|
memset(store, 0, sizeof(*store));
|
|
|
|
|
store->config.hash_id = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static amduat_asl_store_error_t stub_store_get(void *ctx,
|
|
|
|
|
amduat_reference_t ref,
|
|
|
|
|
amduat_artifact_t *out) {
|
|
|
|
|
stub_store_t *store = (stub_store_t *)ctx;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < store->entries_len; ++i) {
|
|
|
|
|
if (amduat_reference_eq(store->entries[i].ref, ref)) {
|
|
|
|
|
amduat_artifact_t src = store->entries[i].artifact;
|
|
|
|
|
uint8_t *payload = NULL;
|
|
|
|
|
if (src.bytes.len != 0u) {
|
|
|
|
|
payload = (uint8_t *)malloc(src.bytes.len);
|
|
|
|
|
if (payload == NULL) {
|
|
|
|
|
return AMDUAT_ASL_STORE_ERR_IO;
|
|
|
|
|
}
|
|
|
|
|
memcpy(payload, src.bytes.data, src.bytes.len);
|
|
|
|
|
}
|
|
|
|
|
out->bytes = amduat_octets(payload, src.bytes.len);
|
|
|
|
|
out->has_type_tag = src.has_type_tag;
|
|
|
|
|
out->type_tag = src.type_tag;
|
|
|
|
|
return AMDUAT_ASL_STORE_OK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return AMDUAT_ASL_STORE_ERR_NOT_FOUND;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static amduat_reference_t make_ref(amduat_hash_id_t hash_id,
|
|
|
|
|
const uint8_t *bytes,
|
|
|
|
|
size_t len) {
|
|
|
|
|
return amduat_reference(hash_id, amduat_octets(bytes, len));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static amduat_fed_record_t make_record(uint32_t domain_id,
|
|
|
|
|
uint8_t visibility,
|
|
|
|
|
uint64_t logseq,
|
|
|
|
|
amduat_reference_t ref) {
|
|
|
|
|
amduat_fed_record_t record;
|
|
|
|
|
|
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
|
|
|
record.meta.domain_id = domain_id;
|
|
|
|
|
record.meta.visibility = visibility;
|
|
|
|
|
record.meta.has_source = 0;
|
|
|
|
|
record.id.type = AMDUAT_FED_REC_ARTIFACT;
|
|
|
|
|
record.id.ref = ref;
|
|
|
|
|
record.logseq = logseq;
|
|
|
|
|
record.snapshot_id = 1;
|
|
|
|
|
record.log_prefix = 10;
|
|
|
|
|
return record;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int test_view_and_resolve(void) {
|
|
|
|
|
uint8_t a_bytes[] = {0x01};
|
|
|
|
|
uint8_t b_bytes[] = {0x02};
|
|
|
|
|
uint8_t c_bytes[] = {0x03};
|
|
|
|
|
uint8_t d_bytes[] = {0x04};
|
|
|
|
|
amduat_reference_t ref_a = make_ref(1, a_bytes, sizeof(a_bytes));
|
|
|
|
|
amduat_reference_t ref_b = make_ref(1, b_bytes, sizeof(b_bytes));
|
|
|
|
|
amduat_reference_t ref_c = make_ref(1, c_bytes, sizeof(c_bytes));
|
|
|
|
|
amduat_reference_t ref_d = make_ref(1, d_bytes, sizeof(d_bytes));
|
|
|
|
|
amduat_fed_record_t records[4];
|
|
|
|
|
amduat_fed_view_bounds_t bounds[2];
|
|
|
|
|
amduat_fed_view_t view;
|
|
|
|
|
amduat_fed_policy_deny_t denies[1];
|
|
|
|
|
stub_store_t stub;
|
|
|
|
|
amduat_asl_store_ops_t ops;
|
|
|
|
|
amduat_asl_store_t store;
|
|
|
|
|
amduat_artifact_t artifact;
|
|
|
|
|
|
|
|
|
|
records[0] = make_record(1, 0, 1, ref_a);
|
|
|
|
|
records[1] = make_record(1, 1, 2, ref_b);
|
|
|
|
|
records[2] = make_record(2, 1, 1, ref_c);
|
|
|
|
|
records[3] = make_record(2, 0, 1, ref_d);
|
|
|
|
|
|
|
|
|
|
bounds[0].domain_id = 1;
|
|
|
|
|
bounds[0].snapshot_id = 1;
|
|
|
|
|
bounds[0].log_prefix = 10;
|
|
|
|
|
bounds[1].domain_id = 2;
|
|
|
|
|
bounds[1].snapshot_id = 1;
|
|
|
|
|
bounds[1].log_prefix = 10;
|
|
|
|
|
|
|
|
|
|
denies[0].id.type = AMDUAT_FED_REC_ARTIFACT;
|
|
|
|
|
denies[0].id.ref = ref_d;
|
|
|
|
|
denies[0].reason_code = 0;
|
|
|
|
|
|
|
|
|
|
if (!amduat_fed_view_build(records, 4, 1, bounds, 2, denies, 1, &view)) {
|
|
|
|
|
fprintf(stderr, "view build failed\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (view.len != 3) {
|
|
|
|
|
fprintf(stderr, "view size mismatch\n");
|
|
|
|
|
amduat_fed_view_free(&view);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stub_store_init(&stub);
|
|
|
|
|
stub.entries[0].ref = ref_a;
|
|
|
|
|
stub.entries[0].artifact = amduat_artifact(amduat_octets(a_bytes,
|
|
|
|
|
sizeof(a_bytes)));
|
|
|
|
|
stub.entries_len = 1;
|
|
|
|
|
amduat_asl_store_ops_init(&ops);
|
|
|
|
|
ops.get = stub_store_get;
|
|
|
|
|
amduat_asl_store_init(&store, stub.config, ops, &stub);
|
|
|
|
|
|
2026-01-18 11:43:03 +01:00
|
|
|
if (amduat_fed_resolve(&view, &store, ref_a, &artifact) !=
|
2026-01-18 11:17:35 +01:00
|
|
|
AMDUAT_FED_RESOLVE_OK) {
|
|
|
|
|
fprintf(stderr, "resolve local failed\n");
|
|
|
|
|
amduat_fed_view_free(&view);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
amduat_artifact_free(&artifact);
|
|
|
|
|
|
2026-01-18 11:43:03 +01:00
|
|
|
if (amduat_fed_resolve(&view, &store, ref_c, &artifact) !=
|
2026-01-18 11:17:35 +01:00
|
|
|
AMDUAT_FED_RESOLVE_FOUND_REMOTE_NO_BYTES) {
|
|
|
|
|
fprintf(stderr, "resolve remote mismatch\n");
|
|
|
|
|
amduat_fed_view_free(&view);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-18 11:43:03 +01:00
|
|
|
if (amduat_fed_resolve(&view, &store, ref_d, &artifact) !=
|
2026-01-18 11:17:35 +01:00
|
|
|
AMDUAT_FED_RESOLVE_POLICY_DENIED) {
|
|
|
|
|
fprintf(stderr, "resolve policy denied mismatch\n");
|
|
|
|
|
amduat_fed_view_free(&view);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
amduat_fed_view_free(&view);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int test_view_conflict(void) {
|
|
|
|
|
uint8_t key[] = {0x09};
|
|
|
|
|
amduat_reference_t ref = make_ref(1, key, sizeof(key));
|
|
|
|
|
amduat_fed_record_t records[2];
|
|
|
|
|
amduat_fed_view_bounds_t bounds[2];
|
|
|
|
|
amduat_fed_view_t view;
|
|
|
|
|
|
|
|
|
|
records[0] = make_record(1, 1, 1, ref);
|
|
|
|
|
records[1] = make_record(2, 1, 1, ref);
|
|
|
|
|
|
|
|
|
|
bounds[0].domain_id = 1;
|
|
|
|
|
bounds[0].snapshot_id = 1;
|
|
|
|
|
bounds[0].log_prefix = 10;
|
|
|
|
|
bounds[1].domain_id = 2;
|
|
|
|
|
bounds[1].snapshot_id = 1;
|
|
|
|
|
bounds[1].log_prefix = 10;
|
|
|
|
|
|
|
|
|
|
if (amduat_fed_view_build(records, 2, 1, bounds, 2, NULL, 0, &view)) {
|
|
|
|
|
fprintf(stderr, "expected conflict\n");
|
|
|
|
|
amduat_fed_view_free(&view);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-18 11:43:03 +01:00
|
|
|
static int test_view_rebuild_metadata(void) {
|
|
|
|
|
uint8_t key[] = {0x08};
|
|
|
|
|
amduat_reference_t ref = make_ref(1, key, sizeof(key));
|
|
|
|
|
amduat_fed_record_t records[1];
|
|
|
|
|
amduat_fed_view_bounds_t bounds[1];
|
|
|
|
|
amduat_fed_view_t view_a;
|
|
|
|
|
amduat_fed_view_t view_b;
|
|
|
|
|
|
|
|
|
|
records[0] = make_record(1, 1, 1, ref);
|
|
|
|
|
records[0].meta.has_source = 1;
|
|
|
|
|
records[0].meta.source_domain = 5;
|
|
|
|
|
|
|
|
|
|
bounds[0].domain_id = 1;
|
|
|
|
|
bounds[0].snapshot_id = 1;
|
|
|
|
|
bounds[0].log_prefix = 10;
|
|
|
|
|
|
|
|
|
|
if (!amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_a)) {
|
|
|
|
|
fprintf(stderr, "view rebuild build A failed\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_b)) {
|
|
|
|
|
fprintf(stderr, "view rebuild build B failed\n");
|
|
|
|
|
amduat_fed_view_free(&view_a);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (view_a.len != 1 || view_b.len != 1) {
|
|
|
|
|
fprintf(stderr, "view rebuild length mismatch\n");
|
|
|
|
|
amduat_fed_view_free(&view_a);
|
|
|
|
|
amduat_fed_view_free(&view_b);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (view_a.records[0].meta.has_source !=
|
|
|
|
|
view_b.records[0].meta.has_source ||
|
|
|
|
|
view_a.records[0].meta.source_domain !=
|
|
|
|
|
view_b.records[0].meta.source_domain ||
|
|
|
|
|
view_a.records[0].meta.visibility != view_b.records[0].meta.visibility) {
|
|
|
|
|
fprintf(stderr, "view rebuild metadata mismatch\n");
|
|
|
|
|
amduat_fed_view_free(&view_a);
|
|
|
|
|
amduat_fed_view_free(&view_b);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
amduat_fed_view_free(&view_a);
|
|
|
|
|
amduat_fed_view_free(&view_b);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-18 11:17:35 +01:00
|
|
|
int main(void) {
|
|
|
|
|
if (test_view_and_resolve() != 0) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
if (test_view_conflict() != 0) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2026-01-18 11:43:03 +01:00
|
|
|
if (test_view_rebuild_metadata() != 0) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2026-01-18 11:17:35 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|