| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| #include "erl_nif.h"
|
| #include <string.h>
|
| #include <stdint.h>
|
|
|
|
|
| #include "seb_lattice.c"
|
|
|
|
|
| #define FIXED_HEADER_SIZE 68
|
| #define FIXED_FOOTER_SIZE 64
|
| #define HASH_SIZE_BYTES 32
|
|
|
|
|
| typedef struct {
|
| uint64_t current_segment_id;
|
| uint64_t current_sequence;
|
| uint8_t tip[HASH_SIZE_BYTES];
|
| uint64_t tip_offset;
|
| uint64_t events_sealed;
|
| uint64_t segments_rotated;
|
| } seb_kernel_handle;
|
|
|
| ErlNifResourceType* kernel_handle_type = NULL;
|
|
|
| static void kernel_handle_dtor(ErlNifEnv* env, void* obj) { (void)env; (void)obj; }
|
|
|
|
|
| static ERL_NIF_TERM nif_init_kernel(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
| {
|
| if (argc != 2) return enif_make_badarg(env);
|
|
|
| uint64_t segment_id, segment_sequence;
|
| if (!enif_get_uint64(env, argv[0], &segment_id)) return enif_make_badarg(env);
|
| if (!enif_get_uint64(env, argv[1], &segment_sequence)) return enif_make_badarg(env);
|
|
|
| seb_kernel_handle* h = enif_alloc_resource(kernel_handle_type, sizeof(seb_kernel_handle));
|
| if (!h) return enif_make_atom(env, "error");
|
|
|
| h->current_segment_id = segment_id;
|
| h->current_sequence = segment_sequence;
|
| memset(h->tip, 0, HASH_SIZE_BYTES);
|
| h->tip_offset = 0;
|
| h->events_sealed = 0;
|
| h->segments_rotated = 0;
|
|
|
| ERL_NIF_TERM res = enif_make_resource(env, h);
|
| enif_release_resource(h);
|
| return enif_make_tuple2(env, enif_make_atom(env, "ok"), res);
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| static ERL_NIF_TERM nif_append_event(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
| {
|
| if (argc != 4) return enif_make_badarg(env);
|
|
|
| seb_kernel_handle* h;
|
| ErlNifBinary header_bin, payload_bin, footer_bin;
|
|
|
| if (!enif_get_resource(env, argv[0], kernel_handle_type, (void**)&h))
|
| return enif_make_atom(env, "error");
|
|
|
| if (!enif_inspect_binary(env, argv[1], &header_bin) ||
|
| header_bin.size != FIXED_HEADER_SIZE)
|
| return enif_make_tuple2(env, enif_make_atom(env, "error"),
|
| enif_make_atom(env, "invalid_header"));
|
|
|
| if (!enif_inspect_binary(env, argv[2], &payload_bin))
|
| return enif_make_tuple2(env, enif_make_atom(env, "error"),
|
| enif_make_atom(env, "invalid_payload"));
|
|
|
| if (!enif_inspect_binary(env, argv[3], &footer_bin) ||
|
| footer_bin.size != FIXED_FOOTER_SIZE)
|
| return enif_make_tuple2(env, enif_make_atom(env, "error"),
|
| enif_make_atom(env, "invalid_footer"));
|
|
|
|
|
| uint64_t event_size = FIXED_HEADER_SIZE + payload_bin.size + FIXED_FOOTER_SIZE;
|
| if (event_size > (1ULL << 30) - h->tip_offset)
|
| return enif_make_tuple2(env, enif_make_atom(env, "error"),
|
| enif_make_atom(env, "segment_full"));
|
|
|
|
|
| const uint8_t* prev_commit = footer_bin.data;
|
| const uint8_t* recv_commit = footer_bin.data + 32;
|
|
|
| if (h->events_sealed > 0) {
|
| if (memcmp(prev_commit, h->tip, HASH_SIZE_BYTES) != 0)
|
| return enif_make_tuple2(env, enif_make_atom(env, "error"),
|
| enif_make_atom(env, "hash_chain_broken"));
|
| }
|
|
|
| |
| |
|
|
| uint8_t in96[96];
|
| memcpy(in96, h->tip, HASH_SIZE_BYTES);
|
| memcpy(in96 + 32, header_bin.data, 64);
|
|
|
| uint8_t computed[HASH_SIZE_BYTES];
|
| circuit(in96, computed);
|
|
|
| if (memcmp(computed, recv_commit, HASH_SIZE_BYTES) != 0)
|
| return enif_make_tuple2(env, enif_make_atom(env, "error"),
|
| enif_make_atom(env, "invalid_commitment"));
|
|
|
|
|
| uint64_t committed_offset = h->tip_offset;
|
| h->tip_offset += event_size;
|
| memcpy(h->tip, recv_commit, HASH_SIZE_BYTES);
|
| h->events_sealed++;
|
|
|
| return enif_make_tuple2(env, enif_make_atom(env, "ok"),
|
| enif_make_uint64(env, committed_offset));
|
| }
|
|
|
|
|
| static ERL_NIF_TERM nif_rotate_segment(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
| {
|
| if (argc != 3) return enif_make_badarg(env);
|
|
|
| seb_kernel_handle* h;
|
| uint64_t new_id, new_seq;
|
|
|
| if (!enif_get_resource(env, argv[0], kernel_handle_type, (void**)&h))
|
| return enif_make_atom(env, "error");
|
| if (!enif_get_uint64(env, argv[1], &new_id)) return enif_make_badarg(env);
|
| if (!enif_get_uint64(env, argv[2], &new_seq)) return enif_make_badarg(env);
|
|
|
|
|
| if (new_seq <= h->current_sequence)
|
| return enif_make_tuple2(env, enif_make_atom(env, "error"),
|
| enif_make_atom(env, "sequence_not_monotonic"));
|
|
|
| h->current_segment_id = new_id;
|
| h->current_sequence = new_seq;
|
| h->tip_offset = 0;
|
| h->segments_rotated++;
|
|
|
| return enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_uint64(env, 0));
|
| }
|
|
|
|
|
| static ERL_NIF_TERM nif_verify_chain(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
| {
|
| if (argc != 1) return enif_make_badarg(env);
|
| seb_kernel_handle* h;
|
| if (!enif_get_resource(env, argv[0], kernel_handle_type, (void**)&h))
|
| return enif_make_atom(env, "error");
|
| return enif_make_tuple2(env, enif_make_atom(env, "ok"),
|
| enif_make_uint64(env, h->events_sealed));
|
| }
|
|
|
|
|
| static ERL_NIF_TERM nif_commit_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
| {
|
| if (argc != 4) return enif_make_badarg(env);
|
| seb_kernel_handle* h;
|
| uint64_t agent_id, partition, offset;
|
| if (!enif_get_resource(env, argv[0], kernel_handle_type, (void**)&h))
|
| return enif_make_atom(env, "error");
|
| if (!enif_get_uint64(env, argv[1], &agent_id)) return enif_make_badarg(env);
|
| if (!enif_get_uint64(env, argv[2], &partition)) return enif_make_badarg(env);
|
| if (!enif_get_uint64(env, argv[3], &offset)) return enif_make_badarg(env);
|
| (void)agent_id; (void)partition; (void)offset;
|
| return enif_make_atom(env, "ok");
|
| }
|
|
|
|
|
| static ERL_NIF_TERM nif_get_state(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
|
| {
|
| if (argc != 1) return enif_make_badarg(env);
|
| seb_kernel_handle* h;
|
| if (!enif_get_resource(env, argv[0], kernel_handle_type, (void**)&h))
|
| return enif_make_atom(env, "error");
|
| return enif_make_tuple5(env,
|
| enif_make_uint64(env, h->current_segment_id),
|
| enif_make_uint64(env, h->current_sequence),
|
| enif_make_uint64(env, h->events_sealed),
|
| enif_make_uint64(env, h->segments_rotated),
|
| enif_make_uint64(env, h->tip_offset));
|
| }
|
|
|
| static ErlNifFunc nif_funcs[] = {
|
| {"init_kernel", 2, nif_init_kernel},
|
| {"append_event", 4, nif_append_event},
|
| {"rotate_segment", 3, nif_rotate_segment},
|
| {"verify_chain", 1, nif_verify_chain},
|
| {"commit_offset", 4, nif_commit_offset},
|
| {"get_state", 1, nif_get_state}
|
| };
|
|
|
| static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
|
| {
|
| (void)priv_data; (void)load_info;
|
| kernel_handle_type = enif_open_resource_type(env, NULL, "seb_kernel_handle",
|
| kernel_handle_dtor,
|
| ERL_NIF_RT_CREATE, NULL);
|
| return kernel_handle_type ? 0 : -1;
|
| }
|
|
|
| ERL_NIF_INIT(seb_kernel_nif, nif_funcs, on_load, NULL, NULL, NULL)
|
|
|