Repository: MinaProtocol/mina
Pull request #16970: Keep snark work proofs on disk until we actually need them
Author: login: mrmr1993
Created: 2025-04-16T22:24:19Z | Merged: 2025-04-17T10:58:42Z

This PR changes the types for snark work while we're passing it around. Most importantly, this means that we aren't reading all of the proofs any time we enumerate the available work.

This PR addresses on [this review comment](https://github.com/MinaProtocol/mina/pull/16964#discussion_r2047380211) on PR #16964.


Diff:
diff --git a/src/app/cli/src/init/mina_run.ml b/src/app/cli/src/init/mina_run.ml
index 313f700eab0b..9de502f1b837 100644
--- a/src/app/cli/src/init/mina_run.ml
+++ b/src/app/cli/src/init/mina_run.ml
@@ -437,6 +437,13 @@ let setup_local_server ?(client_trustlist = []) ?rest_server_port
                 ~f:Fn.const
             in
             let%map work = Mina_lib.request_work mina in
+            let work =
+              Snark_work_lib.Work.Spec.map work
+                ~f:
+                  (Snark_work_lib.Work.Single.Spec.map
+                     ~f_proof:Ledger_proof.Cached.read_proof_from_disk
+                     ~f_witness:Transaction_witness.read_all_proofs_from_disk )
+            in
             [%log trace]
               ~metadata:[ ("work_spec", Snark_worker.Work.Spec.to_yojson work) ]
               "responding to a Get_work request with some new work" ;
diff --git a/src/lib/mina_graphql/mina_graphql.ml b/src/lib/mina_graphql/mina_graphql.ml
index 37e356a9eb94..3dc9b748082f 100644
--- a/src/lib/mina_graphql/mina_graphql.ml
+++ b/src/lib/mina_graphql/mina_graphql.ml
@@ -2374,6 +2374,13 @@ module Queries = struct
         let less_than uint1 uint2 = Unsigned.UInt32.compare uint1 uint2 < 0 in
         let to_bundle_specs =
           List.map ~f:(fun (spec, fee_prover) ->
+              let spec =
+                One_or_two.map spec
+                  ~f:
+                    (Snark_work_lib.Work.Single.Spec.map
+                       ~f_proof:Ledger_proof.Cached.read_proof_from_disk
+                       ~f_witness:Transaction_witness.read_all_proofs_from_disk )
+              in
               { Types.Snark_work_bundle.spec; fee_prover } )
         in
         match end_idx with
diff --git a/src/lib/mina_graphql/types.ml b/src/lib/mina_graphql/types.ml
index d40f550a333c..3309d99acb82 100644
--- a/src/lib/mina_graphql/types.ml
+++ b/src/lib/mina_graphql/types.ml
@@ -624,7 +624,7 @@ let pending_work =
 
 module Snark_work_bundle = struct
   type t =
-    { spec : Work_selector.work One_or_two.t
+    { spec : Work_selector.in_memory_work One_or_two.t
     ; fee_prover : (Currency.Fee.t * Public_key.Compressed.t) option
     }
 
@@ -638,7 +638,7 @@ module Snark_work_bundle = struct
             ~doc:"Snark work specification in json format"
             ~args:Arg.[]
             ~resolve:(fun _ { spec; _ } ->
-              One_or_two.to_yojson Work_selector.work_to_yojson spec
+              One_or_two.to_yojson Work_selector.in_memory_work_to_yojson spec
               |> Yojson.Safe.to_string )
         ; field "snarkFee" ~typ:fee
             ~doc:"Fee if proof for the spec exists in snark pool"
diff --git a/src/lib/mina_lib/mina_lib.ml b/src/lib/mina_lib/mina_lib.ml
index 902faaf880a3..7de658ac00b0 100644
--- a/src/lib/mina_lib/mina_lib.ml
+++ b/src/lib/mina_lib/mina_lib.ml
@@ -2586,3 +2586,5 @@ let best_chain_block_by_state_hash (t : t) hash =
             (State_hash.to_base58_check hash) )
 
 let zkapp_cmd_limit t = t.config.zkapp_cmd_limit
+
+let proof_cache_db t = t.proof_cache_db
diff --git a/src/lib/mina_lib/mina_lib.mli b/src/lib/mina_lib/mina_lib.mli
index ba7ecbb8600e..950d5602bd1c 100644
--- a/src/lib/mina_lib/mina_lib.mli
+++ b/src/lib/mina_lib/mina_lib.mli
@@ -258,3 +258,5 @@ val best_chain_block_by_state_hash :
   t -> State_hash.t -> (Transition_frontier.Breadcrumb.t, string) Result.t
 
 val zkapp_cmd_limit : t -> int option ref
+
+val proof_cache_db : t -> Proof_cache_tag.cache_db
diff --git a/src/lib/snark_work_lib/work.ml b/src/lib/snark_work_lib/work.ml
index 6387a1d78f0c..7866530d3146 100644
--- a/src/lib/snark_work_lib/work.ml
+++ b/src/lib/snark_work_lib/work.ml
@@ -87,6 +87,9 @@ module Spec = struct
   type 'single t = 'single Stable.Latest.t =
     { instances : 'single One_or_two.t; fee : Currency.Fee.t }
   [@@deriving fields, sexp, yojson]
+
+  let map ~f { instances; fee } =
+    { instances = One_or_two.map ~f instances; fee }
 end
 
 module Result = struct
diff --git a/src/lib/staged_ledger/staged_ledger.mli b/src/lib/staged_ledger/staged_ledger.mli
index 1471ca5d7a60..cd8443d8d5a5 100644
--- a/src/lib/staged_ledger/staged_ledger.mli
+++ b/src/lib/staged_ledger/staged_ledger.mli
@@ -334,7 +334,7 @@ val of_scan_state_pending_coinbases_and_snarked_ledger_unchecked :
 val all_work_pairs :
      t
   -> get_state:(State_hash.t -> Mina_state.Protocol_state.value Or_error.t)
-  -> ( Transaction_witness.Stable.Latest.t
+  -> ( Transaction_witness.t
      , Ledger_proof.Cached.t )
      Snark_work_lib.Work.Single.Spec.t
      One_or_two.t
diff --git a/src/lib/transaction_inclusion_status/transaction_inclusion_status.ml b/src/lib/transaction_inclusion_status/transaction_inclusion_status.ml
index 94973af4f87d..939143c484d4 100644
--- a/src/lib/transaction_inclusion_status/transaction_inclusion_status.ml
+++ b/src/lib/transaction_inclusion_status/transaction_inclusion_status.ml
@@ -38,9 +38,7 @@ let get_status ~frontier_broadcast_pipe ~transaction_pool cmd =
         |> Mina_block.Validated.valid_commands
         |> List.exists ~f:(fun { data = found; _ } ->
                let found' = User_command.forget_check found in
-               User_command.equal_ignoring_proofs_and_hashes cmd found'
-               && User_command.Stable.Latest.equal cmd
-                    (User_command.read_all_proofs_from_disk found') )
+               User_command.equal_ignoring_proofs_and_hashes cmd found' )
       in
       if List.exists ~f:in_breadcrumb best_tip_path then State.Included
       else if
diff --git a/src/lib/transaction_snark_scan_state/transaction_snark_scan_state.ml b/src/lib/transaction_snark_scan_state/transaction_snark_scan_state.ml
index 8623f458c98d..87eb72dd2a57 100644
--- a/src/lib/transaction_snark_scan_state/transaction_snark_scan_state.ml
+++ b/src/lib/transaction_snark_scan_state/transaction_snark_scan_state.ml
@@ -1298,7 +1298,7 @@ let work_statements_for_new_diff t : Transaction_snark_work.Statement.t list =
 
 let all_work_pairs t
     ~(get_state : State_hash.t -> Mina_state.Protocol_state.value Or_error.t) :
-    ( Transaction_witness.Stable.Latest.t
+    ( Transaction_witness.t
     , Ledger_proof.Cached.t )
     Snark_work_lib.Work.Single.Spec.t
     One_or_two.t
@@ -1333,10 +1333,9 @@ let all_work_pairs t
             | Merge ->
                 Or_error.error_string "init_stack was Merge"
           in
-          { Transaction_witness.Stable.Latest.first_pass_ledger =
-              first_pass_ledger_witness
+          { Transaction_witness.first_pass_ledger = first_pass_ledger_witness
           ; second_pass_ledger = second_pass_ledger_witness
-          ; transaction = Transaction.read_all_proofs_from_disk transaction
+          ; transaction
           ; protocol_state_body
           ; init_stack
           ; status
diff --git a/src/lib/transaction_snark_scan_state/transaction_snark_scan_state.mli b/src/lib/transaction_snark_scan_state/transaction_snark_scan_state.mli
index 4c1499345b2c..b4cfbaefc24b 100644
--- a/src/lib/transaction_snark_scan_state/transaction_snark_scan_state.mli
+++ b/src/lib/transaction_snark_scan_state/transaction_snark_scan_state.mli
@@ -257,7 +257,7 @@ val check_required_protocol_states :
 val all_work_pairs :
      t
   -> get_state:(State_hash.t -> Mina_state.Protocol_state.value Or_error.t)
-  -> ( Transaction_witness.Stable.Latest.t
+  -> ( Transaction_witness.t
      , Ledger_proof.Cached.t )
      Snark_work_lib.Work.Single.Spec.t
      One_or_two.t
diff --git a/src/lib/uptime_service/uptime_service.ml b/src/lib/uptime_service/uptime_service.ml
index 3b2199f09607..0fb7b0e79224 100644
--- a/src/lib/uptime_service/uptime_service.ml
+++ b/src/lib/uptime_service/uptime_service.ml
@@ -197,7 +197,8 @@ let send_produced_block_at ~logger ~interruptor ~url ~peer_id
 
 let read_all_proofs_for_work_single_spec =
   Snark_work_lib.Work.Single.Spec.map
-    ~f_proof:Ledger_proof.Cached.read_proof_from_disk ~f_witness:ident
+    ~f_proof:Ledger_proof.Cached.read_proof_from_disk
+    ~f_witness:Transaction_witness.read_all_proofs_from_disk
 
 let send_block_and_transaction_snark ~logger ~constraint_constants ~interruptor
     ~url ~snark_worker ~transition_frontier ~peer_id
diff --git a/src/lib/work_selector/inputs.ml b/src/lib/work_selector/inputs.ml
index e45f2ce01ba2..03dd55bcec2c 100644
--- a/src/lib/work_selector/inputs.ml
+++ b/src/lib/work_selector/inputs.ml
@@ -2,12 +2,7 @@ open Core_kernel
 open Currency
 
 module Test_inputs = struct
-  module Transaction_witness = struct
-    module Stable = struct
-      module Latest = Int
-    end
-  end
-
+  module Transaction_witness = Int
   module Ledger_hash = Int
   module Sparse_ledger = Int
   module Transaction = Int
diff --git a/src/lib/work_selector/intf.ml b/src/lib/work_selector/intf.ml
index cd71d3b5287e..0e4bc21bf7c3 100644
--- a/src/lib/work_selector/intf.ml
+++ b/src/lib/work_selector/intf.ml
@@ -23,11 +23,7 @@ module type Inputs_intf = sig
   end
 
   module Transaction_witness : sig
-    module Stable : sig
-      module Latest : sig
-        type t
-      end
-    end
+    type t
   end
 
   module Ledger_proof : sig
@@ -76,7 +72,7 @@ module type Inputs_intf = sig
          t
       -> get_state:
            (Mina_base.State_hash.t -> Mina_state.Protocol_state.value Or_error.t)
-      -> ( Transaction_witness.Stable.Latest.t
+      -> ( Transaction_witness.t
          , Ledger_proof.Cached.t )
          Snark_work_lib.Work.Single.Spec.t
          One_or_two.t
@@ -125,8 +121,8 @@ module type Lib_intf = sig
     (**Jobs that have not been assigned yet*)
     val all_unseen_works :
          t
-      -> ( Transaction_witness.Stable.Latest.t
-         , Ledger_proof.t )
+      -> ( Transaction_witness.t
+         , Ledger_proof.Cached.t )
          Snark_work_lib.Work.Single.Spec.t
          One_or_two.t
          list
@@ -135,8 +131,8 @@ module type Lib_intf = sig
 
     val set :
          t
-      -> ( Transaction_witness.Stable.Latest.t
-         , Ledger_proof.t )
+      -> ( Transaction_witness.t
+         , Ledger_proof.Cached.t )
          Snark_work_lib.Work.Single.Spec.t
          One_or_two.t
       -> unit
@@ -145,13 +141,13 @@ module type Lib_intf = sig
   val get_expensive_work :
        snark_pool:Snark_pool.t
     -> fee:Fee.t
-    -> ( Transaction_witness.Stable.Latest.t
-       , Ledger_proof.t )
+    -> ( Transaction_witness.t
+       , Ledger_proof.Cached.t )
        Snark_work_lib.Work.Single.Spec.t
        One_or_two.t
        list
-    -> ( Transaction_witness.Stable.Latest.t
-       , Ledger_proof.t )
+    -> ( Transaction_witness.t
+       , Ledger_proof.Cached.t )
        Snark_work_lib.Work.Single.Spec.t
        One_or_two.t
        list
@@ -195,8 +191,8 @@ module type Make_selection_method_intf = functor (Lib : Lib_intf) ->
   Selection_method_intf
     with type staged_ledger := Lib.Inputs.Staged_ledger.t
      and type work :=
-      ( Lib.Inputs.Transaction_witness.Stable.Latest.t
-      , Lib.Inputs.Ledger_proof.t )
+      ( Lib.Inputs.Transaction_witness.t
+      , Lib.Inputs.Ledger_proof.Cached.t )
       Snark_work_lib.Work.Single.Spec.t
      and type snark_pool := Lib.Inputs.Snark_pool.t
      and type transition_frontier := Lib.Inputs.Transition_frontier.t
diff --git a/src/lib/work_selector/work_lib.ml b/src/lib/work_selector/work_lib.ml
index 24cffe759e06..980f87a0edb9 100644
--- a/src/lib/work_selector/work_lib.ml
+++ b/src/lib/work_selector/work_lib.ml
@@ -28,8 +28,8 @@ module Make (Inputs : Intf.Inputs_intf) = struct
 
     type t =
       { mutable available_jobs :
-          ( Inputs.Transaction_witness.Stable.Latest.t
-          , Inputs.Ledger_proof.t )
+          ( Inputs.Transaction_witness.t
+          , Inputs.Ledger_proof.Cached.t )
           Work_spec.t
           One_or_two.t
           list
@@ -83,20 +83,7 @@ module Make (Inputs : Intf.Inputs_intf) = struct
                                 ( Time.diff end_time start_time
                                 |> Time.Span.to_ms ) )
                           ] ;
-                      let new_available_jobs_unwrapped :
-                          ( Inputs.Transaction_witness.Stable.Latest.t
-                          , Inputs.Ledger_proof.t )
-                          Work_spec.t
-                          One_or_two.t
-                          list =
-                        let f =
-                          Snark_work_lib.Work.Single.Spec.map ~f_witness:ident
-                            ~f_proof:
-                              Inputs.Ledger_proof.Cached.read_proof_from_disk
-                        in
-                        List.map new_available_jobs ~f:(One_or_two.map ~f)
-                      in
-                      t.available_jobs <- new_available_jobs_unwrapped ) ;
+                      t.available_jobs <- new_available_jobs ) ;
                   Deferred.unit )
               |> Deferred.don't_wait_for ) ;
           Deferred.unit )
diff --git a/src/lib/work_selector/work_selector.ml b/src/lib/work_selector/work_selector.ml
index 21bd8cb5bdc5..f0b475acf77c 100644
--- a/src/lib/work_selector/work_selector.ml
+++ b/src/lib/work_selector/work_selector.ml
@@ -2,6 +2,11 @@ module Lib = Work_lib.Make (Inputs.Implementation_inputs)
 module State = Lib.State
 
 type work =
+  ( Transaction_witness.t
+  , Ledger_proof.Cached.t )
+  Snark_work_lib.Work.Single.Spec.t
+
+type in_memory_work =
   ( Transaction_witness.Stable.Latest.t
   , Ledger_proof.t )
   Snark_work_lib.Work.Single.Spec.t
diff --git a/src/lib/work_selector/work_selector.mli b/src/lib/work_selector/work_selector.mli
index c479847cd641..ad3a92b14d58 100644
--- a/src/lib/work_selector/work_selector.mli
+++ b/src/lib/work_selector/work_selector.mli
@@ -2,6 +2,11 @@ module State :
   Intf.State_intf with type transition_frontier := Transition_frontier.t
 
 type work =
+  ( Transaction_witness.t
+  , Ledger_proof.Cached.t )
+  Snark_work_lib.Work.Single.Spec.t
+
+type in_memory_work =
   ( Transaction_witness.Stable.Latest.t
   , Ledger_proof.t )
   Snark_work_lib.Work.Single.Spec.t


Reviews:
user: login: svv232
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-17T01:36:37Z
user: login: georgeee
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-17T16:24:32Z

Review comments:
diff_hunk: @@ -2586,3 +2586,5 @@ let best_chain_block_by_state_hash (t : t) hash =
             (State_hash.to_base58_check hash) )
 
 let zkapp_cmd_limit t = t.config.zkapp_cmd_limit
+
+let proof_cache_db t = t.proof_cache_db
path: src/lib/mina_lib/mina_lib.ml
user: login: georgeee
body: Isn't this auto-generated by `fields` extensions?
created_at: 2025-04-17T16:21:15Z
author_association: MEMBER
diff_hunk: @@ -38,9 +38,7 @@ let get_status ~frontier_broadcast_pipe ~transaction_pool cmd =
         |> Mina_block.Validated.valid_commands
         |> List.exists ~f:(fun { data = found; _ } ->
                let found' = User_command.forget_check found in
-               User_command.equal_ignoring_proofs_and_hashes cmd found'
path: src/lib/transaction_inclusion_status/transaction_inclusion_status.ml
user: login: georgeee
body: This change is perhaps ok. Originally I tried to retain the previous behavior as much as possible. But now I see that in case of transaction being in the pool, we'll only check transaction hash, and that doesn't rely on contents of proofs.

The only thing that might be different is that someone will submit their transactions into a block, and then recreate it (same contents, new proofs) and it will be said as included, although nobody has seen exactly this sequence of bytes before.

Sounds like something someone could potentially misuse, but nothing convincing comes to my mind as a demonstration of this potential.
created_at: 2025-04-17T16:24:29Z
author_association: MEMBER

PR comments:
user: login: mrmr1993
created_at: 2025-04-16T22:24:28Z
body: !ci-bypass-changelog
author_association: CONTRIBUTOR
user: login: mrmr1993
created_at: 2025-04-16T22:24:30Z
body: !ci-build-me
author_association: CONTRIBUTOR