code wiki / (root) / nx_model_get.nx

nx_model_get.nx source

↩ module page · 395 lines · 19427 B

1// nx_model_get.nx -- G9 mg_fetch: hash-verified model acquisition into the gen model store (/compare/gen watch G9). 2// usage: nx_model_get <url> <expected-sha256> <dest-name> 3// <dest-name> is relative to the model-store root (e.g. diffusion/z_image_turbo-Q8_0.gguf, loras/x.safetensors). 4// 5// COMPOSES INCUMBENTS, DUPLICATES NONE: 6// transport = nx_https_fetch_range (own TLS-1.3, Range windows -- one bounded window in memory at a time, so a 7// multi-GB body STREAMS and can never be silently truncated by a buffer cap) 8// hashing = the streaming sha256_init/update/final already inside this compilation unit (nx_sha256 via the 9// TLS stack / nx_weights_pull) -- the digest covers the FULL body across all windows 10// verify law = nx_weights_pull (wp_hexenc + fail-closed compare): a mismatched body is DELETED, never stored 11// decisions = nx_model_get_lib (dest admission, hex normalize, Content-Range/Length parse, conf parse), 12// offline-proven by nx_model_get_gate 13// Redirects (HF resolve -> CDN) are followed by a bytes=0-0 probe that reads the Location header, because the 14// range fetcher itself deliberately does not follow. Resumable: an interrupted pull leaves <dest>.part; a re-run 15// re-hashes the part from disk and continues from its byte count. Idempotent: an existing verified dest returns 16// OK-ALREADY-VERIFIED with no network touched. An existing dest that does NOT match is REFUSED and kept 17// (additive-only: this organ never deletes a model already in the store; only its own .part on hash mismatch). 18// exit: 0 VERIFIED / OK-ALREADY-VERIFIED | 1 REFUSED (hash mismatch / existing mismatch) | 2 usage/bad-args | 19// 3 UNPROVEN (transport, trust store, no-length -- partial kept for resume where one exists) 20// license_tier: ORIGINAL 21import "nx_syscalls.nx" 22import "nx_https_fetch_follow.nx" 23import "nx_trust_store_load_from_certdata.nx" 24import "nx_weights_pull.nx" 25import "nx_model_get_lib.nx" 26 27const MG_URLCAP: i64 = 4096 // same bound the fetch stack uses for a url buffer 28const MG_PROBE_CAP: i64 = 262144 // bytes=0-0 probe response buffer (headers + stub body) 29const MG_HOPS_MAX: i64 = 6 // redirect budget, mirrors the fetch stack's default 30const MG_HASH_WIN: i64 = 1048576 // on-disk re-hash window for idempotency/resume 31const MG_WINDOW_DEFAULT: i64 = 8388608 // range window when the conf carries none (8 MiB) 32const MG_WIN_RETRIES: i64 = 3 // window failures tolerated before declaring TRANSPORT-STALL 33const MG_PROGRESS_EVERY: i64 = 32 // announce progress every N windows 34const MG_CERT_ROOTS: i64 = 512 35const MG_CERT_CAP: i64 = 4194304 36const MG_CONF_PATH: *u8 = "knowledge/gen_model_store.conf" 37const MG_ROOT_DEFAULT: *u8 = "/volume1/ai/models" 38const MG_RECEIPT_PATH: *u8 = "knowledge/status/modelget.log" 39 40func mgo_put(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 41func mgo_num(v: i64) -> i64 { 42 var m: i64=v 43 if m<0 { mgo_put("-" as *u8); m=0-m } 44 let t: *u8=sys_mmap(28) 45 var k: i64=0 46 if m==0 { t[0]=48 as u8; k=1 } 47 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 48 let b: *u8=sys_mmap(28) 49 var j: i64=0 50 while j<k { b[j]=t[k-1-j]; j=j+1 } 51 sys_write(1,b,k) 52 return 0 53} 54 55// stream-hash an on-disk file into ctx (sha256_update over MG_HASH_WIN windows). Returns bytes hashed, -1 unreadable. 56func mg_hash_file_into(ctx: *Sha256, path: *u8) -> i64 { 57 let fd: i64 = sys_openat_rd(path) 58 if fd<0 { return 0-1 } 59 let buf: *u8 = sys_mmap(MG_HASH_WIN) 60 var total: i64=0 61 var go: i64=1 62 var bad: i64=0 63 while go==1 { 64 let r: i64 = sys_read(fd, buf, MG_HASH_WIN) 65 if r<0 { bad=1; go=0 } else { if r==0 { go=0 } else { sha256_update(ctx, buf, r); total=total+r } } 66 } 67 sys_close(fd) 68 if bad==1 { return 0-1 } 69 return total 70} 71 72// full-file sha256 hex of an existing artifact (for the idempotency check). Returns bytes, -1 unreadable. 73func mg_hash_file_hex(path: *u8, out_hex: *u8) -> i64 { 74 let ctx_raw: *u8 = sys_mmap(256) 75 let ctx: *Sha256 = ctx_raw as *Sha256 76 sha256_init(ctx) 77 let n: i64 = mg_hash_file_into(ctx, path) 78 if n<0 { return 0-1 } 79 let dig: *u8 = sys_mmap(32) 80 sha256_final(ctx, dig) 81 wp_hexenc(dig, 32, out_hex) 82 return n 83} 84 85// ONE bytes=0-0 range request with HEADER visibility (status, Location, Content-Range total, Content-Length). 86// The shipped nx_https_fetch_range returns only the body, so redirect-following a range pull needs this probe. 87// Returns: 3 REDIRECT (locout filled) | 2 RANGE-OK (totbox = total from Content-Range) | 88// 1 NO-RANGE (200; totbox = Content-Length or -1) | negative = transport/parse failure. 89func mg_probe(urlbuf: *u8, store: *TrustStore, use_chrome: i64, st: *i64, locout: *u8, totbox: *i64) -> i64 { 90 st[0]=0 91 totbox[0]=0-1 92 let target_raw: *u8 = sys_mmap(64) 93 let target: *NxHttpsTarget = target_raw as *NxHttpsTarget 94 target.url = nx_url_new() 95 target.port = 0 96 if nx_https_url_for_fetch(urlbuf, target) != NX_HTTPS_URL_OK { return 0-1 } 97 let fd_p: *i64 = (sys_mmap(16)) as *i64 98 if nx_https_url_connect(target, urlbuf, sys_now_realtime_sec(), fd_p) != NX_HTTPS_CONNECT_OK { return 0-2 } 99 let fd: i64 = fd_p[0] 100 sys_set_socket_timeout(fd, 30) 101 let cr: *u8 = sys_mmap(32) 102 let priv: *u8 = sys_mmap(32) 103 nx_csprng_fill(cr, 32) 104 nx_csprng_fill(priv, 32) 105 let vc_raw: *u8 = sys_mmap(64) 106 let vc: *TlsValidationContext = vc_raw as *TlsValidationContext 107 vc.store = store 108 vc.sni_host = urlbuf + target.url.host_off 109 vc.sni_host_len = target.url.host_len 110 vc.now_epoch = sys_now_realtime_sec() 111 var sr: i64 = 0 112 if use_chrome==1 { sr = nx_tls13_client_session_run_chrome(fd, urlbuf + target.url.host_off, target.url.host_len, cr, priv, vc) } 113 else { sr = nx_tls13_client_session_run(fd, urlbuf + target.url.host_off, target.url.host_len, cr, priv, vc) } 114 if sr<=0 { sys_close(fd); return 0-3 } 115 let session: *Tls13ClientSession = sr as *Tls13ClientSession 116 let path: *u8 = sys_mmap(2048) 117 let plen: i64 = ff_path(urlbuf, target, path) 118 let req: *u8 = sys_mmap(4096) 119 let req_len: i64 = nx_http_build_range_get(path, plen, urlbuf + target.url.host_off, target.url.host_len, 0, 0, req) 120 let buf: *u8 = sys_mmap(MG_PROBE_CAP) 121 let gc: i64 = nx_https_req_complete(session, fd, req, req_len, buf, MG_PROBE_CAP) 122 sys_close(fd) 123 if gc<0 { return 0-4 } 124 let r: *i64 = (sys_mmap(128)) as *i64 125 if nx_http_response_parse(buf, gc, r) != 0 { return 0-5 } 126 st[0] = r[1] 127 let body_off: i64 = r[6] 128 if nx_redir_is_redirect(r[1])==1 { 129 let loc: *u8 = sys_mmap(MG_URLCAP) 130 let ln: i64 = nx_redir_location(buf, gc, loc, MG_URLCAP) 131 if ln<=0 { return 0-6 } 132 let resolved: *u8 = sys_mmap(MG_URLCAP) 133 ff_resolve_location(loc, urlbuf, target, resolved) 134 var k: i64=0 135 while resolved[k]!=(0 as u8) { locout[k]=resolved[k]; k=k+1 } 136 locout[k]=0 as u8 137 return 3 138 } 139 if r[1]==206 { totbox[0] = mg_content_range_total(buf, body_off); return 2 } 140 if r[1]==200 { totbox[0] = mg_content_length(buf, body_off); return 1 } 141 return 0-7 142} 143 144// follow redirects (bounded) until a range-answering endpoint; urlbuf ends holding the FINAL url. 145// chromebox carries/returns the TLS-hello flavor that worked. Returns probe mode (1/2) or negative. 146func mg_resolve(urlbuf: *u8, store: *TrustStore, chromebox: *i64, totbox: *i64) -> i64 { 147 let st: *i64 = (sys_mmap(16)) as *i64 148 let loc: *u8 = sys_mmap(MG_URLCAP) 149 var hop: i64=0 150 var lastrc: i64=0-9 151 while hop<=MG_HOPS_MAX { 152 var rc: i64 = mg_probe(urlbuf, store, chromebox[0], st, loc, totbox) 153 if rc<0 { if chromebox[0]==0 { 154 chromebox[0]=1 155 rc = mg_probe(urlbuf, store, 1, st, loc, totbox) 156 } } 157 if rc==3 { 158 mgo_put("hop=" as *u8); mgo_num(hop+1); mgo_put(" status=" as *u8); mgo_num(st[0]); mgo_put(" -> " as *u8); mgo_put(loc); mgo_put("\n" as *u8) 159 var k: i64=0 160 while loc[k]!=(0 as u8) { urlbuf[k]=loc[k]; k=k+1 } 161 urlbuf[k]=0 as u8 162 hop=hop+1 163 } else { 164 if rc==2 { return 2 } 165 if rc==1 { return 1 } 166 mgo_put("probe rc=" as *u8); mgo_num(rc); mgo_put(" status=" as *u8); mgo_num(st[0]); mgo_put("\n" as *u8) 167 return rc 168 } 169 lastrc=rc 170 } 171 return 0-8 172} 173 174func mg_receipt(verdict: *u8, dest: *u8, bytes: i64, hex: *u8, url: *u8) -> i64 { 175 let fd: i64 = sys_openat_append(MG_RECEIPT_PATH, MODE_0644) 176 if fd<0 { mgo_put("receipt=UNWRITTEN (knowledge/status/modelget.log not writable from this cwd)\n" as *u8); return 0-1 } 177 let line: *u8 = sys_mmap(8192) 178 var o: i64=0 179 o = mg_cat(line, o, "ts=" as *u8) 180 let t: *u8 = sys_mmap(28) 181 var m: i64 = sys_now_realtime_sec() 182 var k: i64=0 183 if m==0 { t[0]=48 as u8; k=1 } 184 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 185 var j: i64=k-1 186 while j>=0 { line[o]=t[j]; o=o+1; j=j-1 } 187 o = mg_cat(line, o, " verdict=" as *u8) 188 o = mg_cat(line, o, verdict) 189 o = mg_cat(line, o, " dest=" as *u8) 190 o = mg_cat(line, o, dest) 191 o = mg_cat(line, o, " bytes=" as *u8) 192 var b: i64 = bytes 193 if b<0 { line[o]=45 as u8; o=o+1; b=0-b } 194 k=0 195 if b==0 { t[0]=48 as u8; k=1 } 196 while b>0 { t[k]=(48+(b%10)) as u8; b=b/10; k=k+1 } 197 j=k-1 198 while j>=0 { line[o]=t[j]; o=o+1; j=j-1 } 199 o = mg_cat(line, o, " sha256=" as *u8) 200 o = mg_cat(line, o, hex) 201 o = mg_cat(line, o, " url=" as *u8) 202 o = mg_cat(line, o, url) 203 line[o]=10 as u8 204 o=o+1 205 sys_write(fd, line, o) 206 sys_fsync(fd) 207 sys_close(fd) 208 mgo_put("receipt=knowledge/status/modelget.log\n" as *u8) 209 return 0 210} 211 212func main(argc: i64, argv: *i64) -> i64 { 213 if argc<4 { 214 mgo_put("usage: nx_model_get <url> <expected-sha256> <dest-name>\n dest-name is relative to the model-store root (diffusion/... or loras/...)\n" as *u8) 215 return 2 216 } 217 let url: *u8 = argv[1] as *u8 218 let hexarg: *u8 = argv[2] as *u8 219 let name: *u8 = argv[3] as *u8 220 221 let exphex: *u8 = sys_mmap(80) 222 if mg_hex_norm(hexarg, exphex)!=1 { 223 mgo_put("REFUSED-BAD-HASH rule=expected-sha256-must-be-64-hex -- got " as *u8); mgo_put(hexarg); mgo_put("\n" as *u8) 224 return 2 225 } 226 if mg_dest_ok(name)!=1 { 227 mgo_put("REFUSED-DEST-NAME rule=relative-name-only (no leading slash, no .., no backslash) -- got " as *u8); mgo_put(name); mgo_put("\n" as *u8) 228 return 2 229 } 230 231 // ---- model-store root + window from the conf (env > conf > code default: rule 17; provenance announced) ---- 232 let root: *u8 = sys_mmap(2048) 233 var window: i64 = MG_WINDOW_DEFAULT 234 var rootsrc: *u8 = "DEFAULT" as *u8 235 let clen: *i64 = (sys_mmap(16)) as *i64 236 let conf: *u8 = sys_read_file(MG_CONF_PATH, clen) 237 if (conf as i64)!=0 { 238 let rl: i64 = mg_conf_val(conf, clen[0], "root" as *u8, root, 2048) 239 if rl>0 { rootsrc = "CONF" as *u8 } 240 let wv: *u8 = sys_mmap(64) 241 let wl: i64 = mg_conf_val(conf, clen[0], "window_bytes" as *u8, wv, 64) 242 if wl>0 { let w: i64 = mg_parse_uint(wv, wl, 0); if w>0 { window = w } } 243 } 244 if root[0]==(0 as u8) { mg_cat(root, 0, MG_ROOT_DEFAULT); root[mg_slen(MG_ROOT_DEFAULT)]=0 as u8 } 245 mgo_put("store_root=" as *u8); mgo_put(root); mgo_put(" (" as *u8); mgo_put(rootsrc); mgo_put(") window_bytes=" as *u8); mgo_num(window); mgo_put("\n" as *u8) 246 247 let dest: *u8 = sys_mmap(4096) 248 mg_join(root, name, dest) 249 let part: *u8 = sys_mmap(4096) 250 var po: i64 = mg_cat(part, 0, dest) 251 po = mg_cat(part, po, ".part" as *u8) 252 part[po]=0 as u8 253 254 // ---- idempotency: an existing verified dest is a no-op (no network) ---- 255 let efd: i64 = sys_openat_rd(dest) 256 if efd>=0 { 257 sys_close(efd) 258 let ehex: *u8 = sys_mmap(80) 259 let en: i64 = mg_hash_file_hex(dest, ehex) 260 if en>=0 { if wp_streq(ehex, exphex)==1 { 261 mgo_put("OK-ALREADY-VERIFIED dest=" as *u8); mgo_put(dest); mgo_put(" bytes=" as *u8); mgo_num(en); mgo_put(" sha256=" as *u8); mgo_put(ehex); mgo_put("\n" as *u8) 262 return 0 263 } } 264 mgo_put("REFUSED-EXISTING-MISMATCH dest=" as *u8); mgo_put(dest); mgo_put(" bytes=" as *u8); mgo_num(en); mgo_put(" sha256=" as *u8); mgo_put(ehex); mgo_put(" expected=" as *u8); mgo_put(exphex) 265 mgo_put(" rule=additive-only -- an existing store artifact is never overwritten or deleted; remove it deliberately or pick a new dest-name\n" as *u8) 266 return 1 267 } 268 269 // ---- trust store (same Mozilla bundle every sovereign fetcher loads) ---- 270 var tr: i64 = nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, MG_CERT_ROOTS, MG_CERT_CAP) 271 if tr<=0 { tr = nx_trust_store_load_from_certdata("/tmp/mozilla_certdata.txt" as *u8, MG_CERT_ROOTS, MG_CERT_CAP) } 272 if tr<=0 { mgo_put("UNPROVEN trust-store-load-failed (need data/mozilla_certdata.txt on cwd) -- nothing fetched\n" as *u8); return 3 } 273 let store: *TrustStore = tr as *TrustStore 274 275 return mg_fetch(url, exphex, dest, part, store, window) 276} 277 278// G9 mg_fetch -- THE CONTRACT SYMBOL (/compare/gen watch row MODEL: hash-verified acquisition). resolve redirects 279// -> range-window stream (hash as it lands) -> fail-closed verify over the FULL body -> rename into the store. 280// exit-code shaped return: 0 VERIFIED | 1 REFUSED-HASH-MISMATCH | 3 UNPROVEN (transport; partial kept for resume) 281func mg_fetch(url: *u8, exphex: *u8, dest: *u8, part: *u8, store: *TrustStore, window: i64) -> i64 { 282 // ---- resolve redirects to the final range-answering url ---- 283 let urlbuf: *u8 = sys_mmap(MG_URLCAP) 284 var ui: i64=0 285 while url[ui]!=(0 as u8) { urlbuf[ui]=url[ui]; ui=ui+1 } 286 urlbuf[ui]=0 as u8 287 let chromebox: *i64 = (sys_mmap(16)) as *i64 288 chromebox[0]=0 289 let totbox: *i64 = (sys_mmap(16)) as *i64 290 var mode: i64 = mg_resolve(urlbuf, store, chromebox, totbox) 291 if mode<0 { mgo_put("UNPROVEN transport-resolve-failed rc=" as *u8); mgo_num(mode); mgo_put(" -- nothing stored\n" as *u8); return 3 } 292 var total: i64 = totbox[0] 293 if total<=0 { mgo_put("UNPROVEN no-declared-length (no Content-Range total, no Content-Length) -- refusing: completeness would be unjudgeable\n" as *u8); return 3 } 294 mgo_put("resolved mode=" as *u8); mgo_num(mode); mgo_put(" total_bytes=" as *u8); mgo_num(total); mgo_put(" chrome_hello=" as *u8); mgo_num(chromebox[0]); mgo_put("\n" as *u8) 295 if mode==1 { if total>window { 296 mgo_put("REFUSED-NO-RANGE-SUPPORT rule=a-body-larger-than-the-window-must-stream -- server ignored Range and the body (" as *u8); mgo_num(total) 297 mgo_put(" B) exceeds window_bytes (" as *u8); mgo_num(window); mgo_put(" B); refusing rather than truncate\n" as *u8) 298 return 3 299 } } 300 301 // ---- streaming hash context; resume from .part if one exists ---- 302 let ctx_raw: *u8 = sys_mmap(256) 303 let ctx: *Sha256 = ctx_raw as *Sha256 304 sha256_init(ctx) 305 var base: i64=0 306 let pfd: i64 = sys_openat_rd(part) 307 if pfd>=0 { 308 sys_close(pfd) 309 let pn: i64 = mg_hash_file_into(ctx, part) 310 if pn>0 { if pn<total { 311 base=pn 312 mgo_put("RESUME part=" as *u8); mgo_put(part); mgo_put(" bytes_already=" as *u8); mgo_num(pn); mgo_put("\n" as *u8) 313 } } 314 if base==0 { 315 sys_unlinkat(part) 316 sha256_init(ctx) 317 mgo_put("part-discarded (empty or not smaller than total) -- restarting from 0\n" as *u8) 318 } 319 } 320 321 let afd: i64 = sys_openat_append(part, MODE_0644) 322 if afd<0 { mgo_put("UNPROVEN cannot-open-part-for-append dest=" as *u8); mgo_put(part); mgo_put("\n" as *u8); return 3 } 323 324 // ---- the window loop: fetch range -> hash -> append; never more than one window in memory ---- 325 let wbuf: *u8 = sys_mmap(window) 326 let st: *i64 = (sys_mmap(16)) as *i64 327 var off: i64=base 328 var winfails: i64=0 329 var wincount: i64=0 330 var stalled: i64=0 331 while off<total { 332 var want: i64=window 333 if off+want>total { want=total-off } 334 let n: i64 = nx_https_fetch_range(urlbuf, store, wbuf, window, off, off+want-1, st, chromebox[0]) 335 var okwin: i64=0 336 if n==want { 337 if st[0]==206 { okwin=1 } 338 if st[0]==200 { if off==0 { if want==total { okwin=1 } } } 339 } 340 if okwin==0 { 341 winfails=winfails+1 342 mgo_put("window-retry off=" as *u8); mgo_num(off); mgo_put(" got=" as *u8); mgo_num(n); mgo_put(" want=" as *u8); mgo_num(want); mgo_put(" status=" as *u8); mgo_num(st[0]); mgo_put(" fail=" as *u8); mgo_num(winfails); mgo_put("\n" as *u8) 343 if winfails>=MG_WIN_RETRIES { 344 sys_close(afd) 345 mgo_put("UNPROVEN transport-stall at off=" as *u8); mgo_num(off); mgo_put(" of " as *u8); mgo_num(total) 346 mgo_put(" -- PARTIAL KEPT for resume at " as *u8); mgo_put(part); mgo_put(" (re-run the same command)\n" as *u8) 347 off=total 348 stalled=1 349 } else { 350 // a signed CDN url may have expired mid-pull: re-resolve from the ORIGINAL url and retry this window 351 var ri: i64=0 352 while url[ri]!=(0 as u8) { urlbuf[ri]=url[ri]; ri=ri+1 } 353 urlbuf[ri]=0 as u8 354 let m2: i64 = mg_resolve(urlbuf, store, chromebox, totbox) 355 if m2<0 { mgo_put("re-resolve failed rc=" as *u8); mgo_num(m2); mgo_put("\n" as *u8) } 356 } 357 } else { 358 winfails=0 359 sha256_update(ctx, wbuf, n) 360 var sent: i64=0 361 var wbad: i64=0 362 while sent<n { let w: i64 = sys_write(afd, (wbuf as i64 + sent) as *u8, n-sent); if w<=0 { wbad=1; sent=n } else { sent=sent+w } } 363 if wbad==1 { 364 sys_close(afd) 365 mgo_put("UNPROVEN disk-write-failed at off=" as *u8); mgo_num(off); mgo_put(" -- PARTIAL KEPT at " as *u8); mgo_put(part); mgo_put("\n" as *u8) 366 off=total 367 stalled=1 368 } else { 369 off=off+n 370 wincount=wincount+1 371 if wincount%MG_PROGRESS_EVERY==0 { mgo_put("progress bytes=" as *u8); mgo_num(off); mgo_put(" of " as *u8); mgo_num(total); mgo_put("\n" as *u8) } 372 } 373 } 374 } 375 if stalled==1 { return 3 } 376 sys_fsync(afd) 377 sys_close(afd) 378 379 // ---- fail-closed verify over the FULL body (the nx_weights_pull law: never store a corrupt artifact) ---- 380 let dig: *u8 = sys_mmap(32) 381 sha256_final(ctx, dig) 382 let got: *u8 = sys_mmap(80) 383 wp_hexenc(dig, 32, got) 384 if wp_streq(got, exphex)==1 { 385 sys_renameat(part, dest) 386 mgo_put("VERIFIED dest=" as *u8); mgo_put(dest); mgo_put(" bytes=" as *u8); mgo_num(total); mgo_put(" sha256=" as *u8); mgo_put(got); mgo_put("\n" as *u8) 387 mg_receipt("VERIFIED" as *u8, dest, total, got, url) 388 return 0 389 } 390 sys_unlinkat(part) 391 mgo_put("REFUSED-HASH-MISMATCH expected=" as *u8); mgo_put(exphex); mgo_put(" got=" as *u8); mgo_put(got); mgo_put(" bytes=" as *u8); mgo_num(total) 392 mgo_put(" rule=fail-closed-verify -- the partial was DELETED; a corrupt, tampered or truncated body is never stored (nx_weights_pull law)\n" as *u8) 393 mg_receipt("REFUSED-HASH-MISMATCH" as *u8, dest, total, got, url) 394 return 1 395}