From f1f5385060ca38db2fd80dd8df9dc88c56de4e70 Mon Sep 17 00:00:00 2001 From: Artur Wieczorek Date: Wed, 24 Jul 2024 14:50:54 +0200 Subject: [PATCH 1/5] Add --current-treasury-value and --treasury-donation to transaction build and build-raw - adds `--treasury-donation` to transaction build - adds `--current-treasury-value` and `--treasury-donation` to transaction build-raw In case of transaction build-raw donation must be passed together with current treasury value. --- cardano_clusterlib/structs.py | 2 ++ cardano_clusterlib/transaction_group.py | 48 +++++++++++++++++++++++++ cardano_clusterlib/txtools.py | 15 ++++++-- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/cardano_clusterlib/structs.py b/cardano_clusterlib/structs.py index 71a399c..b92ce1d 100644 --- a/cardano_clusterlib/structs.py +++ b/cardano_clusterlib/structs.py @@ -248,6 +248,8 @@ class TxRawOutput: mint: OptionalMint = () # Minting data (Tx outputs, script, etc.) invalid_hereafter: tp.Optional[int] = None # Validity interval upper bound invalid_before: tp.Optional[int] = None # Validity interval lower bound + current_treasury_value: tp.Optional[int] = None # Current treasury value + treasury_donation: tp.Optional[int] = None # Amount of funds that will be donated to treasury withdrawals: OptionalTxOuts = () # All withdrawals (including those combined with scripts) change_address: str = "" # Address for change return_collateral_txouts: OptionalTxOuts = () # Tx outputs for returning collateral diff --git a/cardano_clusterlib/transaction_group.py b/cardano_clusterlib/transaction_group.py index 06ff90c..c3da42b 100644 --- a/cardano_clusterlib/transaction_group.py +++ b/cardano_clusterlib/transaction_group.py @@ -167,6 +167,8 @@ def build_raw_tx_bare( # noqa: C901 script_votes: structs.OptionalScriptVotes = (), invalid_hereafter: tp.Optional[int] = None, invalid_before: tp.Optional[int] = None, + current_treasury_value: tp.Optional[int] = None, + treasury_donation: tp.Optional[int] = None, script_valid: bool = True, join_txouts: bool = True, ) -> structs.TxRawOutput: @@ -202,6 +204,8 @@ def build_raw_tx_bare( # noqa: C901 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). + current_treasury_value: The current treasury value (optional). + treasury_donation: A donation to the treasury to perform (optional). script_valid: A bool indicating that the script is valid (True by default). join_txouts: A bool indicating whether to aggregate transaction outputs by payment address (True by default). @@ -210,6 +214,12 @@ def build_raw_tx_bare( # noqa: C901 structs.TxRawOutput: A tuple with transaction output details. """ # pylint: disable=too-many-arguments,too-many-branches,too-many-locals,too-many-statements + if (treasury_donation is not None) != (current_treasury_value is not None): + msg = ( + "Both `treasury_donation` and `current_treasury_value` must be specified together." + ) + raise ValueError(msg) + if tx_files.certificate_files and complex_certs: LOGGER.warning( "Mixing `tx_files.certificate_files` and `complex_certs`, " @@ -252,6 +262,11 @@ def build_raw_tx_bare( # noqa: C901 # `--ttl` and `--invalid-hereafter` are the same misc_args.extend(["--ttl", str(ttl)]) + if current_treasury_value is not None: + misc_args.extend(["--current-treasury-value", str(current_treasury_value)]) + if treasury_donation is not None: + misc_args.extend(["--treasury-donation", str(treasury_donation)]) + if not script_valid: misc_args.append("--script-invalid") @@ -340,6 +355,8 @@ def build_raw_tx_bare( # noqa: C901 mint=mint, invalid_hereafter=invalid_hereafter or ttl, invalid_before=invalid_before, + current_treasury_value=current_treasury_value, + treasury_donation=treasury_donation, withdrawals=withdrawals, return_collateral_txouts=return_collateral_txouts, total_collateral_amount=total_collateral_amount, @@ -378,6 +395,8 @@ def build_raw_tx( script_withdrawals: structs.OptionalScriptWithdrawals = (), script_votes: structs.OptionalScriptVotes = (), deposit: tp.Optional[int] = None, + current_treasury_value: tp.Optional[int] = None, + treasury_donation: tp.Optional[int] = None, invalid_hereafter: tp.Optional[int] = None, invalid_before: tp.Optional[int] = None, join_txouts: bool = True, @@ -416,6 +435,8 @@ def build_raw_tx( data (optional). script_votes: An iterable of `ScriptVote`, specifying vote script data (optional). deposit: A deposit amount needed by the transaction (optional). + current_treasury_value: The current treasury value (optional). + treasury_donation: A donation to the treasury to perform (optional). invalid_hereafter: A last block when the transaction is still valid (optional). invalid_before: A first block when the transaction is valid (optional). join_txouts: A bool indicating whether to aggregate transaction outputs @@ -426,6 +447,12 @@ def build_raw_tx( structs.TxRawOutput: A tuple with transaction output details. """ # pylint: disable=too-many-arguments + if (treasury_donation is not None) != (current_treasury_value is not None): + msg = ( + "Both `treasury_donation` and `current_treasury_value` must be specified together." + ) + raise ValueError(msg) + destination_dir = pl.Path(destination_dir).expanduser() out_file = destination_dir / f"{tx_name}_tx.body" clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) @@ -446,6 +473,7 @@ def build_raw_tx( withdrawals=withdrawals, script_withdrawals=script_withdrawals, deposit=deposit, + treasury_donation=treasury_donation, ) if ( @@ -478,6 +506,8 @@ def build_raw_tx( script_votes=script_votes, invalid_hereafter=invalid_hereafter or ttl, invalid_before=invalid_before, + current_treasury_value=current_treasury_value, + treasury_donation=treasury_donation, join_txouts=join_txouts, ) @@ -556,6 +586,8 @@ def calculate_tx_fee( script_withdrawals: structs.OptionalScriptWithdrawals = (), script_votes: structs.OptionalScriptVotes = (), deposit: tp.Optional[int] = None, + current_treasury_value: tp.Optional[int] = None, + treasury_donation: tp.Optional[int] = None, invalid_hereafter: tp.Optional[int] = None, invalid_before: tp.Optional[int] = None, witness_count_add: int = 0, @@ -595,6 +627,8 @@ def calculate_tx_fee( data (optional). script_votes: An iterable of `ScriptVote`, specifying vote script data (optional). deposit: A deposit amount needed by the transaction (optional). + current_treasury_value: The current treasury value (optional). + treasury_donation: A donation to the treasury to perform (optional). invalid_hereafter: A last block when the transaction is still valid (optional). invalid_before: A first block when the transaction is valid (optional). witness_count_add: A number of witnesses to add - workaround to make the fee @@ -607,6 +641,12 @@ def calculate_tx_fee( int: An estimated fee. """ # pylint: disable=too-many-arguments + if (treasury_donation is not None) != (current_treasury_value is not None): + msg = ( + "Both `treasury_donation` and `current_treasury_value` must be specified together." + ) + raise ValueError(msg) + tx_files = tx_files or structs.TxFiles() tx_name = f"{tx_name}_estimate" @@ -641,6 +681,8 @@ def calculate_tx_fee( invalid_hereafter=invalid_hereafter or ttl, invalid_before=invalid_before, deposit=deposit, + current_treasury_value=current_treasury_value, + treasury_donation=treasury_donation, join_txouts=join_txouts, destination_dir=destination_dir, ) @@ -768,6 +810,7 @@ def build_tx( # noqa: C901 script_withdrawals: structs.OptionalScriptWithdrawals = (), script_votes: structs.OptionalScriptVotes = (), deposit: tp.Optional[int] = None, + treasury_donation: tp.Optional[int] = None, invalid_hereafter: tp.Optional[int] = None, invalid_before: tp.Optional[int] = None, witness_override: tp.Optional[int] = None, @@ -810,6 +853,7 @@ def build_tx( # noqa: C901 data (optional). script_votes: An iterable of `ScriptVote`, specifying vote script data (optional). deposit: A deposit amount needed by the transaction (optional). + treasury_donation: A donation to the treasury to perform (optional). invalid_hereafter: A last block when the transaction is still valid (optional). invalid_before: A first block when the transaction is valid (optional). witness_override: An integer indicating real number of witnesses. Can be used to fix @@ -892,6 +936,9 @@ def build_tx( # noqa: C901 if invalid_hereafter is not None: misc_args.extend(["--invalid-hereafter", str(invalid_hereafter)]) + if treasury_donation is not None: + misc_args.extend(["--treasury-donation", str(treasury_donation)]) + if not script_valid: misc_args.append("--script-invalid") @@ -980,6 +1027,7 @@ def build_tx( # noqa: C901 mint=mint, invalid_hereafter=invalid_hereafter, invalid_before=invalid_before, + treasury_donation=treasury_donation, withdrawals=collected_data.withdrawals, change_address=change_address or src_address, return_collateral_txouts=return_collateral_txouts, diff --git a/cardano_clusterlib/txtools.py b/cardano_clusterlib/txtools.py index a0548c2..2ff7e82 100644 --- a/cardano_clusterlib/txtools.py +++ b/cardano_clusterlib/txtools.py @@ -99,6 +99,7 @@ def _select_utxos( withdrawals: structs.OptionalTxOuts, min_change_value: int, deposit: int = 0, + treasury_donation: int = 0, ) -> tp.Set[str]: """Select UTxOs that can satisfy all outputs, deposits and fee. @@ -121,7 +122,7 @@ def _select_utxos( continue tx_fee = fee if fee > 1 else 1 - funds_needed = total_output_amount + tx_fee + deposit + funds_needed = total_output_amount + tx_fee + deposit + treasury_donation total_withdrawals_amount = functools.reduce(lambda x, y: x + y.amount, withdrawals, 0) # fee needs an input, even if withdrawal would cover all needed funds input_funds_needed = max(funds_needed - total_withdrawals_amount, tx_fee) @@ -150,6 +151,7 @@ def _balance_txouts( # noqa: C901 fee: int, withdrawals: structs.OptionalTxOuts, deposit: int = 0, + treasury_donation: int = 0, lovelace_balanced: bool = False, skip_asset_balancing: bool = False, ) -> tp.List[structs.TxOut]: @@ -190,7 +192,7 @@ def _balance_txouts( # noqa: C901 tx_fee = fee if fee > 0 else 0 total_withdrawals_amount = functools.reduce(lambda x, y: x + y.amount, withdrawals, 0) funds_available = total_input_amount + total_withdrawals_amount - funds_needed = total_output_amount + tx_fee + deposit + funds_needed = total_output_amount + tx_fee + deposit + treasury_donation change = funds_available - funds_needed if change < 0: LOGGER.error( @@ -492,6 +494,7 @@ def _get_tx_ins_outs( txouts: structs.OptionalTxOuts = (), fee: int = 0, deposit: tp.Optional[int] = None, + treasury_donation: tp.Optional[int] = None, withdrawals: structs.OptionalTxOuts = (), mint_txouts: structs.OptionalTxOuts = (), lovelace_balanced: bool = False, @@ -507,6 +510,7 @@ def _get_tx_ins_outs( txouts: A list (iterable) of `TxOuts`, specifying transaction outputs (optional). fee: A fee amount (optional). deposit: A deposit amount needed by the transaction (optional). + treasury_donation: A donation to the treasury to perform (optional). withdrawals: A list (iterable) of `TxOuts`, specifying reward withdrawals (optional). mint_txouts: A list (iterable) of `TxOuts`, specifying minted tokens (optional). lovelace_balanced: A bool indicating whether Lovelace ins/outs are balanced @@ -554,6 +558,8 @@ def _get_tx_ins_outs( else deposit ) + tx_treasury_donation = treasury_donation if treasury_donation is not None else 0 + if txins: # don't touch txins that were passed to the function txins_filtered = txins_all @@ -568,6 +574,7 @@ def _get_tx_ins_outs( withdrawals=withdrawals, min_change_value=clusterlib_obj._min_change_value, deposit=tx_deposit, + treasury_donation=tx_treasury_donation, ) txins_by_id: tp.Dict[str, tp.List[structs.UTXOData]] = _organize_utxos_by_id(txins_all) _txins_filtered = [utxo for uid, utxo in txins_by_id.items() if uid in selected_utxo_ids] @@ -595,6 +602,7 @@ def _get_tx_ins_outs( fee=fee, withdrawals=withdrawals, deposit=tx_deposit, + treasury_donation=tx_treasury_donation, lovelace_balanced=lovelace_balanced, skip_asset_balancing=skip_asset_balancing, ) @@ -616,6 +624,7 @@ def collect_data_for_build( withdrawals: structs.OptionalTxOuts = (), script_withdrawals: structs.OptionalScriptWithdrawals = (), deposit: tp.Optional[int] = None, + treasury_donation: tp.Optional[int] = None, lovelace_balanced: bool = False, skip_asset_balancing: bool = False, ) -> structs.DataForBuild: @@ -639,6 +648,7 @@ def collect_data_for_build( script_withdrawals: An iterable of `ScriptWithdrawal`, specifying withdrawal script data (optional). deposit: A deposit amount needed by the transaction (optional). + treasury_donation: A donation to the treasury to perform (optional). lovelace_balanced: A bool indicating whether Lovelace ins/outs are balanced (by `build` command). skip_asset_balancing: A bool indicating if assets balancing should be skipped @@ -688,6 +698,7 @@ def collect_data_for_build( txouts=txouts, fee=fee, deposit=deposit, + treasury_donation=treasury_donation, withdrawals=withdrawals_txouts, mint_txouts=mint_txouts, lovelace_balanced=lovelace_balanced, From 2d597c2abfd96d237cb8f1744cc1efb6263969d0 Mon Sep 17 00:00:00 2001 From: Martin Kourim Date: Fri, 16 Aug 2024 15:28:09 +0200 Subject: [PATCH 2/5] Remove unnecessary checks This check will be performed by `build_raw_tx_bare` eventually. --- cardano_clusterlib/transaction_group.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/cardano_clusterlib/transaction_group.py b/cardano_clusterlib/transaction_group.py index c3da42b..507d7a4 100644 --- a/cardano_clusterlib/transaction_group.py +++ b/cardano_clusterlib/transaction_group.py @@ -447,12 +447,6 @@ def build_raw_tx( structs.TxRawOutput: A tuple with transaction output details. """ # pylint: disable=too-many-arguments - if (treasury_donation is not None) != (current_treasury_value is not None): - msg = ( - "Both `treasury_donation` and `current_treasury_value` must be specified together." - ) - raise ValueError(msg) - destination_dir = pl.Path(destination_dir).expanduser() out_file = destination_dir / f"{tx_name}_tx.body" clusterlib_helpers._check_files_exist(out_file, clusterlib_obj=self._clusterlib_obj) @@ -641,12 +635,6 @@ def calculate_tx_fee( int: An estimated fee. """ # pylint: disable=too-many-arguments - if (treasury_donation is not None) != (current_treasury_value is not None): - msg = ( - "Both `treasury_donation` and `current_treasury_value` must be specified together." - ) - raise ValueError(msg) - tx_files = tx_files or structs.TxFiles() tx_name = f"{tx_name}_estimate" From 7e51f4d271e55f5ab648bc2d222509e7362fe925 Mon Sep 17 00:00:00 2001 From: Martin Kourim Date: Fri, 16 Aug 2024 15:31:00 +0200 Subject: [PATCH 3/5] We raise `AssertionError` in similar cases in other places --- cardano_clusterlib/transaction_group.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cardano_clusterlib/transaction_group.py b/cardano_clusterlib/transaction_group.py index 507d7a4..10c570a 100644 --- a/cardano_clusterlib/transaction_group.py +++ b/cardano_clusterlib/transaction_group.py @@ -218,7 +218,7 @@ def build_raw_tx_bare( # noqa: C901 msg = ( "Both `treasury_donation` and `current_treasury_value` must be specified together." ) - raise ValueError(msg) + raise AssertionError(msg) if tx_files.certificate_files and complex_certs: LOGGER.warning( From e21e5a4b093bd662b53c72fdd876a76bea3aee12 Mon Sep 17 00:00:00 2001 From: Martin Kourim Date: Fri, 16 Aug 2024 15:32:10 +0200 Subject: [PATCH 4/5] Add the missing arg to the `collect_data_for_build` call --- cardano_clusterlib/transaction_group.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cardano_clusterlib/transaction_group.py b/cardano_clusterlib/transaction_group.py index 10c570a..96aa9c1 100644 --- a/cardano_clusterlib/transaction_group.py +++ b/cardano_clusterlib/transaction_group.py @@ -899,6 +899,7 @@ def build_tx( # noqa: C901 withdrawals=withdrawals, script_withdrawals=script_withdrawals, deposit=deposit, + treasury_donation=treasury_donation, lovelace_balanced=True, skip_asset_balancing=skip_asset_balancing, ) From c429df6fe4d86dd6071942f5fe80295d6defa4ff Mon Sep 17 00:00:00 2001 From: Martin Kourim Date: Fri, 16 Aug 2024 15:33:17 +0200 Subject: [PATCH 5/5] Add the arguments to `send_tx` as well --- cardano_clusterlib/transaction_group.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cardano_clusterlib/transaction_group.py b/cardano_clusterlib/transaction_group.py index 96aa9c1..1573f38 100644 --- a/cardano_clusterlib/transaction_group.py +++ b/cardano_clusterlib/transaction_group.py @@ -1244,6 +1244,8 @@ def send_tx( script_withdrawals: structs.OptionalScriptWithdrawals = (), script_votes: structs.OptionalScriptVotes = (), deposit: tp.Optional[int] = None, + current_treasury_value: tp.Optional[int] = None, + treasury_donation: tp.Optional[int] = None, invalid_hereafter: tp.Optional[int] = None, invalid_before: tp.Optional[int] = None, witness_count_add: int = 0, @@ -1291,6 +1293,8 @@ def send_tx( data (optional). script_votes: An iterable of `ScriptVote`, specifying vote script data (optional). deposit: A deposit amount needed by the transaction (optional). + current_treasury_value: The current treasury value (optional). + treasury_donation: A donation to the treasury to perform (optional). invalid_hereafter: A last block when the transaction is still valid (optional). invalid_before: A first block when the transaction is valid (optional). witness_count_add: A number of witnesses to add - workaround to make the fee @@ -1335,6 +1339,8 @@ def send_tx( withdrawals=withdrawals, script_withdrawals=script_withdrawals, deposit=deposit, + current_treasury_value=current_treasury_value, + treasury_donation=treasury_donation, invalid_hereafter=invalid_hereafter or ttl, witness_count_add=witness_count_add, join_txouts=join_txouts, @@ -1365,6 +1371,8 @@ def send_tx( script_withdrawals=script_withdrawals, script_votes=script_votes, deposit=deposit, + current_treasury_value=current_treasury_value, + treasury_donation=treasury_donation, invalid_hereafter=invalid_hereafter or ttl, invalid_before=invalid_before, join_txouts=join_txouts,