Skip to content

Commit

Permalink
Add support for voting with scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
mkoura committed Jul 3, 2024
1 parent de8a5dc commit 070ed6c
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 10 deletions.
2 changes: 2 additions & 0 deletions cardano_clusterlib/clusterlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from cardano_clusterlib.structs import OptionalMint
from cardano_clusterlib.structs import OptionalScriptCerts
from cardano_clusterlib.structs import OptionalScriptTxIn
from cardano_clusterlib.structs import OptionalScriptVotes
from cardano_clusterlib.structs import OptionalScriptWithdrawals
from cardano_clusterlib.structs import OptionalTxOuts
from cardano_clusterlib.structs import OptionalUTXOData
Expand All @@ -48,6 +49,7 @@
from cardano_clusterlib.structs import PoolParamsTop
from cardano_clusterlib.structs import PoolUser
from cardano_clusterlib.structs import ScriptTxIn
from cardano_clusterlib.structs import ScriptVote
from cardano_clusterlib.structs import ScriptWithdrawal
from cardano_clusterlib.structs import StakeAddrInfo
from cardano_clusterlib.structs import TxFiles
Expand Down
7 changes: 7 additions & 0 deletions cardano_clusterlib/conway_gov_action_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ def _get_cc_members_args(
str(cc_member.cold_vkey_hash),
]
)
elif cc_member.cold_script_hash:
cc_members_args.extend(
[
f"--{arg_action}-cc-cold-script-hash",
str(cc_member.cold_script_hash),
]
)
else:
msg = f"Either {arg_action} cold verification key or its hash must be set."
raise AssertionError(msg)
Expand Down
28 changes: 18 additions & 10 deletions cardano_clusterlib/conway_gov_vote_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def create_committee(
cc_hot_vkey: str = "",
cc_hot_vkey_file: tp.Optional[itp.FileType] = None,
cc_hot_key_hash: str = "",
cc_hot_script_hash: str = "",
anchor_url: str = "",
anchor_data_hash: str = "",
destination_dir: itp.FileType = ".",
Expand All @@ -92,13 +93,15 @@ def create_committee(
)

if cc_hot_vkey:
key_args = ["--cc-hot-verification-key", cc_hot_vkey]
cred_args = ["--cc-hot-verification-key", cc_hot_vkey]
elif cc_hot_vkey_file:
key_args = ["--cc-hot-verification-key-file", str(cc_hot_vkey_file)]
cred_args = ["--cc-hot-verification-key-file", str(cc_hot_vkey_file)]
elif cc_hot_key_hash:
key_args = ["--cc-hot-key-hash", cc_hot_key_hash]
cred_args = ["--cc-hot-key-hash", cc_hot_key_hash]
elif cc_hot_script_hash:
cred_args = ["--cc-hot-script-hash", cc_hot_script_hash]
else:
msg = "No CC key was specified."
msg = "No CC key or script hash was specified."
raise AssertionError(msg)

self._clusterlib_obj.cli(
Expand All @@ -107,7 +110,7 @@ def create_committee(
"create",
*vote_args,
*gov_action_args,
*key_args,
*cred_args,
*anchor_args,
"--out-file",
str(out_file),
Expand All @@ -123,6 +126,7 @@ def create_committee(
cc_hot_vkey=cc_hot_vkey,
cc_hot_vkey_file=helpers._maybe_path(cc_hot_vkey_file),
cc_hot_key_hash=cc_hot_key_hash,
cc_hot_script_hash=cc_hot_script_hash,
anchor_url=anchor_url,
anchor_data_hash=anchor_data_hash,
)
Expand All @@ -137,6 +141,7 @@ def create_drep(
drep_vkey: str = "",
drep_vkey_file: tp.Optional[itp.FileType] = None,
drep_key_hash: str = "",
drep_script_hash: str = "",
anchor_url: str = "",
anchor_data_hash: str = "",
destination_dir: itp.FileType = ".",
Expand All @@ -154,13 +159,15 @@ def create_drep(
)

if drep_vkey:
key_args = ["--drep-verification-key", drep_vkey]
cred_args = ["--drep-verification-key", drep_vkey]
elif drep_vkey_file:
key_args = ["--drep-verification-key-file", str(drep_vkey_file)]
cred_args = ["--drep-verification-key-file", str(drep_vkey_file)]
elif drep_key_hash:
key_args = ["--drep-key-hash", drep_key_hash]
cred_args = ["--drep-key-hash", drep_key_hash]
elif drep_script_hash:
cred_args = ["--drep-script-hash", drep_script_hash]
else:
msg = "No DRep key was specified."
msg = "No DRep key or script hash was specified."
raise AssertionError(msg)

self._clusterlib_obj.cli(
Expand All @@ -169,7 +176,7 @@ def create_drep(
"create",
*vote_args,
*gov_action_args,
*key_args,
*cred_args,
*anchor_args,
"--out-file",
str(out_file),
Expand All @@ -185,6 +192,7 @@ def create_drep(
drep_vkey=drep_vkey,
drep_vkey_file=helpers._maybe_path(drep_vkey_file),
drep_key_hash=drep_key_hash,
drep_script_hash=drep_script_hash,
anchor_url=anchor_url,
anchor_data_hash=anchor_data_hash,
)
Expand Down
20 changes: 20 additions & 0 deletions cardano_clusterlib/structs.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,20 @@ class ComplexCert:
redeemer_value: str = ""


@dataclasses.dataclass(frozen=True, order=True)
class ScriptVote:
"""Data structure for voting that are combined with scripts."""

vote_file: itp.FileType = ""
script_file: itp.FileType = ""
# values below needed only when working with Plutus
collaterals: OptionalUTXOData = ()
execution_units: tp.Optional[tp.Tuple[int, int]] = None
redeemer_file: itp.FileType = ""
redeemer_cbor_file: itp.FileType = ""
redeemer_value: str = ""


@dataclasses.dataclass(frozen=True, order=True)
class Mint:
txouts: tp.List[TxOut]
Expand All @@ -161,6 +175,8 @@ class Mint:
OptionalScriptWithdrawals = tp.Union[tp.List[ScriptWithdrawal], tp.Tuple[()]]
# list of `Mint`s, empty list, or empty tuple
OptionalMint = tp.Union[tp.List[Mint], tp.Tuple[()]]
# list of `ScriptVote`s, empty list, or empty tuple
OptionalScriptVotes = tp.Union[tp.List[ScriptVote], tp.Tuple[()]]


@dataclasses.dataclass(frozen=True, order=True)
Expand Down Expand Up @@ -206,6 +222,7 @@ class TxRawOutput:
era: str = "" # Era used for the transaction
script_txins: OptionalScriptTxIn = () # Tx inputs that are combined with scripts
script_withdrawals: OptionalScriptWithdrawals = () # Withdrawals that are combined with scripts
script_votes: OptionalScriptVotes = () # Votes that are combined with scripts
complex_certs: OptionalScriptCerts = () # Certificates that are combined with scripts
mint: OptionalMint = () # Minting data (Tx outputs, script, etc.)
invalid_hereafter: tp.Optional[int] = None # Validity interval upper bound
Expand Down Expand Up @@ -287,6 +304,7 @@ class CCMember:
cold_skey: str = ""
cold_skey_file: itp.FileType = ""
cold_skey_hash: str = ""
cold_script_hash: str = ""
hot_vkey: str = ""
hot_vkey_file: itp.FileType = ""
hot_vkey_hash: str = ""
Expand All @@ -304,6 +322,7 @@ class VoteCC:
cc_hot_vkey: str = ""
cc_hot_vkey_file: tp.Optional[pl.Path] = None
cc_hot_key_hash: str = ""
cc_hot_script_hash: str = ""
anchor_url: str = ""
anchor_data_hash: str = ""

Expand All @@ -317,6 +336,7 @@ class VoteDrep:
drep_vkey: str = ""
drep_vkey_file: tp.Optional[pl.Path] = None
drep_key_hash: str = ""
drep_script_hash: str = ""
anchor_url: str = ""
anchor_data_hash: str = ""

Expand Down
19 changes: 19 additions & 0 deletions cardano_clusterlib/transaction_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ def build_raw_tx_bare(
ttl: tp.Optional[int] = None,
withdrawals: structs.OptionalTxOuts = (),
script_withdrawals: structs.OptionalScriptWithdrawals = (),
script_votes: structs.OptionalScriptVotes = (),
invalid_hereafter: tp.Optional[int] = None,
invalid_before: tp.Optional[int] = None,
script_valid: bool = True,
Expand Down Expand Up @@ -195,6 +196,7 @@ def build_raw_tx_bare(
withdrawals: A list (iterable) of `TxOuts`, specifying reward withdrawals (optional).
script_withdrawals: An iterable of `ScriptWithdrawal`, specifying withdrawal script
data (optional).
script_votes: An iterable of `ScriptVote`, specifying vote script data (optional).
invalid_hereafter: A last block when the transaction is still valid (optional).
invalid_before: A first block when the transaction is valid (optional).
script_valid: A bool indicating that the script is valid (True by default).
Expand Down Expand Up @@ -256,6 +258,7 @@ def build_raw_tx_bare(
mint=mint,
complex_certs=complex_certs,
script_withdrawals=script_withdrawals,
script_votes=script_votes,
for_build=False,
)

Expand Down Expand Up @@ -321,6 +324,7 @@ def build_raw_tx_bare(
era=self._clusterlib_obj.command_era or self._clusterlib_obj.tx_era,
script_txins=script_txins,
script_withdrawals=script_withdrawals,
script_votes=script_votes,
complex_certs=complex_certs,
mint=mint,
invalid_hereafter=invalid_hereafter or ttl,
Expand Down Expand Up @@ -360,6 +364,7 @@ def build_raw_tx(
ttl: tp.Optional[int] = None,
withdrawals: structs.OptionalTxOuts = (),
script_withdrawals: structs.OptionalScriptWithdrawals = (),
script_votes: structs.OptionalScriptVotes = (),
deposit: tp.Optional[int] = None,
invalid_hereafter: tp.Optional[int] = None,
invalid_before: tp.Optional[int] = None,
Expand Down Expand Up @@ -395,6 +400,7 @@ def build_raw_tx(
withdrawals: A list (iterable) of `TxOuts`, specifying reward withdrawals (optional).
script_withdrawals: An iterable of `ScriptWithdrawal`, specifying withdrawal script
data (optional).
script_votes: An iterable of `ScriptVote`, specifying vote script data (optional).
deposit: A deposit amount needed by the transaction (optional).
invalid_hereafter: A last block when the transaction is still valid (optional).
invalid_before: A first block when the transaction is valid (optional).
Expand Down Expand Up @@ -453,6 +459,7 @@ def build_raw_tx(
required_signer_hashes=required_signer_hashes,
withdrawals=collected_data.withdrawals,
script_withdrawals=collected_data.script_withdrawals,
script_votes=script_votes,
invalid_hereafter=invalid_hereafter or ttl,
invalid_before=invalid_before,
join_txouts=join_txouts,
Expand Down Expand Up @@ -530,6 +537,7 @@ def calculate_tx_fee(
ttl: tp.Optional[int] = None,
withdrawals: structs.OptionalTxOuts = (),
script_withdrawals: structs.OptionalScriptWithdrawals = (),
script_votes: structs.OptionalScriptVotes = (),
deposit: tp.Optional[int] = None,
invalid_hereafter: tp.Optional[int] = None,
invalid_before: tp.Optional[int] = None,
Expand Down Expand Up @@ -566,6 +574,7 @@ def calculate_tx_fee(
withdrawals: A list (iterable) of `TxOuts`, specifying reward withdrawals (optional).
script_withdrawals: An iterable of `ScriptWithdrawal`, specifying withdrawal script
data (optional).
script_votes: An iterable of `ScriptVote`, specifying vote script data (optional).
deposit: A deposit amount needed by the transaction (optional).
invalid_hereafter: A last block when the transaction is still valid (optional).
invalid_before: A first block when the transaction is valid (optional).
Expand Down Expand Up @@ -608,6 +617,7 @@ def calculate_tx_fee(
fee=self.min_fee,
withdrawals=withdrawals,
script_withdrawals=script_withdrawals,
script_votes=script_votes,
invalid_hereafter=invalid_hereafter or ttl,
invalid_before=invalid_before,
deposit=deposit,
Expand Down Expand Up @@ -735,6 +745,7 @@ def build_tx( # noqa: C901
required_signer_hashes: tp.Optional[tp.List[str]] = None,
withdrawals: structs.OptionalTxOuts = (),
script_withdrawals: structs.OptionalScriptWithdrawals = (),
script_votes: structs.OptionalScriptVotes = (),
deposit: tp.Optional[int] = None,
invalid_hereafter: tp.Optional[int] = None,
invalid_before: tp.Optional[int] = None,
Expand Down Expand Up @@ -774,6 +785,7 @@ def build_tx( # noqa: C901
withdrawals: A list (iterable) of `TxOuts`, specifying reward withdrawals (optional).
script_withdrawals: An iterable of `ScriptWithdrawal`, specifying withdrawal script
data (optional).
script_votes: An iterable of `ScriptVote`, specifying vote script data (optional).
deposit: A deposit amount needed by the transaction (optional).
invalid_hereafter: A last block when the transaction is still valid (optional).
invalid_before: A first block when the transaction is valid (optional).
Expand Down Expand Up @@ -866,6 +878,7 @@ def build_tx( # noqa: C901
mint=mint,
complex_certs=complex_certs,
script_withdrawals=collected_data.script_withdrawals,
script_votes=script_votes,
for_build=True,
)

Expand Down Expand Up @@ -931,6 +944,7 @@ def build_tx( # noqa: C901
era=self._clusterlib_obj.command_era or self._clusterlib_obj.tx_era,
script_txins=script_txins,
script_withdrawals=collected_data.script_withdrawals,
script_votes=script_votes,
complex_certs=complex_certs,
mint=mint,
invalid_hereafter=invalid_hereafter,
Expand Down Expand Up @@ -1159,6 +1173,7 @@ def send_tx(
ttl: tp.Optional[int] = None,
withdrawals: structs.OptionalTxOuts = (),
script_withdrawals: structs.OptionalScriptWithdrawals = (),
script_votes: structs.OptionalScriptVotes = (),
deposit: tp.Optional[int] = None,
invalid_hereafter: tp.Optional[int] = None,
invalid_before: tp.Optional[int] = None,
Expand Down Expand Up @@ -1203,6 +1218,7 @@ def send_tx(
withdrawals: A list (iterable) of `TxOuts`, specifying reward withdrawals (optional).
script_withdrawals: An iterable of `ScriptWithdrawal`, specifying withdrawal script
data (optional).
script_votes: An iterable of `ScriptVote`, specifying vote script data (optional).
deposit: A deposit amount needed by the transaction (optional).
invalid_hereafter: A last block when the transaction is still valid (optional).
invalid_before: A first block when the transaction is valid (optional).
Expand Down Expand Up @@ -1274,6 +1290,7 @@ def send_tx(
required_signer_hashes=required_signer_hashes,
withdrawals=withdrawals,
script_withdrawals=script_withdrawals,
script_votes=script_votes,
deposit=deposit,
invalid_hereafter=invalid_hereafter or ttl,
invalid_before=invalid_before,
Expand Down Expand Up @@ -1387,6 +1404,7 @@ def calculate_plutus_script_cost(
required_signer_hashes: tp.Optional[tp.List[str]] = None,
withdrawals: structs.OptionalTxOuts = (),
script_withdrawals: structs.OptionalScriptWithdrawals = (),
script_votes: structs.OptionalScriptVotes = (),
deposit: tp.Optional[int] = None,
invalid_hereafter: tp.Optional[int] = None,
invalid_before: tp.Optional[int] = None,
Expand Down Expand Up @@ -1425,6 +1443,7 @@ def calculate_plutus_script_cost(
withdrawals: A list (iterable) of `TxOuts`, specifying reward withdrawals (optional).
script_withdrawals: An iterable of `ScriptWithdrawal`, specifying withdrawal script
data (optional).
script_votes: An iterable of `ScriptVote`, specifying vote script data (optional).
deposit: A deposit amount needed by the transaction (optional).
invalid_hereafter: A last block when the transaction is still valid (optional).
invalid_before: A first block when the transaction is valid (optional).
Expand Down
35 changes: 35 additions & 0 deletions cardano_clusterlib/txtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,7 @@ def _get_script_args( # noqa: C901
mint: structs.OptionalMint,
complex_certs: structs.OptionalScriptCerts,
script_withdrawals: structs.OptionalScriptWithdrawals,
script_votes: structs.OptionalScriptVotes,
for_build: bool = True,
) -> tp.List[str]:
# pylint: disable=too-many-statements,too-many-branches
Expand Down Expand Up @@ -1195,6 +1196,40 @@ def _get_script_args( # noqa: C901
["--withdrawal-reference-tx-in-redeemer-value", str(wrec.redeemer_value)]
)

# voting
for vrec in script_votes:
vrec_collaterals = {f"{c.utxo_hash}#{c.utxo_ix}" for c in vrec.collaterals}
collaterals_all.update(vrec_collaterals)
grouped_args.extend(
[
"--vote-file",
str(vrec.vote_file),
]
)

if vrec.script_file:
grouped_args.extend(
[
"--vote-script-file",
str(vrec.script_file),
]
)

if not for_build and vrec.execution_units:
grouped_args.extend(
[
"--vote-execution-units",
f"({vrec.execution_units[0]},{vrec.execution_units[1]})",
]
)

if vrec.redeemer_file:
grouped_args.extend(["--vote-redeemer-file", str(vrec.redeemer_file)])
if vrec.redeemer_cbor_file:
grouped_args.extend(["--vote-redeemer-cbor-file", str(vrec.redeemer_cbor_file)])
if vrec.redeemer_value:
grouped_args.extend(["--vote-redeemer-value", str(vrec.redeemer_value)])

# add unique collaterals
grouped_args.extend(
[
Expand Down

0 comments on commit 070ed6c

Please sign in to comment.