Add PEL program authoring endpoint and UI
This commit is contained in:
parent
2ec2055826
commit
eb355613d5
19
README.md
19
README.md
|
|
@ -43,6 +43,14 @@ Query store meta:
|
||||||
curl --unix-socket amduatd.sock http://localhost/v1/meta
|
curl --unix-socket amduatd.sock http://localhost/v1/meta
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Browser UI (use `socat` to forward the Unix socket):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
socat TCP-LISTEN:8080,fork,reuseaddr UNIX-CONNECT:amduatd.sock
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open `http://localhost:8080/v1/ui`.
|
||||||
|
|
||||||
Discover the store-backed API contract ref:
|
Discover the store-backed API contract ref:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
@ -84,6 +92,14 @@ curl --unix-socket amduatd.sock -X POST http://localhost/v1/pel/run \
|
||||||
-d '{"program_ref":"<program_ref>","input_refs":["<input_ref_0>"],"params_ref":"<params_ref>"}'
|
-d '{"program_ref":"<program_ref>","input_refs":["<input_ref_0>"],"params_ref":"<params_ref>"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Define a PEL/PROGRAM-DAG/1 program (store-backed):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --unix-socket amduatd.sock -X POST http://localhost/v1/pel/programs \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"nodes":[{"id":1,"op":{"name":"pel.bytes.concat","version":1},"inputs":[{"external":{"input_index":0}},{"external":{"input_index":1}}],"params_hex":""}],"roots":[{"node_id":1,"output_index":0}]}'
|
||||||
|
```
|
||||||
|
|
||||||
## Current endpoints
|
## Current endpoints
|
||||||
|
|
||||||
- `GET /v1/meta` → `{store_id, encoding_profile_id, hash_id, api_contract_ref}`
|
- `GET /v1/meta` → `{store_id, encoding_profile_id, hash_id, api_contract_ref}`
|
||||||
|
|
@ -98,6 +114,9 @@ curl --unix-socket amduatd.sock -X POST http://localhost/v1/pel/run \
|
||||||
- `POST /v1/pel/run`
|
- `POST /v1/pel/run`
|
||||||
- request: `{program_ref, input_refs[], params_ref?, scheme_ref?}` (refs are hex strings; omit `scheme_ref` to use `dag`)
|
- request: `{program_ref, input_refs[], params_ref?, scheme_ref?}` (refs are hex strings; omit `scheme_ref` to use `dag`)
|
||||||
- response: `{result_ref, trace_ref?, output_refs[], status}`
|
- response: `{result_ref, trace_ref?, output_refs[], status}`
|
||||||
|
- `POST /v1/pel/programs`
|
||||||
|
- request: authoring JSON for `PEL/PROGRAM-DAG/1` (kernel ops only; `params_hex` is raw hex bytes)
|
||||||
|
- response: `{program_ref}`
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1 @@
|
||||||
{"contract":"AMDUATD/API/1","base_path":"/v1","endpoints":[{"method":"GET","path":"/v1/meta"},{"method":"HEAD","path":"/v1/meta"},{"method":"GET","path":"/v1/contract"},{"method":"POST","path":"/v1/artifacts"},{"method":"GET","path":"/v1/artifacts/{ref}"},{"method":"HEAD","path":"/v1/artifacts/{ref}"},{"method":"POST","path":"/v1/pel/run"}],"schemas":{"pel_run_request":{"type":"object","required":["program_ref","input_refs"],"properties":{"program_ref":{"type":"string","description":"hex ref"},"input_refs":{"type":"array","items":{"type":"string","description":"hex ref"}},"params_ref":{"type":"string","description":"hex ref"},"scheme_ref":{"type":"string","description":"hex ref or 'dag'"}}},"pel_run_response":{"type":"object","required":["result_ref","output_refs","status"],"properties":{"result_ref":{"type":"string","description":"hex ref"},"trace_ref":{"type":"string","description":"hex ref"},"output_refs":{"type":"array","items":{"type":"string","description":"hex ref"}},"status":{"type":"string"}}}}}
|
{"contract":"AMDUATD/API/1","base_path":"/v1","endpoints":[{"method":"GET","path":"/v1/meta"},{"method":"HEAD","path":"/v1/meta"},{"method":"GET","path":"/v1/contract"},{"method":"POST","path":"/v1/artifacts"},{"method":"GET","path":"/v1/artifacts/{ref}"},{"method":"HEAD","path":"/v1/artifacts/{ref}"},{"method":"POST","path":"/v1/pel/run"},{"method":"POST","path":"/v1/pel/programs"}],"schemas":{"pel_run_request":{"type":"object","required":["program_ref","input_refs"],"properties":{"program_ref":{"type":"string","description":"hex ref"},"input_refs":{"type":"array","items":{"type":"string","description":"hex ref"}},"params_ref":{"type":"string","description":"hex ref"},"scheme_ref":{"type":"string","description":"hex ref or 'dag'"}}},"pel_run_response":{"type":"object","required":["result_ref","output_refs","status"],"properties":{"result_ref":{"type":"string","description":"hex ref"},"trace_ref":{"type":"string","description":"hex ref"},"output_refs":{"type":"array","items":{"type":"string","description":"hex ref"}},"status":{"type":"string"}}},"pel_program_author_request":{"type":"object","required":["nodes","roots"],"properties":{"nodes":{"type":"array"},"roots":{"type":"array"}}}}}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
{"registry":"AMDUATD/API","contract":"AMDUATD/API/1","handle":"amduat.api.amduatd.contract.v1@1","media_type":"application/json","status":"active","bytes_sha256":"9bc4d4c8cf9512afebfb4b6674c58c93d6f47831e948f1795804a27872556263","notes":"Seeded into the ASL store at amduatd startup; ref is advertised via /v1/meta."}
|
{"registry":"AMDUATD/API","contract":"AMDUATD/API/1","handle":"amduat.api.amduatd.contract.v1@1","media_type":"application/json","status":"active","bytes_sha256":"7c15cf17844d0b4637f4c164088a260ab898f235643659bc8fd5230a6b5745ee","notes":"Seeded into the ASL store at amduatd startup; ref is advertised via /v1/meta."}
|
||||||
|
|
|
||||||
918
src/amduatd.c
918
src/amduatd.c
|
|
@ -8,9 +8,13 @@
|
||||||
#include "amduat/asl/ref_derive.h"
|
#include "amduat/asl/ref_derive.h"
|
||||||
#include "amduat/enc/asl1_core_codec.h"
|
#include "amduat/enc/asl1_core_codec.h"
|
||||||
#include "amduat/enc/pel1_result.h"
|
#include "amduat/enc/pel1_result.h"
|
||||||
|
#include "amduat/enc/pel_program_dag.h"
|
||||||
#include "amduat/format/pel.h"
|
#include "amduat/format/pel.h"
|
||||||
|
#include "amduat/pel/program_dag.h"
|
||||||
#include "amduat/pel/program_dag_desc.h"
|
#include "amduat/pel/program_dag_desc.h"
|
||||||
|
#include "amduat/pel/opreg_kernel.h"
|
||||||
#include "amduat/pel/run.h"
|
#include "amduat/pel/run.h"
|
||||||
|
#include "amduat/util/hex.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
@ -42,7 +46,8 @@ static const char k_amduatd_contract_v1_json[] =
|
||||||
"{\"method\":\"POST\",\"path\":\"/v1/artifacts\"},"
|
"{\"method\":\"POST\",\"path\":\"/v1/artifacts\"},"
|
||||||
"{\"method\":\"GET\",\"path\":\"/v1/artifacts/{ref}\"},"
|
"{\"method\":\"GET\",\"path\":\"/v1/artifacts/{ref}\"},"
|
||||||
"{\"method\":\"HEAD\",\"path\":\"/v1/artifacts/{ref}\"},"
|
"{\"method\":\"HEAD\",\"path\":\"/v1/artifacts/{ref}\"},"
|
||||||
"{\"method\":\"POST\",\"path\":\"/v1/pel/run\"}"
|
"{\"method\":\"POST\",\"path\":\"/v1/pel/run\"},"
|
||||||
|
"{\"method\":\"POST\",\"path\":\"/v1/pel/programs\"}"
|
||||||
"],"
|
"],"
|
||||||
"\"schemas\":{"
|
"\"schemas\":{"
|
||||||
"\"pel_run_request\":{"
|
"\"pel_run_request\":{"
|
||||||
|
|
@ -64,10 +69,159 @@ static const char k_amduatd_contract_v1_json[] =
|
||||||
"\"output_refs\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"description\":\"hex ref\"}},"
|
"\"output_refs\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"description\":\"hex ref\"}},"
|
||||||
"\"status\":{\"type\":\"string\"}"
|
"\"status\":{\"type\":\"string\"}"
|
||||||
"}"
|
"}"
|
||||||
|
"},"
|
||||||
|
"\"pel_program_author_request\":{"
|
||||||
|
"\"type\":\"object\","
|
||||||
|
"\"required\":[\"nodes\",\"roots\"],"
|
||||||
|
"\"properties\":{"
|
||||||
|
"\"nodes\":{\"type\":\"array\"},"
|
||||||
|
"\"roots\":{\"type\":\"array\"}"
|
||||||
|
"}"
|
||||||
"}"
|
"}"
|
||||||
"}"
|
"}"
|
||||||
"}\n";
|
"}\n";
|
||||||
|
|
||||||
|
static const char k_amduatd_ui_html[] =
|
||||||
|
"<!doctype html>\n"
|
||||||
|
"<html lang=\"en\">\n"
|
||||||
|
"<head>\n"
|
||||||
|
" <meta charset=\"utf-8\" />\n"
|
||||||
|
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
|
||||||
|
" <title>amduatd — PEL Program Builder</title>\n"
|
||||||
|
" <style>\n"
|
||||||
|
" :root{color-scheme:light dark;}\n"
|
||||||
|
" body{font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial,sans-serif;"
|
||||||
|
" margin:0;line-height:1.35;}\n"
|
||||||
|
" header{padding:18px 18px 8px;max-width:1100px;margin:0 auto;}\n"
|
||||||
|
" main{padding:0 18px 24px;max-width:1100px;margin:0 auto;}\n"
|
||||||
|
" h1{margin:0 0 4px;font-size:18px;}\n"
|
||||||
|
" .muted{opacity:.75;font-size:13px;}\n"
|
||||||
|
" .grid{display:grid;grid-template-columns:1fr;gap:14px;}\n"
|
||||||
|
" @media (min-width: 980px){.grid{grid-template-columns: 1.2fr .8fr;}}\n"
|
||||||
|
" .card{border:1px solid rgba(127,127,127,.35);border-radius:14px;padding:14px;"
|
||||||
|
" background:rgba(127,127,127,.06);}\n"
|
||||||
|
" textarea{width:100%;min-height:380px;resize:vertical;box-sizing:border-box;"
|
||||||
|
" font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;"
|
||||||
|
" font-size:12.5px;line-height:1.35;border-radius:10px;padding:10px;"
|
||||||
|
" border:1px solid rgba(127,127,127,.35);background:transparent;}\n"
|
||||||
|
" input{width:100%;box-sizing:border-box;border-radius:10px;padding:10px;"
|
||||||
|
" border:1px solid rgba(127,127,127,.35);background:transparent;"
|
||||||
|
" font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;"
|
||||||
|
" font-size:12.5px;}\n"
|
||||||
|
" button{border-radius:10px;padding:10px 12px;border:1px solid rgba(127,127,127,.35);"
|
||||||
|
" background:rgba(127,127,127,.12);cursor:pointer;}\n"
|
||||||
|
" button:hover{background:rgba(127,127,127,.18);}\n"
|
||||||
|
" .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center;}\n"
|
||||||
|
" .row button{flex:0 0 auto;}\n"
|
||||||
|
" pre{white-space:pre-wrap;word-break:break-word;margin:0;padding:10px;border-radius:10px;"
|
||||||
|
" border:1px solid rgba(127,127,127,.35);background:rgba(0,0,0,.08);"
|
||||||
|
" font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;"
|
||||||
|
" font-size:12.5px;min-height:120px;}\n"
|
||||||
|
" a{color:inherit;}\n"
|
||||||
|
" </style>\n"
|
||||||
|
"</head>\n"
|
||||||
|
"<body>\n"
|
||||||
|
" <header>\n"
|
||||||
|
" <h1>amduatd — PEL/PROGRAM-DAG/1 builder</h1>\n"
|
||||||
|
" <div class=\"muted\">POSTs to <code>/v1/pel/programs</code> and <code>/v1/pel/run</code> on this daemon.</div>\n"
|
||||||
|
" </header>\n"
|
||||||
|
" <main>\n"
|
||||||
|
" <div class=\"grid\">\n"
|
||||||
|
" <div class=\"card\">\n"
|
||||||
|
" <div class=\"row\" style=\"justify-content:space-between;\">\n"
|
||||||
|
" <div class=\"muted\">Program authoring JSON</div>\n"
|
||||||
|
" <div class=\"row\">\n"
|
||||||
|
" <button id=\"btnTemplate\" type=\"button\">Insert template</button>\n"
|
||||||
|
" <button id=\"btnCreate\" type=\"button\">Create program</button>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <textarea id=\"program\" spellcheck=\"false\"></textarea>\n"
|
||||||
|
" </div>\n"
|
||||||
|
"\n"
|
||||||
|
" <div class=\"card\">\n"
|
||||||
|
" <div class=\"muted\" style=\"margin-bottom:8px;\">Run</div>\n"
|
||||||
|
" <div style=\"margin-bottom:8px;\" class=\"muted\">program_ref</div>\n"
|
||||||
|
" <input id=\"programRef\" placeholder=\"<program_ref>\" />\n"
|
||||||
|
" <div style=\"margin:10px 0 8px;\" class=\"muted\">input_refs (comma-separated hex refs)</div>\n"
|
||||||
|
" <input id=\"inputRefs\" placeholder=\"<ref0>,<ref1>,...\" />\n"
|
||||||
|
" <div style=\"margin:10px 0 8px;\" class=\"muted\">params_ref (optional)</div>\n"
|
||||||
|
" <input id=\"paramsRef\" placeholder=\"<params_ref>\" />\n"
|
||||||
|
" <div style=\"margin:10px 0 8px;\" class=\"muted\">scheme_ref (optional, default dag)</div>\n"
|
||||||
|
" <input id=\"schemeRef\" placeholder=\"dag\" />\n"
|
||||||
|
" <div class=\"row\" style=\"margin-top:12px;\">\n"
|
||||||
|
" <button id=\"btnRun\" type=\"button\">Run program</button>\n"
|
||||||
|
" <a class=\"muted\" href=\"/v1/contract\">/v1/contract</a>\n"
|
||||||
|
" <a class=\"muted\" href=\"/v1/meta\">/v1/meta</a>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <div class=\"muted\" style=\"margin:14px 0 8px;\">Response</div>\n"
|
||||||
|
" <pre id=\"out\"></pre>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" </div>\n"
|
||||||
|
" </main>\n"
|
||||||
|
"\n"
|
||||||
|
" <script>\n"
|
||||||
|
" const el = (id) => document.getElementById(id);\n"
|
||||||
|
" const out = (v) => { el('out').textContent = typeof v === 'string' ? v : JSON.stringify(v, null, 2); };\n"
|
||||||
|
" const template = {\n"
|
||||||
|
" nodes: [\n"
|
||||||
|
" {\n"
|
||||||
|
" id: 1,\n"
|
||||||
|
" op: { name: 'pel.bytes.concat', version: 1 },\n"
|
||||||
|
" inputs: [ { external: { input_index: 0 } }, { external: { input_index: 1 } } ],\n"
|
||||||
|
" params_hex: ''\n"
|
||||||
|
" }\n"
|
||||||
|
" ],\n"
|
||||||
|
" roots: [ { node_id: 1, output_index: 0 } ]\n"
|
||||||
|
" };\n"
|
||||||
|
"\n"
|
||||||
|
" el('btnTemplate').addEventListener('click', () => {\n"
|
||||||
|
" el('program').value = JSON.stringify(template, null, 2);\n"
|
||||||
|
" });\n"
|
||||||
|
"\n"
|
||||||
|
" el('btnCreate').addEventListener('click', async () => {\n"
|
||||||
|
" try {\n"
|
||||||
|
" const body = JSON.parse(el('program').value || '{}');\n"
|
||||||
|
" const resp = await fetch('/v1/pel/programs', {\n"
|
||||||
|
" method: 'POST',\n"
|
||||||
|
" headers: { 'Content-Type': 'application/json' },\n"
|
||||||
|
" body: JSON.stringify(body)\n"
|
||||||
|
" });\n"
|
||||||
|
" const text = await resp.text();\n"
|
||||||
|
" out(text);\n"
|
||||||
|
" if (resp.ok) {\n"
|
||||||
|
" const j = JSON.parse(text);\n"
|
||||||
|
" if (j && j.program_ref) el('programRef').value = j.program_ref;\n"
|
||||||
|
" }\n"
|
||||||
|
" } catch (e) {\n"
|
||||||
|
" out(String(e));\n"
|
||||||
|
" }\n"
|
||||||
|
" });\n"
|
||||||
|
"\n"
|
||||||
|
" el('btnRun').addEventListener('click', async () => {\n"
|
||||||
|
" try {\n"
|
||||||
|
" const program_ref = el('programRef').value.trim();\n"
|
||||||
|
" const input_refs = (el('inputRefs').value || '').split(',').map(s => s.trim()).filter(Boolean);\n"
|
||||||
|
" const params_ref = el('paramsRef').value.trim();\n"
|
||||||
|
" const scheme_ref = el('schemeRef').value.trim();\n"
|
||||||
|
" const body = { program_ref, input_refs };\n"
|
||||||
|
" if (params_ref) body.params_ref = params_ref;\n"
|
||||||
|
" if (scheme_ref) body.scheme_ref = scheme_ref;\n"
|
||||||
|
" const resp = await fetch('/v1/pel/run', {\n"
|
||||||
|
" method: 'POST',\n"
|
||||||
|
" headers: { 'Content-Type': 'application/json' },\n"
|
||||||
|
" body: JSON.stringify(body)\n"
|
||||||
|
" });\n"
|
||||||
|
" out(await resp.text());\n"
|
||||||
|
" } catch (e) {\n"
|
||||||
|
" out(String(e));\n"
|
||||||
|
" }\n"
|
||||||
|
" });\n"
|
||||||
|
"\n"
|
||||||
|
" el('btnTemplate').click();\n"
|
||||||
|
" </script>\n"
|
||||||
|
"</body>\n"
|
||||||
|
"</html>\n";
|
||||||
|
|
||||||
static volatile sig_atomic_t amduatd_should_exit = 0;
|
static volatile sig_atomic_t amduatd_should_exit = 0;
|
||||||
|
|
||||||
static void amduatd_on_signal(int signo) {
|
static void amduatd_on_signal(int signo) {
|
||||||
|
|
@ -546,6 +700,53 @@ static bool amduatd_json_parse_string_noesc(const char **p,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool amduatd_json_parse_u32(const char **p,
|
||||||
|
const char *end,
|
||||||
|
uint32_t *out) {
|
||||||
|
const char *cur;
|
||||||
|
const char *start;
|
||||||
|
unsigned long long v;
|
||||||
|
char *tmp = NULL;
|
||||||
|
char *endp = NULL;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
if (out != NULL) {
|
||||||
|
*out = 0;
|
||||||
|
}
|
||||||
|
if (p == NULL || *p == NULL || out == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = amduatd_json_skip_ws(*p, end);
|
||||||
|
start = cur;
|
||||||
|
if (cur >= end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (*cur < '0' || *cur > '9') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cur++;
|
||||||
|
while (cur < end && *cur >= '0' && *cur <= '9') {
|
||||||
|
cur++;
|
||||||
|
}
|
||||||
|
n = (size_t)(cur - start);
|
||||||
|
tmp = (char *)malloc(n + 1u);
|
||||||
|
if (tmp == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(tmp, start, n);
|
||||||
|
tmp[n] = '\0';
|
||||||
|
errno = 0;
|
||||||
|
v = strtoull(tmp, &endp, 10);
|
||||||
|
free(tmp);
|
||||||
|
if (errno != 0 || endp == NULL || *endp != '\0' || v > 0xffffffffULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*out = (uint32_t)v;
|
||||||
|
*p = cur;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool amduatd_json_skip_string(const char **p, const char *end) {
|
static bool amduatd_json_skip_string(const char **p, const char *end) {
|
||||||
const char *cur;
|
const char *cur;
|
||||||
if (p == NULL || *p == NULL) {
|
if (p == NULL || *p == NULL) {
|
||||||
|
|
@ -1564,6 +1765,713 @@ pel_run_cleanup:
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
amduat_pel_node_t *nodes;
|
||||||
|
size_t nodes_len;
|
||||||
|
amduat_pel_root_ref_t *roots;
|
||||||
|
size_t roots_len;
|
||||||
|
} amduatd_pel_program_tmp_t;
|
||||||
|
|
||||||
|
static void amduatd_pel_program_tmp_free(amduatd_pel_program_tmp_t *tmp) {
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (tmp == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tmp->nodes != NULL) {
|
||||||
|
for (i = 0; i < tmp->nodes_len; ++i) {
|
||||||
|
free(tmp->nodes[i].inputs);
|
||||||
|
tmp->nodes[i].inputs = NULL;
|
||||||
|
tmp->nodes[i].inputs_len = 0;
|
||||||
|
free((void *)tmp->nodes[i].params.data);
|
||||||
|
tmp->nodes[i].params.data = NULL;
|
||||||
|
tmp->nodes[i].params.len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(tmp->nodes);
|
||||||
|
tmp->nodes = NULL;
|
||||||
|
tmp->nodes_len = 0;
|
||||||
|
free(tmp->roots);
|
||||||
|
tmp->roots = NULL;
|
||||||
|
tmp->roots_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_parse_params_hex(const char *s,
|
||||||
|
size_t len,
|
||||||
|
amduat_octets_t *out) {
|
||||||
|
char *tmp = NULL;
|
||||||
|
uint8_t *bytes = NULL;
|
||||||
|
size_t out_len = 0;
|
||||||
|
|
||||||
|
if (out != NULL) {
|
||||||
|
*out = amduat_octets(NULL, 0);
|
||||||
|
}
|
||||||
|
if (s == NULL || out == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduatd_copy_json_str(s, len, &tmp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (tmp[0] == '\0') {
|
||||||
|
free(tmp);
|
||||||
|
*out = amduat_octets(NULL, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!amduat_hex_decode_alloc(tmp, &bytes, &out_len)) {
|
||||||
|
free(tmp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
free(tmp);
|
||||||
|
*out = amduat_octets(bytes, out_len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_handle_post_pel_programs(int fd,
|
||||||
|
amduat_asl_store_t *store,
|
||||||
|
const amduatd_http_req_t *req) {
|
||||||
|
uint8_t *body = NULL;
|
||||||
|
const char *p = NULL;
|
||||||
|
const char *end = NULL;
|
||||||
|
amduatd_pel_program_tmp_t tmp;
|
||||||
|
amduat_pel_program_t program;
|
||||||
|
amduat_octets_t program_bytes;
|
||||||
|
amduat_artifact_t artifact;
|
||||||
|
amduat_reference_t program_ref;
|
||||||
|
char *ref_hex = NULL;
|
||||||
|
char json[2048];
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
memset(&tmp, 0, sizeof(tmp));
|
||||||
|
memset(&program, 0, sizeof(program));
|
||||||
|
program_bytes = amduat_octets(NULL, 0);
|
||||||
|
memset(&artifact, 0, sizeof(artifact));
|
||||||
|
memset(&program_ref, 0, sizeof(program_ref));
|
||||||
|
|
||||||
|
if (store == NULL || req == NULL) {
|
||||||
|
return amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"internal error");
|
||||||
|
}
|
||||||
|
if (req->content_length == 0) {
|
||||||
|
return amduatd_send_json_error(fd, 400, "Bad Request", "missing body");
|
||||||
|
}
|
||||||
|
if (req->content_length > (2u * 1024u * 1024u)) {
|
||||||
|
return amduatd_send_json_error(fd, 413, "Payload Too Large",
|
||||||
|
"payload too large");
|
||||||
|
}
|
||||||
|
|
||||||
|
body = (uint8_t *)malloc(req->content_length);
|
||||||
|
if (body == NULL) {
|
||||||
|
return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom");
|
||||||
|
}
|
||||||
|
if (!amduatd_read_exact(fd, body, req->content_length)) {
|
||||||
|
free(body);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = (const char *)body;
|
||||||
|
end = (const char *)body + req->content_length;
|
||||||
|
if (!amduatd_json_expect(&p, end, '{')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
bool have_nodes = false;
|
||||||
|
bool have_roots = false;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const char *key = NULL;
|
||||||
|
size_t key_len = 0;
|
||||||
|
const char *cur = NULL;
|
||||||
|
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == '}') {
|
||||||
|
p = cur + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_json_parse_string_noesc(&p, end, &key, &key_len) ||
|
||||||
|
!amduatd_json_expect(&p, end, ':')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_len == strlen("nodes") && memcmp(key, "nodes", key_len) == 0) {
|
||||||
|
size_t cap = 0;
|
||||||
|
if (have_nodes) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"duplicate nodes");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (!amduatd_json_expect(&p, end, '[')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid nodes");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == ']') {
|
||||||
|
p = cur + 1;
|
||||||
|
have_nodes = true;
|
||||||
|
} else {
|
||||||
|
for (;;) {
|
||||||
|
amduat_pel_node_t node;
|
||||||
|
bool have_id = false;
|
||||||
|
bool have_op = false;
|
||||||
|
bool have_inputs = false;
|
||||||
|
bool have_params_hex = false;
|
||||||
|
amduat_octets_t op_name = amduat_octets(NULL, 0);
|
||||||
|
uint32_t op_ver = 0;
|
||||||
|
|
||||||
|
memset(&node, 0, sizeof(node));
|
||||||
|
|
||||||
|
if (!amduatd_json_expect(&p, end, '{')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid node");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
const char *nk = NULL;
|
||||||
|
size_t nk_len = 0;
|
||||||
|
const char *sv = NULL;
|
||||||
|
size_t sv_len = 0;
|
||||||
|
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == '}') {
|
||||||
|
p = cur + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!amduatd_json_parse_string_noesc(&p, end, &nk, &nk_len) ||
|
||||||
|
!amduatd_json_expect(&p, end, ':')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid node");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nk_len == strlen("id") && memcmp(nk, "id", nk_len) == 0) {
|
||||||
|
uint32_t id = 0;
|
||||||
|
if (have_id || !amduatd_json_parse_u32(&p, end, &id)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid node id");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
node.id = (amduat_pel_node_id_t)id;
|
||||||
|
have_id = true;
|
||||||
|
} else if (nk_len == strlen("op") &&
|
||||||
|
memcmp(nk, "op", nk_len) == 0) {
|
||||||
|
bool have_name = false;
|
||||||
|
bool have_version = false;
|
||||||
|
if (have_op || !amduatd_json_expect(&p, end, '{')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid op");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
const char *okey = NULL;
|
||||||
|
size_t okey_len = 0;
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == '}') {
|
||||||
|
p = cur + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!amduatd_json_parse_string_noesc(&p, end, &okey,
|
||||||
|
&okey_len) ||
|
||||||
|
!amduatd_json_expect(&p, end, ':')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid op");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (okey_len == strlen("name") &&
|
||||||
|
memcmp(okey, "name", okey_len) == 0) {
|
||||||
|
if (have_name ||
|
||||||
|
!amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid op name");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
op_name = amduat_octets(sv, sv_len);
|
||||||
|
have_name = true;
|
||||||
|
} else if (okey_len == strlen("version") &&
|
||||||
|
memcmp(okey, "version", okey_len) == 0) {
|
||||||
|
if (have_version ||
|
||||||
|
!amduatd_json_parse_u32(&p, end, &op_ver)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid op version");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
have_version = true;
|
||||||
|
} else {
|
||||||
|
if (!amduatd_json_skip_value(&p, end, 0)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid op");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == ',') {
|
||||||
|
p = cur + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur < end && *cur == '}') {
|
||||||
|
p = cur + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid op");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (!have_name || !have_version) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"missing op fields");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
node.op.name = op_name;
|
||||||
|
node.op.version = op_ver;
|
||||||
|
have_op = true;
|
||||||
|
} else if (nk_len == strlen("inputs") &&
|
||||||
|
memcmp(nk, "inputs", nk_len) == 0) {
|
||||||
|
size_t icap = 0;
|
||||||
|
if (have_inputs || !amduatd_json_expect(&p, end, '[')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid inputs");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == ']') {
|
||||||
|
p = cur + 1;
|
||||||
|
have_inputs = true;
|
||||||
|
} else {
|
||||||
|
for (;;) {
|
||||||
|
amduat_pel_dag_input_t in;
|
||||||
|
memset(&in, 0, sizeof(in));
|
||||||
|
if (!amduatd_json_expect(&p, end, '{')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid input");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const char *ik = NULL;
|
||||||
|
size_t ik_len = 0;
|
||||||
|
if (!amduatd_json_parse_string_noesc(&p, end, &ik,
|
||||||
|
&ik_len) ||
|
||||||
|
!amduatd_json_expect(&p, end, ':') ||
|
||||||
|
!amduatd_json_expect(&p, end, '{')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid input");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (ik_len == strlen("external") &&
|
||||||
|
memcmp(ik, "external", ik_len) == 0) {
|
||||||
|
uint32_t idx = 0;
|
||||||
|
const char *k2 = NULL;
|
||||||
|
size_t k2_len = 0;
|
||||||
|
if (!amduatd_json_parse_string_noesc(&p, end, &k2,
|
||||||
|
&k2_len) ||
|
||||||
|
k2_len != strlen("input_index") ||
|
||||||
|
memcmp(k2, "input_index", k2_len) != 0 ||
|
||||||
|
!amduatd_json_expect(&p, end, ':') ||
|
||||||
|
!amduatd_json_parse_u32(&p, end, &idx) ||
|
||||||
|
!amduatd_json_expect(&p, end, '}')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid external input");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
in.kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL;
|
||||||
|
in.value.external.input_index = idx;
|
||||||
|
} else if (ik_len == strlen("node") &&
|
||||||
|
memcmp(ik, "node", ik_len) == 0) {
|
||||||
|
bool have_node_id = false;
|
||||||
|
bool have_output_index = false;
|
||||||
|
uint32_t nid = 0;
|
||||||
|
uint32_t oidx = 0;
|
||||||
|
for (;;) {
|
||||||
|
const char *k2 = NULL;
|
||||||
|
size_t k2_len = 0;
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == '}') {
|
||||||
|
p = cur + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!amduatd_json_parse_string_noesc(&p, end, &k2,
|
||||||
|
&k2_len) ||
|
||||||
|
!amduatd_json_expect(&p, end, ':')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid node input");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (k2_len == strlen("node_id") &&
|
||||||
|
memcmp(k2, "node_id", k2_len) == 0) {
|
||||||
|
if (have_node_id ||
|
||||||
|
!amduatd_json_parse_u32(&p, end, &nid)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid node_id");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
have_node_id = true;
|
||||||
|
} else if (k2_len == strlen("output_index") &&
|
||||||
|
memcmp(k2, "output_index", k2_len) == 0) {
|
||||||
|
if (have_output_index ||
|
||||||
|
!amduatd_json_parse_u32(&p, end, &oidx)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid output_index");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
have_output_index = true;
|
||||||
|
} else {
|
||||||
|
if (!amduatd_json_skip_value(&p, end, 0)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid node input");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == ',') {
|
||||||
|
p = cur + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur < end && *cur == '}') {
|
||||||
|
p = cur + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid node input");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (!have_node_id || !have_output_index) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"missing node input fields");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
in.kind = AMDUAT_PEL_DAG_INPUT_NODE;
|
||||||
|
in.value.node.node_id = (amduat_pel_node_id_t)nid;
|
||||||
|
in.value.node.output_index = oidx;
|
||||||
|
} else {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid input kind");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (!amduatd_json_expect(&p, end, '}')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid input");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.inputs_len == icap) {
|
||||||
|
size_t next_cap = icap != 0 ? icap * 2u : 4u;
|
||||||
|
amduat_pel_dag_input_t *next =
|
||||||
|
(amduat_pel_dag_input_t *)realloc(
|
||||||
|
node.inputs, next_cap * sizeof(*node.inputs));
|
||||||
|
if (next == NULL) {
|
||||||
|
ok = amduatd_send_json_error(fd, 500,
|
||||||
|
"Internal Server Error",
|
||||||
|
"oom");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
node.inputs = next;
|
||||||
|
icap = next_cap;
|
||||||
|
}
|
||||||
|
node.inputs[node.inputs_len++] = in;
|
||||||
|
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == ',') {
|
||||||
|
p = cur + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur < end && *cur == ']') {
|
||||||
|
p = cur + 1;
|
||||||
|
have_inputs = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid inputs");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (nk_len == strlen("params_hex") &&
|
||||||
|
memcmp(nk, "params_hex", nk_len) == 0) {
|
||||||
|
if (have_params_hex ||
|
||||||
|
!amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) ||
|
||||||
|
!amduatd_parse_params_hex(sv, sv_len, &node.params)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid params_hex");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
have_params_hex = true;
|
||||||
|
} else {
|
||||||
|
if (!amduatd_json_skip_value(&p, end, 0)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid node");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == ',') {
|
||||||
|
p = cur + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur < end && *cur == '}') {
|
||||||
|
p = cur + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!have_id || !have_op || !have_inputs || !have_params_hex) {
|
||||||
|
free(node.inputs);
|
||||||
|
free((void *)node.params.data);
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"missing node fields");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (amduat_pel_kernel_op_lookup(node.op.name, node.op.version) ==
|
||||||
|
NULL) {
|
||||||
|
free(node.inputs);
|
||||||
|
free((void *)node.params.data);
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"unknown op");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp.nodes_len == cap) {
|
||||||
|
size_t next_cap = cap != 0 ? cap * 2u : 4u;
|
||||||
|
amduat_pel_node_t *next =
|
||||||
|
(amduat_pel_node_t *)realloc(tmp.nodes,
|
||||||
|
next_cap * sizeof(*tmp.nodes));
|
||||||
|
if (next == NULL) {
|
||||||
|
free(node.inputs);
|
||||||
|
free((void *)node.params.data);
|
||||||
|
ok = amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"oom");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
tmp.nodes = next;
|
||||||
|
cap = next_cap;
|
||||||
|
}
|
||||||
|
tmp.nodes[tmp.nodes_len++] = node;
|
||||||
|
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == ',') {
|
||||||
|
p = cur + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur < end && *cur == ']') {
|
||||||
|
p = cur + 1;
|
||||||
|
have_nodes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid nodes");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (key_len == strlen("roots") &&
|
||||||
|
memcmp(key, "roots", key_len) == 0) {
|
||||||
|
size_t cap = 0;
|
||||||
|
if (have_roots) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"duplicate roots");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (!amduatd_json_expect(&p, end, '[')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid roots");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == ']') {
|
||||||
|
p = cur + 1;
|
||||||
|
have_roots = true;
|
||||||
|
} else {
|
||||||
|
for (;;) {
|
||||||
|
amduat_pel_root_ref_t root;
|
||||||
|
bool have_node_id = false;
|
||||||
|
bool have_output_index = false;
|
||||||
|
|
||||||
|
memset(&root, 0, sizeof(root));
|
||||||
|
if (!amduatd_json_expect(&p, end, '{')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid root");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
const char *rk = NULL;
|
||||||
|
size_t rk_len = 0;
|
||||||
|
uint32_t v = 0;
|
||||||
|
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == '}') {
|
||||||
|
p = cur + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!amduatd_json_parse_string_noesc(&p, end, &rk, &rk_len) ||
|
||||||
|
!amduatd_json_expect(&p, end, ':')) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid root");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (rk_len == strlen("node_id") &&
|
||||||
|
memcmp(rk, "node_id", rk_len) == 0) {
|
||||||
|
if (have_node_id || !amduatd_json_parse_u32(&p, end, &v)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid root node_id");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
root.node_id = (amduat_pel_node_id_t)v;
|
||||||
|
have_node_id = true;
|
||||||
|
} else if (rk_len == strlen("output_index") &&
|
||||||
|
memcmp(rk, "output_index", rk_len) == 0) {
|
||||||
|
if (have_output_index || !amduatd_json_parse_u32(&p, end, &v)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid root output_index");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
root.output_index = v;
|
||||||
|
have_output_index = true;
|
||||||
|
} else {
|
||||||
|
if (!amduatd_json_skip_value(&p, end, 0)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid root");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == ',') {
|
||||||
|
p = cur + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur < end && *cur == '}') {
|
||||||
|
p = cur + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid root");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (!have_node_id || !have_output_index) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"missing root fields");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (tmp.roots_len == cap) {
|
||||||
|
size_t next_cap = cap != 0 ? cap * 2u : 2u;
|
||||||
|
amduat_pel_root_ref_t *next =
|
||||||
|
(amduat_pel_root_ref_t *)realloc(
|
||||||
|
tmp.roots, next_cap * sizeof(*tmp.roots));
|
||||||
|
if (next == NULL) {
|
||||||
|
ok = amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"oom");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
tmp.roots = next;
|
||||||
|
cap = next_cap;
|
||||||
|
}
|
||||||
|
tmp.roots[tmp.roots_len++] = root;
|
||||||
|
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == ',') {
|
||||||
|
p = cur + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur < end && *cur == ']') {
|
||||||
|
p = cur + 1;
|
||||||
|
have_roots = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid roots");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!amduatd_json_skip_value(&p, end, 0)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = amduatd_json_skip_ws(p, end);
|
||||||
|
if (cur < end && *cur == ',') {
|
||||||
|
p = cur + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cur < end && *cur == '}') {
|
||||||
|
p = cur + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = amduatd_json_skip_ws(p, end);
|
||||||
|
if (p != end) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
if (!have_nodes || !have_roots) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"missing nodes/roots");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
program.nodes = tmp.nodes;
|
||||||
|
program.nodes_len = tmp.nodes_len;
|
||||||
|
program.roots = tmp.roots;
|
||||||
|
program.roots_len = tmp.roots_len;
|
||||||
|
if (!amduat_pel_program_dag_validate(&program)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid program");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduat_enc_pel_program_dag_encode_v1(&program, &program_bytes)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"encode error");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
artifact = amduat_artifact_with_type(program_bytes,
|
||||||
|
amduat_type_tag(AMDUAT_PEL_TYPE_TAG_PROGRAM_DAG_1));
|
||||||
|
if (amduat_asl_store_put(store, artifact, &program_ref) != AMDUAT_ASL_STORE_OK) {
|
||||||
|
ok = amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"store error");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduat_asl_ref_encode_hex(program_ref, &ref_hex)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"encode ref error");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int n = snprintf(json, sizeof(json), "{\"program_ref\":\"%s\"}\n", ref_hex);
|
||||||
|
free(ref_hex);
|
||||||
|
if (n <= 0 || (size_t)n >= sizeof(json)) {
|
||||||
|
ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error");
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
ok = amduatd_http_send_json(fd, 200, "OK", json, false);
|
||||||
|
goto pel_programs_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
pel_programs_cleanup:
|
||||||
|
free(body);
|
||||||
|
free((void *)program_bytes.data);
|
||||||
|
amduat_reference_free(&program_ref);
|
||||||
|
amduatd_pel_program_tmp_free(&tmp);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_handle_get_ui(int fd) {
|
||||||
|
return amduatd_http_send_status(fd,
|
||||||
|
200,
|
||||||
|
"OK",
|
||||||
|
"text/html; charset=utf-8",
|
||||||
|
(const uint8_t *)k_amduatd_ui_html,
|
||||||
|
strlen(k_amduatd_ui_html),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
static bool amduatd_handle_conn(int fd,
|
static bool amduatd_handle_conn(int fd,
|
||||||
amduat_asl_store_t *store,
|
amduat_asl_store_t *store,
|
||||||
const amduat_asl_store_fs_config_t *cfg,
|
const amduat_asl_store_fs_config_t *cfg,
|
||||||
|
|
@ -1577,6 +2485,10 @@ static bool amduatd_handle_conn(int fd,
|
||||||
|
|
||||||
amduatd_path_without_query(req.path, no_query, sizeof(no_query));
|
amduatd_path_without_query(req.path, no_query, sizeof(no_query));
|
||||||
|
|
||||||
|
if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/ui") == 0) {
|
||||||
|
return amduatd_handle_get_ui(fd);
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/meta") == 0) {
|
if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/meta") == 0) {
|
||||||
return amduatd_handle_meta(fd, cfg, api_contract_ref, false);
|
return amduatd_handle_meta(fd, cfg, api_contract_ref, false);
|
||||||
}
|
}
|
||||||
|
|
@ -1593,6 +2505,10 @@ static bool amduatd_handle_conn(int fd,
|
||||||
if (strcmp(req.method, "POST") == 0 && strcmp(no_query, "/v1/pel/run") == 0) {
|
if (strcmp(req.method, "POST") == 0 && strcmp(no_query, "/v1/pel/run") == 0) {
|
||||||
return amduatd_handle_post_pel_run(fd, store, &req);
|
return amduatd_handle_post_pel_run(fd, store, &req);
|
||||||
}
|
}
|
||||||
|
if (strcmp(req.method, "POST") == 0 &&
|
||||||
|
strcmp(no_query, "/v1/pel/programs") == 0) {
|
||||||
|
return amduatd_handle_post_pel_programs(fd, store, &req);
|
||||||
|
}
|
||||||
if (strcmp(req.method, "GET") == 0 &&
|
if (strcmp(req.method, "GET") == 0 &&
|
||||||
strncmp(no_query, "/v1/artifacts/", 14) == 0) {
|
strncmp(no_query, "/v1/artifacts/", 14) == 0) {
|
||||||
return amduatd_handle_get_artifact(fd, store, &req, req.path, false);
|
return amduatd_handle_get_artifact(fd, store, &req, req.path, false);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue