Skip to content

Commit 7092382

Browse files
authored
Add Certificate types for staking and voting delegation (#401)
1 parent e1189ba commit 7092382

File tree

2 files changed

+274
-2
lines changed

2 files changed

+274
-2
lines changed

pycardano/certificate.py

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
from dataclasses import dataclass, field
4+
from enum import Enum, unique
45
from typing import Optional, Tuple, Type, Union
56

67
from pycardano.exception import DeserializeException
@@ -15,6 +16,15 @@
1516
"StakeDelegation",
1617
"PoolRegistration",
1718
"PoolRetirement",
19+
"StakeRegistrationConway",
20+
"StakeDeregistrationConway",
21+
"VoteDelegation",
22+
"StakeAndVoteDelegation",
23+
"StakeRegistrationAndDelegation",
24+
"StakeRegistrationAndVoteDelegation",
25+
"StakeRegistrationAndDelegationAndVoteDelegation",
26+
"DRep",
27+
"DRepKind",
1828
]
1929

2030
from pycardano.pool_params import PoolParams
@@ -168,10 +178,242 @@ def from_primitive(
168178
raise DeserializeException(f"Invalid PoolRetirement type {values[0]}")
169179

170180

181+
@dataclass(repr=False)
182+
class StakeRegistrationConway(ArrayCBORSerializable):
183+
_CODE: int = field(init=False, default=7)
184+
185+
stake_credential: StakeCredential
186+
coin: int
187+
188+
def __post_init__(self):
189+
self._CODE = 7
190+
191+
@classmethod
192+
@limit_primitive_type(list)
193+
def from_primitive(
194+
cls: Type[StakeRegistrationConway], values: Union[list, tuple]
195+
) -> StakeRegistrationConway:
196+
if values[0] == 7:
197+
return cls(
198+
stake_credential=StakeCredential.from_primitive(values[1]),
199+
coin=values[2],
200+
)
201+
else:
202+
raise DeserializeException(
203+
f"Invalid StakeRegistrationConway type {values[0]}"
204+
)
205+
206+
207+
@dataclass(repr=False)
208+
class StakeDeregistrationConway(ArrayCBORSerializable):
209+
_CODE: int = field(init=False, default=8)
210+
211+
stake_credential: StakeCredential
212+
coin: int
213+
214+
def __post_init__(self):
215+
self._CODE = 8
216+
217+
@classmethod
218+
@limit_primitive_type(list)
219+
def from_primitive(
220+
cls: Type[StakeDeregistrationConway], values: Union[list, tuple]
221+
) -> StakeDeregistrationConway:
222+
if values[0] == 8:
223+
return cls(
224+
stake_credential=StakeCredential.from_primitive(values[1]),
225+
coin=values[2],
226+
)
227+
else:
228+
raise DeserializeException(
229+
f"Invalid StakeDeregistrationConway type {values[0]}"
230+
)
231+
232+
233+
@dataclass(repr=False)
234+
class VoteDelegation(ArrayCBORSerializable):
235+
_CODE: int = field(init=False, default=9)
236+
237+
stake_credential: StakeCredential
238+
drep: DRep
239+
240+
def __post_init__(self):
241+
self._CODE = 9
242+
243+
@classmethod
244+
@limit_primitive_type(list)
245+
def from_primitive(
246+
cls: Type[VoteDelegation], values: Union[list, tuple]
247+
) -> VoteDelegation:
248+
if values[0] == 9:
249+
return cls(
250+
stake_credential=StakeCredential.from_primitive(values[1]),
251+
drep=DRep.from_primitive(values[2]),
252+
)
253+
else:
254+
raise DeserializeException(f"Invalid VoteDelegation type {values[0]}")
255+
256+
257+
@dataclass(repr=False)
258+
class StakeAndVoteDelegation(ArrayCBORSerializable):
259+
_CODE: int = field(init=False, default=10)
260+
261+
stake_credential: StakeCredential
262+
pool_keyhash: PoolKeyHash
263+
drep: DRep
264+
265+
def __post_init__(self):
266+
self._CODE = 10
267+
268+
@classmethod
269+
@limit_primitive_type(list)
270+
def from_primitive(
271+
cls: Type[StakeAndVoteDelegation], values: Union[list, tuple]
272+
) -> StakeAndVoteDelegation:
273+
if values[0] == 10:
274+
return cls(
275+
stake_credential=StakeCredential.from_primitive(values[1]),
276+
pool_keyhash=PoolKeyHash.from_primitive(values[2]),
277+
drep=DRep.from_primitive(values[3]),
278+
)
279+
else:
280+
raise DeserializeException(
281+
f"Invalid StakeAndVoteDelegation type {values[0]}"
282+
)
283+
284+
285+
@dataclass(repr=False)
286+
class StakeRegistrationAndDelegation(ArrayCBORSerializable):
287+
_CODE: int = field(init=False, default=11)
288+
289+
stake_credential: StakeCredential
290+
pool_keyhash: PoolKeyHash
291+
coin: int
292+
293+
def __post_init__(self):
294+
self._CODE = 11
295+
296+
@classmethod
297+
@limit_primitive_type(list)
298+
def from_primitive(
299+
cls: Type[StakeRegistrationAndDelegation], values: Union[list, tuple]
300+
) -> StakeRegistrationAndDelegation:
301+
if values[0] == 11:
302+
return cls(
303+
stake_credential=StakeCredential.from_primitive(values[1]),
304+
pool_keyhash=PoolKeyHash.from_primitive(values[2]),
305+
coin=values[3],
306+
)
307+
else:
308+
raise DeserializeException(f"Invalid {cls.__name__} type {values[0]}")
309+
310+
311+
@dataclass(repr=False)
312+
class StakeRegistrationAndVoteDelegation(ArrayCBORSerializable):
313+
_CODE: int = field(init=False, default=12)
314+
315+
stake_credential: StakeCredential
316+
drep: DRep
317+
coin: int
318+
319+
def __post_init__(self):
320+
self._CODE = 12
321+
322+
@classmethod
323+
@limit_primitive_type(list)
324+
def from_primitive(
325+
cls: Type[StakeRegistrationAndVoteDelegation], values: Union[list, tuple]
326+
) -> StakeRegistrationAndVoteDelegation:
327+
if values[0] == 12:
328+
return cls(
329+
stake_credential=StakeCredential.from_primitive(values[1]),
330+
drep=DRep.from_primitive(values[2]),
331+
coin=values[3],
332+
)
333+
else:
334+
raise DeserializeException(f"Invalid {cls.__name__} type {values[0]}")
335+
336+
337+
@dataclass(repr=False)
338+
class StakeRegistrationAndDelegationAndVoteDelegation(ArrayCBORSerializable):
339+
_CODE: int = field(init=False, default=13)
340+
341+
stake_credential: StakeCredential
342+
pool_keyhash: PoolKeyHash
343+
drep: DRep
344+
coin: int
345+
346+
def __post_init__(self):
347+
self._CODE = 13
348+
349+
@classmethod
350+
@limit_primitive_type(list)
351+
def from_primitive(
352+
cls: Type[StakeRegistrationAndDelegationAndVoteDelegation],
353+
values: Union[list, tuple],
354+
) -> StakeRegistrationAndDelegationAndVoteDelegation:
355+
if values[0] == 13:
356+
return cls(
357+
stake_credential=StakeCredential.from_primitive(values[1]),
358+
pool_keyhash=PoolKeyHash.from_primitive(values[2]),
359+
drep=DRep.from_primitive(values[3]),
360+
coin=values[4],
361+
)
362+
else:
363+
raise DeserializeException(f"Invalid {cls.__name__} type {values[0]}")
364+
365+
366+
@unique
367+
class DRepKind(Enum):
368+
VERIFICATION_KEY_HASH = 0
369+
SCRIPT_HASH = 1
370+
ALWAYS_ABSTAIN = 2
371+
ALWAYS_NO_CONFIDENCE = 3
372+
373+
374+
@dataclass(repr=False)
375+
class DRep(ArrayCBORSerializable):
376+
kind: DRepKind
377+
credential: Optional[Union[VerificationKeyHash, ScriptHash]] = field(
378+
default=None, metadata={"optional": True}
379+
)
380+
381+
@classmethod
382+
@limit_primitive_type(list)
383+
def from_primitive(cls: Type[DRep], values: Union[list, tuple]) -> DRep:
384+
try:
385+
kind = DRepKind(values[0])
386+
except ValueError:
387+
raise DeserializeException(f"Invalid DRep type {values[0]}")
388+
389+
if kind == DRepKind.VERIFICATION_KEY_HASH:
390+
return cls(kind=kind, credential=VerificationKeyHash(values[1]))
391+
elif kind == DRepKind.SCRIPT_HASH:
392+
return cls(kind=kind, credential=ScriptHash(values[1]))
393+
elif kind == DRepKind.ALWAYS_ABSTAIN:
394+
return cls(kind=kind)
395+
elif kind == DRepKind.ALWAYS_NO_CONFIDENCE:
396+
return cls(kind=kind)
397+
else:
398+
raise DeserializeException(f"Invalid DRep type {values[0]}")
399+
400+
def to_primitive(self):
401+
if self.credential is not None:
402+
return [self.kind.value, self.credential.to_primitive()]
403+
return [self.kind.value]
404+
405+
171406
Certificate = Union[
172407
StakeRegistration,
173408
StakeDeregistration,
174409
StakeDelegation,
175410
PoolRegistration,
176411
PoolRetirement,
412+
StakeRegistrationConway,
413+
StakeDeregistrationConway,
414+
VoteDelegation,
415+
StakeAndVoteDelegation,
416+
StakeRegistrationAndDelegation,
417+
StakeRegistrationAndVoteDelegation,
418+
StakeRegistrationAndDelegationAndVoteDelegation,
177419
]

pycardano/txbuilder.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,17 @@
1111
Certificate,
1212
PoolRegistration,
1313
PoolRetirement,
14+
StakeAndVoteDelegation,
1415
StakeCredential,
1516
StakeDelegation,
1617
StakeDeregistration,
18+
StakeDeregistrationConway,
1719
StakeRegistration,
20+
StakeRegistrationAndDelegation,
21+
StakeRegistrationAndDelegationAndVoteDelegation,
22+
StakeRegistrationAndVoteDelegation,
23+
StakeRegistrationConway,
24+
VoteDelegation,
1825
)
1926
from pycardano.coinselection import (
2027
LargestFirstSelector,
@@ -854,7 +861,19 @@ def _check_and_add_vkey(stake_credential: StakeCredential):
854861
if self.certificates:
855862
for cert in self.certificates:
856863
if isinstance(
857-
cert, (StakeRegistration, StakeDeregistration, StakeDelegation)
864+
cert,
865+
(
866+
StakeRegistration,
867+
StakeDeregistration,
868+
StakeDelegation,
869+
StakeRegistrationConway,
870+
StakeDeregistrationConway,
871+
VoteDelegation,
872+
StakeAndVoteDelegation,
873+
StakeRegistrationAndDelegation,
874+
StakeRegistrationAndVoteDelegation,
875+
StakeRegistrationAndDelegationAndVoteDelegation,
876+
),
858877
):
859878
_check_and_add_vkey(cert.stake_credential)
860879
elif isinstance(cert, PoolRegistration):
@@ -865,6 +884,7 @@ def _check_and_add_vkey(stake_credential: StakeCredential):
865884

866885
def _get_total_key_deposit(self):
867886
stake_registration_certs = set()
887+
stake_registration_certs_with_explicit_deposit = set()
868888
stake_pool_registration_certs = set()
869889

870890
protocol_params = self.context.protocol_param
@@ -873,6 +893,16 @@ def _get_total_key_deposit(self):
873893
for cert in self.certificates:
874894
if isinstance(cert, StakeRegistration):
875895
stake_registration_certs.add(cert.stake_credential.credential)
896+
elif isinstance(
897+
cert,
898+
(
899+
StakeRegistrationConway,
900+
StakeRegistrationAndDelegation,
901+
StakeRegistrationAndVoteDelegation,
902+
StakeRegistrationAndDelegationAndVoteDelegation,
903+
),
904+
):
905+
stake_registration_certs_with_explicit_deposit.add(cert.coin)
876906
elif (
877907
isinstance(cert, PoolRegistration)
878908
and self.initial_stake_pool_registration
@@ -881,7 +911,7 @@ def _get_total_key_deposit(self):
881911

882912
stake_registration_deposit = protocol_params.key_deposit * len(
883913
stake_registration_certs
884-
)
914+
) + sum(stake_registration_certs_with_explicit_deposit)
885915
stake_pool_registration_deposit = protocol_params.pool_deposit * len(
886916
stake_pool_registration_certs
887917
)

0 commit comments

Comments
 (0)