Skip to content

Commit

Permalink
Merge pull request #25 from mkoura/plutus_support
Browse files Browse the repository at this point in the history
Add basic support for plutus-related commands and arguments
  • Loading branch information
mkoura committed Jun 17, 2021
2 parents 7d6329c + 0c03705 commit 51f8fef
Showing 1 changed file with 112 additions and 14 deletions.
126 changes: 112 additions & 14 deletions cardano_clusterlib/clusterlib.py
Expand Up @@ -65,23 +65,38 @@ class UTXOData(NamedTuple):
amount: int
address: str
coin: str = DEFAULT_COIN
datum_hash: str = ""


class TxOut(NamedTuple):
address: str
amount: int
coin: str = DEFAULT_COIN
datum_hash: str = ""


class PlutusTxIn(NamedTuple):
txin: UTXOData
collateral: UTXOData
script_file: FileType
execution_units: Tuple[int, int]
datum_file: FileType = ""
datum_value: str = ""
redeemer_file: FileType = ""
redeemer_value: str = ""


# list of `TxOut`s, empty list, or empty tuple
OptionalTxOuts = Union[List[TxOut], Tuple[()]]
# list of `UTXOData`s, empty list, or empty tuple
OptionalUTXOData = Union[List[UTXOData], Tuple[()]]
# list of `PlutusTxIn`s, empty list, or empty tuple
OptionalPlutusTxIns = Union[List[PlutusTxIn], Tuple[()]]


class ScriptFiles(NamedTuple):
txin_scripts: OptionalFiles = ()
minting_scripts: OptionalFiles = ()
minting_scripts: OptionalFiles = () # TODO: rename to `mint_scripts`
certificate_scripts: OptionalFiles = ()
withdrawal_scripts: OptionalFiles = ()
auxiliary_scripts: OptionalFiles = ()
Expand Down Expand Up @@ -122,6 +137,7 @@ class TxRawOutput(NamedTuple):
tx_files: TxFiles
out_file: Path
fee: int
plutus_txins: OptionalPlutusTxIns = ()
invalid_hereafter: Optional[int] = None
invalid_before: Optional[int] = None
withdrawals: OptionalTxOuts = ()
Expand Down Expand Up @@ -483,6 +499,7 @@ def get_utxo(self, address: str, coins: UnpackableSequence = ()) -> List[UTXODat
for utxo_rec, utxo_data in utxo_dict.items():
utxo_hash, utxo_ix = utxo_rec.split("#")
addr_data = utxo_data["value"]
datum_hash = utxo_data.get("data") or ""
for policyid, coin_data in addr_data.items():
if policyid == DEFAULT_COIN:
utxo.append(
Expand All @@ -492,6 +509,7 @@ def get_utxo(self, address: str, coins: UnpackableSequence = ()) -> List[UTXODat
amount=coin_data,
address=address,
coin=DEFAULT_COIN,
datum_hash=datum_hash,
)
)
continue
Expand All @@ -503,6 +521,7 @@ def get_utxo(self, address: str, coins: UnpackableSequence = ()) -> List[UTXODat
amount=amount,
address=address,
coin=f"{policyid}.{asset_name}" if asset_name else policyid,
datum_hash=datum_hash,
)
)

Expand Down Expand Up @@ -641,8 +660,8 @@ def gen_script_addr(
self.cli(
[
"address",
"build-script",
"--script-file",
"build",
"--payment-script-file",
str(script_file),
*self.magic_args,
"--out-file",
Expand Down Expand Up @@ -1363,11 +1382,12 @@ def get_txid(self, tx_body_file: FileType = "", tx_file: FileType = "") -> str:
Returns:
str: A transaction ID.
"""
cli_args = []
if tx_body_file:
cli_args = ["--tx-body-file", str(tx_body_file)]
elif tx_file:
cli_args = ["--tx-file", str(tx_file)]
else:
raise CLIError("Either `tx_body_file` or `tx_file` is needed.")

return self.cli(["transaction", "txid", *cli_args]).stdout.rstrip().decode("ascii")

Expand All @@ -1386,10 +1406,33 @@ def view_tx(self, tx_body_file: FileType = "", tx_file: FileType = "") -> str:
elif tx_file:
cli_args = ["--tx-file", str(tx_file)]
else:
raise CLIError("Either ``tx_body_file` or `tx_file` is needed.")
raise CLIError("Either `tx_body_file` or `tx_file` is needed.")

return self.cli(["transaction", "view", *cli_args]).stdout.rstrip().decode("utf-8")

def get_hash_script_data(
self, script_data_file: FileType = "", script_data_value: str = ""
) -> str:
"""Return the hash of script data.
Args:
script_data_file: A path to the file containing the script data (optional).
script_data_value: A value (in JSON syntax) for the script data (optional).
Returns:
str: A hash of script data.
"""
if script_data_file:
cli_args = ["--script-data-file", str(script_data_file)]
elif script_data_value:
cli_args = ["--script-data-value", str(script_data_value)]
else:
raise CLIError("Either `script_data_file` or `script_data_value` is needed.")

return (
self.cli(["transaction", "hash-script-data", *cli_args]).stdout.rstrip().decode("ascii")
)

def address_info(
self,
address: str,
Expand Down Expand Up @@ -1712,13 +1755,14 @@ def get_withdrawals(self, withdrawals: List[TxOut]) -> List[TxOut]:

return resolved_withdrawals

def build_raw_tx_bare(
def build_raw_tx_bare( # noqa: C901
self,
out_file: FileType,
txins: List[UTXOData],
txouts: List[TxOut],
tx_files: TxFiles,
fee: int,
txins: OptionalUTXOData = (),
plutus_txins: OptionalPlutusTxIns = (),
ttl: Optional[int] = None,
withdrawals: OptionalTxOuts = (),
invalid_hereafter: Optional[int] = None,
Expand All @@ -1730,10 +1774,11 @@ def build_raw_tx_bare(
Args:
out_file: An output file.
txins: An iterable of `UTXOData`, specifying input UTxOs.
txouts: A list (iterable) of `TxOuts`, specifying transaction outputs.
tx_files: A `TxFiles` tuple containing files needed for the transaction.
fee: A fee amount.
txins: An iterable of `UTXOData`, specifying input UTxOs.
plutus_txins: An iterable of `PlutusTxIn`, specifying input Plutus UTxOs.
ttl: A last block when the transaction is still valid
(deprecated in favor of `invalid_hereafter`, optional).
withdrawals: A list (iterable) of `TxOuts`, specifying reward withdrawals (optional).
Expand All @@ -1746,13 +1791,16 @@ def build_raw_tx_bare(
Returns:
TxRawOutput: A tuple with transaction output details.
"""
# pylint: disable=too-many-arguments
# pylint: disable=too-many-arguments,too-many-statements,too-many-branches,too-many-locals
out_file = Path(out_file)

# exclude TxOuts with datum_hash for now
txouts_no_datum = [t for t in txouts if not t.datum_hash]

if join_txouts:
# aggregate TX outputs by address
txouts_by_addr: Dict[str, List[str]] = {}
for rec in txouts:
for rec in txouts_no_datum:
if rec.address not in txouts_by_addr:
txouts_by_addr[rec.address] = []
coin = f" {rec.coin}" if rec.coin and rec.coin != DEFAULT_COIN else ""
Expand All @@ -1764,7 +1812,7 @@ def build_raw_tx_bare(
amounts_joined = "+".join(amounts)
txout_args.append(f"{addr}+{amounts_joined}")
else:
txout_args = [f"{rec.address}+{rec.amount}" for rec in txouts]
txout_args = [f"{rec.address}+{rec.amount}" for rec in txouts_no_datum]

# filter out duplicate txins
txins_combined = {f"{x.utxo_hash}#{x.utxo_ix}" for x in txins}
Expand All @@ -1787,7 +1835,7 @@ def build_raw_tx_bare(
if tx_files.script_files:
script_args = [
*self._prepend_flag("--txin-script-file", tx_files.script_files.txin_scripts),
*self._prepend_flag("--minting-script-file", tx_files.script_files.minting_scripts),
*self._prepend_flag("--mint-script-file", tx_files.script_files.minting_scripts),
*self._prepend_flag(
"--certificate-script-file", tx_files.script_files.certificate_scripts
),
Expand All @@ -1799,6 +1847,53 @@ def build_raw_tx_bare(
),
]

plutus_txout_args = []
for tout in txouts:
if not tout.datum_hash:
continue
plutus_txout_args.extend(
[
"--tx-out",
f"{tout.address}+{tout.amount}",
"--tx-out-datum-hash",
tout.datum_hash,
]
)

plutus_txin_args = []
for tin in plutus_txins:
tin_args = []
tin_args.extend(
[
"--tx-in",
f"{tin.txin.utxo_hash}#{tin.txin.utxo_ix}",
"--tx-in-collateral",
f"{tin.collateral.utxo_hash}#{tin.collateral.utxo_ix}",
"--tx-in-script-file",
str(tin.script_file),
"--tx-in-execution-units",
f"({tin.execution_units[0]},{tin.execution_units[1]})",
]
)
if tin.datum_file:
tin_args.extend(["--tx-in-datum-file", str(tin.datum_file)])
if tin.datum_value:
tin_args.extend(["--tx-in-datum-value", str(tin.datum_value)])
if tin.redeemer_file:
tin_args.extend(["--tx-in-redeemer-file", str(tin.redeemer_file)])
if tin.redeemer_value:
tin_args.extend(["--tx-in-redeemer-value", str(tin.redeemer_value)])

plutus_txin_args.extend(tin_args)

if plutus_txin_args:
plutus_txin_args.extend(
[
"--protocol-params-file",
str(self.pparams_file),
]
)

self.cli(
[
"transaction",
Expand All @@ -1807,7 +1902,9 @@ def build_raw_tx_bare(
str(fee),
"--out-file",
str(out_file),
*plutus_txin_args,
*self._prepend_flag("--tx-in", txins_combined),
*plutus_txout_args,
*self._prepend_flag("--tx-out", txout_args),
*self._prepend_flag("--certificate-file", tx_files.certificate_files),
*self._prepend_flag("--update-proposal-file", tx_files.proposal_files),
Expand All @@ -1822,7 +1919,8 @@ def build_raw_tx_bare(
)

return TxRawOutput(
txins=txins,
txins=list(txins),
plutus_txins=plutus_txins,
txouts=txouts,
tx_files=tx_files,
out_file=out_file,
Expand Down Expand Up @@ -1896,10 +1994,10 @@ def build_raw_tx(

tx_raw_output = self.build_raw_tx_bare(
out_file=out_file,
txins=txins_copy,
txouts=txouts_copy,
tx_files=tx_files,
fee=fee,
txins=txins_copy,
withdrawals=withdrawals,
invalid_hereafter=invalid_hereafter or ttl,
invalid_before=invalid_before,
Expand Down

0 comments on commit 51f8fef

Please sign in to comment.