Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Certificate types for staking and voting delegation #401

Merged
merged 1 commit into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
242 changes: 242 additions & 0 deletions pycardano/certificate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum, unique
from typing import Optional, Tuple, Type, Union

from pycardano.exception import DeserializeException
Expand All @@ -15,6 +16,15 @@
"StakeDelegation",
"PoolRegistration",
"PoolRetirement",
"StakeRegistrationConway",
"StakeDeregistrationConway",
"VoteDelegation",
"StakeAndVoteDelegation",
"StakeRegistrationAndDelegation",
"StakeRegistrationAndVoteDelegation",
"StakeRegistrationAndDelegationAndVoteDelegation",
"DRep",
"DRepKind",
]

from pycardano.pool_params import PoolParams
Expand Down Expand Up @@ -168,10 +178,242 @@
raise DeserializeException(f"Invalid PoolRetirement type {values[0]}")


@dataclass(repr=False)
class StakeRegistrationConway(ArrayCBORSerializable):
_CODE: int = field(init=False, default=7)

stake_credential: StakeCredential
coin: int

def __post_init__(self):
self._CODE = 7

Check warning on line 189 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L189

Added line #L189 was not covered by tests

@classmethod
@limit_primitive_type(list)
def from_primitive(
cls: Type[StakeRegistrationConway], values: Union[list, tuple]
) -> StakeRegistrationConway:
if values[0] == 7:
return cls(

Check warning on line 197 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L197

Added line #L197 was not covered by tests
stake_credential=StakeCredential.from_primitive(values[1]),
coin=values[2],
)
else:
raise DeserializeException(

Check warning on line 202 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L202

Added line #L202 was not covered by tests
f"Invalid StakeRegistrationConway type {values[0]}"
)


@dataclass(repr=False)
class StakeDeregistrationConway(ArrayCBORSerializable):
_CODE: int = field(init=False, default=8)

stake_credential: StakeCredential
coin: int

def __post_init__(self):
self._CODE = 8

Check warning on line 215 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L215

Added line #L215 was not covered by tests

@classmethod
@limit_primitive_type(list)
def from_primitive(
cls: Type[StakeDeregistrationConway], values: Union[list, tuple]
) -> StakeDeregistrationConway:
if values[0] == 8:
return cls(

Check warning on line 223 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L223

Added line #L223 was not covered by tests
stake_credential=StakeCredential.from_primitive(values[1]),
coin=values[2],
)
else:
raise DeserializeException(

Check warning on line 228 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L228

Added line #L228 was not covered by tests
f"Invalid StakeDeregistrationConway type {values[0]}"
)


@dataclass(repr=False)
class VoteDelegation(ArrayCBORSerializable):
_CODE: int = field(init=False, default=9)

stake_credential: StakeCredential
drep: DRep

def __post_init__(self):
self._CODE = 9

Check warning on line 241 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L241

Added line #L241 was not covered by tests

@classmethod
@limit_primitive_type(list)
def from_primitive(
cls: Type[VoteDelegation], values: Union[list, tuple]
) -> VoteDelegation:
if values[0] == 9:
return cls(

Check warning on line 249 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L249

Added line #L249 was not covered by tests
stake_credential=StakeCredential.from_primitive(values[1]),
drep=DRep.from_primitive(values[2]),
)
else:
raise DeserializeException(f"Invalid VoteDelegation type {values[0]}")

Check warning on line 254 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L254

Added line #L254 was not covered by tests


@dataclass(repr=False)
class StakeAndVoteDelegation(ArrayCBORSerializable):
_CODE: int = field(init=False, default=10)

stake_credential: StakeCredential
pool_keyhash: PoolKeyHash
drep: DRep

def __post_init__(self):
self._CODE = 10

Check warning on line 266 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L266

Added line #L266 was not covered by tests

@classmethod
@limit_primitive_type(list)
def from_primitive(
cls: Type[StakeAndVoteDelegation], values: Union[list, tuple]
) -> StakeAndVoteDelegation:
if values[0] == 10:
return cls(

Check warning on line 274 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L274

Added line #L274 was not covered by tests
stake_credential=StakeCredential.from_primitive(values[1]),
pool_keyhash=PoolKeyHash.from_primitive(values[2]),
drep=DRep.from_primitive(values[3]),
)
else:
raise DeserializeException(

Check warning on line 280 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L280

Added line #L280 was not covered by tests
f"Invalid StakeAndVoteDelegation type {values[0]}"
)


@dataclass(repr=False)
class StakeRegistrationAndDelegation(ArrayCBORSerializable):
_CODE: int = field(init=False, default=11)

stake_credential: StakeCredential
pool_keyhash: PoolKeyHash
coin: int

def __post_init__(self):
self._CODE = 11

Check warning on line 294 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L294

Added line #L294 was not covered by tests

@classmethod
@limit_primitive_type(list)
def from_primitive(
cls: Type[StakeRegistrationAndDelegation], values: Union[list, tuple]
) -> StakeRegistrationAndDelegation:
if values[0] == 11:
return cls(

Check warning on line 302 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L302

Added line #L302 was not covered by tests
stake_credential=StakeCredential.from_primitive(values[1]),
pool_keyhash=PoolKeyHash.from_primitive(values[2]),
coin=values[3],
)
else:
raise DeserializeException(f"Invalid {cls.__name__} type {values[0]}")

Check warning on line 308 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L308

Added line #L308 was not covered by tests


@dataclass(repr=False)
class StakeRegistrationAndVoteDelegation(ArrayCBORSerializable):
_CODE: int = field(init=False, default=12)

stake_credential: StakeCredential
drep: DRep
coin: int

def __post_init__(self):
self._CODE = 12

Check warning on line 320 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L320

Added line #L320 was not covered by tests

@classmethod
@limit_primitive_type(list)
def from_primitive(
cls: Type[StakeRegistrationAndVoteDelegation], values: Union[list, tuple]
) -> StakeRegistrationAndVoteDelegation:
if values[0] == 12:
return cls(

Check warning on line 328 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L328

Added line #L328 was not covered by tests
stake_credential=StakeCredential.from_primitive(values[1]),
drep=DRep.from_primitive(values[2]),
coin=values[3],
)
else:
raise DeserializeException(f"Invalid {cls.__name__} type {values[0]}")

Check warning on line 334 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L334

Added line #L334 was not covered by tests


@dataclass(repr=False)
class StakeRegistrationAndDelegationAndVoteDelegation(ArrayCBORSerializable):
_CODE: int = field(init=False, default=13)

stake_credential: StakeCredential
pool_keyhash: PoolKeyHash
drep: DRep
coin: int

def __post_init__(self):
self._CODE = 13

Check warning on line 347 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L347

Added line #L347 was not covered by tests

@classmethod
@limit_primitive_type(list)
def from_primitive(
cls: Type[StakeRegistrationAndDelegationAndVoteDelegation],
values: Union[list, tuple],
) -> StakeRegistrationAndDelegationAndVoteDelegation:
if values[0] == 13:
return cls(

Check warning on line 356 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L356

Added line #L356 was not covered by tests
stake_credential=StakeCredential.from_primitive(values[1]),
pool_keyhash=PoolKeyHash.from_primitive(values[2]),
drep=DRep.from_primitive(values[3]),
coin=values[4],
)
else:
raise DeserializeException(f"Invalid {cls.__name__} type {values[0]}")

Check warning on line 363 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L363

Added line #L363 was not covered by tests


@unique
class DRepKind(Enum):
VERIFICATION_KEY_HASH = 0
SCRIPT_HASH = 1
ALWAYS_ABSTAIN = 2
ALWAYS_NO_CONFIDENCE = 3


@dataclass(repr=False)
class DRep(ArrayCBORSerializable):
kind: DRepKind
credential: Optional[Union[VerificationKeyHash, ScriptHash]] = field(
default=None, metadata={"optional": True}
)

@classmethod
@limit_primitive_type(list)
def from_primitive(cls: Type[DRep], values: Union[list, tuple]) -> DRep:
try:
kind = DRepKind(values[0])
except ValueError:
raise DeserializeException(f"Invalid DRep type {values[0]}")

Check warning on line 387 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L384-L387

Added lines #L384 - L387 were not covered by tests

if kind == DRepKind.VERIFICATION_KEY_HASH:
return cls(kind=kind, credential=VerificationKeyHash(values[1]))

Check warning on line 390 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L390

Added line #L390 was not covered by tests
elif kind == DRepKind.SCRIPT_HASH:
return cls(kind=kind, credential=ScriptHash(values[1]))

Check warning on line 392 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L392

Added line #L392 was not covered by tests
elif kind == DRepKind.ALWAYS_ABSTAIN:
return cls(kind=kind)

Check warning on line 394 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L394

Added line #L394 was not covered by tests
elif kind == DRepKind.ALWAYS_NO_CONFIDENCE:
return cls(kind=kind)

Check warning on line 396 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L396

Added line #L396 was not covered by tests
else:
raise DeserializeException(f"Invalid DRep type {values[0]}")

Check warning on line 398 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L398

Added line #L398 was not covered by tests

def to_primitive(self):
if self.credential is not None:
return [self.kind.value, self.credential.to_primitive()]
return [self.kind.value]

Check warning on line 403 in pycardano/certificate.py

View check run for this annotation

Codecov / codecov/patch

pycardano/certificate.py#L402-L403

Added lines #L402 - L403 were not covered by tests


Certificate = Union[
StakeRegistration,
StakeDeregistration,
StakeDelegation,
PoolRegistration,
PoolRetirement,
StakeRegistrationConway,
StakeDeregistrationConway,
VoteDelegation,
StakeAndVoteDelegation,
StakeRegistrationAndDelegation,
StakeRegistrationAndVoteDelegation,
StakeRegistrationAndDelegationAndVoteDelegation,
]
34 changes: 32 additions & 2 deletions pycardano/txbuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@
Certificate,
PoolRegistration,
PoolRetirement,
StakeAndVoteDelegation,
StakeCredential,
StakeDelegation,
StakeDeregistration,
StakeDeregistrationConway,
StakeRegistration,
StakeRegistrationAndDelegation,
StakeRegistrationAndDelegationAndVoteDelegation,
StakeRegistrationAndVoteDelegation,
StakeRegistrationConway,
VoteDelegation,
)
from pycardano.coinselection import (
LargestFirstSelector,
Expand Down Expand Up @@ -865,7 +872,19 @@
if self.certificates:
for cert in self.certificates:
if isinstance(
cert, (StakeRegistration, StakeDeregistration, StakeDelegation)
cert,
(
StakeRegistration,
StakeDeregistration,
StakeDelegation,
StakeRegistrationConway,
StakeDeregistrationConway,
VoteDelegation,
StakeAndVoteDelegation,
StakeRegistrationAndDelegation,
StakeRegistrationAndVoteDelegation,
StakeRegistrationAndDelegationAndVoteDelegation,
),
):
_check_and_add_vkey(cert.stake_credential)
elif isinstance(cert, PoolRegistration):
Expand All @@ -876,6 +895,7 @@

def _get_total_key_deposit(self):
stake_registration_certs = set()
stake_registration_certs_with_explicit_deposit = set()
stake_pool_registration_certs = set()

protocol_params = self.context.protocol_param
Expand All @@ -884,6 +904,16 @@
for cert in self.certificates:
if isinstance(cert, StakeRegistration):
stake_registration_certs.add(cert.stake_credential.credential)
elif isinstance(
cert,
(
StakeRegistrationConway,
StakeRegistrationAndDelegation,
StakeRegistrationAndVoteDelegation,
StakeRegistrationAndDelegationAndVoteDelegation,
),
):
stake_registration_certs_with_explicit_deposit.add(cert.coin)

Check warning on line 916 in pycardano/txbuilder.py

View check run for this annotation

Codecov / codecov/patch

pycardano/txbuilder.py#L916

Added line #L916 was not covered by tests
elif (
isinstance(cert, PoolRegistration)
and self.initial_stake_pool_registration
Expand All @@ -892,7 +922,7 @@

stake_registration_deposit = protocol_params.key_deposit * len(
stake_registration_certs
)
) + sum(stake_registration_certs_with_explicit_deposit)
stake_pool_registration_deposit = protocol_params.pool_deposit * len(
stake_pool_registration_certs
)
Expand Down
Loading