mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-11 16:00:57 +00:00
refactor(cardano): introduce Signer and its subclasses
This commit is contained in:
parent
b1bee00a3a
commit
2724d29968
@ -523,6 +523,16 @@ if not utils.BITCOIN_ONLY:
|
||||
import apps.cardano.seed
|
||||
apps.cardano.sign_tx
|
||||
import apps.cardano.sign_tx
|
||||
apps.cardano.sign_tx.multisig_signer
|
||||
import apps.cardano.sign_tx.multisig_signer
|
||||
apps.cardano.sign_tx.ordinary_signer
|
||||
import apps.cardano.sign_tx.ordinary_signer
|
||||
apps.cardano.sign_tx.plutus_signer
|
||||
import apps.cardano.sign_tx.plutus_signer
|
||||
apps.cardano.sign_tx.pool_owner_signer
|
||||
import apps.cardano.sign_tx.pool_owner_signer
|
||||
apps.cardano.sign_tx.signer
|
||||
import apps.cardano.sign_tx.signer
|
||||
apps.common.mnemonic
|
||||
import apps.common.mnemonic
|
||||
apps.eos
|
||||
|
@ -1,10 +1,6 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor.enums import (
|
||||
CardanoCertificateType,
|
||||
CardanoPoolRelayType,
|
||||
CardanoTxSigningMode,
|
||||
)
|
||||
from trezor.enums import CardanoCertificateType, CardanoPoolRelayType
|
||||
|
||||
from apps.common import cbor
|
||||
|
||||
@ -41,22 +37,10 @@ MAX_PORT_NUMBER = 65535
|
||||
|
||||
def validate_certificate(
|
||||
certificate: CardanoTxCertificate,
|
||||
signing_mode: CardanoTxSigningMode,
|
||||
protocol_magic: int,
|
||||
network_id: int,
|
||||
account_path_checker: AccountPathChecker,
|
||||
) -> None:
|
||||
if (
|
||||
signing_mode != CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER
|
||||
and certificate.type == CardanoCertificateType.STAKE_POOL_REGISTRATION
|
||||
):
|
||||
raise INVALID_CERTIFICATE
|
||||
elif (
|
||||
signing_mode == CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER
|
||||
and certificate.type != CardanoCertificateType.STAKE_POOL_REGISTRATION
|
||||
):
|
||||
raise INVALID_CERTIFICATE
|
||||
|
||||
_validate_certificate_structure(certificate)
|
||||
|
||||
if certificate.type in (
|
||||
@ -68,7 +52,6 @@ def validate_certificate(
|
||||
certificate.path,
|
||||
certificate.script_hash,
|
||||
certificate.key_hash,
|
||||
signing_mode,
|
||||
INVALID_CERTIFICATE,
|
||||
)
|
||||
|
||||
|
@ -6,8 +6,8 @@ from trezor.messages import CardanoAddress
|
||||
from . import seed
|
||||
from .address import derive_human_readable_address, validate_address_parameters
|
||||
from .helpers.credential import Credential, should_show_address_credentials
|
||||
from .helpers.utils import validate_network_info
|
||||
from .layout import show_address_credentials, show_cardano_address
|
||||
from .sign_tx import validate_network_info
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor.messages import (
|
||||
|
@ -1,8 +1,9 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor import wire
|
||||
from trezor.crypto import hashlib
|
||||
from trezor.enums import CardanoTxSigningMode
|
||||
|
||||
from apps.cardano.helpers import network_ids, protocol_magics
|
||||
from apps.cardano.helpers.paths import (
|
||||
ACCOUNT_PATH_INDEX,
|
||||
SCHEMA_STAKING_ANY_ACCOUNT,
|
||||
@ -13,7 +14,6 @@ from apps.common.seed import remove_ed25519_prefix
|
||||
from . import ADDRESS_KEY_HASH_SIZE, SCRIPT_HASH_SIZE, bech32
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from trezor import wire
|
||||
from .. import seed
|
||||
|
||||
|
||||
@ -84,32 +84,27 @@ def validate_stake_credential(
|
||||
path: list[int],
|
||||
script_hash: bytes | None,
|
||||
key_hash: bytes | None,
|
||||
signing_mode: CardanoTxSigningMode,
|
||||
error: wire.ProcessError,
|
||||
) -> None:
|
||||
if sum(bool(k) for k in (path, script_hash, key_hash)) != 1:
|
||||
raise error
|
||||
|
||||
if path:
|
||||
if signing_mode not in (
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
CardanoTxSigningMode.PLUTUS_TRANSACTION,
|
||||
):
|
||||
raise error
|
||||
if not SCHEMA_STAKING_ANY_ACCOUNT.match(path):
|
||||
raise error
|
||||
elif script_hash:
|
||||
if signing_mode not in (
|
||||
CardanoTxSigningMode.MULTISIG_TRANSACTION,
|
||||
CardanoTxSigningMode.PLUTUS_TRANSACTION,
|
||||
):
|
||||
raise error
|
||||
if len(script_hash) != SCRIPT_HASH_SIZE:
|
||||
raise error
|
||||
elif key_hash:
|
||||
if signing_mode != CardanoTxSigningMode.PLUTUS_TRANSACTION:
|
||||
raise error
|
||||
if len(key_hash) != ADDRESS_KEY_HASH_SIZE:
|
||||
raise error
|
||||
else:
|
||||
if path and not SCHEMA_STAKING_ANY_ACCOUNT.match(path):
|
||||
raise error
|
||||
if script_hash and len(script_hash) != SCRIPT_HASH_SIZE:
|
||||
raise error
|
||||
if key_hash and len(key_hash) != ADDRESS_KEY_HASH_SIZE:
|
||||
raise error
|
||||
|
||||
|
||||
def validate_network_info(network_id: int, protocol_magic: int) -> None:
|
||||
"""
|
||||
We are only concerned about checking that both network_id and protocol_magic
|
||||
belong to the mainnet or that both belong to a testnet. We don't need to check for
|
||||
consistency between various testnets (at least for now).
|
||||
"""
|
||||
is_mainnet_network_id = network_ids.is_mainnet(network_id)
|
||||
is_mainnet_protocol_magic = protocol_magics.is_mainnet(protocol_magic)
|
||||
|
||||
if is_mainnet_network_id != is_mainnet_protocol_magic:
|
||||
raise wire.ProcessError("Invalid network id/protocol magic combination!")
|
||||
|
@ -7,7 +7,6 @@ from trezor.enums import (
|
||||
CardanoCertificateType,
|
||||
CardanoNativeScriptHashDisplayFormat,
|
||||
CardanoNativeScriptType,
|
||||
CardanoTxSigningMode,
|
||||
)
|
||||
from trezor.messages import CardanoAddressParametersType
|
||||
from trezor.strings import format_amount
|
||||
@ -193,26 +192,25 @@ async def show_script_hash(
|
||||
)
|
||||
|
||||
|
||||
async def show_transaction_signing_mode(
|
||||
ctx: wire.Context, signing_mode: CardanoTxSigningMode
|
||||
) -> None:
|
||||
if signing_mode == CardanoTxSigningMode.MULTISIG_TRANSACTION:
|
||||
await confirm_metadata(
|
||||
ctx,
|
||||
"confirm_signing_mode",
|
||||
title="Confirm transaction",
|
||||
content="Confirming a multisig transaction.",
|
||||
larger_vspace=True,
|
||||
br_code=ButtonRequestType.Other,
|
||||
)
|
||||
elif signing_mode == CardanoTxSigningMode.PLUTUS_TRANSACTION:
|
||||
await confirm_metadata(
|
||||
ctx,
|
||||
"confirm_signing_mode",
|
||||
title="Confirm transaction",
|
||||
content="Confirming a Plutus transaction - loss of collateral is possible. Check all items carefully.",
|
||||
br_code=ButtonRequestType.Other,
|
||||
)
|
||||
async def show_multisig_transaction(ctx: wire.Context) -> None:
|
||||
await confirm_metadata(
|
||||
ctx,
|
||||
"confirm_signing_mode",
|
||||
title="Confirm transaction",
|
||||
content="Confirming a multisig transaction.",
|
||||
larger_vspace=True,
|
||||
br_code=ButtonRequestType.Other,
|
||||
)
|
||||
|
||||
|
||||
async def show_plutus_transaction(ctx: wire.Context) -> None:
|
||||
await confirm_metadata(
|
||||
ctx,
|
||||
"confirm_signing_mode",
|
||||
title="Confirm transaction",
|
||||
content="Confirming a Plutus transaction - loss of collateral is possible. Check all items carefully.",
|
||||
br_code=ButtonRequestType.Other,
|
||||
)
|
||||
|
||||
|
||||
async def confirm_input(ctx: wire.Context, input: CardanoTxInput) -> None:
|
||||
|
File diff suppressed because it is too large
Load Diff
44
core/src/apps/cardano/sign_tx/__init__.py
Normal file
44
core/src/apps/cardano/sign_tx/__init__.py
Normal file
@ -0,0 +1,44 @@
|
||||
from typing import Type
|
||||
|
||||
from trezor import log, wire
|
||||
from trezor.enums import CardanoTxSigningMode
|
||||
from trezor.messages import CardanoSignTxFinished, CardanoSignTxInit
|
||||
|
||||
from .. import seed
|
||||
from .signer import Signer
|
||||
|
||||
|
||||
@seed.with_keychain
|
||||
async def sign_tx(
|
||||
ctx: wire.Context, msg: messages.CardanoSignTxInit, keychain: seed.Keychain
|
||||
) -> messages.CardanoSignTxFinished:
|
||||
signer_type: Type[Signer]
|
||||
if msg.signing_mode == CardanoTxSigningMode.ORDINARY_TRANSACTION:
|
||||
from .ordinary_signer import OrdinarySigner
|
||||
|
||||
signer_type = OrdinarySigner
|
||||
elif msg.signing_mode == CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER:
|
||||
from .pool_owner_signer import PoolOwnerSigner
|
||||
|
||||
signer_type = PoolOwnerSigner
|
||||
elif msg.signing_mode == CardanoTxSigningMode.MULTISIG_TRANSACTION:
|
||||
from .multisig_signer import MultisigSigner
|
||||
|
||||
signer_type = MultisigSigner
|
||||
elif msg.signing_mode == CardanoTxSigningMode.PLUTUS_TRANSACTION:
|
||||
from .plutus_signer import PlutusSigner
|
||||
|
||||
signer_type = PlutusSigner
|
||||
else:
|
||||
raise RuntimeError # should be unreachable
|
||||
|
||||
signer = signer_type(ctx, msg, keychain)
|
||||
|
||||
try:
|
||||
await signer.sign()
|
||||
except ValueError as e:
|
||||
if __debug__:
|
||||
log.exception(__name__, e)
|
||||
raise wire.ProcessError("Signing failed")
|
||||
|
||||
return CardanoSignTxFinished()
|
88
core/src/apps/cardano/sign_tx/multisig_signer.py
Normal file
88
core/src/apps/cardano/sign_tx/multisig_signer.py
Normal file
@ -0,0 +1,88 @@
|
||||
from trezor import wire
|
||||
from trezor.enums import CardanoCertificateType
|
||||
from trezor.messages import (
|
||||
CardanoSignTxInit,
|
||||
CardanoTxCertificate,
|
||||
CardanoTxOutput,
|
||||
CardanoTxWithdrawal,
|
||||
CardanoTxWitnessRequest,
|
||||
)
|
||||
|
||||
from .. import seed
|
||||
from ..helpers import (
|
||||
INVALID_CERTIFICATE,
|
||||
INVALID_OUTPUT,
|
||||
INVALID_TX_SIGNING_REQUEST,
|
||||
INVALID_WITHDRAWAL,
|
||||
INVALID_WITNESS_REQUEST,
|
||||
)
|
||||
from ..helpers.paths import SCHEMA_MINT
|
||||
from ..layout import show_multisig_transaction
|
||||
from ..seed import is_multisig_path
|
||||
from .signer import Signer
|
||||
|
||||
|
||||
class MultisigSigner(Signer):
|
||||
"""
|
||||
The multisig signing mode only allows signing with multisig (and minting) keys.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, ctx: wire.Context, msg: CardanoSignTxInit, keychain: seed.Keychain
|
||||
) -> None:
|
||||
super().__init__(ctx, msg, keychain)
|
||||
|
||||
def _validate_tx_signing_request(self) -> None:
|
||||
super()._validate_tx_signing_request()
|
||||
if (
|
||||
self.msg.collateral_inputs_count != 0
|
||||
or self.msg.required_signers_count != 0
|
||||
):
|
||||
raise INVALID_TX_SIGNING_REQUEST
|
||||
|
||||
async def _show_tx_signing_request(self) -> None:
|
||||
await show_multisig_transaction(self.ctx)
|
||||
await super()._show_tx_signing_request()
|
||||
|
||||
async def _confirm_tx(self, tx_hash: bytes) -> None:
|
||||
# super() omitted intentionally
|
||||
is_network_id_verifiable = self._is_network_id_verifiable()
|
||||
await layout.confirm_tx(
|
||||
self.ctx,
|
||||
self.msg.fee,
|
||||
self.msg.network_id,
|
||||
self.msg.protocol_magic,
|
||||
self.msg.ttl,
|
||||
self.msg.validity_interval_start,
|
||||
is_network_id_verifiable,
|
||||
tx_hash=None,
|
||||
)
|
||||
|
||||
def _validate_output(self, output: messages.CardanoTxOutput) -> None:
|
||||
super()._validate_output(output)
|
||||
if output.address_parameters is not None:
|
||||
raise INVALID_OUTPUT
|
||||
|
||||
def _validate_certificate(self, certificate: CardanoTxCertificate) -> None:
|
||||
super()._validate_certificate(certificate)
|
||||
if certificate.type == CardanoCertificateType.STAKE_POOL_REGISTRATION:
|
||||
raise INVALID_CERTIFICATE
|
||||
if certificate.path or certificate.key_hash:
|
||||
raise INVALID_CERTIFICATE
|
||||
|
||||
def _validate_withdrawal(self, withdrawal: CardanoTxWithdrawal) -> None:
|
||||
super()._validate_withdrawal(withdrawal)
|
||||
if withdrawal.path or withdrawal.key_hash:
|
||||
raise INVALID_WITHDRAWAL
|
||||
|
||||
def _validate_witness_request(
|
||||
self, witness_request: CardanoTxWitnessRequest
|
||||
) -> None:
|
||||
super()._validate_witness_request(witness_request)
|
||||
is_minting = SCHEMA_MINT.match(witness_request.path)
|
||||
transaction_has_token_minting = self.msg.minting_asset_groups_count > 0
|
||||
|
||||
if not is_multisig_path(witness_request.path) and not is_minting:
|
||||
raise INVALID_WITNESS_REQUEST
|
||||
if is_minting and not transaction_has_token_minting:
|
||||
raise INVALID_WITNESS_REQUEST
|
102
core/src/apps/cardano/sign_tx/ordinary_signer.py
Normal file
102
core/src/apps/cardano/sign_tx/ordinary_signer.py
Normal file
@ -0,0 +1,102 @@
|
||||
from trezor import wire
|
||||
from trezor.enums import CardanoCertificateType
|
||||
from trezor.messages import (
|
||||
CardanoSignTxInit,
|
||||
CardanoTxCertificate,
|
||||
CardanoTxWithdrawal,
|
||||
CardanoTxWitnessRequest,
|
||||
)
|
||||
|
||||
from .. import seed
|
||||
from ..helpers import (
|
||||
INVALID_CERTIFICATE,
|
||||
INVALID_TX_SIGNING_REQUEST,
|
||||
INVALID_WITHDRAWAL,
|
||||
INVALID_WITNESS_REQUEST,
|
||||
)
|
||||
from ..helpers.paths import (
|
||||
SCHEMA_MINT,
|
||||
SCHEMA_PAYMENT,
|
||||
SCHEMA_STAKING,
|
||||
WITNESS_PATH_NAME,
|
||||
)
|
||||
from ..layout import confirm_witness_request
|
||||
from ..seed import is_byron_path, is_shelley_path
|
||||
from .signer import Signer
|
||||
|
||||
|
||||
class OrdinarySigner(Signer):
|
||||
"""
|
||||
Ordinary txs are meant for usual actions, such as sending funds from addresses
|
||||
controlled by 1852' keys, dealing with staking and minting/burning tokens.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, ctx: wire.Context, msg: CardanoSignTxInit, keychain: seed.Keychain
|
||||
) -> None:
|
||||
super().__init__(ctx, msg, keychain)
|
||||
|
||||
def _validate_tx_signing_request(self) -> None:
|
||||
super()._validate_tx_signing_request()
|
||||
if (
|
||||
self.msg.collateral_inputs_count != 0
|
||||
or self.msg.required_signers_count != 0
|
||||
):
|
||||
raise INVALID_TX_SIGNING_REQUEST
|
||||
|
||||
async def _confirm_tx(self, tx_hash: bytes) -> None:
|
||||
# super() omitted intentionally
|
||||
is_network_id_verifiable = self._is_network_id_verifiable()
|
||||
await layout.confirm_tx(
|
||||
self.ctx,
|
||||
self.msg.fee,
|
||||
self.msg.network_id,
|
||||
self.msg.protocol_magic,
|
||||
self.msg.ttl,
|
||||
self.msg.validity_interval_start,
|
||||
is_network_id_verifiable,
|
||||
tx_hash=None,
|
||||
)
|
||||
|
||||
def _validate_certificate(self, certificate: messages.CardanoTxCertificate) -> None:
|
||||
super()._validate_certificate(certificate)
|
||||
if certificate.type == CardanoCertificateType.STAKE_POOL_REGISTRATION:
|
||||
raise INVALID_CERTIFICATE
|
||||
if certificate.script_hash or certificate.key_hash:
|
||||
raise INVALID_CERTIFICATE
|
||||
|
||||
def _validate_withdrawal(self, withdrawal: CardanoTxWithdrawal) -> None:
|
||||
super()._validate_withdrawal(withdrawal)
|
||||
if withdrawal.script_hash or withdrawal.key_hash:
|
||||
raise INVALID_WITHDRAWAL
|
||||
|
||||
def _validate_witness_request(
|
||||
self, witness_request: CardanoTxWitnessRequest
|
||||
) -> None:
|
||||
super()._validate_witness_request(witness_request)
|
||||
is_minting = SCHEMA_MINT.match(witness_request.path)
|
||||
transaction_has_token_minting = self.msg.minting_asset_groups_count > 0
|
||||
|
||||
if not (
|
||||
is_byron_path(witness_request.path)
|
||||
or is_shelley_path(witness_request.path)
|
||||
or is_minting
|
||||
):
|
||||
raise INVALID_WITNESS_REQUEST
|
||||
if is_minting and not transaction_has_token_minting:
|
||||
raise INVALID_WITNESS_REQUEST
|
||||
|
||||
async def _show_witness_request(self, witness_path: list[int]) -> None:
|
||||
# super() omitted intentionally
|
||||
# We only allow payment, staking or minting paths.
|
||||
# If the path is an unusual payment or staking path, we either fail or show the
|
||||
# path to the user depending on Trezor's configuration. If it's a minting path,
|
||||
# we always show it.
|
||||
is_payment = SCHEMA_PAYMENT.match(witness_path)
|
||||
is_staking = SCHEMA_STAKING.match(witness_path)
|
||||
is_minting = SCHEMA_MINT.match(witness_path)
|
||||
|
||||
if is_minting:
|
||||
await confirm_witness_request(self.ctx, witness_path)
|
||||
elif not is_payment and not is_staking:
|
||||
await self._fail_or_warn_path(witness_path, WITNESS_PATH_NAME)
|
112
core/src/apps/cardano/sign_tx/plutus_signer.py
Normal file
112
core/src/apps/cardano/sign_tx/plutus_signer.py
Normal file
@ -0,0 +1,112 @@
|
||||
from trezor import wire
|
||||
from trezor.enums import CardanoCertificateType
|
||||
from trezor.messages import (
|
||||
CardanoAddressParametersType,
|
||||
CardanoSignTxInit,
|
||||
CardanoTxCertificate,
|
||||
CardanoTxInput,
|
||||
CardanoTxOutput,
|
||||
CardanoTxWitnessRequest,
|
||||
)
|
||||
|
||||
from .. import seed
|
||||
from ..helpers import INVALID_CERTIFICATE, INVALID_WITNESS_REQUEST
|
||||
from ..helpers.credential import Credential, should_show_address_credentials
|
||||
from ..helpers.paths import SCHEMA_MINT
|
||||
from ..layout import (
|
||||
confirm_input,
|
||||
confirm_transaction,
|
||||
show_device_owned_output_credentials,
|
||||
show_plutus_transaction,
|
||||
show_warning_no_collateral_inputs,
|
||||
show_warning_no_script_data_hash,
|
||||
)
|
||||
from ..seed import is_multisig_path, is_shelley_path
|
||||
from .signer import Signer
|
||||
|
||||
|
||||
class PlutusSigner(Signer):
|
||||
"""
|
||||
The Plutus siging mode is meant for txs that involve Plutus script evaluation. The
|
||||
validation rules are less strict, but more tx items/warnings are shown to the user.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, ctx: wire.Context, msg: CardanoSignTxInit, keychain: seed.Keychain
|
||||
) -> None:
|
||||
super().__init__(ctx, msg, keychain)
|
||||
|
||||
async def _show_tx_signing_request(self) -> None:
|
||||
await show_plutus_transaction(self.ctx)
|
||||
await super()._show_tx_signing_request()
|
||||
# These items should be present if a Plutus script is to be executed.
|
||||
if self.msg.script_data_hash is None:
|
||||
await show_warning_no_script_data_hash(self.ctx)
|
||||
if self.msg.collateral_inputs_count == 0:
|
||||
await show_warning_no_collateral_inputs(self.ctx)
|
||||
|
||||
async def _confirm_transaction(self, tx_hash: bytes) -> None:
|
||||
# super() omitted intentionally
|
||||
# We display tx hash so that experienced users can compare it to the tx hash
|
||||
# computed by a trusted device (in case the tx contains many items which are
|
||||
# tedious to check one by one on the Trezor screen).
|
||||
is_network_id_verifiable = self._is_network_id_verifiable()
|
||||
await confirm_transaction(
|
||||
self.ctx,
|
||||
self.msg.fee,
|
||||
self.msg.network_id,
|
||||
self.msg.protocol_magic,
|
||||
self.msg.ttl,
|
||||
self.msg.validity_interval_start,
|
||||
is_network_id_verifiable,
|
||||
tx_hash,
|
||||
)
|
||||
|
||||
async def _show_input(self, input: CardanoTxInput) -> None:
|
||||
# super() omitted intentionally
|
||||
# The inputs are not interchangeable (because of datums), so we must show them.
|
||||
await confirm_input(self.ctx, input)
|
||||
|
||||
async def _show_output_credentials(
|
||||
self, address_parameters: CardanoAddressParametersType
|
||||
) -> None:
|
||||
# In ordinary txs, change outputs with matching payment and staking paths can be
|
||||
# hidden, but we need to show them in Plutus txs because of the script
|
||||
# evaluation. We at least hide the staking path if it matches the payment path.
|
||||
show_both_credentials = should_show_address_credentials(address_parameters)
|
||||
await show_device_owned_output_credentials(
|
||||
self.ctx,
|
||||
Credential.payment_credential(address_parameters),
|
||||
Credential.stake_credential(address_parameters),
|
||||
show_both_credentials,
|
||||
)
|
||||
|
||||
def _should_show_output(self, output: CardanoTxOutput) -> bool:
|
||||
# super() omitted intentionally
|
||||
# All outputs need to be shown (even device-owned), because they might influence
|
||||
# the script evaluation.
|
||||
return True
|
||||
|
||||
def _is_change_output(self, output: CardanoTxOutput) -> bool:
|
||||
# super() omitted intentionally
|
||||
# In Plutus txs, we don't call device-owned outputs "change" outputs.
|
||||
return False
|
||||
|
||||
def _validate_certificate(self, certificate: CardanoTxCertificate) -> None:
|
||||
super()._validate_certificate(certificate)
|
||||
if certificate.type == CardanoCertificateType.STAKE_POOL_REGISTRATION:
|
||||
raise INVALID_CERTIFICATE
|
||||
|
||||
def _validate_witness_request(
|
||||
self, witness_request: CardanoTxWitnessRequest
|
||||
) -> None:
|
||||
super()._validate_witness_request(witness_request)
|
||||
is_minting = SCHEMA_MINT.match(witness_request.path)
|
||||
|
||||
# In Plutus txs, we allow minting witnesses even when the tx doesn't have token minting.
|
||||
if not (
|
||||
is_shelley_path(witness_request.path)
|
||||
or is_multisig_path(witness_request.path)
|
||||
or is_minting
|
||||
):
|
||||
raise INVALID_WITNESS_REQUEST
|
92
core/src/apps/cardano/sign_tx/pool_owner_signer.py
Normal file
92
core/src/apps/cardano/sign_tx/pool_owner_signer.py
Normal file
@ -0,0 +1,92 @@
|
||||
from trezor import wire
|
||||
from trezor.enums import CardanoCertificateType
|
||||
from trezor.messages import (
|
||||
CardanoSignTxInit,
|
||||
CardanoTxCertificate,
|
||||
CardanoTxOutput,
|
||||
CardanoTxWitnessRequest,
|
||||
)
|
||||
|
||||
from .. import seed
|
||||
from ..helpers import (
|
||||
INVALID_CERTIFICATE,
|
||||
INVALID_OUTPUT,
|
||||
INVALID_STAKE_POOL_REGISTRATION_TX_STRUCTURE,
|
||||
INVALID_STAKEPOOL_REGISTRATION_TX_WITNESSES,
|
||||
INVALID_TX_SIGNING_REQUEST,
|
||||
)
|
||||
from ..helpers.paths import SCHEMA_STAKING_ANY_ACCOUNT
|
||||
from ..layout import confirm_stake_pool_registration_final
|
||||
from .signer import Signer
|
||||
|
||||
|
||||
class PoolOwnerSigner(Signer):
|
||||
"""
|
||||
We have a separate tx signing flow for stake pool registration because it's a
|
||||
transaction where the witnessable entries (i.e. inputs, withdrawals, etc.) are not
|
||||
supposed to be controlled by the HW wallet, which means the user is vulnerable to
|
||||
unknowingly supplying a witness for an UTXO or other tx entry they think is external,
|
||||
resulting in the co-signers gaining control over their funds (Something SLIP-0019 is
|
||||
dealing with for BTC but no similar standard is currently available for Cardano).
|
||||
Hence we completely forbid witnessing inputs and other entries of the transaction
|
||||
except the stake pool certificate itself and we provide a witness only to the user's
|
||||
staking key in the list of pool owners.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, ctx: wire.Context, msg: CardanoSignTxInit, keychain: seed.Keychain
|
||||
) -> None:
|
||||
super().__init__(ctx, msg, keychain)
|
||||
|
||||
def _validate_tx_signing_request(self) -> None:
|
||||
super()._validate_tx_signing_request()
|
||||
if (
|
||||
self.msg.certificates_count != 1
|
||||
or self.msg.withdrawals_count != 0
|
||||
or self.msg.minting_asset_groups_count != 0
|
||||
):
|
||||
raise INVALID_STAKE_POOL_REGISTRATION_TX_STRUCTURE
|
||||
|
||||
if (
|
||||
self.msg.script_data_hash is not None
|
||||
or self.msg.collateral_inputs_count != 0
|
||||
or self.msg.required_signers_count != 0
|
||||
):
|
||||
raise INVALID_TX_SIGNING_REQUEST
|
||||
|
||||
async def _confirm_transaction(self, tx_hash: bytes) -> None:
|
||||
# super() omitted intentionally
|
||||
await confirm_stake_pool_registration_final(
|
||||
self.ctx,
|
||||
self.msg.protocol_magic,
|
||||
self.msg.ttl,
|
||||
self.msg.validity_interval_start,
|
||||
)
|
||||
|
||||
def _validate_output(self, output: CardanoTxOutput) -> None:
|
||||
super()._validate_output(output)
|
||||
if output.address_parameters is not None:
|
||||
raise INVALID_OUTPUT
|
||||
if output.datum_hash is not None:
|
||||
raise INVALID_OUTPUT
|
||||
|
||||
def _should_show_output(self, output: CardanoTxOutput) -> bool:
|
||||
# super() omitted intentionally
|
||||
# There are no spending witnesses, it is thus safe to hide outputs.
|
||||
return False
|
||||
|
||||
def _validate_certificate(self, certificate: CardanoTxCertificate) -> None:
|
||||
super()._validate_certificate(certificate)
|
||||
if certificate.type != CardanoCertificateType.STAKE_POOL_REGISTRATION:
|
||||
raise INVALID_CERTIFICATE
|
||||
|
||||
def _validate_witness_request(
|
||||
self, witness_request: CardanoTxWitnessRequest
|
||||
) -> None:
|
||||
super()._validate_witness_request(witness_request)
|
||||
if not SCHEMA_STAKING_ANY_ACCOUNT.match(witness_request.path):
|
||||
raise INVALID_STAKEPOOL_REGISTRATION_TX_WITNESSES
|
||||
|
||||
def _is_network_id_verifiable(self) -> bool:
|
||||
# super() omitted intentionally
|
||||
return True
|
926
core/src/apps/cardano/sign_tx/signer.py
Normal file
926
core/src/apps/cardano/sign_tx/signer.py
Normal file
@ -0,0 +1,926 @@
|
||||
from micropython import const
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from trezor import wire
|
||||
from trezor.crypto import hashlib
|
||||
from trezor.crypto.curve import ed25519
|
||||
from trezor.enums import (
|
||||
CardanoAddressType,
|
||||
CardanoCertificateType,
|
||||
CardanoTxWitnessType,
|
||||
)
|
||||
from trezor.messages import (
|
||||
CardanoAddressParametersType,
|
||||
CardanoAssetGroup,
|
||||
CardanoPoolOwner,
|
||||
CardanoPoolRelayParameters,
|
||||
CardanoSignTxInit,
|
||||
CardanoToken,
|
||||
CardanoTxAuxiliaryData,
|
||||
CardanoTxBodyHash,
|
||||
CardanoTxCertificate,
|
||||
CardanoTxCollateralInput,
|
||||
CardanoTxHostAck,
|
||||
CardanoTxInput,
|
||||
CardanoTxItemAck,
|
||||
CardanoTxMint,
|
||||
CardanoTxOutput,
|
||||
CardanoTxRequiredSigner,
|
||||
CardanoTxWithdrawal,
|
||||
CardanoTxWitnessRequest,
|
||||
CardanoTxWitnessResponse,
|
||||
)
|
||||
|
||||
from apps.common import cbor, safety_checks
|
||||
|
||||
from .. import seed
|
||||
from ..address import (
|
||||
ADDRESS_TYPES_PAYMENT_SCRIPT,
|
||||
derive_address_bytes,
|
||||
derive_human_readable_address,
|
||||
get_address_bytes_unsafe,
|
||||
get_address_type,
|
||||
validate_output_address,
|
||||
validate_output_address_parameters,
|
||||
)
|
||||
from ..auxiliary_data import (
|
||||
get_auxiliary_data_hash_and_supplement,
|
||||
show_auxiliary_data,
|
||||
validate_auxiliary_data,
|
||||
)
|
||||
from ..certificates import (
|
||||
assert_certificate_cond,
|
||||
cborize_certificate,
|
||||
cborize_initial_pool_registration_certificate_fields,
|
||||
cborize_pool_metadata,
|
||||
cborize_pool_owner,
|
||||
cborize_pool_relay,
|
||||
validate_certificate,
|
||||
validate_pool_owner,
|
||||
validate_pool_relay,
|
||||
)
|
||||
from ..helpers import (
|
||||
ADDRESS_KEY_HASH_SIZE,
|
||||
INPUT_PREV_HASH_SIZE,
|
||||
INVALID_COLLATERAL_INPUT,
|
||||
INVALID_INPUT,
|
||||
INVALID_OUTPUT,
|
||||
INVALID_OUTPUT_DATUM_HASH,
|
||||
INVALID_REQUIRED_SIGNER,
|
||||
INVALID_SCRIPT_DATA_HASH,
|
||||
INVALID_TOKEN_BUNDLE_MINT,
|
||||
INVALID_TOKEN_BUNDLE_OUTPUT,
|
||||
INVALID_TX_SIGNING_REQUEST,
|
||||
INVALID_WITHDRAWAL,
|
||||
LOVELACE_MAX_SUPPLY,
|
||||
OUTPUT_DATUM_HASH_SIZE,
|
||||
SCRIPT_DATA_HASH_SIZE,
|
||||
)
|
||||
from ..helpers.account_path_check import AccountPathChecker
|
||||
from ..helpers.credential import Credential, should_show_address_credentials
|
||||
from ..helpers.hash_builder_collection import HashBuilderDict, HashBuilderList
|
||||
from ..helpers.paths import (
|
||||
CERTIFICATE_PATH_NAME,
|
||||
CHANGE_OUTPUT_PATH_NAME,
|
||||
CHANGE_OUTPUT_STAKING_PATH_NAME,
|
||||
POOL_OWNER_STAKING_PATH_NAME,
|
||||
SCHEMA_STAKING,
|
||||
)
|
||||
from ..helpers.utils import (
|
||||
derive_public_key,
|
||||
get_public_key_hash,
|
||||
validate_network_info,
|
||||
validate_stake_credential,
|
||||
)
|
||||
from ..layout import (
|
||||
confirm_certificate,
|
||||
confirm_collateral_input,
|
||||
confirm_required_signer,
|
||||
confirm_script_data_hash,
|
||||
confirm_sending,
|
||||
confirm_sending_token,
|
||||
confirm_stake_pool_metadata,
|
||||
confirm_stake_pool_owner,
|
||||
confirm_stake_pool_parameters,
|
||||
confirm_token_minting,
|
||||
confirm_transaction,
|
||||
confirm_withdrawal,
|
||||
confirm_witness_request,
|
||||
show_change_output_credentials,
|
||||
show_warning_path,
|
||||
show_warning_tx_contains_mint,
|
||||
show_warning_tx_network_unverifiable,
|
||||
show_warning_tx_output_contains_datum_hash,
|
||||
show_warning_tx_output_contains_tokens,
|
||||
show_warning_tx_output_no_datum_hash,
|
||||
)
|
||||
from ..seed import is_byron_path, is_minting_path, is_multisig_path, is_shelley_path
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Any
|
||||
from apps.common.paths import PathSchema
|
||||
|
||||
CardanoTxResponseType = CardanoTxItemAck | CardanoTxWitnessResponse
|
||||
|
||||
MINTING_POLICY_ID_LENGTH = 28
|
||||
MAX_ASSET_NAME_LENGTH = 32
|
||||
|
||||
TX_BODY_KEY_INPUTS = const(0)
|
||||
TX_BODY_KEY_OUTPUTS = const(1)
|
||||
TX_BODY_KEY_FEE = const(2)
|
||||
TX_BODY_KEY_TTL = const(3)
|
||||
TX_BODY_KEY_CERTIFICATES = const(4)
|
||||
TX_BODY_KEY_WITHDRAWALS = const(5)
|
||||
TX_BODY_KEY_AUXILIARY_DATA = const(7)
|
||||
TX_BODY_KEY_VALIDITY_INTERVAL_START = const(8)
|
||||
TX_BODY_KEY_MINT = const(9)
|
||||
TX_BODY_KEY_SCRIPT_DATA_HASH = const(11)
|
||||
TX_BODY_KEY_COLLATERAL_INPUTS = const(13)
|
||||
TX_BODY_KEY_REQUIRED_SIGNERS = const(14)
|
||||
TX_BODY_KEY_NETWORK_ID = const(15)
|
||||
|
||||
POOL_REGISTRATION_CERTIFICATE_ITEMS_COUNT = 10
|
||||
|
||||
|
||||
class Signer:
|
||||
"""
|
||||
This class encapsulates the entire tx signing process. By default, most tx items are
|
||||
allowed and shown to the user. For each signing mode, there is a subclass that
|
||||
overrides some methods, usually to add more validation rules and show/hide some
|
||||
items. Each tx item is processed in a _process_xyz() method which handles validation,
|
||||
user confirmation and serialization of the tx item.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, ctx: wire.Context, msg: CardanoSignTxInit, keychain: seed.Keychain
|
||||
) -> None:
|
||||
self.ctx = ctx
|
||||
self.msg = msg
|
||||
self.keychain = keychain
|
||||
|
||||
self.account_path_checker = AccountPathChecker()
|
||||
|
||||
# Inputs, outputs and fee are mandatory, count the number of optional fields present.
|
||||
tx_body_map_item_count = 3 + sum(
|
||||
(
|
||||
msg.ttl is not None,
|
||||
msg.certificates_count > 0,
|
||||
msg.withdrawals_count > 0,
|
||||
msg.has_auxiliary_data,
|
||||
msg.validity_interval_start is not None,
|
||||
msg.minting_asset_groups_count > 0,
|
||||
msg.include_network_id,
|
||||
msg.script_data_hash is not None,
|
||||
msg.collateral_inputs_count > 0,
|
||||
msg.required_signers_count > 0,
|
||||
)
|
||||
)
|
||||
self.tx_dict: HashBuilderDict[int, Any] = HashBuilderDict(
|
||||
tx_body_map_item_count, INVALID_TX_SIGNING_REQUEST
|
||||
)
|
||||
|
||||
async def sign(self) -> None:
|
||||
hash_fn = hashlib.blake2b(outlen=32)
|
||||
self.tx_dict.start(hash_fn)
|
||||
with self.tx_dict:
|
||||
await self._processs_tx_signing_request()
|
||||
|
||||
tx_hash = hash_fn.digest()
|
||||
await self._confirm_transaction(tx_hash)
|
||||
|
||||
response_after_witness_requests = await self._process_witness_requests(tx_hash)
|
||||
await self.ctx.call(response_after_witness_requests, CardanoTxHostAck)
|
||||
await self.ctx.call(CardanoTxBodyHash(tx_hash=tx_hash), CardanoTxHostAck)
|
||||
|
||||
# signing request
|
||||
|
||||
async def _processs_tx_signing_request(self) -> None:
|
||||
self._validate_tx_signing_request()
|
||||
await self._show_tx_signing_request()
|
||||
|
||||
inputs_list: HashBuilderList[tuple[bytes, int]] = HashBuilderList(
|
||||
self.msg.inputs_count
|
||||
)
|
||||
with self.tx_dict.add(TX_BODY_KEY_INPUTS, inputs_list):
|
||||
await self._process_inputs(inputs_list)
|
||||
|
||||
outputs_list: HashBuilderList = HashBuilderList(self.msg.outputs_count)
|
||||
with self.tx_dict.add(TX_BODY_KEY_OUTPUTS, outputs_list):
|
||||
await self._process_outputs(outputs_list)
|
||||
|
||||
self.tx_dict.add(TX_BODY_KEY_FEE, self.msg.fee)
|
||||
|
||||
if self.msg.ttl is not None:
|
||||
self.tx_dict.add(TX_BODY_KEY_TTL, self.msg.ttl)
|
||||
|
||||
if self.msg.certificates_count > 0:
|
||||
certificates_list: HashBuilderList = HashBuilderList(
|
||||
self.msg.certificates_count
|
||||
)
|
||||
with self.tx_dict.add(TX_BODY_KEY_CERTIFICATES, certificates_list):
|
||||
await self._process_certificates(certificates_list)
|
||||
|
||||
if self.msg.withdrawals_count > 0:
|
||||
withdrawals_dict: HashBuilderDict[bytes, int] = HashBuilderDict(
|
||||
self.msg.withdrawals_count, INVALID_WITHDRAWAL
|
||||
)
|
||||
with self.tx_dict.add(TX_BODY_KEY_WITHDRAWALS, withdrawals_dict):
|
||||
await self._process_withdrawals(withdrawals_dict)
|
||||
|
||||
if self.msg.has_auxiliary_data:
|
||||
await self._process_auxiliary_data()
|
||||
|
||||
if self.msg.validity_interval_start is not None:
|
||||
self.tx_dict.add(
|
||||
TX_BODY_KEY_VALIDITY_INTERVAL_START, self.msg.validity_interval_start
|
||||
)
|
||||
|
||||
if self.msg.minting_asset_groups_count > 0:
|
||||
minting_dict: HashBuilderDict[bytes, HashBuilderDict] = HashBuilderDict(
|
||||
self.msg.minting_asset_groups_count, INVALID_TOKEN_BUNDLE_MINT
|
||||
)
|
||||
with self.tx_dict.add(TX_BODY_KEY_MINT, minting_dict):
|
||||
await self._process_minting(minting_dict)
|
||||
|
||||
if self.msg.script_data_hash is not None:
|
||||
await self._process_script_data_hash()
|
||||
|
||||
if self.msg.collateral_inputs_count > 0:
|
||||
collateral_inputs_list: HashBuilderList[
|
||||
tuple[bytes, int]
|
||||
] = HashBuilderList(self.msg.collateral_inputs_count)
|
||||
with self.tx_dict.add(
|
||||
TX_BODY_KEY_COLLATERAL_INPUTS, collateral_inputs_list
|
||||
):
|
||||
await self._process_collateral_inputs(collateral_inputs_list)
|
||||
|
||||
if self.msg.required_signers_count > 0:
|
||||
required_signers_list: HashBuilderList[bytes] = HashBuilderList(
|
||||
self.msg.required_signers_count
|
||||
)
|
||||
with self.tx_dict.add(TX_BODY_KEY_REQUIRED_SIGNERS, required_signers_list):
|
||||
await self._process_required_signers(required_signers_list)
|
||||
|
||||
if self.msg.include_network_id:
|
||||
self.tx_dict.add(TX_BODY_KEY_NETWORK_ID, self.msg.network_id)
|
||||
|
||||
def _validate_tx_signing_request(self) -> None:
|
||||
if self.msg.fee > LOVELACE_MAX_SUPPLY:
|
||||
raise wire.ProcessError("Fee is out of range!")
|
||||
validate_network_info(self.msg.network_id, self.msg.protocol_magic)
|
||||
|
||||
async def _show_tx_signing_request(self) -> None:
|
||||
if not self._is_network_id_verifiable():
|
||||
await show_warning_tx_network_unverifiable(self.ctx)
|
||||
|
||||
async def _confirm_tx(self, tx_hash: bytes) -> None:
|
||||
# Final signing confirmation is handled separately in each signing mode.
|
||||
raise NotImplementedError
|
||||
|
||||
# inputs
|
||||
|
||||
async def _process_inputs(
|
||||
self, inputs_list: HashBuilderList[tuple[bytes, int]]
|
||||
) -> None:
|
||||
for _ in range(self.msg.inputs_count):
|
||||
input: CardanoTxInput = await self.ctx.call(
|
||||
CardanoTxItemAck(), CardanoTxInput
|
||||
)
|
||||
self._validate_input(input)
|
||||
await self._show_input(input)
|
||||
inputs_list.append((input.prev_hash, input.prev_index))
|
||||
|
||||
def _validate_input(self, input: CardanoTxInput) -> None:
|
||||
if len(input.prev_hash) != INPUT_PREV_HASH_SIZE:
|
||||
raise INVALID_INPUT
|
||||
|
||||
async def _show_input(self, input: CardanoTxInput) -> None:
|
||||
# We never show the inputs, except for Plutus txs.
|
||||
pass
|
||||
|
||||
# outputs
|
||||
|
||||
async def _process_outputs(self, outputs_list: HashBuilderList) -> None:
|
||||
total_amount = 0
|
||||
for _ in range(self.msg.outputs_count):
|
||||
output: CardanoTxOutput = await self.ctx.call(
|
||||
CardanoTxItemAck(), CardanoTxOutput
|
||||
)
|
||||
self._validate_output(output)
|
||||
await self._show_output(output)
|
||||
|
||||
output_address = self._get_output_address(output)
|
||||
|
||||
has_datum_hash = output.datum_hash is not None
|
||||
output_list: HashBuilderList = HashBuilderList(2 + int(has_datum_hash))
|
||||
with outputs_list.append(output_list):
|
||||
output_list.append(output_address)
|
||||
if output.asset_groups_count == 0:
|
||||
# output structure is: [address, amount, datum_hash?]
|
||||
output_list.append(output.amount)
|
||||
else:
|
||||
# output structure is: [address, [amount, asset_groups], datum_hash?]
|
||||
output_value_list: HashBuilderList = HashBuilderList(2)
|
||||
with output_list.append(output_value_list):
|
||||
output_value_list.append(output.amount)
|
||||
asset_groups_dict: HashBuilderDict[
|
||||
bytes, HashBuilderDict[bytes, int]
|
||||
] = HashBuilderDict(
|
||||
output.asset_groups_count, INVALID_TOKEN_BUNDLE_OUTPUT
|
||||
)
|
||||
with output_value_list.append(asset_groups_dict):
|
||||
await self._process_asset_groups(
|
||||
asset_groups_dict,
|
||||
output.asset_groups_count,
|
||||
self._should_show_output(output),
|
||||
)
|
||||
if has_datum_hash:
|
||||
output_list.append(output.datum_hash)
|
||||
|
||||
total_amount += output.amount
|
||||
|
||||
if total_amount > LOVELACE_MAX_SUPPLY:
|
||||
raise wire.ProcessError("Total transaction amount is out of range!")
|
||||
|
||||
def _validate_output(self, output: CardanoTxOutput) -> None:
|
||||
if output.address_parameters is not None and output.address is not None:
|
||||
raise INVALID_OUTPUT
|
||||
|
||||
if output.address_parameters is not None:
|
||||
validate_output_address_parameters(output.address_parameters)
|
||||
self._fail_if_strict_and_unusual(output.address_parameters)
|
||||
elif output.address is not None:
|
||||
validate_output_address(
|
||||
output.address, self.msg.protocol_magic, self.msg.network_id
|
||||
)
|
||||
else:
|
||||
raise INVALID_OUTPUT
|
||||
|
||||
if output.datum_hash is not None:
|
||||
if len(output.datum_hash) != OUTPUT_DATUM_HASH_SIZE:
|
||||
raise INVALID_OUTPUT_DATUM_HASH
|
||||
address_type = self._get_output_address_type(output)
|
||||
if address_type not in ADDRESS_TYPES_PAYMENT_SCRIPT:
|
||||
raise INVALID_OUTPUT
|
||||
|
||||
self.account_path_checker.add_output(output)
|
||||
|
||||
async def _show_output(self, output: CardanoTxOutput) -> None:
|
||||
if not self._should_show_output(output):
|
||||
return
|
||||
|
||||
if output.datum_hash is not None:
|
||||
await show_warning_tx_output_contains_datum_hash(
|
||||
self.ctx, output.datum_hash
|
||||
)
|
||||
|
||||
address_type = self._get_output_address_type(output)
|
||||
if output.datum_hash is None and address_type in ADDRESS_TYPES_PAYMENT_SCRIPT:
|
||||
await show_warning_tx_output_no_datum_hash(self.ctx)
|
||||
|
||||
if output.asset_groups_count > 0:
|
||||
await show_warning_tx_output_contains_tokens(self.ctx)
|
||||
|
||||
if output.address_parameters is not None:
|
||||
address = derive_human_readable_address(
|
||||
self.keychain,
|
||||
output.address_parameters,
|
||||
self.msg.protocol_magic,
|
||||
self.msg.network_id,
|
||||
)
|
||||
await self._show_output_credentials(output.address_parameters)
|
||||
else:
|
||||
assert output.address is not None # _validate_output
|
||||
address = output.address
|
||||
|
||||
await confirm_sending(
|
||||
self.ctx,
|
||||
output.amount,
|
||||
address,
|
||||
self._is_change_output(output),
|
||||
self.msg.network_id,
|
||||
)
|
||||
|
||||
async def _show_output_credentials(
|
||||
self, address_parameters: CardanoAddressParametersType
|
||||
) -> None:
|
||||
await show_change_output_credentials(
|
||||
self.ctx,
|
||||
Credential.payment_credential(address_parameters),
|
||||
Credential.stake_credential(address_parameters),
|
||||
)
|
||||
|
||||
def _should_show_output(self, output: CardanoTxOutput) -> bool:
|
||||
"""
|
||||
Determines whether the output should be shown. Extracted from _show_output because
|
||||
of readability and because the same decision is made when displaying output tokens.
|
||||
"""
|
||||
if output.datum_hash is not None:
|
||||
# The `return False` case below should not be reachable when datum hash is
|
||||
# present, but let's make it explicit.
|
||||
return True
|
||||
|
||||
address_type = self._get_output_address_type(output)
|
||||
if output.datum_hash is None and address_type in ADDRESS_TYPES_PAYMENT_SCRIPT:
|
||||
# Plutus script address without a datum hash is unspendable, we must show a warning.
|
||||
return True
|
||||
|
||||
if output.address_parameters is not None: # change output
|
||||
if not should_show_address_credentials(output.address_parameters):
|
||||
# We don't need to display simple address outputs.
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _is_change_output(self, output: CardanoTxOutput) -> bool:
|
||||
"""Used only to determine what message to show to the user when confirming sending."""
|
||||
return output.address_parameters is not None
|
||||
|
||||
# asset groups
|
||||
|
||||
async def _process_asset_groups(
|
||||
self,
|
||||
asset_groups_dict: HashBuilderDict[bytes, HashBuilderDict[bytes, int]],
|
||||
asset_groups_count: int,
|
||||
should_show_tokens: bool,
|
||||
) -> None:
|
||||
for _ in range(asset_groups_count):
|
||||
asset_group: CardanoAssetGroup = await self.ctx.call(
|
||||
CardanoTxItemAck(), CardanoAssetGroup
|
||||
)
|
||||
self._validate_asset_group(asset_group)
|
||||
|
||||
tokens: HashBuilderDict[bytes, int] = HashBuilderDict(
|
||||
asset_group.tokens_count, INVALID_TOKEN_BUNDLE_OUTPUT
|
||||
)
|
||||
with asset_groups_dict.add(asset_group.policy_id, tokens):
|
||||
await self._process_tokens(
|
||||
tokens,
|
||||
asset_group.policy_id,
|
||||
asset_group.tokens_count,
|
||||
should_show_tokens,
|
||||
)
|
||||
|
||||
def _validate_asset_group(
|
||||
self, asset_group: CardanoAssetGroup, is_mint: bool = False
|
||||
) -> None:
|
||||
INVALID_TOKEN_BUNDLE = (
|
||||
INVALID_TOKEN_BUNDLE_MINT if is_mint else INVALID_TOKEN_BUNDLE_OUTPUT
|
||||
)
|
||||
|
||||
if len(asset_group.policy_id) != MINTING_POLICY_ID_LENGTH:
|
||||
raise INVALID_TOKEN_BUNDLE
|
||||
if asset_group.tokens_count == 0:
|
||||
raise INVALID_TOKEN_BUNDLE
|
||||
|
||||
# tokens
|
||||
|
||||
async def _process_tokens(
|
||||
self,
|
||||
tokens_dict: HashBuilderDict[bytes, int],
|
||||
policy_id: bytes,
|
||||
tokens_count: int,
|
||||
should_show_tokens: bool,
|
||||
) -> None:
|
||||
for _ in range(tokens_count):
|
||||
token: CardanoToken = await self.ctx.call(CardanoTxItemAck(), CardanoToken)
|
||||
self._validate_token(token)
|
||||
if should_show_tokens:
|
||||
await confirm_sending_token(self.ctx, policy_id, token)
|
||||
|
||||
assert token.amount is not None # _validate_token
|
||||
tokens_dict.add(token.asset_name_bytes, token.amount)
|
||||
|
||||
def _validate_token(self, token: CardanoToken, is_mint: bool = False) -> None:
|
||||
INVALID_TOKEN_BUNDLE = (
|
||||
INVALID_TOKEN_BUNDLE_MINT if is_mint else INVALID_TOKEN_BUNDLE_OUTPUT
|
||||
)
|
||||
|
||||
if is_mint:
|
||||
if token.mint_amount is None or token.amount is not None:
|
||||
raise INVALID_TOKEN_BUNDLE
|
||||
else:
|
||||
if token.amount is None or token.mint_amount is not None:
|
||||
raise INVALID_TOKEN_BUNDLE
|
||||
|
||||
if len(token.asset_name_bytes) > MAX_ASSET_NAME_LENGTH:
|
||||
raise INVALID_TOKEN_BUNDLE
|
||||
|
||||
# certificates
|
||||
|
||||
async def _process_certificates(self, certificates_list: HashBuilderList) -> None:
|
||||
for _ in range(self.msg.certificates_count):
|
||||
certificate: CardanoTxCertificate = await self.ctx.call(
|
||||
CardanoTxItemAck(), CardanoTxCertificate
|
||||
)
|
||||
self._validate_certificate(certificate)
|
||||
await self._show_certificate(certificate)
|
||||
|
||||
if certificate.type == CardanoCertificateType.STAKE_POOL_REGISTRATION:
|
||||
pool_parameters = certificate.pool_parameters
|
||||
assert pool_parameters is not None # _validate_certificate
|
||||
|
||||
pool_items_list: HashBuilderList = HashBuilderList(
|
||||
POOL_REGISTRATION_CERTIFICATE_ITEMS_COUNT
|
||||
)
|
||||
with certificates_list.append(pool_items_list):
|
||||
for item in cborize_initial_pool_registration_certificate_fields(
|
||||
certificate
|
||||
):
|
||||
pool_items_list.append(item)
|
||||
|
||||
pool_owners_list: HashBuilderList[bytes] = HashBuilderList(
|
||||
pool_parameters.owners_count
|
||||
)
|
||||
with pool_items_list.append(pool_owners_list):
|
||||
await self._process_pool_owners(
|
||||
pool_owners_list, pool_parameters.owners_count
|
||||
)
|
||||
|
||||
relays_list: HashBuilderList[cbor.CborSequence] = HashBuilderList(
|
||||
pool_parameters.relays_count
|
||||
)
|
||||
with pool_items_list.append(relays_list):
|
||||
await self._process_pool_relays(
|
||||
relays_list, pool_parameters.relays_count
|
||||
)
|
||||
|
||||
pool_items_list.append(
|
||||
cborize_pool_metadata(pool_parameters.metadata)
|
||||
)
|
||||
else:
|
||||
certificates_list.append(
|
||||
cborize_certificate(self.keychain, certificate)
|
||||
)
|
||||
|
||||
def _validate_certificate(self, certificate: CardanoTxCertificate) -> None:
|
||||
validate_certificate(
|
||||
certificate,
|
||||
self.msg.protocol_magic,
|
||||
self.msg.network_id,
|
||||
self.account_path_checker,
|
||||
)
|
||||
|
||||
async def _show_certificate(self, certificate: CardanoTxCertificate) -> None:
|
||||
if certificate.path:
|
||||
await self._fail_or_warn_if_invalid_path(
|
||||
SCHEMA_STAKING, certificate.path, CERTIFICATE_PATH_NAME
|
||||
)
|
||||
|
||||
if certificate.type == CardanoCertificateType.STAKE_POOL_REGISTRATION:
|
||||
assert certificate.pool_parameters is not None
|
||||
await confirm_stake_pool_parameters(
|
||||
self.ctx, certificate.pool_parameters, self.msg.network_id
|
||||
)
|
||||
await confirm_stake_pool_metadata(
|
||||
self.ctx, certificate.pool_parameters.metadata
|
||||
)
|
||||
else:
|
||||
await confirm_certificate(self.ctx, certificate)
|
||||
|
||||
# pool owners
|
||||
|
||||
async def _process_pool_owners(
|
||||
self, pool_owners_list: HashBuilderList[bytes], owners_count: int
|
||||
) -> None:
|
||||
owners_as_path_count = 0
|
||||
for _ in range(owners_count):
|
||||
owner: CardanoPoolOwner = await self.ctx.call(
|
||||
CardanoTxItemAck(), CardanoPoolOwner
|
||||
)
|
||||
validate_pool_owner(owner, self.account_path_checker)
|
||||
await self._show_pool_owner(owner)
|
||||
pool_owners_list.append(cborize_pool_owner(self.keychain, owner))
|
||||
|
||||
if owner.staking_key_path:
|
||||
owners_as_path_count += 1
|
||||
|
||||
assert_certificate_cond(owners_as_path_count == 1)
|
||||
|
||||
async def _show_pool_owner(self, owner: CardanoPoolOwner) -> None:
|
||||
if owner.staking_key_path:
|
||||
await self._fail_or_warn_if_invalid_path(
|
||||
SCHEMA_STAKING, owner.staking_key_path, POOL_OWNER_STAKING_PATH_NAME
|
||||
)
|
||||
|
||||
await confirm_stake_pool_owner(
|
||||
self.ctx, self.keychain, owner, self.msg.protocol_magic, self.msg.network_id
|
||||
)
|
||||
|
||||
# pool relays
|
||||
|
||||
async def _process_pool_relays(
|
||||
self,
|
||||
relays_list: HashBuilderList[cbor.CborSequence],
|
||||
relays_count: int,
|
||||
) -> None:
|
||||
for _ in range(relays_count):
|
||||
relay: CardanoPoolRelayParameters = await self.ctx.call(
|
||||
CardanoTxItemAck(), CardanoPoolRelayParameters
|
||||
)
|
||||
validate_pool_relay(relay)
|
||||
relays_list.append(cborize_pool_relay(relay))
|
||||
|
||||
# withdrawals
|
||||
|
||||
async def _process_withdrawals(
|
||||
self, withdrawals_dict: HashBuilderDict[bytes, int]
|
||||
) -> None:
|
||||
for _ in range(self.msg.withdrawals_count):
|
||||
withdrawal: CardanoTxWithdrawal = await self.ctx.call(
|
||||
CardanoTxItemAck(), CardanoTxWithdrawal
|
||||
)
|
||||
self._validate_withdrawal(withdrawal)
|
||||
reward_address_bytes = self._derive_withdrawal_reward_address_bytes(
|
||||
withdrawal
|
||||
)
|
||||
await confirm_withdrawal(
|
||||
self.ctx, withdrawal, reward_address_bytes, self.msg.network_id
|
||||
)
|
||||
withdrawals_dict.add(reward_address_bytes, withdrawal.amount)
|
||||
|
||||
def _validate_withdrawal(self, withdrawal: CardanoTxWithdrawal) -> None:
|
||||
validate_stake_credential(
|
||||
withdrawal.path,
|
||||
withdrawal.script_hash,
|
||||
withdrawal.key_hash,
|
||||
INVALID_WITHDRAWAL,
|
||||
)
|
||||
|
||||
if not 0 <= withdrawal.amount < LOVELACE_MAX_SUPPLY:
|
||||
raise INVALID_WITHDRAWAL
|
||||
|
||||
self.account_path_checker.add_withdrawal(withdrawal)
|
||||
|
||||
# auxiliary data
|
||||
|
||||
async def _process_auxiliary_data(self) -> None:
|
||||
auxiliary_data: CardanoTxAuxiliaryData = await self.ctx.call(
|
||||
CardanoTxItemAck(), CardanoTxAuxiliaryData
|
||||
)
|
||||
validate_auxiliary_data(auxiliary_data)
|
||||
|
||||
(
|
||||
auxiliary_data_hash,
|
||||
auxiliary_data_supplement,
|
||||
) = get_auxiliary_data_hash_and_supplement(
|
||||
self.keychain, auxiliary_data, self.msg.protocol_magic, self.msg.network_id
|
||||
)
|
||||
await show_auxiliary_data(
|
||||
self.ctx,
|
||||
self.keychain,
|
||||
auxiliary_data_hash,
|
||||
auxiliary_data.catalyst_registration_parameters,
|
||||
self.msg.protocol_magic,
|
||||
self.msg.network_id,
|
||||
)
|
||||
self.tx_dict.add(TX_BODY_KEY_AUXILIARY_DATA, auxiliary_data_hash)
|
||||
|
||||
await self.ctx.call(auxiliary_data_supplement, CardanoTxHostAck)
|
||||
|
||||
# minting
|
||||
|
||||
async def _process_minting(
|
||||
self, minting_dict: HashBuilderDict[bytes, HashBuilderDict]
|
||||
) -> None:
|
||||
token_minting: CardanoTxMint = await self.ctx.call(
|
||||
CardanoTxItemAck(), CardanoTxMint
|
||||
)
|
||||
|
||||
await show_warning_tx_contains_mint(self.ctx)
|
||||
|
||||
for _ in range(token_minting.asset_groups_count):
|
||||
asset_group: CardanoAssetGroup = await self.ctx.call(
|
||||
CardanoTxItemAck(), CardanoAssetGroup
|
||||
)
|
||||
self._validate_asset_group(asset_group, is_mint=True)
|
||||
|
||||
tokens: HashBuilderDict[bytes, int] = HashBuilderDict(
|
||||
asset_group.tokens_count, INVALID_TOKEN_BUNDLE_MINT
|
||||
)
|
||||
with minting_dict.add(asset_group.policy_id, tokens):
|
||||
await self._process_minting_tokens(
|
||||
tokens,
|
||||
asset_group.policy_id,
|
||||
asset_group.tokens_count,
|
||||
)
|
||||
|
||||
# minting tokens
|
||||
|
||||
async def _process_minting_tokens(
|
||||
self,
|
||||
tokens: HashBuilderDict[bytes, int],
|
||||
policy_id: bytes,
|
||||
tokens_count: int,
|
||||
) -> None:
|
||||
for _ in range(tokens_count):
|
||||
token: CardanoToken = await self.ctx.call(CardanoTxItemAck(), CardanoToken)
|
||||
self._validate_token(token, is_mint=True)
|
||||
await confirm_token_minting(self.ctx, policy_id, token)
|
||||
|
||||
assert token.mint_amount is not None # _validate_token
|
||||
tokens.add(token.asset_name_bytes, token.mint_amount)
|
||||
|
||||
# script data hash
|
||||
|
||||
async def _process_script_data_hash(self) -> None:
|
||||
assert self.msg.script_data_hash is not None
|
||||
self._validate_script_data_hash()
|
||||
await confirm_script_data_hash(self.ctx, self.msg.script_data_hash)
|
||||
self.tx_dict.add(TX_BODY_KEY_SCRIPT_DATA_HASH, self.msg.script_data_hash)
|
||||
|
||||
def _validate_script_data_hash(self) -> None:
|
||||
assert self.msg.script_data_hash is not None
|
||||
if len(self.msg.script_data_hash) != SCRIPT_DATA_HASH_SIZE:
|
||||
raise INVALID_SCRIPT_DATA_HASH
|
||||
|
||||
# collateral inputs
|
||||
|
||||
async def _process_collateral_inputs(
|
||||
self, collateral_inputs_list: HashBuilderList[tuple[bytes, int]]
|
||||
) -> None:
|
||||
for _ in range(self.msg.collateral_inputs_count):
|
||||
collateral_input: CardanoTxCollateralInput = await self.ctx.call(
|
||||
CardanoTxItemAck(), CardanoTxCollateralInput
|
||||
)
|
||||
self._validate_collateral_input(collateral_input)
|
||||
await confirm_collateral_input(self.ctx, collateral_input)
|
||||
collateral_inputs_list.append(
|
||||
(collateral_input.prev_hash, collateral_input.prev_index)
|
||||
)
|
||||
|
||||
def _validate_collateral_input(
|
||||
self, collateral_input: CardanoTxCollateralInput
|
||||
) -> None:
|
||||
if len(collateral_input.prev_hash) != INPUT_PREV_HASH_SIZE:
|
||||
raise INVALID_COLLATERAL_INPUT
|
||||
|
||||
# required signers
|
||||
|
||||
async def _process_required_signers(
|
||||
self, required_signers_list: HashBuilderList[bytes]
|
||||
) -> None:
|
||||
for _ in range(self.msg.required_signers_count):
|
||||
required_signer: CardanoTxRequiredSigner = await self.ctx.call(
|
||||
CardanoTxItemAck(), CardanoTxRequiredSigner
|
||||
)
|
||||
self._validate_required_signer(required_signer)
|
||||
await confirm_required_signer(self.ctx, required_signer)
|
||||
|
||||
key_hash = required_signer.key_hash or get_public_key_hash(
|
||||
self.keychain, required_signer.key_path
|
||||
)
|
||||
required_signers_list.append(key_hash)
|
||||
|
||||
def _validate_required_signer(
|
||||
self, required_signer: CardanoTxRequiredSigner
|
||||
) -> None:
|
||||
if required_signer.key_hash and required_signer.key_path:
|
||||
raise INVALID_REQUIRED_SIGNER
|
||||
|
||||
if required_signer.key_hash:
|
||||
if len(required_signer.key_hash) != ADDRESS_KEY_HASH_SIZE:
|
||||
raise INVALID_REQUIRED_SIGNER
|
||||
elif required_signer.key_path:
|
||||
if not (
|
||||
is_shelley_path(required_signer.key_path)
|
||||
or is_multisig_path(required_signer.key_path)
|
||||
or is_minting_path(required_signer.key_path)
|
||||
):
|
||||
raise INVALID_REQUIRED_SIGNER
|
||||
else:
|
||||
raise INVALID_REQUIRED_SIGNER
|
||||
|
||||
# witness requests
|
||||
|
||||
async def _process_witness_requests(self, tx_hash: bytes) -> CardanoTxResponseType:
|
||||
response: CardanoTxResponseType = CardanoTxItemAck()
|
||||
|
||||
for _ in range(self.msg.witness_requests_count):
|
||||
witness_request = await self.ctx.call(response, CardanoTxWitnessRequest)
|
||||
self._validate_witness_request(witness_request)
|
||||
path = witness_request.path
|
||||
await self._show_witness_request(path)
|
||||
if is_byron_path(path):
|
||||
response = self._get_byron_witness(path, tx_hash)
|
||||
else:
|
||||
response = self._get_shelley_witness(path, tx_hash)
|
||||
|
||||
return response
|
||||
|
||||
def _validate_witness_request(
|
||||
self, witness_request: CardanoTxWitnessRequest
|
||||
) -> None:
|
||||
self.account_path_checker.add_witness_request(witness_request)
|
||||
|
||||
async def _show_witness_request(
|
||||
self,
|
||||
witness_path: list[int],
|
||||
) -> None:
|
||||
await confirm_witness_request(self.ctx, witness_path)
|
||||
|
||||
# helpers
|
||||
|
||||
def _is_network_id_verifiable(self) -> bool:
|
||||
"""
|
||||
Checks whether there is at least one element that contains information about
|
||||
network ID, otherwise Trezor cannot guarantee that the tx is actually meant for
|
||||
the given network.
|
||||
|
||||
Note: Shelley addresses contain network id. The intended network of Byron
|
||||
addresses can be determined based on whether they contain the protocol magic.
|
||||
These checks are performed during address validation.
|
||||
"""
|
||||
return (
|
||||
self.msg.include_network_id
|
||||
or self.msg.outputs_count != 0
|
||||
or self.msg.withdrawals_count != 0
|
||||
)
|
||||
|
||||
def _get_output_address(self, output: CardanoTxOutput) -> bytes:
|
||||
if output.address_parameters:
|
||||
return derive_address_bytes(
|
||||
self.keychain,
|
||||
output.address_parameters,
|
||||
self.msg.protocol_magic,
|
||||
self.msg.network_id,
|
||||
)
|
||||
else:
|
||||
assert output.address is not None # _validate_output
|
||||
return get_address_bytes_unsafe(output.address)
|
||||
|
||||
def _get_output_address_type(self, output: CardanoTxOutput) -> CardanoAddressType:
|
||||
if output.address_parameters:
|
||||
return output.address_parameters.address_type
|
||||
assert output.address is not None # _validate_output
|
||||
return get_address_type(get_address_bytes_unsafe(output.address))
|
||||
|
||||
def _derive_withdrawal_reward_address_bytes(
|
||||
self, withdrawal: CardanoTxWithdrawal
|
||||
) -> bytes:
|
||||
reward_address_type = (
|
||||
CardanoAddressType.REWARD
|
||||
if withdrawal.path or withdrawal.key_hash
|
||||
else CardanoAddressType.REWARD_SCRIPT
|
||||
)
|
||||
return derive_address_bytes(
|
||||
self.keychain,
|
||||
CardanoAddressParametersType(
|
||||
address_type=reward_address_type,
|
||||
address_n_staking=withdrawal.path,
|
||||
staking_key_hash=withdrawal.key_hash,
|
||||
script_staking_hash=withdrawal.script_hash,
|
||||
),
|
||||
self.msg.protocol_magic,
|
||||
self.msg.network_id,
|
||||
)
|
||||
|
||||
def _get_byron_witness(
|
||||
self, path: list[int], tx_hash: bytes
|
||||
) -> CardanoTxWitnessResponse:
|
||||
node = self.keychain.derive(path)
|
||||
return CardanoTxWitnessResponse(
|
||||
type=CardanoTxWitnessType.BYRON_WITNESS,
|
||||
pub_key=derive_public_key(self.keychain, path),
|
||||
signature=self._sign_tx_hash(tx_hash, path),
|
||||
chain_code=node.chain_code(),
|
||||
)
|
||||
|
||||
def _get_shelley_witness(
|
||||
self, path: list[int], tx_hash: bytes
|
||||
) -> CardanoTxWitnessResponse:
|
||||
return CardanoTxWitnessResponse(
|
||||
type=CardanoTxWitnessType.SHELLEY_WITNESS,
|
||||
pub_key=derive_public_key(self.keychain, path),
|
||||
signature=self._sign_tx_hash(tx_hash, path),
|
||||
)
|
||||
|
||||
def _sign_tx_hash(self, tx_body_hash: bytes, path: list[int]) -> bytes:
|
||||
node = self.keychain.derive(path)
|
||||
return ed25519.sign_ext(
|
||||
node.private_key(), node.private_key_ext(), tx_body_hash
|
||||
)
|
||||
|
||||
async def _fail_or_warn_if_invalid_path(
|
||||
self, schema: PathSchema, path: list[int], path_name: str
|
||||
) -> None:
|
||||
if not schema.match(path):
|
||||
await self._fail_or_warn_path(path, path_name)
|
||||
|
||||
async def _fail_or_warn_path(self, path: list[int], path_name: str) -> None:
|
||||
if safety_checks.is_strict():
|
||||
raise wire.DataError(f"Invalid {path_name.lower()}")
|
||||
else:
|
||||
await show_warning_path(self.ctx, path, path_name)
|
||||
|
||||
def _fail_if_strict_and_unusual(
|
||||
self, address_parameters: CardanoAddressParametersType
|
||||
) -> None:
|
||||
if not safety_checks.is_strict():
|
||||
return
|
||||
|
||||
if Credential.payment_credential(address_parameters).is_unusual_path:
|
||||
raise wire.DataError(f"Invalid {CHANGE_OUTPUT_PATH_NAME.lower()}")
|
||||
|
||||
if Credential.stake_credential(address_parameters).is_unusual_path:
|
||||
raise wire.DataError(f"Invalid {CHANGE_OUTPUT_STAKING_PATH_NAME.lower()}")
|
@ -1,6 +1,6 @@
|
||||
from common import *
|
||||
from trezor import wire
|
||||
from trezor.enums import CardanoCertificateType, CardanoTxSigningMode
|
||||
from trezor.enums import CardanoCertificateType
|
||||
from trezor.messages import CardanoTxCertificate, CardanoPoolParametersType
|
||||
|
||||
from apps.common.paths import HARDENED
|
||||
@ -15,465 +15,346 @@ if not utils.BITCOIN_ONLY:
|
||||
class TestCardanoCertificate(unittest.TestCase):
|
||||
def test_validate_certificate(self):
|
||||
valid_test_vectors = [
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
),
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
),
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxSigningMode.MULTISIG_TRANSACTION,
|
||||
),
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxSigningMode.PLUTUS_TRANSACTION,
|
||||
),
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
CardanoTxSigningMode.PLUTUS_TRANSACTION,
|
||||
),
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool=unhexlify(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_POOL_REGISTRATION,
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
metadata=None,
|
||||
),
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
),
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
),
|
||||
CardanoTxSigningMode.MULTISIG_TRANSACTION,
|
||||
),
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
),
|
||||
CardanoTxSigningMode.PLUTUS_TRANSACTION,
|
||||
),
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
),
|
||||
CardanoTxSigningMode.PLUTUS_TRANSACTION,
|
||||
),
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
),
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
),
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
),
|
||||
CardanoTxSigningMode.MULTISIG_TRANSACTION,
|
||||
),
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
),
|
||||
CardanoTxSigningMode.PLUTUS_TRANSACTION,
|
||||
),
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
),
|
||||
CardanoTxSigningMode.PLUTUS_TRANSACTION,
|
||||
),
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_POOL_REGISTRATION,
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
metadata=None,
|
||||
),
|
||||
),
|
||||
CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER,
|
||||
),
|
||||
]
|
||||
|
||||
invalid_test_vectors = [
|
||||
# STAKE_REGISTRATION neither path or script_hash is set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
),
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
),
|
||||
# STAKE_REGISTRATION both path and script_hash are set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
),
|
||||
# STAKE_REGISTRATION both script_hash and key_hash are set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxSigningMode.PLUTUS_TRANSACTION,
|
||||
),
|
||||
# STAKE_REGISTRATION pool is set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
),
|
||||
# STAKE_REGISTRATION pool parameters are set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_REGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
),
|
||||
CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER,
|
||||
),
|
||||
# STAKE_DELEGATION neither path or script_hash is set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
),
|
||||
# STAKE_DELEGATION both path and script_hash are set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
),
|
||||
# STAKE_DELEGATION both script_hash and key_hash are set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
CardanoTxSigningMode.PLUTUS_TRANSACTION,
|
||||
),
|
||||
# STAKE_DELEGATION pool parameters are set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool=unhexlify(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DELEGATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
),
|
||||
CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER,
|
||||
),
|
||||
# STAKE_DEREGISTRATION neither path or script_hash is set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
),
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
),
|
||||
# STAKE_DEREGISTRATION both path and script_hash are set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
),
|
||||
# STAKE_DEREGISTRATION both script_hash and key_hash are set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
CardanoTxSigningMode.PLUTUS_TRANSACTION,
|
||||
),
|
||||
# STAKE_DEREGISTRATION pool is set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
),
|
||||
# STAKE_DEREGISTRATION pool parameters are set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
),
|
||||
),
|
||||
CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER,
|
||||
),
|
||||
# STAKE_POOL_REGISTRATION pool parameters are not set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_POOL_REGISTRATION,
|
||||
),
|
||||
CardanoTxSigningMode.ORDINARY_TRANSACTION,
|
||||
),
|
||||
# STAKE_POOL_REGISTRATION path is set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_POOL_REGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
),
|
||||
),
|
||||
CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER,
|
||||
),
|
||||
# STAKE_POOL_REGISTRATION script hash is set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_POOL_REGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
),
|
||||
),
|
||||
CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER,
|
||||
),
|
||||
# STAKE_POOL_REGISTRATION key hash is set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_POOL_REGISTRATION,
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
),
|
||||
),
|
||||
CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER,
|
||||
),
|
||||
# STAKE_POOL_REGISTRATION pool is set
|
||||
(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_POOL_REGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool=unhexlify(
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_DEREGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
),
|
||||
),
|
||||
# STAKE_POOL_REGISTRATION pool parameters are not set
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_POOL_REGISTRATION,
|
||||
),
|
||||
# STAKE_POOL_REGISTRATION path is set
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_POOL_REGISTRATION,
|
||||
path=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
),
|
||||
),
|
||||
# STAKE_POOL_REGISTRATION script hash is set
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_POOL_REGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
),
|
||||
),
|
||||
# STAKE_POOL_REGISTRATION key hash is set
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_POOL_REGISTRATION,
|
||||
key_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
),
|
||||
),
|
||||
# STAKE_POOL_REGISTRATION pool is set
|
||||
CardanoTxCertificate(
|
||||
type=CardanoCertificateType.STAKE_POOL_REGISTRATION,
|
||||
script_hash=unhexlify(
|
||||
"29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd"
|
||||
),
|
||||
pool=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
pool_parameters=CardanoPoolParametersType(
|
||||
pool_id=unhexlify(
|
||||
"f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973"
|
||||
),
|
||||
vrf_key_hash=unhexlify(
|
||||
"198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640"
|
||||
),
|
||||
pledge=500000000,
|
||||
cost=340000000,
|
||||
margin_numerator=1,
|
||||
margin_denominator=2,
|
||||
reward_account="stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
|
||||
owners_count=1,
|
||||
relays_count=1,
|
||||
),
|
||||
CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER,
|
||||
),
|
||||
]
|
||||
|
||||
for certificate, signing_mode in valid_test_vectors:
|
||||
for certificate in valid_test_vectors:
|
||||
validate_certificate(
|
||||
certificate,
|
||||
signing_mode,
|
||||
protocol_magics.MAINNET,
|
||||
network_ids.MAINNET,
|
||||
AccountPathChecker(),
|
||||
)
|
||||
|
||||
for certificate, signing_mode in invalid_test_vectors:
|
||||
for certificate in invalid_test_vectors:
|
||||
with self.assertRaises(wire.ProcessError):
|
||||
validate_certificate(
|
||||
certificate,
|
||||
signing_mode,
|
||||
protocol_magics.MAINNET,
|
||||
network_ids.MAINNET,
|
||||
AccountPathChecker(),
|
||||
|
@ -1184,8 +1184,8 @@
|
||||
"TT_cardano-test_sign_tx.py::test_cardano_sign_tx_failed[margin_higher_than_1]": "9e1f554bb74f847e8f09201dda808fd1e0cdb68737515f34d6ad435957671c77",
|
||||
"TT_cardano-test_sign_tx.py::test_cardano_sign_tx_failed[missing_owner_with_path]": "2dede6a914015cb606124904268abc3cb9034a8e716beb380c62dee29e8a48a4",
|
||||
"TT_cardano-test_sign_tx.py::test_cardano_sign_tx_failed[multisig_transaction_with_1852_multisi-b7679330": "de16c51797b0e28e766553a31c8ad849c7eb247b0101a1996dba2eea1d59ed2d",
|
||||
"TT_cardano-test_sign_tx.py::test_cardano_sign_tx_failed[multisig_transaction_with_a_collateral_input]": "875ec83057c301a416c4ea3e77b931132cef45fafc7272cd95cd04bb6d8c4e86",
|
||||
"TT_cardano-test_sign_tx.py::test_cardano_sign_tx_failed[multisig_transaction_with_a_required_signer]": "875ec83057c301a416c4ea3e77b931132cef45fafc7272cd95cd04bb6d8c4e86",
|
||||
"TT_cardano-test_sign_tx.py::test_cardano_sign_tx_failed[multisig_transaction_with_a_collateral_input]": "9e1f554bb74f847e8f09201dda808fd1e0cdb68737515f34d6ad435957671c77",
|
||||
"TT_cardano-test_sign_tx.py::test_cardano_sign_tx_failed[multisig_transaction_with_a_required_signer]": "9e1f554bb74f847e8f09201dda808fd1e0cdb68737515f34d6ad435957671c77",
|
||||
"TT_cardano-test_sign_tx.py::test_cardano_sign_tx_failed[multisig_transaction_with_long_token_m-9fb3cfe5": "31f7a5a1d32c997c61649ace37fdf3aab3d7bf1cb7c73ffa777ba03ff52470ec",
|
||||
"TT_cardano-test_sign_tx.py::test_cardano_sign_tx_failed[multisig_transaction_with_output_conta-e3b36436": "875ec83057c301a416c4ea3e77b931132cef45fafc7272cd95cd04bb6d8c4e86",
|
||||
"TT_cardano-test_sign_tx.py::test_cardano_sign_tx_failed[multisig_transaction_with_repeated_withdrawal]": "691048e2f57f12bd216e1803237f8ce738a0bc66c546601ed2e26fa76ee831dc",
|
||||
|
Loading…
Reference in New Issue
Block a user