Add TGK provenance operators and tests
This commit is contained in:
parent
8b2a5f830a
commit
257280934f
|
|
@ -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_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
|
add_executable(amduat_test_pel_program_dag_exec
|
||||||
tests/pel/test_pel_program_dag_exec.c)
|
tests/pel/test_pel_program_dag_exec.c)
|
||||||
target_include_directories(amduat_test_pel_program_dag_exec
|
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_INTERNAL_DIR}
|
||||||
PRIVATE ${AMDUAT_INCLUDE_DIR}
|
PRIVATE ${AMDUAT_INCLUDE_DIR}
|
||||||
)
|
)
|
||||||
|
target_compile_definitions(amduat_test_pel_queue
|
||||||
|
PRIVATE _POSIX_C_SOURCE=200809L
|
||||||
|
)
|
||||||
target_link_libraries(amduat_test_pel_queue
|
target_link_libraries(amduat_test_pel_queue
|
||||||
PRIVATE amduat_pel
|
PRIVATE amduat_pel
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
#ifndef AMDUAT_TGK_PROV_H
|
||||||
|
#define AMDUAT_TGK_PROV_H
|
||||||
|
|
||||||
|
#include "amduat/tgk/store.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
|
@ -0,0 +1,843 @@
|
||||||
|
#include "amduat/tgk/prov.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
462
tests/tgk/test_tgk_prov.c
Normal file
462
tests/tgk/test_tgk_prov.c
Normal file
|
|
@ -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 <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue