File size: 7,331 Bytes
119e586 | 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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | (ns snapkitty.lisp.native
"Runtime binding to NASM validators (mutation gate + digest verification)"
(:require [promesa.core :as p]))
;; ============================================================================
;; Load native module at startup
;; ============================================================================
(def native-lib (atom nil))
(def lib-loaded? (atom false))
(defn load-native-library! [lib-path]
"Load the compiled NASM + Node.js binding library
lib-path: path to .node file (e.g., './native/build/Release/skclisp_native.node')"
(p/let [lib (require lib-path)]
(reset! native-lib lib)
(reset! lib-loaded? true)
(js/console.log "[SKC-LISP] Native ASM library loaded:" lib-path)
true))
;; ============================================================================
;; Mutation Validation Gate (8-point check)
;; ============================================================================
(defn validate-mutation! [mutation-event object-store]
"Fast-path mutation validation using NASM
Returns promise with validation result:
{:passes-gate boolean ; true if all 8 checks pass
:error-code number ; 0-8 for failed check, 255 for all pass
:details string} ; human-readable error message"
(if-not @lib-loaded?
(p/rejected (js/Error. "Native library not loaded"))
(p/let [lib @native-lib
; Prepare mutation_event buffer (64 bytes)
; Layout matches NASM struct:
; [0-8] mutation_id
; [8-16] generation_before
; [16-24] generation_after
; [24-32] actor
; [32-40] target
; [40-44] operation
; [44-48] reserved
; [48-56] old_digest (pointer)
; [56-64] new_digest (pointer)
mutation-buf (js/Uint8Array. 64)
result-buf (js/Uint8Array. 2)
; Copy fields into buffer (simplified for now)
_ (doseq [[i val] (mapcat
(fn [[offset v]] [(offset 0) v])
{0 (:mutation-id mutation-event)
8 (:generation-before mutation-event)
16 (:generation-after mutation-event)
24 (:actor mutation-event)
32 (:target mutation-event)})]
(aset mutation-buf i val))
; Call NASM function
ret (.validateMutation lib mutation-buf
(or object-store 0)
result-buf)
passes? (= (aget result-buf 0) 1)
error-code (aget result-buf 1)
error-msg (case error-code
0 "Check 1 failed: target does not exist"
1 "Check 2 failed: old_digest mismatch"
2 "Check 3 failed: new_digest mismatch"
3 "Check 4 failed: replacement not well-formed"
4 "Check 5 failed: invalid references"
5 "Check 6 failed: invalid code"
6 "Check 7 failed: invariants not preserved"
7 "Check 8 failed: generation not monotonic"
255 "All checks passed"
"Unknown error")]
{:passes-gate passes?
:error-code error-code
:details error-msg
:native-result ret})))
;; ============================================================================
;; Blake3 Digest Verification
;; ============================================================================
(defn verify-blake3! [payload expected-digest]
"Fast-path Blake3 verification using NASM
Returns promise with verification result:
{:digest-valid boolean ; true if payload matches expected digest
:error-code number ; 0=match, 1=mismatch, 2=invalid_input
:details string}"
(if-not @lib-loaded?
(p/rejected (js/Error. "Native library not loaded"))
(p/let [lib @native-lib
; Convert payload to Uint8Array if needed
payload-buf (if (instance? js/Uint8Array payload)
payload
(js/Uint8Array. (.from js/Array payload)))
; Convert expected digest
digest-buf (if (instance? js/Uint8Array expected-digest)
expected-digest
(js/Uint8Array. (.from js/Array expected-digest)))
result-buf (js/Uint8Array. 2)
; Call NASM function
ret (.verifyBlake3 lib payload-buf digest-buf result-buf)
valid? (= (aget result-buf 0) 1)
error-code (aget result-buf 1)
error-msg (case error-code
0 "Digest matches"
1 "Digest mismatch"
2 "Invalid input (null or malformed)"
"Unknown error")]
{:digest-valid valid?
:error-code error-code
:details error-msg
:native-result ret})))
;; ============================================================================
;; Ed25519 Signature Verification
;; ============================================================================
(defn verify-ed25519! [message signature public-key]
"Fast-path Ed25519 verification using NASM
Returns promise with verification result:
{:signature-valid boolean ; true if signature is valid
:error-code number ; 0=valid, 1=invalid, 2=invalid_input
:details string}"
(if-not @lib-loaded?
(p/rejected (js/Error. "Native library not loaded"))
(p/let [lib @native-lib
msg-buf (if (instance? js/Uint8Array message)
message
(js/Uint8Array. (.from js/Array message)))
sig-buf (if (instance? js/Uint8Array signature)
signature
(js/Uint8Array. (.from js/Array signature)))
key-buf (if (instance? js/Uint8Array public-key)
public-key
(js/Uint8Array. (.from js/Array public-key)))
result-buf (js/Uint8Array. 2)
; Call NASM function
ret (.verifyEd25519 lib msg-buf sig-buf key-buf result-buf)
valid? (= (aget result-buf 0) 1)
error-code (aget result-buf 1)
error-msg (case error-code
0 "Signature is valid"
1 "Signature is invalid"
2 "Invalid input"
"Unknown error")]
{:signature-valid valid?
:error-code error-code
:details error-msg
:native-result ret})))
;; ============================================================================
;; Export for MCP tools
;; ============================================================================
(def native-api
{:load-library load-native-library!
:validate-mutation validate-mutation!
:verify-blake3 verify-blake3!
:verify-ed25519 verify-ed25519!
:is-loaded? (fn [] @lib-loaded?)})
|