aboutsummaryrefslogtreecommitdiffstats
path: root/lib/merkle.ml
blob: 5b30856171c4c5a68edcdd09e696fe8d772c636c (plain) (blame)
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
(** Merkle tree module signature for content-addressed hash sets.

    This is an opaque interface that can be backed by different implementations:
    - A simple hash set (current placeholder)
    - A proper radix/patricia merkle tree (e.g., plebeia when OCaml 5 support
      arrives)

    The tree stores hashes as keys (like a set) and computes a root hash from
    all contained elements. *)

module type S = sig
  type t

  val empty : t
  (** The empty tree *)

  val is_empty : t -> bool
  (** Check if tree is empty *)

  val insert : Conventions.Hash.t -> t -> t
  (** Insert a hash into the tree. Returns a new tree. *)

  val delete : Conventions.Hash.t -> t -> t
  (** Delete a hash from the tree. Returns a new tree. *)

  val member : Conventions.Hash.t -> t -> bool
  (** Check if a hash is in the tree *)

  val keys : t -> Conventions.Hash.t list
  (** Get all hashes in the tree *)

  val root_hash : t -> Conventions.Hash.t option
  (** Compute the root hash of the tree. None if empty. *)

  val size : t -> int
  (** Number of elements in the tree *)
end

(** Simple hash set implementation. This is a placeholder until a proper
    radix-merkle library with OCaml 5 support becomes available. *)
module HashSet : S = struct
  module HashSet' = Set.Make (Conventions.Hash)

  type t = HashSet'.t

  let empty = HashSet'.empty
  let is_empty = HashSet'.is_empty
  let insert hash tree = HashSet'.add hash tree
  let delete hash tree = HashSet'.remove hash tree
  let member hash tree = HashSet'.mem hash tree
  let keys tree = HashSet'.elements tree
  (* TODO: Streaming/pagination for large tuple sets. Currently materializes
     entire keyset into memory, which fails for relations with billions of tuples.
     Replace with paginated access (keys_paginated offset limit) or lazy generator
     to avoid loading all hashes upfront. Consider replacing HashSet with proper
     radix-merkle tree (plebeia) for native lazy traversal. *)

  let root_hash tree =
    if HashSet'.is_empty tree then None
    else
      (* Compute root hash by hashing sorted concatenation of all hashes *)
      let sorted_hashes = HashSet'.elements tree in
      let concatenated = String.concat "" (List.map Conventions.Hash.to_string sorted_hashes) in
      Some (Conventions.Hash.hash_text concatenated)

  let size = HashSet'.cardinal
end

include HashSet
(** Default implementation using HashSet *)