this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: update zig verify to specialized MST decoder (zat v0.2.6)

uses decodeMstNode + in-walk key height verification instead of generic
CBOR decode + full MST rebuild. MST step: 218ms → 39ms on pfrazee.com.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

zzstoatzz a4f54f2f 44e88edf

+47 -61
+2 -2
zig/build.zig.zon
··· 5 5 .minimum_zig_version = "0.15.0", 6 6 .dependencies = .{ 7 7 .zat = .{ 8 - .url = "https://tangled.sh/zat.dev/zat/archive/v0.2.2", 9 - .hash = "zat-0.2.0-5PuC7mAuBADmN7OZkGvymjk9jq4akxdl-IlHSo8r25pr", 8 + .url = "https://tangled.sh/zat.dev/zat/archive/v0.2.6", 9 + .hash = "zat-0.2.6-5PuC7m5IBADrKBpmzfx23NYdlzAmKk_iKvJi8VNd06F8", 10 10 }, 11 11 .k256 = .{ 12 12 .path = "../../k256",
+45 -59
zig/src/verify.zig
··· 181 181 182 182 const sig_ns = step_timer.read(); 183 183 184 - // --- step 7: walk MST --- 184 + // --- step 7: walk + verify MST (specialized decoder + key height checks) --- 185 185 step_timer = try std.time.Timer.start(); 186 186 187 - var records: std.ArrayList(MstRecord) = .{}; 188 - try walkMst(alloc, repo_car.block_map, data_cid.raw, &records); 189 - 190 - const walk_ns = step_timer.read(); 191 - 192 - // --- step 8: rebuild MST + verify root CID --- 193 - step_timer = try std.time.Timer.start(); 194 - 195 - var tree = zat.mst.Mst.init(alloc); 196 - for (records.items) |record| { 197 - try tree.put(record.key, record.value); 198 - } 199 - const rebuilt_root = try tree.rootCid(); 187 + const record_count = try walkAndVerifyMst(alloc, repo_car.block_map, data_cid.raw); 200 188 201 - const rebuild_ns = step_timer.read(); 202 - const cid_match = std.mem.eql(u8, rebuilt_root.raw, data_cid.raw); 189 + const walk_verify_ns = step_timer.read(); 203 190 204 191 // --- output --- 205 192 const rev_short = if (commit_rev.len > 6) commit_rev[0..6] else commit_rev; ··· 219 206 } 220 207 { 221 208 var buf: [80]u8 = undefined; 222 - const desc = std.fmt.bufPrint(&buf, " → MST walk: {d} records", .{records.items.len}) catch ""; 223 - p("{s}", .{desc}); 224 - printPadded(desc.len, walk_ns); 225 - } 226 - if (cid_match) { 227 - const desc = " → MST rebuild + root CID match"; 209 + const desc = std.fmt.bufPrint(&buf, " → MST walk+verify: {d} records", .{record_count}) catch ""; 228 210 p("{s}", .{desc}); 229 - printPadded(desc.len, rebuild_ns); 230 - } else { 231 - p(" → MST rebuild + root CID MISMATCH!\n", .{}); 211 + printPadded(desc.len, walk_verify_ns); 232 212 } 233 213 234 214 const total_ns = total_timer.read(); 235 215 const network_ns = handle_ms + did_ns + fetch_ns; 236 - const compute_ns = car_ns + sig_ns + walk_ns + rebuild_ns; 216 + const compute_ns = car_ns + sig_ns + walk_verify_ns; 237 217 238 218 p("\ntotal {s} (network: {s}, compute: {s})\n\n", .{ 239 219 trimFmt(&fmtNs(total_ns)), ··· 244 224 245 225 // --- helpers --- 246 226 247 - const MstRecord = struct { 248 - key: []const u8, 249 - value: zat.cbor.Cid, 250 - }; 251 - 252 227 fn encodeUnsignedCommit(allocator: Allocator, commit: zat.cbor.Value) ![]u8 { 253 228 const entries = switch (commit) { 254 229 .map => |m| m, ··· 266 241 return zat.cbor.encodeAlloc(allocator, unsigned_value); 267 242 } 268 243 269 - fn walkMst(allocator: Allocator, block_map: BlockMap, node_cid_raw: []const u8, records: *std.ArrayList(MstRecord)) !void { 270 - const block_data = findBlock(block_map, node_cid_raw) orelse return; 271 - const node = zat.cbor.decodeAll(allocator, block_data) catch return; 244 + /// walk + verify MST using specialized decoder + key height checks. 245 + /// combined with CAR block CID verification, this proves canonical structure 246 + /// without a full rebuild. 247 + fn walkAndVerifyMst(allocator: Allocator, block_map: BlockMap, root_cid_raw: []const u8) !usize { 248 + const root_data = findBlock(block_map, root_cid_raw) orelse return error.CommitBlockNotFound; 249 + const root_node = try zat.mst.decodeMstNode(allocator, root_data); 250 + if (root_node.entries.len == 0 and root_node.left == null) return 0; 272 251 273 - if (node.get("l")) |left_val| { 274 - switch (left_val) { 275 - .cid => |left_cid| try walkMst(allocator, block_map, left_cid.raw, records), 276 - else => {}, 277 - } 278 - } 252 + const root_layer = zat.mst.keyHeight(root_node.entries[0].key_suffix); 253 + return walkVerifyNode(allocator, block_map, root_node, root_layer); 254 + } 279 255 280 - const entries_arr = node.getArray("e") orelse return; 281 - var prev_key: []const u8 = ""; 256 + const WalkError = error{ 257 + CommitBlockNotFound, 258 + MstRootMismatch, 259 + } || zat.mst.MstDecodeError; 282 260 283 - for (entries_arr) |entry_val| { 284 - const p = entry_val.getInt("p") orelse continue; 285 - const prefix_len: usize = @intCast(p); 286 - const k = entry_val.getBytes("k") orelse continue; 261 + fn walkVerifyNode(allocator: Allocator, block_map: BlockMap, node: zat.mst.MstNodeData, expected_layer: u32) WalkError!usize { 262 + var count: usize = 0; 263 + var key_buf: [512]u8 = undefined; 264 + var key_len: usize = 0; 287 265 288 - const full_key = try std.mem.concat(allocator, u8, &.{ prev_key[0..prefix_len], k }); 289 - prev_key = full_key; 266 + if (node.left) |left_cid| { 267 + if (expected_layer == 0) return error.MstRootMismatch; 268 + count += try walkVerifyChild(allocator, block_map, left_cid, expected_layer - 1); 269 + } 290 270 291 - if (entry_val.get("v")) |v| { 292 - switch (v) { 293 - .cid => |value_cid| try records.append(allocator, .{ .key = full_key, .value = value_cid }), 294 - else => {}, 295 - } 296 - } 271 + for (node.entries) |entry| { 272 + @memcpy(key_buf[entry.prefix_len..][0..entry.key_suffix.len], entry.key_suffix); 273 + key_len = entry.prefix_len + entry.key_suffix.len; 297 274 298 - if (entry_val.get("t")) |t| { 299 - switch (t) { 300 - .cid => |tree_cid| try walkMst(allocator, block_map, tree_cid.raw, records), 301 - else => {}, 302 - } 275 + if (zat.mst.keyHeight(key_buf[0..key_len]) != expected_layer) return error.MstRootMismatch; 276 + count += 1; 277 + 278 + if (entry.tree) |tree_cid| { 279 + if (expected_layer == 0) return error.MstRootMismatch; 280 + count += try walkVerifyChild(allocator, block_map, tree_cid, expected_layer - 1); 303 281 } 304 282 } 283 + 284 + return count; 285 + } 286 + 287 + fn walkVerifyChild(allocator: Allocator, block_map: BlockMap, cid_raw: []const u8, expected_layer: u32) WalkError!usize { 288 + const block_data = findBlock(block_map, cid_raw) orelse return error.CommitBlockNotFound; 289 + const node = try zat.mst.decodeMstNode(allocator, block_data); 290 + return walkVerifyNode(allocator, block_map, node, expected_layer); 305 291 } 306 292 307 293 // --- inline CAR parser (no size limits, for full-repo downloads) ---