From ecf1b1a8a53f95099748d6c19d27934705a25e65 Mon Sep 17 00:00:00 2001 From: Jerry Date: Sun, 2 Feb 2025 10:33:08 -0800 Subject: [PATCH 1/4] Add account reward summary --- .gitignore | 1 + integration-test/test/base.py | 2 +- integration-test/test/test_certificate.py | 6 +++++- pycardano/backend/ogmios_v6.py | 5 +++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c14a0292..82095d0a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ coverage.xml .code /integration-test/.env /integration-test/tmp_configs/* +/integration-test/.coverage* \ No newline at end of file diff --git a/integration-test/test/base.py b/integration-test/test/base.py index 8e2dff3b..83c8f9ec 100644 --- a/integration-test/test/base.py +++ b/integration-test/test/base.py @@ -8,7 +8,7 @@ from pycardano import * -TEST_RETRIES = 8 +TEST_RETRIES = 1 @retry(tries=10, delay=4) diff --git a/integration-test/test/test_certificate.py b/integration-test/test/test_certificate.py index 3497932f..73d1c4d3 100644 --- a/integration-test/test/test_certificate.py +++ b/integration-test/test/test_certificate.py @@ -73,7 +73,11 @@ def test_stake_delegation(self): network=self.NETWORK, ) - builder.withdrawals = Withdrawals({bytes(stake_address): 0}) + rewards = self.chain_context.query_account_reward_summaries(keys=[stake_address.encode()]) + + stake_address_reward = rewards[stake_address.staking_part.payload.hex()]["rewards"]["ada"]["lovelace"] + + builder.withdrawals = Withdrawals({bytes(stake_address): stake_address_reward}) builder.add_output(TransactionOutput(address, 1000000)) diff --git a/pycardano/backend/ogmios_v6.py b/pycardano/backend/ogmios_v6.py index 5d3c6127..78999b48 100644 --- a/pycardano/backend/ogmios_v6.py +++ b/pycardano/backend/ogmios_v6.py @@ -307,6 +307,11 @@ def utxo_by_tx_id(self, tx_id: str, index: int) -> Optional[UTxO]: return self._utxo_from_ogmios_result(utxos[0]) return None + def query_account_reward_summaries(self, scripts: List[str]=None, keys: List[str]=None) -> List[dict]: + with OgmiosClient(self.host, self.port, self.path, self.secure) as client: + summaries, _ = client.query_reward_account_summaries.execute(scripts=scripts, keys=keys) + return summaries + def submit_tx_cbor(self, cbor: Union[bytes, str]): if isinstance(cbor, bytes): cbor = cbor.hex() From 6d689520551afdf3ba4facdf3b0915e69f0d01c5 Mon Sep 17 00:00:00 2001 From: Jerry Date: Thu, 13 Feb 2025 15:40:55 -0800 Subject: [PATCH 2/4] Fix certificates --- integration-test/test/base.py | 2 +- integration-test/test/test_certificate.py | 34 +++- pycardano/backend/ogmios_v6.py | 8 +- pycardano/certificate.py | 184 +++++++++++++++++++--- pycardano/hash.py | 8 + pycardano/txbuilder.py | 2 + test/pycardano/test_certificate.py | 38 ++--- 7 files changed, 224 insertions(+), 52 deletions(-) diff --git a/integration-test/test/base.py b/integration-test/test/base.py index 83c8f9ec..8e2dff3b 100644 --- a/integration-test/test/base.py +++ b/integration-test/test/base.py @@ -8,7 +8,7 @@ from pycardano import * -TEST_RETRIES = 1 +TEST_RETRIES = 8 @retry(tries=10, delay=4) diff --git a/integration-test/test/test_certificate.py b/integration-test/test/test_certificate.py index 73d1c4d3..43671e7e 100644 --- a/integration-test/test/test_certificate.py +++ b/integration-test/test/test_certificate.py @@ -42,14 +42,36 @@ def test_stake_delegation(self): ) stake_registration = StakeRegistration(stake_credential) pool_hash = PoolKeyHash(bytes.fromhex(os.environ.get("POOL_ID").strip())) - stake_delegation = StakeDelegation(stake_credential, pool_keyhash=pool_hash) + # stake_delegation = StakeDelegation(stake_credential, pool_keyhash=pool_hash) + + drep = DRep( + DRepKind.VERIFICATION_KEY_HASH, + self.stake_key_pair.verification_key.hash(), + ) + + drep_credential = DRepCredential( + self.stake_key_pair.verification_key.hash() + ) + + anchor = Anchor( + url="https://drep.com", + data_hash=AnchorDataHash(bytes.fromhex("0" * 64)), + ) + + drep_registration = RegDRepCert( + drep_credential=drep_credential, coin=500000000, anchor=anchor + ) + + all_in_one_cert = StakeRegistrationAndDelegationAndVoteDelegation( + stake_credential, pool_hash, drep, 1000000 + ) builder = TransactionBuilder(self.chain_context) builder.add_input_address(address) builder.add_output(TransactionOutput(address, 35000000)) - builder.certificates = [stake_registration, stake_delegation] + builder.certificates = [drep_registration, all_in_one_cert] signed_tx = builder.build_and_sign( [self.stake_key_pair.signing_key, self.payment_key_pair.signing_key], @@ -73,9 +95,13 @@ def test_stake_delegation(self): network=self.NETWORK, ) - rewards = self.chain_context.query_account_reward_summaries(keys=[stake_address.encode()]) + rewards = self.chain_context.query_account_reward_summaries( + keys=[stake_address.encode()] + ) - stake_address_reward = rewards[stake_address.staking_part.payload.hex()]["rewards"]["ada"]["lovelace"] + stake_address_reward = rewards[stake_address.staking_part.payload.hex()][ + "rewards" + ]["ada"]["lovelace"] builder.withdrawals = Withdrawals({bytes(stake_address): stake_address_reward}) diff --git a/pycardano/backend/ogmios_v6.py b/pycardano/backend/ogmios_v6.py index 78999b48..2fca989e 100644 --- a/pycardano/backend/ogmios_v6.py +++ b/pycardano/backend/ogmios_v6.py @@ -307,9 +307,13 @@ def utxo_by_tx_id(self, tx_id: str, index: int) -> Optional[UTxO]: return self._utxo_from_ogmios_result(utxos[0]) return None - def query_account_reward_summaries(self, scripts: List[str]=None, keys: List[str]=None) -> List[dict]: + def query_account_reward_summaries( + self, scripts: Optional[List[str]] = None, keys: Optional[List[str]] = None + ) -> List[dict]: with OgmiosClient(self.host, self.port, self.path, self.secure) as client: - summaries, _ = client.query_reward_account_summaries.execute(scripts=scripts, keys=keys) + summaries, _ = client.query_reward_account_summaries.execute( + scripts=scripts, keys=keys + ) return summaries def submit_tx_cbor(self, cbor: Union[bytes, str]): diff --git a/pycardano/certificate.py b/pycardano/certificate.py index 63ca6091..3606d281 100644 --- a/pycardano/certificate.py +++ b/pycardano/certificate.py @@ -5,7 +5,7 @@ from typing import Optional, Tuple, Type, Union from pycardano.exception import DeserializeException -from pycardano.hash import PoolKeyHash, ScriptHash, VerificationKeyHash +from pycardano.hash import AnchorDataHash, PoolKeyHash, ScriptHash, VerificationKeyHash from pycardano.serialization import ArrayCBORSerializable, limit_primitive_type __all__ = [ @@ -28,9 +28,10 @@ "AuthCommitteeHotCertificate", "ResignCommitteeColdCertificate", "Anchor", - "DrepCredential", - "UnregDrepCertificate", - "UpdateDrepCertificate", + "DRepCredential", + "RegDRepCert", + "UnregDRepCertificate", + "UpdateDRepCertificate", ] from pycardano.pool_params import PoolParams @@ -40,8 +41,13 @@ @dataclass(repr=False) class Anchor(ArrayCBORSerializable): + """Represents an anchor in the Cardano blockchain that contains a URL and associated data hash. + + Anchors are used to provide additional metadata or reference external resources in certificates. + """ + url: str - data_hash: bytes + data_hash: AnchorDataHash @classmethod @limit_primitive_type(list) @@ -51,6 +57,12 @@ def from_primitive(cls: Type[Anchor], values: Union[list, tuple]) -> Anchor: @dataclass(repr=False) class StakeCredential(ArrayCBORSerializable): + """Represents a stake credential in the Cardano blockchain. + + A stake credential can either be a verification key hash or a script hash, + used to identify stake rights and permissions. + """ + _CODE: Optional[int] = field(init=False, default=None) credential: Union[VerificationKeyHash, ScriptHash] @@ -76,6 +88,12 @@ def from_primitive( @dataclass(repr=False) class StakeRegistration(ArrayCBORSerializable): + """Represents a stake address registration certificate in the Cardano blockchain. + + This certificate is used to register a stake address on the blockchain, + enabling participation in staking and delegation. + """ + _CODE: int = field(init=False, default=0) stake_credential: StakeCredential @@ -96,6 +114,12 @@ def from_primitive( @dataclass(repr=False) class StakeDeregistration(ArrayCBORSerializable): + """Represents a stake address deregistration certificate in the Cardano blockchain. + + This certificate is used to deregister a stake address, withdrawing it from + participation in staking and delegation. + """ + _CODE: int = field(init=False, default=1) stake_credential: StakeCredential @@ -116,6 +140,12 @@ def from_primitive( @dataclass(repr=False) class StakeDelegation(ArrayCBORSerializable): + """Represents a stake delegation certificate in the Cardano blockchain. + + This certificate is used to delegate stake to a specific stake pool, + allowing participation in the proof-of-stake protocol. + """ + _CODE: int = field(init=False, default=2) stake_credential: StakeCredential @@ -141,6 +171,12 @@ def from_primitive( @dataclass(repr=False) class PoolRegistration(ArrayCBORSerializable): + """Represents a stake pool registration certificate in the Cardano blockchain. + + This certificate is used to register a new stake pool on the network, + including all its operational parameters and metadata. + """ + _CODE: int = field(init=False, default=3) pool_params: PoolParams @@ -174,6 +210,12 @@ def from_primitive( @dataclass(repr=False) class PoolRetirement(ArrayCBORSerializable): + """Represents a stake pool retirement certificate in the Cardano blockchain. + + This certificate is used to signal that a stake pool will cease operations + at a specified epoch. + """ + _CODE: int = field(init=False, default=4) pool_keyhash: PoolKeyHash @@ -197,6 +239,12 @@ def from_primitive( @dataclass(repr=False) class StakeRegistrationConway(ArrayCBORSerializable): + """Represents a stake registration certificate in the Conway era of Cardano. + + This certificate is used to register stake rights with an associated deposit + amount in the Conway era. + """ + _CODE: int = field(init=False, default=7) stake_credential: StakeCredential @@ -223,6 +271,12 @@ def from_primitive( @dataclass(repr=False) class StakeDeregistrationConway(ArrayCBORSerializable): + """Represents a stake deregistration certificate in the Conway era of Cardano. + + This certificate is used to deregister stake rights and reclaim the deposit + in the Conway era. + """ + _CODE: int = field(init=False, default=8) stake_credential: StakeCredential @@ -249,6 +303,11 @@ def from_primitive( @dataclass(repr=False) class VoteDelegation(ArrayCBORSerializable): + """Represents a vote delegation certificate in the Conway era of Cardano. + + This certificate is used to delegate voting rights to a DRep (Delegate Representative). + """ + _CODE: int = field(init=False, default=9) stake_credential: StakeCredential @@ -273,6 +332,12 @@ def from_primitive( @dataclass(repr=False) class StakeAndVoteDelegation(ArrayCBORSerializable): + """Represents a combined stake and vote delegation certificate. + + This certificate allows simultaneous delegation of both staking rights to a pool + and voting rights to a DRep. + """ + _CODE: int = field(init=False, default=10) stake_credential: StakeCredential @@ -301,6 +366,12 @@ def from_primitive( @dataclass(repr=False) class StakeRegistrationAndDelegation(ArrayCBORSerializable): + """Represents a combined stake registration and delegation certificate. + + This certificate allows simultaneous registration of stake rights and + delegation to a stake pool. + """ + _CODE: int = field(init=False, default=11) stake_credential: StakeCredential @@ -327,6 +398,12 @@ def from_primitive( @dataclass(repr=False) class StakeRegistrationAndVoteDelegation(ArrayCBORSerializable): + """Represents a combined stake registration and vote delegation certificate. + + This certificate allows simultaneous registration of stake rights and + delegation of voting rights to a DRep. + """ + _CODE: int = field(init=False, default=12) stake_credential: StakeCredential @@ -353,6 +430,12 @@ def from_primitive( @dataclass(repr=False) class StakeRegistrationAndDelegationAndVoteDelegation(ArrayCBORSerializable): + """Represents a certificate combining stake registration, stake delegation, and vote delegation. + + This certificate allows simultaneous registration of stake rights, delegation to a + stake pool, and delegation of voting rights to a DRep. + """ + _CODE: int = field(init=False, default=13) stake_credential: StakeCredential @@ -380,8 +463,28 @@ def from_primitive( raise DeserializeException(f"Invalid {cls.__name__} type {values[0]}") +@dataclass(repr=False) +class DRepCredential(StakeCredential): + """Represents a Delegate Representative (DRep) credential. + + This credential type is specifically used for DReps in the governance system, + inheriting from StakeCredential. + """ + + pass + + @unique class DRepKind(Enum): + """Enumerates the different types of Delegate Representatives (DReps). + + Defines the possible kinds of DReps in the Cardano governance system: + - VERIFICATION_KEY_HASH: A DRep identified by a verification key hash + - SCRIPT_HASH: A DRep identified by a script hash + - ALWAYS_ABSTAIN: A special DRep that always abstains from voting + - ALWAYS_NO_CONFIDENCE: A special DRep that always votes no confidence + """ + VERIFICATION_KEY_HASH = 0 SCRIPT_HASH = 1 ALWAYS_ABSTAIN = 2 @@ -390,6 +493,11 @@ class DRepKind(Enum): @dataclass(repr=False) class DRep(ArrayCBORSerializable): + """Represents a Delegate Representative (DRep) in the Cardano governance system. + + DReps are entities that can represent stake holders in governance decisions. + """ + kind: DRepKind credential: Optional[Union[VerificationKeyHash, ScriptHash]] = field( default=None, metadata={"optional": True} @@ -422,6 +530,11 @@ def to_primitive(self): @dataclass(repr=False) class AuthCommitteeHotCertificate(ArrayCBORSerializable): + """Represents an authorization certificate for a Constitutional Committee hot key. + + This certificate authorizes a hot key for use by a Constitutional Committee member. + """ + _CODE: int = field(init=False, default=14) committee_cold_credential: StakeCredential @@ -448,6 +561,11 @@ def from_primitive( @dataclass(repr=False) class ResignCommitteeColdCertificate(ArrayCBORSerializable): + """Represents a resignation certificate for a Constitutional Committee member. + + This certificate is used when a committee member wishes to resign their position. + """ + _CODE: int = field(init=False, default=15) committee_cold_credential: StakeCredential @@ -475,12 +593,16 @@ def from_primitive( @dataclass(repr=False) -class DrepCredential(ArrayCBORSerializable): - """DRep credential is identical to StakeCredential in structure.""" +class RegDRepCert(ArrayCBORSerializable): + """Represents a certificate for registering a new Delegate Representative (DRep). + + This certificate is used to register a new DRep in the governance system with an + associated deposit amount and optional metadata. + """ _CODE: int = field(init=False, default=16) - drep_credential: StakeCredential + drep_credential: DRepCredential coin: int anchor: Optional[Anchor] = field(default=None) @@ -490,25 +612,30 @@ def __post_init__(self): @classmethod @limit_primitive_type(list) def from_primitive( - cls: Type[DrepCredential], values: Union[list, tuple] - ) -> DrepCredential: + cls: Type[RegDRepCert], values: Union[list, tuple] + ) -> RegDRepCert: if values[0] == 16: return cls( - drep_credential=StakeCredential.from_primitive(values[1]), + drep_credential=DRepCredential.from_primitive(values[1]), coin=values[2], anchor=( Anchor.from_primitive(values[3]) if values[3] is not None else None ), ) else: - raise DeserializeException(f"Invalid DrepCredential type {values[0]}") + raise DeserializeException(f"Invalid RegDRepCert type {values[0]}") @dataclass(repr=False) -class UnregDrepCertificate(ArrayCBORSerializable): +class UnregDRepCertificate(ArrayCBORSerializable): + """Represents a certificate for unregistering a Delegate Representative (DRep). + + This certificate is used to remove a DRep from the governance system. + """ + _CODE: int = field(init=False, default=17) - drep_credential: DrepCredential + drep_credential: DRepCredential coin: int def __post_init__(self): @@ -517,22 +644,27 @@ def __post_init__(self): @classmethod @limit_primitive_type(list) def from_primitive( - cls: Type[UnregDrepCertificate], values: Union[list, tuple] - ) -> UnregDrepCertificate: + cls: Type[UnregDRepCertificate], values: Union[list, tuple] + ) -> UnregDRepCertificate: if values[0] == 17: return cls( - drep_credential=DrepCredential.from_primitive(values[1]), + drep_credential=DRepCredential.from_primitive(values[1]), coin=values[2], ) else: - raise DeserializeException(f"Invalid UnregDrepCertificate type {values[0]}") + raise DeserializeException(f"Invalid UnregDRepCertificate type {values[0]}") @dataclass(repr=False) -class UpdateDrepCertificate(ArrayCBORSerializable): +class UpdateDRepCertificate(ArrayCBORSerializable): + """Represents a certificate for updating a Delegate Representative (DRep)'s metadata. + + This certificate is used to modify the metadata associated with a DRep. + """ + _CODE: int = field(init=False, default=18) - drep_credential: DrepCredential + drep_credential: DRepCredential anchor: Optional[Anchor] def __post_init__(self): @@ -541,18 +673,18 @@ def __post_init__(self): @classmethod @limit_primitive_type(list) def from_primitive( - cls: Type[UpdateDrepCertificate], values: Union[list, tuple] - ) -> UpdateDrepCertificate: + cls: Type[UpdateDRepCertificate], values: Union[list, tuple] + ) -> UpdateDRepCertificate: if values[0] == 18: return cls( - drep_credential=DrepCredential.from_primitive(values[1]), + drep_credential=DRepCredential.from_primitive(values[1]), anchor=( Anchor.from_primitive(values[2]) if values[2] is not None else None ), ) else: raise DeserializeException( - f"Invalid UpdateDrepCertificate type {values[0]}" + f"Invalid UpdateDRepCertificate type {values[0]}" ) @@ -571,6 +703,6 @@ def from_primitive( StakeRegistrationAndDelegationAndVoteDelegation, AuthCommitteeHotCertificate, ResignCommitteeColdCertificate, - UnregDrepCertificate, - UpdateDrepCertificate, + UnregDRepCertificate, + UpdateDRepCertificate, ] diff --git a/pycardano/hash.py b/pycardano/hash.py index e4023b8a..000beabc 100644 --- a/pycardano/hash.py +++ b/pycardano/hash.py @@ -26,6 +26,7 @@ "PoolMetadataHash", "VrfKeyHash", "RewardAccountHash", + "AnchorDataHash", ] VERIFICATION_KEY_HASH_SIZE = 28 @@ -38,6 +39,7 @@ POOL_METADATA_HASH_SIZE = 32 VRF_KEY_HASH_SIZE = 32 REWARD_ACCOUNT_HASH_SIZE = 29 +ANCHOR_DATA_HASH_SIZE = 32 T = TypeVar("T", bound="ConstrainedBytes") @@ -155,3 +157,9 @@ class RewardAccountHash(ConstrainedBytes): """Hash of a Cardano VRF key.""" MAX_SIZE = MIN_SIZE = REWARD_ACCOUNT_HASH_SIZE + + +class AnchorDataHash(ConstrainedBytes): + """Hash of anchor data.""" + + MAX_SIZE = MIN_SIZE = ANCHOR_DATA_HASH_SIZE diff --git a/pycardano/txbuilder.py b/pycardano/txbuilder.py index e631b0e4..960ae819 100644 --- a/pycardano/txbuilder.py +++ b/pycardano/txbuilder.py @@ -11,6 +11,7 @@ Certificate, PoolRegistration, PoolRetirement, + RegDRepCert, StakeAndVoteDelegation, StakeCredential, StakeDelegation, @@ -897,6 +898,7 @@ def _get_total_key_deposit(self): elif isinstance( cert, ( + RegDRepCert, StakeRegistrationConway, StakeRegistrationAndDelegation, StakeRegistrationAndVoteDelegation, diff --git a/test/pycardano/test_certificate.py b/test/pycardano/test_certificate.py index f3a42f21..1dd2a755 100644 --- a/test/pycardano/test_certificate.py +++ b/test/pycardano/test_certificate.py @@ -7,7 +7,7 @@ from pycardano.certificate import ( Anchor, AuthCommitteeHotCertificate, - DrepCredential, + DRepCredential, PoolRegistration, PoolRetirement, ResignCommitteeColdCertificate, @@ -15,8 +15,8 @@ StakeDelegation, StakeDeregistration, StakeRegistration, - UnregDrepCertificate, - UpdateDrepCertificate, + UnregDRepCertificate, + UpdateDRepCertificate, ) from pycardano.exception import DeserializeException, InvalidArgumentException from pycardano.hash import ( # plutus_script_hash, @@ -186,20 +186,20 @@ def test_anchor(): def test_drep_credential(): stake_credential = StakeCredential(TEST_ADDR.staking_part) - drep_credential = DrepCredential(stake_credential, coin=0) + drep_credential = DRepCredential(stake_credential, coin=0) drep_credential_cbor_hex = drep_credential.to_cbor_hex() assert ( drep_credential_cbor_hex == "84108200581c4828a2dadba97ca9fd0cdc99975899470c219bdc0d828cfa6ddf6d6900f6" ) - assert DrepCredential.from_cbor(drep_credential_cbor_hex) == drep_credential + assert DRepCredential.from_cbor(drep_credential_cbor_hex) == drep_credential def test_unreg_drep_certificate(): stake_credential = StakeCredential(TEST_ADDR.staking_part) - drep_credential = DrepCredential(stake_credential, coin=0) + drep_credential = DRepCredential(stake_credential, coin=0) coin = 1000000 - unreg_drep_cert = UnregDrepCertificate(drep_credential=drep_credential, coin=coin) + unreg_drep_cert = UnregDRepCertificate(drep_credential=drep_credential, coin=coin) unreg_drep_cert_cbor_hex = unreg_drep_cert.to_cbor_hex() @@ -208,16 +208,16 @@ def test_unreg_drep_certificate(): == "831184108200581c4828a2dadba97ca9fd0cdc99975899470c219bdc0d828cfa6ddf6d6900f61a000f4240" ) - assert UnregDrepCertificate.from_cbor(unreg_drep_cert_cbor_hex) == unreg_drep_cert + assert UnregDRepCertificate.from_cbor(unreg_drep_cert_cbor_hex) == unreg_drep_cert def test_update_drep_certificate_with_anchor(): stake_credential = StakeCredential(TEST_ADDR.staking_part) - drep_credential = DrepCredential(stake_credential, coin=0) + drep_credential = DRepCredential(stake_credential, coin=0) url = "https://pycardano.com" data_hash = bytes.fromhex("1234567890" * 6 + "12") # 32 bytes anchor = Anchor(url=url, data_hash=data_hash) - update_drep_cert = UpdateDrepCertificate( + update_drep_cert = UpdateDRepCertificate( drep_credential=drep_credential, anchor=anchor ) @@ -229,14 +229,14 @@ def test_update_drep_certificate_with_anchor(): ) assert ( - UpdateDrepCertificate.from_cbor(update_drep_cert_cbor_hex) == update_drep_cert + UpdateDRepCertificate.from_cbor(update_drep_cert_cbor_hex) == update_drep_cert ) def test_update_drep_certificate_without_anchor(): stake_credential = StakeCredential(TEST_ADDR.staking_part) - drep_credential = DrepCredential(stake_credential, coin=0) - update_drep_cert = UpdateDrepCertificate( + drep_credential = DRepCredential(stake_credential, coin=0) + update_drep_cert = UpdateDRepCertificate( drep_credential=drep_credential, anchor=None ) update_drep_cert_cbor_hex = update_drep_cert.to_cbor_hex() @@ -247,7 +247,7 @@ def test_update_drep_certificate_without_anchor(): ) assert ( - UpdateDrepCertificate.from_cbor(update_drep_cert_cbor_hex) == update_drep_cert + UpdateDRepCertificate.from_cbor(update_drep_cert_cbor_hex) == update_drep_cert ) @@ -354,15 +354,15 @@ def test_invalid_certificate_types(): assert "Invalid ResignCommitteeColdCertificate type 14" in str(excinfo.value) stake_credential = StakeCredential(TEST_ADDR.staking_part) - drep_credential = DrepCredential(stake_credential, coin=0) + drep_credential = DRepCredential(stake_credential, coin=0) with pytest.raises(DeserializeException) as excinfo: - UnregDrepCertificate.from_primitive([18, drep_credential, 1000000]) - assert "Invalid UnregDrepCertificate type 18" in str(excinfo.value) + UnregDRepCertificate.from_primitive([18, drep_credential, 1000000]) + assert "Invalid UnregDRepCertificate type 18" in str(excinfo.value) with pytest.raises(DeserializeException) as excinfo: - UpdateDrepCertificate.from_primitive([17, drep_credential, None]) - assert "Invalid UpdateDrepCertificate type 17" in str(excinfo.value) + UpdateDRepCertificate.from_primitive([17, drep_credential, None]) + assert "Invalid UpdateDRepCertificate type 17" in str(excinfo.value) def test_certificate_in_transaction(): From 0a81cc530c4d9a3a9d43dc047999a84795d36b47 Mon Sep 17 00:00:00 2001 From: Jerry Date: Thu, 13 Feb 2025 16:15:32 -0800 Subject: [PATCH 3/4] Increase epoch length --- integration-test/configs/local-chang/config.json | 4 ++-- integration-test/configs/local-chang/shelley-genesis.json | 2 +- integration-test/docker-compose-chang.yml | 2 +- integration-test/test/test_certificate.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/integration-test/configs/local-chang/config.json b/integration-test/configs/local-chang/config.json index d5f44b21..17f5ec71 100644 --- a/integration-test/configs/local-chang/config.json +++ b/integration-test/configs/local-chang/config.json @@ -99,8 +99,8 @@ "TestAllegraHardForkAtEpoch": 0, "TestMaryHardForkAtEpoch": 0, "TestAlonzoHardForkAtEpoch": 0, - "TestBabbageHardForkAtEpoch": 10, - "TestConwayHardForkAtEpoch": 11, + "TestBabbageHardForkAtEpoch": 4, + "TestConwayHardForkAtEpoch": 5, "EnableDevelopmentHardForkEras": true, "ExperimentalProtocolsEnabled": true } \ No newline at end of file diff --git a/integration-test/configs/local-chang/shelley-genesis.json b/integration-test/configs/local-chang/shelley-genesis.json index 6c5a3cc6..46b263b3 100644 --- a/integration-test/configs/local-chang/shelley-genesis.json +++ b/integration-test/configs/local-chang/shelley-genesis.json @@ -46,5 +46,5 @@ "pools": {}, "stake": {} }, - "epochLength": 4 + "epochLength": 10 } \ No newline at end of file diff --git a/integration-test/docker-compose-chang.yml b/integration-test/docker-compose-chang.yml index c9538d77..df9ac91a 100644 --- a/integration-test/docker-compose-chang.yml +++ b/integration-test/docker-compose-chang.yml @@ -56,7 +56,7 @@ services: max-file: "10" ogmios: - image: cardanosolutions/ogmios:v6.11.0 + image: cardanosolutions/ogmios:v6.11.2 platform: linux/amd64 environment: NETWORK: "${NETWORK:-local-alonzo}" diff --git a/integration-test/test/test_certificate.py b/integration-test/test/test_certificate.py index 43671e7e..1e2acdec 100644 --- a/integration-test/test/test_certificate.py +++ b/integration-test/test/test_certificate.py @@ -84,7 +84,7 @@ def test_stake_delegation(self): print("############### Submitting transaction ###############") self.chain_context.submit_tx(signed_tx) - time.sleep(8) + time.sleep(120) builder = TransactionBuilder(self.chain_context) From f69ddd5d467a17bc4c20bdfc8154e7fe4aabd492 Mon Sep 17 00:00:00 2001 From: tacmota Date: Thu, 13 Feb 2025 21:20:22 -0800 Subject: [PATCH 4/4] cert test bugs fix --- integration-test/test/test_certificate.py | 2 +- pycardano/certificate.py | 5 --- test/pycardano/test_certificate.py | 50 +++++++++++++---------- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/integration-test/test/test_certificate.py b/integration-test/test/test_certificate.py index 1e2acdec..b7196a0e 100644 --- a/integration-test/test/test_certificate.py +++ b/integration-test/test/test_certificate.py @@ -55,7 +55,7 @@ def test_stake_delegation(self): anchor = Anchor( url="https://drep.com", - data_hash=AnchorDataHash(bytes.fromhex("0" * 64)), + data_hash=AnchorDataHash((bytes.fromhex("0" * 64))), ) drep_registration = RegDRepCert( diff --git a/pycardano/certificate.py b/pycardano/certificate.py index 3606d281..efbf1137 100644 --- a/pycardano/certificate.py +++ b/pycardano/certificate.py @@ -49,11 +49,6 @@ class Anchor(ArrayCBORSerializable): url: str data_hash: AnchorDataHash - @classmethod - @limit_primitive_type(list) - def from_primitive(cls: Type[Anchor], values: Union[list, tuple]) -> Anchor: - return cls(url=values[0], data_hash=values[1]) - @dataclass(repr=False) class StakeCredential(ArrayCBORSerializable): diff --git a/test/pycardano/test_certificate.py b/test/pycardano/test_certificate.py index 1dd2a755..cd14e9e1 100644 --- a/test/pycardano/test_certificate.py +++ b/test/pycardano/test_certificate.py @@ -2,7 +2,7 @@ import pytest -from pycardano import StakeSigningKey, TransactionBody +from pycardano import AnchorDataHash, StakeSigningKey, TransactionBody from pycardano.address import Address from pycardano.certificate import ( Anchor, @@ -171,33 +171,37 @@ def test_staking_certificate_serdes(): def test_anchor(): url = "https://example.com" - data_hash = bytes.fromhex("1234567890" * 6 + "12") # 32 bytes + data_hash = AnchorDataHash(bytes.fromhex("0" * 64)) # 32 bytes anchor = Anchor(url=url, data_hash=data_hash) anchor_cbor_hex = anchor.to_cbor_hex() assert ( anchor_cbor_hex - == "827368747470733a2f2f6578616d706c652e636f6d581f12345678901234567890123456789012345678901234567890123456789012" + == "827368747470733a2f2f6578616d706c652e636f6d58200000000000000000000000000000000000000000000000000000000000000000" ) assert Anchor.from_cbor(anchor_cbor_hex) == anchor def test_drep_credential(): - stake_credential = StakeCredential(TEST_ADDR.staking_part) - drep_credential = DRepCredential(stake_credential, coin=0) + staking_key = StakeSigningKey.from_cbor( + "5820ff3a330df8859e4e5f42a97fcaee73f6a00d0cf864f4bca902bd106d423f02c0" + ) + drep_credential = DRepCredential(staking_key.to_verification_key().hash()) drep_credential_cbor_hex = drep_credential.to_cbor_hex() assert ( drep_credential_cbor_hex - == "84108200581c4828a2dadba97ca9fd0cdc99975899470c219bdc0d828cfa6ddf6d6900f6" + == "8200581c4828a2dadba97ca9fd0cdc99975899470c219bdc0d828cfa6ddf6d69" ) assert DRepCredential.from_cbor(drep_credential_cbor_hex) == drep_credential def test_unreg_drep_certificate(): - stake_credential = StakeCredential(TEST_ADDR.staking_part) - drep_credential = DRepCredential(stake_credential, coin=0) + staking_key = StakeSigningKey.from_cbor( + "5820ff3a330df8859e4e5f42a97fcaee73f6a00d0cf864f4bca902bd106d423f02c0" + ) + drep_credential = DRepCredential(staking_key.to_verification_key().hash()) coin = 1000000 unreg_drep_cert = UnregDRepCertificate(drep_credential=drep_credential, coin=coin) @@ -205,17 +209,19 @@ def test_unreg_drep_certificate(): assert ( unreg_drep_cert_cbor_hex - == "831184108200581c4828a2dadba97ca9fd0cdc99975899470c219bdc0d828cfa6ddf6d6900f61a000f4240" + == "83118200581c4828a2dadba97ca9fd0cdc99975899470c219bdc0d828cfa6ddf6d691a000f4240" ) assert UnregDRepCertificate.from_cbor(unreg_drep_cert_cbor_hex) == unreg_drep_cert def test_update_drep_certificate_with_anchor(): - stake_credential = StakeCredential(TEST_ADDR.staking_part) - drep_credential = DRepCredential(stake_credential, coin=0) + staking_key = StakeSigningKey.from_cbor( + "5820ff3a330df8859e4e5f42a97fcaee73f6a00d0cf864f4bca902bd106d423f02c0" + ) + drep_credential = DRepCredential(staking_key.to_verification_key().hash()) url = "https://pycardano.com" - data_hash = bytes.fromhex("1234567890" * 6 + "12") # 32 bytes + data_hash = AnchorDataHash(bytes.fromhex("0" * 64)) # 32 bytes anchor = Anchor(url=url, data_hash=data_hash) update_drep_cert = UpdateDRepCertificate( drep_credential=drep_credential, anchor=anchor @@ -225,7 +231,7 @@ def test_update_drep_certificate_with_anchor(): assert ( update_drep_cert_cbor_hex - == "831284108200581c4828a2dadba97ca9fd0cdc99975899470c219bdc0d828cfa6ddf6d6900f6827568747470733a2f2f707963617264616e6f2e636f6d581f12345678901234567890123456789012345678901234567890123456789012" + == "83128200581c4828a2dadba97ca9fd0cdc99975899470c219bdc0d828cfa6ddf6d69827568747470733a2f2f707963617264616e6f2e636f6d58200000000000000000000000000000000000000000000000000000000000000000" ) assert ( @@ -234,8 +240,10 @@ def test_update_drep_certificate_with_anchor(): def test_update_drep_certificate_without_anchor(): - stake_credential = StakeCredential(TEST_ADDR.staking_part) - drep_credential = DRepCredential(stake_credential, coin=0) + staking_key = StakeSigningKey.from_cbor( + "5820ff3a330df8859e4e5f42a97fcaee73f6a00d0cf864f4bca902bd106d423f02c0" + ) + drep_credential = DRepCredential(staking_key.to_verification_key().hash()) update_drep_cert = UpdateDRepCertificate( drep_credential=drep_credential, anchor=None ) @@ -243,7 +251,7 @@ def test_update_drep_certificate_without_anchor(): assert ( update_drep_cert_cbor_hex - == "831284108200581c4828a2dadba97ca9fd0cdc99975899470c219bdc0d828cfa6ddf6d6900f6f6" + == "83128200581c4828a2dadba97ca9fd0cdc99975899470c219bdc0d828cfa6ddf6d69f6" ) assert ( @@ -275,7 +283,7 @@ def test_auth_committee_hot_certificate(): def test_resign_committee_cold_certificate_with_anchor(): committee_cold_credential = StakeCredential(TEST_ADDR.staking_part) url = "https://pycardano.com" - data_hash = bytes.fromhex("1234567890" * 6 + "12") + data_hash = AnchorDataHash(bytes.fromhex("0" * 64)) anchor = Anchor(url=url, data_hash=data_hash) resign_committee_cold_cert = ResignCommitteeColdCertificate( committee_cold_credential=committee_cold_credential, @@ -286,7 +294,7 @@ def test_resign_committee_cold_certificate_with_anchor(): assert ( resign_committee_cold_cert_cbor_hex - == "830f8200581c4828a2dadba97ca9fd0cdc99975899470c219bdc0d828cfa6ddf6d69827568747470733a2f2f707963617264616e6f2e636f6d581f12345678901234567890123456789012345678901234567890123456789012" + == "830f8200581c4828a2dadba97ca9fd0cdc99975899470c219bdc0d828cfa6ddf6d69827568747470733a2f2f707963617264616e6f2e636f6d58200000000000000000000000000000000000000000000000000000000000000000" ) assert ( @@ -353,8 +361,8 @@ def test_invalid_certificate_types(): ) assert "Invalid ResignCommitteeColdCertificate type 14" in str(excinfo.value) - stake_credential = StakeCredential(TEST_ADDR.staking_part) - drep_credential = DRepCredential(stake_credential, coin=0) + staking_key = StakeSigningKey.generate() + drep_credential = DRepCredential(staking_key.to_verification_key().hash()) with pytest.raises(DeserializeException) as excinfo: UnregDRepCertificate.from_primitive([18, drep_credential, 1000000]) @@ -378,7 +386,7 @@ def test_certificate_in_transaction(): ) url = "https://example.com" - data_hash = bytes.fromhex("1234567890" * 6 + "12") # 32 bytes + data_hash = AnchorDataHash(bytes.fromhex("0" * 64)) # 32 bytes anchor = Anchor(url=url, data_hash=data_hash) resign_committee_cold_cert = ResignCommitteeColdCertificate( committee_cold_credential=committee_cold_credential,