diff --git a/src/bin_flextesa/command_daemons_protocol_change.ml b/src/bin_flextesa/command_daemons_protocol_change.ml index eb087a55a0bbe62908746aba180152d8a0e7063c..b13a10f6c6b3a4e2dd7127b25bf9b4df2f4481a3 100644 --- a/src/bin_flextesa/command_daemons_protocol_change.ml +++ b/src/bin_flextesa/command_daemons_protocol_change.ml @@ -4,18 +4,18 @@ open Console let failf fmt = ksprintf (fun s -> fail (`Scenario_error s)) fmt -let wait_for_voting_period ?level_withing_period state ~client ~attempts period +let wait_for_voting_period ?level_within_period state ~client ~attempts period = let period_name = Tezos_protocol.Voting_period.to_string period in let message = sprintf "Waiting for voting period: `%s`%s" period_name - (Option.value_map level_withing_period ~default:"" - ~f:(sprintf " (and level-within-period ≤ %d)")) + (Option.value_map level_within_period ~default:"" + ~f:(sprintf " (and level-within-period ≥ %d)")) in Console.say state EF.(wf "%s" message) >>= fun () -> Helpers.wait_for state ~attempts ~seconds:10. (fun nth -> - Asynchronous_result.map_option level_withing_period ~f:(fun lvl -> + Asynchronous_result.map_option level_within_period ~f:(fun lvl -> Tezos_client.rpc state ~client `Get ~path:"/chains/main/blocks/head/metadata" >>= fun json -> @@ -25,7 +25,7 @@ let wait_for_voting_period ?level_withing_period state ~client ~attempts period |> Jqo.field ~k:"voting_period_position" |> Jqo.get_int in - return (voting_period_position <= lvl) + return (voting_period_position >= lvl) with e -> failf "Cannot get level.voting_period_position: %s" (Printexc.to_string e) ) @@ -50,8 +50,10 @@ let run state ~protocol ~size ~base_port ~no_daemons_for ?external_peer_ports ?generate_kiln_config ~node_exec ~client_exec ~first_baker_exec ~first_endorser_exec ~first_accuser_exec ~second_baker_exec ~second_endorser_exec ~second_accuser_exec ~admin_exec ~new_protocol_path - () = + ~extra_dummy_proposals_batch_size ~extra_dummy_proposals_batch_levels + ~waiting_attempts test_variant () = Helpers.System_dependencies.precheck state `Or_fail + ~protocol_paths:[new_protocol_path] ~executables: [ node_exec; client_exec; first_baker_exec; first_endorser_exec ; first_accuser_exec; second_baker_exec; second_endorser_exec @@ -142,12 +144,6 @@ let run state ~protocol ~size ~base_port ~no_daemons_for ?external_peer_ports (List.map nodes ~f:(Tezos_client.of_node ~exec:client_exec)) ; arbitrary_command_on_clients state ~command_names:["c0"; "client-0"] ~make_admin ~clients:[client_0] ]) ; - Test_scenario.Queries.wait_for_all_levels_to_be state ~attempts:50 - ~seconds:10. nodes - (* TODO: wait for /chains/main/blocks/head/votes/listings to be - non-empty instead of counting blocks *) - (`At_least protocol.Tezos_protocol.blocks_per_voting_period) - >>= fun () -> (* For each node we try to see if the node knows about the protocol, if it does we're good, if not we inject it. @@ -211,8 +207,20 @@ let run state ~protocol ~size ~base_port ~no_daemons_for ?external_peer_ports , first_endorser_exec ) ; (new_protocol_hash, second_baker_exec, second_endorser_exec) ] >>= fun () -> - return EF.(wf "Kiln was configured at `%s`" kiln_config.path) ) + let msg = + EF.( + desc + (shout "Kiln-Configuration DONE") + (wf "Kiln was configured at `%s`" kiln_config.path)) + in + Console.say state msg >>= fun () -> return msg ) >>= fun kiln_info_opt -> + Test_scenario.Queries.wait_for_all_levels_to_be state + ~attempts:waiting_attempts ~seconds:10. nodes + (* TODO: wait for /chains/main/blocks/head/votes/listings to be + non-empty instead of counting blocks *) + (`At_least protocol.Tezos_protocol.blocks_per_voting_period) + >>= fun () -> Interactive_test.Pauser.generic state EF. [ wf "Test becomes interactive." @@ -220,23 +228,51 @@ let run state ~protocol ~size ~base_port ~no_daemons_for ?external_peer_ports ; wf "Please type `q` to start a voting/protocol-change period." ] ~force:true >>= fun () -> - wait_for_voting_period state ~client:client_0 ~attempts:10 Proposal - ~level_withing_period:3 + wait_for_voting_period state ~client:client_0 ~attempts:waiting_attempts + Proposal ~level_within_period:3 >>= fun _ -> + let submit_prop acc client hash = + Tezos_client.successful_client_cmd state ~client + [ "submit"; "proposals"; "for" + ; Tezos_protocol.Account.name acc + ; hash; "--force" ] + >>= fun _ -> + Console.sayf state + Fmt.( + fun ppf () -> + pf ppf "%s voted for %s" (Tezos_protocol.Account.name acc) hash) + in List_sequential.iter keys_and_daemons ~f:(fun (acc, client, _) -> - Tezos_client.successful_client_cmd state ~client - [ "submit"; "proposals"; "for" - ; Tezos_protocol.Account.name acc - ; new_protocol_hash ] - >>= fun _ -> - Console.sayf state - Fmt.( - fun ppf () -> - pf ppf "%s voted for %s" - (Tezos_protocol.Account.name acc) - new_protocol_hash) ) + submit_prop acc client new_protocol_hash ) >>= fun () -> - wait_for_voting_period state ~client:client_0 ~attempts:50 Testing_vote + let make_dummy_protocol_hashes t tag = + List.map + (List.init extra_dummy_proposals_batch_size ~f:(fun s -> + sprintf "proto-%s-%d" tag s )) + ~f:(fun s -> + (t, Tezos_crypto.Protocol_hash.(hash_string [s] |> to_b58check)) ) + in + let extra_dummy_protocols = + List.bind extra_dummy_proposals_batch_levels ~f:(fun l -> + make_dummy_protocol_hashes l (sprintf "%d" l) ) + in + Console.say state + EF.( + wf "Going to also vote for %s" + (String.concat ~sep:", " (List.map extra_dummy_protocols ~f:snd))) + >>= fun () -> + List_sequential.iteri extra_dummy_protocols + ~f:(fun nth (level, proto_hash) -> + match List.nth keys_and_daemons (nth / 19) with + | None -> + failf "Too many dummy protocols Vs available voting power (%d)" nth + | Some (acc, client, _) -> + wait_for_voting_period state ~client:client_0 + ~attempts:waiting_attempts Proposal ~level_within_period:level + >>= fun _ -> submit_prop acc client proto_hash ) + >>= fun () -> + wait_for_voting_period state ~client:client_0 ~attempts:waiting_attempts + Testing_vote >>= fun _ -> List_sequential.iter keys_and_daemons ~f:(fun (acc, client, _) -> Tezos_client.successful_client_cmd state ~client @@ -251,13 +287,20 @@ let run state ~protocol ~size ~base_port ~no_daemons_for ?external_peer_ports (Tezos_protocol.Account.name acc) new_protocol_hash) ) >>= fun () -> - wait_for_voting_period state ~client:client_0 ~attempts:50 Promotion_vote + wait_for_voting_period state ~client:client_0 ~attempts:waiting_attempts + Promotion_vote >>= fun _ -> + let protocol_switch_will_happen = + match test_variant with + | `Full_upgrade -> true + | `Nay_for_promotion -> false + in List_sequential.iter keys_and_daemons ~f:(fun (acc, client, _) -> Tezos_client.successful_client_cmd state ~client [ "submit"; "ballot"; "for" ; Tezos_protocol.Account.name acc - ; new_protocol_hash; "yea" ] + ; new_protocol_hash + ; (if protocol_switch_will_happen then "yea" else "nay") ] >>= fun _ -> Console.sayf state Fmt.( @@ -266,12 +309,17 @@ let run state ~protocol ~size ~base_port ~no_daemons_for ?external_peer_ports (Tezos_protocol.Account.name acc) new_protocol_hash) ) >>= fun () -> - wait_for_voting_period state ~client:client_0 ~attempts:50 Proposal + wait_for_voting_period state ~client:client_0 ~attempts:waiting_attempts + Proposal >>= fun _ -> Tezos_client.successful_client_cmd state ~client:client_0 ["show"; "voting"; "period"] >>= fun res -> - Helpers.wait_for state ~attempts:3 ~seconds:4. (fun _ -> + let protocol_to_wait_for = + if protocol_switch_will_happen then new_protocol_hash + else protocol.Tezos_protocol.hash + in + Helpers.wait_for state ~attempts:waiting_attempts ~seconds:4. (fun _ -> Console.say state EF.(wf "Checking actual protocol transition") >>= fun () -> Tezos_client.rpc state ~client:client_0 `Get @@ -280,26 +328,37 @@ let run state ~protocol ~size ~base_port ~no_daemons_for ?external_peer_ports ( try Jqo.field ~k:"protocol" json |> Jqo.get_string |> return with e -> failf "Cannot parse metadata: %s" (Printexc.to_string e) ) >>= fun proto_hash -> - if proto_hash <> new_protocol_hash then + if proto_hash <> protocol_to_wait_for then return (`Not_done - (sprintf "Protocol not done: %s Vs %s" proto_hash new_protocol_hash)) + (sprintf "Protocol not done: %s Vs %s" proto_hash + protocol_to_wait_for)) else return (`Done ()) ) >>= fun () -> Interactive_test.Pauser.generic state EF. [ wf "Test finished, protocol is now %s, things should keep baking." - new_protocol_hash + protocol_to_wait_for ; markdown_verbatim (String.concat ~sep:"\n" res#out) ] ~force:true let cmd ~pp_error () = let open Cmdliner in let open Term in + let variants = + [ ( "full-upgrade" + , `Full_upgrade + , "Go through the whole voting process and do the protocol change." ) + ; ( "nay-for-promotion" + , `Nay_for_promotion + , "Go through the whole voting process but vote Nay at the last period \ + and hence stay on the same protocol." ) ] + in Test_command_line.Run_command.make ~pp_error ( pure (fun size base_port + (`Attempts waiting_attempts) (`External_peers external_peer_ports) (`No_daemons_for no_daemons_for) protocol @@ -313,7 +372,11 @@ let cmd ~pp_error () = second_endorser_exec second_accuser_exec (`Protocol_path new_protocol_path) + (`Extra_dummy_proposals_batch_size extra_dummy_proposals_batch_size) + (`Extra_dummy_proposals_batch_levels + extra_dummy_proposals_batch_levels) generate_kiln_config + test_variant state -> let actual_test = @@ -321,7 +384,9 @@ let cmd ~pp_error () = ~first_baker_exec ~first_endorser_exec ~first_accuser_exec ~second_baker_exec ~second_endorser_exec ~second_accuser_exec ~admin_exec ?generate_kiln_config ~external_peer_ports - ~no_daemons_for ~new_protocol_path + ~no_daemons_for ~new_protocol_path test_variant ~waiting_attempts + ~extra_dummy_proposals_batch_size + ~extra_dummy_proposals_batch_levels in (state, Interactive_test.Pauser.run_test ~pp_error state actual_test) ) @@ -331,6 +396,13 @@ let cmd ~pp_error () = $ Arg.( value & opt int 20_000 & info ["base-port"; "P"] ~doc:"Base port number to build upon.") + $ Arg.( + pure (fun n -> `Attempts n) + $ value + (opt int 60 + (info ["waiting-attempts"] + ~doc: + "Number of attempts done while waiting for voting periods"))) $ Arg.( pure (fun l -> `External_peers l) $ value @@ -359,7 +431,36 @@ let cmd ~pp_error () = (pos 0 (some string) None (info [] ~doc:"The protocol to inject and vote on." ~docv:"PROTOCOL-PATH"))) + $ Arg.( + pure (fun l -> `Extra_dummy_proposals_batch_size l) + $ value + (opt int 0 + (info + ["extra-dummy-proposals-batch-size"] + ~docv:"NUMBER" + ~doc:"Submit $(docv) extra proposals per batch."))) + $ Arg.( + pure (fun x -> `Extra_dummy_proposals_batch_levels x) + $ value + (opt (list ~sep:',' int) [] + (info + ["extra-dummy-proposals-batch-levels"] + ~docv:"NUMBER" + ~doc: + "Set the levels within the proposal period where batches \ + of extra proposals appear, e.g. `3,5,7`."))) $ Kiln.Configuration_directory.cli_term () + $ Arg.( + let doc = + sprintf "Which variant of the test to run (one of {%s})" + ( List.map ~f:(fun (n, _, _) -> n) variants + |> String.concat ~sep:", " ) + in + value + (opt + (enum (List.map variants ~f:(fun (n, v, _) -> (n, v)))) + `Full_upgrade + (info ["test-variant"] ~doc))) $ Test_command_line.cli_state ~name:"daemons-upgrade" () ) (let doc = "Vote and Protocol-upgrade with bakers, endorsers, and accusers." @@ -369,6 +470,13 @@ let cmd ~pp_error () = ; `P "This test builds and runs a sandbox network to do a full voting \ round followed by a protocol change while all the daemons." + ; `P + (sprintf + "There are for now %d variants (see option `--test-variant`):" + (List.length variants)) + ; `Blocks + (List.concat_map variants ~f:(fun (n, _, desc) -> + [`Noblank; `P (sprintf "* `%s`: %s" n desc)] )) ; `P "The test is interactive-only:" ; `Blocks (List.concat_mapi @@ -382,8 +490,9 @@ let cmd ~pp_error () = full voting round happens with a single proposal: the one at \ `PROTOCOL-PATH` (which should be the one understood by the \ `--second-*` executables)." - ; "Once the protocol switch has happened (and been verified), \ - the test re-enters an interactive prompt to let the user \ - play with the new protocol." ]) ] + ; "Once the potential protocol switch has happened (and been \ + verified), the test re-enters an interactive prompt to let \ + the user play with the protocol (the first or second one, \ + depending on the `--test-variant` option)." ]) ] in info "daemons-upgrade" ~man ~doc) diff --git a/src/bin_flextesa/command_voting.ml b/src/bin_flextesa/command_voting.ml index e8e8f4479a1f16a4081424e0fdbf247c0cf9ac72..56bf78f83a1490db16ad7da322399ef18a3bce5c 100644 --- a/src/bin_flextesa/command_voting.ml +++ b/src/bin_flextesa/command_voting.ml @@ -50,7 +50,8 @@ let transfer state ~client ~src ~dst ~amount = let register state ~client ~dst = Tezos_client.successful_client_cmd state ~client - [ "--wait"; "none"; "register"; "key" ; dst ; "as" ; "delegate" ; "--fee"; "0.05" ] + [ "--wait"; "none"; "register"; "key"; dst; "as"; "delegate"; "--fee" + ; "0.05" ] let bake_until_voting_period ?keep_alive_delegate state ~baker ~attempts period = @@ -63,8 +64,7 @@ let bake_until_voting_period ?keep_alive_delegate state ~baker ~attempts period | `String p when p = period_name -> return (`Done (nth - 1)) | other -> Asynchronous_result.map_option keep_alive_delegate ~f:(fun dst -> - register state ~client ~dst - >>= fun res -> return () ) + register state ~client ~dst >>= fun res -> return () ) >>= fun _ -> ksprintf (Tezos_client.Keyed.bake state baker) @@ -611,10 +611,12 @@ let cmd ~pp_error () = pure Filename.dirname $ required (pos 1 (some string) None - (info [] ~docv:"LOOSER-PROTOCOL-PATH" + (info [] ~docv:"LOSER-PROTOCOL-PATH" ~doc: "The protocol to inject and down-vote, e.g. \ - `./src/bin_client/test/proto_test_injection/TEZOS_PROTOCOL`."))) + `./src/bin_client/test/proto_test_injection/TEZOS_PROTOCOL` \ + (if same as `WINNER-PROTOCOL-PATH` the scenario will \ + make them automatically & artificially different)."))) $ Tezos_executable.cli_term `Node "current" $ Tezos_executable.cli_term `Client "current" $ Tezos_executable.cli_term `Admin "current" diff --git a/src/lib_network_sandbox/interactive_test.ml b/src/lib_network_sandbox/interactive_test.ml index fa3bcc4c65ac2af16b29bc491d7966aa739b9a46..c5632997241e6f68783129f887105adefe272191 100644 --- a/src/lib_network_sandbox/interactive_test.ml +++ b/src/lib_network_sandbox/interactive_test.ml @@ -397,7 +397,7 @@ module Interactivity = struct match (interactive, pause_end, pause_error) with | true, _, _ -> `Full | false, true, _ -> `At_end - | false, false, true -> `At_end + | false, false, true -> `On_error | false, false, false -> `None ) $ Arg.( value @@ -486,7 +486,10 @@ module Pauser = struct ~force:(Interactivity.pause_on_error state) EF. [ haf "Last pause before the test will Kill 'Em All and Quit." - ; desc (shout "Error:") (af "%a" pp_error error_value) ] + ; desc (shout "Error:") + (af "%a" + (fun ppf c -> Attached_result.pp ppf c ~pp_error) + result) ] >>= fun () -> finish () >>= fun () -> fail error_value ~attach:result.attachments ) end diff --git a/src/lib_network_sandbox/internal_pervasives.ml b/src/lib_network_sandbox/internal_pervasives.ml index bf876287b454b6645c693fdb0ce418771496b6a8..095e8a3311f545a8b332898999fee6f199064e82 100644 --- a/src/lib_network_sandbox/internal_pervasives.ml +++ b/src/lib_network_sandbox/internal_pervasives.ml @@ -87,7 +87,8 @@ end (** An “decorated result type” based on polymorphic variants *) module Attached_result = struct - type content = [`Text of string | `String_value of string] + type content = + [`Text of string | `String_value of string | `Verbatim of string list] type ('ok, 'error) t = {result: ('ok, 'error) result; attachments: (string * content) list} @@ -98,6 +99,7 @@ module Attached_result = struct let pp ppf ?pp_ok ?pp_error {result; attachments} = let open Format in + pp_open_hovbox ppf 4 ; ( match result with | Ok o -> pp_open_hvbox ppf 2 ; @@ -115,18 +117,30 @@ module Attached_result = struct pp_close_tag ppf () ; Option.iter pp_error ~f:(fun pp -> pp ppf e) ; pp_close_box ppf () ) ; - match attachments with + ( match attachments with | [] -> () | more -> - pp_print_newline ppf () ; - pp_open_hovbox ppf 4 ; + pp_open_vbox ppf 4 ; List.iter more ~f:(fun (k, v) -> - pp_print_if_newline ppf () ; + pp_print_cut ppf () ; + pp_open_hovbox ppf 2 ; pp_print_string ppf "* " ; fprintf ppf "%s:@ " k ; - match v with + ( match v with | `Text s -> pp_print_text ppf s - | `String_value s -> fprintf ppf "%S" s ) + | `String_value s -> fprintf ppf "%S" s + | `Verbatim lines -> + pp_open_vbox ppf 0 ; + pp_print_cut ppf () ; + fprintf ppf "```````````````" ; + List.iter lines ~f:(fun s -> + pp_print_cut ppf () ; pp_print_string ppf s ) ; + pp_print_cut ppf () ; + fprintf ppf "```````````````" ; + pp_close_box ppf () ) ; + pp_close_box ppf () ) ; + pp_close_box ppf () ) ; + pp_close_box ppf () end (** A wrapper around [('ok, 'a Error.t) result Lwt.t]. *) @@ -172,7 +186,9 @@ module Asynchronous_result = struct | {result= Error e; attachments= attach} as res -> f ~result:res e >>= fun {result; attachments} -> - Lwt.return {result; attachments= attachments @ attach} + Lwt.return + { result + ; attachments= List.dedup_and_sort ~compare (attachments @ attach) } let transform_error o ~f = let open Lwt.Infix in @@ -182,6 +198,13 @@ module Asynchronous_result = struct | {result= Error e; attachments} -> Lwt.return {result= Error (f e); attachments} + let enrich : attachment:(string * content) list -> 'a -> ('b, 'c) t = + fun ~attachment x -> + bind_on_error x ~f:(fun ~result _ -> + Lwt.return + Attached_result. + {result with attachments= result.attachments @ attachment} ) + let bind_all : ('ok, 'error) t -> f:(('ok, 'error) Attached_result.t -> ('ok2, 'error2) t) diff --git a/src/lib_network_sandbox/kiln.ml b/src/lib_network_sandbox/kiln.ml index 492b4f3b7d6ce4bb1efeeb60abc07ee2c159ba38..d32e8ab99e2d525e75ef05fdd060cdb5cc47332b 100644 --- a/src/lib_network_sandbox/kiln.ml +++ b/src/lib_network_sandbox/kiln.ml @@ -66,6 +66,10 @@ module Configuration_directory = struct System.write_file state ~perm:0o777 (path // "network") ~content:network_string >>= fun () -> + System.write_file state ~perm:0o777 + (path // "enable-obsidian-node") + ~content:(sprintf "%b" false) + >>= fun () -> System.write_file state ~perm:0o777 (path // "binary-paths") ~content: Ezjsonm.( diff --git a/src/lib_network_sandbox/running_processes.ml b/src/lib_network_sandbox/running_processes.ml index 1e34f13e7687dc324fde30270176db5aace49adb..5a95e76abe2eb4870c9fcb4207e1819409b18591 100644 --- a/src/lib_network_sandbox/running_processes.ml +++ b/src/lib_network_sandbox/running_processes.ml @@ -240,12 +240,13 @@ let fresh_id _state prefix ~seed = let run_cmdf state fmt = let get_file path = Lwt_exception.catch - Lwt.Infix.( + Lwt.( fun () -> Lwt_io.open_file ~mode:Lwt_io.input path >>= fun inchan -> let stream = Lwt_io.read_lines inchan in - Lwt_stream.to_list stream) + Lwt_stream.to_list stream + >>= fun l -> Lwt_io.close inchan >>= fun () -> return l) () in ksprintf diff --git a/src/lib_network_sandbox/tezos_protocol.ml b/src/lib_network_sandbox/tezos_protocol.ml index 4af00cc56278c96710f0a3cda5a14bc363543f39..2ded9e698b145f3d88d3ac11fb997527a8e75def 100644 --- a/src/lib_network_sandbox/tezos_protocol.ml +++ b/src/lib_network_sandbox/tezos_protocol.ml @@ -311,62 +311,72 @@ let ensure t ~config = let cli_term () = let open Cmdliner in let open Term in + let def = default () in + let docs = "PROTOCOL OPTIONS" in pure (fun remove_default_bas - (`Blocks_per_voting_period bpvp) - (`Protocol_hash hashopt) - (`Time_between_blocks tbb) + (`Blocks_per_voting_period blocks_per_voting_period) + (`Protocol_hash hash) + (`Time_between_blocks time_between_blocks) + (`Blocks_per_cycle blocks_per_cycle) + (`Preserved_cycles preserved_cycles) add_bootstraps -> - let d = default () in - let id = - if add_bootstraps = [] && remove_default_bas = false then d.id - else "default-and-command-line" - in - let time_between_blocks = - Option.value tbb ~default:d.time_between_blocks - in + let id = "default-and-command-line" in let bootstrap_accounts = add_bootstraps - @ if remove_default_bas then [] else d.bootstrap_accounts + @ if remove_default_bas then [] else def.bootstrap_accounts in - let blocks_per_voting_period = - match bpvp with Some v -> v | None -> d.blocks_per_voting_period - in - let hash = Option.value hashopt ~default:d.hash in - { d with + { def with id + ; blocks_per_cycle ; hash ; bootstrap_accounts ; time_between_blocks + ; preserved_cycles ; blocks_per_voting_period } ) $ Arg.( value (flag (info ~doc:"Do not create any of the default bootstrap accounts." + ~docs ["remove-default-bootstrap-accounts"]))) $ Arg.( pure (fun x -> `Blocks_per_voting_period x) $ value - (opt (some int) None - (info + (opt int def.blocks_per_voting_period + (info ~docs ["blocks-per-voting-period"] - ~doc:"Set the length of voting periods"))) + ~doc:"Set the length of voting periods."))) $ Arg.( pure (fun x -> `Protocol_hash x) $ value - (opt (some string) None - (info ["protocol-hash"] ~doc:"Set the (starting) protocol hash."))) + (opt string def.hash + (info ["protocol-hash"] ~docs + ~doc:"Set the (initial) protocol hash."))) $ Arg.( pure (fun x -> `Time_between_blocks x) $ value - (opt - (some (list ~sep:',' int)) - None + (opt (list ~sep:',' int) def.time_between_blocks (info ["time-between-blocks"] ~docv:"COMMA-SEPARATED-SECONDS" + ~docs ~doc: "Set the time between blocks bootstrap-parameter, e.g. \ `2,3,2`."))) + $ Arg.( + pure (fun x -> `Blocks_per_cycle x) + $ value + (opt int def.blocks_per_cycle + (info ["blocks-per-cycle"] ~docv:"NUMBER" ~docs + ~doc:"Number of blocks per cycle."))) + $ Arg.( + pure (fun x -> `Preserved_cycles x) + $ value + (opt int def.preserved_cycles + (info ["preserved-cycles"] ~docv:"NUMBER" ~docs + ~doc: + "Base constant for baking rights (search for \ + `PRESERVED_CYCLES` in the white paper)."))) $ Arg.( pure (fun l -> List.map l ~f:(fun ((name, pubkey, pubkey_hash, private_key), tez) -> @@ -376,7 +386,7 @@ let cli_term () = (opt_all (pair ~sep:'@' (t4 ~sep:',' string string string string) int64) [] - (info ["add-bootstrap-account"] + (info ["add-bootstrap-account"] ~docs ~docv:"NAME,PUBKEY,PUBKEY-HASH,PRIVATE-URI@MUTEZ-AMOUNT" ~doc: "Add a custom bootstrap account, e.g. \