diff --git a/pycardano/governance.py b/pycardano/governance.py index c217fc4f..edf2ffaa 100644 --- a/pycardano/governance.py +++ b/pycardano/governance.py @@ -416,13 +416,13 @@ class HardForkInitiationAction(CodedSerializable): gov_action_id: Optional[GovActionId] """Optional reference to a previous governance action""" - protocol_version: Fraction + protocol_version: Tuple[int, int] """The target protocol version as (major, minor) version numbers""" def __post_init__(self): major, minor = self.protocol_version - if not 1 <= major <= 10: - raise ValueError("Major protocol version must be between 1 and 10") + if not 0 <= major <= 12: + raise ValueError("Major protocol version must be between 0 and 12") class TreasuryWithdrawal(DictCBORSerializable): diff --git a/pycardano/serialization.py b/pycardano/serialization.py index 2422b3f3..47310fa8 100644 --- a/pycardano/serialization.py +++ b/pycardano/serialization.py @@ -836,6 +836,19 @@ def _restore_typed_primitive( raise DeserializeException(f"Expected type list but got {type(v)}") v_list = [_restore_typed_primitive(t_subtype, w) for w in v] return v.__class__(v_list) + elif hasattr(t, "__origin__") and (t.__origin__ is tuple): + t_args = t.__args__ + if not isinstance(v, (list, IndefiniteList)): + raise DeserializeException(f"Expected type list but got {type(v)}") + if len(t_args) == 2 and t_args[1] is Ellipsis: + return tuple(_restore_typed_primitive(t_args[0], w) for w in v) + if len(t_args) != len(v): + raise DeserializeException( + f"Tuple types need exactly {len(v)} type arguments, but got {t_args}" + ) + return tuple( + _restore_typed_primitive(t_subtype, w) for t_subtype, w in zip(t_args, v) + ) elif isclass(t) and t == ByteString: if not isinstance(v, bytes): raise DeserializeException(f"Expected type bytes but got {type(v)}") diff --git a/test/pycardano/test_governance.py b/test/pycardano/test_governance.py index 2f9b933f..a4bfa96e 100644 --- a/test/pycardano/test_governance.py +++ b/test/pycardano/test_governance.py @@ -373,12 +373,34 @@ def test_invalid_protocol_version(self): gov_action_id = GovActionId(transaction_id=tx_id, gov_action_index=1) with pytest.raises( - ValueError, match="Major protocol version must be between 1 and 10" + ValueError, match="Major protocol version must be between 0 and 12" ): HardForkInitiationAction( - gov_action_id=gov_action_id, protocol_version=(11, 0) + gov_action_id=gov_action_id, protocol_version=(13, 0) ) + def test_hard_fork_action_cbor_round_trip(self): + # The ledger encodes protocol_version as a [major, minor] array + # (Conway CDDL: protocol_version = [major_protocol_version, + # uint .size 4], major in 0..12). Protocol version 11 is the + # version live on mainnet, so it must be constructible. + action = HardForkInitiationAction( + gov_action_id=None, protocol_version=(11, 0) + ) + + assert action.to_cbor_hex() == "8301f6820b00" + assert HardForkInitiationAction.from_cbor(action.to_cbor()) == action + + def test_hard_fork_action_cbor_round_trip_with_gov_action_id(self): + tx_id = TransactionId(bytes.fromhex("ff" * TRANSACTION_HASH_SIZE)) + gov_action_id = GovActionId(transaction_id=tx_id, gov_action_index=65535) + + action = HardForkInitiationAction( + gov_action_id=gov_action_id, protocol_version=(10, 0) + ) + + assert HardForkInitiationAction.from_cbor(action.to_cbor()) == action + class TestUpdateCommittee: def test_update_committee_creation(self):