From 948a156f5cd25f1540818730afb39887e0c41d5a Mon Sep 17 00:00:00 2001 From: Carl Niklas Rydberg Date: Sun, 18 Jan 2026 09:21:25 +0100 Subject: [PATCH] Add PEL program builder and derivation indexing --- CMakeLists.txt | 25 +- include/amduat/asl/asl_derivation_index_fs.h | 1 + .../asl_derivation_index_fs.c | 16 +- src/tools/amduat_asl_cli.c | 32 +- src/tools/amduat_pel_build.c | 1012 +++++++++++++++++ src/tools/amduat_pel_cli.c | 98 +- src/tools/amduat_pel_run.c | 3 +- src/tools/amduat_pel_seed.c | 5 +- tests/asl/test_asl_derivation_index_fs.c | 8 +- tests/pel/test_pel_program_build_concat.c | 348 ++++++ 10 files changed, 1532 insertions(+), 16 deletions(-) create mode 100644 src/tools/amduat_pel_build.c create mode 100644 tests/pel/test_pel_program_build_concat.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c35d22..3542acf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -233,11 +233,22 @@ target_include_directories(amduat_pel_cli PRIVATE ${AMDUAT_INCLUDE_DIR} ) target_link_libraries(amduat_pel_cli - PRIVATE amduat_format amduat_pel amduat_asl_store_fs amduat_asl amduat_enc + PRIVATE amduat_format amduat_pel amduat_asl_store_fs + amduat_asl_derivation_index_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util ) set_target_properties(amduat_pel_cli PROPERTIES OUTPUT_NAME amduat-pel) +add_executable(amduat_pel_build src/tools/amduat_pel_build.c) +target_include_directories(amduat_pel_build + PRIVATE ${AMDUAT_INTERNAL_DIR} + PRIVATE ${AMDUAT_INCLUDE_DIR} +) +target_link_libraries(amduat_pel_build + PRIVATE amduat_pel amduat_asl amduat_enc amduat_util +) +set_target_properties(amduat_pel_build PROPERTIES OUTPUT_NAME amduat-pel-build) + enable_testing() add_executable(amduat_test_pel_program_dag tests/enc/test_pel_program_dag.c) @@ -260,6 +271,18 @@ target_link_libraries(amduat_test_pel_trace_dag ) add_test(NAME pel_trace_dag COMMAND amduat_test_pel_trace_dag) +add_executable(amduat_test_pel_program_build_concat + tests/pel/test_pel_program_build_concat.c +) +target_include_directories(amduat_test_pel_program_build_concat + PRIVATE ${AMDUAT_INTERNAL_DIR} + PRIVATE ${AMDUAT_INCLUDE_DIR} +) +target_link_libraries(amduat_test_pel_program_build_concat + PRIVATE amduat_pel amduat_asl amduat_enc amduat_hash_asl1 amduat_util +) +add_test(NAME pel_program_build_concat COMMAND amduat_test_pel_program_build_concat) + add_executable(amduat_test_pel1_result tests/enc/test_pel1_result.c) target_include_directories(amduat_test_pel1_result PRIVATE ${AMDUAT_INTERNAL_DIR} diff --git a/include/amduat/asl/asl_derivation_index_fs.h b/include/amduat/asl/asl_derivation_index_fs.h index 1535e03..caa4db6 100644 --- a/include/amduat/asl/asl_derivation_index_fs.h +++ b/include/amduat/asl/asl_derivation_index_fs.h @@ -17,6 +17,7 @@ enum { AMDUAT_ASL_DERIVATION_INDEX_FS_ROOT_MAX = 1024 }; typedef struct { amduat_octets_t sid; amduat_reference_t program_ref; + uint32_t output_index; amduat_reference_t *input_refs; size_t input_refs_len; bool has_params_ref; diff --git a/src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c b/src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c index 3f374bc..e1b0dfd 100644 --- a/src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c +++ b/src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c @@ -15,12 +15,12 @@ enum { AMDUAT_ASL_DERIVATION_INDEX_MAGIC_LEN = 8, AMDUAT_ASL_DERIVATION_INDEX_HEADER_LEN = 16, - AMDUAT_ASL_DERIVATION_INDEX_VERSION = 1 + AMDUAT_ASL_DERIVATION_INDEX_VERSION = 2 }; static const uint8_t k_amduat_asl_derivation_index_magic[ AMDUAT_ASL_DERIVATION_INDEX_MAGIC_LEN] = {'A', 'S', 'L', 'D', - 'R', 'V', '0', '1'}; + 'R', 'V', '0', '2'}; typedef struct { const uint8_t *data; @@ -445,6 +445,7 @@ static amduat_asl_store_error_t amduat_asl_derivation_write_record( total_len = 0u; if (!amduat_asl_derivation_add_size(&total_len, 4u + record->sid.len) || !amduat_asl_derivation_add_size(&total_len, 4u + program_bytes.len) || + !amduat_asl_derivation_add_size(&total_len, 4u) || !amduat_asl_derivation_add_size(&total_len, 4u)) { goto cleanup; } @@ -496,6 +497,10 @@ static amduat_asl_store_error_t amduat_asl_derivation_write_record( memcpy(buffer + offset, program_bytes.data, program_bytes.len); offset += program_bytes.len; + amduat_asl_derivation_store_u32_le(buffer + offset, + record->output_index); + offset += 4u; + amduat_asl_derivation_store_u32_le(buffer + offset, input_count); offset += 4u; for (i = 0u; i < record->input_refs_len; ++i) { @@ -750,6 +755,7 @@ static amduat_asl_store_error_t amduat_asl_derivation_decode_record( uint32_t record_len; size_t start_offset; uint32_t sid_len; + uint32_t output_index; uint32_t input_count; uint32_t params_len; uint32_t exec_len; @@ -790,6 +796,12 @@ static amduat_asl_store_error_t amduat_asl_derivation_decode_record( return AMDUAT_ASL_STORE_ERR_INTEGRITY; } + if (!amduat_asl_derivation_read_u32_le(cur, &output_index)) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + out_record->output_index = output_index; + if (!amduat_asl_derivation_read_u32_le(cur, &input_count)) { amduat_asl_derivation_record_free(out_record); return AMDUAT_ASL_STORE_ERR_INTEGRITY; diff --git a/src/tools/amduat_asl_cli.c b/src/tools/amduat_asl_cli.c index f098d7a..6ca74d3 100644 --- a/src/tools/amduat_asl_cli.c +++ b/src/tools/amduat_asl_cli.c @@ -99,7 +99,8 @@ static void amduat_asl_cli_print_usage(FILE *stream) { " [--expect-type-tag TAG] [--print-type-tag]\n" " [--quiet]\n" " amduat-asl derivations add --artifact-ref REF --sid SID\n" - " --program-ref REF [--input-ref REF...]\n" + " --program-ref REF --output-index N\n" + " [--input-ref REF...]\n" " [--params-ref REF] [--exec-profile PATH|-]\n" " [--root PATH] [--ref-format hex|bytes]\n" " [--sid-format hex|bytes]\n" @@ -120,10 +121,14 @@ static void amduat_asl_cli_print_usage(FILE *stream) { " --input-format raw\n" " --output-format raw\n" " --ref-format hex\n" + " --shards 1\n" "\n" "notes:\n" " --ref-format bytes expects refs as paths or -\n" - " derivations list outputs refs/sid in hex\n", + " --sid-format bytes expects SID values as paths or -\n" + " derivations add requires --output-index\n" + " derivations list outputs refs/sid in hex\n" + " segment verify without --segment checks all segments\n", AMDUAT_ASL_CLI_DEFAULT_ROOT); } @@ -1294,6 +1299,8 @@ static int amduat_asl_cli_cmd_derivations_add(int argc, char **argv) { const char *program_ref_arg = NULL; const char *params_ref_arg = NULL; const char *exec_profile_path = NULL; + uint32_t output_index = 0u; + bool has_output_index = false; amduat_format_ref_format_t ref_format = AMDUAT_FORMAT_REF_HEX; bool sid_is_hex = true; const char **input_args = NULL; @@ -1326,6 +1333,16 @@ static int amduat_asl_cli_cmd_derivations_add(int argc, char **argv) { return AMDUAT_ASL_CLI_EXIT_USAGE; } program_ref_arg = argv[++i]; + } else if (strcmp(argv[i], "--output-index") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --output-index requires a value\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + if (!amduat_asl_parse_u32(argv[++i], &output_index)) { + fprintf(stderr, "error: invalid output-index\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + has_output_index = true; } else if (strcmp(argv[i], "--input-ref") == 0) { if (i + 1 >= argc) { fprintf(stderr, "error: --input-ref requires a value\n"); @@ -1394,9 +1411,10 @@ static int amduat_asl_cli_cmd_derivations_add(int argc, char **argv) { } } - if (artifact_ref_arg == NULL || sid_arg == NULL || program_ref_arg == NULL) { + if (artifact_ref_arg == NULL || sid_arg == NULL || + program_ref_arg == NULL || !has_output_index) { fprintf(stderr, - "error: --artifact-ref, --sid, and --program-ref are required\n"); + "error: --artifact-ref, --sid, --program-ref, and --output-index are required\n"); free(input_args); return AMDUAT_ASL_CLI_EXIT_USAGE; } @@ -1462,6 +1480,7 @@ static int amduat_asl_cli_cmd_derivations_add(int argc, char **argv) { goto cleanup_decoded; } record.program_ref = program_ref; + record.output_index = output_index; record.input_refs = inputs; record.input_refs_len = input_len; @@ -1611,9 +1630,10 @@ static int amduat_asl_cli_cmd_derivations_list(int argc, char **argv) { &exec_hex); } - printf("sid=%s program=%s inputs=", + printf("sid=%s program=%s output=%u inputs=", sid_hex == NULL ? "(null)" : sid_hex, - prog_hex == NULL ? "(null)" : prog_hex); + prog_hex == NULL ? "(null)" : prog_hex, + (unsigned int)records[j].output_index); for (k = 0u; k < records[j].input_refs_len; ++k) { char *input_hex = NULL; (void)amduat_asl_ref_encode_hex(records[j].input_refs[k], &input_hex); diff --git a/src/tools/amduat_pel_build.c b/src/tools/amduat_pel_build.c new file mode 100644 index 0000000..131e318 --- /dev/null +++ b/src/tools/amduat_pel_build.c @@ -0,0 +1,1012 @@ +#include "amduat/asl/core.h" +#include "amduat/asl/io.h" +#include "amduat/asl/parse.h" +#include "amduat/enc/asl1_core_codec.h" +#include "amduat/enc/pel_program_dag.h" +#include "amduat/pel/program_dag.h" +#include "amduat/pel/program_dag_desc.h" +#include "amduat/util/hex.h" + +#include +#include +#include +#include +#include +#include +#include + +enum { + AMDUAT_PEL_BUILD_EXIT_OK = 0, + AMDUAT_PEL_BUILD_EXIT_USAGE = 2, + AMDUAT_PEL_BUILD_EXIT_IO = 3, + AMDUAT_PEL_BUILD_EXIT_CODEC = 7 +}; + +typedef enum { + AMDUAT_PEL_BUILD_OUTPUT_RAW = 0, + AMDUAT_PEL_BUILD_OUTPUT_ARTIFACT = 1 +} amduat_pel_build_output_format_t; + +typedef struct { + const char *input_path; + const char *output_path; + amduat_pel_build_output_format_t output_format; + bool quiet; + bool validate; + bool has_type_tag; + amduat_type_tag_t type_tag; +} amduat_pel_build_opts_t; + +typedef struct { + const char *cur; + const char *end; + const char *error; +} amduat_json_parser_t; + +static void amduat_pel_build_print_usage(FILE *stream) { + fprintf(stream, + "usage:\n" + " amduat-pel-build --input PATH|- [--output PATH|-]\n" + " [--output-format raw|artifact]\n" + " [--type-tag TAG] [--no-validate]\n" + " [--quiet]\n" + "\n" + "json format:\n" + " {\n" + " \"nodes\": [\n" + " {\n" + " \"id\": 1,\n" + " \"op\": \"pel.bytes.const\",\n" + " \"version\": 1,\n" + " \"inputs\": [\n" + " {\"kind\": \"external\", \"index\": 0},\n" + " {\"kind\": \"node\", \"node\": 2, \"output\": 0}\n" + " ],\n" + " \"params_hex\": \"00\"\n" + " }\n" + " ],\n" + " \"roots\": [{\"node\": 1, \"output\": 0}]\n" + " }\n" + "\n" + "defaults:\n" + " --input -\n" + " --output -\n" + " --output-format raw\n" + "\n" + "notes:\n" + " params_hex is the op parameter bytes in hex (no 0x prefix).\n" + " --output-format artifact emits ASL/1-CORE artifact bytes.\n"); +} + +static void amduat_json_set_error(amduat_json_parser_t *parser, + const char *error) { + if (parser == NULL || parser->error != NULL) { + return; + } + parser->error = error; +} + +static void amduat_json_skip_ws(amduat_json_parser_t *parser) { + while (parser->cur < parser->end && + isspace((unsigned char)*parser->cur)) { + parser->cur++; + } +} + +static bool amduat_json_expect(amduat_json_parser_t *parser, char ch) { + amduat_json_skip_ws(parser); + if (parser->cur >= parser->end || *parser->cur != ch) { + amduat_json_set_error(parser, "unexpected character"); + return false; + } + parser->cur++; + return true; +} + +static bool amduat_json_parse_string(amduat_json_parser_t *parser, + char **out_text) { + size_t cap = 64u; + size_t len = 0u; + char *buffer; + + if (out_text == NULL) { + return false; + } + *out_text = NULL; + + amduat_json_skip_ws(parser); + if (parser->cur >= parser->end || *parser->cur != '"') { + amduat_json_set_error(parser, "expected string"); + return false; + } + parser->cur++; + + buffer = (char *)malloc(cap); + if (buffer == NULL) { + amduat_json_set_error(parser, "out of memory"); + return false; + } + + while (parser->cur < parser->end) { + char ch = *parser->cur++; + if (ch == '"') { + buffer[len] = '\0'; + *out_text = buffer; + return true; + } + if (ch == '\\') { + if (parser->cur >= parser->end) { + free(buffer); + amduat_json_set_error(parser, "invalid escape"); + return false; + } + ch = *parser->cur++; + switch (ch) { + case '"': + case '\\': + case '/': + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + default: + free(buffer); + amduat_json_set_error(parser, "unsupported escape"); + return false; + } + } + if (len + 1u >= cap) { + size_t next_cap = cap * 2u; + char *next = (char *)realloc(buffer, next_cap); + if (next == NULL) { + free(buffer); + amduat_json_set_error(parser, "out of memory"); + return false; + } + buffer = next; + cap = next_cap; + } + buffer[len++] = ch; + } + + free(buffer); + amduat_json_set_error(parser, "unterminated string"); + return false; +} + +static bool amduat_json_parse_uint(amduat_json_parser_t *parser, + uint32_t *out_value) { + char *endptr; + unsigned long value; + + if (out_value == NULL) { + return false; + } + amduat_json_skip_ws(parser); + if (parser->cur >= parser->end || !isdigit((unsigned char)*parser->cur)) { + amduat_json_set_error(parser, "expected integer"); + return false; + } + + value = strtoul(parser->cur, &endptr, 10); + if (endptr == parser->cur || value > UINT32_MAX) { + amduat_json_set_error(parser, "invalid integer"); + return false; + } + parser->cur = endptr; + *out_value = (uint32_t)value; + return true; +} + +static bool amduat_json_parse_hex_octets(amduat_json_parser_t *parser, + amduat_octets_t *out_octets) { + char *text = NULL; + uint8_t *bytes = NULL; + size_t len = 0u; + + if (out_octets == NULL) { + return false; + } + *out_octets = amduat_octets(NULL, 0u); + + if (!amduat_json_parse_string(parser, &text)) { + return false; + } + if (text[0] == '\0') { + free(text); + return true; + } + if (!amduat_hex_decode_alloc(text, &bytes, &len)) { + free(text); + amduat_json_set_error(parser, "invalid hex"); + return false; + } + free(text); + *out_octets = amduat_octets(bytes, len); + return true; +} + +static bool amduat_json_parse_value_skip(amduat_json_parser_t *parser); + +static bool amduat_json_parse_array_skip(amduat_json_parser_t *parser) { + if (!amduat_json_expect(parser, '[')) { + return false; + } + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ']') { + parser->cur++; + return true; + } + while (parser->cur < parser->end) { + if (!amduat_json_parse_value_skip(parser)) { + return false; + } + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ',') { + parser->cur++; + continue; + } + if (parser->cur < parser->end && *parser->cur == ']') { + parser->cur++; + return true; + } + amduat_json_set_error(parser, "expected ',' or ']'"); + return false; + } + amduat_json_set_error(parser, "unterminated array"); + return false; +} + +static bool amduat_json_parse_object_skip(amduat_json_parser_t *parser) { + if (!amduat_json_expect(parser, '{')) { + return false; + } + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == '}') { + parser->cur++; + return true; + } + while (parser->cur < parser->end) { + char *key = NULL; + if (!amduat_json_parse_string(parser, &key)) { + return false; + } + free(key); + if (!amduat_json_expect(parser, ':')) { + return false; + } + if (!amduat_json_parse_value_skip(parser)) { + return false; + } + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ',') { + parser->cur++; + continue; + } + if (parser->cur < parser->end && *parser->cur == '}') { + parser->cur++; + return true; + } + amduat_json_set_error(parser, "expected ',' or '}'"); + return false; + } + amduat_json_set_error(parser, "unterminated object"); + return false; +} + +static bool amduat_json_parse_value_skip(amduat_json_parser_t *parser) { + amduat_json_skip_ws(parser); + if (parser->cur >= parser->end) { + amduat_json_set_error(parser, "unexpected end of input"); + return false; + } + switch (*parser->cur) { + case '{': + return amduat_json_parse_object_skip(parser); + case '[': + return amduat_json_parse_array_skip(parser); + case '"': { + char *text = NULL; + bool ok = amduat_json_parse_string(parser, &text); + free(text); + return ok; + } + default: + if (isdigit((unsigned char)*parser->cur) || *parser->cur == '-') { + uint32_t value = 0; + return amduat_json_parse_uint(parser, &value); + } + if (strncmp(parser->cur, "true", 4) == 0) { + parser->cur += 4; + return true; + } + if (strncmp(parser->cur, "false", 5) == 0) { + parser->cur += 5; + return true; + } + if (strncmp(parser->cur, "null", 4) == 0) { + parser->cur += 4; + return true; + } + amduat_json_set_error(parser, "unexpected value"); + return false; + } +} + +static void amduat_pel_build_program_free(amduat_pel_program_t *program) { + size_t i; + + if (program == NULL) { + return; + } + if (program->nodes != NULL) { + for (i = 0; i < program->nodes_len; ++i) { + amduat_pel_node_t *node = &program->nodes[i]; + free(node->inputs); + free((void *)node->op.name.data); + free((void *)node->params.data); + } + } + free(program->nodes); + free(program->roots); + memset(program, 0, sizeof(*program)); +} + +static bool amduat_pel_build_append_node(amduat_pel_program_t *program, + amduat_pel_node_t node) { + amduat_pel_node_t *next; + + next = (amduat_pel_node_t *)realloc( + program->nodes, (program->nodes_len + 1u) * sizeof(*next)); + if (next == NULL) { + return false; + } + next[program->nodes_len] = node; + program->nodes = next; + program->nodes_len += 1u; + return true; +} + +static bool amduat_pel_build_append_root(amduat_pel_program_t *program, + amduat_pel_root_ref_t root) { + amduat_pel_root_ref_t *next; + + next = (amduat_pel_root_ref_t *)realloc( + program->roots, (program->roots_len + 1u) * sizeof(*next)); + if (next == NULL) { + return false; + } + next[program->roots_len] = root; + program->roots = next; + program->roots_len += 1u; + return true; +} + +static bool amduat_json_parse_inputs(amduat_json_parser_t *parser, + amduat_pel_dag_input_t **out_inputs, + size_t *out_inputs_len) { + amduat_pel_dag_input_t *inputs = NULL; + size_t inputs_len = 0u; + + if (out_inputs == NULL || out_inputs_len == NULL) { + return false; + } + *out_inputs = NULL; + *out_inputs_len = 0u; + + if (!amduat_json_expect(parser, '[')) { + return false; + } + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ']') { + parser->cur++; + return true; + } + + while (parser->cur < parser->end) { + amduat_pel_dag_input_t input; + bool has_kind = false; + bool has_index = false; + bool has_node = false; + bool has_output = false; + + memset(&input, 0, sizeof(input)); + if (!amduat_json_expect(parser, '{')) { + goto fail; + } + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == '}') { + parser->cur++; + } else { + while (parser->cur < parser->end) { + char *key = NULL; + if (!amduat_json_parse_string(parser, &key)) { + goto fail; + } + if (!amduat_json_expect(parser, ':')) { + free(key); + goto fail; + } + if (strcmp(key, "kind") == 0) { + char *kind = NULL; + if (!amduat_json_parse_string(parser, &kind)) { + free(key); + goto fail; + } + if (strcmp(kind, "external") == 0) { + input.kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + } else if (strcmp(kind, "node") == 0) { + input.kind = AMDUAT_PEL_DAG_INPUT_NODE; + } else { + free(kind); + free(key); + amduat_json_set_error(parser, "invalid input kind"); + goto fail; + } + free(kind); + has_kind = true; + } else if (strcmp(key, "index") == 0) { + uint32_t index = 0; + if (!amduat_json_parse_uint(parser, &index)) { + free(key); + goto fail; + } + input.value.external.input_index = index; + has_index = true; + } else if (strcmp(key, "node") == 0) { + uint32_t node_id = 0; + if (!amduat_json_parse_uint(parser, &node_id)) { + free(key); + goto fail; + } + input.value.node.node_id = node_id; + has_node = true; + } else if (strcmp(key, "output") == 0) { + uint32_t output_index = 0; + if (!amduat_json_parse_uint(parser, &output_index)) { + free(key); + goto fail; + } + input.value.node.output_index = output_index; + has_output = true; + } else { + if (!amduat_json_parse_value_skip(parser)) { + free(key); + goto fail; + } + } + free(key); + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ',') { + parser->cur++; + continue; + } + if (parser->cur < parser->end && *parser->cur == '}') { + parser->cur++; + break; + } + amduat_json_set_error(parser, "expected ',' or '}'"); + goto fail; + } + } + + if (!has_kind) { + amduat_json_set_error(parser, "input missing kind"); + goto fail; + } + if (input.kind == AMDUAT_PEL_DAG_INPUT_EXTERNAL) { + if (!has_index) { + amduat_json_set_error(parser, "external input missing index"); + goto fail; + } + } else { + if (!has_node || !has_output) { + amduat_json_set_error(parser, "node input missing node/output"); + goto fail; + } + } + + { + amduat_pel_dag_input_t *next = (amduat_pel_dag_input_t *)realloc( + inputs, (inputs_len + 1u) * sizeof(*next)); + if (next == NULL) { + amduat_json_set_error(parser, "out of memory"); + goto fail; + } + inputs = next; + inputs[inputs_len++] = input; + } + + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ',') { + parser->cur++; + continue; + } + if (parser->cur < parser->end && *parser->cur == ']') { + parser->cur++; + *out_inputs = inputs; + *out_inputs_len = inputs_len; + return true; + } + amduat_json_set_error(parser, "expected ',' or ']'"); + goto fail; + } + + amduat_json_set_error(parser, "unterminated inputs array"); +fail: + free(inputs); + return false; +} + +static bool amduat_json_parse_nodes(amduat_json_parser_t *parser, + amduat_pel_program_t *program) { + if (!amduat_json_expect(parser, '[')) { + return false; + } + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ']') { + parser->cur++; + return true; + } + + while (parser->cur < parser->end) { + amduat_pel_node_t node; + char *op_text = NULL; + bool has_id = false; + bool has_op = false; + uint32_t version = 1u; + amduat_pel_dag_input_t *inputs = NULL; + size_t inputs_len = 0u; + amduat_octets_t params = amduat_octets(NULL, 0u); + + memset(&node, 0, sizeof(node)); + if (!amduat_json_expect(parser, '{')) { + return false; + } + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == '}') { + parser->cur++; + } else { + while (parser->cur < parser->end) { + char *key = NULL; + if (!amduat_json_parse_string(parser, &key)) { + goto node_fail; + } + if (!amduat_json_expect(parser, ':')) { + free(key); + goto node_fail; + } + if (strcmp(key, "id") == 0) { + uint32_t id = 0; + if (!amduat_json_parse_uint(parser, &id)) { + free(key); + goto node_fail; + } + node.id = id; + has_id = true; + } else if (strcmp(key, "op") == 0) { + if (!amduat_json_parse_string(parser, &op_text)) { + free(key); + goto node_fail; + } + has_op = true; + } else if (strcmp(key, "version") == 0) { + if (!amduat_json_parse_uint(parser, &version)) { + free(key); + goto node_fail; + } + } else if (strcmp(key, "inputs") == 0) { + if (!amduat_json_parse_inputs(parser, &inputs, &inputs_len)) { + free(key); + goto node_fail; + } + } else if (strcmp(key, "params_hex") == 0) { + if (!amduat_json_parse_hex_octets(parser, ¶ms)) { + free(key); + goto node_fail; + } + } else { + if (!amduat_json_parse_value_skip(parser)) { + free(key); + goto node_fail; + } + } + free(key); + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ',') { + parser->cur++; + continue; + } + if (parser->cur < parser->end && *parser->cur == '}') { + parser->cur++; + break; + } + amduat_json_set_error(parser, "expected ',' or '}'"); + goto node_fail; + } + } + + if (!has_id || !has_op) { + amduat_json_set_error(parser, "node missing id or op"); + goto node_fail; + } + if (op_text[0] == '\0') { + amduat_json_set_error(parser, "node op is empty"); + goto node_fail; + } + { + size_t op_len = strlen(op_text); + uint8_t *op_bytes = (uint8_t *)malloc(op_len); + if (op_bytes == NULL) { + amduat_json_set_error(parser, "out of memory"); + goto node_fail; + } + memcpy(op_bytes, op_text, op_len); + node.op.name = amduat_octets(op_bytes, op_len); + } + node.op.version = version; + node.inputs = inputs; + node.inputs_len = inputs_len; + node.params = params; + free(op_text); + + if (!amduat_pel_build_append_node(program, node)) { + amduat_json_set_error(parser, "out of memory"); + goto node_fail; + } + + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ',') { + parser->cur++; + continue; + } + if (parser->cur < parser->end && *parser->cur == ']') { + parser->cur++; + return true; + } + amduat_json_set_error(parser, "expected ',' or ']'"); + return false; + +node_fail: + free(op_text); + free(inputs); + free((void *)params.data); + free((void *)node.op.name.data); + return false; + } + + amduat_json_set_error(parser, "unterminated nodes array"); + return false; +} + +static bool amduat_json_parse_roots(amduat_json_parser_t *parser, + amduat_pel_program_t *program) { + if (!amduat_json_expect(parser, '[')) { + return false; + } + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ']') { + parser->cur++; + return true; + } + + while (parser->cur < parser->end) { + amduat_pel_root_ref_t root; + bool has_node = false; + bool has_output = false; + + memset(&root, 0, sizeof(root)); + if (!amduat_json_expect(parser, '{')) { + return false; + } + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == '}') { + parser->cur++; + } else { + while (parser->cur < parser->end) { + char *key = NULL; + if (!amduat_json_parse_string(parser, &key)) { + goto root_fail; + } + if (!amduat_json_expect(parser, ':')) { + free(key); + goto root_fail; + } + if (strcmp(key, "node") == 0) { + uint32_t node_id = 0; + if (!amduat_json_parse_uint(parser, &node_id)) { + free(key); + goto root_fail; + } + root.node_id = node_id; + has_node = true; + } else if (strcmp(key, "output") == 0) { + uint32_t output_index = 0; + if (!amduat_json_parse_uint(parser, &output_index)) { + free(key); + goto root_fail; + } + root.output_index = output_index; + has_output = true; + } else { + if (!amduat_json_parse_value_skip(parser)) { + free(key); + goto root_fail; + } + } + free(key); + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ',') { + parser->cur++; + continue; + } + if (parser->cur < parser->end && *parser->cur == '}') { + parser->cur++; + break; + } + amduat_json_set_error(parser, "expected ',' or '}'"); + goto root_fail; + } + } + + if (!has_node || !has_output) { + amduat_json_set_error(parser, "root missing node or output"); + goto root_fail; + } + if (!amduat_pel_build_append_root(program, root)) { + amduat_json_set_error(parser, "out of memory"); + goto root_fail; + } + + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ',') { + parser->cur++; + continue; + } + if (parser->cur < parser->end && *parser->cur == ']') { + parser->cur++; + return true; + } + amduat_json_set_error(parser, "expected ',' or ']'"); + return false; + +root_fail: + return false; + } + + amduat_json_set_error(parser, "unterminated roots array"); + return false; +} + +static bool amduat_json_parse_program(amduat_json_parser_t *parser, + amduat_pel_program_t *program) { + bool has_nodes = false; + bool has_roots = false; + + if (!amduat_json_expect(parser, '{')) { + return false; + } + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == '}') { + parser->cur++; + amduat_json_set_error(parser, "program missing nodes"); + return false; + } + + while (parser->cur < parser->end) { + char *key = NULL; + if (!amduat_json_parse_string(parser, &key)) { + return false; + } + if (!amduat_json_expect(parser, ':')) { + free(key); + return false; + } + if (strcmp(key, "nodes") == 0) { + if (!amduat_json_parse_nodes(parser, program)) { + free(key); + return false; + } + has_nodes = true; + } else if (strcmp(key, "roots") == 0) { + if (!amduat_json_parse_roots(parser, program)) { + free(key); + return false; + } + has_roots = true; + } else { + amduat_json_set_error(parser, "unknown top-level key"); + free(key); + return false; + } + free(key); + amduat_json_skip_ws(parser); + if (parser->cur < parser->end && *parser->cur == ',') { + parser->cur++; + continue; + } + if (parser->cur < parser->end && *parser->cur == '}') { + parser->cur++; + break; + } + amduat_json_set_error(parser, "expected ',' or '}'"); + return false; + } + + if (!has_nodes || !has_roots) { + amduat_json_set_error(parser, "program missing nodes or roots"); + return false; + } + return true; +} + +int main(int argc, char **argv) { + amduat_pel_build_opts_t opts; + uint8_t *json_bytes = NULL; + size_t json_len = 0u; + amduat_json_parser_t parser; + amduat_pel_program_t program; + amduat_octets_t encoded = amduat_octets(NULL, 0u); + amduat_octets_t artifact_bytes = amduat_octets(NULL, 0u); + amduat_type_tag_t program_tag = amduat_type_tag(0); + int exit_code = AMDUAT_PEL_BUILD_EXIT_OK; + int i; + + memset(&opts, 0, sizeof(opts)); + opts.input_path = "-"; + opts.output_path = "-"; + opts.output_format = AMDUAT_PEL_BUILD_OUTPUT_RAW; + opts.validate = true; + + for (i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--input") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input requires a path\n"); + return AMDUAT_PEL_BUILD_EXIT_USAGE; + } + opts.input_path = argv[++i]; + } else if (strcmp(argv[i], "--output") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --output requires a path\n"); + return AMDUAT_PEL_BUILD_EXIT_USAGE; + } + opts.output_path = argv[++i]; + } else if (strcmp(argv[i], "--output-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --output-format requires a value\n"); + return AMDUAT_PEL_BUILD_EXIT_USAGE; + } + const char *fmt = argv[++i]; + if (strcmp(fmt, "raw") == 0) { + opts.output_format = AMDUAT_PEL_BUILD_OUTPUT_RAW; + } else if (strcmp(fmt, "artifact") == 0) { + opts.output_format = AMDUAT_PEL_BUILD_OUTPUT_ARTIFACT; + } else { + fprintf(stderr, "error: invalid output-format\n"); + return AMDUAT_PEL_BUILD_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--type-tag") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --type-tag requires a value\n"); + return AMDUAT_PEL_BUILD_EXIT_USAGE; + } + if (!amduat_asl_parse_type_tag(argv[++i], &opts.type_tag)) { + fprintf(stderr, "error: invalid type-tag\n"); + return AMDUAT_PEL_BUILD_EXIT_USAGE; + } + opts.has_type_tag = true; + } else if (strcmp(argv[i], "--no-validate") == 0) { + opts.validate = false; + } else if (strcmp(argv[i], "--quiet") == 0) { + opts.quiet = true; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_pel_build_print_usage(stdout); + return AMDUAT_PEL_BUILD_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_BUILD_EXIT_USAGE; + } + } + + if (!amduat_asl_read_path(opts.input_path, &json_bytes, &json_len)) { + fprintf(stderr, "error: failed to read input: %s\n", opts.input_path); + return AMDUAT_PEL_BUILD_EXIT_IO; + } + + memset(&program, 0, sizeof(program)); + parser.cur = (const char *)json_bytes; + parser.end = (const char *)json_bytes + json_len; + parser.error = NULL; + + if (!amduat_json_parse_program(&parser, &program)) { + size_t offset = (size_t)(parser.cur - (const char *)json_bytes); + fprintf(stderr, "error: invalid json at offset %zu", offset); + if (parser.error != NULL) { + fprintf(stderr, ": %s", parser.error); + } + fprintf(stderr, "\n"); + amduat_pel_build_program_free(&program); + free(json_bytes); + return AMDUAT_PEL_BUILD_EXIT_USAGE; + } + + if (opts.validate && !amduat_pel_program_dag_validate(&program)) { + fprintf(stderr, "error: program validation failed\n"); + amduat_pel_build_program_free(&program); + free(json_bytes); + return AMDUAT_PEL_BUILD_EXIT_CODEC; + } + + if (!amduat_enc_pel_program_dag_encode_v1(&program, &encoded)) { + fprintf(stderr, "error: failed to encode program\n"); + amduat_pel_build_program_free(&program); + free(json_bytes); + return AMDUAT_PEL_BUILD_EXIT_CODEC; + } + + if (opts.output_format == AMDUAT_PEL_BUILD_OUTPUT_ARTIFACT) { + amduat_asl_encoding_profile_id_t program_profile; + if (!amduat_pel_program_dag_desc_get_program_binding(&program_tag, + &program_profile)) { + fprintf(stderr, "error: failed to load program binding\n"); + amduat_pel_build_program_free(&program); + free(json_bytes); + free((void *)encoded.data); + return AMDUAT_PEL_BUILD_EXIT_CODEC; + } + (void)program_profile; + amduat_type_tag_t tag = opts.has_type_tag ? opts.type_tag : program_tag; + amduat_artifact_t artifact = amduat_artifact_with_type(encoded, tag); + if (!amduat_enc_asl1_core_encode_artifact_v1(artifact, &artifact_bytes)) { + fprintf(stderr, "error: failed to encode artifact\n"); + exit_code = AMDUAT_PEL_BUILD_EXIT_CODEC; + goto cleanup; + } + if (!amduat_asl_write_path(opts.output_path, + artifact_bytes.data, + artifact_bytes.len)) { + fprintf(stderr, "error: failed to write output: %s\n", + opts.output_path); + exit_code = AMDUAT_PEL_BUILD_EXIT_IO; + goto cleanup; + } + } else { + if (!amduat_asl_write_path(opts.output_path, + encoded.data, + encoded.len)) { + fprintf(stderr, "error: failed to write output: %s\n", + opts.output_path); + exit_code = AMDUAT_PEL_BUILD_EXIT_IO; + goto cleanup; + } + } + + if (!opts.quiet) { + fprintf(stderr, "nodes=%zu\n", program.nodes_len); + fprintf(stderr, "roots=%zu\n", program.roots_len); + fprintf(stderr, "bytes=%zu\n", encoded.len); + if (opts.output_format == AMDUAT_PEL_BUILD_OUTPUT_ARTIFACT) { + amduat_type_tag_t tag = opts.has_type_tag ? opts.type_tag : program_tag; + fprintf(stderr, "type_tag=0x%08" PRIx32 "\n", tag.tag_id); + } + } + +cleanup: + free((void *)artifact_bytes.data); + free((void *)encoded.data); + amduat_pel_build_program_free(&program); + free(json_bytes); + return exit_code; +} diff --git a/src/tools/amduat_pel_cli.c b/src/tools/amduat_pel_cli.c index f153f1d..ba0b5aa 100644 --- a/src/tools/amduat_pel_cli.c +++ b/src/tools/amduat_pel_cli.c @@ -1,4 +1,5 @@ #include "amduat/asl/artifact_io.h" +#include "amduat/asl/asl_derivation_index_fs.h" #include "amduat/asl/asl_store_fs.h" #include "amduat/asl/asl_store_fs_meta.h" #include "amduat/asl/io.h" @@ -79,6 +80,7 @@ static void amduat_pel_cli_print_usage(FILE *stream) { " [--input-ref REF ...] [--inputs-file PATH]\n" " [--params-ref REF]\n" " [--format text|json] [--ref-format hex|bytes]\n" + " [--no-index]\n" " Output: result_ref, trace_ref, output_refs (formatted per --format).\n" "\n" "exec:\n" @@ -94,12 +96,14 @@ static void amduat_pel_cli_print_usage(FILE *stream) { " [--input-format raw|artifact]\n" " [--expect-type-tag TAG]\n" " [--format text|json]\n" + " [--ref-format hex|bytes] [--root PATH]\n" "\n" "program:\n" " amduat-pel program decode [--input PATH|- | --program-ref REF]\n" " [--input-format raw|artifact]\n" " [--expect-type-tag TAG]\n" " [--format text|json]\n" + " [--ref-format hex|bytes] [--root PATH]\n" " amduat-pel program normalize --input PATH|- [--input-format raw|artifact]\n" " [--output PATH|-] [--output-format raw|artifact]\n" " [--type-tag TAG]\n" @@ -109,14 +113,17 @@ static void amduat_pel_cli_print_usage(FILE *stream) { " [--input-format raw|artifact]\n" " [--expect-type-tag TAG]\n" " [--format text|json]\n" + " [--ref-format hex|bytes] [--root PATH]\n" " amduat-pel trace from-result --result-ref REF [--root PATH]\n" " [--format text|json]\n" + " [--ref-format hex|bytes]\n" "\n" "result:\n" " amduat-pel result decode [--input PATH|- | --result-ref REF]\n" " [--input-format raw|artifact]\n" " [--expect-type-tag TAG]\n" " [--format text|json]\n" + " [--ref-format hex|bytes] [--root PATH]\n" "\n" "op:\n" " amduat-pel op list [--format text|json]\n" @@ -125,7 +132,7 @@ static void amduat_pel_cli_print_usage(FILE *stream) { " --input PATH|- [--format text|json]\n" "\n" "scheme:\n" - " amduat-pel scheme show [--format text|json]\n" + " amduat-pel scheme show [--format text|json] [--ref-format hex|bytes]\n" " amduat-pel scheme dag-ref [--format hex|bytes]\n" "\n" "defaults:\n" @@ -135,15 +142,18 @@ static void amduat_pel_cli_print_usage(FILE *stream) { " --output -\n" " --input-format raw\n" " --output-format raw\n" + " --output-dir .\n" " --scheme-ref dag\n" + " --result-out -\n" "\n" "notes:\n" " - Only PEL/PROGRAM-DAG/1 is supported; other scheme refs yield SCHEME_UNSUPPORTED.\n" " - Expected type tags: program 0x00000101, trace 0x00000102, result 0x00000103.\n" " - Encoding profile IDs: program 0x0101, trace 0x0102, result 0x0103.\n" - " - Kernel ops: pel.bytes.concat, pel.bytes.slice, pel.bytes.const, pel.bytes.hash.asl1, pel.format.encode.\n" + " - Kernel ops: pel.bytes.concat, pel.bytes.slice, pel.bytes.const, pel.bytes.hash.asl1, pel.bytes.params, pel.format.encode.\n" " - --ref-format bytes expects REF arguments to be a path or '-' (raw reference bytes).\n" - " - --inputs-file with --ref-format bytes expects raw concatenated references.\n"); + " - --inputs-file with --ref-format bytes expects raw concatenated references.\n" + " - run stores derivation records for outputs; use --no-index to skip.\n"); } static const char *amduat_pel_cli_store_error_str( @@ -322,6 +332,69 @@ static bool amduat_pel_cli_append_refs_from_file( return true; } +static bool amduat_pel_cli_index_derivations( + const char *root, + amduat_reference_t program_ref, + const amduat_reference_t *input_refs, + size_t input_refs_len, + bool has_params_ref, + amduat_reference_t params_ref, + const amduat_reference_t *output_refs, + size_t output_refs_len, + bool quiet) { + amduat_asl_derivation_index_fs_t index; + size_t i; + + if (output_refs_len == 0u || output_refs == NULL) { + return true; + } + if (!amduat_asl_derivation_index_fs_init(&index, root)) { + if (!quiet) { + fprintf(stderr, "error: failed to init derivation index\n"); + } + return false; + } + + for (i = 0u; i < output_refs_len; ++i) { + amduat_asl_derivation_record_t record; + amduat_octets_t sid = amduat_octets(NULL, 0u); + amduat_asl_store_error_t err; + + if (!amduat_octets_clone(output_refs[i].digest, &sid)) { + if (!quiet) { + fprintf(stderr, "error: failed to allocate derivation sid\n"); + } + return false; + } + + memset(&record, 0, sizeof(record)); + record.sid = sid; + record.program_ref = program_ref; + record.output_index = (uint32_t)i; + record.input_refs = (amduat_reference_t *)input_refs; + record.input_refs_len = input_refs_len; + record.has_params_ref = has_params_ref; + if (has_params_ref) { + record.params_ref = params_ref; + } + record.has_exec_profile = false; + record.exec_profile = amduat_octets(NULL, 0u); + + err = amduat_asl_derivation_index_fs_add(&index, + output_refs[i], + &record); + amduat_octets_free(&record.sid); + if (err != AMDUAT_ASL_STORE_OK) { + if (!quiet) { + fprintf(stderr, "error: derivation index add failed: %d\n", err); + } + return false; + } + } + + return true; +} + static int amduat_pel_cli_cmd_run(int argc, char **argv, const amduat_pel_cli_global_opts_t *global) { @@ -348,6 +421,7 @@ static int amduat_pel_cli_cmd_run(int argc, amduat_asl_store_fs_t fs; amduat_asl_store_t store; bool stdin_used = false; + bool no_index = false; int exit_code = AMDUAT_PEL_CLI_EXIT_OK; int i; @@ -425,6 +499,8 @@ static int amduat_pel_cli_cmd_run(int argc, return AMDUAT_PEL_CLI_EXIT_USAGE; } root = argv[++i]; + } else if (strcmp(argv[i], "--no-index") == 0) { + no_index = true; } else if (strcmp(argv[i], "--quiet") == 0) { quiet = true; } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { @@ -639,6 +715,22 @@ static int amduat_pel_cli_cmd_run(int argc, fputs("]}\n", stdout); } + if (!no_index && exit_code == AMDUAT_PEL_CLI_EXIT_OK && + run_result.has_result_value && + run_result.result_value.core_result.status == AMDUAT_PEL_EXEC_STATUS_OK) { + if (!amduat_pel_cli_index_derivations(root, + program_ref, + input_refs, + input_refs_len, + has_params_ref, + params_ref, + run_result.output_refs, + run_result.output_refs_len, + quiet)) { + exit_code = AMDUAT_PEL_CLI_EXIT_STORE; + } + } + run_cleanup: if (run_result.has_result_value) { amduat_enc_pel1_result_free(&run_result.result_value); diff --git a/src/tools/amduat_pel_run.c b/src/tools/amduat_pel_run.c index 4caa4d8..a1cbea5 100644 --- a/src/tools/amduat_pel_run.c +++ b/src/tools/amduat_pel_run.c @@ -53,7 +53,8 @@ static void amduat_pel_run_print_usage(FILE *stream) { "\n" "notes:\n" " REF values are ASL reference hex strings.\n" - " --output-raw writes output bytes to stdout and exec result to stderr.\n", + " --output-raw writes output bytes to stdout and exec result to stderr.\n" + " Uses the PEL/PROGRAM-DAG/1 scheme ref.\n", AMDUAT_PEL_RUN_DEFAULT_ROOT); } diff --git a/src/tools/amduat_pel_seed.c b/src/tools/amduat_pel_seed.c index 2f7f33c..8773af6 100644 --- a/src/tools/amduat_pel_seed.c +++ b/src/tools/amduat_pel_seed.c @@ -69,7 +69,10 @@ static void amduat_pel_seed_print_usage(FILE *stream) { " --root %s\n" " --seed const\n" " --ref-format hex\n" - " --output -\n", + " --output -\n" + "\n" + "notes:\n" + " --list prints available seeds and exits\n", AMDUAT_PEL_SEED_DEFAULT_ROOT); } diff --git a/tests/asl/test_asl_derivation_index_fs.c b/tests/asl/test_asl_derivation_index_fs.c index d45d75d..666ddd8 100644 --- a/tests/asl/test_asl_derivation_index_fs.c +++ b/tests/asl/test_asl_derivation_index_fs.c @@ -100,6 +100,7 @@ static int test_round_trip(void) { artifact_ref = make_ref(0x10, 4); record.program_ref = make_ref(0x20, 4); + record.output_index = 0u; inputs[0] = make_ref(0x30, 4); inputs[1] = make_ref(0x40, 4); memset(sid_bytes, 0xa5, sizeof(sid_bytes)); @@ -113,6 +114,7 @@ static int test_round_trip(void) { record2 = record; record2.sid = amduat_octets(sid_bytes, sizeof(sid_bytes)); + record2.output_index = 1u; record2.has_params_ref = false; record2.has_exec_profile = false; @@ -147,12 +149,14 @@ static int test_round_trip(void) { } if (records[0].input_refs_len != 2u || records[0].has_params_ref != true || - records[0].has_exec_profile != true) { + records[0].has_exec_profile != true || + records[0].output_index != 0u) { fprintf(stderr, "record fields mismatch\n"); goto cleanup_records_list; } if (records[1].has_params_ref != false || - records[1].has_exec_profile != false) { + records[1].has_exec_profile != false || + records[1].output_index != 1u) { fprintf(stderr, "record2 flags mismatch\n"); goto cleanup_records_list; } diff --git a/tests/pel/test_pel_program_build_concat.c b/tests/pel/test_pel_program_build_concat.c new file mode 100644 index 0000000..5366ed7 --- /dev/null +++ b/tests/pel/test_pel_program_build_concat.c @@ -0,0 +1,348 @@ +#include "amduat/asl/ref_derive.h" +#include "amduat/asl/store.h" +#include "amduat/enc/asl1_core.h" +#include "amduat/enc/pel1_result.h" +#include "amduat/enc/pel_program_dag.h" +#include "amduat/hash/asl1.h" +#include "amduat/pel/program_dag.h" +#include "amduat/pel/program_dag_desc.h" +#include "amduat/pel/run.h" + +#include +#include +#include +#include +#include + +typedef struct { + amduat_reference_t ref; + amduat_artifact_t artifact; +} stub_store_entry_t; + +typedef struct { + stub_store_entry_t *entries; + size_t len; + size_t cap; + amduat_asl_store_config_t config; +} stub_store_t; + +static void stub_store_init(stub_store_t *store) { + if (store == NULL) { + return; + } + store->entries = NULL; + store->len = 0; + store->cap = 0; + store->config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1; + store->config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256; +} + +static void stub_store_free(stub_store_t *store) { + size_t i; + + if (store == NULL) { + return; + } + for (i = 0; i < store->len; ++i) { + stub_store_entry_t *entry = &store->entries[i]; + free((void *)entry->ref.digest.data); + entry->ref.digest.data = NULL; + entry->ref.digest.len = 0; + free((void *)entry->artifact.bytes.data); + entry->artifact.bytes.data = NULL; + entry->artifact.bytes.len = 0; + } + free(store->entries); + store->entries = NULL; + store->len = 0; + store->cap = 0; +} + +static bool stub_store_add_entry(stub_store_t *store, + amduat_reference_t stored_ref, + amduat_artifact_t stored_artifact) { + stub_store_entry_t *entries; + size_t new_cap; + + if (store->len == store->cap) { + new_cap = store->cap == 0 ? 8 : store->cap * 2; + entries = (stub_store_entry_t *)realloc(store->entries, + new_cap * sizeof(*entries)); + if (entries == NULL) { + return false; + } + store->entries = entries; + store->cap = new_cap; + } + + store->entries[store->len++] = + (stub_store_entry_t){stored_ref, stored_artifact}; + return true; +} + +static amduat_asl_store_error_t stub_store_put( + void *ctx, + amduat_artifact_t artifact, + amduat_reference_t *out_ref) { + stub_store_t *store; + amduat_reference_t derived_ref; + amduat_reference_t stored_ref; + amduat_artifact_t stored_artifact; + size_t i; + + if (ctx == NULL || out_ref == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + store = (stub_store_t *)ctx; + out_ref->hash_id = 0; + out_ref->digest = amduat_octets(NULL, 0); + + derived_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + if (!amduat_asl_ref_derive(artifact, + store->config.encoding_profile_id, + store->config.hash_id, + &derived_ref, + NULL)) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + *out_ref = derived_ref; + + for (i = 0; i < store->len; ++i) { + if (amduat_reference_eq(store->entries[i].ref, *out_ref)) { + return AMDUAT_ASL_STORE_OK; + } + } + + if (!amduat_reference_clone(*out_ref, &stored_ref)) { + amduat_reference_free(out_ref); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + stored_artifact.bytes = amduat_octets(NULL, 0u); + if (artifact.bytes.len != 0) { + uint8_t *payload = (uint8_t *)malloc(artifact.bytes.len); + if (payload == NULL) { + amduat_reference_free(out_ref); + amduat_reference_free(&stored_ref); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + memcpy(payload, artifact.bytes.data, artifact.bytes.len); + stored_artifact.bytes = amduat_octets(payload, artifact.bytes.len); + } + stored_artifact.has_type_tag = artifact.has_type_tag; + stored_artifact.type_tag = artifact.type_tag; + + if (!stub_store_add_entry(store, stored_ref, stored_artifact)) { + amduat_reference_free(out_ref); + amduat_reference_free(&stored_ref); + free((void *)stored_artifact.bytes.data); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + return AMDUAT_ASL_STORE_OK; +} + +static amduat_asl_store_error_t stub_store_get( + void *ctx, + amduat_reference_t ref, + amduat_artifact_t *out_artifact) { + stub_store_t *store; + size_t i; + + if (ctx == NULL || out_artifact == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + store = (stub_store_t *)ctx; + out_artifact->bytes = amduat_octets(NULL, 0u); + out_artifact->has_type_tag = false; + out_artifact->type_tag = amduat_type_tag(0); + + for (i = 0; i < store->len; ++i) { + stub_store_entry_t *entry = &store->entries[i]; + if (amduat_reference_eq(entry->ref, ref)) { + uint8_t *payload = NULL; + if (entry->artifact.bytes.len != 0u) { + payload = (uint8_t *)malloc(entry->artifact.bytes.len); + if (payload == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + memcpy(payload, entry->artifact.bytes.data, + entry->artifact.bytes.len); + } + out_artifact->bytes = amduat_octets(payload, + entry->artifact.bytes.len); + out_artifact->has_type_tag = entry->artifact.has_type_tag; + out_artifact->type_tag = entry->artifact.type_tag; + return AMDUAT_ASL_STORE_OK; + } + } + return AMDUAT_ASL_STORE_ERR_NOT_FOUND; +} + +static int test_program_concat_two_inputs(void) { + stub_store_t stub; + amduat_asl_store_ops_t ops; + amduat_asl_store_t store; + amduat_pel_program_t program; + amduat_pel_node_t *nodes; + amduat_pel_dag_input_t *inputs; + amduat_pel_root_ref_t *roots; + amduat_octets_t encoded; + amduat_type_tag_t program_tag; + amduat_asl_encoding_profile_id_t program_profile; + amduat_artifact_t program_artifact; + amduat_reference_t program_ref; + amduat_artifact_t input_artifacts[2]; + amduat_reference_t input_refs[2]; + amduat_pel_run_result_t run_result; + amduat_artifact_t output_artifact; + int exit_code = 0; + + stub_store_init(&stub); + amduat_asl_store_ops_init(&ops); + ops.put = stub_store_put; + ops.get = stub_store_get; + amduat_asl_store_init(&store, stub.config, ops, &stub); + + nodes = (amduat_pel_node_t *)calloc(1u, sizeof(*nodes)); + inputs = (amduat_pel_dag_input_t *)calloc(2u, sizeof(*inputs)); + roots = (amduat_pel_root_ref_t *)calloc(1u, sizeof(*roots)); + if (nodes == NULL || inputs == NULL || roots == NULL) { + fprintf(stderr, "out of memory\n"); + exit_code = 1; + goto cleanup; + } + + inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + inputs[0].value.external.input_index = 0u; + inputs[1].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + inputs[1].value.external.input_index = 1u; + + nodes[0].id = 1u; + nodes[0].op.name = + amduat_octets("pel.bytes.concat", strlen("pel.bytes.concat")); + nodes[0].op.version = 1u; + nodes[0].inputs = inputs; + nodes[0].inputs_len = 2u; + nodes[0].params = amduat_octets(NULL, 0u); + + roots[0].node_id = 1u; + roots[0].output_index = 0u; + + program.nodes = nodes; + program.nodes_len = 1u; + program.roots = roots; + program.roots_len = 1u; + + if (!amduat_pel_program_dag_validate(&program)) { + fprintf(stderr, "program validation failed\n"); + exit_code = 1; + goto cleanup; + } + + encoded = amduat_octets(NULL, 0u); + if (!amduat_enc_pel_program_dag_encode_v1(&program, &encoded)) { + fprintf(stderr, "program encode failed\n"); + exit_code = 1; + goto cleanup; + } + + if (!amduat_pel_program_dag_desc_get_program_binding(&program_tag, + &program_profile)) { + fprintf(stderr, "program binding failed\n"); + free((void *)encoded.data); + exit_code = 1; + goto cleanup; + } + (void)program_profile; + + program_artifact = amduat_artifact_with_type(encoded, program_tag); + program_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + if (amduat_asl_store_put(&store, program_artifact, + &program_ref) != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "store put program failed\n"); + free((void *)encoded.data); + exit_code = 1; + goto cleanup; + } + free((void *)encoded.data); + + input_artifacts[0] = + amduat_artifact(amduat_octets("foo", 3u)); + input_artifacts[1] = + amduat_artifact(amduat_octets("bar", 3u)); + + input_refs[0] = amduat_reference(0u, amduat_octets(NULL, 0u)); + input_refs[1] = amduat_reference(0u, amduat_octets(NULL, 0u)); + if (amduat_asl_store_put(&store, input_artifacts[0], + &input_refs[0]) != AMDUAT_ASL_STORE_OK || + amduat_asl_store_put(&store, input_artifacts[1], + &input_refs[1]) != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "store put inputs failed\n"); + exit_code = 1; + goto cleanup; + } + + memset(&run_result, 0, sizeof(run_result)); + if (!amduat_pel_surf_run_with_result( + &store, + amduat_pel_program_dag_scheme_ref(), + program_ref, + input_refs, + 2u, + false, + amduat_reference(0u, amduat_octets(NULL, 0u)), + &run_result)) { + fprintf(stderr, "surf run failed\n"); + exit_code = 1; + goto cleanup; + } + + if (!run_result.has_result_value || + run_result.result_value.core_result.status != AMDUAT_PEL_EXEC_STATUS_OK) { + fprintf(stderr, "execution status not OK\n"); + exit_code = 1; + goto cleanup_run; + } + if (run_result.output_refs_len != 1u) { + fprintf(stderr, "unexpected output_refs_len=%zu\n", + run_result.output_refs_len); + exit_code = 1; + goto cleanup_run; + } + + memset(&output_artifact, 0, sizeof(output_artifact)); + if (amduat_asl_store_get(&store, + run_result.output_refs[0], + &output_artifact) != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "store get output failed\n"); + exit_code = 1; + goto cleanup_run; + } + if (output_artifact.bytes.len != 6u || + memcmp(output_artifact.bytes.data, "foobar", 6u) != 0) { + fprintf(stderr, "unexpected output bytes\n"); + exit_code = 1; + } + free((void *)output_artifact.bytes.data); + +cleanup_run: + if (run_result.has_result_value) { + amduat_enc_pel1_result_free(&run_result.result_value); + } + if (run_result.output_refs != NULL) { + amduat_pel_surf_free_refs(run_result.output_refs, + run_result.output_refs_len); + } + amduat_pel_surf_free_ref(&run_result.result_ref); + +cleanup: + free(nodes); + free(inputs); + free(roots); + stub_store_free(&stub); + return exit_code; +} + +int main(void) { + return test_program_concat_two_inputs(); +}