@@ -297,12 +297,113 @@ def registration_certificate(
297297 self ._clusterlib_obj .cli (cmd , add_default_args = False )
298298
299299
300+ class GovernanceActionGroup :
301+ """Governance action subcommands for compatible eras."""
302+
303+ def __init__ (self , clusterlib_obj : "ClusterLib" , era : str ) -> None :
304+ self ._clusterlib_obj = clusterlib_obj
305+ self ._base = ("cardano-cli" , "compatible" , era , "governance" , "action" )
306+
307+ def _resolve_pparam_args (
308+ self ,
309+ * ,
310+ epoch : int | None = None ,
311+ genesis_vkey_file : itp .FileType | None = None ,
312+ pparams : dict [str , object ] | None = None ,
313+ ) -> list [str ]:
314+ """Resolve protocol parameter update CLI arguments."""
315+ if epoch is None :
316+ message = "The `epoch` parameter is required for protocol parameters update."
317+ raise ValueError (message )
318+
319+ if not genesis_vkey_file :
320+ message = "`genesis_vkey_file` is required for protocol parameters update."
321+ raise ValueError (message )
322+
323+ if not pparams :
324+ message = "At least one protocol parameter must be provided."
325+ raise ValueError (message )
326+
327+ args : list [str ] = ["--epoch" , str (epoch )]
328+
329+ for flag , value in pparams .items ():
330+ if value is None :
331+ continue
332+ args .extend ([flag , str (value )])
333+
334+ args .extend (["--genesis-verification-key-file" , str (genesis_vkey_file )])
335+
336+ return args
337+
338+ def create_protocol_parameters_update (
339+ self ,
340+ * ,
341+ epoch : int ,
342+ genesis_vkey_file : itp .FileType ,
343+ out_file : itp .FileType ,
344+ ** pparams : object ,
345+ ) -> None :
346+ """Wrap the protocol parameters update command."""
347+ flag_map = {
348+ "min_fee_linear" : "--min-fee-linear" ,
349+ "min_fee_constant" : "--min-fee-constant" ,
350+ "max_block_body_size" : "--max-block-body-size" ,
351+ "max_tx_size" : "--max-tx-size" ,
352+ "max_block_header_size" : "--max-block-header-size" ,
353+ "key_reg_deposit_amt" : "--key-reg-deposit-amt" ,
354+ "pool_reg_deposit" : "--pool-reg-deposit" ,
355+ "pool_retirement_epoch_interval" : "--pool-retirement-epoch-interval" ,
356+ "number_of_pools" : "--number-of-pools" ,
357+ "pool_influence" : "--pool-influence" ,
358+ "treasury_expansion" : "--treasury-expansion" ,
359+ "monetary_expansion" : "--monetary-expansion" ,
360+ "min_pool_cost" : "--min-pool-cost" ,
361+ "price_execution_steps" : "--price-execution-steps" ,
362+ "price_execution_memory" : "--price-execution-memory" ,
363+ "max_tx_execution_units" : "--max-tx-execution-units" ,
364+ "max_block_execution_units" : "--max-block-execution-units" ,
365+ "max_value_size" : "--max-value-size" ,
366+ "collateral_percent" : "--collateral-percent" ,
367+ "max_collateral_inputs" : "--max-collateral-inputs" ,
368+ "protocol_major_version" : "--protocol-major-version" ,
369+ "protocol_minor_version" : "--protocol-minor-version" ,
370+ "utxo_cost_per_byte" : "--utxo-cost-per-byte" ,
371+ "cost_model_file" : "--cost-model-file" ,
372+ }
373+
374+ pparam_args : dict [str , object ] = {}
375+
376+ for py_key , value in pparams .items ():
377+ if py_key not in flag_map :
378+ msg = f"Unknown protocol parameter: { py_key } "
379+ raise ValueError (msg )
380+ if value is not None :
381+ pparam_args [flag_map [py_key ]] = value
382+
383+ resolved_args = self ._resolve_pparam_args (
384+ epoch = epoch ,
385+ genesis_vkey_file = genesis_vkey_file ,
386+ pparams = pparam_args ,
387+ )
388+
389+ cmd = [
390+ * self ._base ,
391+ "create-protocol-parameters-update" ,
392+ * resolved_args ,
393+ "--out-file" ,
394+ str (out_file ),
395+ ]
396+
397+ self ._clusterlib_obj .cli (cmd , add_default_args = False )
398+
399+
300400class GovernanceGroup :
301401 """Generic governance group for all compatible eras."""
302402
303403 def __init__ (self , clusterlib_obj : "ClusterLib" , era : str ) -> None :
304404 self ._clusterlib_obj = clusterlib_obj
305405 self ._base = ("cardano-cli" , "compatible" , era , "governance" )
406+ self .action = GovernanceActionGroup (clusterlib_obj , era )
306407
307408 def _resolve_mir_direct_args (
308409 self ,
@@ -313,19 +414,9 @@ def _resolve_mir_direct_args(
313414 reward : int | None ,
314415 out_file : itp .FileType ,
315416 ) -> list [str ] | None :
316- """Resolve direct MIR mode args (no subcommand).
317-
318- Direct mode syntax:
319- (--reserves | --treasury)
320- --stake-address ADDRESS
321- --reward LOVELACE
322- --out-file FILEPATH
323- """
324- # No direct mode selected
325417 if not reserves and not treasury :
326418 return None
327419
328- # Invalid: both pots selected
329420 if reserves and treasury :
330421 msg = "Cannot specify both `reserves` and `treasury` in direct MIR mode."
331422 raise ValueError (msg )
@@ -338,10 +429,10 @@ def _resolve_mir_direct_args(
338429 msg = "`reward` is required in direct MIR mode."
339430 raise ValueError (msg )
340431
341- pot_flag = "--reserves" if reserves else "--treasury"
432+ flag = "--reserves" if reserves else "--treasury"
342433
343- args : list [ str ] = [
344- pot_flag ,
434+ args = [
435+ flag ,
345436 "--stake-address" ,
346437 stake_address ,
347438 "--reward" ,
@@ -359,7 +450,6 @@ def _mir_stake_addresses_args(
359450 funds : str | None ,
360451 out_file : itp .FileType ,
361452 ) -> list [str ]:
362- """Arguments for `create-mir-certificate stake-addresses`."""
363453 if not stake_address :
364454 msg = "`stake_address` is required for 'stake-addresses' MIR subcommand."
365455 raise ValueError (msg )
@@ -372,7 +462,7 @@ def _mir_stake_addresses_args(
372462 msg = "`funds` must be either 'reserves' or 'treasury' for 'stake-addresses'."
373463 raise ValueError (msg )
374464
375- args : list [ str ] = [
465+ args = [
376466 "stake-addresses" ,
377467 f"--{ funds } " ,
378468 "--stake-address" ,
@@ -390,39 +480,35 @@ def _mir_transfer_to_treasury_args(
390480 transfer_amt : int | None ,
391481 out_file : itp .FileType ,
392482 ) -> list [str ]:
393- """Arguments for `create-mir-certificate transfer-to-treasury`."""
394483 if transfer_amt is None :
395484 msg = "`transfer_amt` is required for 'transfer-to-treasury' MIR subcommand."
396485 raise ValueError (msg )
397486
398- args : list [ str ] = [
487+ return [
399488 "transfer-to-treasury" ,
400489 "--transfer" ,
401490 str (transfer_amt ),
402491 "--out-file" ,
403492 str (out_file ),
404493 ]
405- return args
406494
407495 def _mir_transfer_to_rewards_args (
408496 self ,
409497 * ,
410498 transfer_amt : int | None ,
411499 out_file : itp .FileType ,
412500 ) -> list [str ]:
413- """Arguments for `create-mir-certificate transfer-to-rewards`."""
414501 if transfer_amt is None :
415502 msg = "`transfer_amt` is required for 'transfer-to-rewards' MIR subcommand."
416503 raise ValueError (msg )
417504
418- args : list [ str ] = [
505+ return [
419506 "transfer-to-rewards" ,
420507 "--transfer" ,
421508 str (transfer_amt ),
422509 "--out-file" ,
423510 str (out_file ),
424511 ]
425- return args
426512
427513 def _resolve_mir_subcommand_args (
428514 self ,
@@ -434,13 +520,6 @@ def _resolve_mir_subcommand_args(
434520 funds : str | None ,
435521 out_file : itp .FileType ,
436522 ) -> list [str ] | None :
437- """Resolve MIR subcommand arguments, if any.
438-
439- Supported subcommands:
440- * stake-addresses
441- * transfer-to-treasury
442- * transfer-to-rewards
443- """
444523 if not subcommand :
445524 return None
446525
@@ -470,34 +549,15 @@ def _resolve_mir_subcommand_args(
470549 def create_mir_certificate (
471550 self ,
472551 * ,
473- # Direct MIR mode
474552 reserves : bool = False ,
475553 treasury : bool = False ,
476554 reward : int | None = None ,
477555 stake_address : str | None = None ,
478- # Subcommand mode
479556 subcommand : str | None = None ,
480557 transfer_amt : int | None = None ,
481558 funds : str | None = None ,
482- # Output
483559 out_file : itp .FileType ,
484560 ) -> None :
485- """Wrap the `governance create-mir-certificate` command (compatible eras).
486-
487- Two usage modes:
488-
489- 1. Direct MIR:
490- * `reserves` or `treasury` (exactly one)
491- * `stake_address`
492- * `reward`
493- * `out_file`
494-
495- 2. Subcommands:
496- * `subcommand="stake-addresses"`, `funds=("reserves"|"treasury")`,
497- `stake_address`, `reward`, `out_file`
498- * `subcommand="transfer-to-treasury"`, `transfer_amt`, `out_file`
499- * `subcommand="transfer-to-rewards"`, `transfer_amt`, `out_file`
500- """
501561 direct_args = self ._resolve_mir_direct_args (
502562 reserves = reserves ,
503563 treasury = treasury ,
@@ -515,21 +575,24 @@ def create_mir_certificate(
515575 out_file = out_file ,
516576 )
517577
518- # Cannot mix modes
519578 if direct_args and subcmd_args :
520579 msg = "Cannot mix direct MIR mode with MIR subcommand mode."
521580 raise ValueError (msg )
522581
523582 if not direct_args and not subcmd_args :
524583 msg = (
525584 "No MIR mode selected. Provide either direct MIR parameters "
526- "(reserves/treasury + stake_address + reward) "
527585 "or a valid MIR subcommand."
528586 )
529587 raise ValueError (msg )
530588
531- final_args = direct_args or subcmd_args
532- assert final_args is not None
589+ if direct_args is not None :
590+ final_args : list [str ] = direct_args
591+ else :
592+ msg = "Internal error: MIR subcommand arguments missing."
593+ if subcmd_args is None :
594+ raise ValueError (msg )
595+ final_args = subcmd_args
533596
534597 cmd = [
535598 * self ._base ,
@@ -539,14 +602,17 @@ def create_mir_certificate(
539602
540603 self ._clusterlib_obj .cli (cmd , add_default_args = False )
541604
605+ self ._clusterlib_obj .cli (cmd , add_default_args = False )
606+
607+ self ._clusterlib_obj .cli (cmd , add_default_args = False )
608+
542609 def _resolve_genesis_key_args (
543610 self ,
544611 * ,
545612 genesis_vkey : str = "" ,
546613 genesis_vkey_file : itp .FileType | None = None ,
547614 genesis_vkey_hash : str = "" ,
548615 ) -> list [str ]:
549- """Resolve genesis verification key specification."""
550616 if genesis_vkey :
551617 return ["--genesis-verification-key" , genesis_vkey ]
552618
@@ -556,10 +622,7 @@ def _resolve_genesis_key_args(
556622 if genesis_vkey_hash :
557623 return ["--genesis-verification-key-hash" , genesis_vkey_hash ]
558624
559- msg = (
560- "One of genesis_vkey, genesis_vkey_file or genesis_vkey_hash "
561- "must be provided for genesis key delegation."
562- )
625+ msg = "One of genesis_vkey, genesis_vkey_file or genesis_vkey_hash must be provided."
563626 raise ValueError (msg )
564627
565628 def _resolve_delegate_key_args (
@@ -569,7 +632,6 @@ def _resolve_delegate_key_args(
569632 delegate_vkey_file : itp .FileType | None = None ,
570633 delegate_vkey_hash : str = "" ,
571634 ) -> list [str ]:
572- """Resolve delegate verification key specification."""
573635 if delegate_vkey :
574636 return ["--genesis-delegate-verification-key" , delegate_vkey ]
575637
@@ -579,10 +641,7 @@ def _resolve_delegate_key_args(
579641 if delegate_vkey_hash :
580642 return ["--genesis-delegate-verification-key-hash" , delegate_vkey_hash ]
581643
582- msg = (
583- "One of delegate_vkey, delegate_vkey_file or delegate_vkey_hash "
584- "must be provided for genesis key delegation."
585- )
644+ msg = "One of delegate_vkey, delegate_vkey_file or delegate_vkey_hash must be provided."
586645 raise ValueError (msg )
587646
588647 def _resolve_vrf_key_args (
@@ -592,7 +651,6 @@ def _resolve_vrf_key_args(
592651 vrf_vkey_file : itp .FileType | None = None ,
593652 vrf_vkey_hash : str = "" ,
594653 ) -> list [str ]:
595- """Resolve VRF key specification."""
596654 if vrf_vkey :
597655 return ["--vrf-verification-key" , vrf_vkey ]
598656
@@ -602,10 +660,7 @@ def _resolve_vrf_key_args(
602660 if vrf_vkey_hash :
603661 return ["--vrf-verification-key-hash" , vrf_vkey_hash ]
604662
605- msg = (
606- "One of vrf_vkey, vrf_vkey_file or vrf_vkey_hash "
607- "must be provided for genesis key delegation."
608- )
663+ msg = "One VRF key argument must be provided."
609664 raise ValueError (msg )
610665
611666 def create_genesis_key_delegation_certificate (
@@ -622,7 +677,6 @@ def create_genesis_key_delegation_certificate(
622677 vrf_vkey_hash : str = "" ,
623678 out_file : itp .FileType ,
624679 ) -> None :
625- """Wrap the `governance create-genesis-key-delegation-certificate` command."""
626680 genesis_args = self ._resolve_genesis_key_args (
627681 genesis_vkey = genesis_vkey ,
628682 genesis_vkey_file = genesis_vkey_file ,
0 commit comments