diff --git a/CMakeLists.txt b/CMakeLists.txt index 559ca17..30b71ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -251,6 +251,16 @@ target_link_libraries(amduat_test_tgk_store_mem ) add_test(NAME tgk_store_mem COMMAND amduat_test_tgk_store_mem) +add_executable(amduat_test_tgk_prov tests/tgk/test_tgk_prov.c) +target_include_directories(amduat_test_tgk_prov + PRIVATE ${AMDUAT_INTERNAL_DIR} + PRIVATE ${AMDUAT_INCLUDE_DIR} +) +target_link_libraries(amduat_test_tgk_prov + PRIVATE amduat_tgk_store_mem +) +add_test(NAME tgk_prov COMMAND amduat_test_tgk_prov) + add_executable(amduat_test_pel_program_dag_exec tests/pel/test_pel_program_dag_exec.c) target_include_directories(amduat_test_pel_program_dag_exec @@ -277,6 +287,9 @@ target_include_directories(amduat_test_pel_queue PRIVATE ${AMDUAT_INTERNAL_DIR} PRIVATE ${AMDUAT_INCLUDE_DIR} ) +target_compile_definitions(amduat_test_pel_queue + PRIVATE _POSIX_C_SOURCE=200809L +) target_link_libraries(amduat_test_pel_queue PRIVATE amduat_pel ) diff --git a/include/amduat/tgk/prov.h b/include/amduat/tgk/prov.h index e69de29..67efedc 100644 --- a/include/amduat/tgk/prov.h +++ b/include/amduat/tgk/prov.h @@ -0,0 +1,89 @@ +#ifndef AMDUAT_TGK_PROV_H +#define AMDUAT_TGK_PROV_H + +#include "amduat/tgk/store.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + AMDUAT_TGK_PROV_DIR_BACKWARD = 1, + AMDUAT_TGK_PROV_DIR_FORWARD = 2, + AMDUAT_TGK_PROV_DIR_BOTH = 3 +} amduat_tgk_prov_direction_t; + +typedef struct { + amduat_tgk_prov_direction_t direction; + amduat_tgk_edge_type_filter_t type_filter; + uint32_t depth_limit; + bool has_depth_limit; +} amduat_tgk_prov_query_t; + +typedef struct { + amduat_reference_t node; + uint32_t depth; +} amduat_tgk_prov_depth_entry_t; + +typedef struct { + amduat_tgk_prov_depth_entry_t *entries; + size_t len; +} amduat_tgk_prov_depth_map_t; + +typedef struct { + uint32_t depth; + amduat_reference_t *nodes; + size_t nodes_len; +} amduat_tgk_prov_layer_t; + +typedef struct { + amduat_tgk_prov_layer_t *layers; + size_t layers_len; +} amduat_tgk_prov_layering_t; + +typedef struct { + amduat_reference_t *seeds; + size_t seeds_len; + amduat_reference_t *nodes; + size_t nodes_len; + amduat_tgk_graph_edge_view_t *edges; + size_t edges_len; +} amduat_tgk_trace_graph_t; + +bool amduat_tgk_prov_closure_nodes(amduat_tgk_store_t *store, + const amduat_reference_t *seeds, + size_t seeds_len, + amduat_tgk_prov_query_t query, + amduat_tgk_node_list_t *out_nodes); + +bool amduat_tgk_prov_depths(amduat_tgk_store_t *store, + const amduat_reference_t *seeds, + size_t seeds_len, + amduat_tgk_prov_query_t query, + amduat_tgk_prov_depth_map_t *out_depths); + +bool amduat_tgk_prov_layers(amduat_tgk_store_t *store, + const amduat_reference_t *seeds, + size_t seeds_len, + amduat_tgk_prov_query_t query, + amduat_tgk_prov_layering_t *out_layers); + +bool amduat_tgk_prov_trace(amduat_tgk_store_t *store, + const amduat_reference_t *seeds, + size_t seeds_len, + amduat_tgk_prov_query_t query, + amduat_tgk_trace_graph_t *out_trace); + +void amduat_tgk_prov_depth_map_free(amduat_tgk_prov_depth_map_t *map); +void amduat_tgk_prov_layering_free(amduat_tgk_prov_layering_t *layers); +void amduat_tgk_trace_graph_free(amduat_tgk_trace_graph_t *trace); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AMDUAT_TGK_PROV_H */ diff --git a/src/tgk_stack/prov/prov.c b/src/tgk_stack/prov/prov.c index e69de29..bfbc137 100644 --- a/src/tgk_stack/prov/prov.c +++ b/src/tgk_stack/prov/prov.c @@ -0,0 +1,843 @@ +#include "amduat/tgk/prov.h" + +#include +#include + +typedef struct { + amduat_tgk_prov_depth_entry_t *entries; + size_t len; + size_t cap; +} amduat_tgk_prov_depth_builder_t; + +typedef struct { + size_t *items; + size_t len; + size_t cap; + size_t head; +} amduat_tgk_prov_index_queue_t; + +static void amduat_tgk_prov_reference_free(amduat_reference_t *ref) { + if (ref == NULL) { + return; + } + free((void *)ref->digest.data); + ref->digest.data = NULL; + ref->digest.len = 0; +} + +static bool amduat_tgk_prov_reference_clone(amduat_reference_t src, + amduat_reference_t *dst) { + uint8_t *digest_copy; + + if (dst == NULL) { + return false; + } + *dst = amduat_reference(src.hash_id, amduat_octets(NULL, 0)); + if (src.digest.len == 0) { + return true; + } + if (src.digest.data == NULL) { + return false; + } + digest_copy = (uint8_t *)malloc(src.digest.len); + if (digest_copy == NULL) { + return false; + } + memcpy(digest_copy, src.digest.data, src.digest.len); + dst->digest = amduat_octets(digest_copy, src.digest.len); + return true; +} + +static int amduat_tgk_prov_octets_cmp(amduat_octets_t a, amduat_octets_t b) { + size_t min_len; + int cmp; + + min_len = a.len < b.len ? a.len : b.len; + if (min_len != 0 && a.data != NULL && b.data != NULL) { + cmp = memcmp(a.data, b.data, min_len); + if (cmp != 0) { + return cmp; + } + } else if (min_len != 0) { + return (a.data == NULL) ? -1 : 1; + } + + if (a.len < b.len) { + return -1; + } + if (a.len > b.len) { + return 1; + } + return 0; +} + +static int amduat_tgk_prov_ref_cmp(amduat_reference_t a, + amduat_reference_t b) { + if (a.hash_id < b.hash_id) { + return -1; + } + if (a.hash_id > b.hash_id) { + return 1; + } + return amduat_tgk_prov_octets_cmp(a.digest, b.digest); +} + +static int amduat_tgk_prov_ref_ptr_cmp(const void *a, const void *b) { + const amduat_reference_t *ref_a = (const amduat_reference_t *)a; + const amduat_reference_t *ref_b = (const amduat_reference_t *)b; + + return amduat_tgk_prov_ref_cmp(*ref_a, *ref_b); +} + +static int amduat_tgk_prov_depth_entry_cmp(const void *a, const void *b) { + const amduat_tgk_prov_depth_entry_t *entry_a = + (const amduat_tgk_prov_depth_entry_t *)a; + const amduat_tgk_prov_depth_entry_t *entry_b = + (const amduat_tgk_prov_depth_entry_t *)b; + + if (entry_a->depth < entry_b->depth) { + return -1; + } + if (entry_a->depth > entry_b->depth) { + return 1; + } + return amduat_tgk_prov_ref_cmp(entry_a->node, entry_b->node); +} + +static int amduat_tgk_prov_edge_cmp(const void *a, const void *b) { + const amduat_tgk_graph_edge_view_t *edge_a = + (const amduat_tgk_graph_edge_view_t *)a; + const amduat_tgk_graph_edge_view_t *edge_b = + (const amduat_tgk_graph_edge_view_t *)b; + + return amduat_tgk_prov_ref_cmp(edge_a->edge_ref, edge_b->edge_ref); +} + +static bool amduat_tgk_prov_direction_to_graph( + amduat_tgk_prov_direction_t dir, + amduat_tgk_graph_direction_t *out_dir) { + if (out_dir == NULL) { + return false; + } + switch (dir) { + case AMDUAT_TGK_PROV_DIR_BACKWARD: + *out_dir = AMDUAT_TGK_GRAPH_DIR_IN; + return true; + case AMDUAT_TGK_PROV_DIR_FORWARD: + *out_dir = AMDUAT_TGK_GRAPH_DIR_OUT; + return true; + case AMDUAT_TGK_PROV_DIR_BOTH: + *out_dir = AMDUAT_TGK_GRAPH_DIR_BOTH; + return true; + default: + return false; + } +} + +static bool amduat_tgk_prov_depth_builder_insert( + amduat_tgk_prov_depth_builder_t *builder, + amduat_reference_t node, + uint32_t depth, + size_t *out_index, + bool *out_added) { + size_t i; + size_t new_cap; + amduat_tgk_prov_depth_entry_t *next; + + if (builder == NULL) { + return false; + } + + for (i = 0; i < builder->len; ++i) { + if (amduat_reference_eq(builder->entries[i].node, node)) { + if (out_index != NULL) { + *out_index = i; + } + if (out_added != NULL) { + *out_added = false; + } + return true; + } + } + + if (builder->len == builder->cap) { + new_cap = (builder->cap == 0) ? 8u : builder->cap * 2u; + next = (amduat_tgk_prov_depth_entry_t *)realloc( + builder->entries, new_cap * sizeof(*builder->entries)); + if (next == NULL) { + return false; + } + builder->entries = next; + builder->cap = new_cap; + } + + if (!amduat_tgk_prov_reference_clone(node, + &builder->entries[builder->len].node)) { + return false; + } + builder->entries[builder->len].depth = depth; + if (out_index != NULL) { + *out_index = builder->len; + } + if (out_added != NULL) { + *out_added = true; + } + builder->len++; + return true; +} + +static void amduat_tgk_prov_depth_builder_free( + amduat_tgk_prov_depth_builder_t *builder) { + size_t i; + + if (builder == NULL) { + return; + } + for (i = 0; i < builder->len; ++i) { + amduat_tgk_prov_reference_free(&builder->entries[i].node); + } + free(builder->entries); + builder->entries = NULL; + builder->len = 0; + builder->cap = 0; +} + +static bool amduat_tgk_prov_queue_push(amduat_tgk_prov_index_queue_t *queue, + size_t index) { + size_t new_cap; + size_t *next; + + if (queue == NULL) { + return false; + } + if (queue->len == queue->cap) { + new_cap = (queue->cap == 0) ? 8u : queue->cap * 2u; + next = (size_t *)realloc(queue->items, new_cap * sizeof(*queue->items)); + if (next == NULL) { + return false; + } + queue->items = next; + queue->cap = new_cap; + } + queue->items[queue->len++] = index; + return true; +} + +static void amduat_tgk_prov_queue_free(amduat_tgk_prov_index_queue_t *queue) { + if (queue == NULL) { + return; + } + free(queue->items); + queue->items = NULL; + queue->len = 0; + queue->cap = 0; + queue->head = 0; +} + +static bool amduat_tgk_prov_node_set_add(amduat_reference_t node, + amduat_reference_t **nodes, + size_t *nodes_len, + size_t *nodes_cap) { + size_t i; + size_t new_cap; + amduat_reference_t *next; + + if (nodes == NULL || nodes_len == NULL || nodes_cap == NULL) { + return false; + } + + for (i = 0; i < *nodes_len; ++i) { + if (amduat_reference_eq((*nodes)[i], node)) { + return true; + } + } + + if (*nodes_len == *nodes_cap) { + new_cap = (*nodes_cap == 0) ? 8u : (*nodes_cap * 2u); + next = (amduat_reference_t *)realloc( + *nodes, new_cap * sizeof(**nodes)); + if (next == NULL) { + return false; + } + *nodes = next; + *nodes_cap = new_cap; + } + + if (!amduat_tgk_prov_reference_clone(node, &(*nodes)[*nodes_len])) { + return false; + } + (*nodes_len)++; + return true; +} + +static void amduat_tgk_prov_reference_list_free(amduat_reference_t *nodes, + size_t nodes_len) { + size_t i; + + if (nodes == NULL) { + return; + } + for (i = 0; i < nodes_len; ++i) { + amduat_tgk_prov_reference_free(&nodes[i]); + } + free(nodes); +} + +static bool amduat_tgk_prov_edge_body_clone(const amduat_tgk_edge_body_t *src, + amduat_tgk_edge_body_t *dst) { + size_t i; + + if (src == NULL || dst == NULL) { + return false; + } + + dst->type = src->type; + dst->from = NULL; + dst->from_len = 0; + dst->to = NULL; + dst->to_len = 0; + dst->payload = amduat_reference(0, amduat_octets(NULL, 0)); + + if (src->from_len != 0) { + if (src->from == NULL) { + return false; + } + dst->from = (amduat_reference_t *)calloc( + src->from_len, sizeof(*dst->from)); + if (dst->from == NULL) { + return false; + } + for (i = 0; i < src->from_len; ++i) { + if (!amduat_tgk_prov_reference_clone(src->from[i], &dst->from[i])) { + amduat_tgk_edge_body_free(dst); + return false; + } + dst->from_len++; + } + } + + if (src->to_len != 0) { + if (src->to == NULL) { + amduat_tgk_edge_body_free(dst); + return false; + } + dst->to = (amduat_reference_t *)calloc(src->to_len, sizeof(*dst->to)); + if (dst->to == NULL) { + amduat_tgk_edge_body_free(dst); + return false; + } + for (i = 0; i < src->to_len; ++i) { + if (!amduat_tgk_prov_reference_clone(src->to[i], &dst->to[i])) { + amduat_tgk_edge_body_free(dst); + return false; + } + dst->to_len++; + } + } + + if (!amduat_tgk_prov_reference_clone(src->payload, &dst->payload)) { + amduat_tgk_edge_body_free(dst); + return false; + } + + return true; +} + +static bool amduat_tgk_prov_edge_view_clone( + const amduat_tgk_graph_edge_view_t *src, + amduat_tgk_graph_edge_view_t *dst) { + if (src == NULL || dst == NULL) { + return false; + } + if (!amduat_tgk_prov_reference_clone(src->edge_ref, &dst->edge_ref)) { + return false; + } + if (!amduat_tgk_prov_edge_body_clone(&src->body, &dst->body)) { + amduat_tgk_prov_reference_free(&dst->edge_ref); + return false; + } + return true; +} + +static void amduat_tgk_prov_edge_view_free(amduat_tgk_graph_edge_view_t *edge) { + if (edge == NULL) { + return; + } + amduat_tgk_prov_reference_free(&edge->edge_ref); + amduat_tgk_edge_body_free(&edge->body); +} + +static bool amduat_tgk_prov_edge_list_add( + const amduat_tgk_graph_edge_view_t *edge, + amduat_tgk_graph_edge_view_t **edges, + size_t *edges_len, + size_t *edges_cap) { + size_t i; + size_t new_cap; + amduat_tgk_graph_edge_view_t *next; + + if (edge == NULL || edges == NULL || edges_len == NULL || edges_cap == NULL) { + return false; + } + + for (i = 0; i < *edges_len; ++i) { + if (amduat_reference_eq((*edges)[i].edge_ref, edge->edge_ref)) { + return true; + } + } + + if (*edges_len == *edges_cap) { + new_cap = (*edges_cap == 0) ? 8u : (*edges_cap * 2u); + next = (amduat_tgk_graph_edge_view_t *)realloc( + *edges, new_cap * sizeof(**edges)); + if (next == NULL) { + return false; + } + *edges = next; + *edges_cap = new_cap; + } + + if (!amduat_tgk_prov_edge_view_clone(edge, &(*edges)[*edges_len])) { + return false; + } + (*edges_len)++; + return true; +} + +static void amduat_tgk_prov_edge_list_free( + amduat_tgk_graph_edge_view_t *edges, + size_t edges_len) { + size_t i; + + if (edges == NULL) { + return; + } + for (i = 0; i < edges_len; ++i) { + amduat_tgk_prov_edge_view_free(&edges[i]); + } + free(edges); +} + +static bool amduat_tgk_prov_build_depth_entries( + amduat_tgk_store_t *store, + const amduat_reference_t *seeds, + size_t seeds_len, + amduat_tgk_prov_query_t query, + amduat_tgk_prov_depth_entry_t **out_entries, + size_t *out_len) { + amduat_tgk_prov_depth_builder_t builder = {0}; + amduat_tgk_prov_index_queue_t queue = {0}; + amduat_tgk_graph_direction_t dir; + size_t i; + + if (out_entries == NULL || out_len == NULL) { + return false; + } + *out_entries = NULL; + *out_len = 0; + + if (store == NULL) { + return false; + } + if (seeds_len != 0 && seeds == NULL) { + return false; + } + if (!amduat_tgk_prov_direction_to_graph(query.direction, &dir)) { + return false; + } + + for (i = 0; i < seeds_len; ++i) { + size_t index = 0; + bool added = false; + if (!amduat_tgk_prov_depth_builder_insert(&builder, seeds[i], 0, &index, + &added)) { + goto cleanup; + } + if (added) { + if (!amduat_tgk_prov_queue_push(&queue, index)) { + goto cleanup; + } + } + } + + while (queue.head < queue.len) { + size_t index = queue.items[queue.head++]; + uint32_t depth = builder.entries[index].depth; + amduat_tgk_node_list_t neighbors; + + if (query.has_depth_limit && depth >= query.depth_limit) { + continue; + } + + neighbors.nodes = NULL; + neighbors.len = 0; + if (!amduat_tgk_store_neighbors(store, builder.entries[index].node, + query.type_filter, dir, &neighbors)) { + amduat_tgk_node_list_free(&neighbors); + goto cleanup; + } + + for (i = 0; i < neighbors.len; ++i) { + size_t next_index = 0; + bool added = false; + if (!amduat_tgk_prov_depth_builder_insert(&builder, neighbors.nodes[i], + depth + 1, &next_index, + &added)) { + amduat_tgk_node_list_free(&neighbors); + goto cleanup; + } + if (added) { + if (!amduat_tgk_prov_queue_push(&queue, next_index)) { + amduat_tgk_node_list_free(&neighbors); + goto cleanup; + } + } + } + + amduat_tgk_node_list_free(&neighbors); + } + + *out_entries = builder.entries; + *out_len = builder.len; + amduat_tgk_prov_queue_free(&queue); + return true; + +cleanup: + amduat_tgk_prov_queue_free(&queue); + amduat_tgk_prov_depth_builder_free(&builder); + return false; +} + +bool amduat_tgk_prov_closure_nodes(amduat_tgk_store_t *store, + const amduat_reference_t *seeds, + size_t seeds_len, + amduat_tgk_prov_query_t query, + amduat_tgk_node_list_t *out_nodes) { + amduat_tgk_prov_depth_entry_t *entries = NULL; + size_t entries_len = 0; + amduat_reference_t *nodes = NULL; + size_t nodes_len = 0; + size_t nodes_cap = 0; + size_t i; + + if (out_nodes == NULL) { + return false; + } + out_nodes->nodes = NULL; + out_nodes->len = 0; + + if (!amduat_tgk_prov_build_depth_entries(store, seeds, seeds_len, query, + &entries, &entries_len)) { + return false; + } + + for (i = 0; i < entries_len; ++i) { + if (!amduat_tgk_prov_node_set_add(entries[i].node, &nodes, &nodes_len, + &nodes_cap)) { + amduat_tgk_prov_reference_list_free(nodes, nodes_len); + amduat_tgk_prov_depth_map_free(&(amduat_tgk_prov_depth_map_t){ + .entries = entries, .len = entries_len}); + return false; + } + } + + if (nodes_len > 1) { + qsort(nodes, nodes_len, sizeof(*nodes), amduat_tgk_prov_ref_ptr_cmp); + } + + out_nodes->nodes = nodes; + out_nodes->len = nodes_len; + amduat_tgk_prov_depth_map_free(&(amduat_tgk_prov_depth_map_t){ + .entries = entries, .len = entries_len}); + return true; +} + +bool amduat_tgk_prov_depths(amduat_tgk_store_t *store, + const amduat_reference_t *seeds, + size_t seeds_len, + amduat_tgk_prov_query_t query, + amduat_tgk_prov_depth_map_t *out_depths) { + amduat_tgk_prov_depth_entry_t *entries = NULL; + size_t entries_len = 0; + + if (out_depths == NULL) { + return false; + } + out_depths->entries = NULL; + out_depths->len = 0; + + if (!amduat_tgk_prov_build_depth_entries(store, seeds, seeds_len, query, + &entries, &entries_len)) { + return false; + } + + if (entries_len > 1) { + qsort(entries, entries_len, sizeof(*entries), + amduat_tgk_prov_depth_entry_cmp); + } + + out_depths->entries = entries; + out_depths->len = entries_len; + return true; +} + +bool amduat_tgk_prov_layers(amduat_tgk_store_t *store, + const amduat_reference_t *seeds, + size_t seeds_len, + amduat_tgk_prov_query_t query, + amduat_tgk_prov_layering_t *out_layers) { + amduat_tgk_prov_depth_map_t depth_map; + amduat_tgk_prov_layer_t *layers = NULL; + size_t layers_len = 0; + size_t layers_cap = 0; + size_t i = 0; + + if (out_layers == NULL) { + return false; + } + out_layers->layers = NULL; + out_layers->layers_len = 0; + + if (!amduat_tgk_prov_depths(store, seeds, seeds_len, query, &depth_map)) { + return false; + } + + while (i < depth_map.len) { + uint32_t depth = depth_map.entries[i].depth; + size_t start = i; + size_t count; + size_t j; + size_t new_cap; + amduat_tgk_prov_layer_t *next; + + while (i < depth_map.len && depth_map.entries[i].depth == depth) { + i++; + } + count = i - start; + + if (layers_len == layers_cap) { + new_cap = (layers_cap == 0) ? 4u : layers_cap * 2u; + next = (amduat_tgk_prov_layer_t *)realloc( + layers, new_cap * sizeof(*layers)); + if (next == NULL) { + goto cleanup; + } + layers = next; + layers_cap = new_cap; + } + + layers[layers_len].depth = depth; + layers[layers_len].nodes = NULL; + layers[layers_len].nodes_len = 0; + layers_len++; + + if (count != 0) { + layers[layers_len - 1].nodes = (amduat_reference_t *)calloc( + count, sizeof(*layers[layers_len - 1].nodes)); + if (layers[layers_len - 1].nodes == NULL) { + goto cleanup; + } + } + + for (j = 0; j < count; ++j) { + if (!amduat_tgk_prov_reference_clone( + depth_map.entries[start + j].node, + &layers[layers_len - 1].nodes[j])) { + goto cleanup; + } + layers[layers_len - 1].nodes_len++; + } + } + + out_layers->layers = layers; + out_layers->layers_len = layers_len; + amduat_tgk_prov_depth_map_free(&depth_map); + return true; + +cleanup: + if (layers != NULL) { + for (i = 0; i < layers_len; ++i) { + amduat_tgk_prov_reference_list_free(layers[i].nodes, layers[i].nodes_len); + layers[i].nodes = NULL; + layers[i].nodes_len = 0; + } + } + free(layers); + amduat_tgk_prov_depth_map_free(&depth_map); + return false; +} + +bool amduat_tgk_prov_trace(amduat_tgk_store_t *store, + const amduat_reference_t *seeds, + size_t seeds_len, + amduat_tgk_prov_query_t query, + amduat_tgk_trace_graph_t *out_trace) { + amduat_tgk_node_list_t closure = {0}; + amduat_tgk_graph_edge_view_t *edges = NULL; + size_t edges_len = 0; + size_t edges_cap = 0; + amduat_reference_t *seed_nodes = NULL; + size_t seed_nodes_len = 0; + size_t seed_nodes_cap = 0; + amduat_reference_t *trace_nodes = NULL; + size_t trace_nodes_len = 0; + size_t trace_nodes_cap = 0; + size_t i; + + if (out_trace == NULL) { + return false; + } + out_trace->seeds = NULL; + out_trace->seeds_len = 0; + out_trace->nodes = NULL; + out_trace->nodes_len = 0; + out_trace->edges = NULL; + out_trace->edges_len = 0; + + if (store == NULL) { + return false; + } + if (seeds_len != 0 && seeds == NULL) { + return false; + } + + if (!amduat_tgk_prov_closure_nodes(store, seeds, seeds_len, query, + &closure)) { + return false; + } + + for (i = 0; i < closure.len; ++i) { + amduat_tgk_graph_edge_view_list_t incident = {0}; + size_t j; + + if (!amduat_tgk_store_edges_incident(store, closure.nodes[i], + query.type_filter, &incident)) { + goto cleanup; + } + + for (j = 0; j < incident.len; ++j) { + if (!amduat_tgk_prov_edge_list_add(&incident.edges[j], &edges, + &edges_len, &edges_cap)) { + amduat_tgk_graph_edge_view_list_free(&incident); + goto cleanup; + } + } + + amduat_tgk_graph_edge_view_list_free(&incident); + } + + if (edges_len > 1) { + qsort(edges, edges_len, sizeof(*edges), amduat_tgk_prov_edge_cmp); + } + + for (i = 0; i < seeds_len; ++i) { + if (!amduat_tgk_prov_node_set_add(seeds[i], &seed_nodes, &seed_nodes_len, + &seed_nodes_cap)) { + goto cleanup; + } + } + + if (seed_nodes_len > 1) { + qsort(seed_nodes, seed_nodes_len, sizeof(*seed_nodes), + amduat_tgk_prov_ref_ptr_cmp); + } + + for (i = 0; i < seed_nodes_len; ++i) { + if (!amduat_tgk_prov_node_set_add(seed_nodes[i], &trace_nodes, + &trace_nodes_len, &trace_nodes_cap)) { + goto cleanup; + } + } + + for (i = 0; i < edges_len; ++i) { + size_t j; + const amduat_tgk_edge_body_t *body = &edges[i].body; + for (j = 0; j < body->from_len; ++j) { + if (!amduat_tgk_prov_node_set_add(body->from[j], &trace_nodes, + &trace_nodes_len, &trace_nodes_cap)) { + goto cleanup; + } + } + for (j = 0; j < body->to_len; ++j) { + if (!amduat_tgk_prov_node_set_add(body->to[j], &trace_nodes, + &trace_nodes_len, &trace_nodes_cap)) { + goto cleanup; + } + } + if (!amduat_tgk_prov_node_set_add(body->payload, &trace_nodes, + &trace_nodes_len, &trace_nodes_cap)) { + goto cleanup; + } + } + + if (trace_nodes_len > 1) { + qsort(trace_nodes, trace_nodes_len, sizeof(*trace_nodes), + amduat_tgk_prov_ref_ptr_cmp); + } + + out_trace->seeds = seed_nodes; + out_trace->seeds_len = seed_nodes_len; + out_trace->nodes = trace_nodes; + out_trace->nodes_len = trace_nodes_len; + out_trace->edges = edges; + out_trace->edges_len = edges_len; + amduat_tgk_node_list_free(&closure); + return true; + +cleanup: + amduat_tgk_node_list_free(&closure); + amduat_tgk_prov_edge_list_free(edges, edges_len); + amduat_tgk_prov_reference_list_free(seed_nodes, seed_nodes_len); + amduat_tgk_prov_reference_list_free(trace_nodes, trace_nodes_len); + return false; +} + +void amduat_tgk_prov_depth_map_free(amduat_tgk_prov_depth_map_t *map) { + size_t i; + + if (map == NULL) { + return; + } + if (map->entries != NULL) { + for (i = 0; i < map->len; ++i) { + amduat_tgk_prov_reference_free(&map->entries[i].node); + } + } + free(map->entries); + map->entries = NULL; + map->len = 0; +} + +void amduat_tgk_prov_layering_free(amduat_tgk_prov_layering_t *layers) { + size_t i; + + if (layers == NULL) { + return; + } + if (layers->layers != NULL) { + for (i = 0; i < layers->layers_len; ++i) { + amduat_tgk_prov_reference_list_free(layers->layers[i].nodes, + layers->layers[i].nodes_len); + layers->layers[i].nodes = NULL; + layers->layers[i].nodes_len = 0; + } + } + free(layers->layers); + layers->layers = NULL; + layers->layers_len = 0; +} + +void amduat_tgk_trace_graph_free(amduat_tgk_trace_graph_t *trace) { + if (trace == NULL) { + return; + } + amduat_tgk_prov_reference_list_free(trace->seeds, trace->seeds_len); + amduat_tgk_prov_reference_list_free(trace->nodes, trace->nodes_len); + amduat_tgk_prov_edge_list_free(trace->edges, trace->edges_len); + trace->seeds = NULL; + trace->seeds_len = 0; + trace->nodes = NULL; + trace->nodes_len = 0; + trace->edges = NULL; + trace->edges_len = 0; +} diff --git a/tests/tgk/test_tgk_prov.c b/tests/tgk/test_tgk_prov.c new file mode 100644 index 0000000..b331f01 --- /dev/null +++ b/tests/tgk/test_tgk_prov.c @@ -0,0 +1,462 @@ +#include "amduat/enc/asl1_core.h" +#include "amduat/enc/tgk1_edge.h" +#include "amduat/hash/asl1.h" +#include "amduat/tgk/prov.h" +#include "amduat/tgk/tgk_store_mem.h" + +#include +#include +#include +#include +#include + +typedef struct { + amduat_tgk_store_t store; + amduat_tgk_store_mem_t mem; + amduat_tgk_store_mem_artifact_t artifacts[4]; + amduat_octets_t edge_bytes[4]; + amduat_reference_t ref_edge1; + amduat_reference_t ref_edge2; + amduat_reference_t ref_edge3; + amduat_reference_t ref_edge4; + amduat_reference_t node_a; + amduat_reference_t node_b; + amduat_reference_t node_c; + amduat_reference_t node_d; + amduat_reference_t payload1; + amduat_reference_t payload2; + amduat_reference_t payload3; + amduat_reference_t payload4; + uint8_t digest_edge1[32]; + uint8_t digest_edge2[32]; + uint8_t digest_edge3[32]; + uint8_t digest_edge4[32]; + uint8_t digest_node_a[32]; + uint8_t digest_node_b[32]; + uint8_t digest_node_c[32]; + uint8_t digest_node_d[32]; + uint8_t digest_payload1[32]; + uint8_t digest_payload2[32]; + uint8_t digest_payload3[32]; + uint8_t digest_payload4[32]; + amduat_tgk_identity_domain_t domains[1]; + uint32_t edge_tags[1]; + amduat_tgk_edge_type_id_t edge_types[2]; + amduat_asl_encoding_profile_id_t encodings[1]; + amduat_tgk_store_config_t config; +} test_env_t; + +static void fill_digest(uint8_t *out, uint8_t value) { + memset(out, value, 32); +} + +static amduat_reference_t make_ref(uint8_t value, uint8_t *storage) { + fill_digest(storage, value); + return amduat_reference(AMDUAT_HASH_ASL1_ID_SHA256, + amduat_octets(storage, 32)); +} + +static void free_edge_bytes(test_env_t *env) { + size_t i; + + for (i = 0; i < 4; ++i) { + free((void *)env->edge_bytes[i].data); + env->edge_bytes[i].data = NULL; + env->edge_bytes[i].len = 0; + } +} + +static bool init_env(test_env_t *env) { + amduat_tgk_edge_body_t edge1; + amduat_tgk_edge_body_t edge2; + amduat_tgk_edge_body_t edge3; + amduat_tgk_edge_body_t edge4; + amduat_reference_t edge1_from[1]; + amduat_reference_t edge1_to[1]; + amduat_reference_t edge2_from[1]; + amduat_reference_t edge2_to[1]; + amduat_reference_t edge3_from[1]; + amduat_reference_t edge3_to[1]; + amduat_reference_t edge4_from[1]; + amduat_reference_t edge4_to[1]; + + memset(env, 0, sizeof(*env)); + + env->domains[0].encoding_profile = AMDUAT_ENC_ASL1_CORE_V1; + env->domains[0].hash_id = AMDUAT_HASH_ASL1_ID_SHA256; + env->edge_tags[0] = TYPE_TAG_TGK1_EDGE_V1; + env->edge_types[0] = 0x10; + env->edge_types[1] = 0x20; + env->encodings[0] = TGK1_EDGE_ENC_V1; + + env->config.id_space.domains = env->domains; + env->config.id_space.domains_len = 1; + env->config.artifact_scope.description = amduat_octets(NULL, 0); + env->config.tgk_profiles.edge_tags = env->edge_tags; + env->config.tgk_profiles.edge_tags_len = 1; + env->config.tgk_profiles.edge_types = env->edge_types; + env->config.tgk_profiles.edge_types_len = 2; + env->config.tgk_profiles.encodings = env->encodings; + env->config.tgk_profiles.encodings_len = 1; + + env->ref_edge1 = make_ref(0x11, env->digest_edge1); + env->ref_edge2 = make_ref(0x12, env->digest_edge2); + env->ref_edge3 = make_ref(0x13, env->digest_edge3); + env->ref_edge4 = make_ref(0x14, env->digest_edge4); + + env->node_a = make_ref(0xa1, env->digest_node_a); + env->node_b = make_ref(0xb1, env->digest_node_b); + env->node_c = make_ref(0xc1, env->digest_node_c); + env->node_d = make_ref(0xd1, env->digest_node_d); + + env->payload1 = make_ref(0xe1, env->digest_payload1); + env->payload2 = make_ref(0xe2, env->digest_payload2); + env->payload3 = make_ref(0xe3, env->digest_payload3); + env->payload4 = make_ref(0xe4, env->digest_payload4); + + memset(&edge1, 0, sizeof(edge1)); + edge1.type = 0x10; + edge1_from[0] = env->node_a; + edge1.from = edge1_from; + edge1.from_len = 1; + edge1_to[0] = env->node_b; + edge1.to = edge1_to; + edge1.to_len = 1; + edge1.payload = env->payload1; + + memset(&edge2, 0, sizeof(edge2)); + edge2.type = 0x10; + edge2_from[0] = env->node_b; + edge2.from = edge2_from; + edge2.from_len = 1; + edge2_to[0] = env->node_c; + edge2.to = edge2_to; + edge2.to_len = 1; + edge2.payload = env->payload2; + + memset(&edge3, 0, sizeof(edge3)); + edge3.type = 0x10; + edge3_from[0] = env->node_c; + edge3.from = edge3_from; + edge3.from_len = 1; + edge3_to[0] = env->node_d; + edge3.to = edge3_to; + edge3.to_len = 1; + edge3.payload = env->payload3; + + memset(&edge4, 0, sizeof(edge4)); + edge4.type = 0x20; + edge4_from[0] = env->node_b; + edge4.from = edge4_from; + edge4.from_len = 1; + edge4_to[0] = env->node_a; + edge4.to = edge4_to; + edge4.to_len = 1; + edge4.payload = env->payload4; + + if (!amduat_enc_tgk1_edge_encode_v1(&edge1, &env->edge_bytes[0]) || + !amduat_enc_tgk1_edge_encode_v1(&edge2, &env->edge_bytes[1]) || + !amduat_enc_tgk1_edge_encode_v1(&edge3, &env->edge_bytes[2]) || + !amduat_enc_tgk1_edge_encode_v1(&edge4, &env->edge_bytes[3])) { + free_edge_bytes(env); + return false; + } + + env->artifacts[0].ref = env->ref_edge1; + env->artifacts[0].artifact = + amduat_artifact_with_type(env->edge_bytes[0], + amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1)); + env->artifacts[1].ref = env->ref_edge2; + env->artifacts[1].artifact = + amduat_artifact_with_type(env->edge_bytes[1], + amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1)); + env->artifacts[2].ref = env->ref_edge3; + env->artifacts[2].artifact = + amduat_artifact_with_type(env->edge_bytes[2], + amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1)); + env->artifacts[3].ref = env->ref_edge4; + env->artifacts[3].artifact = + amduat_artifact_with_type(env->edge_bytes[3], + amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1)); + + if (!amduat_tgk_store_mem_init(&env->mem, env->config, env->artifacts, 4)) { + free_edge_bytes(env); + return false; + } + + amduat_tgk_store_init(&env->store, env->config, amduat_tgk_store_mem_ops(), + &env->mem); + return true; +} + +static void free_env(test_env_t *env) { + amduat_tgk_store_mem_free(&env->mem); + free_edge_bytes(env); +} + +static bool list_contains(const amduat_reference_t *nodes, + size_t len, + amduat_reference_t node) { + size_t i; + + for (i = 0; i < len; ++i) { + if (amduat_reference_eq(nodes[i], node)) { + return true; + } + } + return false; +} + +static bool depth_map_get(const amduat_tgk_prov_depth_map_t *map, + amduat_reference_t node, + uint32_t *out_depth) { + size_t i; + + for (i = 0; i < map->len; ++i) { + if (amduat_reference_eq(map->entries[i].node, node)) { + if (out_depth != NULL) { + *out_depth = map->entries[i].depth; + } + return true; + } + } + return false; +} + +static int test_direction(const test_env_t *env) { + amduat_tgk_prov_query_t query; + amduat_tgk_node_list_t nodes; + amduat_tgk_edge_type_id_t types[1] = {0x10}; + amduat_reference_t seeds[1]; + + seeds[0] = env->node_b; + query.type_filter.types = types; + query.type_filter.types_len = 1; + query.has_depth_limit = false; + + query.direction = AMDUAT_TGK_PROV_DIR_FORWARD; + if (!amduat_tgk_prov_closure_nodes((amduat_tgk_store_t *)&env->store, seeds, 1, + query, &nodes)) { + fprintf(stderr, "direction forward closure failed\n"); + return 1; + } + if (nodes.len != 3 || !list_contains(nodes.nodes, nodes.len, env->node_b) || + !list_contains(nodes.nodes, nodes.len, env->node_c) || + !list_contains(nodes.nodes, nodes.len, env->node_d)) { + fprintf(stderr, "direction forward closure mismatch\n"); + amduat_tgk_node_list_free(&nodes); + return 1; + } + amduat_tgk_node_list_free(&nodes); + + query.direction = AMDUAT_TGK_PROV_DIR_BACKWARD; + if (!amduat_tgk_prov_closure_nodes((amduat_tgk_store_t *)&env->store, seeds, 1, + query, &nodes)) { + fprintf(stderr, "direction backward closure failed\n"); + return 1; + } + if (nodes.len != 2 || !list_contains(nodes.nodes, nodes.len, env->node_a) || + !list_contains(nodes.nodes, nodes.len, env->node_b)) { + fprintf(stderr, "direction backward closure mismatch\n"); + amduat_tgk_node_list_free(&nodes); + return 1; + } + amduat_tgk_node_list_free(&nodes); + + query.direction = AMDUAT_TGK_PROV_DIR_BOTH; + if (!amduat_tgk_prov_closure_nodes((amduat_tgk_store_t *)&env->store, seeds, 1, + query, &nodes)) { + fprintf(stderr, "direction both closure failed\n"); + return 1; + } + if (nodes.len != 4 || !list_contains(nodes.nodes, nodes.len, env->node_a) || + !list_contains(nodes.nodes, nodes.len, env->node_b) || + !list_contains(nodes.nodes, nodes.len, env->node_c) || + !list_contains(nodes.nodes, nodes.len, env->node_d)) { + fprintf(stderr, "direction both closure mismatch\n"); + amduat_tgk_node_list_free(&nodes); + return 1; + } + amduat_tgk_node_list_free(&nodes); + + return 0; +} + +static int test_depth_limits(const test_env_t *env) { + amduat_tgk_prov_query_t query; + amduat_tgk_prov_depth_map_t depth_map; + amduat_tgk_prov_layering_t layers; + amduat_tgk_edge_type_id_t types[1] = {0x10}; + amduat_reference_t seeds[1]; + uint32_t depth = 0; + + seeds[0] = env->node_a; + query.direction = AMDUAT_TGK_PROV_DIR_FORWARD; + query.type_filter.types = types; + query.type_filter.types_len = 1; + query.has_depth_limit = false; + + if (!amduat_tgk_prov_depths((amduat_tgk_store_t *)&env->store, seeds, 1, query, + &depth_map)) { + fprintf(stderr, "depth map unbounded failed\n"); + return 1; + } + if (depth_map.len != 4 || + !depth_map_get(&depth_map, env->node_a, &depth) || depth != 0 || + !depth_map_get(&depth_map, env->node_b, &depth) || depth != 1 || + !depth_map_get(&depth_map, env->node_c, &depth) || depth != 2 || + !depth_map_get(&depth_map, env->node_d, &depth) || depth != 3) { + fprintf(stderr, "depth map unbounded mismatch\n"); + amduat_tgk_prov_depth_map_free(&depth_map); + return 1; + } + amduat_tgk_prov_depth_map_free(&depth_map); + + query.has_depth_limit = true; + query.depth_limit = 1; + if (!amduat_tgk_prov_layers((amduat_tgk_store_t *)&env->store, seeds, 1, query, + &layers)) { + fprintf(stderr, "layers bounded failed\n"); + return 1; + } + if (layers.layers_len != 2 || layers.layers[0].depth != 0 || + layers.layers[1].depth != 1 || layers.layers[0].nodes_len != 1 || + layers.layers[1].nodes_len != 1 || + !list_contains(layers.layers[0].nodes, layers.layers[0].nodes_len, + env->node_a) || + !list_contains(layers.layers[1].nodes, layers.layers[1].nodes_len, + env->node_b)) { + fprintf(stderr, "layers bounded mismatch\n"); + amduat_tgk_prov_layering_free(&layers); + return 1; + } + amduat_tgk_prov_layering_free(&layers); + return 0; +} + +static int test_payload_in_trace(const test_env_t *env) { + amduat_tgk_prov_query_t query; + amduat_tgk_trace_graph_t trace; + amduat_tgk_node_list_t nodes; + amduat_tgk_edge_type_id_t types[1] = {0x10}; + amduat_reference_t seeds[1]; + + seeds[0] = env->node_a; + query.direction = AMDUAT_TGK_PROV_DIR_FORWARD; + query.type_filter.types = types; + query.type_filter.types_len = 1; + query.has_depth_limit = false; + + if (!amduat_tgk_prov_trace((amduat_tgk_store_t *)&env->store, seeds, 1, query, + &trace)) { + fprintf(stderr, "trace failed\n"); + return 1; + } + if (trace.edges_len != 3 || + !list_contains(trace.nodes, trace.nodes_len, env->payload1) || + !list_contains(trace.nodes, trace.nodes_len, env->payload2) || + !list_contains(trace.nodes, trace.nodes_len, env->payload3)) { + fprintf(stderr, "trace payload nodes mismatch\n"); + amduat_tgk_trace_graph_free(&trace); + return 1; + } + amduat_tgk_trace_graph_free(&trace); + + if (!amduat_tgk_prov_closure_nodes((amduat_tgk_store_t *)&env->store, seeds, 1, + query, &nodes)) { + fprintf(stderr, "closure for payload test failed\n"); + return 1; + } + if (list_contains(nodes.nodes, nodes.len, env->payload1) || + list_contains(nodes.nodes, nodes.len, env->payload2) || + list_contains(nodes.nodes, nodes.len, env->payload3)) { + fprintf(stderr, "closure should not include payload nodes\n"); + amduat_tgk_node_list_free(&nodes); + return 1; + } + amduat_tgk_node_list_free(&nodes); + + return 0; +} + +static int test_empty_seed(const test_env_t *env) { + amduat_tgk_prov_query_t query; + amduat_tgk_node_list_t nodes; + amduat_tgk_prov_depth_map_t depth_map; + amduat_tgk_prov_layering_t layers; + amduat_tgk_trace_graph_t trace; + amduat_tgk_edge_type_id_t types[1] = {0x10}; + + query.direction = AMDUAT_TGK_PROV_DIR_FORWARD; + query.type_filter.types = types; + query.type_filter.types_len = 1; + query.has_depth_limit = false; + + if (!amduat_tgk_prov_closure_nodes((amduat_tgk_store_t *)&env->store, NULL, 0, + query, &nodes)) { + fprintf(stderr, "empty seed closure failed\n"); + return 1; + } + if (nodes.len != 0) { + fprintf(stderr, "empty seed closure not empty\n"); + amduat_tgk_node_list_free(&nodes); + return 1; + } + amduat_tgk_node_list_free(&nodes); + + if (!amduat_tgk_prov_depths((amduat_tgk_store_t *)&env->store, NULL, 0, query, + &depth_map)) { + fprintf(stderr, "empty seed depths failed\n"); + return 1; + } + if (depth_map.len != 0) { + fprintf(stderr, "empty seed depths not empty\n"); + amduat_tgk_prov_depth_map_free(&depth_map); + return 1; + } + amduat_tgk_prov_depth_map_free(&depth_map); + + if (!amduat_tgk_prov_layers((amduat_tgk_store_t *)&env->store, NULL, 0, query, + &layers)) { + fprintf(stderr, "empty seed layers failed\n"); + return 1; + } + if (layers.layers_len != 0) { + fprintf(stderr, "empty seed layers not empty\n"); + amduat_tgk_prov_layering_free(&layers); + return 1; + } + amduat_tgk_prov_layering_free(&layers); + + if (!amduat_tgk_prov_trace((amduat_tgk_store_t *)&env->store, NULL, 0, query, + &trace)) { + fprintf(stderr, "empty seed trace failed\n"); + return 1; + } + if (trace.seeds_len != 0 || trace.nodes_len != 0 || trace.edges_len != 0) { + fprintf(stderr, "empty seed trace not empty\n"); + amduat_tgk_trace_graph_free(&trace); + return 1; + } + amduat_tgk_trace_graph_free(&trace); + + return 0; +} + +int main(void) { + test_env_t env; + + if (!init_env(&env)) { + fprintf(stderr, "failed to initialize test environment\n"); + return 1; + } + + if (test_direction(&env) != 0 || test_depth_limits(&env) != 0 || + test_payload_in_trace(&env) != 0 || test_empty_seed(&env) != 0) { + free_env(&env); + return 1; + } + + free_env(&env); + return 0; +}