orama/src/peer/verify.zig
anonpenguin23 7c165b9579 Squashed 'vault/' content from commit 5424568
git-subtree-dir: vault
git-subtree-split: 54245680dafb7486a644df470c2b8bba0c2e50ae
2026-03-26 18:15:11 +02:00

119 lines
3.9 KiB
Zig

/// Periodic verification of share integrity across guardians.
///
/// Randomly selects shares and asks peers to confirm they have the same
/// commitment hash. Detects tampering and data corruption.
const std = @import("std");
const log = @import("../log.zig");
const protocol = @import("protocol.zig");
const node_list = @import("../membership/node_list.zig");
pub const VerifyResult = struct {
identity: []const u8,
peer_address: []const u8,
matches: bool,
peer_has_share: bool,
};
/// Send a verify request to a peer and wait for the response.
/// Returns null if the peer is unreachable or returns invalid data.
pub fn verifyWithPeer(
peer_address: []const u8,
peer_port: u16,
identity: []const u8,
) ?protocol.VerifyResponse {
const address = std.net.Address.parseIp4(peer_address, peer_port) catch return null;
const stream = std.net.tcpConnectToAddress(address) catch return null;
defer stream.close();
// Build request
var req = protocol.VerifyRequest{
.identity = .{0} ** 64,
.identity_len = @intCast(@min(identity.len, 64)),
};
@memcpy(req.identity[0..req.identity_len], identity[0..req.identity_len]);
const payload = protocol.encodeVerifyRequest(req);
const header = protocol.encodeHeader(.{
.version = protocol.PROTOCOL_VERSION,
.msg_type = .verify_request,
.payload_len = payload.len,
});
stream.writeAll(&header) catch return null;
stream.writeAll(&payload) catch return null;
// Read response header
var resp_header_buf: [protocol.HEADER_SIZE]u8 = undefined;
stream.readAll(&resp_header_buf) catch return null;
const resp_header = protocol.decodeHeader(resp_header_buf) orelse return null;
if (resp_header.msg_type != .verify_response) return null;
if (resp_header.payload_len < 98) return null;
// Read response payload
var resp_buf: [98]u8 = undefined;
stream.readAll(&resp_buf) catch return null;
return protocol.decodeVerifyResponse(&resp_buf);
}
/// Compare our local commitment with a peer's commitment.
pub fn compareCommitments(
local_root: [32]u8,
peer_response: protocol.VerifyResponse,
) VerifyResult {
return VerifyResult{
.identity = peer_response.identity[0..peer_response.identity_len],
.peer_address = "", // caller fills in
.peer_has_share = peer_response.has_share,
.matches = peer_response.has_share and
std.mem.eql(u8, &local_root, &peer_response.commitment_root),
};
}
// ── Tests ────────────────────────────────────────────────────────────────────
test "compareCommitments: matching roots" {
const root = [_]u8{0xAB} ** 32;
const resp = protocol.VerifyResponse{
.identity = .{0} ** 64,
.identity_len = 4,
.has_share = true,
.commitment_root = root,
};
const result = compareCommitments(root, resp);
try std.testing.expect(result.matches);
try std.testing.expect(result.peer_has_share);
}
test "compareCommitments: mismatched roots" {
const local_root = [_]u8{0xAB} ** 32;
var peer_root = [_]u8{0xAB} ** 32;
peer_root[0] = 0xCD; // tamper
const resp = protocol.VerifyResponse{
.identity = .{0} ** 64,
.identity_len = 4,
.has_share = true,
.commitment_root = peer_root,
};
const result = compareCommitments(local_root, resp);
try std.testing.expect(!result.matches);
}
test "compareCommitments: peer missing share" {
const root = [_]u8{0xAB} ** 32;
const resp = protocol.VerifyResponse{
.identity = .{0} ** 64,
.identity_len = 4,
.has_share = false,
.commitment_root = .{0} ** 32,
};
const result = compareCommitments(root, resp);
try std.testing.expect(!result.matches);
try std.testing.expect(!result.peer_has_share);
}