sov-kernel-monster / seb /runtime /c_src /seb_wal_nif.c
SNAPKITTYWEST's picture
chore: push full sov-kernel-monster content from local build
9425aed verified
Raw
History Blame Contribute Delete
12.2 kB
/*
* seb_wal_nif.c β€” Erlang NIF bridge to SEB WAL kernel (seb_wal.adb)
*
* This is the FULL kernel NIF. It replaces the lattice-only seb_kernel_nif.c
* for the production path. The lattice circuit (seb_lattice.c) is the
* commitment primitive used inside the WAL for WORM sealing.
*
* Exports to Erlang:
* init_kernel(SegId, Seq) -> {ok, Handle} | {error, Reason}
* append_event(Handle, Hdr, Pay, Ftr) -> {ok, Offset} | {error, Reason}
* rotate_segment(Handle, Id, Seq) -> {ok, 0} | {error, Reason}
* verify_chain(Handle) -> {ok, Count} | {error, Reason}
* worm_flush(Handle) -> ok
* get_state(Handle) -> {SegId, Seq, Events, Rotated, TipOffset}
* get_tip_hash(Handle) -> binary (32 bytes)
*
* Wire layout constants (must match seb_types.ads):
* Fixed_Header_Size = 68
* Fixed_Footer_Size = 128
* Hash_Size = 32
* Sig_Size = 64
*
* The commitment (WORM seal) uses the GF(2^8) lattice circuit from
* seb_lattice.c instead of standalone blake3. Both produce 32-byte outputs.
* For the chain integrity check, eventHash in the footer is the lattice
* commitment of (prev_tip || header_bytes). This unifies the two halves.
*/
#include "erl_nif.h"
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
/* Pull in the lattice circuit (zero external deps) */
#include "seb_lattice.c"
#define FIXED_HEADER_SIZE 68
#define FIXED_FOOTER_SIZE 128
#define HASH_SIZE 32
#define SIG_SIZE 64
/* Footer layout: prev_hash[32] || event_hash[32] || signature[64] = 128 */
#define FOOTER_PREV_HASH_OFF 0
#define FOOTER_EVENT_HASH_OFF 32
#define FOOTER_SIG_OFF 64
/* Per-handle kernel state */
typedef struct {
uint64_t segment_id;
uint64_t sequence;
uint8_t tip_hash[HASH_SIZE]; /* current commitment tip */
uint64_t tip_offset;
uint64_t events_sealed;
uint64_t segments_rotated;
int initialized;
} seb_wal_handle;
ErlNifResourceType *wal_handle_type = NULL;
static void wal_handle_dtor(ErlNifEnv *env, void *obj) { (void)env; (void)obj; }
/* ── init_kernel/2 ─────────────────────────────────────────────────────── */
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 seg_id, seq;
if (!enif_get_uint64(env, argv[0], &seg_id)) return enif_make_badarg(env);
if (!enif_get_uint64(env, argv[1], &seq)) return enif_make_badarg(env);
seb_wal_handle *h = enif_alloc_resource(wal_handle_type, sizeof(seb_wal_handle));
if (!h) return enif_make_atom(env, "error");
h->segment_id = seg_id;
h->sequence = seq;
memset(h->tip_hash, 0, HASH_SIZE); /* genesis tip = all zeros */
h->tip_offset = 0;
h->events_sealed = 0;
h->segments_rotated = 0;
h->initialized = 1;
ERL_NIF_TERM res = enif_make_resource(env, h);
enif_release_resource(h);
return enif_make_tuple2(env, enif_make_atom(env, "ok"), res);
}
/* ── append_event/4 ─────────────────────────────────────────────────────── */
/*
* append_event(Handle, Header::binary(68), Payload::binary, Footer::binary(128))
* -> {ok, CommittedOffset::uint64} | {error, Reason}
*
* L0 invariants enforced:
* 1. Commitment chain: lattice_circuit(prev_tip || header[0:64]) == footer.event_hash
* 2. Hash chain: footer.prev_hash == handle.tip_hash
* 3. Offset monotonic: header.offset (bytes 0-7 LE) > tip_offset
* 4. Segment bounds: event size fits in segment
*/
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_wal_handle *h;
ErlNifBinary hdr_bin, pay_bin, ftr_bin;
if (!enif_get_resource(env, argv[0], wal_handle_type, (void**)&h))
return enif_make_atom(env, "error");
if (!h->initialized)
return enif_make_tuple2(env, enif_make_atom(env, "error"),
enif_make_atom(env, "not_initialized"));
if (!enif_inspect_binary(env, argv[1], &hdr_bin) ||
hdr_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], &pay_bin))
return enif_make_tuple2(env, enif_make_atom(env, "error"),
enif_make_atom(env, "invalid_payload"));
if (!enif_inspect_binary(env, argv[3], &ftr_bin) ||
ftr_bin.size != FIXED_FOOTER_SIZE)
return enif_make_tuple2(env, enif_make_atom(env, "error"),
enif_make_atom(env, "invalid_footer"));
const uint8_t *prev_hash = ftr_bin.data + FOOTER_PREV_HASH_OFF;
const uint8_t *event_hash = ftr_bin.data + FOOTER_EVENT_HASH_OFF;
/* Invariant 2: hash chain */
if (h->events_sealed > 0) {
uint8_t diff = 0;
for (int i = 0; i < HASH_SIZE; i++) diff |= prev_hash[i] ^ h->tip_hash[i];
if (diff)
return enif_make_tuple2(env, enif_make_atom(env, "error"),
enif_make_atom(env, "hash_chain_broken"));
}
/* Invariant 1: lattice commitment circuit(prev_tip[32] || header[0:64]) == footer.event_hash
* The circuit input is 96 bytes: 32 tip + 64 header bytes */
uint8_t in96[96];
memcpy(in96, h->tip_hash, HASH_SIZE); /* prev tip */
memcpy(in96 + 32, hdr_bin.data, 64); /* header[0:64] */
uint8_t computed[HASH_SIZE];
circuit(in96, computed); /* GF(2^8) lattice circuit from seb_lattice.c */
{
uint8_t diff = 0;
for (int i = 0; i < HASH_SIZE; i++) diff |= computed[i] ^ event_hash[i];
if (diff)
return enif_make_tuple2(env, enif_make_atom(env, "error"),
enif_make_atom(env, "invalid_commitment"));
}
/* Invariant 3: offset monotonic β€” header bytes 0-7 are offset (little-endian) */
uint64_t new_offset = 0;
for (int i = 0; i < 8; i++)
new_offset |= ((uint64_t)hdr_bin.data[i]) << (i * 8);
if (h->events_sealed > 0 && new_offset <= h->tip_offset)
return enif_make_tuple2(env, enif_make_atom(env, "error"),
enif_make_atom(env, "offset_not_monotonic"));
/* Invariant 4: segment bounds */
uint32_t payload_size = 0;
for (int i = 0; i < 4; i++)
payload_size |= ((uint32_t)hdr_bin.data[24 + i]) << (i * 8);
uint64_t event_size = FIXED_HEADER_SIZE + payload_size + FIXED_FOOTER_SIZE;
if (event_size > (1ULL << 30))
return enif_make_tuple2(env, enif_make_atom(env, "error"),
enif_make_atom(env, "segment_full"));
/* Commit */
uint64_t committed = h->tip_offset;
memcpy(h->tip_hash, event_hash, HASH_SIZE);
h->tip_offset = new_offset;
h->events_sealed++;
return enif_make_tuple2(env, enif_make_atom(env, "ok"),
enif_make_uint64(env, committed));
}
/* ── rotate_segment/3 ───────────────────────────────────────────────────── */
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_wal_handle *h;
uint64_t new_id, new_seq;
if (!enif_get_resource(env, argv[0], wal_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->sequence)
return enif_make_tuple2(env, enif_make_atom(env, "error"),
enif_make_atom(env, "sequence_not_monotonic"));
h->segment_id = new_id;
h->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));
}
/* ── verify_chain/1 ─────────────────────────────────────────────────────── */
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_wal_handle *h;
if (!enif_get_resource(env, argv[0], wal_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));
}
/* ── worm_flush/1 ───────────────────────────────────────────────────────── */
static ERL_NIF_TERM nif_worm_flush(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
if (argc != 1) return enif_make_badarg(env);
seb_wal_handle *h;
if (!enif_get_resource(env, argv[0], wal_handle_type, (void**)&h))
return enif_make_atom(env, "error");
(void)h; /* mmap msync in production */
return enif_make_atom(env, "ok");
}
/* ── get_state/1 ────────────────────────────────────────────────────────── */
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_wal_handle *h;
if (!enif_get_resource(env, argv[0], wal_handle_type, (void**)&h))
return enif_make_atom(env, "error");
return enif_make_tuple5(env,
enif_make_uint64(env, h->segment_id),
enif_make_uint64(env, h->sequence),
enif_make_uint64(env, h->events_sealed),
enif_make_uint64(env, h->segments_rotated),
enif_make_uint64(env, h->tip_offset));
}
/* ── get_tip_hash/1 ─────────────────────────────────────────────────────── */
static ERL_NIF_TERM nif_get_tip_hash(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{
if (argc != 1) return enif_make_badarg(env);
seb_wal_handle *h;
if (!enif_get_resource(env, argv[0], wal_handle_type, (void**)&h))
return enif_make_atom(env, "error");
ERL_NIF_TERM bin;
uint8_t *buf = enif_make_new_binary(env, HASH_SIZE, &bin);
memcpy(buf, h->tip_hash, HASH_SIZE);
return enif_make_tuple2(env, enif_make_atom(env, "ok"), bin);
}
/* ── NIF registry + init ────────────────────────────────────────────────── */
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},
{"worm_flush", 1, nif_worm_flush},
{"get_state", 1, nif_get_state},
{"get_tip_hash", 1, nif_get_tip_hash}
};
static int on_load(ErlNifEnv *env, void **priv, ERL_NIF_TERM info)
{
(void)priv; (void)info;
wal_handle_type = enif_open_resource_type(env, NULL, "seb_wal_handle",
wal_handle_dtor,
ERL_NIF_RT_CREATE, NULL);
return wal_handle_type ? 0 : -1;
}
ERL_NIF_INIT(seb_kernel_nif, nif_funcs, on_load, NULL, NULL, NULL)