diff --git a/docs/spec-clarifications.md b/docs/spec-clarifications.md index b96995e..330cf01 100644 --- a/docs/spec-clarifications.md +++ b/docs/spec-clarifications.md @@ -174,3 +174,28 @@ Concrete rules: References: - `tier1/srs.md` - `tier1/enc-fer1-receipt-1.md` + +## FER/1 v1.1 Encoding Notes (Implementation) + +Decision: +- The v1.1 encoder appends a TLV extension block after the v1 base layout. +- Unknown or duplicate TLV tags are rejected during decode. + +TLV tags (implementation): +- `0x0001` executor fingerprint reference (encoded reference bytes). +- `0x0002` run id (`U32` length + bytes). +- `0x0003` logs (`U32` count; per entry: `U32 kind`, encoded ref, `U32` sha256 + length + bytes). Entries must be ordered by `(kind, ref)` byte order. +- `0x0004` limits (`U64` cpu_ms, `U64` wall_ms, `U64` max_rss_kib, + `U64` io_reads, `U64` io_writes). +- `0x0005` determinism (`U8` level, `U32` seed_len + seed bytes). +- `0x0006` signature (opaque bytes). + +Helper usage: +- `amduat_fer1_receipt_from_pel_run_v1_1` emits v1.1 receipts and uses the + same output_ref fallback as v1: when no outputs exist, `output_ref` is the + stored PEL result reference. + +References: +- `include/amduat/enc/fer1_receipt.h` +- `src/near_core/enc/fer1_receipt.c` diff --git a/tests/enc/test_fer1_receipt.c b/tests/enc/test_fer1_receipt.c index bc63663..36c375d 100644 --- a/tests/enc/test_fer1_receipt.c +++ b/tests/enc/test_fer1_receipt.c @@ -748,6 +748,154 @@ cleanup: return exit_code; } +static int test_receipt_helper_v1_1(void) { + amduat_pel_run_result_t pel_run; + amduat_artifact_t artifact; + amduat_fer1_receipt_t decoded; + amduat_fer1_log_entry_t log_entry; + amduat_fer1_limits_t limits; + uint8_t f0[32], i0[32], e0[32], o0[32], ex0[32], fp0[32], lr0[32]; + uint8_t run_id[] = {0x01, 0x02, 0x03, 0x04}; + uint8_t rng_seed[] = {0x09, 0x08, 0x07}; + uint8_t signature[] = {0xde, 0xad, 0xbe, 0xef}; + uint8_t digest0[] = {0xaa, 0xbb, 0xcc}; + int exit_code = 1; + + memset(&pel_run, 0, sizeof(pel_run)); + pel_run.result_ref = make_ref(0x77, o0); + pel_run.output_refs = &pel_run.result_ref; + pel_run.output_refs_len = 1; + pel_run.has_result_value = true; + pel_run.result_value.pel1_version = 1; + pel_run.result_value.program_ref = make_ref(0x11, f0); + + memset(&limits, 0, sizeof(limits)); + limits.cpu_ms = 1; + limits.wall_ms = 2; + limits.max_rss_kib = 3; + limits.io_reads = 4; + limits.io_writes = 5; + + memset(&log_entry, 0, sizeof(log_entry)); + log_entry.kind = 1; + log_entry.log_ref = make_ref(0x70, lr0); + log_entry.sha256 = amduat_octets(digest0, sizeof(digest0)); + + if (!amduat_fer1_receipt_from_pel_run_v1_1( + &pel_run, + make_ref(0x22, i0), + make_ref(0x33, e0), + amduat_octets("tester", 6), + make_ref(0x50, ex0), + false, + amduat_reference(0, amduat_octets(NULL, 0)), + amduat_octets(NULL, 0), + 10, + 20, + true, + make_ref(0x66, fp0), + true, + amduat_octets(run_id, sizeof(run_id)), + true, + limits, + &log_entry, + 1, + true, + 2, + true, + amduat_octets(rng_seed, sizeof(rng_seed)), + true, + amduat_octets(signature, sizeof(signature)), + &artifact)) { + fprintf(stderr, "v1.1 helper failed\n"); + return exit_code; + } + + if (!amduat_enc_fer1_receipt_decode_v1_1(artifact.bytes, &decoded)) { + fprintf(stderr, "v1.1 helper decode failed\n"); + amduat_artifact_free(&artifact); + return exit_code; + } + + if (!decoded.has_run_id || !decoded.has_limits || !decoded.has_determinism || + !decoded.has_signature || + !amduat_reference_eq(decoded.output_ref, pel_run.result_ref)) { + fprintf(stderr, "v1.1 helper decoded fields mismatch\n"); + goto cleanup_decoded; + } + + exit_code = 0; + +cleanup_decoded: + amduat_enc_fer1_receipt_free(&decoded); + amduat_artifact_free(&artifact); + return exit_code; +} + +static int test_receipt_helper_v1_1_failed_run(void) { + amduat_pel_run_result_t pel_run; + amduat_artifact_t artifact; + amduat_fer1_receipt_t decoded; + uint8_t f0[32], i0[32], e0[32], r0[32], ex0[32]; + int exit_code = 1; + + memset(&pel_run, 0, sizeof(pel_run)); + pel_run.result_ref = make_ref(0x77, r0); + pel_run.output_refs = NULL; + pel_run.output_refs_len = 0; + pel_run.has_result_value = true; + pel_run.result_value.pel1_version = 1; + pel_run.result_value.program_ref = make_ref(0x11, f0); + + if (!amduat_fer1_receipt_from_pel_run_v1_1( + &pel_run, + make_ref(0x22, i0), + make_ref(0x33, e0), + amduat_octets("tester", 6), + make_ref(0x50, ex0), + false, + amduat_reference(0, amduat_octets(NULL, 0)), + amduat_octets(NULL, 0), + 10, + 20, + false, + amduat_reference(0, amduat_octets(NULL, 0)), + false, + amduat_octets(NULL, 0), + false, + (amduat_fer1_limits_t){0}, + NULL, + 0, + false, + 0, + false, + amduat_octets(NULL, 0), + false, + amduat_octets(NULL, 0), + &artifact)) { + fprintf(stderr, "v1.1 failed run helper failed\n"); + return exit_code; + } + + if (!amduat_enc_fer1_receipt_decode_v1_1(artifact.bytes, &decoded)) { + fprintf(stderr, "v1.1 failed run helper decode failed\n"); + amduat_artifact_free(&artifact); + return exit_code; + } + + if (!amduat_reference_eq(decoded.output_ref, pel_run.result_ref)) { + fprintf(stderr, "v1.1 failed run output_ref mismatch\n"); + goto cleanup_decoded; + } + + exit_code = 0; + +cleanup_decoded: + amduat_enc_fer1_receipt_free(&decoded); + amduat_artifact_free(&artifact); + return exit_code; +} + int main(void) { if (test_receipt_round_trip() != 0) { return 1; @@ -767,5 +915,11 @@ int main(void) { if (test_receipt_v1_1_reject_duplicate_tag() != 0) { return 1; } + if (test_receipt_helper_v1_1() != 0) { + return 1; + } + if (test_receipt_helper_v1_1_failed_run() != 0) { + return 1; + } return 0; }