mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-17 21:22:10 +00:00
feat(cardano): add support for script addresses derivation
This commit is contained in:
parent
dd9652cd07
commit
21281d7cf4
@ -128,6 +128,8 @@ message CardanoAddressParametersType {
|
||||
// can be sent directly e.g. if it doesn't belong to
|
||||
// the same account as address_n
|
||||
optional CardanoBlockchainPointerType certificate_pointer = 5; // a pointer to the staking key registration certificate
|
||||
optional bytes script_payment_hash = 6;
|
||||
optional bytes script_staking_hash = 7;
|
||||
}
|
||||
|
||||
/**
|
||||
|
80
common/tests/fixtures/cardano/get_base_address_with_script_hashes.json
vendored
Normal file
80
common/tests/fixtures/cardano/get_base_address_with_script_hashes.json
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
{
|
||||
"setup": {
|
||||
"mnemonic": "all all all all all all all all all all all all",
|
||||
"passphrase": ""
|
||||
},
|
||||
"tests": [
|
||||
{
|
||||
"parameters": {
|
||||
"address_type": "base_script_key",
|
||||
"script_payment_hash": "0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe",
|
||||
"staking_path": "m/1852'/1815'/0'/2/0",
|
||||
"network_id": 1,
|
||||
"protocol_magic": 764824073
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "addr1zyx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3lsj922xhxkn6twlq2wn4q50q352annk3903tj00h45mgfmsf42dkl"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"address_type": "base_script_key",
|
||||
"script_payment_hash": "0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe",
|
||||
"staking_path": "m/1852'/1815'/0'/2/0",
|
||||
"network_id": 0,
|
||||
"protocol_magic": 42
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "addr_test1zqx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3lsj922xhxkn6twlq2wn4q50q352annk3903tj00h45mgfms2rhd6q"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"address_type": "base_key_script",
|
||||
"path": "m/1852'/1815'/0'/0/0",
|
||||
"script_staking_hash": "8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9",
|
||||
"network_id": 1,
|
||||
"protocol_magic": 764824073
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "addr1yxq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z925d004u0fv0r3a4ld7f8yg8rmxnk5dsxf542ghcc42ngw5s8vnrtt"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"address_type": "base_key_script",
|
||||
"path": "m/1852'/1815'/0'/0/0",
|
||||
"script_staking_hash": "8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9",
|
||||
"network_id": 0,
|
||||
"protocol_magic": 42
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "addr_test1yzq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z925d004u0fv0r3a4ld7f8yg8rmxnk5dsxf542ghcc42ngw5sy6wr85"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"address_type": "base_script_script",
|
||||
"script_payment_hash": "0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe",
|
||||
"script_staking_hash": "8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9",
|
||||
"network_id": 1,
|
||||
"protocol_magic": 764824073
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "addr1xyx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3l5d004u0fv0r3a4ld7f8yg8rmxnk5dsxf542ghcc42ngw5s3gftll"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"address_type": "base_script_script",
|
||||
"script_payment_hash": "0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe",
|
||||
"script_staking_hash": "8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9",
|
||||
"network_id": 0,
|
||||
"protocol_magic": 42
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "addr_test1xqx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3l5d004u0fv0r3a4ld7f8yg8rmxnk5dsxf542ghcc42ngw5sj75tnq"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -25,6 +25,28 @@
|
||||
"result": {
|
||||
"expected_address": "addr_test1vzq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z92s8k2y47"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"address_type": "enterprise_script",
|
||||
"script_payment_hash": "0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe",
|
||||
"network_id": 1,
|
||||
"protocol_magic": 764824073
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "addr1wyx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3lsqee7sp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"address_type": "enterprise_script",
|
||||
"script_payment_hash": "0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe",
|
||||
"network_id": 0,
|
||||
"protocol_magic": 42
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "addr_test1wqx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3lsm3dzly"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -31,6 +31,34 @@
|
||||
"result": {
|
||||
"expected_address": "addr_test1gzq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z925ph3wczvf2ag2x9t"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"address_type": "pointer_script",
|
||||
"script_payment_hash": "0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe",
|
||||
"block_index": 24157,
|
||||
"tx_index": 177,
|
||||
"certificate_index": 42,
|
||||
"network_id": 1,
|
||||
"protocol_magic": 764824073
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "addr12yx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3l5ph3wczvf2zmd4yp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"address_type": "pointer_script",
|
||||
"script_payment_hash": "0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe",
|
||||
"block_index": 24157,
|
||||
"tx_index": 177,
|
||||
"certificate_index": 42,
|
||||
"network_id": 0,
|
||||
"protocol_magic": 42
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "addr_test12qx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3l5ph3wczvf2d4sugn"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -6,8 +6,8 @@
|
||||
"tests": [
|
||||
{
|
||||
"parameters": {
|
||||
"path": "m/1852'/1815'/0'/2/0",
|
||||
"address_type": "reward",
|
||||
"staking_path": "m/1852'/1815'/0'/2/0",
|
||||
"network_id": 1,
|
||||
"protocol_magic": 764824073
|
||||
},
|
||||
@ -17,14 +17,36 @@
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"path": "m/1852'/1815'/0'/2/0",
|
||||
"address_type": "reward",
|
||||
"staking_path": "m/1852'/1815'/0'/2/0",
|
||||
"network_id": 0,
|
||||
"protocol_magic": 42
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "stake_test1uqfz49rtntfa9h0s98f6s28sg69weemgjhc4e8hm66d5yac643znq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"address_type": "reward_script",
|
||||
"script_staking_hash": "8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9",
|
||||
"network_id": 1,
|
||||
"protocol_magic": 764824073
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "stake17xxhh6785k83c76lklynjyr3anfm2xcry624ytuv24f582gt5mad4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"address_type": "reward_script",
|
||||
"script_staking_hash": "8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9",
|
||||
"network_id": 0,
|
||||
"protocol_magic": 42
|
||||
},
|
||||
"result": {
|
||||
"expected_address": "stake_test17zxhh6785k83c76lklynjyr3anfm2xcry624ytuv24f582gv73lfg"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -462,6 +462,8 @@ if not utils.BITCOIN_ONLY:
|
||||
import apps.cardano.helpers.account_path_check
|
||||
apps.cardano.helpers.bech32
|
||||
import apps.cardano.helpers.bech32
|
||||
apps.cardano.helpers.credential
|
||||
import apps.cardano.helpers.credential
|
||||
apps.cardano.helpers.hash_builder_collection
|
||||
import apps.cardano.helpers.hash_builder_collection
|
||||
apps.cardano.helpers.network_ids
|
||||
@ -470,8 +472,6 @@ if not utils.BITCOIN_ONLY:
|
||||
import apps.cardano.helpers.paths
|
||||
apps.cardano.helpers.protocol_magics
|
||||
import apps.cardano.helpers.protocol_magics
|
||||
apps.cardano.helpers.staking_use_cases
|
||||
import apps.cardano.helpers.staking_use_cases
|
||||
apps.cardano.helpers.utils
|
||||
import apps.cardano.helpers.utils
|
||||
apps.cardano.layout
|
||||
|
@ -1,4 +1,4 @@
|
||||
from trezor.crypto import base58, hashlib
|
||||
from trezor.crypto import base58
|
||||
from trezor.enums import CardanoAddressType
|
||||
|
||||
from .byron_address import derive_byron_address, validate_byron_address
|
||||
@ -7,17 +7,20 @@ from .helpers import (
|
||||
INVALID_ADDRESS,
|
||||
INVALID_ADDRESS_PARAMETERS,
|
||||
NETWORK_MISMATCH,
|
||||
SCRIPT_HASH_SIZE,
|
||||
bech32,
|
||||
network_ids,
|
||||
)
|
||||
from .helpers.paths import SCHEMA_STAKING_ANY_ACCOUNT
|
||||
from .helpers.utils import derive_public_key, variable_length_encode
|
||||
from .helpers.utils import get_public_key_hash, variable_length_encode
|
||||
from .seed import is_byron_path, is_shelley_path
|
||||
|
||||
if False:
|
||||
from typing import Any
|
||||
|
||||
from trezor.messages import (
|
||||
CardanoBlockchainPointerType,
|
||||
CardanoAddressParametersType,
|
||||
CardanoBlockchainPointerType,
|
||||
)
|
||||
from . import seed
|
||||
|
||||
@ -33,6 +36,7 @@ ADDRESS_TYPES_SHELLEY = (
|
||||
CardanoAddressType.REWARD,
|
||||
CardanoAddressType.REWARD_SCRIPT,
|
||||
)
|
||||
|
||||
MIN_ADDRESS_BYTES_LENGTH = 29
|
||||
MAX_ADDRESS_BYTES_LENGTH = 65
|
||||
|
||||
@ -43,20 +47,56 @@ def validate_address_parameters(parameters: CardanoAddressParametersType) -> Non
|
||||
if parameters.address_type == CardanoAddressType.BYRON:
|
||||
if not is_byron_path(parameters.address_n):
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
elif parameters.address_type in ADDRESS_TYPES_SHELLEY:
|
||||
|
||||
elif parameters.address_type == CardanoAddressType.BASE:
|
||||
if not is_shelley_path(parameters.address_n):
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
|
||||
if parameters.address_type == CardanoAddressType.BASE:
|
||||
_validate_base_address_staking_info(
|
||||
parameters.address_n_staking, parameters.staking_key_hash
|
||||
)
|
||||
|
||||
elif parameters.address_type == CardanoAddressType.BASE_SCRIPT_KEY:
|
||||
_validate_script_hash(parameters.script_payment_hash)
|
||||
_validate_base_address_staking_info(
|
||||
parameters.address_n_staking, parameters.staking_key_hash
|
||||
)
|
||||
|
||||
elif parameters.address_type == CardanoAddressType.BASE_KEY_SCRIPT:
|
||||
if not is_shelley_path(parameters.address_n):
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
_validate_script_hash(parameters.script_staking_hash)
|
||||
|
||||
elif parameters.address_type == CardanoAddressType.BASE_SCRIPT_SCRIPT:
|
||||
_validate_script_hash(parameters.script_payment_hash)
|
||||
_validate_script_hash(parameters.script_staking_hash)
|
||||
|
||||
elif parameters.address_type == CardanoAddressType.POINTER:
|
||||
if not is_shelley_path(parameters.address_n):
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
if parameters.certificate_pointer is None:
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
elif parameters.address_type == CardanoAddressType.REWARD:
|
||||
if not SCHEMA_STAKING_ANY_ACCOUNT.match(parameters.address_n):
|
||||
|
||||
elif parameters.address_type == CardanoAddressType.POINTER_SCRIPT:
|
||||
_validate_script_hash(parameters.script_payment_hash)
|
||||
if parameters.certificate_pointer is None:
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
|
||||
elif parameters.address_type == CardanoAddressType.ENTERPRISE:
|
||||
if not is_shelley_path(parameters.address_n):
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
|
||||
elif parameters.address_type == CardanoAddressType.ENTERPRISE_SCRIPT:
|
||||
_validate_script_hash(parameters.script_payment_hash)
|
||||
|
||||
elif parameters.address_type == CardanoAddressType.REWARD:
|
||||
if not is_shelley_path(parameters.address_n_staking):
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
if not SCHEMA_STAKING_ANY_ACCOUNT.match(parameters.address_n_staking):
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
|
||||
elif parameters.address_type == CardanoAddressType.REWARD_SCRIPT:
|
||||
_validate_script_hash(parameters.script_staking_hash)
|
||||
|
||||
else:
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
|
||||
@ -64,23 +104,86 @@ def validate_address_parameters(parameters: CardanoAddressParametersType) -> Non
|
||||
def _validate_address_parameters_structure(
|
||||
parameters: CardanoAddressParametersType,
|
||||
) -> None:
|
||||
address_n = parameters.address_n
|
||||
address_n_staking = parameters.address_n_staking
|
||||
staking_key_hash = parameters.staking_key_hash
|
||||
certificate_pointer = parameters.certificate_pointer
|
||||
script_payment_hash = parameters.script_payment_hash
|
||||
script_staking_hash = parameters.script_staking_hash
|
||||
|
||||
fields_to_be_empty: tuple = ()
|
||||
if parameters.address_type in (
|
||||
CardanoAddressType.BYRON,
|
||||
CardanoAddressType.REWARD,
|
||||
CardanoAddressType.ENTERPRISE,
|
||||
fields_to_be_empty: dict[CardanoAddressType, tuple[Any, ...]] = {
|
||||
CardanoAddressType.BASE: (
|
||||
certificate_pointer,
|
||||
script_payment_hash,
|
||||
script_staking_hash,
|
||||
),
|
||||
CardanoAddressType.BASE_KEY_SCRIPT: (
|
||||
address_n_staking,
|
||||
certificate_pointer,
|
||||
script_payment_hash,
|
||||
),
|
||||
CardanoAddressType.BASE_SCRIPT_KEY: (
|
||||
address_n,
|
||||
certificate_pointer,
|
||||
script_staking_hash,
|
||||
),
|
||||
CardanoAddressType.BASE_SCRIPT_SCRIPT: (
|
||||
address_n,
|
||||
address_n_staking,
|
||||
certificate_pointer,
|
||||
),
|
||||
CardanoAddressType.POINTER: (
|
||||
address_n_staking,
|
||||
staking_key_hash,
|
||||
script_payment_hash,
|
||||
script_staking_hash,
|
||||
),
|
||||
CardanoAddressType.POINTER_SCRIPT: (
|
||||
address_n,
|
||||
address_n_staking,
|
||||
staking_key_hash,
|
||||
script_staking_hash,
|
||||
),
|
||||
CardanoAddressType.ENTERPRISE: (
|
||||
address_n_staking,
|
||||
staking_key_hash,
|
||||
certificate_pointer,
|
||||
script_payment_hash,
|
||||
script_staking_hash,
|
||||
),
|
||||
CardanoAddressType.ENTERPRISE_SCRIPT: (
|
||||
address_n,
|
||||
address_n_staking,
|
||||
staking_key_hash,
|
||||
certificate_pointer,
|
||||
script_staking_hash,
|
||||
),
|
||||
CardanoAddressType.BYRON: (
|
||||
address_n_staking,
|
||||
staking_key_hash,
|
||||
certificate_pointer,
|
||||
script_payment_hash,
|
||||
script_staking_hash,
|
||||
),
|
||||
CardanoAddressType.REWARD: (
|
||||
address_n,
|
||||
staking_key_hash,
|
||||
certificate_pointer,
|
||||
script_payment_hash,
|
||||
script_staking_hash,
|
||||
),
|
||||
CardanoAddressType.REWARD_SCRIPT: (
|
||||
address_n,
|
||||
address_n_staking,
|
||||
staking_key_hash,
|
||||
certificate_pointer,
|
||||
script_payment_hash,
|
||||
),
|
||||
}
|
||||
|
||||
if parameters.address_type not in fields_to_be_empty or any(
|
||||
fields_to_be_empty[parameters.address_type]
|
||||
):
|
||||
fields_to_be_empty = (address_n_staking, staking_key_hash, certificate_pointer)
|
||||
elif parameters.address_type == CardanoAddressType.BASE:
|
||||
fields_to_be_empty = (certificate_pointer,)
|
||||
elif parameters.address_type == CardanoAddressType.POINTER:
|
||||
fields_to_be_empty = (address_n_staking, staking_key_hash)
|
||||
|
||||
if any(fields_to_be_empty):
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
|
||||
|
||||
@ -101,6 +204,11 @@ def _validate_base_address_staking_info(
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
|
||||
|
||||
def _validate_script_hash(script_hash: bytes | None) -> None:
|
||||
if not script_hash or len(script_hash) != SCRIPT_HASH_SIZE:
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
|
||||
|
||||
def _validate_address_and_get_type(
|
||||
address: str, protocol_magic: int, network_id: int
|
||||
) -> int:
|
||||
@ -189,7 +297,7 @@ def _get_bech32_hrp_for_address(
|
||||
# Byron address uses base58 encoding
|
||||
raise ValueError
|
||||
|
||||
if address_type == CardanoAddressType.REWARD:
|
||||
if address_type in (CardanoAddressType.REWARD, CardanoAddressType.REWARD_SCRIPT):
|
||||
if network_ids.is_mainnet(network_id):
|
||||
return bech32.HRP_REWARD_ADDRESS
|
||||
else:
|
||||
@ -210,11 +318,6 @@ def _get_address_network_id(address: bytes) -> int:
|
||||
return address[0] & 0x0F
|
||||
|
||||
|
||||
def get_public_key_hash(keychain: seed.Keychain, path: list[int]) -> bytes:
|
||||
public_key = derive_public_key(keychain, path)
|
||||
return hashlib.blake2b(data=public_key, outlen=ADDRESS_KEY_HASH_SIZE).digest()
|
||||
|
||||
|
||||
def derive_human_readable_address(
|
||||
keychain: seed.Keychain,
|
||||
parameters: CardanoAddressParametersType,
|
||||
@ -258,36 +361,14 @@ def derive_address_bytes(
|
||||
|
||||
|
||||
def _derive_shelley_address(
|
||||
keychain: seed.Keychain,
|
||||
parameters: CardanoAddressParametersType,
|
||||
network_id: int,
|
||||
keychain: seed.Keychain, parameters: CardanoAddressParametersType, network_id: int
|
||||
) -> bytes:
|
||||
if parameters.address_type == CardanoAddressType.BASE:
|
||||
address = _derive_base_address(
|
||||
keychain,
|
||||
parameters.address_n,
|
||||
parameters.address_n_staking,
|
||||
parameters.staking_key_hash,
|
||||
network_id,
|
||||
)
|
||||
elif parameters.address_type == CardanoAddressType.POINTER:
|
||||
# ensured by validate_address_parameters
|
||||
assert parameters.certificate_pointer is not None
|
||||
header = _create_address_header(parameters.address_type, network_id)
|
||||
|
||||
address = _derive_pointer_address(
|
||||
keychain,
|
||||
parameters.address_n,
|
||||
parameters.certificate_pointer,
|
||||
network_id,
|
||||
)
|
||||
elif parameters.address_type == CardanoAddressType.ENTERPRISE:
|
||||
address = _derive_enterprise_address(keychain, parameters.address_n, network_id)
|
||||
elif parameters.address_type == CardanoAddressType.REWARD:
|
||||
address = _derive_reward_address(keychain, parameters.address_n, network_id)
|
||||
else:
|
||||
raise INVALID_ADDRESS_PARAMETERS
|
||||
payment_part = _get_address_payment_part(keychain, parameters)
|
||||
staking_part = _get_address_staking_part(keychain, parameters)
|
||||
|
||||
return address
|
||||
return header + payment_part + staking_part
|
||||
|
||||
|
||||
def _create_address_header(address_type: CardanoAddressType, network_id: int) -> bytes:
|
||||
@ -295,33 +376,30 @@ def _create_address_header(address_type: CardanoAddressType, network_id: int) ->
|
||||
return header.to_bytes(1, "little")
|
||||
|
||||
|
||||
def _derive_base_address(
|
||||
keychain: seed.Keychain,
|
||||
path: list[int],
|
||||
staking_path: list[int],
|
||||
staking_key_hash: bytes | None,
|
||||
network_id: int,
|
||||
def _get_address_payment_part(
|
||||
keychain: seed.Keychain, parameters: CardanoAddressParametersType
|
||||
) -> bytes:
|
||||
header = _create_address_header(CardanoAddressType.BASE, network_id)
|
||||
spending_key_hash = get_public_key_hash(keychain, path)
|
||||
|
||||
if staking_key_hash is None:
|
||||
staking_key_hash = get_public_key_hash(keychain, staking_path)
|
||||
|
||||
return header + spending_key_hash + staking_key_hash
|
||||
if parameters.address_n:
|
||||
return get_public_key_hash(keychain, parameters.address_n)
|
||||
elif parameters.script_payment_hash:
|
||||
return parameters.script_payment_hash
|
||||
else:
|
||||
return bytes()
|
||||
|
||||
|
||||
def _derive_pointer_address(
|
||||
keychain: seed.Keychain,
|
||||
path: list[int],
|
||||
pointer: CardanoBlockchainPointerType,
|
||||
network_id: int,
|
||||
def _get_address_staking_part(
|
||||
keychain: seed.Keychain, parameters: CardanoAddressParametersType
|
||||
) -> bytes:
|
||||
header = _create_address_header(CardanoAddressType.POINTER, network_id)
|
||||
spending_key_hash = get_public_key_hash(keychain, path)
|
||||
encoded_pointer = _encode_certificate_pointer(pointer)
|
||||
|
||||
return header + spending_key_hash + encoded_pointer
|
||||
if parameters.staking_key_hash:
|
||||
return parameters.staking_key_hash
|
||||
elif parameters.address_n_staking:
|
||||
return get_public_key_hash(keychain, parameters.address_n_staking)
|
||||
elif parameters.script_staking_hash:
|
||||
return parameters.script_staking_hash
|
||||
elif parameters.certificate_pointer:
|
||||
return _encode_certificate_pointer(parameters.certificate_pointer)
|
||||
else:
|
||||
return bytes()
|
||||
|
||||
|
||||
def _encode_certificate_pointer(pointer: CardanoBlockchainPointerType) -> bytes:
|
||||
@ -330,36 +408,3 @@ def _encode_certificate_pointer(pointer: CardanoBlockchainPointerType) -> bytes:
|
||||
certificate_index_encoded = variable_length_encode(pointer.certificate_index)
|
||||
|
||||
return bytes(block_index_encoded + tx_index_encoded + certificate_index_encoded)
|
||||
|
||||
|
||||
def _derive_enterprise_address(
|
||||
keychain: seed.Keychain,
|
||||
path: list[int],
|
||||
network_id: int,
|
||||
) -> bytes:
|
||||
header = _create_address_header(CardanoAddressType.ENTERPRISE, network_id)
|
||||
spending_key_hash = get_public_key_hash(keychain, path)
|
||||
|
||||
return header + spending_key_hash
|
||||
|
||||
|
||||
def _derive_reward_address(
|
||||
keychain: seed.Keychain,
|
||||
path: list[int],
|
||||
network_id: int,
|
||||
) -> bytes:
|
||||
staking_key_hash = get_public_key_hash(keychain, path)
|
||||
|
||||
return pack_reward_address_bytes(staking_key_hash, network_id)
|
||||
|
||||
|
||||
def pack_reward_address_bytes(
|
||||
staking_key_hash: bytes,
|
||||
network_id: int,
|
||||
) -> bytes:
|
||||
"""
|
||||
Helper function to transform raw staking key hash into reward address
|
||||
"""
|
||||
header = _create_address_header(CardanoAddressType.REWARD, network_id)
|
||||
|
||||
return header + staking_key_hash
|
||||
|
@ -1,19 +1,10 @@
|
||||
from trezor import log, wire
|
||||
from trezor.messages import CardanoAddress
|
||||
from trezor.ui.layouts import show_address
|
||||
|
||||
from apps.common import paths
|
||||
|
||||
from . import seed
|
||||
from .address import derive_human_readable_address, validate_address_parameters
|
||||
from .helpers import protocol_magics, staking_use_cases
|
||||
from .helpers.paths import SCHEMA_PAYMENT, SCHEMA_STAKING
|
||||
from .helpers.utils import to_account_path
|
||||
from .layout import (
|
||||
ADDRESS_TYPE_NAMES,
|
||||
show_warning_address_foreign_staking_key,
|
||||
show_warning_address_pointer,
|
||||
)
|
||||
from .helpers.credential import Credential, should_show_address_credentials
|
||||
from .layout import show_cardano_address, show_credentials
|
||||
from .sign_tx import validate_network_info
|
||||
|
||||
if False:
|
||||
@ -29,15 +20,6 @@ async def get_address(
|
||||
) -> CardanoAddress:
|
||||
address_parameters = msg.address_parameters
|
||||
|
||||
await paths.validate_path(
|
||||
ctx,
|
||||
keychain,
|
||||
address_parameters.address_n,
|
||||
# path must match the PAYMENT or STAKING schema
|
||||
SCHEMA_PAYMENT.match(address_parameters.address_n)
|
||||
or SCHEMA_STAKING.match(address_parameters.address_n),
|
||||
)
|
||||
|
||||
validate_network_info(msg.network_id, msg.protocol_magic)
|
||||
validate_address_parameters(address_parameters)
|
||||
|
||||
@ -51,51 +33,22 @@ async def get_address(
|
||||
raise wire.ProcessError("Deriving address failed")
|
||||
|
||||
if msg.show_display:
|
||||
await _display_address(
|
||||
ctx, keychain, address_parameters, address, msg.protocol_magic
|
||||
)
|
||||
await _display_address(ctx, address_parameters, address, msg.protocol_magic)
|
||||
|
||||
return CardanoAddress(address=address)
|
||||
|
||||
|
||||
async def _display_address(
|
||||
ctx: wire.Context,
|
||||
keychain: seed.Keychain,
|
||||
address_parameters: CardanoAddressParametersType,
|
||||
address: str,
|
||||
protocol_magic: int,
|
||||
) -> None:
|
||||
await _show_staking_warnings(ctx, keychain, address_parameters)
|
||||
|
||||
network_name = None
|
||||
if not protocol_magics.is_mainnet(protocol_magic):
|
||||
network_name = protocol_magics.to_ui_string(protocol_magic)
|
||||
|
||||
address_n = paths.address_n_to_str(address_parameters.address_n)
|
||||
await show_address(
|
||||
if should_show_address_credentials(address_parameters):
|
||||
await show_credentials(
|
||||
ctx,
|
||||
address=address,
|
||||
title="%s address" % ADDRESS_TYPE_NAMES[address_parameters.address_type],
|
||||
network=network_name,
|
||||
address_extra=address_n,
|
||||
title_qr=address_n,
|
||||
Credential.payment_credential(address_parameters),
|
||||
Credential.stake_credential(address_parameters),
|
||||
)
|
||||
|
||||
|
||||
async def _show_staking_warnings(
|
||||
ctx: wire.Context,
|
||||
keychain: seed.Keychain,
|
||||
address_parameters: CardanoAddressParametersType,
|
||||
) -> None:
|
||||
staking_type = staking_use_cases.get(keychain, address_parameters)
|
||||
if staking_type == staking_use_cases.MISMATCH:
|
||||
await show_warning_address_foreign_staking_key(
|
||||
ctx,
|
||||
to_account_path(address_parameters.address_n),
|
||||
to_account_path(address_parameters.address_n_staking),
|
||||
address_parameters.staking_key_hash,
|
||||
)
|
||||
elif staking_type == staking_use_cases.POINTER_ADDRESS:
|
||||
# ensured in _derive_shelley_address:
|
||||
assert address_parameters.certificate_pointer is not None
|
||||
await show_warning_address_pointer(ctx, address_parameters.certificate_pointer)
|
||||
await show_cardano_address(ctx, address_parameters, address, protocol_magic)
|
||||
|
220
core/src/apps/cardano/helpers/credential.py
Normal file
220
core/src/apps/cardano/helpers/credential.py
Normal file
@ -0,0 +1,220 @@
|
||||
from trezor.enums import CardanoAddressType
|
||||
|
||||
from ...common.paths import address_n_to_str
|
||||
from .paths import CHAIN_STAKING_KEY, SCHEMA_PAYMENT, SCHEMA_STAKING
|
||||
from .utils import format_key_hash, format_script_hash, to_account_path
|
||||
|
||||
if False:
|
||||
from trezor.messages import (
|
||||
CardanoBlockchainPointerType,
|
||||
CardanoAddressParametersType,
|
||||
)
|
||||
from trezor.ui.layouts import PropertyType
|
||||
|
||||
|
||||
class Credential:
|
||||
"""
|
||||
Serves mainly as a wrapper object for credentials (so that they don't have to be
|
||||
passed into functions separately) which also determines all properties that should be shown
|
||||
as warnings.
|
||||
Also contains functions which simplify displaying the credential.
|
||||
"""
|
||||
|
||||
type_name: str
|
||||
address_type: CardanoAddressType
|
||||
path: list[int]
|
||||
key_hash: bytes | None
|
||||
script_hash: bytes | None
|
||||
pointer: CardanoBlockchainPointerType | None
|
||||
|
||||
is_reward: bool = False
|
||||
is_no_staking: bool = False
|
||||
is_mismatch: bool = False
|
||||
is_unusual_path: bool = False
|
||||
is_other_warning: bool = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type_name: str,
|
||||
address_type: CardanoAddressType,
|
||||
path: list[int],
|
||||
key_hash: bytes | None,
|
||||
script_hash: bytes | None,
|
||||
pointer: CardanoBlockchainPointerType | None,
|
||||
):
|
||||
self.type_name = type_name
|
||||
self.address_type = address_type
|
||||
self.path = path
|
||||
self.key_hash = key_hash
|
||||
self.script_hash = script_hash
|
||||
self.pointer = pointer
|
||||
|
||||
@classmethod
|
||||
def payment_credential(
|
||||
cls, address_params: CardanoAddressParametersType
|
||||
) -> "Credential":
|
||||
address_type = address_params.address_type
|
||||
credential = cls(
|
||||
"payment",
|
||||
address_type,
|
||||
address_params.address_n,
|
||||
None,
|
||||
address_params.script_payment_hash,
|
||||
None,
|
||||
)
|
||||
|
||||
if address_type in (
|
||||
CardanoAddressType.BASE,
|
||||
CardanoAddressType.BASE_KEY_SCRIPT,
|
||||
CardanoAddressType.POINTER,
|
||||
CardanoAddressType.ENTERPRISE,
|
||||
CardanoAddressType.BYRON,
|
||||
):
|
||||
if not SCHEMA_PAYMENT.match(address_params.address_n):
|
||||
credential.is_unusual_path = True
|
||||
|
||||
elif address_type in (
|
||||
CardanoAddressType.BASE_SCRIPT_KEY,
|
||||
CardanoAddressType.BASE_SCRIPT_SCRIPT,
|
||||
CardanoAddressType.POINTER_SCRIPT,
|
||||
CardanoAddressType.ENTERPRISE_SCRIPT,
|
||||
):
|
||||
credential.is_other_warning = True
|
||||
|
||||
elif address_type in (
|
||||
CardanoAddressType.REWARD,
|
||||
CardanoAddressType.REWARD_SCRIPT,
|
||||
):
|
||||
credential.is_reward = True
|
||||
|
||||
else:
|
||||
raise RuntimeError # we didn't cover all address types
|
||||
|
||||
return credential
|
||||
|
||||
@classmethod
|
||||
def stake_credential(
|
||||
cls, address_params: CardanoAddressParametersType
|
||||
) -> "Credential":
|
||||
address_type = address_params.address_type
|
||||
credential = cls(
|
||||
"stake",
|
||||
address_type,
|
||||
address_params.address_n_staking,
|
||||
address_params.staking_key_hash,
|
||||
address_params.script_staking_hash,
|
||||
address_params.certificate_pointer,
|
||||
)
|
||||
|
||||
if address_type == CardanoAddressType.BASE:
|
||||
if address_params.staking_key_hash:
|
||||
credential.is_other_warning = True
|
||||
else:
|
||||
if not SCHEMA_STAKING.match(address_params.address_n_staking):
|
||||
credential.is_unusual_path = True
|
||||
if not _do_base_address_credentials_match(
|
||||
address_params.address_n,
|
||||
address_params.address_n_staking,
|
||||
):
|
||||
credential.is_mismatch = True
|
||||
|
||||
elif address_type == CardanoAddressType.BASE_SCRIPT_KEY:
|
||||
if address_params.address_n_staking and not SCHEMA_STAKING.match(
|
||||
address_params.address_n_staking
|
||||
):
|
||||
credential.is_unusual_path = True
|
||||
|
||||
elif address_type in (
|
||||
CardanoAddressType.POINTER,
|
||||
CardanoAddressType.POINTER_SCRIPT,
|
||||
):
|
||||
credential.is_other_warning = True
|
||||
|
||||
elif address_type == CardanoAddressType.REWARD:
|
||||
if not SCHEMA_STAKING.match(address_params.address_n_staking):
|
||||
credential.is_unusual_path = True
|
||||
|
||||
elif address_type in (
|
||||
CardanoAddressType.BASE_KEY_SCRIPT,
|
||||
CardanoAddressType.BASE_SCRIPT_SCRIPT,
|
||||
CardanoAddressType.REWARD_SCRIPT,
|
||||
):
|
||||
credential.is_other_warning = True
|
||||
|
||||
elif address_type in (
|
||||
CardanoAddressType.ENTERPRISE,
|
||||
CardanoAddressType.ENTERPRISE_SCRIPT,
|
||||
CardanoAddressType.BYRON,
|
||||
):
|
||||
credential.is_no_staking = True
|
||||
|
||||
else:
|
||||
raise RuntimeError # we didn't cover all address types
|
||||
|
||||
return credential
|
||||
|
||||
def should_warn(self) -> bool:
|
||||
return any(
|
||||
(
|
||||
self.is_reward,
|
||||
self.is_no_staking,
|
||||
self.is_mismatch,
|
||||
self.is_unusual_path,
|
||||
self.is_other_warning,
|
||||
)
|
||||
)
|
||||
|
||||
def is_set(self) -> bool:
|
||||
return any((self.path, self.key_hash, self.script_hash, self.pointer))
|
||||
|
||||
def get_title(self) -> str:
|
||||
if self.path:
|
||||
return "path"
|
||||
elif self.key_hash:
|
||||
return "key hash"
|
||||
elif self.script_hash:
|
||||
return "script"
|
||||
elif self.pointer:
|
||||
return "pointer"
|
||||
else:
|
||||
return ""
|
||||
|
||||
def format(self) -> list[PropertyType]:
|
||||
if self.path:
|
||||
return [(None, address_n_to_str(self.path))]
|
||||
elif self.key_hash:
|
||||
return [(None, format_key_hash(self.key_hash, False))]
|
||||
elif self.script_hash:
|
||||
return [(None, format_script_hash(self.script_hash))]
|
||||
elif self.pointer:
|
||||
return [
|
||||
("Block: %s" % self.pointer.block_index, None),
|
||||
("Transaction: %s" % self.pointer.tx_index, None),
|
||||
("Certificate: %s" % self.pointer.certificate_index, None),
|
||||
]
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
def should_show_address_credentials(
|
||||
address_parameters: CardanoAddressParametersType,
|
||||
) -> bool:
|
||||
return not (
|
||||
address_parameters.address_type == CardanoAddressType.BASE
|
||||
and SCHEMA_PAYMENT.match(address_parameters.address_n)
|
||||
and _do_base_address_credentials_match(
|
||||
address_parameters.address_n,
|
||||
address_parameters.address_n_staking,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _do_base_address_credentials_match(
|
||||
address_n: list[int],
|
||||
address_n_staking: list[int],
|
||||
) -> bool:
|
||||
return address_n_staking == _path_to_staking_path(address_n)
|
||||
|
||||
|
||||
def _path_to_staking_path(path: list[int]) -> list[int]:
|
||||
return to_account_path(path) + [CHAIN_STAKING_KEY, 0]
|
@ -19,8 +19,9 @@ SCHEMA_STAKING = PathSchema.parse("m/[1852]'/coin_type'/account'/2/0", SLIP44_ID
|
||||
SCHEMA_STAKING_ANY_ACCOUNT = PathSchema.parse("m/[1852]'/coin_type'/[0-%s]'/2/0" % (HARDENED - 1), SLIP44_ID)
|
||||
# fmt: on
|
||||
|
||||
ACCOUNT_PATH_LENGTH = const(3)
|
||||
ACCOUNT_PATH_INDEX = const(2)
|
||||
ACCOUNT_PATH_LENGTH = const(3)
|
||||
CHAIN_STAKING_KEY = const(2)
|
||||
|
||||
CHANGE_OUTPUT_PATH_NAME = "Change output path"
|
||||
CHANGE_OUTPUT_STAKING_PATH_NAME = "Change output staking path"
|
||||
|
@ -1,54 +0,0 @@
|
||||
from trezor.enums import CardanoAddressType
|
||||
|
||||
from ..address import get_public_key_hash
|
||||
from ..seed import is_shelley_path
|
||||
from .utils import to_account_path
|
||||
|
||||
if False:
|
||||
from trezor.messages import CardanoAddressParametersType
|
||||
from ..seed import Keychain
|
||||
|
||||
|
||||
"""
|
||||
Used as a helper when deciding what warnings we should
|
||||
display to the user during get_address and sign_tx depending
|
||||
on the type of address and its parameters.
|
||||
"""
|
||||
|
||||
|
||||
NO_STAKING = 0
|
||||
MATCH = 1
|
||||
MISMATCH = 2
|
||||
POINTER_ADDRESS = 3
|
||||
|
||||
|
||||
def get(keychain: Keychain, address_parameters: CardanoAddressParametersType) -> int:
|
||||
address_type = address_parameters.address_type
|
||||
if address_type == CardanoAddressType.BASE:
|
||||
if not is_shelley_path(address_parameters.address_n):
|
||||
return MISMATCH
|
||||
|
||||
spending_account_staking_path = _path_to_staking_path(
|
||||
address_parameters.address_n
|
||||
)
|
||||
if address_parameters.address_n_staking:
|
||||
if address_parameters.address_n_staking != spending_account_staking_path:
|
||||
return MISMATCH
|
||||
else:
|
||||
staking_key_hash = get_public_key_hash(
|
||||
keychain, spending_account_staking_path
|
||||
)
|
||||
if address_parameters.staking_key_hash != staking_key_hash:
|
||||
return MISMATCH
|
||||
|
||||
return MATCH
|
||||
elif address_type == CardanoAddressType.POINTER:
|
||||
return POINTER_ADDRESS
|
||||
elif address_type == CardanoAddressType.REWARD:
|
||||
return MATCH
|
||||
else:
|
||||
return NO_STAKING
|
||||
|
||||
|
||||
def _path_to_staking_path(path: list[int]) -> list[int]:
|
||||
return to_account_path(path) + [2, 0]
|
@ -6,6 +6,7 @@ from trezor.enums import (
|
||||
CardanoNativeScriptHashDisplayFormat,
|
||||
CardanoNativeScriptType,
|
||||
)
|
||||
from trezor.messages import CardanoAddressParametersType
|
||||
from trezor.strings import format_amount
|
||||
from trezor.ui.layouts import (
|
||||
confirm_blob,
|
||||
@ -13,16 +14,13 @@ from trezor.ui.layouts import (
|
||||
confirm_output,
|
||||
confirm_path_warning,
|
||||
confirm_properties,
|
||||
show_address,
|
||||
)
|
||||
|
||||
from apps.common.paths import address_n_to_str
|
||||
|
||||
from . import seed
|
||||
from .address import (
|
||||
encode_human_readable_address,
|
||||
get_public_key_hash,
|
||||
pack_reward_address_bytes,
|
||||
)
|
||||
from .address import derive_human_readable_address
|
||||
from .helpers import protocol_magics
|
||||
from .helpers.utils import (
|
||||
format_account_number,
|
||||
@ -37,7 +35,6 @@ from .helpers.utils import (
|
||||
if False:
|
||||
from trezor import wire
|
||||
from trezor.messages import (
|
||||
CardanoBlockchainPointerType,
|
||||
CardanoNativeScript,
|
||||
CardanoTxCertificate,
|
||||
CardanoTxWithdrawal,
|
||||
@ -48,14 +45,21 @@ if False:
|
||||
)
|
||||
|
||||
from trezor.ui.layouts import PropertyType
|
||||
from .helpers.credential import Credential
|
||||
|
||||
|
||||
ADDRESS_TYPE_NAMES = {
|
||||
CardanoAddressType.BYRON: "Legacy",
|
||||
CardanoAddressType.BASE: "Base",
|
||||
CardanoAddressType.BASE_SCRIPT_KEY: "Base",
|
||||
CardanoAddressType.BASE_KEY_SCRIPT: "Base",
|
||||
CardanoAddressType.BASE_SCRIPT_SCRIPT: "Base",
|
||||
CardanoAddressType.POINTER: "Pointer",
|
||||
CardanoAddressType.POINTER_SCRIPT: "Pointer",
|
||||
CardanoAddressType.ENTERPRISE: "Enterprise",
|
||||
CardanoAddressType.ENTERPRISE_SCRIPT: "Enterprise",
|
||||
CardanoAddressType.REWARD: "Reward",
|
||||
CardanoAddressType.REWARD_SCRIPT: "Reward",
|
||||
}
|
||||
|
||||
SCRIPT_TYPE_NAMES = {
|
||||
@ -184,13 +188,15 @@ async def confirm_sending(
|
||||
ctx: wire.Context,
|
||||
ada_amount: int,
|
||||
to: str,
|
||||
is_change_output: bool,
|
||||
) -> None:
|
||||
subtitle = "Change amount:" if is_change_output else "Confirm sending:"
|
||||
await confirm_output(
|
||||
ctx,
|
||||
to,
|
||||
format_coin_amount(ada_amount),
|
||||
title="Confirm transaction",
|
||||
subtitle="Confirm sending:",
|
||||
subtitle=subtitle,
|
||||
font_amount=ui.BOLD,
|
||||
width_paginated=17,
|
||||
to_str="\nto\n",
|
||||
@ -220,13 +226,76 @@ async def confirm_sending_token(
|
||||
)
|
||||
|
||||
|
||||
async def show_warning_tx_output_contains_tokens(ctx: wire.Context) -> None:
|
||||
await confirm_metadata(
|
||||
async def show_credentials(
|
||||
ctx: wire.Context,
|
||||
payment_credential: Credential,
|
||||
stake_credential: Credential,
|
||||
is_change_output: bool = False,
|
||||
) -> None:
|
||||
await _show_credential(ctx, payment_credential, is_change_output)
|
||||
await _show_credential(ctx, stake_credential, is_change_output)
|
||||
|
||||
|
||||
async def _show_credential(
|
||||
ctx: wire.Context,
|
||||
credential: Credential,
|
||||
is_change_output: bool = False,
|
||||
) -> None:
|
||||
if is_change_output:
|
||||
title = "Confirm transaction"
|
||||
else:
|
||||
title = "%s address" % ADDRESS_TYPE_NAMES[credential.address_type]
|
||||
|
||||
props: list[PropertyType] = []
|
||||
|
||||
# Credential can be empty in case of enterprise address stake credential
|
||||
# and reward address payment credential. In that case we don't want to
|
||||
# show some of the "props".
|
||||
if credential.is_set():
|
||||
if is_change_output:
|
||||
address_usage = "Change address"
|
||||
else:
|
||||
address_usage = "Address"
|
||||
|
||||
credential_title = credential.get_title()
|
||||
props.append(
|
||||
(
|
||||
"%s %s credential is a %s:"
|
||||
% (address_usage, credential.type_name, credential_title),
|
||||
None,
|
||||
)
|
||||
)
|
||||
props.extend(credential.format())
|
||||
|
||||
if credential.is_unusual_path:
|
||||
props.append((None, "Path is unusual."))
|
||||
if credential.is_mismatch:
|
||||
props.append((None, "Credential doesn't match payment credential."))
|
||||
if credential.is_reward:
|
||||
props.append(("Address is a reward address.", None))
|
||||
if credential.is_no_staking:
|
||||
props.append(
|
||||
(
|
||||
"%s address - no staking rewards."
|
||||
% ADDRESS_TYPE_NAMES[credential.address_type],
|
||||
None,
|
||||
)
|
||||
)
|
||||
|
||||
if credential.should_warn():
|
||||
icon = ui.ICON_WRONG
|
||||
icon_color = ui.RED
|
||||
else:
|
||||
icon = ui.ICON_SEND
|
||||
icon_color = ui.GREEN
|
||||
|
||||
await confirm_properties(
|
||||
ctx,
|
||||
"confirm_tokens",
|
||||
title="Confirm transaction",
|
||||
content="The following\ntransaction output\ncontains tokens.",
|
||||
larger_vspace=True,
|
||||
"confirm_credential",
|
||||
title=title,
|
||||
props=props,
|
||||
icon=icon,
|
||||
icon_color=icon_color,
|
||||
br_code=ButtonRequestType.Other,
|
||||
)
|
||||
|
||||
@ -235,88 +304,13 @@ async def show_warning_path(ctx: wire.Context, path: list[int], title: str) -> N
|
||||
await confirm_path_warning(ctx, address_n_to_str(path), path_type=title)
|
||||
|
||||
|
||||
async def show_warning_tx_no_staking_info(
|
||||
ctx: wire.Context, address_type: CardanoAddressType, amount: int
|
||||
) -> None:
|
||||
atype = ADDRESS_TYPE_NAMES[address_type].lower()
|
||||
content = "Change %s address has no stake rights.\nChange amount:\n{}" % atype
|
||||
async def show_warning_tx_output_contains_tokens(ctx: wire.Context) -> None:
|
||||
await confirm_metadata(
|
||||
ctx,
|
||||
"warning_staking",
|
||||
"confirm_tokens",
|
||||
title="Confirm transaction",
|
||||
content=content,
|
||||
param=format_coin_amount(amount),
|
||||
hide_continue=True,
|
||||
br_code=ButtonRequestType.Other,
|
||||
)
|
||||
|
||||
|
||||
async def show_warning_tx_pointer_address(
|
||||
ctx: wire.Context,
|
||||
pointer: CardanoBlockchainPointerType,
|
||||
amount: int,
|
||||
) -> None:
|
||||
await confirm_properties(
|
||||
ctx,
|
||||
"warning_pointer",
|
||||
title="Confirm transaction",
|
||||
props=[
|
||||
("Change address has a\npointer with staking\nrights.\n\n\n", None),
|
||||
(
|
||||
"Pointer:",
|
||||
"%s, %s, %s"
|
||||
% (
|
||||
pointer.block_index,
|
||||
pointer.tx_index,
|
||||
pointer.certificate_index,
|
||||
),
|
||||
),
|
||||
("Change amount:", format_coin_amount(amount)),
|
||||
],
|
||||
br_code=ButtonRequestType.Other,
|
||||
)
|
||||
|
||||
|
||||
async def show_warning_tx_different_staking_account(
|
||||
ctx: wire.Context,
|
||||
staking_account_path: list[int],
|
||||
amount: int,
|
||||
) -> None:
|
||||
await confirm_properties(
|
||||
ctx,
|
||||
"warning_differentstaking",
|
||||
title="Confirm transaction",
|
||||
props=[
|
||||
(
|
||||
"Change address staking rights do not match the current account.\n\n",
|
||||
None,
|
||||
),
|
||||
(
|
||||
"Staking account %s:" % format_account_number(staking_account_path),
|
||||
address_n_to_str(staking_account_path),
|
||||
),
|
||||
("Change amount:", format_coin_amount(amount)),
|
||||
],
|
||||
br_code=ButtonRequestType.Other,
|
||||
)
|
||||
|
||||
|
||||
async def show_warning_tx_staking_key_hash(
|
||||
ctx: wire.Context,
|
||||
staking_key_hash: bytes,
|
||||
amount: int,
|
||||
) -> None:
|
||||
props = [
|
||||
("Change address staking rights do not match the current account.\n\n", None),
|
||||
("Staking key hash:", staking_key_hash),
|
||||
("Change amount:", format_coin_amount(amount)),
|
||||
]
|
||||
|
||||
await confirm_properties(
|
||||
ctx,
|
||||
"confirm_different_stakingrights",
|
||||
title="Confirm transaction",
|
||||
props=props,
|
||||
content="The following\ntransaction output\ncontains tokens.",
|
||||
larger_vspace=True,
|
||||
br_code=ButtonRequestType.Other,
|
||||
)
|
||||
|
||||
@ -414,6 +408,7 @@ async def confirm_stake_pool_owner(
|
||||
ctx: wire.Context,
|
||||
keychain: seed.Keychain,
|
||||
owner: CardanoPoolOwner,
|
||||
protocol_magic: int,
|
||||
network_id: int,
|
||||
) -> None:
|
||||
props: list[tuple[str, str | None]] = []
|
||||
@ -421,11 +416,14 @@ async def confirm_stake_pool_owner(
|
||||
props.append(("Pool owner:", address_n_to_str(owner.staking_key_path)))
|
||||
props.append(
|
||||
(
|
||||
encode_human_readable_address(
|
||||
pack_reward_address_bytes(
|
||||
get_public_key_hash(keychain, owner.staking_key_path),
|
||||
derive_human_readable_address(
|
||||
keychain,
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.REWARD,
|
||||
address_n=owner.staking_key_path,
|
||||
),
|
||||
protocol_magic,
|
||||
network_id,
|
||||
)
|
||||
),
|
||||
None,
|
||||
)
|
||||
@ -435,8 +433,14 @@ async def confirm_stake_pool_owner(
|
||||
props.append(
|
||||
(
|
||||
"Pool owner:",
|
||||
encode_human_readable_address(
|
||||
pack_reward_address_bytes(owner.staking_key_hash, network_id)
|
||||
derive_human_readable_address(
|
||||
keychain,
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.REWARD,
|
||||
staking_key_hash=owner.staking_key_hash,
|
||||
),
|
||||
protocol_magic,
|
||||
network_id,
|
||||
),
|
||||
)
|
||||
)
|
||||
@ -605,20 +609,39 @@ async def show_warning_tx_network_unverifiable(ctx: wire.Context) -> None:
|
||||
)
|
||||
|
||||
|
||||
async def show_warning_address_pointer(
|
||||
ctx: wire.Context, pointer: CardanoBlockchainPointerType
|
||||
async def show_cardano_address(
|
||||
ctx: wire.Context,
|
||||
address_parameters: CardanoAddressParametersType,
|
||||
address: str,
|
||||
protocol_magic: int,
|
||||
) -> None:
|
||||
content = "Pointer address:\nBlock: %s\nTransaction: %s\nCertificate: %s" % (
|
||||
pointer.block_index,
|
||||
pointer.tx_index,
|
||||
pointer.certificate_index,
|
||||
)
|
||||
await confirm_metadata(
|
||||
network_name = None
|
||||
if not protocol_magics.is_mainnet(protocol_magic):
|
||||
network_name = protocol_magics.to_ui_string(protocol_magic)
|
||||
|
||||
title = "%s address" % ADDRESS_TYPE_NAMES[address_parameters.address_type]
|
||||
address_extra = None
|
||||
title_qr = title
|
||||
if address_parameters.address_type in (
|
||||
CardanoAddressType.BYRON,
|
||||
CardanoAddressType.BASE,
|
||||
CardanoAddressType.BASE_KEY_SCRIPT,
|
||||
CardanoAddressType.POINTER,
|
||||
CardanoAddressType.ENTERPRISE,
|
||||
CardanoAddressType.REWARD,
|
||||
):
|
||||
if address_parameters.address_n:
|
||||
address_extra = address_n_to_str(address_parameters.address_n)
|
||||
title_qr = address_n_to_str(address_parameters.address_n)
|
||||
elif address_parameters.address_n_staking:
|
||||
address_extra = address_n_to_str(address_parameters.address_n_staking)
|
||||
title_qr = address_n_to_str(address_parameters.address_n_staking)
|
||||
|
||||
await show_address(
|
||||
ctx,
|
||||
"warning_pointer",
|
||||
title="Warning",
|
||||
icon=ui.ICON_WRONG,
|
||||
icon_color=ui.RED,
|
||||
content=content,
|
||||
br_code=ButtonRequestType.Other,
|
||||
address=address,
|
||||
title=title,
|
||||
network=network_name,
|
||||
address_extra=address_extra,
|
||||
title_qr=title_qr,
|
||||
)
|
||||
|
@ -64,9 +64,9 @@ from .helpers import (
|
||||
LOVELACE_MAX_SUPPLY,
|
||||
network_ids,
|
||||
protocol_magics,
|
||||
staking_use_cases,
|
||||
)
|
||||
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,
|
||||
@ -89,13 +89,10 @@ from .layout import (
|
||||
confirm_stake_pool_registration_final,
|
||||
confirm_transaction,
|
||||
confirm_withdrawal,
|
||||
show_credentials,
|
||||
show_warning_path,
|
||||
show_warning_tx_different_staking_account,
|
||||
show_warning_tx_network_unverifiable,
|
||||
show_warning_tx_no_staking_info,
|
||||
show_warning_tx_output_contains_tokens,
|
||||
show_warning_tx_pointer_address,
|
||||
show_warning_tx_staking_key_hash,
|
||||
)
|
||||
from .seed import is_byron_path
|
||||
|
||||
@ -302,8 +299,16 @@ async def _process_outputs(
|
||||
total_amount = 0
|
||||
for _ in range(outputs_count):
|
||||
output: CardanoTxOutput = await ctx.call(CardanoTxItemAck(), CardanoTxOutput)
|
||||
_validate_output(output, protocol_magic, network_id, account_path_checker)
|
||||
if signing_mode == CardanoTxSigningMode.ORDINARY_TRANSACTION:
|
||||
_validate_output(
|
||||
output,
|
||||
signing_mode,
|
||||
protocol_magic,
|
||||
network_id,
|
||||
account_path_checker,
|
||||
)
|
||||
|
||||
should_show_output = _should_show_output(output, signing_mode)
|
||||
if should_show_output:
|
||||
await _show_output(
|
||||
ctx,
|
||||
keychain,
|
||||
@ -335,7 +340,7 @@ async def _process_outputs(
|
||||
ctx,
|
||||
asset_groups_dict,
|
||||
output.asset_groups_count,
|
||||
_should_show_tokens(output, signing_mode),
|
||||
should_show_output,
|
||||
)
|
||||
|
||||
total_amount += output.amount
|
||||
@ -634,6 +639,7 @@ def _validate_stake_pool_registration_tx_structure(msg: CardanoSignTxInit) -> No
|
||||
|
||||
def _validate_output(
|
||||
output: CardanoTxOutput,
|
||||
signing_mode: CardanoTxSigningMode,
|
||||
protocol_magic: int,
|
||||
network_id: int,
|
||||
account_path_checker: AccountPathChecker,
|
||||
@ -641,8 +647,9 @@ def _validate_output(
|
||||
if output.address_parameters and output.address is not None:
|
||||
raise INVALID_OUTPUT
|
||||
|
||||
if output.address_parameters:
|
||||
validate_address_parameters(output.address_parameters)
|
||||
if address_parameters := output.address_parameters:
|
||||
validate_address_parameters(address_parameters)
|
||||
_fail_if_strict_and_unusual(address_parameters)
|
||||
elif output.address is not None:
|
||||
validate_output_address(output.address, protocol_magic, network_id)
|
||||
else:
|
||||
@ -651,6 +658,23 @@ def _validate_output(
|
||||
account_path_checker.add_output(output)
|
||||
|
||||
|
||||
def _should_show_output(
|
||||
output: CardanoTxOutput,
|
||||
signing_mode: CardanoTxSigningMode,
|
||||
) -> bool:
|
||||
if signing_mode == CardanoTxSigningMode.POOL_REGISTRATION_AS_OWNER:
|
||||
# In a pool registration transaction, there are no inputs belonging to the user
|
||||
# and no spending witnesses. It is thus safe to not show the outputs.
|
||||
return False
|
||||
|
||||
if output.address_parameters: # is change output
|
||||
if not should_show_address_credentials(output.address_parameters):
|
||||
# we don't need to display simple address outputs
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def _show_output(
|
||||
ctx: wire.Context,
|
||||
keychain: seed.Keychain,
|
||||
@ -659,33 +683,30 @@ async def _show_output(
|
||||
protocol_magic: int,
|
||||
network_id: int,
|
||||
) -> None:
|
||||
if output.address_parameters:
|
||||
await _fail_or_warn_if_invalid_path(
|
||||
ctx,
|
||||
SCHEMA_PAYMENT,
|
||||
output.address_parameters.address_n,
|
||||
CHANGE_OUTPUT_PATH_NAME,
|
||||
)
|
||||
|
||||
await _show_change_output_staking_warnings(
|
||||
ctx, keychain, output.address_parameters, output.amount
|
||||
)
|
||||
|
||||
if _should_hide_output(output.address_parameters.address_n):
|
||||
return
|
||||
|
||||
address = derive_human_readable_address(
|
||||
keychain, output.address_parameters, protocol_magic, network_id
|
||||
)
|
||||
else:
|
||||
assert output.address is not None # _validate_output
|
||||
address = output.address
|
||||
|
||||
if output.asset_groups_count > 0:
|
||||
await show_warning_tx_output_contains_tokens(ctx)
|
||||
|
||||
if signing_mode == CardanoTxSigningMode.ORDINARY_TRANSACTION:
|
||||
await confirm_sending(ctx, output.amount, address)
|
||||
is_change_output: bool
|
||||
if address_parameters := output.address_parameters:
|
||||
is_change_output = True
|
||||
|
||||
await show_credentials(
|
||||
ctx,
|
||||
Credential.payment_credential(address_parameters),
|
||||
Credential.stake_credential(address_parameters),
|
||||
is_change_output=True,
|
||||
)
|
||||
|
||||
address = derive_human_readable_address(
|
||||
keychain, address_parameters, protocol_magic, network_id
|
||||
)
|
||||
else:
|
||||
is_change_output = False
|
||||
|
||||
assert output.address is not None # _validate_output
|
||||
address = output.address
|
||||
|
||||
await confirm_sending(ctx, output.amount, address, is_change_output)
|
||||
|
||||
|
||||
def _validate_asset_group(
|
||||
@ -777,7 +798,11 @@ async def _show_stake_pool_registration_certificate(
|
||||
|
||||
|
||||
async def _show_pool_owner(
|
||||
ctx: wire.Context, keychain: seed.Keychain, owner: CardanoPoolOwner, network_id: int
|
||||
ctx: wire.Context,
|
||||
keychain: seed.Keychain,
|
||||
owner: CardanoPoolOwner,
|
||||
protocol_magic: int,
|
||||
network_id: int,
|
||||
) -> None:
|
||||
if owner.staking_key_path:
|
||||
await _fail_or_warn_if_invalid_path(
|
||||
@ -787,7 +812,7 @@ async def _show_pool_owner(
|
||||
POOL_OWNER_STAKING_PATH_NAME,
|
||||
)
|
||||
|
||||
await confirm_stake_pool_owner(ctx, keychain, owner, network_id)
|
||||
await confirm_stake_pool_owner(ctx, keychain, owner, protocol_magic, network_id)
|
||||
|
||||
|
||||
def _validate_witness_request(
|
||||
@ -824,78 +849,11 @@ async def _show_witness(
|
||||
ctx: wire.Context,
|
||||
witness_path: list[int],
|
||||
) -> None:
|
||||
if not SCHEMA_PAYMENT.match(witness_path) and not SCHEMA_STAKING.match(
|
||||
witness_path
|
||||
):
|
||||
await _fail_or_warn_path(
|
||||
ctx,
|
||||
witness_path,
|
||||
WITNESS_PATH_NAME,
|
||||
)
|
||||
is_payment = SCHEMA_PAYMENT.match(witness_path)
|
||||
is_staking = SCHEMA_STAKING.match(witness_path)
|
||||
|
||||
|
||||
async def _show_change_output_staking_warnings(
|
||||
ctx: wire.Context,
|
||||
keychain: seed.Keychain,
|
||||
address_parameters: CardanoAddressParametersType,
|
||||
amount: int,
|
||||
) -> None:
|
||||
address_type = address_parameters.address_type
|
||||
|
||||
if (
|
||||
address_type == CardanoAddressType.BASE
|
||||
and not address_parameters.staking_key_hash
|
||||
):
|
||||
await _fail_or_warn_if_invalid_path(
|
||||
ctx,
|
||||
SCHEMA_STAKING,
|
||||
address_parameters.address_n_staking,
|
||||
CHANGE_OUTPUT_STAKING_PATH_NAME,
|
||||
)
|
||||
|
||||
staking_use_case = staking_use_cases.get(keychain, address_parameters)
|
||||
if staking_use_case == staking_use_cases.NO_STAKING:
|
||||
await show_warning_tx_no_staking_info(ctx, address_type, amount)
|
||||
elif staking_use_case == staking_use_cases.POINTER_ADDRESS:
|
||||
# ensured in _derive_shelley_address:
|
||||
assert address_parameters.certificate_pointer is not None
|
||||
await show_warning_tx_pointer_address(
|
||||
ctx,
|
||||
address_parameters.certificate_pointer,
|
||||
amount,
|
||||
)
|
||||
elif staking_use_case == staking_use_cases.MISMATCH:
|
||||
if address_parameters.address_n_staking:
|
||||
await show_warning_tx_different_staking_account(
|
||||
ctx,
|
||||
to_account_path(address_parameters.address_n_staking),
|
||||
amount,
|
||||
)
|
||||
else:
|
||||
# ensured in _validate_base_address_staking_info:
|
||||
assert address_parameters.staking_key_hash
|
||||
await show_warning_tx_staking_key_hash(
|
||||
ctx,
|
||||
address_parameters.staking_key_hash,
|
||||
amount,
|
||||
)
|
||||
|
||||
|
||||
def _should_hide_output(path: list[int]) -> bool:
|
||||
"""Return whether the output address is from a safe path, so it could be hidden."""
|
||||
return SCHEMA_PAYMENT.match(path)
|
||||
|
||||
|
||||
def _should_show_tokens(
|
||||
output: CardanoTxOutput, signing_mode: CardanoTxSigningMode
|
||||
) -> bool:
|
||||
if signing_mode != CardanoTxSigningMode.ORDINARY_TRANSACTION:
|
||||
return False
|
||||
|
||||
if output.address_parameters:
|
||||
return not _should_hide_output(output.address_parameters.address_n)
|
||||
|
||||
return True
|
||||
if not is_payment and not is_staking:
|
||||
await _fail_or_warn_path(ctx, witness_path, WITNESS_PATH_NAME)
|
||||
|
||||
|
||||
def _is_network_id_verifiable(msg: CardanoSignTxInit) -> bool:
|
||||
@ -925,3 +883,16 @@ async def _fail_or_warn_path(
|
||||
raise wire.DataError("Invalid %s" % path_name.lower())
|
||||
else:
|
||||
await show_warning_path(ctx, path, path_name)
|
||||
|
||||
|
||||
def _fail_if_strict_and_unusual(
|
||||
address_parameters: CardanoAddressParametersType,
|
||||
) -> None:
|
||||
if not safety_checks.is_strict():
|
||||
return
|
||||
|
||||
if Credential.payment_credential(address_parameters).is_unusual_path:
|
||||
raise wire.DataError("Invalid %s" % CHANGE_OUTPUT_PATH_NAME.lower())
|
||||
|
||||
if Credential.stake_credential(address_parameters).is_unusual_path:
|
||||
raise wire.DataError("Invalid %s" % CHANGE_OUTPUT_STAKING_PATH_NAME.lower())
|
||||
|
@ -1123,6 +1123,8 @@ if TYPE_CHECKING:
|
||||
address_n_staking: "list[int]"
|
||||
staking_key_hash: "bytes | None"
|
||||
certificate_pointer: "CardanoBlockchainPointerType | None"
|
||||
script_payment_hash: "bytes | None"
|
||||
script_staking_hash: "bytes | None"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -1132,6 +1134,8 @@ if TYPE_CHECKING:
|
||||
address_n_staking: "list[int] | None" = None,
|
||||
staking_key_hash: "bytes | None" = None,
|
||||
certificate_pointer: "CardanoBlockchainPointerType | None" = None,
|
||||
script_payment_hash: "bytes | None" = None,
|
||||
script_staking_hash: "bytes | None" = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
@ -285,121 +285,6 @@ class TestCardanoAddress(unittest.TestCase):
|
||||
self.assertEqual(hexlify(seed.remove_ed25519_prefix(n.public_key())), pub)
|
||||
self.assertEqual(hexlify(n.chain_code()), chain)
|
||||
|
||||
def test_base_address(self):
|
||||
mnemonic = "test walk nut penalty hip pave soap entry language right filter choice"
|
||||
passphrase = ""
|
||||
node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
|
||||
keychain = Keychain(node)
|
||||
|
||||
test_vectors = [
|
||||
# network id, account, expected result
|
||||
# data generated with code under test
|
||||
(network_ids.MAINNET, 4, "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r"),
|
||||
(network_ids.TESTNET, 4, "addr_test1qr4sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlswzkqcu"),
|
||||
]
|
||||
|
||||
for network_id, account, expected_address in test_vectors:
|
||||
address_parameters = CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, account | HARDENED, 0, 0],
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, account | HARDENED, 2, 0]
|
||||
)
|
||||
actual_address = derive_human_readable_address(keychain, address_parameters, protocol_magics.MAINNET, network_id)
|
||||
|
||||
self.assertEqual(actual_address, expected_address)
|
||||
|
||||
def test_base_address_with_staking_key_hash(self):
|
||||
mnemonic = "test walk nut penalty hip pave soap entry language right filter choice"
|
||||
passphrase = ""
|
||||
node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
|
||||
keychain = Keychain(node)
|
||||
|
||||
test_vectors = [
|
||||
# network id, account, staking key hash, expected result
|
||||
# own staking key hash
|
||||
# data generated with code under test
|
||||
(network_ids.MAINNET, 4, unhexlify("1bc428e4720702ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff"), "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r"),
|
||||
(network_ids.TESTNET, 4, unhexlify("1bc428e4720702ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff"), "addr_test1qr4sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlswzkqcu"),
|
||||
# staking key hash not owned - derived with "all all..." mnenomnic, data generated with code under test
|
||||
(network_ids.MAINNET, 4, unhexlify("122a946b9ad3d2ddf029d3a828f0468aece76895f15c9efbd69b4277"), "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgj922xhxkn6twlq2wn4q50q352annk3903tj00h45mgfmsxrrvc2"),
|
||||
(network_ids.MAINNET, 0, unhexlify("122a946b9ad3d2ddf029d3a828f0468aece76895f15c9efbd69b4277"), "addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzersj922xhxkn6twlq2wn4q50q352annk3903tj00h45mgfms6xjnst"),
|
||||
(network_ids.TESTNET, 4, unhexlify("122a946b9ad3d2ddf029d3a828f0468aece76895f15c9efbd69b4277"), "addr_test1qr4sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgj922xhxkn6twlq2wn4q50q352annk3903tj00h45mgfms947v54"),
|
||||
]
|
||||
|
||||
for network_id, account, staking_key_hash, expected_address in test_vectors:
|
||||
address_parameters = CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, account | HARDENED, 0, 0],
|
||||
staking_key_hash=staking_key_hash,
|
||||
)
|
||||
actual_address = derive_human_readable_address(keychain, address_parameters, protocol_magics.MAINNET, network_id)
|
||||
|
||||
self.assertEqual(actual_address, expected_address)
|
||||
|
||||
def test_enterprise_address(self):
|
||||
mnemonic = "test walk nut penalty hip pave soap entry language right filter choice"
|
||||
passphrase = ""
|
||||
node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
|
||||
keychain = Keychain(node)
|
||||
|
||||
test_vectors = [
|
||||
# network id, expected result
|
||||
(network_ids.MAINNET, "addr1vx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzers66hrl8"),
|
||||
(network_ids.TESTNET, "addr_test1vz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzerspjrlsz")
|
||||
]
|
||||
|
||||
for network_id, expected_address in test_vectors:
|
||||
address_parameters = CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.ENTERPRISE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
)
|
||||
actual_address = derive_human_readable_address(keychain, address_parameters, protocol_magics.MAINNET, network_id)
|
||||
|
||||
self.assertEqual(actual_address, expected_address)
|
||||
|
||||
def test_pointer_address(self):
|
||||
mnemonic = "test walk nut penalty hip pave soap entry language right filter choice"
|
||||
passphrase = ""
|
||||
node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
|
||||
keychain = Keychain(node)
|
||||
|
||||
test_vectors = [
|
||||
# network id, pointer, expected result
|
||||
(network_ids.MAINNET, CardanoBlockchainPointerType(block_index=1, tx_index=2, certificate_index=3), "addr1gx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzerspqgpse33frd"),
|
||||
(network_ids.TESTNET, CardanoBlockchainPointerType(block_index=24157, tx_index=177, certificate_index=42), "addr_test1gz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer5ph3wczvf2pfz4ly")
|
||||
]
|
||||
|
||||
for network_id, pointer, expected_address in test_vectors:
|
||||
address_parameters = CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.POINTER,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
certificate_pointer=pointer,
|
||||
)
|
||||
actual_address = derive_human_readable_address(keychain, address_parameters, protocol_magics.MAINNET, network_id)
|
||||
|
||||
self.assertEqual(actual_address, expected_address)
|
||||
|
||||
def test_reward_address(self):
|
||||
mnemonic = "test walk nut penalty hip pave soap entry language right filter choice"
|
||||
passphrase = ""
|
||||
node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
|
||||
keychain = Keychain(node)
|
||||
|
||||
test_vectors = [
|
||||
# network id, expected result
|
||||
(network_ids.MAINNET, "stake1uyevw2xnsc0pvn9t9r9c7qryfqfeerchgrlm3ea2nefr9hqxdekzz"),
|
||||
(network_ids.TESTNET, "stake_test1uqevw2xnsc0pvn9t9r9c7qryfqfeerchgrlm3ea2nefr9hqp8n5xl")
|
||||
]
|
||||
|
||||
for network_id, expected_address in test_vectors:
|
||||
address_parameters = CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.REWARD,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
)
|
||||
actual_address = derive_human_readable_address(keychain, address_parameters, protocol_magics.MAINNET, network_id)
|
||||
|
||||
self.assertEqual(actual_address, expected_address)
|
||||
|
||||
def test_testnet_byron_address(self):
|
||||
mnemonic = "all all all all all all all all all all all all"
|
||||
passphrase = ""
|
||||
@ -421,6 +306,133 @@ class TestCardanoAddress(unittest.TestCase):
|
||||
address = derive_human_readable_address(keychain, address_parameters, protocol_magics.TESTNET, 0)
|
||||
self.assertEqual(expected, address)
|
||||
|
||||
def test_derive_address(self):
|
||||
mnemonic = "all all all all all all all all all all all all"
|
||||
passphrase = ""
|
||||
node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
|
||||
keychain = Keychain(node)
|
||||
|
||||
address_parameters = {
|
||||
"BASE": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 4 | HARDENED, 0, 0],
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 4 | HARDENED, 2, 0]
|
||||
),
|
||||
"BASE_OWN_STAKING_KEY_HASH": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 4 | HARDENED, 0, 0],
|
||||
staking_key_hash=unhexlify("1bc428e4720702ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff")
|
||||
),
|
||||
"BASE_OWN_STAKING_KEY_HASH": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 4 | HARDENED, 0, 0],
|
||||
staking_key_hash=unhexlify("1bc428e4720702ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff")
|
||||
),
|
||||
# staking key hash not owned - derived with "all all..." mnenomnic
|
||||
"BASE_FOREIGN_STAKING_KEY_HASH_ACCOUNT_4": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 4 | HARDENED, 0, 0],
|
||||
staking_key_hash=unhexlify("122a946b9ad3d2ddf029d3a828f0468aece76895f15c9efbd69b4277")
|
||||
),
|
||||
# staking key hash not owned - derived with "all all..." mnenomnic
|
||||
"BASE_FOREIGN_STAKING_KEY_HASH_ACCOUNT_0": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
staking_key_hash=unhexlify("122a946b9ad3d2ddf029d3a828f0468aece76895f15c9efbd69b4277")
|
||||
),
|
||||
"BASE_SCRIPT_KEY_SCRIPT_HASH":CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_SCRIPT_KEY,
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
script_payment_hash=unhexlify("0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe"),
|
||||
),
|
||||
"BASE_KEY_SCRIPT_HASH":CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_KEY_SCRIPT,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
script_staking_hash=unhexlify("8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9"),
|
||||
),
|
||||
"BASE_SCRIPT_SCRIPT_HASHES": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_SCRIPT_SCRIPT,
|
||||
script_payment_hash=unhexlify("0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe"),
|
||||
script_staking_hash=unhexlify("8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9"),
|
||||
),
|
||||
"POINTER1": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.POINTER,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
certificate_pointer=CardanoBlockchainPointerType(block_index=1, tx_index=2, certificate_index=3),
|
||||
),
|
||||
"POINTER2": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.POINTER,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
certificate_pointer=CardanoBlockchainPointerType(block_index=24157, tx_index=177, certificate_index=42),
|
||||
),
|
||||
"POINTER_SCRIPT_HASH": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.POINTER_SCRIPT,
|
||||
certificate_pointer=CardanoBlockchainPointerType(block_index=24157, tx_index=177, certificate_index=42),
|
||||
script_payment_hash=unhexlify("0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe"),
|
||||
),
|
||||
"ENTERPRISE": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.ENTERPRISE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
),
|
||||
"ENTERPRISE_SCRIPT_HASH": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.ENTERPRISE_SCRIPT,
|
||||
script_payment_hash=unhexlify("0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe"),
|
||||
),
|
||||
"REWARD": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.REWARD,
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
),
|
||||
"REWARD_SCRIPT_HASH": CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.REWARD_SCRIPT,
|
||||
script_staking_hash=unhexlify("8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9"),
|
||||
),
|
||||
}
|
||||
test_vectors = [
|
||||
# base address
|
||||
(network_ids.MAINNET, CardanoAddressType.BASE, address_parameters["BASE"], "addr1q8v42wjda8r6mpfj40d36znlgfdcqp7jtj03ah8skh6u8wnrqua2vw243tmjfjt0h5wsru6appuz8c0pfd75ur7myyeqsx9990"),
|
||||
(network_ids.TESTNET, CardanoAddressType.BASE, address_parameters["BASE"], "addr_test1qrv42wjda8r6mpfj40d36znlgfdcqp7jtj03ah8skh6u8wnrqua2vw243tmjfjt0h5wsru6appuz8c0pfd75ur7myyeqnsc9fs"),
|
||||
# base address with staking key hash
|
||||
(network_ids.MAINNET, CardanoAddressType.BASE, address_parameters["BASE_OWN_STAKING_KEY_HASH"], "addr1q8v42wjda8r6mpfj40d36znlgfdcqp7jtj03ah8skh6u8wsmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsydc62k"),
|
||||
(network_ids.TESTNET, CardanoAddressType.BASE, address_parameters["BASE_OWN_STAKING_KEY_HASH"], "addr_test1qrv42wjda8r6mpfj40d36znlgfdcqp7jtj03ah8skh6u8wsmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hls8m96xf"),
|
||||
(network_ids.MAINNET, CardanoAddressType.BASE, address_parameters["BASE_FOREIGN_STAKING_KEY_HASH_ACCOUNT_4"], "addr1q8v42wjda8r6mpfj40d36znlgfdcqp7jtj03ah8skh6u8wsj922xhxkn6twlq2wn4q50q352annk3903tj00h45mgfms06skxl"),
|
||||
(network_ids.MAINNET, CardanoAddressType.BASE, address_parameters["BASE_FOREIGN_STAKING_KEY_HASH_ACCOUNT_0"], "addr1qxq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z92sj922xhxkn6twlq2wn4q50q352annk3903tj00h45mgfmsl3s9zt"),
|
||||
(network_ids.TESTNET, CardanoAddressType.BASE, address_parameters["BASE_FOREIGN_STAKING_KEY_HASH_ACCOUNT_4"], "addr_test1qrv42wjda8r6mpfj40d36znlgfdcqp7jtj03ah8skh6u8wsj922xhxkn6twlq2wn4q50q352annk3903tj00h45mgfmsvvdk2q"),
|
||||
# base_script_key address
|
||||
(network_ids.MAINNET, CardanoAddressType.BASE_SCRIPT_KEY, address_parameters["BASE_SCRIPT_KEY_SCRIPT_HASH"], "addr1zyx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3lsj922xhxkn6twlq2wn4q50q352annk3903tj00h45mgfmsf42dkl"),
|
||||
(network_ids.TESTNET, CardanoAddressType.BASE_SCRIPT_KEY, address_parameters["BASE_SCRIPT_KEY_SCRIPT_HASH"], "addr_test1zqx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3lsj922xhxkn6twlq2wn4q50q352annk3903tj00h45mgfms2rhd6q"),
|
||||
# base_key_script address
|
||||
(network_ids.MAINNET, CardanoAddressType.BASE_KEY_SCRIPT, address_parameters["BASE_KEY_SCRIPT_HASH"], "addr1yxq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z925d004u0fv0r3a4ld7f8yg8rmxnk5dsxf542ghcc42ngw5s8vnrtt"),
|
||||
(network_ids.TESTNET, CardanoAddressType.BASE_KEY_SCRIPT, address_parameters["BASE_KEY_SCRIPT_HASH"], "addr_test1yzq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z925d004u0fv0r3a4ld7f8yg8rmxnk5dsxf542ghcc42ngw5sy6wr85"),
|
||||
# base_script_script address
|
||||
(network_ids.MAINNET, CardanoAddressType.BASE_SCRIPT_SCRIPT, address_parameters["BASE_SCRIPT_SCRIPT_HASHES"], "addr1xyx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3l5d004u0fv0r3a4ld7f8yg8rmxnk5dsxf542ghcc42ngw5s3gftll"),
|
||||
(network_ids.TESTNET, CardanoAddressType.BASE_SCRIPT_SCRIPT, address_parameters["BASE_SCRIPT_SCRIPT_HASHES"], "addr_test1xqx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3l5d004u0fv0r3a4ld7f8yg8rmxnk5dsxf542ghcc42ngw5sj75tnq"),
|
||||
# pointer address
|
||||
(network_ids.MAINNET, CardanoAddressType.POINTER, address_parameters["POINTER1"], "addr1gxq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z92spqgpsl97q83"),
|
||||
(network_ids.TESTNET, CardanoAddressType.POINTER, address_parameters["POINTER2"], "addr_test1gzq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z925ph3wczvf2ag2x9t"),
|
||||
# pointer_script address
|
||||
(network_ids.MAINNET, CardanoAddressType.POINTER_SCRIPT, address_parameters["POINTER_SCRIPT_HASH"], "addr12yx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3l5ph3wczvf2zmd4yp"),
|
||||
(network_ids.TESTNET, CardanoAddressType.POINTER_SCRIPT, address_parameters["POINTER_SCRIPT_HASH"], "addr_test12qx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3l5ph3wczvf2d4sugn"),
|
||||
# enterprise address
|
||||
(network_ids.MAINNET, CardanoAddressType.ENTERPRISE, address_parameters["ENTERPRISE"], "addr1vxq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z92su77c6m"),
|
||||
(network_ids.TESTNET, CardanoAddressType.ENTERPRISE, address_parameters["ENTERPRISE"], "addr_test1vzq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z92s8k2y47"),
|
||||
# enterprise_script address
|
||||
(network_ids.MAINNET, CardanoAddressType.ENTERPRISE_SCRIPT, address_parameters["ENTERPRISE_SCRIPT_HASH"], "addr1wyx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3lsqee7sp"),
|
||||
(network_ids.TESTNET, CardanoAddressType.ENTERPRISE_SCRIPT, address_parameters["ENTERPRISE_SCRIPT_HASH"], "addr_test1wqx44jlk580mpjrjfesd7v2fsuc4ejlh3wmvp7dk702k3lsm3dzly"),
|
||||
# reward address
|
||||
(network_ids.MAINNET, CardanoAddressType.REWARD, address_parameters["REWARD"], "stake1uyfz49rtntfa9h0s98f6s28sg69weemgjhc4e8hm66d5yacalmqha"),
|
||||
(network_ids.TESTNET, CardanoAddressType.REWARD, address_parameters["REWARD"], "stake_test1uqfz49rtntfa9h0s98f6s28sg69weemgjhc4e8hm66d5yac643znq"),
|
||||
# reward_script address
|
||||
(network_ids.MAINNET, CardanoAddressType.REWARD_SCRIPT, address_parameters["REWARD_SCRIPT_HASH"], "stake17xxhh6785k83c76lklynjyr3anfm2xcry624ytuv24f582gt5mad4"),
|
||||
(network_ids.TESTNET, CardanoAddressType.REWARD_SCRIPT, address_parameters["REWARD_SCRIPT_HASH"], "stake_test17zxhh6785k83c76lklynjyr3anfm2xcry624ytuv24f582gv73lfg"),
|
||||
]
|
||||
|
||||
|
||||
for network_id, address_type, address_parameters, expected_address in test_vectors:
|
||||
validate_address_parameters(address_parameters)
|
||||
actual_address = derive_human_readable_address(keychain, address_parameters, protocol_magics.MAINNET, network_id)
|
||||
|
||||
self.assertEqual(actual_address, expected_address)
|
||||
|
||||
def test_validate_address_parameters(self):
|
||||
test_vectors = [
|
||||
# base address - both address_n_staking and staking_key_hash are None
|
||||
@ -451,22 +463,80 @@ class TestCardanoAddress(unittest.TestCase):
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
staking_key_hash=None,
|
||||
),
|
||||
# base_script_key address - script_payment_hash is None
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_SCRIPT_KEY,
|
||||
script_payment_hash=None,
|
||||
staking_key_hash=unhexlify("1bc428e4720702ebd5dab4fb175324c192dc9bb76cc5da956e3c8d"),
|
||||
),
|
||||
# base_script_key address - address_n_staking is not a staking path
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_SCRIPT_KEY,
|
||||
script_payment_hash=unhexlify("0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe"),
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
),
|
||||
# base_key_script address - script_staking_hash is None
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_KEY_SCRIPT,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
script_staking_hash=None,
|
||||
),
|
||||
# base_script_script address - script_payment_hash is None
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_SCRIPT_SCRIPT,
|
||||
script_payment_hash=None,
|
||||
script_staking_hash=unhexlify("8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9"),
|
||||
),
|
||||
# base_script_script address - script_staking and script_staking_hash are None
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_SCRIPT_SCRIPT,
|
||||
script_payment_hash=unhexlify("0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe"),
|
||||
script_staking_hash=None,
|
||||
),
|
||||
# pointer address - pointer is None
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.POINTER,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
certificate_pointer=None,
|
||||
),
|
||||
# pointer_script address - pointer is None
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.POINTER_SCRIPT,
|
||||
script_payment_hash=unhexlify("0d5acbf6a1dfb0c8724e60df314987315ccbf78bb6c0f9b6f3d568fe"),
|
||||
certificate_pointer=None,
|
||||
),
|
||||
# pointer_script address - script_payment_script is None
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.POINTER_SCRIPT,
|
||||
script_payment_hash=None,
|
||||
certificate_pointer=CardanoBlockchainPointerType(block_index=24157, tx_index=177, certificate_index=42),
|
||||
),
|
||||
# enterprise_script address - script_payment_hash is None
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.ENTERPRISE_SCRIPT,
|
||||
script_payment_hash=None,
|
||||
),
|
||||
# reward address - non staking path
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.REWARD,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0]
|
||||
),
|
||||
# reward_script address - script_staking_hash is None
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.REWARD_SCRIPT,
|
||||
script_staking_hash=None,
|
||||
),
|
||||
|
||||
# Shelley addresses with Byron namespace
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0]
|
||||
address_n=[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
staking_key_hash=unhexlify("1bc428e4720702ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff"),
|
||||
),
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_KEY_SCRIPT,
|
||||
address_n=[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
script_staking_hash=unhexlify("8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9"),
|
||||
),
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.POINTER,
|
||||
|
267
core/tests/test_apps.cardano.credential.py
Normal file
267
core/tests/test_apps.cardano.credential.py
Normal file
@ -0,0 +1,267 @@
|
||||
from common import *
|
||||
|
||||
from apps.cardano.helpers.credential import Credential
|
||||
from apps.common.paths import HARDENED
|
||||
from trezor.enums import CardanoAddressType
|
||||
from trezor.messages import CardanoAddressParametersType, CardanoBlockchainPointerType
|
||||
|
||||
|
||||
CERTIFICATE_POINTER = CardanoBlockchainPointerType(
|
||||
block_index=24157,
|
||||
tx_index=177,
|
||||
certificate_index=42,
|
||||
)
|
||||
|
||||
|
||||
def _create_flags(
|
||||
is_reward: bool = False,
|
||||
is_no_staking: bool = False,
|
||||
is_mismatch: bool = False,
|
||||
is_unusual_path: bool = False,
|
||||
is_other_warning: bool = False,
|
||||
) -> tuple[bool, ...]:
|
||||
return (is_reward, is_no_staking, is_mismatch, is_unusual_path, is_other_warning)
|
||||
|
||||
|
||||
ADDRESS_PARAMETERS_CASES = [
|
||||
# base
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
),
|
||||
_create_flags(),
|
||||
_create_flags(),
|
||||
),
|
||||
# base mismatch
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 1 | HARDENED, 2, 0],
|
||||
),
|
||||
_create_flags(),
|
||||
_create_flags(is_mismatch=True),
|
||||
),
|
||||
# base payment unusual
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 101 | HARDENED, 0, 0],
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
),
|
||||
_create_flags(is_unusual_path=True),
|
||||
_create_flags(is_mismatch=True),
|
||||
),
|
||||
# base staking unusual
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 101 | HARDENED, 0, 0],
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 101 | HARDENED, 2, 0],
|
||||
),
|
||||
_create_flags(is_unusual_path=True),
|
||||
_create_flags(is_unusual_path=True),
|
||||
),
|
||||
# base both unusual and mismatch
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 101 | HARDENED, 0, 0],
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 102 | HARDENED, 2, 0],
|
||||
),
|
||||
_create_flags(is_unusual_path=True),
|
||||
_create_flags(is_mismatch=True, is_unusual_path=True),
|
||||
),
|
||||
# base staking key hash
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
staking_key_hash="1bc428e4720732ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff",
|
||||
),
|
||||
_create_flags(),
|
||||
_create_flags(is_other_warning=True),
|
||||
),
|
||||
# base key script
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_KEY_SCRIPT,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
staking_script_hash="1bc428e4720732ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff",
|
||||
),
|
||||
_create_flags(),
|
||||
_create_flags(is_other_warning=True),
|
||||
),
|
||||
# base key script unusual
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_KEY_SCRIPT,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 101 | HARDENED, 0, 0],
|
||||
staking_script_hash="1bc428e4720732ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff",
|
||||
),
|
||||
_create_flags(is_unusual_path=True),
|
||||
_create_flags(is_other_warning=True),
|
||||
),
|
||||
# base script key
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_SCRIPT_KEY,
|
||||
payment_script_hash="1bc428e4720732ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff",
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
),
|
||||
_create_flags(is_other_warning=True),
|
||||
_create_flags(),
|
||||
),
|
||||
# base script key unusual
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_SCRIPT_KEY,
|
||||
payment_script_hash="1bc428e4720732ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff",
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 101 | HARDENED, 2, 0],
|
||||
),
|
||||
_create_flags(is_other_warning=True),
|
||||
_create_flags(is_unusual_path=True),
|
||||
),
|
||||
# base script script
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE_SCRIPT_SCRIPT,
|
||||
payment_script_hash="1bc428e4720732ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff",
|
||||
staking_script_hash="2bc428e4720732ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff",
|
||||
),
|
||||
_create_flags(is_other_warning=True),
|
||||
_create_flags(is_other_warning=True),
|
||||
),
|
||||
# pointer
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.POINTER,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
certificate_pointer=CERTIFICATE_POINTER,
|
||||
),
|
||||
_create_flags(),
|
||||
_create_flags(is_other_warning=True),
|
||||
),
|
||||
# pointer unusual
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.POINTER,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 101 | HARDENED, 0, 0],
|
||||
certificate_pointer=CERTIFICATE_POINTER,
|
||||
),
|
||||
_create_flags(is_unusual_path=True),
|
||||
_create_flags(is_other_warning=True),
|
||||
),
|
||||
# pointer script
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.POINTER_SCRIPT,
|
||||
payment_script_hash="1bc428e4720732ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff",
|
||||
certificate_pointer=CERTIFICATE_POINTER,
|
||||
),
|
||||
_create_flags(is_other_warning=True),
|
||||
_create_flags(is_other_warning=True),
|
||||
),
|
||||
# enterprise
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.ENTERPRISE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
),
|
||||
_create_flags(),
|
||||
_create_flags(is_no_staking=True),
|
||||
),
|
||||
# enterprise unusual
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.ENTERPRISE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 101 | HARDENED, 0, 0],
|
||||
),
|
||||
_create_flags(is_unusual_path=True),
|
||||
_create_flags(is_no_staking=True),
|
||||
),
|
||||
# enterprise script
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.ENTERPRISE_SCRIPT,
|
||||
payment_script_hash="1bc428e4720732ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff",
|
||||
),
|
||||
_create_flags(is_other_warning=True),
|
||||
_create_flags(is_no_staking=True),
|
||||
),
|
||||
# reward
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.REWARD,
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
),
|
||||
_create_flags(is_reward=True),
|
||||
_create_flags(),
|
||||
),
|
||||
# reward unusual
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.REWARD,
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 101 | HARDENED, 2, 0],
|
||||
),
|
||||
_create_flags(is_reward=True),
|
||||
_create_flags(is_unusual_path=True),
|
||||
),
|
||||
# reward script
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.REWARD_SCRIPT,
|
||||
staking_script_hash="2bc428e4720732ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff",
|
||||
),
|
||||
_create_flags(is_reward=True),
|
||||
_create_flags(is_other_warning=True),
|
||||
),
|
||||
# byron
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BYRON,
|
||||
address_n=[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
),
|
||||
_create_flags(),
|
||||
_create_flags(is_no_staking=True),
|
||||
),
|
||||
# byron unusual
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BYRON,
|
||||
address_n=[44 | HARDENED, 1815 | HARDENED, 101 | HARDENED, 0, 0],
|
||||
),
|
||||
_create_flags(is_unusual_path=True),
|
||||
_create_flags(is_no_staking=True),
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def _get_flags(credential: Credential) -> tuple[bool, ...]:
|
||||
return (
|
||||
credential.is_reward,
|
||||
credential.is_no_staking,
|
||||
credential.is_mismatch,
|
||||
credential.is_unusual_path,
|
||||
credential.is_other_warning,
|
||||
)
|
||||
|
||||
|
||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||
class TestCardanoCredential(unittest.TestCase):
|
||||
def test_credential_flags(self):
|
||||
for (
|
||||
address_parameters,
|
||||
expected_payment_flags,
|
||||
expected_stake_flags,
|
||||
) in ADDRESS_PARAMETERS_CASES:
|
||||
payment_credential = Credential.payment_credential(address_parameters)
|
||||
stake_credential = Credential.stake_credential(address_parameters)
|
||||
self.assertEqual(_get_flags(payment_credential), expected_payment_flags)
|
||||
self.assertEqual(_get_flags(stake_credential), expected_stake_flags)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
@ -1,42 +0,0 @@
|
||||
from common import *
|
||||
from apps.common.paths import HARDENED
|
||||
|
||||
if not utils.BITCOIN_ONLY:
|
||||
from apps.cardano.sign_tx import _should_hide_output
|
||||
|
||||
|
||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||
class TestCardanoSignTransaction(unittest.TestCase):
|
||||
def test_should_show_outputs(self):
|
||||
outputs_to_hide = [
|
||||
# byron path
|
||||
[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
# shelley path
|
||||
[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
# path account is 2
|
||||
[1852 | HARDENED, 1815 | HARDENED, 2 | HARDENED, 0, 0],
|
||||
# path index is 2
|
||||
[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 2],
|
||||
]
|
||||
outputs_to_show = [
|
||||
# path is not complete
|
||||
[1852 | HARDENED, 1815 | HARDENED],
|
||||
# path is not complete
|
||||
[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED],
|
||||
# staking output path
|
||||
[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0,],
|
||||
# max safe account number exceeded
|
||||
[1852 | HARDENED, 1815 | HARDENED, 101 | HARDENED, 0, 0],
|
||||
# output address too large
|
||||
[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 1000001],
|
||||
]
|
||||
|
||||
for output_path in outputs_to_hide:
|
||||
self.assertTrue(_should_hide_output(output_path))
|
||||
|
||||
for output_path in outputs_to_show:
|
||||
self.assertFalse(_should_hide_output(output_path))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
@ -1,100 +0,0 @@
|
||||
from ubinascii import unhexlify
|
||||
|
||||
from common import *
|
||||
|
||||
from apps.common.paths import HARDENED
|
||||
from trezor.crypto import bip32
|
||||
from trezor.enums import CardanoAddressType
|
||||
from trezor.messages import CardanoAddressParametersType
|
||||
from trezor.messages import CardanoBlockchainPointerType
|
||||
|
||||
|
||||
if not utils.BITCOIN_ONLY:
|
||||
from apps.cardano.helpers import staking_use_cases
|
||||
from apps.cardano.seed import Keychain
|
||||
|
||||
|
||||
@unittest.skipUnless(not utils.BITCOIN_ONLY, "altcoin")
|
||||
class TestCardanoStakingUseCases(unittest.TestCase):
|
||||
def test_get(self):
|
||||
mnemonic = (
|
||||
"test walk nut penalty hip pave soap entry language right filter choice"
|
||||
)
|
||||
passphrase = ""
|
||||
node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
|
||||
keychain = Keychain(node)
|
||||
|
||||
expected_staking_use_cases = [
|
||||
# address parameters, expected staking use case
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
),
|
||||
staking_use_cases.MATCH,
|
||||
),
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 2 | HARDENED, 2, 0],
|
||||
),
|
||||
staking_use_cases.MISMATCH,
|
||||
),
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
staking_key_hash=unhexlify("32c728d3861e164cab28cb8f006448139c8f1740ffb8e7aa9e5232dc"),
|
||||
),
|
||||
staking_use_cases.MATCH,
|
||||
),
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BASE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
staking_key_hash=unhexlify("122a946b9ad3d2ddf029d3a828f0468aece76895f15c9efbd69b4277"),
|
||||
),
|
||||
staking_use_cases.MISMATCH,
|
||||
),
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.POINTER,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
certificate_pointer=CardanoBlockchainPointerType(
|
||||
block_index=1, tx_index=2, certificate_index=3
|
||||
),
|
||||
),
|
||||
staking_use_cases.POINTER_ADDRESS,
|
||||
),
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.REWARD,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 2, 0],
|
||||
),
|
||||
staking_use_cases.MATCH,
|
||||
),
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.ENTERPRISE,
|
||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
),
|
||||
staking_use_cases.NO_STAKING,
|
||||
),
|
||||
(
|
||||
CardanoAddressParametersType(
|
||||
address_type=CardanoAddressType.BYRON,
|
||||
address_n=[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||
),
|
||||
staking_use_cases.NO_STAKING,
|
||||
),
|
||||
]
|
||||
|
||||
for address_parameters, expected_staking_use_case in expected_staking_use_cases:
|
||||
actual_staking_use_case = staking_use_cases.get(keychain, address_parameters)
|
||||
self.assertEqual(actual_staking_use_case, expected_staking_use_case)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
@ -55,14 +55,6 @@ INCOMPLETE_OUTPUT_ERROR_MESSAGE = "The output is missing some fields"
|
||||
|
||||
INVALID_OUTPUT_TOKEN_BUNDLE_ENTRY = "The output's token_bundle entry is invalid"
|
||||
|
||||
ADDRESS_TYPES = (
|
||||
messages.CardanoAddressType.BYRON,
|
||||
messages.CardanoAddressType.BASE,
|
||||
messages.CardanoAddressType.POINTER,
|
||||
messages.CardanoAddressType.ENTERPRISE,
|
||||
messages.CardanoAddressType.REWARD,
|
||||
)
|
||||
|
||||
InputWithPath = Tuple[messages.CardanoTxInput, List[int]]
|
||||
AssetGroupWithTokens = Tuple[messages.CardanoAssetGroup, List[messages.CardanoToken]]
|
||||
OutputWithAssetGroups = Tuple[messages.CardanoTxOutput, List[AssetGroupWithTokens]]
|
||||
@ -102,13 +94,15 @@ def create_address_parameters(
|
||||
block_index: int = None,
|
||||
tx_index: int = None,
|
||||
certificate_index: int = None,
|
||||
script_payment_hash: bytes = None,
|
||||
script_staking_hash: bytes = None,
|
||||
) -> messages.CardanoAddressParametersType:
|
||||
certificate_pointer = None
|
||||
|
||||
if address_type not in ADDRESS_TYPES:
|
||||
raise ValueError("Unknown address type")
|
||||
|
||||
if address_type == messages.CardanoAddressType.POINTER:
|
||||
if address_type in (
|
||||
messages.CardanoAddressType.POINTER,
|
||||
messages.CardanoAddressType.POINTER_SCRIPT,
|
||||
):
|
||||
certificate_pointer = _create_certificate_pointer(
|
||||
block_index, tx_index, certificate_index
|
||||
)
|
||||
@ -119,6 +113,8 @@ def create_address_parameters(
|
||||
address_n_staking=address_n_staking,
|
||||
staking_key_hash=staking_key_hash,
|
||||
certificate_pointer=certificate_pointer,
|
||||
script_payment_hash=script_payment_hash,
|
||||
script_staking_hash=script_staking_hash,
|
||||
)
|
||||
|
||||
|
||||
@ -220,21 +216,31 @@ def _parse_tokens(tokens) -> List[messages.CardanoToken]:
|
||||
def _parse_address_parameters(
|
||||
address_parameters,
|
||||
) -> messages.CardanoAddressParametersType:
|
||||
if "path" not in address_parameters:
|
||||
if "addressType" not in address_parameters:
|
||||
raise ValueError(INCOMPLETE_OUTPUT_ERROR_MESSAGE)
|
||||
|
||||
staking_key_hash_bytes = None
|
||||
if "stakingKeyHash" in address_parameters:
|
||||
staking_key_hash_bytes = bytes.fromhex(address_parameters.get("stakingKeyHash"))
|
||||
path = tools.parse_path(address_parameters.get("path"))
|
||||
staking_path = tools.parse_path(address_parameters.get("stakingPath"))
|
||||
staking_key_hash_bytes = parse_optional_bytes(
|
||||
address_parameters.get("stakingKeyHash")
|
||||
)
|
||||
script_payment_hash = parse_optional_bytes(
|
||||
address_parameters.get("scriptPaymentHash")
|
||||
)
|
||||
script_staking_hash = parse_optional_bytes(
|
||||
address_parameters.get("scriptStakingHash")
|
||||
)
|
||||
|
||||
return create_address_parameters(
|
||||
int(address_parameters["addressType"]),
|
||||
tools.parse_path(address_parameters["path"]),
|
||||
tools.parse_path(address_parameters.get("stakingPath")),
|
||||
path,
|
||||
staking_path,
|
||||
staking_key_hash_bytes,
|
||||
address_parameters.get("blockIndex"),
|
||||
address_parameters.get("txIndex"),
|
||||
address_parameters.get("certificateIndex"),
|
||||
script_payment_hash,
|
||||
script_staking_hash,
|
||||
)
|
||||
|
||||
|
||||
@ -248,7 +254,7 @@ def parse_native_script(native_script) -> messages.CardanoNativeScript:
|
||||
for sub_script in native_script.get("scripts", ())
|
||||
]
|
||||
|
||||
key_hash = _parse_optional_bytes(native_script.get("key_hash"))
|
||||
key_hash = parse_optional_bytes(native_script.get("key_hash"))
|
||||
key_path = tools.parse_path(native_script.get("key_path"))
|
||||
required_signatures_count = parse_optional_int(
|
||||
native_script.get("required_signatures_count")
|
||||
|
@ -23,14 +23,6 @@ from . import ChoiceType, with_client
|
||||
|
||||
PATH_HELP = "BIP-32 path to key, e.g. m/44'/1815'/0'/0/0"
|
||||
|
||||
ADDRESS_TYPES = {
|
||||
"byron": messages.CardanoAddressType.BYRON,
|
||||
"base": messages.CardanoAddressType.BASE,
|
||||
"pointer": messages.CardanoAddressType.POINTER,
|
||||
"enterprise": messages.CardanoAddressType.ENTERPRISE,
|
||||
"reward": messages.CardanoAddressType.REWARD,
|
||||
}
|
||||
|
||||
|
||||
@click.group(name="cardano")
|
||||
def cli():
|
||||
@ -115,14 +107,21 @@ def sign_tx(client, file, signing_mode, protocol_magic, network_id, testnet):
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option("-n", "--address", required=True, help=PATH_HELP)
|
||||
@click.option("-n", "--address", type=str, default=None, help=PATH_HELP)
|
||||
@click.option("-d", "--show-display", is_flag=True)
|
||||
@click.option("-t", "--address-type", type=ChoiceType(ADDRESS_TYPES), default="base")
|
||||
@click.option(
|
||||
"-t",
|
||||
"--address-type",
|
||||
type=ChoiceType({m.name: m for m in messages.CardanoAddressType}),
|
||||
default="BASE",
|
||||
)
|
||||
@click.option("-s", "--staking-address", type=str, default=None)
|
||||
@click.option("-h", "--staking-key-hash", type=str, default=None)
|
||||
@click.option("-b", "--block_index", type=int, default=None)
|
||||
@click.option("-x", "--tx_index", type=int, default=None)
|
||||
@click.option("-c", "--certificate_index", type=int, default=None)
|
||||
@click.option("--script-payment-hash", type=str, default=None)
|
||||
@click.option("--script-staking-hash", type=str, default=None)
|
||||
@click.option(
|
||||
"-p", "--protocol-magic", type=int, default=cardano.PROTOCOL_MAGICS["mainnet"]
|
||||
)
|
||||
@ -138,6 +137,8 @@ def get_address(
|
||||
block_index,
|
||||
tx_index,
|
||||
certificate_index,
|
||||
script_payment_hash,
|
||||
script_staking_hash,
|
||||
protocol_magic,
|
||||
network_id,
|
||||
show_display,
|
||||
@ -161,9 +162,9 @@ def get_address(
|
||||
protocol_magic = cardano.PROTOCOL_MAGICS["testnet"]
|
||||
network_id = cardano.NETWORK_IDS["testnet"]
|
||||
|
||||
staking_key_hash_bytes = None
|
||||
if staking_key_hash:
|
||||
staking_key_hash_bytes = bytes.fromhex(staking_key_hash)
|
||||
staking_key_hash_bytes = cardano.parse_optional_bytes(staking_key_hash)
|
||||
script_payment_hash_bytes = cardano.parse_optional_bytes(script_payment_hash)
|
||||
script_staking_hash_bytes = cardano.parse_optional_bytes(script_staking_hash)
|
||||
|
||||
address_parameters = cardano.create_address_parameters(
|
||||
address_type,
|
||||
@ -173,6 +174,8 @@ def get_address(
|
||||
block_index,
|
||||
tx_index,
|
||||
certificate_index,
|
||||
script_payment_hash_bytes,
|
||||
script_staking_hash_bytes,
|
||||
)
|
||||
|
||||
return cardano.get_address(
|
||||
|
@ -1962,6 +1962,8 @@ class CardanoAddressParametersType(protobuf.MessageType):
|
||||
3: protobuf.Field("address_n_staking", "uint32", repeated=True, required=False),
|
||||
4: protobuf.Field("staking_key_hash", "bytes", repeated=False, required=False),
|
||||
5: protobuf.Field("certificate_pointer", "CardanoBlockchainPointerType", repeated=False, required=False),
|
||||
6: protobuf.Field("script_payment_hash", "bytes", repeated=False, required=False),
|
||||
7: protobuf.Field("script_staking_hash", "bytes", repeated=False, required=False),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
@ -1972,12 +1974,16 @@ class CardanoAddressParametersType(protobuf.MessageType):
|
||||
address_n_staking: Optional[List["int"]] = None,
|
||||
staking_key_hash: Optional["bytes"] = None,
|
||||
certificate_pointer: Optional["CardanoBlockchainPointerType"] = None,
|
||||
script_payment_hash: Optional["bytes"] = None,
|
||||
script_staking_hash: Optional["bytes"] = None,
|
||||
) -> None:
|
||||
self.address_n = address_n if address_n is not None else []
|
||||
self.address_n_staking = address_n_staking if address_n_staking is not None else []
|
||||
self.address_type = address_type
|
||||
self.staking_key_hash = staking_key_hash
|
||||
self.certificate_pointer = certificate_pointer
|
||||
self.script_payment_hash = script_payment_hash
|
||||
self.script_staking_hash = script_staking_hash
|
||||
|
||||
|
||||
class CardanoGetAddress(protobuf.MessageType):
|
||||
|
@ -16,7 +16,12 @@
|
||||
|
||||
import pytest
|
||||
|
||||
from trezorlib.cardano import create_address_parameters, get_address, get_public_key
|
||||
from trezorlib.cardano import (
|
||||
create_address_parameters,
|
||||
get_address,
|
||||
get_public_key,
|
||||
parse_optional_bytes,
|
||||
)
|
||||
from trezorlib.messages import CardanoAddressType
|
||||
from trezorlib.tools import parse_path
|
||||
|
||||
@ -34,6 +39,7 @@ pytestmark = [
|
||||
"cardano/get_address_byron.slip39.json",
|
||||
"cardano/get_base_address.json",
|
||||
"cardano/get_base_address_with_staking_key_hash.json",
|
||||
"cardano/get_base_address_with_script_hashes.json",
|
||||
"cardano/get_enterprise_address.json",
|
||||
"cardano/get_pointer_address.json",
|
||||
"cardano/get_reward_address.json",
|
||||
@ -45,16 +51,22 @@ def test_cardano_get_address(client, parameters, result):
|
||||
address_type=getattr(
|
||||
CardanoAddressType, parameters["address_type"].upper()
|
||||
),
|
||||
address_n=parse_path(parameters["path"]),
|
||||
address_n=parse_path(parameters.get("path"))
|
||||
if "path" in parameters
|
||||
else None,
|
||||
address_n_staking=parse_path(parameters.get("staking_path"))
|
||||
if "staking_path" in parameters
|
||||
else None,
|
||||
staking_key_hash=bytes.fromhex(parameters.get("staking_key_hash"))
|
||||
if "staking_key_hash" in parameters
|
||||
else None,
|
||||
staking_key_hash=parse_optional_bytes(parameters.get("staking_key_hash")),
|
||||
block_index=parameters.get("block_index"),
|
||||
tx_index=parameters.get("tx_index"),
|
||||
certificate_index=parameters.get("certificate_index"),
|
||||
script_payment_hash=parse_optional_bytes(
|
||||
parameters.get("script_payment_hash")
|
||||
),
|
||||
script_staking_hash=parse_optional_bytes(
|
||||
parameters.get("script_staking_hash")
|
||||
),
|
||||
),
|
||||
protocol_magic=parameters["protocol_magic"],
|
||||
network_id=parameters["network_id"],
|
||||
|
Loading…
Reference in New Issue
Block a user