gguf-hash.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. #include "ggml.h"
  2. #include "gguf.h"
  3. #include <cstdlib> /* abort() */
  4. #include <cstddef>
  5. #include <cstdio>
  6. #include <string>
  7. #include <stdexcept>
  8. #include <algorithm>
  9. #include <cstring>
  10. #include <sstream>
  11. #include <fstream>
  12. #ifdef __cplusplus
  13. extern "C" {
  14. #endif
  15. #include "xxhash/xxhash.h"
  16. #include "sha1/sha1.h"
  17. #include "sha256/sha256.h"
  18. #ifdef __cplusplus
  19. }
  20. #endif
  21. // uuid.uuid5(uuid.NAMESPACE_URL, 'en.wikipedia.org/wiki/Llama.cpp')
  22. #define UUID_NAMESPACE_LLAMA_CPP "ef001206-dadc-5f6d-a15f-3359e577d4e5"
  23. #define UUID_NAMESPACE_LLAMA_CPP_HEX 0xef, 0x00, 0x12, 0x06, 0xda, 0xdc, 0x5f, 0x6d, 0xa1, 0x5f, 0x33, 0x59, 0xe5, 0x77, 0xd4, 0xe5
  24. #define HASH_TYPE_SHA256_STR "sha256"
  25. #define HASH_TYPE_SHA1_STR "sha1"
  26. #define HASH_TYPE_XXH64_STR "xxh64"
  27. #define HASH_TYPE_UUID_STR "uuid"
  28. typedef enum {
  29. HASH_EXIT_SUCCESS = 0, // All hash has been generated or validated
  30. HASH_EXIT_FAILURE = 1, // Generic Failure
  31. HASH_EXIT_MISMATCH = 2, // Hash mismatched during validation
  32. HASH_EXIT_MANIFEST_MISSING_ENTRY = 3, // Hash attempted validation but missing entry in manifest
  33. HASH_EXIT_MANIFEST_UNKNOWN_HASH = 4, // Manifest is present, but we do not know any hash format within it
  34. HASH_EXIT_MANIFEST_FILE_ERROR = 5 // Manifest is either missing or not a known format
  35. } hash_exit_code_t;
  36. typedef enum {
  37. HASH_MANIFEST_NOT_FOUND,
  38. HASH_MANIFEST_MISMATCH,
  39. HASH_MANIFEST_OK,
  40. } hash_manifest_result_t;
  41. struct hash_params {
  42. std::string input;
  43. bool xxh64 = false;
  44. bool sha1 = false;
  45. bool sha256 = false;
  46. bool uuid = false;
  47. bool no_layer = false;
  48. bool manifest_is_usable = false;
  49. std::string manifest_file;
  50. };
  51. struct manifest_check_params {
  52. bool xxh64 = false;
  53. bool sha1 = false;
  54. bool sha256 = false;
  55. bool uuid = false;
  56. };
  57. static char const * hash_manifest_result_to_str(hash_manifest_result_t value) {
  58. switch (value) {
  59. case HASH_MANIFEST_NOT_FOUND: return "Not Found";
  60. case HASH_MANIFEST_MISMATCH: return "Mismatch";
  61. case HASH_MANIFEST_OK: return "Ok";
  62. }
  63. return "?";
  64. }
  65. static char const * hash_exit_code_to_str(hash_exit_code_t value) {
  66. switch (value) {
  67. case HASH_EXIT_SUCCESS: return "Success";
  68. case HASH_EXIT_FAILURE: return "Failure";
  69. case HASH_EXIT_MISMATCH: return "Mismatch";
  70. case HASH_EXIT_MANIFEST_MISSING_ENTRY: return "Manifest Missing Entry";
  71. case HASH_EXIT_MANIFEST_UNKNOWN_HASH: return "Manifest Unknown Hash";
  72. case HASH_EXIT_MANIFEST_FILE_ERROR: return "Manifest File Error";
  73. }
  74. return "?";
  75. }
  76. static void hash_print_usage(const char * executable) {
  77. const hash_params default_params;
  78. printf("\n");
  79. printf("usage: %s [options] GGUF_IN\n", executable);
  80. printf("\n");
  81. printf("Hash a GGUF file");
  82. printf("\n");
  83. printf("options:\n");
  84. printf(" -h, --help show this help message and exit\n");
  85. printf(" --xxh64 use xxh64 hash\n");
  86. printf(" --sha1 use sha1 hash\n");
  87. printf(" --sha256 use sha256 hash\n");
  88. printf(" --all use all hash\n");
  89. printf(" --no-layer exclude per layer hash\n");
  90. printf(" --uuid generate UUIDv5 ID\n");
  91. printf(" -c, --check <manifest> verify against a manifest\n");
  92. printf("\n");
  93. }
  94. static void hash_params_parse_ex(int argc, const char ** argv, hash_params & params) {
  95. std::string arg;
  96. bool invalid_param = false;
  97. const std::string arg_prefix = "--";
  98. int arg_idx = 1;
  99. for (; arg_idx < argc && strncmp(argv[arg_idx], "--", 2) == 0; arg_idx++) {
  100. arg = argv[arg_idx];
  101. if (arg.compare(0, arg_prefix.size(), arg_prefix) == 0) {
  102. std::replace(arg.begin(), arg.end(), '_', '-');
  103. }
  104. bool arg_found = false;
  105. if (arg == "-h" || arg == "--help") {
  106. hash_print_usage(argv[0]);
  107. exit(0);
  108. }
  109. if (arg == "--xxh64") {
  110. arg_found = true;
  111. params.xxh64 = true;
  112. }
  113. if (arg == "--sha1") {
  114. arg_found = true;
  115. params.sha1 = true;
  116. }
  117. if (arg == "--uuid") {
  118. arg_found = true;
  119. params.uuid = true;
  120. }
  121. if (arg == "--sha256") {
  122. arg_found = true;
  123. params.sha256 = true;
  124. }
  125. if (arg == "--all") {
  126. arg_found = true;
  127. params.sha256 = true;
  128. params.sha1 = true;
  129. params.xxh64 = true;
  130. }
  131. if (arg == "--no-layer") {
  132. arg_found = true;
  133. params.no_layer = true;
  134. }
  135. if (arg == "-c" || arg == "--check") {
  136. if (++arg_idx >= argc) {
  137. invalid_param = true;
  138. break;
  139. }
  140. arg_found = true;
  141. params.manifest_file = argv[arg_idx];
  142. }
  143. if (!arg_found) {
  144. throw std::invalid_argument("error: unknown argument: " + arg);
  145. }
  146. }
  147. if (invalid_param) {
  148. throw std::invalid_argument("error: invalid parameter for argument:" + arg);
  149. }
  150. if (argc - arg_idx < 1) {
  151. throw std::invalid_argument("error: bad arguments");
  152. }
  153. params.input = argv[arg_idx++];
  154. }
  155. static bool hash_params_parse(int argc, const char ** argv, hash_params & params) {
  156. bool result = true;
  157. try {
  158. hash_params_parse_ex(argc, argv, params);
  159. }
  160. catch (const std::invalid_argument & ex) {
  161. fprintf(stderr, "%s\n", ex.what());
  162. hash_print_usage(argv[0]);
  163. exit(EXIT_FAILURE);
  164. }
  165. return result;
  166. }
  167. static bool manifest_type(const std::string & manifest_file, manifest_check_params & manifest_check) {
  168. if (manifest_file.empty()) {
  169. return false;
  170. }
  171. std::ifstream file(manifest_file);
  172. if (!file.is_open()) {
  173. return false;
  174. }
  175. std::string manifest_entry_line;
  176. while (getline(file, manifest_entry_line)) {
  177. // hash_type_str hash_str tensor_name
  178. // e.g. 'xxh64 f66e9cd66a4396a0 test.gguf:tensor_0'
  179. std::istringstream line_stream(manifest_entry_line);
  180. std::string file_hash_type;
  181. if (line_stream >> file_hash_type) {
  182. if (file_hash_type == HASH_TYPE_SHA256_STR) {
  183. manifest_check.sha256 = true;
  184. } else if (file_hash_type == HASH_TYPE_SHA1_STR) {
  185. manifest_check.sha1 = true;
  186. } else if (file_hash_type == HASH_TYPE_XXH64_STR) {
  187. manifest_check.xxh64 = true;
  188. } else if (file_hash_type == HASH_TYPE_UUID_STR) {
  189. manifest_check.uuid = true;
  190. }
  191. }
  192. }
  193. return true;
  194. }
  195. static hash_manifest_result_t manifest_verify(const std::string& manifest_file, const std::string& hash_type_str, const std::string& hash_str, const std::string& tensor_name) {
  196. if (manifest_file.empty()) {
  197. return HASH_MANIFEST_NOT_FOUND;
  198. }
  199. std::ifstream file(manifest_file);
  200. if (!file.is_open()) {
  201. return HASH_MANIFEST_NOT_FOUND;
  202. }
  203. std::string manifest_entry_line;
  204. while (getline(file, manifest_entry_line)) {
  205. std::istringstream line_stream(manifest_entry_line);
  206. std::string file_hash_type;
  207. std::string file_hash;
  208. std::string file_tensor_name;
  209. if (line_stream >> file_hash_type >> file_hash >> file_tensor_name) {
  210. // Line parsed. Check hash validity
  211. if (file_hash_type != hash_type_str) {
  212. continue;
  213. }
  214. if (file_tensor_name != tensor_name) {
  215. continue;
  216. }
  217. return (file_hash == hash_str) ? HASH_MANIFEST_OK : HASH_MANIFEST_MISMATCH;
  218. }
  219. }
  220. return HASH_MANIFEST_NOT_FOUND;
  221. }
  222. static void generate_uuidv5(const unsigned char sha1_digest[20], unsigned char uuid[16]) {
  223. // Ref: https://www.rfc-editor.org/rfc/rfc9562.html#section-5.5
  224. // Assumes that digest was processed correctly with the expected namespace
  225. for (int i = 0; i < 16; i++) {
  226. uuid[i] = sha1_digest[i];
  227. }
  228. // Set bits corresponding to UUID ver 5
  229. uuid[ 6] &= ~(0xF << 4);
  230. uuid[ 6] |= (5 << 4);
  231. // Set bits corresponding to UUID variant 0b10XX
  232. uuid[ 8] &= ~(0xc << 4);
  233. uuid[ 8] |= (0x8 << 4);
  234. }
  235. static hash_exit_code_t gguf_hash(const hash_params & hash_params) {
  236. const std::string & fname = hash_params.input;
  237. struct ggml_context * ctx_data = NULL;
  238. struct gguf_init_params params = {
  239. /*.no_alloc = */ false,
  240. /*.ctx = */ &ctx_data,
  241. };
  242. // xxh64 init
  243. XXH64_state_t* xxh64_model_hash_state = NULL;
  244. if (hash_params.xxh64) {
  245. xxh64_model_hash_state = XXH64_createState();
  246. if (xxh64_model_hash_state==NULL) {
  247. abort();
  248. }
  249. XXH64_hash_t const seed = 0;
  250. if (XXH64_reset(xxh64_model_hash_state, seed) == XXH_ERROR) {
  251. abort();
  252. }
  253. }
  254. // sha1 init
  255. SHA1_CTX sha1_model_hash_ctx;
  256. if (hash_params.sha1) {
  257. SHA1Init(&sha1_model_hash_ctx);
  258. }
  259. // sha256 init
  260. sha256_t sha256_model_hash_ctx;
  261. if (hash_params.sha256) {
  262. sha256_init(&sha256_model_hash_ctx);
  263. }
  264. // sha1 for uuid init
  265. SHA1_CTX sha1_for_uuid_ctx;
  266. if (hash_params.uuid) {
  267. unsigned char const uuidv5_namespace[] = {UUID_NAMESPACE_LLAMA_CPP_HEX};
  268. SHA1Init(&sha1_for_uuid_ctx);
  269. SHA1Update( &sha1_for_uuid_ctx, (unsigned char const *)uuidv5_namespace, sizeof(uuidv5_namespace));
  270. }
  271. struct gguf_context * ctx = gguf_init_from_file(fname.c_str(), params);
  272. const int n_tensors = gguf_get_n_tensors(ctx);
  273. bool tensor_layer_in_manifest = false;
  274. bool model_in_manifest = false;
  275. bool tensor_layer_has_mismatch = false;
  276. bool model_has_mismatch = false;
  277. for (int i = 0; i < n_tensors; ++i) {
  278. const char * name = gguf_get_tensor_name(ctx, i);
  279. struct ggml_tensor * cur = ggml_get_tensor(ctx_data, name);
  280. auto n_bytes = ggml_nbytes(cur);
  281. auto *raw_data = cur->data;
  282. const std::string tensor_layer_name = fname + ":" + name;
  283. if (hash_params.xxh64) {
  284. if (!hash_params.no_layer) {
  285. // Per Layer Hash
  286. XXH64_hash_t hash = XXH64(raw_data, n_bytes, 0);
  287. char hex_result[17];
  288. for (int offset = 0; offset < 8; offset++) {
  289. unsigned int shift_bits_by = (8 * (8 - offset - 1));
  290. snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", (unsigned char) (hash >> shift_bits_by)&0xff);
  291. }
  292. if (hash_params.manifest_is_usable) {
  293. hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_XXH64_STR, hex_result, tensor_layer_name);
  294. switch (verify_result) {
  295. case HASH_MANIFEST_NOT_FOUND:
  296. break;
  297. case HASH_MANIFEST_MISMATCH:
  298. tensor_layer_in_manifest = true;
  299. tensor_layer_has_mismatch = true;
  300. break;
  301. case HASH_MANIFEST_OK:
  302. tensor_layer_in_manifest = true;
  303. break;
  304. }
  305. printf("%-8s %-s %s - %s\n", HASH_TYPE_XXH64_STR, hex_result, tensor_layer_name.c_str(), hash_manifest_result_to_str(verify_result));
  306. } else {
  307. printf("%-8s %-s %s\n", HASH_TYPE_XXH64_STR, hex_result, tensor_layer_name.c_str());
  308. }
  309. }
  310. // Overall Model Hash
  311. if (XXH64_update(xxh64_model_hash_state, raw_data, n_bytes) == XXH_ERROR) abort();
  312. }
  313. if (hash_params.sha1) {
  314. if (!hash_params.no_layer) {
  315. // Per Layer Hash
  316. char result[21]; // sha1 outputs 20 bytes
  317. SHA1( result, (const char *)raw_data, n_bytes);
  318. char hex_result[41] = {0};
  319. for (int offset = 0; offset < 20; offset++) {
  320. snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", result[offset]&0xff);
  321. }
  322. if (hash_params.manifest_is_usable) {
  323. hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA1_STR, hex_result, tensor_layer_name);
  324. switch (verify_result) {
  325. case HASH_MANIFEST_NOT_FOUND:
  326. break;
  327. case HASH_MANIFEST_MISMATCH:
  328. tensor_layer_in_manifest = true;
  329. tensor_layer_has_mismatch = true;
  330. break;
  331. case HASH_MANIFEST_OK:
  332. tensor_layer_in_manifest = true;
  333. break;
  334. }
  335. printf("%-8s %-s %s - %s\n", HASH_TYPE_SHA1_STR, hex_result, tensor_layer_name.c_str(), hash_manifest_result_to_str(verify_result));
  336. } else {
  337. printf("%-8s %-s %s\n", HASH_TYPE_SHA1_STR, hex_result, tensor_layer_name.c_str());
  338. }
  339. }
  340. // Overall Model Hash
  341. SHA1Update( &sha1_model_hash_ctx, (unsigned char const *)raw_data, n_bytes);
  342. }
  343. if (hash_params.sha256) {
  344. if (!hash_params.no_layer) {
  345. // Per Layer Hash
  346. unsigned char result[SHA256_DIGEST_SIZE]; // sha256 outputs 32 bytes
  347. sha256_hash((unsigned char*) result, (const unsigned char *)raw_data, n_bytes);
  348. char hex_result[SHA256_DIGEST_SIZE * 2 + 1] = {0};
  349. for (int offset = 0; offset < SHA256_DIGEST_SIZE; offset++) {
  350. snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", result[offset]&0xff);
  351. }
  352. if (hash_params.manifest_is_usable) {
  353. hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA256_STR, hex_result, tensor_layer_name);
  354. switch (verify_result) {
  355. case HASH_MANIFEST_NOT_FOUND:
  356. break;
  357. case HASH_MANIFEST_MISMATCH:
  358. tensor_layer_in_manifest = true;
  359. tensor_layer_has_mismatch = true;
  360. break;
  361. case HASH_MANIFEST_OK:
  362. tensor_layer_in_manifest = true;
  363. break;
  364. }
  365. printf("%-8s %-s %s - %s\n", HASH_TYPE_SHA256_STR, hex_result, tensor_layer_name.c_str(), hash_manifest_result_to_str(verify_result));
  366. } else {
  367. printf("%-8s %-s %s\n", HASH_TYPE_SHA256_STR, hex_result, tensor_layer_name.c_str());
  368. }
  369. }
  370. // Overall Model Hash
  371. sha256_update( &sha256_model_hash_ctx, (unsigned char const *)raw_data, n_bytes);
  372. }
  373. if (hash_params.uuid) {
  374. SHA1Update( &sha1_for_uuid_ctx, (unsigned char const *)raw_data, n_bytes);
  375. }
  376. }
  377. if (hash_params.xxh64) {
  378. XXH64_hash_t const hash = XXH64_digest(xxh64_model_hash_state);
  379. char hex_result[17];
  380. for (int offset = 0; offset < 8; offset++) {
  381. unsigned int shift_bits_by = (8 * (8 - offset - 1));
  382. snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", (unsigned char) (hash >> shift_bits_by)&0xff);
  383. }
  384. if (hash_params.manifest_is_usable) {
  385. hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_XXH64_STR, hex_result, fname);
  386. switch (verify_result) {
  387. case HASH_MANIFEST_NOT_FOUND:
  388. break;
  389. case HASH_MANIFEST_MISMATCH:
  390. model_in_manifest = true;
  391. model_has_mismatch = true;
  392. break;
  393. case HASH_MANIFEST_OK:
  394. model_in_manifest = true;
  395. break;
  396. }
  397. printf("%-8s %-s %s - %s\n", HASH_TYPE_XXH64_STR, hex_result, fname.c_str(), hash_manifest_result_to_str(verify_result));
  398. } else {
  399. printf("%-8s %-s %s\n", HASH_TYPE_XXH64_STR, hex_result, fname.c_str());
  400. }
  401. }
  402. if (hash_params.sha1) {
  403. unsigned char result[21];
  404. SHA1Final(result, &sha1_model_hash_ctx);
  405. char hex_result[41];
  406. for (int offset = 0; offset < 20; offset++) {
  407. snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", result[offset]&0xff);
  408. }
  409. if (hash_params.manifest_is_usable) {
  410. hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA1_STR, hex_result, fname);
  411. switch (verify_result) {
  412. case HASH_MANIFEST_NOT_FOUND:
  413. break;
  414. case HASH_MANIFEST_MISMATCH:
  415. model_in_manifest = true;
  416. model_has_mismatch = true;
  417. break;
  418. case HASH_MANIFEST_OK:
  419. model_in_manifest = true;
  420. break;
  421. }
  422. printf("%-8s %-s %s - %s\n", HASH_TYPE_SHA1_STR, hex_result, fname.c_str(), hash_manifest_result_to_str(verify_result));
  423. } else {
  424. printf("%-8s %-s %s\n", HASH_TYPE_SHA1_STR, hex_result, fname.c_str());
  425. }
  426. }
  427. if (hash_params.sha256) {
  428. unsigned char result[SHA256_DIGEST_SIZE]; // sha256 outputs 32 bytes
  429. sha256_final( &sha256_model_hash_ctx, result);
  430. char hex_result[SHA256_DIGEST_SIZE * 2 + 1] = {0};
  431. for (int offset = 0; offset < SHA256_DIGEST_SIZE; offset++) {
  432. snprintf( ( hex_result + (2*offset)), sizeof(hex_result) - (2*offset), "%02x", result[offset]&0xff);
  433. }
  434. if (hash_params.manifest_is_usable) {
  435. hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA256_STR, hex_result, fname);
  436. switch (verify_result) {
  437. case HASH_MANIFEST_NOT_FOUND:
  438. break;
  439. case HASH_MANIFEST_MISMATCH:
  440. model_in_manifest = true;
  441. model_has_mismatch = true;
  442. break;
  443. case HASH_MANIFEST_OK:
  444. model_in_manifest = true;
  445. break;
  446. }
  447. printf("%-8s %-s %s - %s\n", HASH_TYPE_SHA256_STR, hex_result, fname.c_str(), hash_manifest_result_to_str(verify_result));
  448. } else {
  449. printf("%-8s %-s %s\n", HASH_TYPE_SHA256_STR, hex_result, fname.c_str());
  450. }
  451. }
  452. if (hash_params.uuid) {
  453. unsigned char result[21];
  454. SHA1Final(result, &sha1_for_uuid_ctx);
  455. unsigned char uuid[16];
  456. generate_uuidv5(result, uuid);
  457. char string_buffer[37] = {0};
  458. snprintf(string_buffer, sizeof(string_buffer), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
  459. uuid[0], uuid[1], uuid[2], uuid[3],
  460. uuid[4], uuid[5], uuid[6], uuid[7],
  461. uuid[8], uuid[9], uuid[10], uuid[11],
  462. uuid[12], uuid[13], uuid[14], uuid[15]);
  463. if (hash_params.manifest_is_usable) {
  464. hash_manifest_result_t verify_result = manifest_verify(hash_params.manifest_file, HASH_TYPE_SHA256_STR, string_buffer, fname);
  465. switch (verify_result) {
  466. case HASH_MANIFEST_NOT_FOUND:
  467. break;
  468. case HASH_MANIFEST_MISMATCH:
  469. model_in_manifest = true;
  470. model_has_mismatch = true;
  471. break;
  472. case HASH_MANIFEST_OK:
  473. model_in_manifest = true;
  474. break;
  475. }
  476. printf("%-8s %-s %s - %s\n", HASH_TYPE_UUID_STR, string_buffer, fname.c_str(), hash_manifest_result_to_str(verify_result));
  477. } else {
  478. printf("%-8s %-s %s\n", HASH_TYPE_UUID_STR, string_buffer, fname.c_str());
  479. }
  480. }
  481. ggml_free(ctx_data);
  482. gguf_free(ctx);
  483. if (hash_params.manifest_is_usable) {
  484. // In hash verification mode
  485. if (!model_in_manifest) {
  486. // model missing in manifest?
  487. // Check tensor layer...
  488. if (!tensor_layer_in_manifest) {
  489. // Still missing? Maybe we are reading the wrong manifest.
  490. return HASH_EXIT_MANIFEST_MISSING_ENTRY;
  491. }
  492. if (tensor_layer_has_mismatch) {
  493. // Per tensor check found error
  494. return HASH_EXIT_FAILURE;
  495. }
  496. // All per tensor layer checks passed? Sounds good enough.
  497. return HASH_EXIT_SUCCESS;
  498. }
  499. // Overall model check passed, but let's check per layer just in case
  500. // If missing, we don't care too much as the overall model checked
  501. if (tensor_layer_in_manifest && tensor_layer_has_mismatch) {
  502. return HASH_EXIT_FAILURE;
  503. }
  504. if (model_has_mismatch) {
  505. // model has failed hash somewhere in the model
  506. return HASH_EXIT_FAILURE;
  507. }
  508. // All checks appears to be fine
  509. return HASH_EXIT_SUCCESS;
  510. }
  511. // In hash generation mode
  512. return HASH_EXIT_SUCCESS;
  513. }
  514. int main(int argc, const char ** argv) {
  515. hash_params params;
  516. manifest_check_params manifest_check;
  517. hash_params_parse(argc, argv, params);
  518. if (!params.manifest_file.empty()) {
  519. if (!manifest_type(params.manifest_file, manifest_check)) {
  520. printf("ERROR cannot open manifest %s", params.manifest_file.c_str());
  521. return HASH_EXIT_MANIFEST_FILE_ERROR;
  522. }
  523. if (!manifest_check.sha256 && !manifest_check.sha1 && !manifest_check.xxh64 && !manifest_check.uuid) {
  524. printf("ERROR manifest does not have any known hash format in %s", params.manifest_file.c_str());
  525. return HASH_EXIT_MANIFEST_UNKNOWN_HASH;
  526. }
  527. printf("manifest %s", params.manifest_file.c_str());
  528. if (manifest_check.sha256) {
  529. printf(" sha256");
  530. }
  531. if (manifest_check.sha1) {
  532. printf(" sha1");
  533. }
  534. if (manifest_check.xxh64) {
  535. printf(" xxh64");
  536. }
  537. if (manifest_check.uuid) {
  538. printf(" uuid");
  539. }
  540. printf("\n");
  541. // Autoselect the highest security hash if manifest is provided but
  542. // the user has not specifically defined the hash they care about
  543. if (!params.xxh64 && !params.sha1 && !params.uuid && !params.sha256) {
  544. // User has not selected a specific value, pick most secure hash
  545. if (manifest_check.sha256) {
  546. params.sha256 = true;
  547. } else if (manifest_check.sha1) {
  548. params.sha1 = true;
  549. } else if (manifest_check.xxh64) {
  550. params.xxh64 = true;
  551. } else if (manifest_check.uuid) {
  552. params.uuid = true;
  553. }
  554. }
  555. params.manifest_is_usable = true;
  556. }
  557. // By default if no swich argument provided, assume xxh64
  558. if (!params.xxh64 && !params.sha1 && !params.uuid && !params.sha256) {
  559. params.xxh64 = true;
  560. }
  561. hash_exit_code_t exit_code = gguf_hash(params);
  562. if (params.manifest_is_usable) {
  563. printf("\nVerification results for %s - %s\n", params.manifest_file.c_str(), hash_exit_code_to_str(exit_code));
  564. }
  565. return exit_code;
  566. }