• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MinaProtocol / mina / 1661

18 Dec 2025 03:32PM UTC coverage: 61.328% (+27.9%) from 33.382%
1661

push

buildkite

web-flow
Merge pull request #18232 from MinaProtocol/amcie-merging-release-330-to-master

Merging 3.3.0 release branch to master

1229 of 2006 new or added lines in 108 files covered. (61.27%)

54 existing lines in 27 files now uncovered.

51257 of 83578 relevant lines covered (61.33%)

472886.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

10.47
/src/lib/user_command_input/user_command_input.ml
1
open Mina_base
59✔
2
open Signature_lib
3
open Mina_numbers
4
open Core_kernel
5
open Async_kernel
6

7
module Payload = struct
8
  module Common = struct
9
    [%%versioned
10
    module Stable = struct
11
      module V2 = struct
12
        type t =
59✔
13
          ( Currency.Fee.Stable.V1.t
×
14
          , Public_key.Compressed.Stable.V1.t
×
15
          , Account_nonce.Stable.V1.t option
×
16
          , Global_slot_since_genesis.Stable.V1.t
×
17
          , Signed_command_memo.Stable.V1.t )
×
18
          Signed_command_payload.Common.Poly.Stable.V2.t
19
        [@@deriving sexp, to_yojson]
295✔
20

21
        let to_latest = Fn.id
22
      end
23
    end]
24

25
    let create ~fee ~fee_payer_pk ?nonce ~valid_until ~memo : t =
26
      { fee; fee_payer_pk; nonce; valid_until; memo }
×
27

28
    let to_user_command_common (t : t) ~minimum_nonce ~inferred_nonce :
29
        (Signed_command_payload.Common.t, string) Result.t =
30
      let open Result.Let_syntax in
×
31
      let%map nonce =
32
        match t.nonce with
33
        | None ->
×
34
            (*User did not provide a nonce, use inferred*)
35
            Ok inferred_nonce
36
        | Some nonce ->
×
37
            (* NB: A lower, explicitly given nonce can be used to cancel
38
               transactions or to re-issue them with a higher fee.
39
            *)
40
            if Account_nonce.(minimum_nonce <= nonce && nonce <= inferred_nonce)
×
41
            then Ok nonce
×
42
            else
43
              (* IMPORTANT! Do not change the content of this error without
44
               * updating Rosetta's construction API to handle the changes *)
45
              Error
×
46
                (sprintf
×
47
                   !"Input nonce %s either different from inferred nonce %s or \
48
                     below minimum_nonce %s"
49
                   (Account_nonce.to_string nonce)
×
50
                   (Account_nonce.to_string inferred_nonce)
×
51
                   (Account_nonce.to_string minimum_nonce) )
×
52
      in
53
      { Signed_command_payload.Common.Poly.fee = t.fee
×
54
      ; fee_payer_pk = t.fee_payer_pk
55
      ; nonce
56
      ; valid_until = t.valid_until
57
      ; memo = t.memo
58
      }
59

60
    let fee_payer ({ fee_payer_pk; _ } : t) =
61
      Account_id.create fee_payer_pk Mina_base.Token_id.default
×
62
  end
63

64
  [%%versioned
65
  module Stable = struct
66
    module V2 = struct
67
      type t =
59✔
68
        ( Common.Stable.V2.t
×
69
        , Signed_command_payload.Body.Stable.V2.t )
×
70
        Signed_command_payload.Poly.Stable.V1.t
71
      [@@deriving sexp, to_yojson]
295✔
72

73
      let to_latest = Fn.id
74
    end
75
  end]
76

77
  let create ~fee ~fee_payer_pk ?nonce ~valid_until ~memo ~body : t =
78
    { common = Common.create ~fee ~fee_payer_pk ?nonce ~valid_until ~memo
×
79
    ; body
80
    }
81

82
  let to_user_command_payload (t : t) ~minimum_nonce ~inferred_nonce :
83
      (Signed_command_payload.t, string) Result.t =
84
    let open Result.Let_syntax in
×
85
    let%map common =
86
      Common.to_user_command_common t.common ~minimum_nonce ~inferred_nonce
×
87
    in
88
    { Signed_command_payload.Poly.common; body = t.body }
×
89

90
  let fee_payer ({ common; _ } : t) = Common.fee_payer common
×
91
end
92

93
module Sign_choice = struct
94
  [%%versioned
95
  module Stable = struct
96
    module V1 = struct
97
      type t =
118✔
98
        | Signature of Signature.Stable.V1.t
×
99
        | Hd_index of Unsigned_extended.UInt32.Stable.V1.t
×
100
        | Keypair of Keypair.Stable.V1.t
×
101
      [@@deriving sexp, to_yojson]
295✔
102

103
      let to_latest = Fn.id
104
    end
105
  end]
106
end
107

108
[%%versioned
109
module Stable = struct
110
  module V2 = struct
111
    type t =
59✔
112
      ( Payload.Stable.V2.t
×
113
      , Public_key.Compressed.Stable.V1.t
×
114
      , Sign_choice.Stable.V1.t )
×
115
      Signed_command.Poly.Stable.V1.t
116
    [@@deriving sexp, to_yojson]
295✔
117

118
    let to_latest = Fn.id
119
  end
120
end]
121

122
[%%define_locally Stable.Latest.(to_yojson)]
123

124
let fee_payer ({ payload; _ } : t) = Payload.fee_payer payload
×
125

126
let create ?nonce ~fee ~fee_payer_pk ~valid_until ~memo ~body ~signer
127
    ~sign_choice () : t =
128
  let valid_until =
×
129
    Option.value valid_until ~default:Global_slot_since_genesis.max_value
130
  in
131
  let payload =
×
132
    Payload.create ~fee ~fee_payer_pk ?nonce ~valid_until ~memo ~body
133
  in
134
  { payload; signer; signature = sign_choice }
135

136
let sign ~signer ~(user_command_payload : Signed_command_payload.t)
137
    ~signature_kind = function
UNCOV
138
  | Sign_choice.Signature signature ->
×
139
      Option.value_map
140
        ~default:(Deferred.return (Error "Invalid_signature"))
×
141
        (Signed_command.create_with_signature_checked ~signature_kind signature
×
142
           signer user_command_payload )
143
        ~f:Deferred.Result.return
144
  | Keypair signer_kp ->
×
145
      Deferred.Result.return
146
        (Signed_command.sign ~signature_kind signer_kp user_command_payload)
×
147
  | Hd_index hd_index ->
×
148
      Secrets.Hardware_wallets.sign ~hd_index
149
        ~public_key:(Public_key.decompress_exn signer)
×
150
        ~user_command_payload
151

152
let inferred_nonce ~get_current_nonce ~(fee_payer : Account_id.t) ~nonce_map =
153
  let open Result.Let_syntax in
×
154
  let update_map = Map.set nonce_map ~key:fee_payer in
155
  match Map.find nonce_map fee_payer with
×
156
  | Some (min_nonce, nonce) ->
×
157
      (* Multiple user commands from the same fee-payer. *)
158
      (* TODO: this logic does not currently support zkapp_command transactions, as zkapp_command transactions can increment the fee payer nonce more than once (#11001) *)
159
      let next_nonce = Account_nonce.succ nonce in
160
      let updated_map = update_map ~data:(min_nonce, next_nonce) in
×
161
      Ok (min_nonce, next_nonce, updated_map)
162
  | None ->
×
163
      let%map `Min min_nonce, txn_pool_or_account_nonce =
164
        get_current_nonce fee_payer
×
165
      in
166
      let updated_map =
×
167
        update_map ~data:(min_nonce, txn_pool_or_account_nonce)
168
      in
169
      (min_nonce, txn_pool_or_account_nonce, updated_map)
170

171
(* If the receiver account doesn't exist yet (as far as we can tell) *and* the
172
 * user command isn't sufficient to cover the account creation fee, log a
173
 * warning. *)
174
let warn_if_unable_to_pay_account_creation_fee ~get_account
175
    ~(constraint_constants : Genesis_constants.Constraint_constants.t) ~logger
176
    user_command_payload =
177
  let receiver_pk = Signed_command_payload.receiver_pk user_command_payload in
×
178
  let token = Signed_command_payload.token user_command_payload in
×
179
  let receiver = Account_id.create receiver_pk token in
×
180
  let receiver_account = get_account receiver in
×
181
  let amount = Signed_command_payload.amount user_command_payload in
×
182
  match (receiver_account, amount) with
×
183
  | `Bootstrapping, _ | `Active (Some _), _ | _, None ->
×
184
      ()
185
  | `Active None, Some amount ->
×
186
      let open Currency.Amount in
187
      let account_creation_fee =
188
        of_fee constraint_constants.account_creation_fee
189
      in
190
      if amount < account_creation_fee then
×
191
        [%log warn]
×
192
          "A transaction was submitted that is likely to fail because the \
193
           receiver account doesn't appear to have been created already and \
194
           the transaction amount of %s is smaller than the account creation \
195
           fee of %s."
196
          (to_mina_string amount)
×
197
          (to_mina_string account_creation_fee) ;
×
198
      ()
×
199

200
let to_user_command ?(nonce_map = Account_id.Map.empty) ~get_current_nonce
×
201
    ~get_account ~constraint_constants ~logger ~signature_kind (client_input : t)
202
    =
UNCOV
203
  Deferred.map
×
204
    ~f:
205
      (Result.map_error ~f:(fun str ->
206
           Error.createf "Error creating user command: %s Error: %s"
×
207
             (Yojson.Safe.to_string (to_yojson client_input))
×
208
             str ) )
209
  @@
210
  let open Deferred.Result.Let_syntax in
211
  let fee_payer = fee_payer client_input in
212
  let%bind minimum_nonce, inferred_nonce, updated_nonce_map =
213
    inferred_nonce ~get_current_nonce ~fee_payer ~nonce_map |> Deferred.return
×
214
  in
215
  let%bind user_command_payload =
216
    Payload.to_user_command_payload client_input.payload ~minimum_nonce
×
217
      ~inferred_nonce
218
    |> Deferred.return
×
219
  in
220
  let () =
×
221
    warn_if_unable_to_pay_account_creation_fee ~get_account
222
      ~constraint_constants ~logger user_command_payload
223
  in
224
  let%map signed_user_command =
NEW
225
    sign ~signature_kind ~signer:client_input.signer ~user_command_payload
×
226
      client_input.signature
227
  in
228
  (Signed_command.forget_check signed_user_command, updated_nonce_map)
×
229

230
let to_user_commands ?(nonce_map = Account_id.Map.empty) ~get_current_nonce
×
231
    ~get_account ~constraint_constants ~logger ~signature_kind uc_inputs :
232
    Signed_command.t list Deferred.Or_error.t =
233
  (* When batching multiple user commands, keep track of the nonces and send
234
      all the user commands if they are valid or none if there is an error in
235
      one of them.
236
  *)
237
  let open Deferred.Or_error.Let_syntax in
×
238
  let%map user_commands, _ =
239
    Deferred.Or_error.List.fold ~init:([], nonce_map) uc_inputs
×
240
      ~f:(fun (valid_user_commands, nonce_map) uc_input ->
241
        let%map res, updated_nonce_map =
242
          to_user_command ~nonce_map ~get_current_nonce ~get_account
×
243
            ~constraint_constants ~logger ~signature_kind uc_input
244
        in
245
        (res :: valid_user_commands, updated_nonce_map) )
×
246
  in
247
  List.rev user_commands
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc