File size: 5,837 Bytes
9425aed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
%%%-------------------------------------------------------------------
%% @doc Sovereign Event Bus — WAL Kernel NIF Bridge
%%
%% Bridges Erlang/OTP to the C WAL kernel (seb_wal_nif.c) which
%% enforces all five L0 invariants on every append:
%%   1. Lattice commitment: circuit(prev_tip || header[0:64]) == footer.event_hash
%%   2. Hash chain:         footer.prev_hash == handle.tip_hash
%%   3. Offset monotonic:   header.offset > tip_offset
%%   4. Segment bounds:     event fits in 1 GiB segment
%%   5. Sequence monotonic: on segment rotation
%%
%% The commitment uses the GF(2^8) lattice circuit (seb_lattice.c),
%% which unifies the WAL kernel with the formal lattice specification.
%%
%% Wire constants (from SEB_Protocol.idr / seb_types.ads):
%%   Header = 68 bytes, Footer = 128 bytes, Overhead = 196 bytes
%%   Tip hash = 32 bytes (lattice commitment)
%% @end
%%%-------------------------------------------------------------------
-module(seb_kernel_nif).
-behaviour(gen_server).

-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-export([

    init_kernel/2,        %% (SegmentId, Sequence) -> {ok, Handle} | {error, Reason}

    append_event/4,       %% (Handle, Header68, Payload, Footer128) -> {ok, Offset} | {error, Reason}

    rotate_segment/3,     %% (Handle, NewSegId, NewSeq) -> {ok, 0} | {error, Reason}

    verify_chain/1,       %% (Handle) -> {ok, EventsSealed} | {error, Reason}

    worm_flush/1,         %% (Handle) -> ok

    get_state/1,          %% (Handle) -> {SegId, Seq, Sealed, Rotated, TipOffset}

    get_tip_hash/1        %% (Handle) -> {ok, Hash32::binary} | {error, Reason}

]).

-define(SERVER, ?MODULE).
-define(NIF_LIB, "seb_wal_nif").  %% built from seb_wal_nif.c

-record(state, {handle}).

%%%===================================================================
%%% API
%%%===================================================================

start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

-spec init_kernel(non_neg_integer(), non_neg_integer()) ->
    {ok, reference()} | {error, term()}.
init_kernel(SegId, Seq) ->
    gen_server:call(?SERVER, {init_kernel, SegId, Seq}).

-spec append_event(reference(), binary(), binary(), binary()) ->
    {ok, non_neg_integer()} | {error, term()}.
append_event(Handle, Header, Payload, Footer) ->
    gen_server:call(?SERVER, {append_event, Handle, Header, Payload, Footer}).

-spec rotate_segment(reference(), non_neg_integer(), non_neg_integer()) ->
    {ok, 0} | {error, term()}.
rotate_segment(Handle, NewSegId, NewSeq) ->
    gen_server:call(?SERVER, {rotate_segment, Handle, NewSegId, NewSeq}).

-spec verify_chain(reference()) -> {ok, non_neg_integer()} | {error, term()}.
verify_chain(Handle) ->
    gen_server:call(?SERVER, {verify_chain, Handle}).

-spec worm_flush(reference()) -> ok.
worm_flush(Handle) ->
    gen_server:call(?SERVER, {worm_flush, Handle}).

-spec get_state(reference()) ->
    {non_neg_integer(), non_neg_integer(), non_neg_integer(),
     non_neg_integer(), non_neg_integer()}.
get_state(Handle) ->
    gen_server:call(?SERVER, {get_state, Handle}).

-spec get_tip_hash(reference()) -> {ok, binary()} | {error, term()}.
get_tip_hash(Handle) ->
    gen_server:call(?SERVER, {get_tip_hash, Handle}).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

init([]) ->
    SoPath = filename:join([code:priv_dir(seb), ?NIF_LIB]),
    case erlang:load_nif(SoPath, []) of
        ok ->
            {ok, Handle} = nif_init_kernel(0, 0),
            {ok, #state{handle = Handle}};
        {error, {reload, _}} ->
            {ok, Handle} = nif_init_kernel(0, 0),
            {ok, #state{handle = Handle}};
        {error, Reason} ->
            {stop, {nif_load_failed, SoPath, Reason}}
    end.

handle_call({init_kernel, SegId, Seq}, _From, State) ->
    {reply, nif_init_kernel(SegId, Seq), State};
handle_call({append_event, Handle, Hdr, Pay, Ftr}, _From, State) ->
    {reply, nif_append_event(Handle, Hdr, Pay, Ftr), State};
handle_call({rotate_segment, Handle, Id, Seq}, _From, State) ->
    {reply, nif_rotate_segment(Handle, Id, Seq), State};
handle_call({verify_chain, Handle}, _From, State) ->
    {reply, nif_verify_chain(Handle), State};
handle_call({worm_flush, Handle}, _From, State) ->
    {reply, nif_worm_flush(Handle), State};
handle_call({get_state, Handle}, _From, State) ->
    {reply, nif_get_state(Handle), State};
handle_call({get_tip_hash, Handle}, _From, State) ->
    {reply, nif_get_tip_hash(Handle), State};
handle_call(_Req, _From, State) ->
    {reply, {error, unknown_call}, State}.

handle_cast(_Msg, State) -> {noreply, State}.
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.

%%%===================================================================
%%% NIF stubs — replaced by C dispatch after load_nif succeeds
%%%===================================================================

nif_init_kernel(_SegId, _Seq)                  -> erlang:nif_error(nif_not_loaded).
nif_append_event(_H, _Hdr, _Pay, _Ftr)         -> erlang:nif_error(nif_not_loaded).
nif_rotate_segment(_H, _Id, _Seq)              -> erlang:nif_error(nif_not_loaded).
nif_verify_chain(_H)                           -> erlang:nif_error(nif_not_loaded).
nif_worm_flush(_H)                             -> erlang:nif_error(nif_not_loaded).
nif_get_state(_H)                              -> erlang:nif_error(nif_not_loaded).
nif_get_tip_hash(_H)                           -> erlang:nif_error(nif_not_loaded).