From 1f871b433a8797f57252b9f9ba7dc289432766fe Mon Sep 17 00:00:00 2001 From: femi Date: Fri, 14 Nov 2025 11:07:13 +0000 Subject: [PATCH 01/28] Compatible group --- .../compatilbe_group/compatible_group.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 cardano_clusterlib/compatilbe_group/compatible_group.py diff --git a/cardano_clusterlib/compatilbe_group/compatible_group.py b/cardano_clusterlib/compatilbe_group/compatible_group.py new file mode 100644 index 0000000..2c6b80f --- /dev/null +++ b/cardano_clusterlib/compatilbe_group/compatible_group.py @@ -0,0 +1,30 @@ +"""Group of subgroups for cardano-cli 'compatible' commands (legacy eras).""" + +import logging + +from cardano_clusterlib.compatilbe_group import compatible_alonzo_group + +LOGGER = logging.getLogger(__name__) + + +class CompatibleGroup: + def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: + self._clusterlib_obj = clusterlib_obj + + # Groups of commands per era + self._alonzo_group: compatible_alonzo_group.CompatibleAlonzoGroup | None = None + # self._mary_group: compatible_mary_group.CompatibleMaryGroup | None = None + # self._shelley_group: compatible_shelley_group.CompatibleShelleyGroup | None = None + # ... + + @property + def alonzo(self) -> "compatible_alonzo_group.CompatibleAlonzoGroup": + """Alonzo compatible era group.""" + if not self._alonzo_group: + self._alonzo_group = compatible_alonzo_group.CompatibleAlonzoGroup( + clusterlib_obj=self._clusterlib_obj + ) + return self._alonzo_group + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}: clusterlib_obj={id(self._clusterlib_obj)}>" From 0ee54fdd7fcc4bf5d8b9742454968d4fb6468006 Mon Sep 17 00:00:00 2001 From: femi Date: Mon, 17 Nov 2025 03:07:25 +0000 Subject: [PATCH 02/28] Compatible group --- .../compatible_alonzo_group.py | 31 +++++++++++ .../compatible_alonzo_transaction_group.py | 52 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 cardano_clusterlib/compatilbe_group/compatible_alonzo_group.py create mode 100644 cardano_clusterlib/compatilbe_group/compatible_alonzo_transaction_group.py diff --git a/cardano_clusterlib/compatilbe_group/compatible_alonzo_group.py b/cardano_clusterlib/compatilbe_group/compatible_alonzo_group.py new file mode 100644 index 0000000..6ff8d42 --- /dev/null +++ b/cardano_clusterlib/compatilbe_group/compatible_alonzo_group.py @@ -0,0 +1,31 @@ +"""Group of subgroups for 'compatible alonzo' commands.""" + +import logging + +from cardano_clusterlib.compatilbe_group import compatible_alonzo_transaction_group + +LOGGER = logging.getLogger(__name__) + + +class CompatibleAlonzoGroup: + def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: + self._clusterlib_obj = clusterlib_obj + + # Groups of commands within this era + self._transaction_group: ( + compatible_alonzo_transaction_group.CompatibleAlonzoTransactionGroup | None + ) = None + + @property + def transaction(self) -> "compatible_alonzo_transaction_group.CompatibleAlonzoTransactionGroup": + """Transaction group for Alonzo compatible commands.""" + if not self._transaction_group: + self._transaction_group = ( + compatible_alonzo_transaction_group.CompatibleAlonzoTransactionGroup( + clusterlib_obj=self._clusterlib_obj + ) + ) + return self._transaction_group + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}: era=alonzo clusterlib_obj={id(self._clusterlib_obj)}>" diff --git a/cardano_clusterlib/compatilbe_group/compatible_alonzo_transaction_group.py b/cardano_clusterlib/compatilbe_group/compatible_alonzo_transaction_group.py new file mode 100644 index 0000000..3aa7791 --- /dev/null +++ b/cardano_clusterlib/compatilbe_group/compatible_alonzo_transaction_group.py @@ -0,0 +1,52 @@ +"""Transaction commands for 'cardano-cli compatible alonzo transaction'.""" + +import logging +from typing import Sequence + +from cardano_clusterlib import types as itp + +LOGGER = logging.getLogger(__name__) + + +class CompatibleAlonzoTransactionGroup: + def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: + self._clusterlib_obj = clusterlib_obj + + def build_raw( + self, + txins: Sequence[str], + txouts: Sequence[str], + out_file: itp.FileType, + extra_args: Sequence[str] | None = None, + ) -> None: + """Simple wrapper for `cardano-cli compatible alonzo transaction build-raw`. + """ + if not txins: + msg = "`txins` must not be empty for compatible transaction build-raw." + raise ValueError(msg) + + extra_args = extra_args or () + + cmd: list[str] = [ + "cardano-cli", + "compatible", + "alonzo", + "transaction", + "build-raw", + ] + + for txin in txins: + cmd.extend(["--tx-in", txin]) + + for txout in txouts: + cmd.extend(["--tx-out", txout]) + + cmd.extend(["--out-file", str(out_file)]) + cmd.extend(list(extra_args)) + + LOGGER.debug("Running compatible Alonzo transaction build-raw: %s", " ".join(cmd)) + + self._clusterlib_obj.cli(cmd) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}: clusterlib_obj={id(self._clusterlib_obj)}>" From ddf4102a4b75faf5e8d428f706da2e2311d60e9d Mon Sep 17 00:00:00 2001 From: femi Date: Mon, 17 Nov 2025 11:37:04 +0000 Subject: [PATCH 03/28] Compatible group... Include group. --- .../compatible_alonzo_group.py | 14 ++++- .../compatible_alonzo_transaction_group.py | 57 ++++++++----------- .../compatilbe_group/compatible_group.py | 1 + 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/cardano_clusterlib/compatilbe_group/compatible_alonzo_group.py b/cardano_clusterlib/compatilbe_group/compatible_alonzo_group.py index 6ff8d42..f26d46d 100644 --- a/cardano_clusterlib/compatilbe_group/compatible_alonzo_group.py +++ b/cardano_clusterlib/compatilbe_group/compatible_alonzo_group.py @@ -2,6 +2,7 @@ import logging +from cardano_clusterlib import types as itp from cardano_clusterlib.compatilbe_group import compatible_alonzo_transaction_group LOGGER = logging.getLogger(__name__) @@ -11,10 +12,15 @@ class CompatibleAlonzoGroup: def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: self._clusterlib_obj = clusterlib_obj + self._group_args = ("compatible", "alonzo") + # Groups of commands within this era self._transaction_group: ( compatible_alonzo_transaction_group.CompatibleAlonzoTransactionGroup | None ) = None + # self._governance_group: CompatibleAlonzoGovernanceGroup | None = None + # self._stake_address_group: CompatibleAlonzoStakeAddressGroup | None = None + # self._stake_pool_group: CompatibleAlonzoStakePoolGroup | None = None @property def transaction(self) -> "compatible_alonzo_transaction_group.CompatibleAlonzoTransactionGroup": @@ -22,10 +28,14 @@ def transaction(self) -> "compatible_alonzo_transaction_group.CompatibleAlonzoTr if not self._transaction_group: self._transaction_group = ( compatible_alonzo_transaction_group.CompatibleAlonzoTransactionGroup( - clusterlib_obj=self._clusterlib_obj + clusterlib_obj=self._clusterlib_obj, + group_args=self._group_args, ) ) return self._transaction_group def __repr__(self) -> str: - return f"<{self.__class__.__name__}: era=alonzo clusterlib_obj={id(self._clusterlib_obj)}>" + return ( + f"<{self.__class__.__name__}: group_args={self._group_args} " + f"clusterlib_obj={id(self._clusterlib_obj)}>" + ) diff --git a/cardano_clusterlib/compatilbe_group/compatible_alonzo_transaction_group.py b/cardano_clusterlib/compatilbe_group/compatible_alonzo_transaction_group.py index 3aa7791..90a9e20 100644 --- a/cardano_clusterlib/compatilbe_group/compatible_alonzo_transaction_group.py +++ b/cardano_clusterlib/compatilbe_group/compatible_alonzo_transaction_group.py @@ -1,7 +1,6 @@ """Transaction commands for 'cardano-cli compatible alonzo transaction'.""" import logging -from typing import Sequence from cardano_clusterlib import types as itp @@ -9,44 +8,38 @@ class CompatibleAlonzoTransactionGroup: - def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: + def __init__(self, clusterlib_obj: "itp.ClusterLib", group_args: tuple[str, str]) -> None: + """Group for 'compatible alonzo transaction' commands. + + Args: + clusterlib_obj: Main ClusterLib instance. + group_args: Fixed CLI prefix, e.g. ("compatible", "alonzo"). + """ self._clusterlib_obj = clusterlib_obj + # ("compatible", "alonzo") + self._group_args = group_args - def build_raw( + def signed_transaction( self, - txins: Sequence[str], - txouts: Sequence[str], - out_file: itp.FileType, - extra_args: Sequence[str] | None = None, + cli_args: itp.UnpackableSequence, ) -> None: - """Simple wrapper for `cardano-cli compatible alonzo transaction build-raw`. - """ - if not txins: - msg = "`txins` must not be empty for compatible transaction build-raw." - raise ValueError(msg) - - extra_args = extra_args or () - - cmd: list[str] = [ - "cardano-cli", - "compatible", - "alonzo", + """Low-level wrapper for `cardano-cli compatible alonzo transaction signed-transaction`.""" + full_args: list[str] = [ + *self._group_args, "transaction", - "build-raw", + "signed-transaction", + *cli_args, ] - for txin in txins: - cmd.extend(["--tx-in", txin]) - - for txout in txouts: - cmd.extend(["--tx-out", txout]) - - cmd.extend(["--out-file", str(out_file)]) - cmd.extend(list(extra_args)) - - LOGGER.debug("Running compatible Alonzo transaction build-raw: %s", " ".join(cmd)) + LOGGER.debug( + "Running compatible Alonzo signed-transaction: %s", + " ".join(str(a) for a in full_args), + ) - self._clusterlib_obj.cli(cmd) + self._clusterlib_obj.cli(full_args) def __repr__(self) -> str: - return f"<{self.__class__.__name__}: clusterlib_obj={id(self._clusterlib_obj)}>" + return ( + f"<{self.__class__.__name__}: group_args={self._group_args} " + f"clusterlib_obj={id(self._clusterlib_obj)}>" + ) diff --git a/cardano_clusterlib/compatilbe_group/compatible_group.py b/cardano_clusterlib/compatilbe_group/compatible_group.py index 2c6b80f..9be61de 100644 --- a/cardano_clusterlib/compatilbe_group/compatible_group.py +++ b/cardano_clusterlib/compatilbe_group/compatible_group.py @@ -2,6 +2,7 @@ import logging +from cardano_clusterlib import types as itp from cardano_clusterlib.compatilbe_group import compatible_alonzo_group LOGGER = logging.getLogger(__name__) From cfce55b0983295d56c588ebe9a8b923eab455ce7 Mon Sep 17 00:00:00 2001 From: femi Date: Wed, 19 Nov 2025 11:59:59 +0000 Subject: [PATCH 04/28] Compatible group... Include group. --- cardano_clusterlib/compat_alonzo_group.py | 126 ++++++++++++++++++ .../compatible_group.py => compat_group.py} | 9 +- .../compatible_alonzo_group.py | 41 ------ .../compatible_alonzo_transaction_group.py | 45 ------- 4 files changed, 130 insertions(+), 91 deletions(-) create mode 100644 cardano_clusterlib/compat_alonzo_group.py rename cardano_clusterlib/{compatilbe_group/compatible_group.py => compat_group.py} (69%) delete mode 100644 cardano_clusterlib/compatilbe_group/compatible_alonzo_group.py delete mode 100644 cardano_clusterlib/compatilbe_group/compatible_alonzo_transaction_group.py diff --git a/cardano_clusterlib/compat_alonzo_group.py b/cardano_clusterlib/compat_alonzo_group.py new file mode 100644 index 0000000..2c56714 --- /dev/null +++ b/cardano_clusterlib/compat_alonzo_group.py @@ -0,0 +1,126 @@ +"""Alonzo era compatible commands for `cardano-cli compatible alonzo`.""" + +import logging +from typing import TYPE_CHECKING + +from cardano_clusterlib import types as itp + +if TYPE_CHECKING: + from cardano_clusterlib.clusterlib_klass import ClusterLib + +LOGGER = logging.getLogger(__name__) + + +class CompatibleAlonzoGroup: + """ + A Single container for ALL Alonzo compatible commands. + + """ + + def __init__(self, clusterlib_obj: "ClusterLib") -> None: + self._clusterlib_obj = clusterlib_obj + self._base_args = ("compatible", "alonzo") + + self.stake_address = _StakeAddressGroup(clusterlib_obj, self._base_args) + self.stake_pool = _StakePoolGroup(clusterlib_obj, self._base_args) + self.governance = _GovernanceGroup(clusterlib_obj, self._base_args) + self.transaction = _TransactionGroup(clusterlib_obj, self._base_args) + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__}: base_args={self._base_args} " + f"clusterlib_obj={id(self._clusterlib_obj)}>" + ) + + +class _StakeAddressGroup: + """`cardano-cli compatible alonzo stake-address` commands.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + self._group_args = (*base_args, "stake-address") + + def run_raw(self, cli_args: itp.UnpackableSequence) -> None: + """Generic low-level wrapper for stake-address commands.""" + full_args = [*self._group_args, *cli_args] + + LOGGER.debug("Running compatible alonzo stake-address: %s", " ".join(full_args)) + self._clusterlib_obj.cli(full_args) + + def __repr__(self) -> str: + return f"" + + +class _StakePoolGroup: + """`cardano-cli compatible alonzo stake-pool` commands.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + self._group_args = (*base_args, "stake-pool") + + def run_raw(self, cli_args: itp.UnpackableSequence) -> None: + """Generic low-level wrapper.""" + full_args = [*self._group_args, *cli_args] + + LOGGER.debug("Running compatible alonzo stake-pool: %s", " ".join(full_args)) + self._clusterlib_obj.cli(full_args) + + def __repr__(self) -> str: + return f"" + + +class _GovernanceGroup: + """`cardano-cli compatible alonzo governance` commands.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + self._group_args = (*base_args, "governance") + + def run_raw(self, cli_args: itp.UnpackableSequence) -> None: + """Low-level wrapper.""" + full_args = [*self._group_args, *cli_args] + + LOGGER.debug("Running compatible alonzo governance: %s", " ".join(full_args)) + self._clusterlib_obj.cli(full_args) + + def __repr__(self) -> str: + return f"" + + +class _TransactionGroup: + """Transaction commands for `cardano-cli compatible alonzo transaction`.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + """Group for 'compatible alonzo transaction' commands. + + Args: + clusterlib_obj: Main ClusterLib instance. + group_args: Fixed CLI prefix, e.g. ("compatible", "alonzo"). + """ + self._clusterlib_obj = clusterlib_obj + self._group_args = (*base_args, "transaction") # corrected: append “transaction” here + + def signed_transaction( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Low-level wrapper for `cardano-cli compatible alonzo transaction signed-transaction`.""" + full_args: list[str] = [ + *self._group_args, + "transaction", + "signed-transaction", + *cli_args, + ] + + LOGGER.debug( + "Running compatible Alonzo signed-transaction: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args) + + def __repr__(self) -> str: + return ( + f"" + ) diff --git a/cardano_clusterlib/compatilbe_group/compatible_group.py b/cardano_clusterlib/compat_group.py similarity index 69% rename from cardano_clusterlib/compatilbe_group/compatible_group.py rename to cardano_clusterlib/compat_group.py index 9be61de..96f1cdd 100644 --- a/cardano_clusterlib/compatilbe_group/compatible_group.py +++ b/cardano_clusterlib/compat_group.py @@ -2,8 +2,8 @@ import logging +from cardano_clusterlib import compat_alonzo_group from cardano_clusterlib import types as itp -from cardano_clusterlib.compatilbe_group import compatible_alonzo_group LOGGER = logging.getLogger(__name__) @@ -13,16 +13,15 @@ def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: self._clusterlib_obj = clusterlib_obj # Groups of commands per era - self._alonzo_group: compatible_alonzo_group.CompatibleAlonzoGroup | None = None + self._alonzo_group: compat_alonzo_group.CompatibleAlonzoGroup | None = None # self._mary_group: compatible_mary_group.CompatibleMaryGroup | None = None # self._shelley_group: compatible_shelley_group.CompatibleShelleyGroup | None = None # ... @property - def alonzo(self) -> "compatible_alonzo_group.CompatibleAlonzoGroup": - """Alonzo compatible era group.""" + def alonzo(self) -> compat_alonzo_group.CompatibleAlonzoGroup: if not self._alonzo_group: - self._alonzo_group = compatible_alonzo_group.CompatibleAlonzoGroup( + self._alonzo_group = compat_alonzo_group.CompatibleAlonzoGroup( clusterlib_obj=self._clusterlib_obj ) return self._alonzo_group diff --git a/cardano_clusterlib/compatilbe_group/compatible_alonzo_group.py b/cardano_clusterlib/compatilbe_group/compatible_alonzo_group.py deleted file mode 100644 index f26d46d..0000000 --- a/cardano_clusterlib/compatilbe_group/compatible_alonzo_group.py +++ /dev/null @@ -1,41 +0,0 @@ -"""Group of subgroups for 'compatible alonzo' commands.""" - -import logging - -from cardano_clusterlib import types as itp -from cardano_clusterlib.compatilbe_group import compatible_alonzo_transaction_group - -LOGGER = logging.getLogger(__name__) - - -class CompatibleAlonzoGroup: - def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: - self._clusterlib_obj = clusterlib_obj - - self._group_args = ("compatible", "alonzo") - - # Groups of commands within this era - self._transaction_group: ( - compatible_alonzo_transaction_group.CompatibleAlonzoTransactionGroup | None - ) = None - # self._governance_group: CompatibleAlonzoGovernanceGroup | None = None - # self._stake_address_group: CompatibleAlonzoStakeAddressGroup | None = None - # self._stake_pool_group: CompatibleAlonzoStakePoolGroup | None = None - - @property - def transaction(self) -> "compatible_alonzo_transaction_group.CompatibleAlonzoTransactionGroup": - """Transaction group for Alonzo compatible commands.""" - if not self._transaction_group: - self._transaction_group = ( - compatible_alonzo_transaction_group.CompatibleAlonzoTransactionGroup( - clusterlib_obj=self._clusterlib_obj, - group_args=self._group_args, - ) - ) - return self._transaction_group - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__}: group_args={self._group_args} " - f"clusterlib_obj={id(self._clusterlib_obj)}>" - ) diff --git a/cardano_clusterlib/compatilbe_group/compatible_alonzo_transaction_group.py b/cardano_clusterlib/compatilbe_group/compatible_alonzo_transaction_group.py deleted file mode 100644 index 90a9e20..0000000 --- a/cardano_clusterlib/compatilbe_group/compatible_alonzo_transaction_group.py +++ /dev/null @@ -1,45 +0,0 @@ -"""Transaction commands for 'cardano-cli compatible alonzo transaction'.""" - -import logging - -from cardano_clusterlib import types as itp - -LOGGER = logging.getLogger(__name__) - - -class CompatibleAlonzoTransactionGroup: - def __init__(self, clusterlib_obj: "itp.ClusterLib", group_args: tuple[str, str]) -> None: - """Group for 'compatible alonzo transaction' commands. - - Args: - clusterlib_obj: Main ClusterLib instance. - group_args: Fixed CLI prefix, e.g. ("compatible", "alonzo"). - """ - self._clusterlib_obj = clusterlib_obj - # ("compatible", "alonzo") - self._group_args = group_args - - def signed_transaction( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Low-level wrapper for `cardano-cli compatible alonzo transaction signed-transaction`.""" - full_args: list[str] = [ - *self._group_args, - "transaction", - "signed-transaction", - *cli_args, - ] - - LOGGER.debug( - "Running compatible Alonzo signed-transaction: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args) - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__}: group_args={self._group_args} " - f"clusterlib_obj={id(self._clusterlib_obj)}>" - ) From 0eb22e41dd4e2942a5113547735a7c6167913d27 Mon Sep 17 00:00:00 2001 From: femi Date: Wed, 19 Nov 2025 12:02:10 +0000 Subject: [PATCH 05/28] Compatible group... Include group. --- cardano_clusterlib/compat_alonzo_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cardano_clusterlib/compat_alonzo_group.py b/cardano_clusterlib/compat_alonzo_group.py index 2c56714..0ff3680 100644 --- a/cardano_clusterlib/compat_alonzo_group.py +++ b/cardano_clusterlib/compat_alonzo_group.py @@ -95,7 +95,7 @@ def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> Args: clusterlib_obj: Main ClusterLib instance. - group_args: Fixed CLI prefix, e.g. ("compatible", "alonzo"). + base_args: Fixed CLI prefix, e.g. ("compatible", "alonzo"). """ self._clusterlib_obj = clusterlib_obj self._group_args = (*base_args, "transaction") # corrected: append “transaction” here From 3a60c0582c6dfce9749257dec137f44d674518f3 Mon Sep 17 00:00:00 2001 From: femi Date: Wed, 19 Nov 2025 12:02:29 +0000 Subject: [PATCH 06/28] Compatible group... Include group. --- cardano_clusterlib/compat_alonzo_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cardano_clusterlib/compat_alonzo_group.py b/cardano_clusterlib/compat_alonzo_group.py index 0ff3680..0ceaf0b 100644 --- a/cardano_clusterlib/compat_alonzo_group.py +++ b/cardano_clusterlib/compat_alonzo_group.py @@ -98,7 +98,7 @@ def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> base_args: Fixed CLI prefix, e.g. ("compatible", "alonzo"). """ self._clusterlib_obj = clusterlib_obj - self._group_args = (*base_args, "transaction") # corrected: append “transaction” here + self._group_args = (*base_args, "transaction") def signed_transaction( self, From 205a26ab6d4fff8264384008c850ff6ec5cde7ba Mon Sep 17 00:00:00 2001 From: femi Date: Wed, 19 Nov 2025 12:03:51 +0000 Subject: [PATCH 07/28] Compatible group... Include group. --- cardano_clusterlib/compat_alonzo_group.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cardano_clusterlib/compat_alonzo_group.py b/cardano_clusterlib/compat_alonzo_group.py index 0ceaf0b..b147975 100644 --- a/cardano_clusterlib/compat_alonzo_group.py +++ b/cardano_clusterlib/compat_alonzo_group.py @@ -100,6 +100,8 @@ def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> self._clusterlib_obj = clusterlib_obj self._group_args = (*base_args, "transaction") + #We have only sign-transaction command for now + def signed_transaction( self, cli_args: itp.UnpackableSequence, From 474c7ab674c7fbdeec0d0d3f431124b0f1f8695c Mon Sep 17 00:00:00 2001 From: femi Date: Thu, 20 Nov 2025 11:52:24 +0000 Subject: [PATCH 08/28] Compatible group... Include group. --- cardano_clusterlib/compat_alonzo_group.py | 39 +++++++++++++++-------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/cardano_clusterlib/compat_alonzo_group.py b/cardano_clusterlib/compat_alonzo_group.py index b147975..09f98ce 100644 --- a/cardano_clusterlib/compat_alonzo_group.py +++ b/cardano_clusterlib/compat_alonzo_group.py @@ -21,10 +21,10 @@ def __init__(self, clusterlib_obj: "ClusterLib") -> None: self._clusterlib_obj = clusterlib_obj self._base_args = ("compatible", "alonzo") - self.stake_address = _StakeAddressGroup(clusterlib_obj, self._base_args) - self.stake_pool = _StakePoolGroup(clusterlib_obj, self._base_args) - self.governance = _GovernanceGroup(clusterlib_obj, self._base_args) - self.transaction = _TransactionGroup(clusterlib_obj, self._base_args) + self.stake_address = StakeAddressGroup(clusterlib_obj, self._base_args) + self.stake_pool = StakePoolGroup(clusterlib_obj, self._base_args) + self.governance = GovernanceGroup(clusterlib_obj, self._base_args) + self.transaction = TransactionGroup(clusterlib_obj, self._base_args) def __repr__(self) -> str: return ( @@ -33,25 +33,38 @@ def __repr__(self) -> str: ) -class _StakeAddressGroup: +class StakeAddressGroup: """`cardano-cli compatible alonzo stake-address` commands.""" def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: self._clusterlib_obj = clusterlib_obj self._group_args = (*base_args, "stake-address") - def run_raw(self, cli_args: itp.UnpackableSequence) -> None: - """Generic low-level wrapper for stake-address commands.""" - full_args = [*self._group_args, *cli_args] + def registration_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrapper for: + cardano-cli compatible alonzo stake-address registration-certificate + """ + full_args = [ + *self._group_args, + "registration-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible alonzo stake-address registration-certificate: %s", + " ".join(str(a) for a in full_args), + ) - LOGGER.debug("Running compatible alonzo stake-address: %s", " ".join(full_args)) self._clusterlib_obj.cli(full_args) def __repr__(self) -> str: return f"" -class _StakePoolGroup: +class StakePoolGroup: """`cardano-cli compatible alonzo stake-pool` commands.""" def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: @@ -69,7 +82,7 @@ def __repr__(self) -> str: return f"" -class _GovernanceGroup: +class GovernanceGroup: """`cardano-cli compatible alonzo governance` commands.""" def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: @@ -87,7 +100,7 @@ def __repr__(self) -> str: return f"" -class _TransactionGroup: +class TransactionGroup: """Transaction commands for `cardano-cli compatible alonzo transaction`.""" def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: @@ -95,7 +108,7 @@ def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> Args: clusterlib_obj: Main ClusterLib instance. - base_args: Fixed CLI prefix, e.g. ("compatible", "alonzo"). + base_args: Fixed CLI prefix """ self._clusterlib_obj = clusterlib_obj self._group_args = (*base_args, "transaction") From 506a25a93add5a0bfcf9fab2c1af5747caca0fc1 Mon Sep 17 00:00:00 2001 From: femi Date: Mon, 1 Dec 2025 12:20:11 +0000 Subject: [PATCH 09/28] Compatible group... Include group. --- cardano_clusterlib/compat_alonzo_group.py | 32 ++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/cardano_clusterlib/compat_alonzo_group.py b/cardano_clusterlib/compat_alonzo_group.py index 09f98ce..8824fa5 100644 --- a/cardano_clusterlib/compat_alonzo_group.py +++ b/cardano_clusterlib/compat_alonzo_group.py @@ -38,17 +38,17 @@ class StakeAddressGroup: def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: self._clusterlib_obj = clusterlib_obj - self._group_args = (*base_args, "stake-address") + self._cli_args = ("cardano-cli", *base_args, "stake-address") def registration_certificate( - self, - cli_args: itp.UnpackableSequence, + self, + cli_args: itp.UnpackableSequence, ) -> None: """Wrapper for: cardano-cli compatible alonzo stake-address registration-certificate """ full_args = [ - *self._group_args, + *self._cli_args, "registration-certificate", *cli_args, ] @@ -58,10 +58,30 @@ def registration_certificate( " ".join(str(a) for a in full_args), ) - self._clusterlib_obj.cli(full_args) + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def stake_delegation_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrapper for: + cardano-cli compatible alonzo stake-address stake-delegation-certificate + """ + full_args = [ + *self._cli_args, + "stake-delegation-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible alonzo stake-address stake-delegation-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) def __repr__(self) -> str: - return f"" + return f"<{self.__class__.__name__} cli_args={self._cli_args}>" class StakePoolGroup: From e3a2ab5246f8499901dbf5ec397c43c06bab60b1 Mon Sep 17 00:00:00 2001 From: femi Date: Tue, 2 Dec 2025 08:56:50 +0000 Subject: [PATCH 10/28] Compatible group... Include group. --- cardano_clusterlib/clusterlib_klass.py | 8 + cardano_clusterlib/compat_alonzo_group.py | 106 +++++++----- cardano_clusterlib/compat_babbage_group.py | 187 ++++++++++++++++++++ cardano_clusterlib/compat_group.py | 33 +++- cardano_clusterlib/compat_mary_group.py | 189 +++++++++++++++++++++ cardano_clusterlib/compat_shelley_group.py | 189 +++++++++++++++++++++ 6 files changed, 669 insertions(+), 43 deletions(-) create mode 100644 cardano_clusterlib/compat_babbage_group.py create mode 100644 cardano_clusterlib/compat_mary_group.py create mode 100644 cardano_clusterlib/compat_shelley_group.py diff --git a/cardano_clusterlib/clusterlib_klass.py b/cardano_clusterlib/clusterlib_klass.py index 9e954de..e892b7e 100644 --- a/cardano_clusterlib/clusterlib_klass.py +++ b/cardano_clusterlib/clusterlib_klass.py @@ -10,6 +10,7 @@ from cardano_clusterlib import address_group from cardano_clusterlib import clusterlib_helpers +from cardano_clusterlib import compat_group from cardano_clusterlib import consts from cardano_clusterlib import exceptions from cardano_clusterlib import genesis_group @@ -125,6 +126,7 @@ def __init__( self._genesis_group: genesis_group.GenesisGroup | None = None self._legacy_gov_group: legacy_gov_group.LegacyGovGroup | None = None self._governance_group: gov_group.GovernanceGroup | None = None + self._compatible_group: compat_group.CompatibleGroup | None = None def set_socket_path(self, socket_path: itp.FileType | None) -> None: """Set a path to socket file for communication with the node.""" @@ -235,6 +237,12 @@ def g_governance(self) -> gov_group.GovernanceGroup: self._governance_group = gov_group.GovernanceGroup(clusterlib_obj=self) return self._governance_group + @property + def g_compatible(self) -> compat_group.CompatibleGroup: + if not self._compatible_group: + self._compatible_group = compat_group.CompatibleGroup(clusterlib_obj=self) + return self._compatible_group + def cli( self, cli_args: list[str], diff --git a/cardano_clusterlib/compat_alonzo_group.py b/cardano_clusterlib/compat_alonzo_group.py index 8824fa5..f61f45a 100644 --- a/cardano_clusterlib/compat_alonzo_group.py +++ b/cardano_clusterlib/compat_alonzo_group.py @@ -12,10 +12,7 @@ class CompatibleAlonzoGroup: - """ - A Single container for ALL Alonzo compatible commands. - - """ + """A single container for all Alonzo compatible commands.""" def __init__(self, clusterlib_obj: "ClusterLib") -> None: self._clusterlib_obj = clusterlib_obj @@ -44,9 +41,7 @@ def registration_certificate( self, cli_args: itp.UnpackableSequence, ) -> None: - """Wrapper for: - cardano-cli compatible alonzo stake-address registration-certificate - """ + """Wrap the `stake-address registration-certificate` command.""" full_args = [ *self._cli_args, "registration-certificate", @@ -64,9 +59,7 @@ def stake_delegation_certificate( self, cli_args: itp.UnpackableSequence, ) -> None: - """Wrapper for: - cardano-cli compatible alonzo stake-address stake-delegation-certificate - """ + """Wrap the `stake-address stake-delegation-certificate` command.""" full_args = [ *self._cli_args, "stake-delegation-certificate", @@ -89,17 +82,28 @@ class StakePoolGroup: def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: self._clusterlib_obj = clusterlib_obj - self._group_args = (*base_args, "stake-pool") + self._cli_args = ("cardano-cli", *base_args, "stake-pool") + + def registration_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `stake-pool registration-certificate` command.""" + full_args = [ + *self._cli_args, + "registration-certificate", + *cli_args, + ] - def run_raw(self, cli_args: itp.UnpackableSequence) -> None: - """Generic low-level wrapper.""" - full_args = [*self._group_args, *cli_args] + LOGGER.debug( + "Running compatible alonzo stake-pool registration-certificate: %s", + " ".join(str(a) for a in full_args), + ) - LOGGER.debug("Running compatible alonzo stake-pool: %s", " ".join(full_args)) - self._clusterlib_obj.cli(full_args) + self._clusterlib_obj.cli(full_args, add_default_args=False) def __repr__(self) -> str: - return f"" + return f"<{self.__class__.__name__} cli_args={self._cli_args}>" class GovernanceGroup: @@ -107,55 +111,77 @@ class GovernanceGroup: def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: self._clusterlib_obj = clusterlib_obj - self._group_args = (*base_args, "governance") + # Full fixed prefix: cardano-cli compatible alonzo governance + self._cli_args = ("cardano-cli", *base_args, "governance") + + def create_mir_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `governance create-mir-certificate` command.""" + full_args = [ + *self._cli_args, + "create-mir-certificate", + *cli_args, + ] - def run_raw(self, cli_args: itp.UnpackableSequence) -> None: - """Low-level wrapper.""" - full_args = [*self._group_args, *cli_args] + LOGGER.debug( + "Running compatible alonzo governance create-mir-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def create_genesis_key_delegation_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `governance create-genesis-key-delegation-certificate` command.""" + full_args = [ + *self._cli_args, + "create-genesis-key-delegation-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible alonzo governance create-genesis-key-delegation-certificate: %s", + " ".join(str(a) for a in full_args), + ) - LOGGER.debug("Running compatible alonzo governance: %s", " ".join(full_args)) - self._clusterlib_obj.cli(full_args) + self._clusterlib_obj.cli(full_args, add_default_args=False) def __repr__(self) -> str: - return f"" + return f"<{self.__class__.__name__} cli_args={self._cli_args}>" class TransactionGroup: """Transaction commands for `cardano-cli compatible alonzo transaction`.""" def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - """Group for 'compatible alonzo transaction' commands. - - Args: - clusterlib_obj: Main ClusterLib instance. - base_args: Fixed CLI prefix - """ self._clusterlib_obj = clusterlib_obj - self._group_args = (*base_args, "transaction") - - #We have only sign-transaction command for now + # Full prefix: cardano-cli compatible alonzo transaction + self._cli_args = ("cardano-cli", *base_args, "transaction") def signed_transaction( self, cli_args: itp.UnpackableSequence, ) -> None: - """Low-level wrapper for `cardano-cli compatible alonzo transaction signed-transaction`.""" - full_args: list[str] = [ - *self._group_args, - "transaction", + """Wrap the `transaction signed-transaction` command.""" + full_args = [ + *self._cli_args, "signed-transaction", *cli_args, ] LOGGER.debug( - "Running compatible Alonzo signed-transaction: %s", + "Running compatible alonzo transaction signed-transaction: %s", " ".join(str(a) for a in full_args), ) - self._clusterlib_obj.cli(full_args) + self._clusterlib_obj.cli(full_args, add_default_args=False) def __repr__(self) -> str: return ( - f"" ) diff --git a/cardano_clusterlib/compat_babbage_group.py b/cardano_clusterlib/compat_babbage_group.py new file mode 100644 index 0000000..51499b3 --- /dev/null +++ b/cardano_clusterlib/compat_babbage_group.py @@ -0,0 +1,187 @@ +"""Babbage era compatible commands for `cardano-cli compatible babbage`.""" + +import logging +from typing import TYPE_CHECKING + +from cardano_clusterlib import types as itp + +if TYPE_CHECKING: + from cardano_clusterlib.clusterlib_klass import ClusterLib + +LOGGER = logging.getLogger(__name__) + + +class CompatibleBabbageGroup: + """Babbage era compatible group.""" + + def __init__(self, clusterlib_obj: "ClusterLib") -> None: + self._clusterlib_obj = clusterlib_obj + self._base_args = ("compatible", "babbage") + + self.stake_address = StakeAddressGroup(clusterlib_obj, self._base_args) + self.stake_pool = StakePoolGroup(clusterlib_obj, self._base_args) + self.governance = GovernanceGroup(clusterlib_obj, self._base_args) + self.transaction = TransactionGroup(clusterlib_obj, self._base_args) + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__}: base_args={self._base_args} " + f"clusterlib_obj={id(self._clusterlib_obj)}>" + ) + + +class StakeAddressGroup: + """`cardano-cli compatible babbage stake-address` commands.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + self._cli_args = ("cardano-cli", *base_args, "stake-address") + + def registration_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `stake-address registration-certificate` command.""" + full_args = [ + *self._cli_args, + "registration-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible babbage stake-address registration-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def stake_delegation_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `stake-address stake-delegation-certificate` command.""" + full_args = [ + *self._cli_args, + "stake-delegation-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible babbage stake-address stake-delegation-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} cli_args={self._cli_args}>" + + +class StakePoolGroup: + """`cardano-cli compatible babbage stake-pool` commands.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + self._cli_args = ("cardano-cli", *base_args, "stake-pool") + + def registration_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `stake-pool registration-certificate` command.""" + full_args = [ + *self._cli_args, + "registration-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible babbage stake-pool registration-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} cli_args={self._cli_args}>" + + +class GovernanceGroup: + """`cardano-cli compatible babbage governance` commands.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + # Full fixed prefix: cardano-cli compatible babbage governance + self._cli_args = ("cardano-cli", *base_args, "governance") + + def create_mir_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `governance create-mir-certificate` command.""" + full_args = [ + *self._cli_args, + "create-mir-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible babbage governance create-mir-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def create_genesis_key_delegation_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `governance create-genesis-key-delegation-certificate` command.""" + full_args = [ + *self._cli_args, + "create-genesis-key-delegation-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible babbage governance create-genesis-key-delegation-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} cli_args={self._cli_args}>" + + +class TransactionGroup: + """Transaction commands for `cardano-cli compatible babbage transaction`.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + # Full prefix: cardano-cli compatible babbage transaction + self._cli_args = ("cardano-cli", *base_args, "transaction") + + def signed_transaction( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `transaction signed-transaction` command.""" + full_args = [ + *self._cli_args, + "signed-transaction", + *cli_args, + ] + + LOGGER.debug( + "Running compatible babbage transaction signed-transaction: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} cli_args={self._cli_args} " + f"clusterlib_obj={id(self._clusterlib_obj)}>" + ) diff --git a/cardano_clusterlib/compat_group.py b/cardano_clusterlib/compat_group.py index 96f1cdd..063eed5 100644 --- a/cardano_clusterlib/compat_group.py +++ b/cardano_clusterlib/compat_group.py @@ -3,6 +3,9 @@ import logging from cardano_clusterlib import compat_alonzo_group +from cardano_clusterlib import compat_babbage_group +from cardano_clusterlib import compat_mary_group +from cardano_clusterlib import compat_shelley_group from cardano_clusterlib import types as itp LOGGER = logging.getLogger(__name__) @@ -14,9 +17,9 @@ def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: # Groups of commands per era self._alonzo_group: compat_alonzo_group.CompatibleAlonzoGroup | None = None - # self._mary_group: compatible_mary_group.CompatibleMaryGroup | None = None - # self._shelley_group: compatible_shelley_group.CompatibleShelleyGroup | None = None - # ... + self._babbage_group: compat_babbage_group.CompatibleBabbageGroup | None = None + self._mary_group: compat_mary_group.CompatibleMaryGroup | None = None + self._shelley_group: compat_shelley_group.CompatibleShelleyGroup | None = None @property def alonzo(self) -> compat_alonzo_group.CompatibleAlonzoGroup: @@ -26,5 +29,29 @@ def alonzo(self) -> compat_alonzo_group.CompatibleAlonzoGroup: ) return self._alonzo_group + @property + def babbage(self) -> compat_babbage_group.CompatibleBabbageGroup: + if not self._babbage_group: + self._babbage_group = compat_babbage_group.CompatibleBabbageGroup( + clusterlib_obj=self._clusterlib_obj + ) + return self._babbage_group + + @property + def mary(self) -> compat_mary_group.CompatibleMaryGroup: + if not self._mary_group: + self._mary_group = compat_mary_group.CompatibleMaryGroup( + clusterlib_obj=self._clusterlib_obj + ) + return self._mary_group + + @property + def shelley(self) -> compat_shelley_group.CompatibleShelleyGroup: + if not self._shelley_group: + self._shelley_group = compat_shelley_group.CompatibleShelleyGroup( + clusterlib_obj=self._clusterlib_obj + ) + return self._shelley_group + def __repr__(self) -> str: return f"<{self.__class__.__name__}: clusterlib_obj={id(self._clusterlib_obj)}>" diff --git a/cardano_clusterlib/compat_mary_group.py b/cardano_clusterlib/compat_mary_group.py new file mode 100644 index 0000000..d8e0520 --- /dev/null +++ b/cardano_clusterlib/compat_mary_group.py @@ -0,0 +1,189 @@ +"""Mary era compatible commands for `cardano-cli compatible mary`.""" + +import logging +from typing import TYPE_CHECKING + +from cardano_clusterlib import types as itp + +if TYPE_CHECKING: + from cardano_clusterlib.clusterlib_klass import ClusterLib + +LOGGER = logging.getLogger(__name__) + + +class CompatibleMaryGroup: + """Mary era compatible group.""" + + def __init__(self, clusterlib_obj: "ClusterLib") -> None: + self._clusterlib_obj = clusterlib_obj + self._base_args = ("compatible", "mary") + + self.stake_address = StakeAddressGroup(clusterlib_obj, self._base_args) + self.stake_pool = StakePoolGroup(clusterlib_obj, self._base_args) + self.governance = GovernanceGroup(clusterlib_obj, self._base_args) + self.transaction = TransactionGroup(clusterlib_obj, self._base_args) + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__}: base_args={self._base_args} " + f"clusterlib_obj={id(self._clusterlib_obj)}>" + ) + + +class StakeAddressGroup: + """`cardano-cli compatible mary stake-address` commands.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + # cardano-cli compatible mary stake-address + self._cli_args = ("cardano-cli", *base_args, "stake-address") + + def registration_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `stake-address registration-certificate` command.""" + full_args = [ + *self._cli_args, + "registration-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible mary stake-address registration-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def stake_delegation_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `stake-address stake-delegation-certificate` command.""" + full_args = [ + *self._cli_args, + "stake-delegation-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible mary stake-address stake-delegation-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} cli_args={self._cli_args}>" + + +class StakePoolGroup: + """`cardano-cli compatible mary stake-pool` commands.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + # cardano-cli compatible mary stake-pool + self._cli_args = ("cardano-cli", *base_args, "stake-pool") + + def registration_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `stake-pool registration-certificate` command.""" + full_args = [ + *self._cli_args, + "registration-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible mary stake-pool registration-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} cli_args={self._cli_args}>" + + +class GovernanceGroup: + """`cardano-cli compatible mary governance` commands.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + # cardano-cli compatible mary governance + self._cli_args = ("cardano-cli", *base_args, "governance") + + def create_mir_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `governance create-mir-certificate` command.""" + full_args = [ + *self._cli_args, + "create-mir-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible mary governance create-mir-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def create_genesis_key_delegation_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `governance create-genesis-key-delegation-certificate` command.""" + full_args = [ + *self._cli_args, + "create-genesis-key-delegation-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible mary governance create-genesis-key-delegation-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} cli_args={self._cli_args}>" + + +class TransactionGroup: + """Transaction commands for `cardano-cli compatible mary transaction`.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + # cardano-cli compatible mary transaction + self._cli_args = ("cardano-cli", *base_args, "transaction") + + def signed_transaction( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `transaction signed-transaction` command.""" + full_args = [ + *self._cli_args, + "signed-transaction", + *cli_args, + ] + + LOGGER.debug( + "Running compatible mary transaction signed-transaction: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} cli_args={self._cli_args} " + f"clusterlib_obj={id(self._clusterlib_obj)}>" + ) diff --git a/cardano_clusterlib/compat_shelley_group.py b/cardano_clusterlib/compat_shelley_group.py new file mode 100644 index 0000000..832703c --- /dev/null +++ b/cardano_clusterlib/compat_shelley_group.py @@ -0,0 +1,189 @@ +"""Shelley era compatible commands for `cardano-cli compatible shelley`.""" + +import logging +from typing import TYPE_CHECKING + +from cardano_clusterlib import types as itp + +if TYPE_CHECKING: + from cardano_clusterlib.clusterlib_klass import ClusterLib + +LOGGER = logging.getLogger(__name__) + + +class CompatibleShelleyGroup: + """Shelley era compatible group.""" + + def __init__(self, clusterlib_obj: "ClusterLib") -> None: + self._clusterlib_obj = clusterlib_obj + self._base_args = ("compatible", "shelley") + + self.stake_address = StakeAddressGroup(clusterlib_obj, self._base_args) + self.stake_pool = StakePoolGroup(clusterlib_obj, self._base_args) + self.governance = GovernanceGroup(clusterlib_obj, self._base_args) + self.transaction = TransactionGroup(clusterlib_obj, self._base_args) + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__}: base_args={self._base_args} " + f"clusterlib_obj={id(self._clusterlib_obj)}>" + ) + + +class StakeAddressGroup: + """`cardano-cli compatible shelley stake-address` commands.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + # cardano-cli compatible shelley stake-address + self._cli_args = ("cardano-cli", *base_args, "stake-address") + + def registration_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `stake-address registration-certificate` command.""" + full_args = [ + *self._cli_args, + "registration-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible shelley stake-address registration-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def stake_delegation_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `stake-address stake-delegation-certificate` command.""" + full_args = [ + *self._cli_args, + "stake-delegation-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible shelley stake-address stake-delegation-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} cli_args={self._cli_args}>" + + +class StakePoolGroup: + """`cardano-cli compatible shelley stake-pool` commands.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + # cardano-cli compatible shelley stake-pool + self._cli_args = ("cardano-cli", *base_args, "stake-pool") + + def registration_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `stake-pool registration-certificate` command.""" + full_args = [ + *self._cli_args, + "registration-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible shelley stake-pool registration-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} cli_args={self._cli_args}>" + + +class GovernanceGroup: + """`cardano-cli compatible shelley governance` commands.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + # cardano-cli compatible shelley governance + self._cli_args = ("cardano-cli", *base_args, "governance") + + def create_mir_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `governance create-mir-certificate` command.""" + full_args = [ + *self._cli_args, + "create-mir-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible shelley governance create-mir-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def create_genesis_key_delegation_certificate( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `governance create-genesis-key-delegation-certificate` command.""" + full_args = [ + *self._cli_args, + "create-genesis-key-delegation-certificate", + *cli_args, + ] + + LOGGER.debug( + "Running compatible shelley governance create-genesis-key-delegation-certificate: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} cli_args={self._cli_args}>" + + +class TransactionGroup: + """Transaction commands for `cardano-cli compatible shelley transaction`.""" + + def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: + self._clusterlib_obj = clusterlib_obj + # cardano-cli compatible shelley transaction + self._cli_args = ("cardano-cli", *base_args, "transaction") + + def signed_transaction( + self, + cli_args: itp.UnpackableSequence, + ) -> None: + """Wrap the `transaction signed-transaction` command.""" + full_args = [ + *self._cli_args, + "signed-transaction", + *cli_args, + ] + + LOGGER.debug( + "Running compatible shelley transaction signed-transaction: %s", + " ".join(str(a) for a in full_args), + ) + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__} cli_args={self._cli_args} " + f"clusterlib_obj={id(self._clusterlib_obj)}>" + ) From 2b881f91a5f35018644309ea4ef8eaca63a2953c Mon Sep 17 00:00:00 2001 From: femi Date: Wed, 3 Dec 2025 11:55:48 +0000 Subject: [PATCH 11/28] Compatible group... Include group. --- cardano_clusterlib/compat_alonzo_group.py | 187 -------------------- cardano_clusterlib/compat_babbage_group.py | 187 -------------------- cardano_clusterlib/compat_common.py | 162 ++++++++++++++++++ cardano_clusterlib/compat_group.py | 99 +++++++---- cardano_clusterlib/compat_mary_group.py | 189 --------------------- cardano_clusterlib/compat_shelley_group.py | 189 --------------------- 6 files changed, 225 insertions(+), 788 deletions(-) delete mode 100644 cardano_clusterlib/compat_alonzo_group.py delete mode 100644 cardano_clusterlib/compat_babbage_group.py create mode 100644 cardano_clusterlib/compat_common.py delete mode 100644 cardano_clusterlib/compat_mary_group.py delete mode 100644 cardano_clusterlib/compat_shelley_group.py diff --git a/cardano_clusterlib/compat_alonzo_group.py b/cardano_clusterlib/compat_alonzo_group.py deleted file mode 100644 index f61f45a..0000000 --- a/cardano_clusterlib/compat_alonzo_group.py +++ /dev/null @@ -1,187 +0,0 @@ -"""Alonzo era compatible commands for `cardano-cli compatible alonzo`.""" - -import logging -from typing import TYPE_CHECKING - -from cardano_clusterlib import types as itp - -if TYPE_CHECKING: - from cardano_clusterlib.clusterlib_klass import ClusterLib - -LOGGER = logging.getLogger(__name__) - - -class CompatibleAlonzoGroup: - """A single container for all Alonzo compatible commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib") -> None: - self._clusterlib_obj = clusterlib_obj - self._base_args = ("compatible", "alonzo") - - self.stake_address = StakeAddressGroup(clusterlib_obj, self._base_args) - self.stake_pool = StakePoolGroup(clusterlib_obj, self._base_args) - self.governance = GovernanceGroup(clusterlib_obj, self._base_args) - self.transaction = TransactionGroup(clusterlib_obj, self._base_args) - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__}: base_args={self._base_args} " - f"clusterlib_obj={id(self._clusterlib_obj)}>" - ) - - -class StakeAddressGroup: - """`cardano-cli compatible alonzo stake-address` commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - self._cli_args = ("cardano-cli", *base_args, "stake-address") - - def registration_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `stake-address registration-certificate` command.""" - full_args = [ - *self._cli_args, - "registration-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible alonzo stake-address registration-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def stake_delegation_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `stake-address stake-delegation-certificate` command.""" - full_args = [ - *self._cli_args, - "stake-delegation-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible alonzo stake-address stake-delegation-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} cli_args={self._cli_args}>" - - -class StakePoolGroup: - """`cardano-cli compatible alonzo stake-pool` commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - self._cli_args = ("cardano-cli", *base_args, "stake-pool") - - def registration_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `stake-pool registration-certificate` command.""" - full_args = [ - *self._cli_args, - "registration-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible alonzo stake-pool registration-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} cli_args={self._cli_args}>" - - -class GovernanceGroup: - """`cardano-cli compatible alonzo governance` commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - # Full fixed prefix: cardano-cli compatible alonzo governance - self._cli_args = ("cardano-cli", *base_args, "governance") - - def create_mir_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `governance create-mir-certificate` command.""" - full_args = [ - *self._cli_args, - "create-mir-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible alonzo governance create-mir-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def create_genesis_key_delegation_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `governance create-genesis-key-delegation-certificate` command.""" - full_args = [ - *self._cli_args, - "create-genesis-key-delegation-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible alonzo governance create-genesis-key-delegation-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} cli_args={self._cli_args}>" - - -class TransactionGroup: - """Transaction commands for `cardano-cli compatible alonzo transaction`.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - # Full prefix: cardano-cli compatible alonzo transaction - self._cli_args = ("cardano-cli", *base_args, "transaction") - - def signed_transaction( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `transaction signed-transaction` command.""" - full_args = [ - *self._cli_args, - "signed-transaction", - *cli_args, - ] - - LOGGER.debug( - "Running compatible alonzo transaction signed-transaction: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__} cli_args={self._cli_args} " - f"clusterlib_obj={id(self._clusterlib_obj)}>" - ) diff --git a/cardano_clusterlib/compat_babbage_group.py b/cardano_clusterlib/compat_babbage_group.py deleted file mode 100644 index 51499b3..0000000 --- a/cardano_clusterlib/compat_babbage_group.py +++ /dev/null @@ -1,187 +0,0 @@ -"""Babbage era compatible commands for `cardano-cli compatible babbage`.""" - -import logging -from typing import TYPE_CHECKING - -from cardano_clusterlib import types as itp - -if TYPE_CHECKING: - from cardano_clusterlib.clusterlib_klass import ClusterLib - -LOGGER = logging.getLogger(__name__) - - -class CompatibleBabbageGroup: - """Babbage era compatible group.""" - - def __init__(self, clusterlib_obj: "ClusterLib") -> None: - self._clusterlib_obj = clusterlib_obj - self._base_args = ("compatible", "babbage") - - self.stake_address = StakeAddressGroup(clusterlib_obj, self._base_args) - self.stake_pool = StakePoolGroup(clusterlib_obj, self._base_args) - self.governance = GovernanceGroup(clusterlib_obj, self._base_args) - self.transaction = TransactionGroup(clusterlib_obj, self._base_args) - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__}: base_args={self._base_args} " - f"clusterlib_obj={id(self._clusterlib_obj)}>" - ) - - -class StakeAddressGroup: - """`cardano-cli compatible babbage stake-address` commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - self._cli_args = ("cardano-cli", *base_args, "stake-address") - - def registration_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `stake-address registration-certificate` command.""" - full_args = [ - *self._cli_args, - "registration-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible babbage stake-address registration-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def stake_delegation_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `stake-address stake-delegation-certificate` command.""" - full_args = [ - *self._cli_args, - "stake-delegation-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible babbage stake-address stake-delegation-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} cli_args={self._cli_args}>" - - -class StakePoolGroup: - """`cardano-cli compatible babbage stake-pool` commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - self._cli_args = ("cardano-cli", *base_args, "stake-pool") - - def registration_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `stake-pool registration-certificate` command.""" - full_args = [ - *self._cli_args, - "registration-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible babbage stake-pool registration-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} cli_args={self._cli_args}>" - - -class GovernanceGroup: - """`cardano-cli compatible babbage governance` commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - # Full fixed prefix: cardano-cli compatible babbage governance - self._cli_args = ("cardano-cli", *base_args, "governance") - - def create_mir_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `governance create-mir-certificate` command.""" - full_args = [ - *self._cli_args, - "create-mir-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible babbage governance create-mir-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def create_genesis_key_delegation_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `governance create-genesis-key-delegation-certificate` command.""" - full_args = [ - *self._cli_args, - "create-genesis-key-delegation-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible babbage governance create-genesis-key-delegation-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} cli_args={self._cli_args}>" - - -class TransactionGroup: - """Transaction commands for `cardano-cli compatible babbage transaction`.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - # Full prefix: cardano-cli compatible babbage transaction - self._cli_args = ("cardano-cli", *base_args, "transaction") - - def signed_transaction( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `transaction signed-transaction` command.""" - full_args = [ - *self._cli_args, - "signed-transaction", - *cli_args, - ] - - LOGGER.debug( - "Running compatible babbage transaction signed-transaction: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__} cli_args={self._cli_args} " - f"clusterlib_obj={id(self._clusterlib_obj)}>" - ) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py new file mode 100644 index 0000000..f99ba45 --- /dev/null +++ b/cardano_clusterlib/compat_common.py @@ -0,0 +1,162 @@ +"""Generic reusable classes for cardano-cli `compatible` commands.""" + +from typing import TYPE_CHECKING + +from cardano_clusterlib import types as itp + +if TYPE_CHECKING: + from cardano_clusterlib.clusterlib_klass import ClusterLib + + +class StakeAddressGroup: + """Generic stake-address group for all compatible eras.""" + + def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: + self._clusterlib_obj = clusterlib_obj + self._base = ("cardano-cli", "compatible", era, "stake-address") + + def _resolve_stake_key_args( + self, + *, + stake_vkey: str = "", + stake_vkey_file: itp.FileType | None = None, + stake_key_hash: str = "", + stake_script_file: itp.FileType | None = None, + stake_address: str | None = None, + ) -> list[str]: + """Resolve stake key CLI args for registration & delegation.""" + if stake_vkey: + return ["--stake-verification-key", stake_vkey] + if stake_vkey_file: + return ["--stake-verification-key-file", str(stake_vkey_file)] + if stake_key_hash: + return ["--stake-key-hash", stake_key_hash] + if stake_script_file: + return ["--stake-script-file", str(stake_script_file)] + if stake_address: + return ["--stake-address", stake_address] + msg = ( + "One of stake_vkey, stake_vkey_file, stake_key_hash, " + "stake_script_file or stake_address must be provided." + ) + raise ValueError(msg) + + def registration_certificate( + self, + *, + stake_vkey: str = "", + stake_vkey_file: itp.FileType | None = None, + stake_key_hash: str = "", + stake_script_file: itp.FileType | None = None, + stake_address: str | None = None, + out_file: itp.FileType, + ) -> None: + """Wrap the legacy stake-address registration-certificate command.""" + stake_args = self._resolve_stake_key_args( + stake_vkey=stake_vkey, + stake_vkey_file=stake_vkey_file, + stake_key_hash=stake_key_hash, + stake_script_file=stake_script_file, + stake_address=stake_address, + ) + + cmd = [ + *self._base, + "registration-certificate", + *stake_args, + "--out-file", + str(out_file), + ] + + self._clusterlib_obj.cli(cmd, add_default_args=False) + + def delegation_certificate( + self, + *, + stake_vkey: str = "", + stake_vkey_file: itp.FileType | None = None, + stake_key_hash: str = "", + stake_script_file: itp.FileType | None = None, + stake_address: str | None = None, + stake_pool_vkey: str = "", + cold_vkey_file: itp.FileType | None = None, + stake_pool_id: str = "", + out_file: itp.FileType, + ) -> None: + """Wrap the legacy stake-address stake-delegation-certificate command.""" + stake_args = self._resolve_stake_key_args( + stake_vkey=stake_vkey, + stake_vkey_file=stake_vkey_file, + stake_key_hash=stake_key_hash, + stake_script_file=stake_script_file, + stake_address=stake_address, + ) + + if stake_pool_vkey: + pool_args = ["--stake-pool-verification-key", stake_pool_vkey] + elif cold_vkey_file: + pool_args = ["--cold-verification-key-file", str(cold_vkey_file)] + elif stake_pool_id: + pool_args = ["--stake-pool-id", stake_pool_id] + + msg = "One of stake_pool_vkey, cold_vkey_file or stake_pool_id must be provided." + raise ValueError(msg) + + cmd = [ + *self._base, + "stake-delegation-certificate", + *stake_args, + *pool_args, + "--out-file", + str(out_file), + ] + + self._clusterlib_obj.cli(cmd, add_default_args=False) + + +class StakePoolGroup: + """Generic stake-pool group for all compatible eras.""" + + def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: + self._clusterlib_obj = clusterlib_obj + self._base = ("cardano-cli", "compatible", era, "stake-pool") + + def registration_certificate(self, cli_args: itp.UnpackableSequence) -> None: + """Wrap the `stake-pool registration-certificate` command.""" + full_args = [*self._base, "registration-certificate", *cli_args] + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + +class GovernanceGroup: + """Generic governance group for all compatible eras.""" + + def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: + self._clusterlib_obj = clusterlib_obj + self._base = ("cardano-cli", "compatible", era, "governance") + + def create_mir_certificate(self, cli_args: itp.UnpackableSequence) -> None: + """Wrap the `governance create-mir-certificate` command.""" + full_args = [*self._base, "create-mir-certificate", *cli_args] + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + def create_genesis_key_delegation_certificate(self, cli_args: itp.UnpackableSequence) -> None: + """Wrap the `governance create-genesis-key-delegation-certificate` command.""" + full_args = [*self._base, "create-genesis-key-delegation-certificate", *cli_args] + + self._clusterlib_obj.cli(full_args, add_default_args=False) + + +class TransactionGroup: + """Generic transaction group for all compatible eras.""" + + def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: + self._clusterlib_obj = clusterlib_obj + self._base = ("cardano-cli", "compatible", era, "transaction") + + def signed_transaction(self, cli_args: itp.UnpackableSequence) -> None: + """Wrap the `transaction signed-transaction` command.""" + full_args = [*self._base, "signed-transaction", *cli_args] + + self._clusterlib_obj.cli(full_args, add_default_args=False) diff --git a/cardano_clusterlib/compat_group.py b/cardano_clusterlib/compat_group.py index 063eed5..133276f 100644 --- a/cardano_clusterlib/compat_group.py +++ b/cardano_clusterlib/compat_group.py @@ -2,56 +2,83 @@ import logging -from cardano_clusterlib import compat_alonzo_group -from cardano_clusterlib import compat_babbage_group -from cardano_clusterlib import compat_mary_group -from cardano_clusterlib import compat_shelley_group +from cardano_clusterlib import compat_common from cardano_clusterlib import types as itp LOGGER = logging.getLogger(__name__) +class CompatibleAlonzoGroup: + """Wrapper exposing the generic compat groups for the Alonzo era.""" + + def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: + self.stake_address = compat_common.StakeAddressGroup(clusterlib_obj, "alonzo") + self.stake_pool = compat_common.StakePoolGroup(clusterlib_obj, "alonzo") + self.governance = compat_common.GovernanceGroup(clusterlib_obj, "alonzo") + self.transaction = compat_common.TransactionGroup(clusterlib_obj, "alonzo") + + +class CompatibleBabbageGroup: + """Wrapper exposing the generic compat groups for the Babbage era.""" + + def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: + self.stake_address = compat_common.StakeAddressGroup(clusterlib_obj, "babbage") + self.stake_pool = compat_common.StakePoolGroup(clusterlib_obj, "babbage") + self.governance = compat_common.GovernanceGroup(clusterlib_obj, "babbage") + self.transaction = compat_common.TransactionGroup(clusterlib_obj, "babbage") + + +class CompatibleMaryGroup: + """Wrapper exposing the generic compat groups for the Mary era.""" + + def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: + self.stake_address = compat_common.StakeAddressGroup(clusterlib_obj, "mary") + self.stake_pool = compat_common.StakePoolGroup(clusterlib_obj, "mary") + self.governance = compat_common.GovernanceGroup(clusterlib_obj, "mary") + self.transaction = compat_common.TransactionGroup(clusterlib_obj, "mary") + + +class CompatibleShelleyGroup: + """Wrapper exposing the generic compat groups for the Shelley era.""" + + def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: + self.stake_address = compat_common.StakeAddressGroup(clusterlib_obj, "shelley") + self.stake_pool = compat_common.StakePoolGroup(clusterlib_obj, "shelley") + self.governance = compat_common.GovernanceGroup(clusterlib_obj, "shelley") + self.transaction = compat_common.TransactionGroup(clusterlib_obj, "shelley") + + class CompatibleGroup: + """Top-level accessor for all compatible-era command groups.""" + def __init__(self, clusterlib_obj: "itp.ClusterLib") -> None: self._clusterlib_obj = clusterlib_obj - # Groups of commands per era - self._alonzo_group: compat_alonzo_group.CompatibleAlonzoGroup | None = None - self._babbage_group: compat_babbage_group.CompatibleBabbageGroup | None = None - self._mary_group: compat_mary_group.CompatibleMaryGroup | None = None - self._shelley_group: compat_shelley_group.CompatibleShelleyGroup | None = None + self._alonzo: CompatibleAlonzoGroup | None = None + self._babbage: CompatibleBabbageGroup | None = None + self._mary: CompatibleMaryGroup | None = None + self._shelley: CompatibleShelleyGroup | None = None @property - def alonzo(self) -> compat_alonzo_group.CompatibleAlonzoGroup: - if not self._alonzo_group: - self._alonzo_group = compat_alonzo_group.CompatibleAlonzoGroup( - clusterlib_obj=self._clusterlib_obj - ) - return self._alonzo_group + def alonzo(self) -> CompatibleAlonzoGroup: + if not self._alonzo: + self._alonzo = CompatibleAlonzoGroup(self._clusterlib_obj) + return self._alonzo @property - def babbage(self) -> compat_babbage_group.CompatibleBabbageGroup: - if not self._babbage_group: - self._babbage_group = compat_babbage_group.CompatibleBabbageGroup( - clusterlib_obj=self._clusterlib_obj - ) - return self._babbage_group + def babbage(self) -> CompatibleBabbageGroup: + if not self._babbage: + self._babbage = CompatibleBabbageGroup(self._clusterlib_obj) + return self._babbage @property - def mary(self) -> compat_mary_group.CompatibleMaryGroup: - if not self._mary_group: - self._mary_group = compat_mary_group.CompatibleMaryGroup( - clusterlib_obj=self._clusterlib_obj - ) - return self._mary_group + def mary(self) -> CompatibleMaryGroup: + if not self._mary: + self._mary = CompatibleMaryGroup(self._clusterlib_obj) + return self._mary @property - def shelley(self) -> compat_shelley_group.CompatibleShelleyGroup: - if not self._shelley_group: - self._shelley_group = compat_shelley_group.CompatibleShelleyGroup( - clusterlib_obj=self._clusterlib_obj - ) - return self._shelley_group - - def __repr__(self) -> str: - return f"<{self.__class__.__name__}: clusterlib_obj={id(self._clusterlib_obj)}>" + def shelley(self) -> CompatibleShelleyGroup: + if not self._shelley: + self._shelley = CompatibleShelleyGroup(self._clusterlib_obj) + return self._shelley diff --git a/cardano_clusterlib/compat_mary_group.py b/cardano_clusterlib/compat_mary_group.py deleted file mode 100644 index d8e0520..0000000 --- a/cardano_clusterlib/compat_mary_group.py +++ /dev/null @@ -1,189 +0,0 @@ -"""Mary era compatible commands for `cardano-cli compatible mary`.""" - -import logging -from typing import TYPE_CHECKING - -from cardano_clusterlib import types as itp - -if TYPE_CHECKING: - from cardano_clusterlib.clusterlib_klass import ClusterLib - -LOGGER = logging.getLogger(__name__) - - -class CompatibleMaryGroup: - """Mary era compatible group.""" - - def __init__(self, clusterlib_obj: "ClusterLib") -> None: - self._clusterlib_obj = clusterlib_obj - self._base_args = ("compatible", "mary") - - self.stake_address = StakeAddressGroup(clusterlib_obj, self._base_args) - self.stake_pool = StakePoolGroup(clusterlib_obj, self._base_args) - self.governance = GovernanceGroup(clusterlib_obj, self._base_args) - self.transaction = TransactionGroup(clusterlib_obj, self._base_args) - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__}: base_args={self._base_args} " - f"clusterlib_obj={id(self._clusterlib_obj)}>" - ) - - -class StakeAddressGroup: - """`cardano-cli compatible mary stake-address` commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - # cardano-cli compatible mary stake-address - self._cli_args = ("cardano-cli", *base_args, "stake-address") - - def registration_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `stake-address registration-certificate` command.""" - full_args = [ - *self._cli_args, - "registration-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible mary stake-address registration-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def stake_delegation_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `stake-address stake-delegation-certificate` command.""" - full_args = [ - *self._cli_args, - "stake-delegation-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible mary stake-address stake-delegation-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} cli_args={self._cli_args}>" - - -class StakePoolGroup: - """`cardano-cli compatible mary stake-pool` commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - # cardano-cli compatible mary stake-pool - self._cli_args = ("cardano-cli", *base_args, "stake-pool") - - def registration_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `stake-pool registration-certificate` command.""" - full_args = [ - *self._cli_args, - "registration-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible mary stake-pool registration-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} cli_args={self._cli_args}>" - - -class GovernanceGroup: - """`cardano-cli compatible mary governance` commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - # cardano-cli compatible mary governance - self._cli_args = ("cardano-cli", *base_args, "governance") - - def create_mir_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `governance create-mir-certificate` command.""" - full_args = [ - *self._cli_args, - "create-mir-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible mary governance create-mir-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def create_genesis_key_delegation_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `governance create-genesis-key-delegation-certificate` command.""" - full_args = [ - *self._cli_args, - "create-genesis-key-delegation-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible mary governance create-genesis-key-delegation-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} cli_args={self._cli_args}>" - - -class TransactionGroup: - """Transaction commands for `cardano-cli compatible mary transaction`.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - # cardano-cli compatible mary transaction - self._cli_args = ("cardano-cli", *base_args, "transaction") - - def signed_transaction( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `transaction signed-transaction` command.""" - full_args = [ - *self._cli_args, - "signed-transaction", - *cli_args, - ] - - LOGGER.debug( - "Running compatible mary transaction signed-transaction: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__} cli_args={self._cli_args} " - f"clusterlib_obj={id(self._clusterlib_obj)}>" - ) diff --git a/cardano_clusterlib/compat_shelley_group.py b/cardano_clusterlib/compat_shelley_group.py deleted file mode 100644 index 832703c..0000000 --- a/cardano_clusterlib/compat_shelley_group.py +++ /dev/null @@ -1,189 +0,0 @@ -"""Shelley era compatible commands for `cardano-cli compatible shelley`.""" - -import logging -from typing import TYPE_CHECKING - -from cardano_clusterlib import types as itp - -if TYPE_CHECKING: - from cardano_clusterlib.clusterlib_klass import ClusterLib - -LOGGER = logging.getLogger(__name__) - - -class CompatibleShelleyGroup: - """Shelley era compatible group.""" - - def __init__(self, clusterlib_obj: "ClusterLib") -> None: - self._clusterlib_obj = clusterlib_obj - self._base_args = ("compatible", "shelley") - - self.stake_address = StakeAddressGroup(clusterlib_obj, self._base_args) - self.stake_pool = StakePoolGroup(clusterlib_obj, self._base_args) - self.governance = GovernanceGroup(clusterlib_obj, self._base_args) - self.transaction = TransactionGroup(clusterlib_obj, self._base_args) - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__}: base_args={self._base_args} " - f"clusterlib_obj={id(self._clusterlib_obj)}>" - ) - - -class StakeAddressGroup: - """`cardano-cli compatible shelley stake-address` commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - # cardano-cli compatible shelley stake-address - self._cli_args = ("cardano-cli", *base_args, "stake-address") - - def registration_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `stake-address registration-certificate` command.""" - full_args = [ - *self._cli_args, - "registration-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible shelley stake-address registration-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def stake_delegation_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `stake-address stake-delegation-certificate` command.""" - full_args = [ - *self._cli_args, - "stake-delegation-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible shelley stake-address stake-delegation-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} cli_args={self._cli_args}>" - - -class StakePoolGroup: - """`cardano-cli compatible shelley stake-pool` commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - # cardano-cli compatible shelley stake-pool - self._cli_args = ("cardano-cli", *base_args, "stake-pool") - - def registration_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `stake-pool registration-certificate` command.""" - full_args = [ - *self._cli_args, - "registration-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible shelley stake-pool registration-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} cli_args={self._cli_args}>" - - -class GovernanceGroup: - """`cardano-cli compatible shelley governance` commands.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - # cardano-cli compatible shelley governance - self._cli_args = ("cardano-cli", *base_args, "governance") - - def create_mir_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `governance create-mir-certificate` command.""" - full_args = [ - *self._cli_args, - "create-mir-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible shelley governance create-mir-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def create_genesis_key_delegation_certificate( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `governance create-genesis-key-delegation-certificate` command.""" - full_args = [ - *self._cli_args, - "create-genesis-key-delegation-certificate", - *cli_args, - ] - - LOGGER.debug( - "Running compatible shelley governance create-genesis-key-delegation-certificate: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} cli_args={self._cli_args}>" - - -class TransactionGroup: - """Transaction commands for `cardano-cli compatible shelley transaction`.""" - - def __init__(self, clusterlib_obj: "ClusterLib", base_args: tuple[str, str]) -> None: - self._clusterlib_obj = clusterlib_obj - # cardano-cli compatible shelley transaction - self._cli_args = ("cardano-cli", *base_args, "transaction") - - def signed_transaction( - self, - cli_args: itp.UnpackableSequence, - ) -> None: - """Wrap the `transaction signed-transaction` command.""" - full_args = [ - *self._cli_args, - "signed-transaction", - *cli_args, - ] - - LOGGER.debug( - "Running compatible shelley transaction signed-transaction: %s", - " ".join(str(a) for a in full_args), - ) - - self._clusterlib_obj.cli(full_args, add_default_args=False) - - def __repr__(self) -> str: - return ( - f"<{self.__class__.__name__} cli_args={self._cli_args} " - f"clusterlib_obj={id(self._clusterlib_obj)}>" - ) From 7432d2db870b38af4893964f7335704be3daf8aa Mon Sep 17 00:00:00 2001 From: femi Date: Wed, 3 Dec 2025 12:00:19 +0000 Subject: [PATCH 12/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index f99ba45..1a7bbc4 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -98,9 +98,9 @@ def delegation_certificate( pool_args = ["--cold-verification-key-file", str(cold_vkey_file)] elif stake_pool_id: pool_args = ["--stake-pool-id", stake_pool_id] - - msg = "One of stake_pool_vkey, cold_vkey_file or stake_pool_id must be provided." - raise ValueError(msg) + else: + msg = "One of stake_pool_vkey, cold_vkey_file or stake_pool_id must be provided." + raise ValueError(msg) cmd = [ *self._base, From 40ce517f7e526654b4bdcc991861019c9b5c3183 Mon Sep 17 00:00:00 2001 From: femi Date: Wed, 3 Dec 2025 23:03:46 +0000 Subject: [PATCH 13/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 177 +++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 4 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 1a7bbc4..9a292ed 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -121,11 +121,180 @@ def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "stake-pool") - def registration_certificate(self, cli_args: itp.UnpackableSequence) -> None: - """Wrap the `stake-pool registration-certificate` command.""" - full_args = [*self._base, "registration-certificate", *cli_args] + def _resolve_pool_vkey_args( + self, + *, + stake_pool_vkey: str = "", + stake_pool_extended_vkey: str = "", + cold_vkey_file: itp.FileType | None = None, + ) -> list[str]: + """Resolve pool key identification.""" + if stake_pool_vkey: + return ["--stake-pool-verification-key", stake_pool_vkey] + if stake_pool_extended_vkey: + return ["--stake-pool-verification-extended-key", stake_pool_extended_vkey] + if cold_vkey_file: + return ["--cold-verification-key-file", str(cold_vkey_file)] - self._clusterlib_obj.cli(full_args, add_default_args=False) + msg = ( + "One of stake_pool_vkey, stake_pool_extended_vkey, or cold_vkey_file must be provided." + ) + raise ValueError(msg) + + def _resolve_vrf_args( + self, + *, + vrf_vkey: str = "", + vrf_vkey_file: itp.FileType | None = None, + ) -> list[str]: + """Resolve VRF key identification.""" + if vrf_vkey: + return ["--vrf-verification-key", vrf_vkey] + if vrf_vkey_file: + return ["--vrf-verification-key-file", str(vrf_vkey_file)] + + msg = "One of vrf_vkey or vrf_vkey_file must be provided." + raise ValueError(msg) + + def _resolve_reward_account_args( + self, + *, + reward_vkey: str = "", + reward_vkey_file: itp.FileType | None = None, + ) -> list[str]: + """Resolve reward account specification.""" + if reward_vkey: + return ["--pool-reward-account-verification-key", reward_vkey] + if reward_vkey_file: + return ["--pool-reward-account-verification-key-file", str(reward_vkey_file)] + + msg = "One of reward_vkey or reward_vkey_file must be provided." + raise ValueError(msg) + + def _resolve_owner_args( + self, + *, + owner_vkey: str = "", + owner_vkey_file: itp.FileType | None = None, + ) -> list[str]: + """Resolve owner stake key.""" + if owner_vkey: + return ["--pool-owner-verification-key", owner_vkey] + if owner_vkey_file: + return ["--pool-owner-stake-verification-key-file", str(owner_vkey_file)] + + msg = "One of owner_vkey or owner_vkey_file must be provided." + raise ValueError(msg) + + def registration_certificate( + self, + *, + # pool identification + stake_pool_vkey: str = "", + stake_pool_extended_vkey: str = "", + cold_vkey_file: itp.FileType | None = None, + # VRF identification + vrf_vkey: str = "", + vrf_vkey_file: itp.FileType | None = None, + # financials + pool_pledge: int, + pool_cost: int, + pool_margin: str, + # reward account + reward_vkey: str = "", + reward_vkey_file: itp.FileType | None = None, + # owner + owner_vkey: str = "", + owner_vkey_file: itp.FileType | None = None, + # relays + relay_ipv4: str = "", + relay_ipv6: str = "", + relay_port: int | None = None, + single_host_relay: str = "", + multi_host_relay: str = "", + # metadata + metadata_url: str = "", + metadata_hash: str = "", + check_metadata_hash: bool = False, + # output + out_file: itp.FileType, + ) -> None: + """Wrap the compat stake-pool registration-certificate command.""" + # Required argument groups + pool_args = self._resolve_pool_vkey_args( + stake_pool_vkey=stake_pool_vkey, + stake_pool_extended_vkey=stake_pool_extended_vkey, + cold_vkey_file=cold_vkey_file, + ) + + vrf_args = self._resolve_vrf_args( + vrf_vkey=vrf_vkey, + vrf_vkey_file=vrf_vkey_file, + ) + + reward_args = self._resolve_reward_account_args( + reward_vkey=reward_vkey, + reward_vkey_file=reward_vkey_file, + ) + + owner_args = self._resolve_owner_args( + owner_vkey=owner_vkey, + owner_vkey_file=owner_vkey_file, + ) + + # Relay handling + relay_args: list[str] = [] + + if relay_ipv4: + relay_args.extend(["--pool-relay-ipv4", relay_ipv4]) + if relay_ipv6: + relay_args.extend(["--pool-relay-ipv6", relay_ipv6]) + if relay_port: + relay_args.extend(["--pool-relay-port", str(relay_port)]) + + if single_host_relay: + relay_args.extend(["--single-host-pool-relay", single_host_relay]) + if relay_port: + relay_args.extend(["--pool-relay-port", str(relay_port)]) + + if multi_host_relay: + relay_args.extend(["--multi-host-pool-relay", multi_host_relay]) + + # Metadata arguments + metadata_args: list[str] = [] + if metadata_url and metadata_hash: + metadata_args.extend( + [ + "--metadata-url", + metadata_url, + "--metadata-hash", + metadata_hash, + ] + ) + if check_metadata_hash: + metadata_args.append("--check-metadata-hash") + + # Build final CLI cmd + cmd = [ + *self._base, + "registration-certificate", + *pool_args, + *vrf_args, + "--pool-pledge", + str(pool_pledge), + "--pool-cost", + str(pool_cost), + "--pool-margin", + str(pool_margin), + *reward_args, + *owner_args, + *relay_args, + *metadata_args, + "--out-file", + str(out_file), + ] + + self._clusterlib_obj.cli(cmd, add_default_args=False) class GovernanceGroup: From 1b12fdc216d3ca1092fa774b84c3b9b949c0eed4 Mon Sep 17 00:00:00 2001 From: femi Date: Thu, 4 Dec 2025 10:21:22 +0000 Subject: [PATCH 14/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 351 +++++++++++++++++++++++++++- 1 file changed, 344 insertions(+), 7 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 9a292ed..336bbd6 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -304,17 +304,354 @@ def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "governance") - def create_mir_certificate(self, cli_args: itp.UnpackableSequence) -> None: - """Wrap the `governance create-mir-certificate` command.""" - full_args = [*self._base, "create-mir-certificate", *cli_args] + def _resolve_mir_direct_args( + self, + *, + reserves: bool, + treasury: bool, + stake_address: str | None, + reward: int | None, + out_file: itp.FileType, + ) -> list[str] | None: + """Resolve direct MIR mode args (no subcommand). + + Direct mode syntax: + (--reserves | --treasury) + --stake-address ADDRESS + --reward LOVELACE + --out-file FILEPATH + """ + # No direct mode selected + if not reserves and not treasury: + return None + + # Invalid: both pots selected + if reserves and treasury: + msg = "Cannot specify both `reserves` and `treasury` in direct MIR mode." + raise ValueError(msg) - self._clusterlib_obj.cli(full_args, add_default_args=False) + if not stake_address: + msg = "`stake_address` is required in direct MIR mode." + raise ValueError(msg) + + if reward is None: + msg = "`reward` is required in direct MIR mode." + raise ValueError(msg) + + pot_flag = "--reserves" if reserves else "--treasury" + + args: list[str] = [ + pot_flag, + "--stake-address", + stake_address, + "--reward", + str(reward), + "--out-file", + str(out_file), + ] + return args + + def _mir_stake_addresses_args( + self, + *, + stake_address: str | None, + reward: int | None, + funds: str | None, + out_file: itp.FileType, + ) -> list[str]: + """Arguments for `create-mir-certificate stake-addresses`.""" + if not stake_address: + msg = "`stake_address` is required for 'stake-addresses' MIR subcommand." + raise ValueError(msg) + + if reward is None: + msg = "`reward` is required for 'stake-addresses' MIR subcommand." + raise ValueError(msg) + + if funds not in ("reserves", "treasury"): + msg = "`funds` must be either 'reserves' or 'treasury' for 'stake-addresses'." + raise ValueError(msg) + + args: list[str] = [ + "stake-addresses", + f"--{funds}", + "--stake-address", + stake_address, + "--reward", + str(reward), + "--out-file", + str(out_file), + ] + return args + + def _mir_transfer_to_treasury_args( + self, + *, + transfer_amt: int | None, + out_file: itp.FileType, + ) -> list[str]: + """Arguments for `create-mir-certificate transfer-to-treasury`.""" + if transfer_amt is None: + msg = "`transfer_amt` is required for 'transfer-to-treasury' MIR subcommand." + raise ValueError(msg) + + args: list[str] = [ + "transfer-to-treasury", + "--transfer", + str(transfer_amt), + "--out-file", + str(out_file), + ] + return args + + def _mir_transfer_to_rewards_args( + self, + *, + transfer_amt: int | None, + out_file: itp.FileType, + ) -> list[str]: + """Arguments for `create-mir-certificate transfer-to-rewards`.""" + if transfer_amt is None: + msg = "`transfer_amt` is required for 'transfer-to-rewards' MIR subcommand." + raise ValueError(msg) + + args: list[str] = [ + "transfer-to-rewards", + "--transfer", + str(transfer_amt), + "--out-file", + str(out_file), + ] + return args - def create_genesis_key_delegation_certificate(self, cli_args: itp.UnpackableSequence) -> None: + def _resolve_mir_subcommand_args( + self, + *, + subcommand: str | None, + stake_address: str | None, + reward: int | None, + transfer_amt: int | None, + funds: str | None, + out_file: itp.FileType, + ) -> list[str] | None: + """Resolve MIR subcommand arguments, if any. + + Supported subcommands: + * stake-addresses + * transfer-to-treasury + * transfer-to-rewards + """ + if not subcommand: + return None + + if subcommand == "stake-addresses": + return self._mir_stake_addresses_args( + stake_address=stake_address, + reward=reward, + funds=funds, + out_file=out_file, + ) + + if subcommand == "transfer-to-treasury": + return self._mir_transfer_to_treasury_args( + transfer_amt=transfer_amt, + out_file=out_file, + ) + + if subcommand == "transfer-to-rewards": + return self._mir_transfer_to_rewards_args( + transfer_amt=transfer_amt, + out_file=out_file, + ) + + msg = f"Unknown MIR subcommand: {subcommand}" + raise ValueError(msg) + + def create_mir_certificate( + self, + *, + # Direct MIR mode + reserves: bool = False, + treasury: bool = False, + reward: int | None = None, + stake_address: str | None = None, + # Subcommand mode + subcommand: str | None = None, + transfer_amt: int | None = None, + funds: str | None = None, + # Output + out_file: itp.FileType, + ) -> None: + """Wrap the `governance create-mir-certificate` command (compatible eras). + + Two usage modes: + + 1. Direct MIR: + * `reserves` or `treasury` (exactly one) + * `stake_address` + * `reward` + * `out_file` + + 2. Subcommands: + * `subcommand="stake-addresses"`, `funds=("reserves"|"treasury")`, + `stake_address`, `reward`, `out_file` + * `subcommand="transfer-to-treasury"`, `transfer_amt`, `out_file` + * `subcommand="transfer-to-rewards"`, `transfer_amt`, `out_file` + """ + direct_args = self._resolve_mir_direct_args( + reserves=reserves, + treasury=treasury, + stake_address=stake_address, + reward=reward, + out_file=out_file, + ) + + subcmd_args = self._resolve_mir_subcommand_args( + subcommand=subcommand, + stake_address=stake_address, + reward=reward, + transfer_amt=transfer_amt, + funds=funds, + out_file=out_file, + ) + + # Cannot mix modes + if direct_args and subcmd_args: + msg = "Cannot mix direct MIR mode with MIR subcommand mode." + raise ValueError(msg) + + if not direct_args and not subcmd_args: + msg = ( + "No MIR mode selected. Provide either direct MIR parameters " + "(reserves/treasury + stake_address + reward) " + "or a valid MIR subcommand." + ) + raise ValueError(msg) + + final_args = direct_args or subcmd_args + assert final_args is not None + + cmd = [ + *self._base, + "create-mir-certificate", + *final_args, + ] + + self._clusterlib_obj.cli(cmd, add_default_args=False) + + def _resolve_genesis_key_args( + self, + *, + genesis_vkey: str = "", + genesis_vkey_file: itp.FileType | None = None, + genesis_vkey_hash: str = "", + ) -> list[str]: + """Resolve genesis verification key specification.""" + if genesis_vkey: + return ["--genesis-verification-key", genesis_vkey] + + if genesis_vkey_file: + return ["--genesis-verification-key-file", str(genesis_vkey_file)] + + if genesis_vkey_hash: + return ["--genesis-verification-key-hash", genesis_vkey_hash] + + msg = ( + "One of genesis_vkey, genesis_vkey_file or genesis_vkey_hash " + "must be provided for genesis key delegation." + ) + raise ValueError(msg) + + def _resolve_delegate_key_args( + self, + *, + delegate_vkey: str = "", + delegate_vkey_file: itp.FileType | None = None, + delegate_vkey_hash: str = "", + ) -> list[str]: + """Resolve delegate verification key specification.""" + if delegate_vkey: + return ["--genesis-delegate-verification-key", delegate_vkey] + + if delegate_vkey_file: + return ["--genesis-delegate-verification-key-file", str(delegate_vkey_file)] + + if delegate_vkey_hash: + return ["--genesis-delegate-verification-key-hash", delegate_vkey_hash] + + msg = ( + "One of delegate_vkey, delegate_vkey_file or delegate_vkey_hash " + "must be provided for genesis key delegation." + ) + raise ValueError(msg) + + def _resolve_vrf_key_args( + self, + *, + vrf_vkey: str = "", + vrf_vkey_file: itp.FileType | None = None, + vrf_vkey_hash: str = "", + ) -> list[str]: + """Resolve VRF key specification.""" + if vrf_vkey: + return ["--vrf-verification-key", vrf_vkey] + + if vrf_vkey_file: + return ["--vrf-verification-key-file", str(vrf_vkey_file)] + + if vrf_vkey_hash: + return ["--vrf-verification-key-hash", vrf_vkey_hash] + + msg = ( + "One of vrf_vkey, vrf_vkey_file or vrf_vkey_hash " + "must be provided for genesis key delegation." + ) + raise ValueError(msg) + + def create_genesis_key_delegation_certificate( + self, + *, + genesis_vkey: str = "", + genesis_vkey_file: itp.FileType | None = None, + genesis_vkey_hash: str = "", + delegate_vkey: str = "", + delegate_vkey_file: itp.FileType | None = None, + delegate_vkey_hash: str = "", + vrf_vkey: str = "", + vrf_vkey_file: itp.FileType | None = None, + vrf_vkey_hash: str = "", + out_file: itp.FileType, + ) -> None: """Wrap the `governance create-genesis-key-delegation-certificate` command.""" - full_args = [*self._base, "create-genesis-key-delegation-certificate", *cli_args] + genesis_args = self._resolve_genesis_key_args( + genesis_vkey=genesis_vkey, + genesis_vkey_file=genesis_vkey_file, + genesis_vkey_hash=genesis_vkey_hash, + ) - self._clusterlib_obj.cli(full_args, add_default_args=False) + delegate_args = self._resolve_delegate_key_args( + delegate_vkey=delegate_vkey, + delegate_vkey_file=delegate_vkey_file, + delegate_vkey_hash=delegate_vkey_hash, + ) + + vrf_args = self._resolve_vrf_key_args( + vrf_vkey=vrf_vkey, + vrf_vkey_file=vrf_vkey_file, + vrf_vkey_hash=vrf_vkey_hash, + ) + + cmd = [ + *self._base, + "create-genesis-key-delegation-certificate", + *genesis_args, + *delegate_args, + *vrf_args, + "--out-file", + str(out_file), + ] + + self._clusterlib_obj.cli(cmd, add_default_args=False) class TransactionGroup: From 1467c4b1cba59a099c321169bfec464148adb08c Mon Sep 17 00:00:00 2001 From: femi Date: Thu, 4 Dec 2025 11:59:46 +0000 Subject: [PATCH 15/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 188 ++++++++++++++++++---------- 1 file changed, 121 insertions(+), 67 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 336bbd6..5df90fb 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -297,12 +297,113 @@ def registration_certificate( self._clusterlib_obj.cli(cmd, add_default_args=False) +class GovernanceActionGroup: + """Governance action subcommands for compatible eras.""" + + def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: + self._clusterlib_obj = clusterlib_obj + self._base = ("cardano-cli", "compatible", era, "governance", "action") + + def _resolve_pparam_args( + self, + *, + epoch: int | None = None, + genesis_vkey_file: itp.FileType | None = None, + pparams: dict[str, object] | None = None, + ) -> list[str]: + """Resolve protocol parameter update CLI arguments.""" + if epoch is None: + message = "The `epoch` parameter is required for protocol parameters update." + raise ValueError(message) + + if not genesis_vkey_file: + message = "`genesis_vkey_file` is required for protocol parameters update." + raise ValueError(message) + + if not pparams: + message = "At least one protocol parameter must be provided." + raise ValueError(message) + + args: list[str] = ["--epoch", str(epoch)] + + for flag, value in pparams.items(): + if value is None: + continue + args.extend([flag, str(value)]) + + args.extend(["--genesis-verification-key-file", str(genesis_vkey_file)]) + + return args + + def create_protocol_parameters_update( + self, + *, + epoch: int, + genesis_vkey_file: itp.FileType, + out_file: itp.FileType, + **pparams: object, + ) -> None: + """Wrap the protocol parameters update command.""" + flag_map = { + "min_fee_linear": "--min-fee-linear", + "min_fee_constant": "--min-fee-constant", + "max_block_body_size": "--max-block-body-size", + "max_tx_size": "--max-tx-size", + "max_block_header_size": "--max-block-header-size", + "key_reg_deposit_amt": "--key-reg-deposit-amt", + "pool_reg_deposit": "--pool-reg-deposit", + "pool_retirement_epoch_interval": "--pool-retirement-epoch-interval", + "number_of_pools": "--number-of-pools", + "pool_influence": "--pool-influence", + "treasury_expansion": "--treasury-expansion", + "monetary_expansion": "--monetary-expansion", + "min_pool_cost": "--min-pool-cost", + "price_execution_steps": "--price-execution-steps", + "price_execution_memory": "--price-execution-memory", + "max_tx_execution_units": "--max-tx-execution-units", + "max_block_execution_units": "--max-block-execution-units", + "max_value_size": "--max-value-size", + "collateral_percent": "--collateral-percent", + "max_collateral_inputs": "--max-collateral-inputs", + "protocol_major_version": "--protocol-major-version", + "protocol_minor_version": "--protocol-minor-version", + "utxo_cost_per_byte": "--utxo-cost-per-byte", + "cost_model_file": "--cost-model-file", + } + + pparam_args: dict[str, object] = {} + + for py_key, value in pparams.items(): + if py_key not in flag_map: + msg = f"Unknown protocol parameter: {py_key}" + raise ValueError(msg) + if value is not None: + pparam_args[flag_map[py_key]] = value + + resolved_args = self._resolve_pparam_args( + epoch=epoch, + genesis_vkey_file=genesis_vkey_file, + pparams=pparam_args, + ) + + cmd = [ + *self._base, + "create-protocol-parameters-update", + *resolved_args, + "--out-file", + str(out_file), + ] + + self._clusterlib_obj.cli(cmd, add_default_args=False) + + class GovernanceGroup: """Generic governance group for all compatible eras.""" def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "governance") + self.action = GovernanceActionGroup(clusterlib_obj, era) def _resolve_mir_direct_args( self, @@ -313,19 +414,9 @@ def _resolve_mir_direct_args( reward: int | None, out_file: itp.FileType, ) -> list[str] | None: - """Resolve direct MIR mode args (no subcommand). - - Direct mode syntax: - (--reserves | --treasury) - --stake-address ADDRESS - --reward LOVELACE - --out-file FILEPATH - """ - # No direct mode selected if not reserves and not treasury: return None - # Invalid: both pots selected if reserves and treasury: msg = "Cannot specify both `reserves` and `treasury` in direct MIR mode." raise ValueError(msg) @@ -338,10 +429,10 @@ def _resolve_mir_direct_args( msg = "`reward` is required in direct MIR mode." raise ValueError(msg) - pot_flag = "--reserves" if reserves else "--treasury" + flag = "--reserves" if reserves else "--treasury" - args: list[str] = [ - pot_flag, + args = [ + flag, "--stake-address", stake_address, "--reward", @@ -359,7 +450,6 @@ def _mir_stake_addresses_args( funds: str | None, out_file: itp.FileType, ) -> list[str]: - """Arguments for `create-mir-certificate stake-addresses`.""" if not stake_address: msg = "`stake_address` is required for 'stake-addresses' MIR subcommand." raise ValueError(msg) @@ -372,7 +462,7 @@ def _mir_stake_addresses_args( msg = "`funds` must be either 'reserves' or 'treasury' for 'stake-addresses'." raise ValueError(msg) - args: list[str] = [ + args = [ "stake-addresses", f"--{funds}", "--stake-address", @@ -390,19 +480,17 @@ def _mir_transfer_to_treasury_args( transfer_amt: int | None, out_file: itp.FileType, ) -> list[str]: - """Arguments for `create-mir-certificate transfer-to-treasury`.""" if transfer_amt is None: msg = "`transfer_amt` is required for 'transfer-to-treasury' MIR subcommand." raise ValueError(msg) - args: list[str] = [ + return [ "transfer-to-treasury", "--transfer", str(transfer_amt), "--out-file", str(out_file), ] - return args def _mir_transfer_to_rewards_args( self, @@ -410,19 +498,17 @@ def _mir_transfer_to_rewards_args( transfer_amt: int | None, out_file: itp.FileType, ) -> list[str]: - """Arguments for `create-mir-certificate transfer-to-rewards`.""" if transfer_amt is None: msg = "`transfer_amt` is required for 'transfer-to-rewards' MIR subcommand." raise ValueError(msg) - args: list[str] = [ + return [ "transfer-to-rewards", "--transfer", str(transfer_amt), "--out-file", str(out_file), ] - return args def _resolve_mir_subcommand_args( self, @@ -434,13 +520,6 @@ def _resolve_mir_subcommand_args( funds: str | None, out_file: itp.FileType, ) -> list[str] | None: - """Resolve MIR subcommand arguments, if any. - - Supported subcommands: - * stake-addresses - * transfer-to-treasury - * transfer-to-rewards - """ if not subcommand: return None @@ -470,34 +549,15 @@ def _resolve_mir_subcommand_args( def create_mir_certificate( self, *, - # Direct MIR mode reserves: bool = False, treasury: bool = False, reward: int | None = None, stake_address: str | None = None, - # Subcommand mode subcommand: str | None = None, transfer_amt: int | None = None, funds: str | None = None, - # Output out_file: itp.FileType, ) -> None: - """Wrap the `governance create-mir-certificate` command (compatible eras). - - Two usage modes: - - 1. Direct MIR: - * `reserves` or `treasury` (exactly one) - * `stake_address` - * `reward` - * `out_file` - - 2. Subcommands: - * `subcommand="stake-addresses"`, `funds=("reserves"|"treasury")`, - `stake_address`, `reward`, `out_file` - * `subcommand="transfer-to-treasury"`, `transfer_amt`, `out_file` - * `subcommand="transfer-to-rewards"`, `transfer_amt`, `out_file` - """ direct_args = self._resolve_mir_direct_args( reserves=reserves, treasury=treasury, @@ -515,7 +575,6 @@ def create_mir_certificate( out_file=out_file, ) - # Cannot mix modes if direct_args and subcmd_args: msg = "Cannot mix direct MIR mode with MIR subcommand mode." raise ValueError(msg) @@ -523,13 +582,17 @@ def create_mir_certificate( if not direct_args and not subcmd_args: msg = ( "No MIR mode selected. Provide either direct MIR parameters " - "(reserves/treasury + stake_address + reward) " "or a valid MIR subcommand." ) raise ValueError(msg) - final_args = direct_args or subcmd_args - assert final_args is not None + if direct_args is not None: + final_args: list[str] = direct_args + else: + msg = "Internal error: MIR subcommand arguments missing." + if subcmd_args is None: + raise ValueError(msg) + final_args = subcmd_args cmd = [ *self._base, @@ -539,6 +602,10 @@ def create_mir_certificate( self._clusterlib_obj.cli(cmd, add_default_args=False) + self._clusterlib_obj.cli(cmd, add_default_args=False) + + self._clusterlib_obj.cli(cmd, add_default_args=False) + def _resolve_genesis_key_args( self, *, @@ -546,7 +613,6 @@ def _resolve_genesis_key_args( genesis_vkey_file: itp.FileType | None = None, genesis_vkey_hash: str = "", ) -> list[str]: - """Resolve genesis verification key specification.""" if genesis_vkey: return ["--genesis-verification-key", genesis_vkey] @@ -556,10 +622,7 @@ def _resolve_genesis_key_args( if genesis_vkey_hash: return ["--genesis-verification-key-hash", genesis_vkey_hash] - msg = ( - "One of genesis_vkey, genesis_vkey_file or genesis_vkey_hash " - "must be provided for genesis key delegation." - ) + msg = "One of genesis_vkey, genesis_vkey_file or genesis_vkey_hash must be provided." raise ValueError(msg) def _resolve_delegate_key_args( @@ -569,7 +632,6 @@ def _resolve_delegate_key_args( delegate_vkey_file: itp.FileType | None = None, delegate_vkey_hash: str = "", ) -> list[str]: - """Resolve delegate verification key specification.""" if delegate_vkey: return ["--genesis-delegate-verification-key", delegate_vkey] @@ -579,10 +641,7 @@ def _resolve_delegate_key_args( if delegate_vkey_hash: return ["--genesis-delegate-verification-key-hash", delegate_vkey_hash] - msg = ( - "One of delegate_vkey, delegate_vkey_file or delegate_vkey_hash " - "must be provided for genesis key delegation." - ) + msg = "One of delegate_vkey, delegate_vkey_file or delegate_vkey_hash must be provided." raise ValueError(msg) def _resolve_vrf_key_args( @@ -592,7 +651,6 @@ def _resolve_vrf_key_args( vrf_vkey_file: itp.FileType | None = None, vrf_vkey_hash: str = "", ) -> list[str]: - """Resolve VRF key specification.""" if vrf_vkey: return ["--vrf-verification-key", vrf_vkey] @@ -602,10 +660,7 @@ def _resolve_vrf_key_args( if vrf_vkey_hash: return ["--vrf-verification-key-hash", vrf_vkey_hash] - msg = ( - "One of vrf_vkey, vrf_vkey_file or vrf_vkey_hash " - "must be provided for genesis key delegation." - ) + msg = "One VRF key argument must be provided." raise ValueError(msg) def create_genesis_key_delegation_certificate( @@ -622,7 +677,6 @@ def create_genesis_key_delegation_certificate( vrf_vkey_hash: str = "", out_file: itp.FileType, ) -> None: - """Wrap the `governance create-genesis-key-delegation-certificate` command.""" genesis_args = self._resolve_genesis_key_args( genesis_vkey=genesis_vkey, genesis_vkey_file=genesis_vkey_file, From 71fa3dadf448aa970d3b768f369f590a839e4eb1 Mon Sep 17 00:00:00 2001 From: femi Date: Fri, 5 Dec 2025 01:48:20 +0000 Subject: [PATCH 16/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 215 +++++++++------------------- cardano_clusterlib/structs.py | 12 ++ 2 files changed, 77 insertions(+), 150 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 5df90fb..ff4b61c 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -1,7 +1,11 @@ """Generic reusable classes for cardano-cli `compatible` commands.""" +import pathlib as pl from typing import TYPE_CHECKING +from cardano_clusterlib import clusterlib_helpers +from cardano_clusterlib import helpers +from cardano_clusterlib import structs from cardano_clusterlib import types as itp if TYPE_CHECKING: @@ -121,180 +125,91 @@ def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "stake-pool") - def _resolve_pool_vkey_args( + def gen_registration_cert( self, *, - stake_pool_vkey: str = "", - stake_pool_extended_vkey: str = "", - cold_vkey_file: itp.FileType | None = None, - ) -> list[str]: - """Resolve pool key identification.""" - if stake_pool_vkey: - return ["--stake-pool-verification-key", stake_pool_vkey] - if stake_pool_extended_vkey: - return ["--stake-pool-verification-extended-key", stake_pool_extended_vkey] - if cold_vkey_file: - return ["--cold-verification-key-file", str(cold_vkey_file)] - - msg = ( - "One of stake_pool_vkey, stake_pool_extended_vkey, or cold_vkey_file must be provided." - ) - raise ValueError(msg) - - def _resolve_vrf_args( - self, - *, - vrf_vkey: str = "", - vrf_vkey_file: itp.FileType | None = None, - ) -> list[str]: - """Resolve VRF key identification.""" - if vrf_vkey: - return ["--vrf-verification-key", vrf_vkey] - if vrf_vkey_file: - return ["--vrf-verification-key-file", str(vrf_vkey_file)] - - msg = "One of vrf_vkey or vrf_vkey_file must be provided." - raise ValueError(msg) - - def _resolve_reward_account_args( - self, - *, - reward_vkey: str = "", - reward_vkey_file: itp.FileType | None = None, - ) -> list[str]: - """Resolve reward account specification.""" - if reward_vkey: - return ["--pool-reward-account-verification-key", reward_vkey] - if reward_vkey_file: - return ["--pool-reward-account-verification-key-file", str(reward_vkey_file)] - - msg = "One of reward_vkey or reward_vkey_file must be provided." - raise ValueError(msg) - - def _resolve_owner_args( - self, - *, - owner_vkey: str = "", - owner_vkey_file: itp.FileType | None = None, - ) -> list[str]: - """Resolve owner stake key.""" - if owner_vkey: - return ["--pool-owner-verification-key", owner_vkey] - if owner_vkey_file: - return ["--pool-owner-stake-verification-key-file", str(owner_vkey_file)] - - msg = "One of owner_vkey or owner_vkey_file must be provided." - raise ValueError(msg) - - def registration_certificate( - self, - *, - # pool identification - stake_pool_vkey: str = "", - stake_pool_extended_vkey: str = "", - cold_vkey_file: itp.FileType | None = None, - # VRF identification - vrf_vkey: str = "", - vrf_vkey_file: itp.FileType | None = None, - # financials - pool_pledge: int, - pool_cost: int, - pool_margin: str, - # reward account - reward_vkey: str = "", - reward_vkey_file: itp.FileType | None = None, - # owner - owner_vkey: str = "", - owner_vkey_file: itp.FileType | None = None, - # relays - relay_ipv4: str = "", - relay_ipv6: str = "", - relay_port: int | None = None, - single_host_relay: str = "", - multi_host_relay: str = "", - # metadata - metadata_url: str = "", - metadata_hash: str = "", - check_metadata_hash: bool = False, - # output - out_file: itp.FileType, - ) -> None: - """Wrap the compat stake-pool registration-certificate command.""" - # Required argument groups - pool_args = self._resolve_pool_vkey_args( - stake_pool_vkey=stake_pool_vkey, - stake_pool_extended_vkey=stake_pool_extended_vkey, - cold_vkey_file=cold_vkey_file, - ) - - vrf_args = self._resolve_vrf_args( - vrf_vkey=vrf_vkey, - vrf_vkey_file=vrf_vkey_file, - ) - - reward_args = self._resolve_reward_account_args( - reward_vkey=reward_vkey, - reward_vkey_file=reward_vkey_file, - ) - - owner_args = self._resolve_owner_args( - owner_vkey=owner_vkey, - owner_vkey_file=owner_vkey_file, - ) - - # Relay handling - relay_args: list[str] = [] + name: str, + pool_params: structs.CompatPoolParams, + destination_dir: itp.FileType = ".", + ) -> pl.Path: + """Generate a compatible stake pool registration certificate.""" + pool_data = pool_params.pool_data - if relay_ipv4: - relay_args.extend(["--pool-relay-ipv4", relay_ipv4]) - if relay_ipv6: - relay_args.extend(["--pool-relay-ipv6", relay_ipv6]) - if relay_port: - relay_args.extend(["--pool-relay-port", str(relay_port)]) + destination_dir_path = pl.Path(destination_dir).expanduser() + out_file = destination_dir_path / f"{name}_pool_reg.cert" + clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) - if single_host_relay: - relay_args.extend(["--single-host-pool-relay", single_host_relay]) - if relay_port: - relay_args.extend(["--pool-relay-port", str(relay_port)]) - - if multi_host_relay: - relay_args.extend(["--multi-host-pool-relay", multi_host_relay]) - - # Metadata arguments metadata_args: list[str] = [] - if metadata_url and metadata_hash: + if pool_data.pool_metadata_url and pool_data.pool_metadata_hash: metadata_args.extend( [ "--metadata-url", - metadata_url, + str(pool_data.pool_metadata_url), "--metadata-hash", - metadata_hash, + str(pool_data.pool_metadata_hash), ] ) - if check_metadata_hash: + if pool_params.check_metadata_hash: metadata_args.append("--check-metadata-hash") - # Build final CLI cmd + relay_args: list[str] = [] + if pool_data.pool_relay_dns: + relay_args.extend(["--single-host-pool-relay", pool_data.pool_relay_dns]) + + if pool_data.pool_relay_ipv4: + relay_args.extend(["--pool-relay-ipv4", pool_data.pool_relay_ipv4]) + + if pool_params.relay_ipv6: + relay_args.extend(["--pool-relay-ipv6", pool_params.relay_ipv6]) + + if pool_data.pool_relay_port: + relay_args.extend(["--pool-relay-port", str(pool_data.pool_relay_port)]) + + if pool_params.multi_host_relay: + relay_args.extend(["--multi-host-pool-relay", pool_params.multi_host_relay]) + + # Reward account, default to first owner if not provided + if pool_params.reward_account_vkey_file: + reward_arg = [ + "--pool-reward-account-verification-key-file", + str(pool_params.reward_account_vkey_file), + ] + else: + default_owner = next(iter(pool_params.owner_stake_vkey_files)) + reward_arg = [ + "--pool-reward-account-verification-key-file", + str(default_owner), + ] + cmd = [ *self._base, "registration-certificate", - *pool_args, - *vrf_args, "--pool-pledge", - str(pool_pledge), + str(pool_data.pool_pledge), "--pool-cost", - str(pool_cost), + str(pool_data.pool_cost), "--pool-margin", - str(pool_margin), - *reward_args, - *owner_args, - *relay_args, - *metadata_args, + str(pool_data.pool_margin), + "--vrf-verification-key-file", + str(pool_params.vrf_vkey_file), + "--cold-verification-key-file", + str(pool_params.cold_vkey_file), + *reward_arg, + *helpers._prepend_flag( + "--pool-owner-stake-verification-key-file", + pool_params.owner_stake_vkey_files, + ), + *self._clusterlib_obj.magic_args, "--out-file", str(out_file), + *metadata_args, + *relay_args, ] self._clusterlib_obj.cli(cmd, add_default_args=False) + helpers._check_outfiles(out_file) + + return out_file class GovernanceActionGroup: diff --git a/cardano_clusterlib/structs.py b/cardano_clusterlib/structs.py index 5c3a3c0..9358bd1 100644 --- a/cardano_clusterlib/structs.py +++ b/cardano_clusterlib/structs.py @@ -232,6 +232,18 @@ class PoolData: pool_relay_port: int = 0 +@dataclasses.dataclass(frozen=True, order=True) +class CompatPoolParams: + pool_data: PoolData + vrf_vkey_file: itp.FileType + cold_vkey_file: itp.FileType + owner_stake_vkey_files: itp.FileTypeList + reward_account_vkey_file: itp.FileType | None = None + relay_ipv6: str = "" + multi_host_relay: str = "" + check_metadata_hash: bool = False + + @dataclasses.dataclass(frozen=True, order=True) class TxRawOutput: txins: list[UTXOData] # UTXOs used as inputs From 398b3aef14cf33baf690a6c1835bb1e8c4e4a6f0 Mon Sep 17 00:00:00 2001 From: femi Date: Fri, 5 Dec 2025 02:15:28 +0000 Subject: [PATCH 17/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 28 ++++++++++++++++++++++++++++ cardano_clusterlib/structs.py | 6 ++++++ 2 files changed, 34 insertions(+) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index ff4b61c..3540b31 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -211,6 +211,34 @@ def gen_registration_cert( return out_file + def gen_dereg_cert( + self, + *, + name: str, + params: structs.CompatPoolDeregParams, + destination_dir: itp.FileType = ".", + ) -> pl.Path: + """Generate a compatible stake pool deregistration certificate.""" + destination_dir_path = pl.Path(destination_dir).expanduser() + out_file = destination_dir_path / f"{name}_pool_dereg.cert" + clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) + + cmd = [ + *self._base, + "deregistration-certificate", + "--cold-verification-key-file", + str(params.cold_vkey_file), + "--epoch", + str(params.epoch), + "--out-file", + str(out_file), + ] + + self._clusterlib_obj.cli(cmd, add_default_args=False) + helpers._check_outfiles(out_file) + + return out_file + class GovernanceActionGroup: """Governance action subcommands for compatible eras.""" diff --git a/cardano_clusterlib/structs.py b/cardano_clusterlib/structs.py index 9358bd1..e3159f2 100644 --- a/cardano_clusterlib/structs.py +++ b/cardano_clusterlib/structs.py @@ -244,6 +244,12 @@ class CompatPoolParams: check_metadata_hash: bool = False +@dataclasses.dataclass(frozen=True) +class CompatPoolDeregParams: + cold_vkey_file: itp.FileType + epoch: int + + @dataclasses.dataclass(frozen=True, order=True) class TxRawOutput: txins: list[UTXOData] # UTXOs used as inputs From 8a9d61373bd699d72cf66fb315f2d522fe02cb80 Mon Sep 17 00:00:00 2001 From: femi Date: Fri, 5 Dec 2025 11:57:41 +0000 Subject: [PATCH 18/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 363 ++++++++++------------------ 1 file changed, 123 insertions(+), 240 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 3540b31..4500e48 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -13,7 +13,7 @@ class StakeAddressGroup: - """Generic stake-address group for all compatible eras.""" + """Compatible stake-address commands for Alonzo / Mary / Babbage eras.""" def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj @@ -28,34 +28,44 @@ def _resolve_stake_key_args( stake_script_file: itp.FileType | None = None, stake_address: str | None = None, ) -> list[str]: - """Resolve stake key CLI args for registration & delegation.""" + """Resolve stake key CLI args (compatible-era logic).""" if stake_vkey: return ["--stake-verification-key", stake_vkey] + if stake_vkey_file: return ["--stake-verification-key-file", str(stake_vkey_file)] + if stake_key_hash: return ["--stake-key-hash", stake_key_hash] + if stake_script_file: return ["--stake-script-file", str(stake_script_file)] + if stake_address: return ["--stake-address", stake_address] - msg = ( + + message = ( "One of stake_vkey, stake_vkey_file, stake_key_hash, " "stake_script_file or stake_address must be provided." ) - raise ValueError(msg) + raise ValueError(message) - def registration_certificate( + def gen_registration_cert( self, *, + name: str, stake_vkey: str = "", stake_vkey_file: itp.FileType | None = None, stake_key_hash: str = "", stake_script_file: itp.FileType | None = None, stake_address: str | None = None, - out_file: itp.FileType, - ) -> None: - """Wrap the legacy stake-address registration-certificate command.""" + destination_dir: itp.FileType = ".", + ) -> pl.Path: + """Generate a stake address registration certificate (compatible era).""" + destination_dir_path = pl.Path(destination_dir).expanduser() + out_file = destination_dir_path / f"{name}_stake_reg.cert" + clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) + stake_args = self._resolve_stake_key_args( stake_vkey=stake_vkey, stake_vkey_file=stake_vkey_file, @@ -68,15 +78,20 @@ def registration_certificate( *self._base, "registration-certificate", *stake_args, + *self._clusterlib_obj.magic_args, "--out-file", str(out_file), ] self._clusterlib_obj.cli(cmd, add_default_args=False) + helpers._check_outfiles(out_file) + + return out_file - def delegation_certificate( + def gen_delegation_cert( self, *, + name: str, stake_vkey: str = "", stake_vkey_file: itp.FileType | None = None, stake_key_hash: str = "", @@ -85,9 +100,14 @@ def delegation_certificate( stake_pool_vkey: str = "", cold_vkey_file: itp.FileType | None = None, stake_pool_id: str = "", - out_file: itp.FileType, - ) -> None: - """Wrap the legacy stake-address stake-delegation-certificate command.""" + destination_dir: itp.FileType = ".", + ) -> pl.Path: + """Generate a stake delegation certificate (compatible era).""" + destination_dir_path = pl.Path(destination_dir).expanduser() + out_file = destination_dir_path / f"{name}_stake_deleg.cert" + clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) + + # Stake identification: stake_args = self._resolve_stake_key_args( stake_vkey=stake_vkey, stake_vkey_file=stake_vkey_file, @@ -96,6 +116,7 @@ def delegation_certificate( stake_address=stake_address, ) + # Pool identification: if stake_pool_vkey: pool_args = ["--stake-pool-verification-key", stake_pool_vkey] elif cold_vkey_file: @@ -103,19 +124,29 @@ def delegation_certificate( elif stake_pool_id: pool_args = ["--stake-pool-id", stake_pool_id] else: - msg = "One of stake_pool_vkey, cold_vkey_file or stake_pool_id must be provided." - raise ValueError(msg) + message = ( + "One of stake_pool_vkey, cold_vkey_file or stake_pool_id " + "must be provided for delegation." + ) + raise ValueError(message) cmd = [ *self._base, "stake-delegation-certificate", *stake_args, *pool_args, + *self._clusterlib_obj.magic_args, "--out-file", str(out_file), ] self._clusterlib_obj.cli(cmd, add_default_args=False) + helpers._check_outfiles(out_file) + + return out_file + + def __repr__(self) -> str: + return f"" class StakePoolGroup: @@ -247,97 +278,37 @@ def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "governance", "action") - def _resolve_pparam_args( - self, - *, - epoch: int | None = None, - genesis_vkey_file: itp.FileType | None = None, - pparams: dict[str, object] | None = None, - ) -> list[str]: - """Resolve protocol parameter update CLI arguments.""" - if epoch is None: - message = "The `epoch` parameter is required for protocol parameters update." - raise ValueError(message) - - if not genesis_vkey_file: - message = "`genesis_vkey_file` is required for protocol parameters update." - raise ValueError(message) - - if not pparams: - message = "At least one protocol parameter must be provided." - raise ValueError(message) - - args: list[str] = ["--epoch", str(epoch)] - - for flag, value in pparams.items(): - if value is None: - continue - args.extend([flag, str(value)]) - - args.extend(["--genesis-verification-key-file", str(genesis_vkey_file)]) - - return args - - def create_protocol_parameters_update( + def gen_pparams_update( self, *, + name: str, epoch: int, genesis_vkey_file: itp.FileType, - out_file: itp.FileType, - **pparams: object, - ) -> None: - """Wrap the protocol parameters update command.""" - flag_map = { - "min_fee_linear": "--min-fee-linear", - "min_fee_constant": "--min-fee-constant", - "max_block_body_size": "--max-block-body-size", - "max_tx_size": "--max-tx-size", - "max_block_header_size": "--max-block-header-size", - "key_reg_deposit_amt": "--key-reg-deposit-amt", - "pool_reg_deposit": "--pool-reg-deposit", - "pool_retirement_epoch_interval": "--pool-retirement-epoch-interval", - "number_of_pools": "--number-of-pools", - "pool_influence": "--pool-influence", - "treasury_expansion": "--treasury-expansion", - "monetary_expansion": "--monetary-expansion", - "min_pool_cost": "--min-pool-cost", - "price_execution_steps": "--price-execution-steps", - "price_execution_memory": "--price-execution-memory", - "max_tx_execution_units": "--max-tx-execution-units", - "max_block_execution_units": "--max-block-execution-units", - "max_value_size": "--max-value-size", - "collateral_percent": "--collateral-percent", - "max_collateral_inputs": "--max-collateral-inputs", - "protocol_major_version": "--protocol-major-version", - "protocol_minor_version": "--protocol-minor-version", - "utxo_cost_per_byte": "--utxo-cost-per-byte", - "cost_model_file": "--cost-model-file", - } - - pparam_args: dict[str, object] = {} - - for py_key, value in pparams.items(): - if py_key not in flag_map: - msg = f"Unknown protocol parameter: {py_key}" - raise ValueError(msg) - if value is not None: - pparam_args[flag_map[py_key]] = value - - resolved_args = self._resolve_pparam_args( - epoch=epoch, - genesis_vkey_file=genesis_vkey_file, - pparams=pparam_args, - ) + cli_args: itp.UnpackableSequence, + destination_dir: itp.FileType = ".", + ) -> pl.Path: + """Generate a protocol parameters update proposal for compatible eras.""" + destination_dir_path = pl.Path(destination_dir).expanduser() + out_file = destination_dir_path / f"{name}_pparams_update.action" + clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) cmd = [ *self._base, "create-protocol-parameters-update", - *resolved_args, + *self._clusterlib_obj.magic_args, + "--epoch", + str(epoch), + "--genesis-verification-key-file", + str(genesis_vkey_file), + *cli_args, # raw CLI flags directly "--out-file", str(out_file), ] self._clusterlib_obj.cli(cmd, add_default_args=False) + helpers._check_outfiles(out_file) + + return out_file class GovernanceGroup: @@ -355,35 +326,32 @@ def _resolve_mir_direct_args( treasury: bool, stake_address: str | None, reward: int | None, - out_file: itp.FileType, ) -> list[str] | None: + """Resolve direct MIR args (no subcommand).""" if not reserves and not treasury: return None if reserves and treasury: - msg = "Cannot specify both `reserves` and `treasury` in direct MIR mode." + msg = "Cannot specify both `reserves` and `treasury` in MIR direct mode." raise ValueError(msg) if not stake_address: - msg = "`stake_address` is required in direct MIR mode." + msg = "`stake_address` is required in MIR direct mode." raise ValueError(msg) if reward is None: - msg = "`reward` is required in direct MIR mode." + msg = "`reward` is required in MIR direct mode." raise ValueError(msg) - flag = "--reserves" if reserves else "--treasury" + pot_flag = "--reserves" if reserves else "--treasury" - args = [ - flag, + return [ + pot_flag, "--stake-address", stake_address, "--reward", str(reward), - "--out-file", - str(out_file), ] - return args def _mir_stake_addresses_args( self, @@ -391,66 +359,57 @@ def _mir_stake_addresses_args( stake_address: str | None, reward: int | None, funds: str | None, - out_file: itp.FileType, ) -> list[str]: + """Args for MIR `stake-addresses` subcommand.""" if not stake_address: - msg = "`stake_address` is required for 'stake-addresses' MIR subcommand." + msg = "`stake_address` is required for MIR stake-addresses." raise ValueError(msg) if reward is None: - msg = "`reward` is required for 'stake-addresses' MIR subcommand." + msg = "`reward` is required for MIR stake-addresses." raise ValueError(msg) if funds not in ("reserves", "treasury"): - msg = "`funds` must be either 'reserves' or 'treasury' for 'stake-addresses'." + msg = "`funds` must be either 'reserves' or 'treasury'." raise ValueError(msg) - args = [ + return [ "stake-addresses", f"--{funds}", "--stake-address", stake_address, "--reward", str(reward), - "--out-file", - str(out_file), ] - return args def _mir_transfer_to_treasury_args( self, *, transfer_amt: int | None, - out_file: itp.FileType, ) -> list[str]: if transfer_amt is None: - msg = "`transfer_amt` is required for 'transfer-to-treasury' MIR subcommand." + msg = "`transfer_amt` is required for MIR transfer-to-treasury." raise ValueError(msg) return [ "transfer-to-treasury", "--transfer", str(transfer_amt), - "--out-file", - str(out_file), ] def _mir_transfer_to_rewards_args( self, *, transfer_amt: int | None, - out_file: itp.FileType, ) -> list[str]: if transfer_amt is None: - msg = "`transfer_amt` is required for 'transfer-to-rewards' MIR subcommand." + msg = "`transfer_amt` is required for MIR transfer-to-rewards." raise ValueError(msg) return [ "transfer-to-rewards", "--transfer", str(transfer_amt), - "--out-file", - str(out_file), ] def _resolve_mir_subcommand_args( @@ -461,8 +420,8 @@ def _resolve_mir_subcommand_args( reward: int | None, transfer_amt: int | None, funds: str | None, - out_file: itp.FileType, ) -> list[str] | None: + """Resolve MIR subcommand args.""" if not subcommand: return None @@ -471,27 +430,25 @@ def _resolve_mir_subcommand_args( stake_address=stake_address, reward=reward, funds=funds, - out_file=out_file, ) if subcommand == "transfer-to-treasury": return self._mir_transfer_to_treasury_args( transfer_amt=transfer_amt, - out_file=out_file, ) if subcommand == "transfer-to-rewards": return self._mir_transfer_to_rewards_args( transfer_amt=transfer_amt, - out_file=out_file, ) msg = f"Unknown MIR subcommand: {subcommand}" raise ValueError(msg) - def create_mir_certificate( + def gen_mir_cert( self, *, + name: str, reserves: bool = False, treasury: bool = False, reward: int | None = None, @@ -499,14 +456,16 @@ def create_mir_certificate( subcommand: str | None = None, transfer_amt: int | None = None, funds: str | None = None, - out_file: itp.FileType, - ) -> None: + destination_dir: itp.FileType = ".", + ) -> pl.Path: + """Generate MIR certificate for Babbage compatible eras.""" + destination_dir_path = pl.Path(destination_dir).expanduser() + direct_args = self._resolve_mir_direct_args( reserves=reserves, treasury=treasury, stake_address=stake_address, reward=reward, - out_file=out_file, ) subcmd_args = self._resolve_mir_subcommand_args( @@ -515,151 +474,75 @@ def create_mir_certificate( reward=reward, transfer_amt=transfer_amt, funds=funds, - out_file=out_file, ) if direct_args and subcmd_args: - msg = "Cannot mix direct MIR mode with MIR subcommand mode." + msg = "Cannot mix MIR direct mode with MIR subcommand mode." raise ValueError(msg) if not direct_args and not subcmd_args: - msg = ( - "No MIR mode selected. Provide either direct MIR parameters " - "or a valid MIR subcommand." - ) + msg = "No MIR mode selected." raise ValueError(msg) - if direct_args is not None: - final_args: list[str] = direct_args + final_args: list[str] = direct_args if direct_args is not None else subcmd_args # type: ignore + + if direct_args: + out_file = destination_dir_path / f"{name}_mir_direct.cert" + elif subcommand == "stake-addresses": + out_file = destination_dir_path / f"{name}_mir_stake.cert" + elif subcommand == "transfer-to-treasury": + out_file = destination_dir_path / f"{name}_mir_to_treasury.cert" else: - msg = "Internal error: MIR subcommand arguments missing." - if subcmd_args is None: - raise ValueError(msg) - final_args = subcmd_args + out_file = destination_dir_path / f"{name}_mir_to_rewards.cert" + + clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) cmd = [ *self._base, "create-mir-certificate", *final_args, + *self._clusterlib_obj.magic_args, + "--out-file", + str(out_file), ] self._clusterlib_obj.cli(cmd, add_default_args=False) + helpers._check_outfiles(out_file) - self._clusterlib_obj.cli(cmd, add_default_args=False) - - self._clusterlib_obj.cli(cmd, add_default_args=False) - - def _resolve_genesis_key_args( - self, - *, - genesis_vkey: str = "", - genesis_vkey_file: itp.FileType | None = None, - genesis_vkey_hash: str = "", - ) -> list[str]: - if genesis_vkey: - return ["--genesis-verification-key", genesis_vkey] - - if genesis_vkey_file: - return ["--genesis-verification-key-file", str(genesis_vkey_file)] - - if genesis_vkey_hash: - return ["--genesis-verification-key-hash", genesis_vkey_hash] - - msg = "One of genesis_vkey, genesis_vkey_file or genesis_vkey_hash must be provided." - raise ValueError(msg) - - def _resolve_delegate_key_args( - self, - *, - delegate_vkey: str = "", - delegate_vkey_file: itp.FileType | None = None, - delegate_vkey_hash: str = "", - ) -> list[str]: - if delegate_vkey: - return ["--genesis-delegate-verification-key", delegate_vkey] - - if delegate_vkey_file: - return ["--genesis-delegate-verification-key-file", str(delegate_vkey_file)] - - if delegate_vkey_hash: - return ["--genesis-delegate-verification-key-hash", delegate_vkey_hash] - - msg = "One of delegate_vkey, delegate_vkey_file or delegate_vkey_hash must be provided." - raise ValueError(msg) - - def _resolve_vrf_key_args( - self, - *, - vrf_vkey: str = "", - vrf_vkey_file: itp.FileType | None = None, - vrf_vkey_hash: str = "", - ) -> list[str]: - if vrf_vkey: - return ["--vrf-verification-key", vrf_vkey] + return out_file - if vrf_vkey_file: - return ["--vrf-verification-key-file", str(vrf_vkey_file)] - if vrf_vkey_hash: - return ["--vrf-verification-key-hash", vrf_vkey_hash] +class TransactionGroup: + """Generic transaction group for all compatible eras.""" - msg = "One VRF key argument must be provided." - raise ValueError(msg) + def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: + self._clusterlib_obj = clusterlib_obj + self._base = ("cardano-cli", "compatible", era, "transaction") - def create_genesis_key_delegation_certificate( + def gen_signed_tx( self, *, - genesis_vkey: str = "", - genesis_vkey_file: itp.FileType | None = None, - genesis_vkey_hash: str = "", - delegate_vkey: str = "", - delegate_vkey_file: itp.FileType | None = None, - delegate_vkey_hash: str = "", - vrf_vkey: str = "", - vrf_vkey_file: itp.FileType | None = None, - vrf_vkey_hash: str = "", - out_file: itp.FileType, - ) -> None: - genesis_args = self._resolve_genesis_key_args( - genesis_vkey=genesis_vkey, - genesis_vkey_file=genesis_vkey_file, - genesis_vkey_hash=genesis_vkey_hash, - ) - - delegate_args = self._resolve_delegate_key_args( - delegate_vkey=delegate_vkey, - delegate_vkey_file=delegate_vkey_file, - delegate_vkey_hash=delegate_vkey_hash, - ) - - vrf_args = self._resolve_vrf_key_args( - vrf_vkey=vrf_vkey, - vrf_vkey_file=vrf_vkey_file, - vrf_vkey_hash=vrf_vkey_hash, - ) + name: str, + cli_args: itp.UnpackableSequence, + destination_dir: itp.FileType = ".", + ) -> pl.Path: + """Generate a simple signed transaction file (compatible-era).""" + destination_dir_path = pl.Path(destination_dir).expanduser() + out_file = destination_dir_path / f"{name}_signed.tx" + clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) cmd = [ *self._base, - "create-genesis-key-delegation-certificate", - *genesis_args, - *delegate_args, - *vrf_args, + "signed-transaction", + *cli_args, "--out-file", str(out_file), ] self._clusterlib_obj.cli(cmd, add_default_args=False) + helpers._check_outfiles(out_file) + return out_file -class TransactionGroup: - """Generic transaction group for all compatible eras.""" - - def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: - self._clusterlib_obj = clusterlib_obj - self._base = ("cardano-cli", "compatible", era, "transaction") - - def signed_transaction(self, cli_args: itp.UnpackableSequence) -> None: - """Wrap the `transaction signed-transaction` command.""" - full_args = [*self._base, "signed-transaction", *cli_args] - - self._clusterlib_obj.cli(full_args, add_default_args=False) + def __repr__(self) -> str: + return f"" From 6d059d3318bd3e35d3442cff752e7b477148116f Mon Sep 17 00:00:00 2001 From: femi Date: Fri, 5 Dec 2025 11:58:22 +0000 Subject: [PATCH 19/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 4500e48..40a63bf 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -300,7 +300,7 @@ def gen_pparams_update( str(epoch), "--genesis-verification-key-file", str(genesis_vkey_file), - *cli_args, # raw CLI flags directly + *cli_args, "--out-file", str(out_file), ] From 4cd20e9c6e596ffa9da51c4210ebf8cc85dbef02 Mon Sep 17 00:00:00 2001 From: femi Date: Mon, 8 Dec 2025 10:29:34 +0000 Subject: [PATCH 20/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 80 +++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 40a63bf..d7fe029 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -4,6 +4,8 @@ from typing import TYPE_CHECKING from cardano_clusterlib import clusterlib_helpers +from cardano_clusterlib import consts +from cardano_clusterlib import exceptions from cardano_clusterlib import helpers from cardano_clusterlib import structs from cardano_clusterlib import types as itp @@ -544,5 +546,83 @@ def gen_signed_tx( return out_file + def create_signed_tx( + self, + *, + name: str, + src_address: str, + txouts: list[structs.TxOut], + signing_key_files: itp.OptionalFiles, + destination_dir: itp.FileType = ".", + ) -> pl.Path: + """Create, balance, and sign a simple compatible-era transaction. + + Constraints: + * ADA only (no multi-asset, no scripts, no metadata) + * simple UTxO selection and balancing + """ + # 1 Query UTxOs for the source address + utxos = self._clusterlib_obj.g_query.get_utxo(address=src_address) + + ada_utxos = [ + u + for u in utxos + if u.coin == consts.DEFAULT_COIN and not u.datum_hash and not u.inline_datum_hash + ] + + if not ada_utxos: + message = f"No usable ADA UTxOs for {src_address}" + raise exceptions.CLIError(message) + + # Required ADA amount (sum of requested outputs) + amount_required = sum(t.amount for t in txouts) + + # 3 Simple fee buffers + min_fee_b = self._clusterlib_obj.genesis["protocolParams"]["minFeeB"] + fee = min(min_fee_b * 6, 2_000_000) + + # 4 Naive UTxO selections (smallest-first) + selected: list[structs.UTXOData] = [] + running_total = 0 + + for u in sorted(ada_utxos, key=lambda x: x.amount): + selected.append(u) + running_total += u.amount + if running_total >= amount_required + fee: + break + + if running_total < amount_required + fee: + needed = amount_required + fee + message = ( + f"Insufficient ADA for transaction. Required {needed}, available {running_total}." + ) + raise exceptions.CLIError(message) + + # 5 compute change and final outputs + change = running_total - (amount_required + fee) + final_txouts = list(txouts) + + if change > 0: + final_txouts.append(structs.TxOut(address=src_address, amount=change)) + + # 6 Build CLI args for signed-transaction + cli_args: list[str] = [] + + for u in selected: + cli_args.extend(["--tx-in", f"{u.utxo_hash}#{u.utxo_ix}"]) + + for t in final_txouts: + cli_args.extend(["--tx-out", f"{t.address}+{t.amount}"]) + + cli_args.extend(["--fee", str(fee)]) + cli_args.extend(helpers._prepend_flag("--signing-key-file", signing_key_files)) + + # 7 Delegate to bare wrapper for actual CLI call and file handling + return self.gen_signed_tx( + name=name, + cli_args=cli_args, + destination_dir=destination_dir, + ) + def __repr__(self) -> str: return f"" From 60228e1fdfdfd1c41c519b3f44f79c26cf0f7a39 Mon Sep 17 00:00:00 2001 From: femi Date: Mon, 8 Dec 2025 12:02:46 +0000 Subject: [PATCH 21/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 186 ++++++++++++++++++++-------- 1 file changed, 136 insertions(+), 50 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index d7fe029..4bf380b 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -8,6 +8,7 @@ from cardano_clusterlib import exceptions from cardano_clusterlib import helpers from cardano_clusterlib import structs +from cardano_clusterlib import txtools from cardano_clusterlib import types as itp if TYPE_CHECKING: @@ -515,20 +516,24 @@ def gen_mir_cert( class TransactionGroup: - """Generic transaction group for all compatible eras.""" + """Compatible transaction commands for legacy eras (Alonzo / Mary / Babbage). + """ def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "transaction") + self._min_fee = self._clusterlib_obj.genesis["protocolParams"]["minFeeB"] - def gen_signed_tx( + + def gen_signed_tx_raw( self, *, name: str, cli_args: itp.UnpackableSequence, destination_dir: itp.FileType = ".", ) -> pl.Path: - """Generate a simple signed transaction file (compatible-era).""" + """Run `... transaction signed-transaction` with pre-built CLI args. + """ destination_dir_path = pl.Path(destination_dir).expanduser() out_file = destination_dir_path / f"{name}_signed.tx" clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) @@ -546,82 +551,163 @@ def gen_signed_tx( return out_file - def create_signed_tx( + + def _select_ada_utxos_naive( self, *, - name: str, src_address: str, - txouts: list[structs.TxOut], - signing_key_files: itp.OptionalFiles, - destination_dir: itp.FileType = ".", - ) -> pl.Path: - """Create, balance, and sign a simple compatible-era transaction. + amount_required: int, + fee: int, + ) -> list[structs.UTXOData]: + """Select simple ADA UTxOs from `src_address` to cover amount + fee. + + Rules: + * Only pure ADA UTxOs (no datum, no inline datum, no reference script). - Constraints: - * ADA only (no multi-asset, no scripts, no metadata) - * simple UTxO selection and balancing """ - # 1 Query UTxOs for the source address - utxos = self._clusterlib_obj.g_query.get_utxo(address=src_address) + utxos = self._clusterlib_obj.g_query.get_utxo( + address=src_address, + coins=[consts.DEFAULT_COIN], + ) - ada_utxos = [ + usable_utxos = [ u for u in utxos - if u.coin == consts.DEFAULT_COIN and not u.datum_hash and not u.inline_datum_hash + if not u.datum_hash and not u.inline_datum_hash and not u.reference_script ] - if not ada_utxos: - message = f"No usable ADA UTxOs for {src_address}" + if not usable_utxos: + message = f"No usable ADA UTxOs for address {src_address}." raise exceptions.CLIError(message) - # Required ADA amount (sum of requested outputs) - amount_required = sum(t.amount for t in txouts) + target = amount_required + fee + if target <= 0: + # At minimum we must cover the fee + target = fee - # 3 Simple fee buffers - min_fee_b = self._clusterlib_obj.genesis["protocolParams"]["minFeeB"] - fee = min(min_fee_b * 6, 2_000_000) + sorted_utxos = sorted(usable_utxos, key=lambda u: u.amount) - # 4 Naive UTxO selections (smallest-first) selected: list[structs.UTXOData] = [] running_total = 0 - for u in sorted(ada_utxos, key=lambda x: x.amount): - selected.append(u) - running_total += u.amount - if running_total >= amount_required + fee: + for utxo in sorted_utxos: + selected.append(utxo) + running_total += utxo.amount + if running_total >= target: break - if running_total < amount_required + fee: - needed = amount_required + fee + if running_total < target: message = ( - f"Insufficient ADA for transaction. Required {needed}, available {running_total}." + f"Insufficient ADA for transaction: required {target}, available {running_total}." ) raise exceptions.CLIError(message) - # 5 compute change and final outputs - change = running_total - (amount_required + fee) - final_txouts = list(txouts) + return selected + + + def gen_signed_tx( + self, + *, + name: str, + src_address: str, + txouts: list[structs.TxOut], + tx_files: structs.TxFiles, + fee: int, + destination_dir: itp.FileType = ".", + ) -> structs.TxRawOutput: + """Generate a simple signed transaction for compatible eras. + """ + destination_dir_path = pl.Path(destination_dir).expanduser() + out_file = destination_dir_path / f"{name}_signed.tx" + clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) + + # 1) Compute net deposit from certificates using the main (non-compatible) Tx group + deposit = 0 + if tx_files.certificate_files: + deposit = self._clusterlib_obj.g_transaction.get_tx_deposit(tx_files=tx_files) + + # 2) Sum ADA outputs (ignore non-ADA coins for now) + ada_outputs = [t for t in txouts if t.coin == consts.DEFAULT_COIN] + total_out = sum(t.amount for t in ada_outputs) + amount_required = total_out + deposit + + # 3) Select ADA UTxOs from the source address + selected_utxos = self._select_ada_utxos_naive( + src_address=src_address, + amount_required=amount_required, + fee=fee, + ) + total_in = sum(u.amount for u in selected_utxos) + + # 4) Compute change (if any) back to the source address + change_amount = total_in - amount_required - fee + change_txouts: list[structs.TxOut] = [] + if change_amount > 0: + change_txouts.append( + structs.TxOut( + address=src_address, + amount=change_amount, + coin=consts.DEFAULT_COIN, + ) + ) + + final_txouts = [*txouts, *change_txouts] + + # 5) Build `--tx-in` and `--tx-out` CLI args + txin_args: list[str] = [] + for utxo in selected_utxos: + txin_args.extend( + [ + "--tx-in", + f"{utxo.utxo_hash}#{utxo.utxo_ix}", + ] + ) + + # Reuse existing joining logic so `txouts_count` etc. are consistent + txout_args, processed_txouts, txouts_count = txtools._process_txouts( + txouts=final_txouts, + join_txouts=True, + ) - if change > 0: - final_txouts.append(structs.TxOut(address=src_address, amount=change)) + # Certificates (if any) + cert_args = helpers._prepend_flag("--certificate-file", tx_files.certificate_files) - # 6 Build CLI args for signed-transaction - cli_args: list[str] = [] + # Signing keys + skey_args = helpers._prepend_flag("--signing-key-file", tx_files.signing_key_files) - for u in selected: - cli_args.extend(["--tx-in", f"{u.utxo_hash}#{u.utxo_ix}"]) + # 6) Compose CLI args for `signed-transaction` + cli_args: list[str] = [ + *txin_args, + *txout_args, + *cert_args, + *skey_args, + "--fee", + str(fee), + *self._clusterlib_obj.magic_args, + ] - for t in final_txouts: - cli_args.extend(["--tx-out", f"{t.address}+{t.amount}"]) + # 7) Run the compat CLI and produce the signed tx file + cmd = [ + *self._base, + "signed-transaction", + *cli_args, + "--out-file", + str(out_file), + ] - cli_args.extend(["--fee", str(fee)]) - cli_args.extend(helpers._prepend_flag("--signing-key-file", signing_key_files)) + self._clusterlib_obj.cli(cmd, add_default_args=False) + helpers._check_outfiles(out_file) - # 7 Delegate to bare wrapper for actual CLI call and file handling - return self.gen_signed_tx( - name=name, - cli_args=cli_args, - destination_dir=destination_dir, + # 8) Return a TxRawOutput describing what we just did + return structs.TxRawOutput( + txins=selected_utxos, + txouts=processed_txouts, + txouts_count=txouts_count, + tx_files=tx_files, + out_file=out_file, + fee=fee, + build_args=cmd, + era=self._clusterlib_obj.era_in_use, ) def __repr__(self) -> str: From daf1e3486f03abfd160d4d423e365654c2c44093 Mon Sep 17 00:00:00 2001 From: femi Date: Tue, 9 Dec 2025 11:01:42 +0000 Subject: [PATCH 22/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 261 ++++++++++++++-------------- 1 file changed, 126 insertions(+), 135 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 4bf380b..357f2e0 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -4,8 +4,6 @@ from typing import TYPE_CHECKING from cardano_clusterlib import clusterlib_helpers -from cardano_clusterlib import consts -from cardano_clusterlib import exceptions from cardano_clusterlib import helpers from cardano_clusterlib import structs from cardano_clusterlib import txtools @@ -517,31 +515,71 @@ def gen_mir_cert( class TransactionGroup: """Compatible transaction commands for legacy eras (Alonzo / Mary / Babbage). + + This is a lightweight wrapper around: + - txtools.collect_data_for_build -> UTxO selection, balancing, deposits + - `cardano-cli compatible transaction signed-transaction` + + It is intentionally simpler than the Conway TransactionGroup: + - No Plutus scripts + - No minting or burning + - No withdrawals + - No collateral + - No reference inputs + + Main use for compatible eras: + - simple ADA transfers + - stake key registration or deregistration + - pool registration or deregistration + - governance certificates (MIR, genesis delegation, protocol parameter updates) """ def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "transaction") - self._min_fee = self._clusterlib_obj.genesis["protocolParams"]["minFeeB"] - - def gen_signed_tx_raw( + def gen_signed_tx_bare( self, *, name: str, - cli_args: itp.UnpackableSequence, + txins: structs.OptionalUTXOData, + txouts: list[structs.TxOut], + tx_files: structs.TxFiles, + fee: int, destination_dir: itp.FileType = ".", + join_txouts: bool = True, ) -> pl.Path: - """Run `... transaction signed-transaction` with pre-built CLI args. + """Generate a simple signed transaction using pre-balanced inputs and outputs. + + Assumptions: + - `txins` already contain selected UTxOs + - `txouts` are already balanced (including change and deposits) + - `fee` is already decided """ destination_dir_path = pl.Path(destination_dir).expanduser() - out_file = destination_dir_path / f"{name}_signed.tx" + out_file = destination_dir_path / f"{name}_tx.signed" clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) + txout_args, processed_txouts, __ = txtools._process_txouts( + txouts=list(txouts), + join_txouts=join_txouts, + ) + + txin_strings = txtools._get_txin_strings( + txins=txins, + script_txins=(), + ) + cmd = [ *self._base, "signed-transaction", - *cli_args, + *helpers._prepend_flag("--tx-in", txin_strings), + *txout_args, + *helpers._prepend_flag("--certificate-file", tx_files.certificate_files), + *helpers._prepend_flag("--signing-key-file", tx_files.signing_key_files), + *self._clusterlib_obj.magic_args, + "--fee", + str(fee), "--out-file", str(out_file), ] @@ -549,165 +587,118 @@ def gen_signed_tx_raw( self._clusterlib_obj.cli(cmd, add_default_args=False) helpers._check_outfiles(out_file) + _ = processed_txouts return out_file - - def _select_ada_utxos_naive( - self, - *, - src_address: str, - amount_required: int, - fee: int, - ) -> list[structs.UTXOData]: - """Select simple ADA UTxOs from `src_address` to cover amount + fee. - - Rules: - * Only pure ADA UTxOs (no datum, no inline datum, no reference script). - - """ - utxos = self._clusterlib_obj.g_query.get_utxo( - address=src_address, - coins=[consts.DEFAULT_COIN], - ) - - usable_utxos = [ - u - for u in utxos - if not u.datum_hash and not u.inline_datum_hash and not u.reference_script - ] - - if not usable_utxos: - message = f"No usable ADA UTxOs for address {src_address}." - raise exceptions.CLIError(message) - - target = amount_required + fee - if target <= 0: - # At minimum we must cover the fee - target = fee - - sorted_utxos = sorted(usable_utxos, key=lambda u: u.amount) - - selected: list[structs.UTXOData] = [] - running_total = 0 - - for utxo in sorted_utxos: - selected.append(utxo) - running_total += utxo.amount - if running_total >= target: - break - - if running_total < target: - message = ( - f"Insufficient ADA for transaction: required {target}, available {running_total}." - ) - raise exceptions.CLIError(message) - - return selected - - def gen_signed_tx( self, *, name: str, src_address: str, - txouts: list[structs.TxOut], + txouts: structs.OptionalTxOuts, tx_files: structs.TxFiles, fee: int, + txins: structs.OptionalUTXOData = (), + deposit: int | None = None, + treasury_donation: int | None = None, + src_addr_utxos: list[structs.UTXOData] | None = None, destination_dir: itp.FileType = ".", + join_txouts: bool = True, + script_valid: bool = True, ) -> structs.TxRawOutput: - """Generate a simple signed transaction for compatible eras. - """ - destination_dir_path = pl.Path(destination_dir).expanduser() - out_file = destination_dir_path / f"{name}_signed.tx" - clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) + """High-level helper to build and sign a compatible transaction. - # 1) Compute net deposit from certificates using the main (non-compatible) Tx group - deposit = 0 - if tx_files.certificate_files: - deposit = self._clusterlib_obj.g_transaction.get_tx_deposit(tx_files=tx_files) + It reuses txtools.collect_data_for_build to: + - select UTxOs (if `txins` not provided) + - compute deposits (stake key, pool, governance) + - balance outputs and change - # 2) Sum ADA outputs (ignore non-ADA coins for now) - ada_outputs = [t for t in txouts if t.coin == consts.DEFAULT_COIN] - total_out = sum(t.amount for t in ada_outputs) - amount_required = total_out + deposit + Then it calls gen_signed_tx_bare to run `signed-transaction` and wraps + everything into TxRawOutput for tests. + """ + destination_dir_path = pl.Path(destination_dir).expanduser() + effective_tx_files = tx_files or structs.TxFiles() - # 3) Select ADA UTxOs from the source address - selected_utxos = self._select_ada_utxos_naive( + collected = txtools.collect_data_for_build( + clusterlib_obj=self._clusterlib_obj, src_address=src_address, - amount_required=amount_required, + txins=txins, + txouts=list(txouts), + script_txins=(), + mint=(), + tx_files=effective_tx_files, + complex_certs=(), + complex_proposals=(), fee=fee, + withdrawals=(), + script_withdrawals=(), + deposit=deposit, + treasury_donation=treasury_donation or 0, + src_addr_utxos=src_addr_utxos, + skip_asset_balancing=False, ) - total_in = sum(u.amount for u in selected_utxos) - - # 4) Compute change (if any) back to the source address - change_amount = total_in - amount_required - fee - change_txouts: list[structs.TxOut] = [] - if change_amount > 0: - change_txouts.append( - structs.TxOut( - address=src_address, - amount=change_amount, - coin=consts.DEFAULT_COIN, - ) - ) - final_txouts = [*txouts, *change_txouts] - - # 5) Build `--tx-in` and `--tx-out` CLI args - txin_args: list[str] = [] - for utxo in selected_utxos: - txin_args.extend( - [ - "--tx-in", - f"{utxo.utxo_hash}#{utxo.utxo_ix}", - ] - ) + signed_tx_file = self.gen_signed_tx_bare( + name=name, + txins=collected.txins, + txouts=collected.txouts, + tx_files=effective_tx_files, + fee=fee, + destination_dir=destination_dir_path, + join_txouts=join_txouts, + ) - # Reuse existing joining logic so `txouts_count` etc. are consistent txout_args, processed_txouts, txouts_count = txtools._process_txouts( - txouts=final_txouts, - join_txouts=True, + txouts=collected.txouts, + join_txouts=join_txouts, + ) + txin_strings = txtools._get_txin_strings( + txins=collected.txins, + script_txins=(), ) - # Certificates (if any) - cert_args = helpers._prepend_flag("--certificate-file", tx_files.certificate_files) - - # Signing keys - skey_args = helpers._prepend_flag("--signing-key-file", tx_files.signing_key_files) - - # 6) Compose CLI args for `signed-transaction` - cli_args: list[str] = [ - *txin_args, + build_args = [ + *self._base, + "signed-transaction", + *helpers._prepend_flag("--tx-in", txin_strings), *txout_args, - *cert_args, - *skey_args, + *helpers._prepend_flag("--certificate-file", effective_tx_files.certificate_files), + *helpers._prepend_flag("--signing-key-file", effective_tx_files.signing_key_files), + *self._clusterlib_obj.magic_args, "--fee", str(fee), - *self._clusterlib_obj.magic_args, - ] - - # 7) Run the compat CLI and produce the signed tx file - cmd = [ - *self._base, - "signed-transaction", - *cli_args, "--out-file", - str(out_file), + str(signed_tx_file), ] - self._clusterlib_obj.cli(cmd, add_default_args=False) - helpers._check_outfiles(out_file) - - # 8) Return a TxRawOutput describing what we just did return structs.TxRawOutput( - txins=selected_utxos, + txins=list(collected.txins), txouts=processed_txouts, txouts_count=txouts_count, - tx_files=tx_files, - out_file=out_file, + tx_files=effective_tx_files, + out_file=signed_tx_file, fee=fee, - build_args=cmd, + build_args=build_args, era=self._clusterlib_obj.era_in_use, + script_txins=(), + script_withdrawals=(), + script_votes=(), + complex_certs=(), + complex_proposals=(), + mint=(), + invalid_hereafter=None, + invalid_before=None, + current_treasury_value=None, + treasury_donation=treasury_donation, + withdrawals=(), + change_address=src_address, + return_collateral_txouts=(), + total_collateral_amount=None, + readonly_reference_txins=(), + script_valid=script_valid, + required_signers=effective_tx_files.signing_key_files, + required_signer_hashes=(), + combined_reference_txins=(), ) def __repr__(self) -> str: From ee70dbaf874aced0845769bf93f74571f0caf551 Mon Sep 17 00:00:00 2001 From: femi Date: Tue, 9 Dec 2025 11:48:07 +0000 Subject: [PATCH 23/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 357f2e0..0a43056 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -19,6 +19,7 @@ class StakeAddressGroup: def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "stake-address") + self.era = era def _resolve_stake_key_args( self, @@ -147,7 +148,7 @@ def gen_delegation_cert( return out_file def __repr__(self) -> str: - return f"" + return f"" class StakePoolGroup: @@ -156,6 +157,7 @@ class StakePoolGroup: def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "stake-pool") + self.era = era def gen_registration_cert( self, @@ -271,6 +273,9 @@ def gen_dereg_cert( return out_file + def __repr__(self) -> str: + return f"" + class GovernanceActionGroup: """Governance action subcommands for compatible eras.""" @@ -278,6 +283,7 @@ class GovernanceActionGroup: def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "governance", "action") + self.era = era def gen_pparams_update( self, @@ -311,6 +317,9 @@ def gen_pparams_update( return out_file + def __repr__(self) -> str: + return f"" + class GovernanceGroup: """Generic governance group for all compatible eras.""" @@ -319,6 +328,7 @@ def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "governance") self.action = GovernanceActionGroup(clusterlib_obj, era) + self.era = era def _resolve_mir_direct_args( self, @@ -512,6 +522,9 @@ def gen_mir_cert( return out_file + def __repr__(self) -> str: + return f"" + class TransactionGroup: """Compatible transaction commands for legacy eras (Alonzo / Mary / Babbage). @@ -537,6 +550,7 @@ class TransactionGroup: def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "transaction") + self.era = era def gen_signed_tx_bare( self, @@ -702,4 +716,4 @@ def gen_signed_tx( ) def __repr__(self) -> str: - return f"" + return f"" From f3cffba8d161cd8ca451ca48852aab72098907ce Mon Sep 17 00:00:00 2001 From: femi Date: Tue, 9 Dec 2025 14:41:52 +0000 Subject: [PATCH 24/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 0a43056..5ccb4d3 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -530,7 +530,7 @@ class TransactionGroup: """Compatible transaction commands for legacy eras (Alonzo / Mary / Babbage). This is a lightweight wrapper around: - - txtools.collect_data_for_build -> UTxO selection, balancing, deposits + - txtools.collect_data_for_build (For Utxo selection balancing and deposits) - `cardano-cli compatible transaction signed-transaction` It is intentionally simpler than the Conway TransactionGroup: @@ -581,7 +581,7 @@ def gen_signed_tx_bare( txin_strings = txtools._get_txin_strings( txins=txins, - script_txins=(), + script_txins=(), # No script support in compatible mode ) cmd = [ @@ -618,6 +618,7 @@ def gen_signed_tx( src_addr_utxos: list[structs.UTXOData] | None = None, destination_dir: itp.FileType = ".", join_txouts: bool = True, + skip_asset_balancing: bool = False, script_valid: bool = True, ) -> structs.TxRawOutput: """High-level helper to build and sign a compatible transaction. @@ -649,7 +650,7 @@ def gen_signed_tx( deposit=deposit, treasury_donation=treasury_donation or 0, src_addr_utxos=src_addr_utxos, - skip_asset_balancing=False, + skip_asset_balancing=skip_asset_balancing, ) signed_tx_file = self.gen_signed_tx_bare( From df6dae6d3c7d2c9e43a1ad3641b4bd3171e0ac06 Mon Sep 17 00:00:00 2001 From: femi Date: Tue, 9 Dec 2025 23:22:36 +0000 Subject: [PATCH 25/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 216 +++++++++------------------- cardano_clusterlib/structs.py | 3 + 2 files changed, 69 insertions(+), 150 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 5ccb4d3..8a84dc9 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -1,7 +1,6 @@ """Generic reusable classes for cardano-cli `compatible` commands.""" import pathlib as pl -from typing import TYPE_CHECKING from cardano_clusterlib import clusterlib_helpers from cardano_clusterlib import helpers @@ -9,14 +8,11 @@ from cardano_clusterlib import txtools from cardano_clusterlib import types as itp -if TYPE_CHECKING: - from cardano_clusterlib.clusterlib_klass import ClusterLib - class StakeAddressGroup: """Compatible stake-address commands for Alonzo / Mary / Babbage eras.""" - def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: + def __init__(self, clusterlib_obj: "itp.ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "stake-address") self.era = era @@ -152,9 +148,9 @@ def __repr__(self) -> str: class StakePoolGroup: - """Generic stake-pool group for all compatible eras.""" + """Compatible stake-pool group for all compatible eras.""" - def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: + def __init__(self, clusterlib_obj: "itp.ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "stake-pool") self.era = era @@ -163,53 +159,55 @@ def gen_registration_cert( self, *, name: str, - pool_params: structs.CompatPoolParams, + pool_data: structs.PoolData, + cold_vkey_file: itp.FileType, + vrf_vkey_file: itp.FileType, + owner_stake_vkey_files: list[itp.FileType], + reward_account_vkey_file: itp.FileType | None = None, destination_dir: itp.FileType = ".", ) -> pl.Path: """Generate a compatible stake pool registration certificate.""" - pool_data = pool_params.pool_data - destination_dir_path = pl.Path(destination_dir).expanduser() out_file = destination_dir_path / f"{name}_pool_reg.cert" clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) - metadata_args: list[str] = [] - if pool_data.pool_metadata_url and pool_data.pool_metadata_hash: - metadata_args.extend( - [ - "--metadata-url", - str(pool_data.pool_metadata_url), - "--metadata-hash", - str(pool_data.pool_metadata_hash), - ] - ) - if pool_params.check_metadata_hash: - metadata_args.append("--check-metadata-hash") - relay_args: list[str] = [] + if pool_data.pool_relay_dns: relay_args.extend(["--single-host-pool-relay", pool_data.pool_relay_dns]) if pool_data.pool_relay_ipv4: relay_args.extend(["--pool-relay-ipv4", pool_data.pool_relay_ipv4]) - if pool_params.relay_ipv6: - relay_args.extend(["--pool-relay-ipv6", pool_params.relay_ipv6]) + if pool_data.pool_relay_ipv6: + relay_args.extend(["--pool-relay-ipv6", pool_data.pool_relay_ipv6]) if pool_data.pool_relay_port: relay_args.extend(["--pool-relay-port", str(pool_data.pool_relay_port)]) - if pool_params.multi_host_relay: - relay_args.extend(["--multi-host-pool-relay", pool_params.multi_host_relay]) + if pool_data.multi_host_relay: + relay_args.extend(["--multi-host-pool-relay", pool_data.multi_host_relay]) + + metadata_args: list[str] = [] + if pool_data.pool_metadata_url and pool_data.pool_metadata_hash: + metadata_args.extend( + [ + "--metadata-url", + str(pool_data.pool_metadata_url), + "--metadata-hash", + str(pool_data.pool_metadata_hash), + ] + ) + if pool_data.check_metadata_hash: + metadata_args.append("--check-metadata-hash") - # Reward account, default to first owner if not provided - if pool_params.reward_account_vkey_file: + if reward_account_vkey_file: reward_arg = [ "--pool-reward-account-verification-key-file", - str(pool_params.reward_account_vkey_file), + str(reward_account_vkey_file), ] else: - default_owner = next(iter(pool_params.owner_stake_vkey_files)) + default_owner = next(iter(owner_stake_vkey_files)) reward_arg = [ "--pool-reward-account-verification-key-file", str(default_owner), @@ -225,31 +223,31 @@ def gen_registration_cert( "--pool-margin", str(pool_data.pool_margin), "--vrf-verification-key-file", - str(pool_params.vrf_vkey_file), + str(vrf_vkey_file), "--cold-verification-key-file", - str(pool_params.cold_vkey_file), + str(cold_vkey_file), *reward_arg, *helpers._prepend_flag( "--pool-owner-stake-verification-key-file", - pool_params.owner_stake_vkey_files, + owner_stake_vkey_files, ), + *metadata_args, + *relay_args, *self._clusterlib_obj.magic_args, "--out-file", str(out_file), - *metadata_args, - *relay_args, ] self._clusterlib_obj.cli(cmd, add_default_args=False) helpers._check_outfiles(out_file) - return out_file def gen_dereg_cert( self, *, name: str, - params: structs.CompatPoolDeregParams, + cold_vkey_file: itp.FileType, + epoch: int, destination_dir: itp.FileType = ".", ) -> pl.Path: """Generate a compatible stake pool deregistration certificate.""" @@ -261,26 +259,22 @@ def gen_dereg_cert( *self._base, "deregistration-certificate", "--cold-verification-key-file", - str(params.cold_vkey_file), + str(cold_vkey_file), "--epoch", - str(params.epoch), + str(epoch), "--out-file", str(out_file), ] self._clusterlib_obj.cli(cmd, add_default_args=False) helpers._check_outfiles(out_file) - return out_file - def __repr__(self) -> str: - return f"" - class GovernanceActionGroup: """Governance action subcommands for compatible eras.""" - def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: + def __init__(self, clusterlib_obj: "itp.ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "governance", "action") self.era = era @@ -324,46 +318,12 @@ def __repr__(self) -> str: class GovernanceGroup: """Generic governance group for all compatible eras.""" - def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: + def __init__(self, clusterlib_obj: "itp.ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "governance") self.action = GovernanceActionGroup(clusterlib_obj, era) self.era = era - def _resolve_mir_direct_args( - self, - *, - reserves: bool, - treasury: bool, - stake_address: str | None, - reward: int | None, - ) -> list[str] | None: - """Resolve direct MIR args (no subcommand).""" - if not reserves and not treasury: - return None - - if reserves and treasury: - msg = "Cannot specify both `reserves` and `treasury` in MIR direct mode." - raise ValueError(msg) - - if not stake_address: - msg = "`stake_address` is required in MIR direct mode." - raise ValueError(msg) - - if reward is None: - msg = "`reward` is required in MIR direct mode." - raise ValueError(msg) - - pot_flag = "--reserves" if reserves else "--treasury" - - return [ - pot_flag, - "--stake-address", - stake_address, - "--reward", - str(reward), - ] - def _mir_stake_addresses_args( self, *, @@ -423,95 +383,52 @@ def _mir_transfer_to_rewards_args( str(transfer_amt), ] - def _resolve_mir_subcommand_args( - self, - *, - subcommand: str | None, - stake_address: str | None, - reward: int | None, - transfer_amt: int | None, - funds: str | None, - ) -> list[str] | None: - """Resolve MIR subcommand args.""" - if not subcommand: - return None - - if subcommand == "stake-addresses": - return self._mir_stake_addresses_args( - stake_address=stake_address, - reward=reward, - funds=funds, - ) - - if subcommand == "transfer-to-treasury": - return self._mir_transfer_to_treasury_args( - transfer_amt=transfer_amt, - ) - - if subcommand == "transfer-to-rewards": - return self._mir_transfer_to_rewards_args( - transfer_amt=transfer_amt, - ) - - msg = f"Unknown MIR subcommand: {subcommand}" - raise ValueError(msg) - def gen_mir_cert( self, *, name: str, - reserves: bool = False, - treasury: bool = False, - reward: int | None = None, + subcommand: str, stake_address: str | None = None, - subcommand: str | None = None, + reward: int | None = None, transfer_amt: int | None = None, funds: str | None = None, destination_dir: itp.FileType = ".", ) -> pl.Path: - """Generate MIR certificate for Babbage compatible eras.""" - destination_dir_path = pl.Path(destination_dir).expanduser() - - direct_args = self._resolve_mir_direct_args( - reserves=reserves, - treasury=treasury, - stake_address=stake_address, - reward=reward, - ) - - subcmd_args = self._resolve_mir_subcommand_args( - subcommand=subcommand, - stake_address=stake_address, - reward=reward, - transfer_amt=transfer_amt, - funds=funds, - ) + """Generate MIR certificate for compatible eras. - if direct_args and subcmd_args: - msg = "Cannot mix MIR direct mode with MIR subcommand mode." - raise ValueError(msg) - - if not direct_args and not subcmd_args: - msg = "No MIR mode selected." - raise ValueError(msg) - - final_args: list[str] = direct_args if direct_args is not None else subcmd_args # type: ignore + Supported subcommands: + - 'stake-addresses' + - 'transfer-to-treasury' + - 'transfer-to-rewards' + """ + destination_dir_path = pl.Path(destination_dir).expanduser() - if direct_args: - out_file = destination_dir_path / f"{name}_mir_direct.cert" - elif subcommand == "stake-addresses": + if subcommand == "stake-addresses": + cmd_args = self._mir_stake_addresses_args( + stake_address=stake_address, + reward=reward, + funds=funds, + ) out_file = destination_dir_path / f"{name}_mir_stake.cert" + elif subcommand == "transfer-to-treasury": + cmd_args = self._mir_transfer_to_treasury_args(transfer_amt=transfer_amt) out_file = destination_dir_path / f"{name}_mir_to_treasury.cert" - else: + + elif subcommand == "transfer-to-rewards": + cmd_args = self._mir_transfer_to_rewards_args(transfer_amt=transfer_amt) out_file = destination_dir_path / f"{name}_mir_to_rewards.cert" + else: + msg = f"Unsupported MIR subcommand: {subcommand}" + raise ValueError(msg) + clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) cmd = [ *self._base, "create-mir-certificate", - *final_args, + *cmd_args, *self._clusterlib_obj.magic_args, "--out-file", str(out_file), @@ -519,7 +436,6 @@ def gen_mir_cert( self._clusterlib_obj.cli(cmd, add_default_args=False) helpers._check_outfiles(out_file) - return out_file def __repr__(self) -> str: @@ -547,7 +463,7 @@ class TransactionGroup: - governance certificates (MIR, genesis delegation, protocol parameter updates) """ - def __init__(self, clusterlib_obj: "ClusterLib", era: str) -> None: + def __init__(self, clusterlib_obj: "itp.ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "transaction") self.era = era diff --git a/cardano_clusterlib/structs.py b/cardano_clusterlib/structs.py index e3159f2..038d365 100644 --- a/cardano_clusterlib/structs.py +++ b/cardano_clusterlib/structs.py @@ -229,7 +229,10 @@ class PoolData: pool_metadata_hash: str = "" pool_relay_dns: str = "" pool_relay_ipv4: str = "" + pool_relay_ipv6: str = "" pool_relay_port: int = 0 + multi_host_relay: str = "" + check_metadata_hash: bool = False @dataclasses.dataclass(frozen=True, order=True) From 2e14ec57541daff0cf2ef82cd551f3439d74fec9 Mon Sep 17 00:00:00 2001 From: femi Date: Wed, 10 Dec 2025 12:05:15 +0000 Subject: [PATCH 26/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 8a84dc9..1da76fc 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -201,17 +201,16 @@ def gen_registration_cert( if pool_data.check_metadata_hash: metadata_args.append("--check-metadata-hash") - if reward_account_vkey_file: - reward_arg = [ - "--pool-reward-account-verification-key-file", - str(reward_account_vkey_file), - ] - else: - default_owner = next(iter(owner_stake_vkey_files)) - reward_arg = [ - "--pool-reward-account-verification-key-file", - str(default_owner), - ] + reward_vkey_file = ( + reward_account_vkey_file + if reward_account_vkey_file + else next(iter(owner_stake_vkey_files)) + ) + + reward_arg = [ + "--pool-reward-account-verification-key-file", + str(reward_vkey_file), + ] cmd = [ *self._base, From 5ac318a9d50e13d9fc6e548575e3e8a111bb1e76 Mon Sep 17 00:00:00 2001 From: femi Date: Mon, 15 Dec 2025 11:17:40 +0000 Subject: [PATCH 27/28] Compatible group... Include group. --- cardano_clusterlib/compat_common.py | 140 +++++++++++----------------- 1 file changed, 52 insertions(+), 88 deletions(-) diff --git a/cardano_clusterlib/compat_common.py b/cardano_clusterlib/compat_common.py index 1da76fc..2d68d67 100644 --- a/cardano_clusterlib/compat_common.py +++ b/cardano_clusterlib/compat_common.py @@ -295,7 +295,6 @@ def gen_pparams_update( cmd = [ *self._base, "create-protocol-parameters-update", - *self._clusterlib_obj.magic_args, "--epoch", str(epoch), "--genesis-verification-key-file", @@ -315,120 +314,88 @@ def __repr__(self) -> str: class GovernanceGroup: - """Generic governance group for all compatible eras.""" - def __init__(self, clusterlib_obj: "itp.ClusterLib", era: str) -> None: self._clusterlib_obj = clusterlib_obj self._base = ("cardano-cli", "compatible", era, "governance") self.action = GovernanceActionGroup(clusterlib_obj, era) self.era = era - def _mir_stake_addresses_args( - self, - *, - stake_address: str | None, - reward: int | None, - funds: str | None, - ) -> list[str]: - """Args for MIR `stake-addresses` subcommand.""" - if not stake_address: - msg = "`stake_address` is required for MIR stake-addresses." - raise ValueError(msg) - - if reward is None: - msg = "`reward` is required for MIR stake-addresses." - raise ValueError(msg) - - if funds not in ("reserves", "treasury"): - msg = "`funds` must be either 'reserves' or 'treasury'." - raise ValueError(msg) - - return [ - "stake-addresses", - f"--{funds}", - "--stake-address", - stake_address, - "--reward", - str(reward), - ] - - def _mir_transfer_to_treasury_args( + def gen_mir_cert_to_treasury( self, *, - transfer_amt: int | None, - ) -> list[str]: - if transfer_amt is None: - msg = "`transfer_amt` is required for MIR transfer-to-treasury." - raise ValueError(msg) + name: str, + transfer: int, + destination_dir: itp.FileType = ".", + ) -> pl.Path: + """Create MIR certificate transferring from reserves pot to treasury pot.""" + destination_dir = pl.Path(destination_dir).expanduser() + out_file = destination_dir / f"{name}_mir_to_treasury.cert" + clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) - return [ + cmd = [ + *self._base, + "create-mir-certificate", "transfer-to-treasury", "--transfer", - str(transfer_amt), + str(transfer), + "--out-file", + str(out_file), ] - def _mir_transfer_to_rewards_args( + self._clusterlib_obj.cli(cmd, add_default_args=False) + helpers._check_outfiles(out_file) + return out_file + + def gen_mir_cert_to_rewards( self, *, - transfer_amt: int | None, - ) -> list[str]: - if transfer_amt is None: - msg = "`transfer_amt` is required for MIR transfer-to-rewards." - raise ValueError(msg) + name: str, + transfer: int, + destination_dir: itp.FileType = ".", + ) -> pl.Path: + """Create MIR certificate transferring from treasury pot to reserves pot.""" + destination_dir = pl.Path(destination_dir).expanduser() + out_file = destination_dir / f"{name}_mir_to_rewards.cert" + clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) - return [ + cmd = [ + *self._base, + "create-mir-certificate", "transfer-to-rewards", "--transfer", - str(transfer_amt), + str(transfer), + "--out-file", + str(out_file), ] - def gen_mir_cert( + self._clusterlib_obj.cli(cmd, add_default_args=False) + helpers._check_outfiles(out_file) + return out_file + + def gen_mir_cert_stake_addr( self, *, name: str, - subcommand: str, - stake_address: str | None = None, - reward: int | None = None, - transfer_amt: int | None = None, - funds: str | None = None, + stake_address: str, + reward: int, + use_treasury: bool = False, destination_dir: itp.FileType = ".", ) -> pl.Path: - """Generate MIR certificate for compatible eras. - - Supported subcommands: - - 'stake-addresses' - - 'transfer-to-treasury' - - 'transfer-to-rewards' - """ - destination_dir_path = pl.Path(destination_dir).expanduser() - - if subcommand == "stake-addresses": - cmd_args = self._mir_stake_addresses_args( - stake_address=stake_address, - reward=reward, - funds=funds, - ) - out_file = destination_dir_path / f"{name}_mir_stake.cert" - - elif subcommand == "transfer-to-treasury": - cmd_args = self._mir_transfer_to_treasury_args(transfer_amt=transfer_amt) - out_file = destination_dir_path / f"{name}_mir_to_treasury.cert" - - elif subcommand == "transfer-to-rewards": - cmd_args = self._mir_transfer_to_rewards_args(transfer_amt=transfer_amt) - out_file = destination_dir_path / f"{name}_mir_to_rewards.cert" - - else: - msg = f"Unsupported MIR subcommand: {subcommand}" - raise ValueError(msg) - + """Create MIR certificate paying a stake address.""" + destination_dir = pl.Path(destination_dir).expanduser() + funds_src = "treasury" if use_treasury else "reserves" + out_file = destination_dir / f"{name}_{funds_src}_mir_stake.cert" clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) cmd = [ *self._base, "create-mir-certificate", - *cmd_args, - *self._clusterlib_obj.magic_args, + "stake-addresses", + f"--{funds_src}", + "--stake-address", + stake_address, + "--reward", + str(reward), "--out-file", str(out_file), ] @@ -437,9 +404,6 @@ def gen_mir_cert( helpers._check_outfiles(out_file) return out_file - def __repr__(self) -> str: - return f"" - class TransactionGroup: """Compatible transaction commands for legacy eras (Alonzo / Mary / Babbage). From 96879b4671c123248a00291177e091456664b97d Mon Sep 17 00:00:00 2001 From: femi Date: Mon, 15 Dec 2025 11:22:55 +0000 Subject: [PATCH 28/28] Compatible group... Include group. --- cardano_clusterlib/structs.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/cardano_clusterlib/structs.py b/cardano_clusterlib/structs.py index 038d365..bbb3694 100644 --- a/cardano_clusterlib/structs.py +++ b/cardano_clusterlib/structs.py @@ -235,24 +235,6 @@ class PoolData: check_metadata_hash: bool = False -@dataclasses.dataclass(frozen=True, order=True) -class CompatPoolParams: - pool_data: PoolData - vrf_vkey_file: itp.FileType - cold_vkey_file: itp.FileType - owner_stake_vkey_files: itp.FileTypeList - reward_account_vkey_file: itp.FileType | None = None - relay_ipv6: str = "" - multi_host_relay: str = "" - check_metadata_hash: bool = False - - -@dataclasses.dataclass(frozen=True) -class CompatPoolDeregParams: - cold_vkey_file: itp.FileType - epoch: int - - @dataclasses.dataclass(frozen=True, order=True) class TxRawOutput: txins: list[UTXOData] # UTXOs used as inputs