1# This file is part of the Trezor project. 2# 3# Copyright (C) 2012-2022 SatoshiLabs and contributors 4# 5# This library is free software: you can redistribute it and/or modify 6# it under the terms of the GNU Lesser General Public License version 3 7# as published by the Free Software Foundation. 8# 9# This library is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU Lesser General Public License for more details. 13# 14# You should have received a copy of the License along with this library. 15# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>. 16 17import json 18from typing import TYPE_CHECKING, Optional, TextIO 19 20import click 21 22from .. import cardano, messages, tools 23from . import ChoiceType, with_client 24 25if TYPE_CHECKING: 26 from ..client import TrezorClient 27 28PATH_HELP = "BIP-32 path to key, e.g. m/44'/1815'/0'/0/0" 29 30 31@click.group(name="cardano") 32def cli() -> None: 33 """Cardano commands.""" 34 35 36@cli.command() 37@click.argument("file", type=click.File("r")) 38@click.option("-f", "--file", "_ignore", is_flag=True, hidden=True, expose_value=False) 39@click.option( 40 "-s", 41 "--signing-mode", 42 required=True, 43 type=ChoiceType({m.name: m for m in messages.CardanoTxSigningMode}), 44) 45@click.option( 46 "-p", "--protocol-magic", type=int, default=cardano.PROTOCOL_MAGICS["mainnet"] 47) 48@click.option("-N", "--network-id", type=int, default=cardano.NETWORK_IDS["mainnet"]) 49@click.option("-t", "--testnet", is_flag=True) 50@click.option( 51 "-D", 52 "--derivation-type", 53 type=ChoiceType({m.name: m for m in messages.CardanoDerivationType}), 54 default=messages.CardanoDerivationType.ICARUS, 55) 56@with_client 57def sign_tx( 58 client: "TrezorClient", 59 file: TextIO, 60 signing_mode: messages.CardanoTxSigningMode, 61 protocol_magic: int, 62 network_id: int, 63 testnet: bool, 64 derivation_type: messages.CardanoDerivationType, 65) -> cardano.SignTxResponse: 66 """Sign Cardano transaction.""" 67 transaction = json.load(file) 68 69 if testnet: 70 protocol_magic = cardano.PROTOCOL_MAGICS["testnet"] 71 network_id = cardano.NETWORK_IDS["testnet"] 72 73 inputs = [cardano.parse_input(input) for input in transaction["inputs"]] 74 outputs = [cardano.parse_output(output) for output in transaction["outputs"]] 75 fee = transaction["fee"] 76 ttl = transaction.get("ttl") 77 validity_interval_start = transaction.get("validity_interval_start") 78 certificates = [ 79 cardano.parse_certificate(certificate) 80 for certificate in transaction.get("certificates", ()) 81 ] 82 withdrawals = [ 83 cardano.parse_withdrawal(withdrawal) 84 for withdrawal in transaction.get("withdrawals", ()) 85 ] 86 auxiliary_data = cardano.parse_auxiliary_data(transaction.get("auxiliary_data")) 87 mint = cardano.parse_mint(transaction.get("mint", ())) 88 additional_witness_requests = [ 89 cardano.parse_additional_witness_request(p) 90 for p in transaction["additional_witness_requests"] 91 ] 92 93 client.init_device(derive_cardano=True) 94 sign_tx_response = cardano.sign_tx( 95 client, 96 signing_mode, 97 inputs, 98 outputs, 99 fee, 100 ttl, 101 validity_interval_start, 102 certificates, 103 withdrawals, 104 protocol_magic, 105 network_id, 106 auxiliary_data, 107 mint, 108 additional_witness_requests, 109 derivation_type=derivation_type, 110 ) 111 112 sign_tx_response["tx_hash"] = sign_tx_response["tx_hash"].hex() 113 sign_tx_response["witnesses"] = [ 114 { 115 "type": witness["type"], 116 "pub_key": witness["pub_key"].hex(), 117 "signature": witness["signature"].hex(), 118 "chain_code": witness["chain_code"].hex() 119 if witness["chain_code"] is not None 120 else None, 121 } 122 for witness in sign_tx_response["witnesses"] 123 ] 124 auxiliary_data_supplement = sign_tx_response.get("auxiliary_data_supplement") 125 if auxiliary_data_supplement: 126 auxiliary_data_supplement["auxiliary_data_hash"] = auxiliary_data_supplement[ 127 "auxiliary_data_hash" 128 ].hex() 129 catalyst_signature = auxiliary_data_supplement.get("catalyst_signature") 130 if catalyst_signature: 131 auxiliary_data_supplement["catalyst_signature"] = catalyst_signature.hex() 132 sign_tx_response["auxiliary_data_supplement"] = auxiliary_data_supplement 133 return sign_tx_response 134 135 136@cli.command() 137@click.option("-n", "--address", type=str, default="", help=PATH_HELP) 138@click.option("-d", "--show-display", is_flag=True) 139@click.option( 140 "-t", 141 "--address-type", 142 type=ChoiceType({m.name: m for m in messages.CardanoAddressType}), 143 default="BASE", 144) 145@click.option("-s", "--staking-address", type=str, default="") 146@click.option("-h", "--staking-key-hash", type=str, default=None) 147@click.option("-b", "--block_index", type=int, default=None) 148@click.option("-x", "--tx_index", type=int, default=None) 149@click.option("-c", "--certificate_index", type=int, default=None) 150@click.option("--script-payment-hash", type=str, default=None) 151@click.option("--script-staking-hash", type=str, default=None) 152@click.option( 153 "-p", "--protocol-magic", type=int, default=cardano.PROTOCOL_MAGICS["mainnet"] 154) 155@click.option("-N", "--network-id", type=int, default=cardano.NETWORK_IDS["mainnet"]) 156@click.option("-e", "--testnet", is_flag=True) 157@click.option( 158 "-D", 159 "--derivation-type", 160 type=ChoiceType({m.name: m for m in messages.CardanoDerivationType}), 161 default=messages.CardanoDerivationType.ICARUS, 162) 163@with_client 164def get_address( 165 client: "TrezorClient", 166 address: str, 167 address_type: messages.CardanoAddressType, 168 staking_address: str, 169 staking_key_hash: Optional[str], 170 block_index: Optional[int], 171 tx_index: Optional[int], 172 certificate_index: Optional[int], 173 script_payment_hash: Optional[str], 174 script_staking_hash: Optional[str], 175 protocol_magic: int, 176 network_id: int, 177 show_display: bool, 178 testnet: bool, 179 derivation_type: messages.CardanoDerivationType, 180) -> str: 181 """ 182 Get Cardano address. 183 184 All address types require the address, address_type, protocol_magic and 185 network_id parameters. 186 187 When deriving a base address you can choose to include staking info as 188 staking_address or staking_key_hash - one has to be chosen. 189 190 When deriving a pointer address you need to specify the block_index, 191 tx_index and certificate_index parameters. 192 193 Byron, enterprise and reward addresses only require the general parameters. 194 """ 195 if testnet: 196 protocol_magic = cardano.PROTOCOL_MAGICS["testnet"] 197 network_id = cardano.NETWORK_IDS["testnet"] 198 199 staking_key_hash_bytes = cardano.parse_optional_bytes(staking_key_hash) 200 script_payment_hash_bytes = cardano.parse_optional_bytes(script_payment_hash) 201 script_staking_hash_bytes = cardano.parse_optional_bytes(script_staking_hash) 202 203 address_parameters = cardano.create_address_parameters( 204 address_type, 205 tools.parse_path(address), 206 tools.parse_path(staking_address), 207 staking_key_hash_bytes, 208 block_index, 209 tx_index, 210 certificate_index, 211 script_payment_hash_bytes, 212 script_staking_hash_bytes, 213 ) 214 215 client.init_device(derive_cardano=True) 216 return cardano.get_address( 217 client, 218 address_parameters, 219 protocol_magic, 220 network_id, 221 show_display, 222 derivation_type=derivation_type, 223 ) 224 225 226@cli.command() 227@click.option("-n", "--address", required=True, help=PATH_HELP) 228@click.option( 229 "-D", 230 "--derivation-type", 231 type=ChoiceType({m.name: m for m in messages.CardanoDerivationType}), 232 default=messages.CardanoDerivationType.ICARUS, 233) 234@with_client 235def get_public_key( 236 client: "TrezorClient", 237 address: str, 238 derivation_type: messages.CardanoDerivationType, 239) -> messages.CardanoPublicKey: 240 """Get Cardano public key.""" 241 address_n = tools.parse_path(address) 242 client.init_device(derive_cardano=True) 243 return cardano.get_public_key(client, address_n, derivation_type=derivation_type) 244 245 246@cli.command() 247@click.argument("file", type=click.File("r")) 248@click.option( 249 "-d", 250 "--display-format", 251 type=ChoiceType({m.name: m for m in messages.CardanoNativeScriptHashDisplayFormat}), 252 default="HIDE", 253) 254@click.option( 255 "-D", 256 "--derivation-type", 257 type=ChoiceType({m.name: m for m in messages.CardanoDerivationType}), 258 default=messages.CardanoDerivationType.ICARUS, 259) 260@with_client 261def get_native_script_hash( 262 client: "TrezorClient", 263 file: TextIO, 264 display_format: messages.CardanoNativeScriptHashDisplayFormat, 265 derivation_type: messages.CardanoDerivationType, 266) -> messages.CardanoNativeScriptHash: 267 """Get Cardano native script hash.""" 268 native_script_json = json.load(file) 269 native_script = cardano.parse_native_script(native_script_json) 270 271 client.init_device(derive_cardano=True) 272 return cardano.get_native_script_hash( 273 client, native_script, display_format, derivation_type=derivation_type 274 ) 275