Document FER/1 v1.1 TLVs and test helpers

This commit is contained in:
Carl Niklas Rydberg 2026-01-17 21:38:04 +01:00
parent b506cc6c7c
commit 4d2fb250cd
2 changed files with 179 additions and 0 deletions

View file

@ -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`

View file

@ -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;
}