mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-18 05:28:40 +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
|
// can be sent directly e.g. if it doesn't belong to
|
||||||
// the same account as address_n
|
// the same account as address_n
|
||||||
optional CardanoBlockchainPointerType certificate_pointer = 5; // a pointer to the staking key registration certificate
|
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": {
|
"result": {
|
||||||
"expected_address": "addr_test1vzq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z92s8k2y47"
|
"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": {
|
"result": {
|
||||||
"expected_address": "addr_test1gzq0nckg3ekgzuqg7w5p9mvgnd9ym28qh5grlph8xd2z925ph3wczvf2ag2x9t"
|
"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": [
|
"tests": [
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"path": "m/1852'/1815'/0'/2/0",
|
|
||||||
"address_type": "reward",
|
"address_type": "reward",
|
||||||
|
"staking_path": "m/1852'/1815'/0'/2/0",
|
||||||
"network_id": 1,
|
"network_id": 1,
|
||||||
"protocol_magic": 764824073
|
"protocol_magic": 764824073
|
||||||
},
|
},
|
||||||
@ -17,14 +17,36 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"path": "m/1852'/1815'/0'/2/0",
|
|
||||||
"address_type": "reward",
|
"address_type": "reward",
|
||||||
|
"staking_path": "m/1852'/1815'/0'/2/0",
|
||||||
"network_id": 0,
|
"network_id": 0,
|
||||||
"protocol_magic": 42
|
"protocol_magic": 42
|
||||||
},
|
},
|
||||||
"result": {
|
"result": {
|
||||||
"expected_address": "stake_test1uqfz49rtntfa9h0s98f6s28sg69weemgjhc4e8hm66d5yac643znq"
|
"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
|
import apps.cardano.helpers.account_path_check
|
||||||
apps.cardano.helpers.bech32
|
apps.cardano.helpers.bech32
|
||||||
import apps.cardano.helpers.bech32
|
import apps.cardano.helpers.bech32
|
||||||
|
apps.cardano.helpers.credential
|
||||||
|
import apps.cardano.helpers.credential
|
||||||
apps.cardano.helpers.hash_builder_collection
|
apps.cardano.helpers.hash_builder_collection
|
||||||
import apps.cardano.helpers.hash_builder_collection
|
import apps.cardano.helpers.hash_builder_collection
|
||||||
apps.cardano.helpers.network_ids
|
apps.cardano.helpers.network_ids
|
||||||
@ -470,8 +472,6 @@ if not utils.BITCOIN_ONLY:
|
|||||||
import apps.cardano.helpers.paths
|
import apps.cardano.helpers.paths
|
||||||
apps.cardano.helpers.protocol_magics
|
apps.cardano.helpers.protocol_magics
|
||||||
import 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
|
apps.cardano.helpers.utils
|
||||||
import apps.cardano.helpers.utils
|
import apps.cardano.helpers.utils
|
||||||
apps.cardano.layout
|
apps.cardano.layout
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from trezor.crypto import base58, hashlib
|
from trezor.crypto import base58
|
||||||
from trezor.enums import CardanoAddressType
|
from trezor.enums import CardanoAddressType
|
||||||
|
|
||||||
from .byron_address import derive_byron_address, validate_byron_address
|
from .byron_address import derive_byron_address, validate_byron_address
|
||||||
@ -7,17 +7,20 @@ from .helpers import (
|
|||||||
INVALID_ADDRESS,
|
INVALID_ADDRESS,
|
||||||
INVALID_ADDRESS_PARAMETERS,
|
INVALID_ADDRESS_PARAMETERS,
|
||||||
NETWORK_MISMATCH,
|
NETWORK_MISMATCH,
|
||||||
|
SCRIPT_HASH_SIZE,
|
||||||
bech32,
|
bech32,
|
||||||
network_ids,
|
network_ids,
|
||||||
)
|
)
|
||||||
from .helpers.paths import SCHEMA_STAKING_ANY_ACCOUNT
|
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
|
from .seed import is_byron_path, is_shelley_path
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from trezor.messages import (
|
from trezor.messages import (
|
||||||
CardanoBlockchainPointerType,
|
|
||||||
CardanoAddressParametersType,
|
CardanoAddressParametersType,
|
||||||
|
CardanoBlockchainPointerType,
|
||||||
)
|
)
|
||||||
from . import seed
|
from . import seed
|
||||||
|
|
||||||
@ -33,6 +36,7 @@ ADDRESS_TYPES_SHELLEY = (
|
|||||||
CardanoAddressType.REWARD,
|
CardanoAddressType.REWARD,
|
||||||
CardanoAddressType.REWARD_SCRIPT,
|
CardanoAddressType.REWARD_SCRIPT,
|
||||||
)
|
)
|
||||||
|
|
||||||
MIN_ADDRESS_BYTES_LENGTH = 29
|
MIN_ADDRESS_BYTES_LENGTH = 29
|
||||||
MAX_ADDRESS_BYTES_LENGTH = 65
|
MAX_ADDRESS_BYTES_LENGTH = 65
|
||||||
|
|
||||||
@ -43,20 +47,56 @@ def validate_address_parameters(parameters: CardanoAddressParametersType) -> Non
|
|||||||
if parameters.address_type == CardanoAddressType.BYRON:
|
if parameters.address_type == CardanoAddressType.BYRON:
|
||||||
if not is_byron_path(parameters.address_n):
|
if not is_byron_path(parameters.address_n):
|
||||||
raise INVALID_ADDRESS_PARAMETERS
|
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):
|
if not is_shelley_path(parameters.address_n):
|
||||||
raise INVALID_ADDRESS_PARAMETERS
|
raise INVALID_ADDRESS_PARAMETERS
|
||||||
|
|
||||||
if parameters.address_type == CardanoAddressType.BASE:
|
|
||||||
_validate_base_address_staking_info(
|
_validate_base_address_staking_info(
|
||||||
parameters.address_n_staking, parameters.staking_key_hash
|
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:
|
elif parameters.address_type == CardanoAddressType.POINTER:
|
||||||
|
if not is_shelley_path(parameters.address_n):
|
||||||
|
raise INVALID_ADDRESS_PARAMETERS
|
||||||
if parameters.certificate_pointer is None:
|
if parameters.certificate_pointer is None:
|
||||||
raise INVALID_ADDRESS_PARAMETERS
|
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
|
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:
|
else:
|
||||||
raise INVALID_ADDRESS_PARAMETERS
|
raise INVALID_ADDRESS_PARAMETERS
|
||||||
|
|
||||||
@ -64,23 +104,86 @@ def validate_address_parameters(parameters: CardanoAddressParametersType) -> Non
|
|||||||
def _validate_address_parameters_structure(
|
def _validate_address_parameters_structure(
|
||||||
parameters: CardanoAddressParametersType,
|
parameters: CardanoAddressParametersType,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
address_n = parameters.address_n
|
||||||
address_n_staking = parameters.address_n_staking
|
address_n_staking = parameters.address_n_staking
|
||||||
staking_key_hash = parameters.staking_key_hash
|
staking_key_hash = parameters.staking_key_hash
|
||||||
certificate_pointer = parameters.certificate_pointer
|
certificate_pointer = parameters.certificate_pointer
|
||||||
|
script_payment_hash = parameters.script_payment_hash
|
||||||
|
script_staking_hash = parameters.script_staking_hash
|
||||||
|
|
||||||
fields_to_be_empty: tuple = ()
|
fields_to_be_empty: dict[CardanoAddressType, tuple[Any, ...]] = {
|
||||||
if parameters.address_type in (
|
CardanoAddressType.BASE: (
|
||||||
CardanoAddressType.BYRON,
|
certificate_pointer,
|
||||||
CardanoAddressType.REWARD,
|
script_payment_hash,
|
||||||
CardanoAddressType.ENTERPRISE,
|
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
|
raise INVALID_ADDRESS_PARAMETERS
|
||||||
|
|
||||||
|
|
||||||
@ -101,6 +204,11 @@ def _validate_base_address_staking_info(
|
|||||||
raise INVALID_ADDRESS_PARAMETERS
|
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(
|
def _validate_address_and_get_type(
|
||||||
address: str, protocol_magic: int, network_id: int
|
address: str, protocol_magic: int, network_id: int
|
||||||
) -> int:
|
) -> int:
|
||||||
@ -189,7 +297,7 @@ def _get_bech32_hrp_for_address(
|
|||||||
# Byron address uses base58 encoding
|
# Byron address uses base58 encoding
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
if address_type == CardanoAddressType.REWARD:
|
if address_type in (CardanoAddressType.REWARD, CardanoAddressType.REWARD_SCRIPT):
|
||||||
if network_ids.is_mainnet(network_id):
|
if network_ids.is_mainnet(network_id):
|
||||||
return bech32.HRP_REWARD_ADDRESS
|
return bech32.HRP_REWARD_ADDRESS
|
||||||
else:
|
else:
|
||||||
@ -210,11 +318,6 @@ def _get_address_network_id(address: bytes) -> int:
|
|||||||
return address[0] & 0x0F
|
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(
|
def derive_human_readable_address(
|
||||||
keychain: seed.Keychain,
|
keychain: seed.Keychain,
|
||||||
parameters: CardanoAddressParametersType,
|
parameters: CardanoAddressParametersType,
|
||||||
@ -258,36 +361,14 @@ def derive_address_bytes(
|
|||||||
|
|
||||||
|
|
||||||
def _derive_shelley_address(
|
def _derive_shelley_address(
|
||||||
keychain: seed.Keychain,
|
keychain: seed.Keychain, parameters: CardanoAddressParametersType, network_id: int
|
||||||
parameters: CardanoAddressParametersType,
|
|
||||||
network_id: int,
|
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
if parameters.address_type == CardanoAddressType.BASE:
|
header = _create_address_header(parameters.address_type, network_id)
|
||||||
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
|
|
||||||
|
|
||||||
address = _derive_pointer_address(
|
payment_part = _get_address_payment_part(keychain, parameters)
|
||||||
keychain,
|
staking_part = _get_address_staking_part(keychain, parameters)
|
||||||
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
|
|
||||||
|
|
||||||
return address
|
return header + payment_part + staking_part
|
||||||
|
|
||||||
|
|
||||||
def _create_address_header(address_type: CardanoAddressType, network_id: int) -> bytes:
|
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")
|
return header.to_bytes(1, "little")
|
||||||
|
|
||||||
|
|
||||||
def _derive_base_address(
|
def _get_address_payment_part(
|
||||||
keychain: seed.Keychain,
|
keychain: seed.Keychain, parameters: CardanoAddressParametersType
|
||||||
path: list[int],
|
|
||||||
staking_path: list[int],
|
|
||||||
staking_key_hash: bytes | None,
|
|
||||||
network_id: int,
|
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
header = _create_address_header(CardanoAddressType.BASE, network_id)
|
if parameters.address_n:
|
||||||
spending_key_hash = get_public_key_hash(keychain, path)
|
return get_public_key_hash(keychain, parameters.address_n)
|
||||||
|
elif parameters.script_payment_hash:
|
||||||
if staking_key_hash is None:
|
return parameters.script_payment_hash
|
||||||
staking_key_hash = get_public_key_hash(keychain, staking_path)
|
else:
|
||||||
|
return bytes()
|
||||||
return header + spending_key_hash + staking_key_hash
|
|
||||||
|
|
||||||
|
|
||||||
def _derive_pointer_address(
|
def _get_address_staking_part(
|
||||||
keychain: seed.Keychain,
|
keychain: seed.Keychain, parameters: CardanoAddressParametersType
|
||||||
path: list[int],
|
|
||||||
pointer: CardanoBlockchainPointerType,
|
|
||||||
network_id: int,
|
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
header = _create_address_header(CardanoAddressType.POINTER, network_id)
|
if parameters.staking_key_hash:
|
||||||
spending_key_hash = get_public_key_hash(keychain, path)
|
return parameters.staking_key_hash
|
||||||
encoded_pointer = _encode_certificate_pointer(pointer)
|
elif parameters.address_n_staking:
|
||||||
|
return get_public_key_hash(keychain, parameters.address_n_staking)
|
||||||
return header + spending_key_hash + encoded_pointer
|
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:
|
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)
|
certificate_index_encoded = variable_length_encode(pointer.certificate_index)
|
||||||
|
|
||||||
return bytes(block_index_encoded + tx_index_encoded + certificate_index_encoded)
|
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 import log, wire
|
||||||
from trezor.messages import CardanoAddress
|
from trezor.messages import CardanoAddress
|
||||||
from trezor.ui.layouts import show_address
|
|
||||||
|
|
||||||
from apps.common import paths
|
|
||||||
|
|
||||||
from . import seed
|
from . import seed
|
||||||
from .address import derive_human_readable_address, validate_address_parameters
|
from .address import derive_human_readable_address, validate_address_parameters
|
||||||
from .helpers import protocol_magics, staking_use_cases
|
from .helpers.credential import Credential, should_show_address_credentials
|
||||||
from .helpers.paths import SCHEMA_PAYMENT, SCHEMA_STAKING
|
from .layout import show_cardano_address, show_credentials
|
||||||
from .helpers.utils import to_account_path
|
|
||||||
from .layout import (
|
|
||||||
ADDRESS_TYPE_NAMES,
|
|
||||||
show_warning_address_foreign_staking_key,
|
|
||||||
show_warning_address_pointer,
|
|
||||||
)
|
|
||||||
from .sign_tx import validate_network_info
|
from .sign_tx import validate_network_info
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
@ -29,15 +20,6 @@ async def get_address(
|
|||||||
) -> CardanoAddress:
|
) -> CardanoAddress:
|
||||||
address_parameters = msg.address_parameters
|
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_network_info(msg.network_id, msg.protocol_magic)
|
||||||
validate_address_parameters(address_parameters)
|
validate_address_parameters(address_parameters)
|
||||||
|
|
||||||
@ -51,51 +33,22 @@ async def get_address(
|
|||||||
raise wire.ProcessError("Deriving address failed")
|
raise wire.ProcessError("Deriving address failed")
|
||||||
|
|
||||||
if msg.show_display:
|
if msg.show_display:
|
||||||
await _display_address(
|
await _display_address(ctx, address_parameters, address, msg.protocol_magic)
|
||||||
ctx, keychain, address_parameters, address, msg.protocol_magic
|
|
||||||
)
|
|
||||||
|
|
||||||
return CardanoAddress(address=address)
|
return CardanoAddress(address=address)
|
||||||
|
|
||||||
|
|
||||||
async def _display_address(
|
async def _display_address(
|
||||||
ctx: wire.Context,
|
ctx: wire.Context,
|
||||||
keychain: seed.Keychain,
|
|
||||||
address_parameters: CardanoAddressParametersType,
|
address_parameters: CardanoAddressParametersType,
|
||||||
address: str,
|
address: str,
|
||||||
protocol_magic: int,
|
protocol_magic: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
await _show_staking_warnings(ctx, keychain, address_parameters)
|
if should_show_address_credentials(address_parameters):
|
||||||
|
await show_credentials(
|
||||||
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(
|
|
||||||
ctx,
|
ctx,
|
||||||
address=address,
|
Credential.payment_credential(address_parameters),
|
||||||
title="%s address" % ADDRESS_TYPE_NAMES[address_parameters.address_type],
|
Credential.stake_credential(address_parameters),
|
||||||
network=network_name,
|
|
||||||
address_extra=address_n,
|
|
||||||
title_qr=address_n,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await show_cardano_address(ctx, address_parameters, address, protocol_magic)
|
||||||
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)
|
|
||||||
|
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)
|
SCHEMA_STAKING_ANY_ACCOUNT = PathSchema.parse("m/[1852]'/coin_type'/[0-%s]'/2/0" % (HARDENED - 1), SLIP44_ID)
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
ACCOUNT_PATH_LENGTH = const(3)
|
|
||||||
ACCOUNT_PATH_INDEX = const(2)
|
ACCOUNT_PATH_INDEX = const(2)
|
||||||
|
ACCOUNT_PATH_LENGTH = const(3)
|
||||||
|
CHAIN_STAKING_KEY = const(2)
|
||||||
|
|
||||||
CHANGE_OUTPUT_PATH_NAME = "Change output path"
|
CHANGE_OUTPUT_PATH_NAME = "Change output path"
|
||||||
CHANGE_OUTPUT_STAKING_PATH_NAME = "Change output staking 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,
|
CardanoNativeScriptHashDisplayFormat,
|
||||||
CardanoNativeScriptType,
|
CardanoNativeScriptType,
|
||||||
)
|
)
|
||||||
|
from trezor.messages import CardanoAddressParametersType
|
||||||
from trezor.strings import format_amount
|
from trezor.strings import format_amount
|
||||||
from trezor.ui.layouts import (
|
from trezor.ui.layouts import (
|
||||||
confirm_blob,
|
confirm_blob,
|
||||||
@ -13,16 +14,13 @@ from trezor.ui.layouts import (
|
|||||||
confirm_output,
|
confirm_output,
|
||||||
confirm_path_warning,
|
confirm_path_warning,
|
||||||
confirm_properties,
|
confirm_properties,
|
||||||
|
show_address,
|
||||||
)
|
)
|
||||||
|
|
||||||
from apps.common.paths import address_n_to_str
|
from apps.common.paths import address_n_to_str
|
||||||
|
|
||||||
from . import seed
|
from . import seed
|
||||||
from .address import (
|
from .address import derive_human_readable_address
|
||||||
encode_human_readable_address,
|
|
||||||
get_public_key_hash,
|
|
||||||
pack_reward_address_bytes,
|
|
||||||
)
|
|
||||||
from .helpers import protocol_magics
|
from .helpers import protocol_magics
|
||||||
from .helpers.utils import (
|
from .helpers.utils import (
|
||||||
format_account_number,
|
format_account_number,
|
||||||
@ -37,7 +35,6 @@ from .helpers.utils import (
|
|||||||
if False:
|
if False:
|
||||||
from trezor import wire
|
from trezor import wire
|
||||||
from trezor.messages import (
|
from trezor.messages import (
|
||||||
CardanoBlockchainPointerType,
|
|
||||||
CardanoNativeScript,
|
CardanoNativeScript,
|
||||||
CardanoTxCertificate,
|
CardanoTxCertificate,
|
||||||
CardanoTxWithdrawal,
|
CardanoTxWithdrawal,
|
||||||
@ -48,14 +45,21 @@ if False:
|
|||||||
)
|
)
|
||||||
|
|
||||||
from trezor.ui.layouts import PropertyType
|
from trezor.ui.layouts import PropertyType
|
||||||
|
from .helpers.credential import Credential
|
||||||
|
|
||||||
|
|
||||||
ADDRESS_TYPE_NAMES = {
|
ADDRESS_TYPE_NAMES = {
|
||||||
CardanoAddressType.BYRON: "Legacy",
|
CardanoAddressType.BYRON: "Legacy",
|
||||||
CardanoAddressType.BASE: "Base",
|
CardanoAddressType.BASE: "Base",
|
||||||
|
CardanoAddressType.BASE_SCRIPT_KEY: "Base",
|
||||||
|
CardanoAddressType.BASE_KEY_SCRIPT: "Base",
|
||||||
|
CardanoAddressType.BASE_SCRIPT_SCRIPT: "Base",
|
||||||
CardanoAddressType.POINTER: "Pointer",
|
CardanoAddressType.POINTER: "Pointer",
|
||||||
|
CardanoAddressType.POINTER_SCRIPT: "Pointer",
|
||||||
CardanoAddressType.ENTERPRISE: "Enterprise",
|
CardanoAddressType.ENTERPRISE: "Enterprise",
|
||||||
|
CardanoAddressType.ENTERPRISE_SCRIPT: "Enterprise",
|
||||||
CardanoAddressType.REWARD: "Reward",
|
CardanoAddressType.REWARD: "Reward",
|
||||||
|
CardanoAddressType.REWARD_SCRIPT: "Reward",
|
||||||
}
|
}
|
||||||
|
|
||||||
SCRIPT_TYPE_NAMES = {
|
SCRIPT_TYPE_NAMES = {
|
||||||
@ -184,13 +188,15 @@ async def confirm_sending(
|
|||||||
ctx: wire.Context,
|
ctx: wire.Context,
|
||||||
ada_amount: int,
|
ada_amount: int,
|
||||||
to: str,
|
to: str,
|
||||||
|
is_change_output: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
subtitle = "Change amount:" if is_change_output else "Confirm sending:"
|
||||||
await confirm_output(
|
await confirm_output(
|
||||||
ctx,
|
ctx,
|
||||||
to,
|
to,
|
||||||
format_coin_amount(ada_amount),
|
format_coin_amount(ada_amount),
|
||||||
title="Confirm transaction",
|
title="Confirm transaction",
|
||||||
subtitle="Confirm sending:",
|
subtitle=subtitle,
|
||||||
font_amount=ui.BOLD,
|
font_amount=ui.BOLD,
|
||||||
width_paginated=17,
|
width_paginated=17,
|
||||||
to_str="\nto\n",
|
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:
|
async def show_credentials(
|
||||||
await confirm_metadata(
|
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,
|
ctx,
|
||||||
"confirm_tokens",
|
"confirm_credential",
|
||||||
title="Confirm transaction",
|
title=title,
|
||||||
content="The following\ntransaction output\ncontains tokens.",
|
props=props,
|
||||||
larger_vspace=True,
|
icon=icon,
|
||||||
|
icon_color=icon_color,
|
||||||
br_code=ButtonRequestType.Other,
|
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)
|
await confirm_path_warning(ctx, address_n_to_str(path), path_type=title)
|
||||||
|
|
||||||
|
|
||||||
async def show_warning_tx_no_staking_info(
|
async def show_warning_tx_output_contains_tokens(ctx: wire.Context) -> None:
|
||||||
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
|
|
||||||
await confirm_metadata(
|
await confirm_metadata(
|
||||||
ctx,
|
ctx,
|
||||||
"warning_staking",
|
"confirm_tokens",
|
||||||
title="Confirm transaction",
|
title="Confirm transaction",
|
||||||
content=content,
|
content="The following\ntransaction output\ncontains tokens.",
|
||||||
param=format_coin_amount(amount),
|
larger_vspace=True,
|
||||||
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,
|
|
||||||
br_code=ButtonRequestType.Other,
|
br_code=ButtonRequestType.Other,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -414,6 +408,7 @@ async def confirm_stake_pool_owner(
|
|||||||
ctx: wire.Context,
|
ctx: wire.Context,
|
||||||
keychain: seed.Keychain,
|
keychain: seed.Keychain,
|
||||||
owner: CardanoPoolOwner,
|
owner: CardanoPoolOwner,
|
||||||
|
protocol_magic: int,
|
||||||
network_id: int,
|
network_id: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
props: list[tuple[str, str | 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(("Pool owner:", address_n_to_str(owner.staking_key_path)))
|
||||||
props.append(
|
props.append(
|
||||||
(
|
(
|
||||||
encode_human_readable_address(
|
derive_human_readable_address(
|
||||||
pack_reward_address_bytes(
|
keychain,
|
||||||
get_public_key_hash(keychain, owner.staking_key_path),
|
CardanoAddressParametersType(
|
||||||
|
address_type=CardanoAddressType.REWARD,
|
||||||
|
address_n=owner.staking_key_path,
|
||||||
|
),
|
||||||
|
protocol_magic,
|
||||||
network_id,
|
network_id,
|
||||||
)
|
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@ -435,8 +433,14 @@ async def confirm_stake_pool_owner(
|
|||||||
props.append(
|
props.append(
|
||||||
(
|
(
|
||||||
"Pool owner:",
|
"Pool owner:",
|
||||||
encode_human_readable_address(
|
derive_human_readable_address(
|
||||||
pack_reward_address_bytes(owner.staking_key_hash, network_id)
|
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(
|
async def show_cardano_address(
|
||||||
ctx: wire.Context, pointer: CardanoBlockchainPointerType
|
ctx: wire.Context,
|
||||||
|
address_parameters: CardanoAddressParametersType,
|
||||||
|
address: str,
|
||||||
|
protocol_magic: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
content = "Pointer address:\nBlock: %s\nTransaction: %s\nCertificate: %s" % (
|
network_name = None
|
||||||
pointer.block_index,
|
if not protocol_magics.is_mainnet(protocol_magic):
|
||||||
pointer.tx_index,
|
network_name = protocol_magics.to_ui_string(protocol_magic)
|
||||||
pointer.certificate_index,
|
|
||||||
)
|
title = "%s address" % ADDRESS_TYPE_NAMES[address_parameters.address_type]
|
||||||
await confirm_metadata(
|
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,
|
ctx,
|
||||||
"warning_pointer",
|
address=address,
|
||||||
title="Warning",
|
title=title,
|
||||||
icon=ui.ICON_WRONG,
|
network=network_name,
|
||||||
icon_color=ui.RED,
|
address_extra=address_extra,
|
||||||
content=content,
|
title_qr=title_qr,
|
||||||
br_code=ButtonRequestType.Other,
|
|
||||||
)
|
)
|
||||||
|
@ -64,9 +64,9 @@ from .helpers import (
|
|||||||
LOVELACE_MAX_SUPPLY,
|
LOVELACE_MAX_SUPPLY,
|
||||||
network_ids,
|
network_ids,
|
||||||
protocol_magics,
|
protocol_magics,
|
||||||
staking_use_cases,
|
|
||||||
)
|
)
|
||||||
from .helpers.account_path_check import AccountPathChecker
|
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.hash_builder_collection import HashBuilderDict, HashBuilderList
|
||||||
from .helpers.paths import (
|
from .helpers.paths import (
|
||||||
CERTIFICATE_PATH_NAME,
|
CERTIFICATE_PATH_NAME,
|
||||||
@ -89,13 +89,10 @@ from .layout import (
|
|||||||
confirm_stake_pool_registration_final,
|
confirm_stake_pool_registration_final,
|
||||||
confirm_transaction,
|
confirm_transaction,
|
||||||
confirm_withdrawal,
|
confirm_withdrawal,
|
||||||
|
show_credentials,
|
||||||
show_warning_path,
|
show_warning_path,
|
||||||
show_warning_tx_different_staking_account,
|
|
||||||
show_warning_tx_network_unverifiable,
|
show_warning_tx_network_unverifiable,
|
||||||
show_warning_tx_no_staking_info,
|
|
||||||
show_warning_tx_output_contains_tokens,
|
show_warning_tx_output_contains_tokens,
|
||||||
show_warning_tx_pointer_address,
|
|
||||||
show_warning_tx_staking_key_hash,
|
|
||||||
)
|
)
|
||||||
from .seed import is_byron_path
|
from .seed import is_byron_path
|
||||||
|
|
||||||
@ -302,8 +299,16 @@ async def _process_outputs(
|
|||||||
total_amount = 0
|
total_amount = 0
|
||||||
for _ in range(outputs_count):
|
for _ in range(outputs_count):
|
||||||
output: CardanoTxOutput = await ctx.call(CardanoTxItemAck(), CardanoTxOutput)
|
output: CardanoTxOutput = await ctx.call(CardanoTxItemAck(), CardanoTxOutput)
|
||||||
_validate_output(output, protocol_magic, network_id, account_path_checker)
|
_validate_output(
|
||||||
if signing_mode == CardanoTxSigningMode.ORDINARY_TRANSACTION:
|
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(
|
await _show_output(
|
||||||
ctx,
|
ctx,
|
||||||
keychain,
|
keychain,
|
||||||
@ -335,7 +340,7 @@ async def _process_outputs(
|
|||||||
ctx,
|
ctx,
|
||||||
asset_groups_dict,
|
asset_groups_dict,
|
||||||
output.asset_groups_count,
|
output.asset_groups_count,
|
||||||
_should_show_tokens(output, signing_mode),
|
should_show_output,
|
||||||
)
|
)
|
||||||
|
|
||||||
total_amount += output.amount
|
total_amount += output.amount
|
||||||
@ -634,6 +639,7 @@ def _validate_stake_pool_registration_tx_structure(msg: CardanoSignTxInit) -> No
|
|||||||
|
|
||||||
def _validate_output(
|
def _validate_output(
|
||||||
output: CardanoTxOutput,
|
output: CardanoTxOutput,
|
||||||
|
signing_mode: CardanoTxSigningMode,
|
||||||
protocol_magic: int,
|
protocol_magic: int,
|
||||||
network_id: int,
|
network_id: int,
|
||||||
account_path_checker: AccountPathChecker,
|
account_path_checker: AccountPathChecker,
|
||||||
@ -641,8 +647,9 @@ def _validate_output(
|
|||||||
if output.address_parameters and output.address is not None:
|
if output.address_parameters and output.address is not None:
|
||||||
raise INVALID_OUTPUT
|
raise INVALID_OUTPUT
|
||||||
|
|
||||||
if output.address_parameters:
|
if address_parameters := output.address_parameters:
|
||||||
validate_address_parameters(output.address_parameters)
|
validate_address_parameters(address_parameters)
|
||||||
|
_fail_if_strict_and_unusual(address_parameters)
|
||||||
elif output.address is not None:
|
elif output.address is not None:
|
||||||
validate_output_address(output.address, protocol_magic, network_id)
|
validate_output_address(output.address, protocol_magic, network_id)
|
||||||
else:
|
else:
|
||||||
@ -651,6 +658,23 @@ def _validate_output(
|
|||||||
account_path_checker.add_output(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(
|
async def _show_output(
|
||||||
ctx: wire.Context,
|
ctx: wire.Context,
|
||||||
keychain: seed.Keychain,
|
keychain: seed.Keychain,
|
||||||
@ -659,33 +683,30 @@ async def _show_output(
|
|||||||
protocol_magic: int,
|
protocol_magic: int,
|
||||||
network_id: int,
|
network_id: int,
|
||||||
) -> None:
|
) -> 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:
|
if output.asset_groups_count > 0:
|
||||||
await show_warning_tx_output_contains_tokens(ctx)
|
await show_warning_tx_output_contains_tokens(ctx)
|
||||||
|
|
||||||
if signing_mode == CardanoTxSigningMode.ORDINARY_TRANSACTION:
|
is_change_output: bool
|
||||||
await confirm_sending(ctx, output.amount, address)
|
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(
|
def _validate_asset_group(
|
||||||
@ -777,7 +798,11 @@ async def _show_stake_pool_registration_certificate(
|
|||||||
|
|
||||||
|
|
||||||
async def _show_pool_owner(
|
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:
|
) -> None:
|
||||||
if owner.staking_key_path:
|
if owner.staking_key_path:
|
||||||
await _fail_or_warn_if_invalid_path(
|
await _fail_or_warn_if_invalid_path(
|
||||||
@ -787,7 +812,7 @@ async def _show_pool_owner(
|
|||||||
POOL_OWNER_STAKING_PATH_NAME,
|
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(
|
def _validate_witness_request(
|
||||||
@ -824,78 +849,11 @@ async def _show_witness(
|
|||||||
ctx: wire.Context,
|
ctx: wire.Context,
|
||||||
witness_path: list[int],
|
witness_path: list[int],
|
||||||
) -> None:
|
) -> None:
|
||||||
if not SCHEMA_PAYMENT.match(witness_path) and not SCHEMA_STAKING.match(
|
is_payment = SCHEMA_PAYMENT.match(witness_path)
|
||||||
witness_path
|
is_staking = SCHEMA_STAKING.match(witness_path)
|
||||||
):
|
|
||||||
await _fail_or_warn_path(
|
|
||||||
ctx,
|
|
||||||
witness_path,
|
|
||||||
WITNESS_PATH_NAME,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
if not is_payment and not is_staking:
|
||||||
async def _show_change_output_staking_warnings(
|
await _fail_or_warn_path(ctx, witness_path, WITNESS_PATH_NAME)
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def _is_network_id_verifiable(msg: CardanoSignTxInit) -> bool:
|
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())
|
raise wire.DataError("Invalid %s" % path_name.lower())
|
||||||
else:
|
else:
|
||||||
await show_warning_path(ctx, path, path_name)
|
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]"
|
address_n_staking: "list[int]"
|
||||||
staking_key_hash: "bytes | None"
|
staking_key_hash: "bytes | None"
|
||||||
certificate_pointer: "CardanoBlockchainPointerType | None"
|
certificate_pointer: "CardanoBlockchainPointerType | None"
|
||||||
|
script_payment_hash: "bytes | None"
|
||||||
|
script_staking_hash: "bytes | None"
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -1132,6 +1134,8 @@ if TYPE_CHECKING:
|
|||||||
address_n_staking: "list[int] | None" = None,
|
address_n_staking: "list[int] | None" = None,
|
||||||
staking_key_hash: "bytes | None" = None,
|
staking_key_hash: "bytes | None" = None,
|
||||||
certificate_pointer: "CardanoBlockchainPointerType | None" = None,
|
certificate_pointer: "CardanoBlockchainPointerType | None" = None,
|
||||||
|
script_payment_hash: "bytes | None" = None,
|
||||||
|
script_staking_hash: "bytes | None" = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -285,121 +285,6 @@ class TestCardanoAddress(unittest.TestCase):
|
|||||||
self.assertEqual(hexlify(seed.remove_ed25519_prefix(n.public_key())), pub)
|
self.assertEqual(hexlify(seed.remove_ed25519_prefix(n.public_key())), pub)
|
||||||
self.assertEqual(hexlify(n.chain_code()), chain)
|
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):
|
def test_testnet_byron_address(self):
|
||||||
mnemonic = "all all all all all all all all all all all all"
|
mnemonic = "all all all all all all all all all all all all"
|
||||||
passphrase = ""
|
passphrase = ""
|
||||||
@ -421,6 +306,133 @@ class TestCardanoAddress(unittest.TestCase):
|
|||||||
address = derive_human_readable_address(keychain, address_parameters, protocol_magics.TESTNET, 0)
|
address = derive_human_readable_address(keychain, address_parameters, protocol_magics.TESTNET, 0)
|
||||||
self.assertEqual(expected, address)
|
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):
|
def test_validate_address_parameters(self):
|
||||||
test_vectors = [
|
test_vectors = [
|
||||||
# base address - both address_n_staking and staking_key_hash are None
|
# 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],
|
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
staking_key_hash=None,
|
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
|
# pointer address - pointer is None
|
||||||
CardanoAddressParametersType(
|
CardanoAddressParametersType(
|
||||||
address_type=CardanoAddressType.POINTER,
|
address_type=CardanoAddressType.POINTER,
|
||||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
|
||||||
certificate_pointer=None,
|
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
|
# reward address - non staking path
|
||||||
CardanoAddressParametersType(
|
CardanoAddressParametersType(
|
||||||
address_type=CardanoAddressType.REWARD,
|
address_type=CardanoAddressType.REWARD,
|
||||||
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0]
|
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
|
# Shelley addresses with Byron namespace
|
||||||
CardanoAddressParametersType(
|
CardanoAddressParametersType(
|
||||||
address_type=CardanoAddressType.BASE,
|
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(
|
CardanoAddressParametersType(
|
||||||
address_type=CardanoAddressType.POINTER,
|
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"
|
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]]
|
InputWithPath = Tuple[messages.CardanoTxInput, List[int]]
|
||||||
AssetGroupWithTokens = Tuple[messages.CardanoAssetGroup, List[messages.CardanoToken]]
|
AssetGroupWithTokens = Tuple[messages.CardanoAssetGroup, List[messages.CardanoToken]]
|
||||||
OutputWithAssetGroups = Tuple[messages.CardanoTxOutput, List[AssetGroupWithTokens]]
|
OutputWithAssetGroups = Tuple[messages.CardanoTxOutput, List[AssetGroupWithTokens]]
|
||||||
@ -102,13 +94,15 @@ def create_address_parameters(
|
|||||||
block_index: int = None,
|
block_index: int = None,
|
||||||
tx_index: int = None,
|
tx_index: int = None,
|
||||||
certificate_index: int = None,
|
certificate_index: int = None,
|
||||||
|
script_payment_hash: bytes = None,
|
||||||
|
script_staking_hash: bytes = None,
|
||||||
) -> messages.CardanoAddressParametersType:
|
) -> messages.CardanoAddressParametersType:
|
||||||
certificate_pointer = None
|
certificate_pointer = None
|
||||||
|
|
||||||
if address_type not in ADDRESS_TYPES:
|
if address_type in (
|
||||||
raise ValueError("Unknown address type")
|
messages.CardanoAddressType.POINTER,
|
||||||
|
messages.CardanoAddressType.POINTER_SCRIPT,
|
||||||
if address_type == messages.CardanoAddressType.POINTER:
|
):
|
||||||
certificate_pointer = _create_certificate_pointer(
|
certificate_pointer = _create_certificate_pointer(
|
||||||
block_index, tx_index, certificate_index
|
block_index, tx_index, certificate_index
|
||||||
)
|
)
|
||||||
@ -119,6 +113,8 @@ def create_address_parameters(
|
|||||||
address_n_staking=address_n_staking,
|
address_n_staking=address_n_staking,
|
||||||
staking_key_hash=staking_key_hash,
|
staking_key_hash=staking_key_hash,
|
||||||
certificate_pointer=certificate_pointer,
|
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(
|
def _parse_address_parameters(
|
||||||
address_parameters,
|
address_parameters,
|
||||||
) -> messages.CardanoAddressParametersType:
|
) -> messages.CardanoAddressParametersType:
|
||||||
if "path" not in address_parameters:
|
if "addressType" not in address_parameters:
|
||||||
raise ValueError(INCOMPLETE_OUTPUT_ERROR_MESSAGE)
|
raise ValueError(INCOMPLETE_OUTPUT_ERROR_MESSAGE)
|
||||||
|
|
||||||
staking_key_hash_bytes = None
|
path = tools.parse_path(address_parameters.get("path"))
|
||||||
if "stakingKeyHash" in address_parameters:
|
staking_path = tools.parse_path(address_parameters.get("stakingPath"))
|
||||||
staking_key_hash_bytes = bytes.fromhex(address_parameters.get("stakingKeyHash"))
|
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(
|
return create_address_parameters(
|
||||||
int(address_parameters["addressType"]),
|
int(address_parameters["addressType"]),
|
||||||
tools.parse_path(address_parameters["path"]),
|
path,
|
||||||
tools.parse_path(address_parameters.get("stakingPath")),
|
staking_path,
|
||||||
staking_key_hash_bytes,
|
staking_key_hash_bytes,
|
||||||
address_parameters.get("blockIndex"),
|
address_parameters.get("blockIndex"),
|
||||||
address_parameters.get("txIndex"),
|
address_parameters.get("txIndex"),
|
||||||
address_parameters.get("certificateIndex"),
|
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", ())
|
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"))
|
key_path = tools.parse_path(native_script.get("key_path"))
|
||||||
required_signatures_count = parse_optional_int(
|
required_signatures_count = parse_optional_int(
|
||||||
native_script.get("required_signatures_count")
|
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"
|
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")
|
@click.group(name="cardano")
|
||||||
def cli():
|
def cli():
|
||||||
@ -115,14 +107,21 @@ def sign_tx(client, file, signing_mode, protocol_magic, network_id, testnet):
|
|||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@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("-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("-s", "--staking-address", type=str, default=None)
|
||||||
@click.option("-h", "--staking-key-hash", 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("-b", "--block_index", type=int, default=None)
|
||||||
@click.option("-x", "--tx_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("-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(
|
@click.option(
|
||||||
"-p", "--protocol-magic", type=int, default=cardano.PROTOCOL_MAGICS["mainnet"]
|
"-p", "--protocol-magic", type=int, default=cardano.PROTOCOL_MAGICS["mainnet"]
|
||||||
)
|
)
|
||||||
@ -138,6 +137,8 @@ def get_address(
|
|||||||
block_index,
|
block_index,
|
||||||
tx_index,
|
tx_index,
|
||||||
certificate_index,
|
certificate_index,
|
||||||
|
script_payment_hash,
|
||||||
|
script_staking_hash,
|
||||||
protocol_magic,
|
protocol_magic,
|
||||||
network_id,
|
network_id,
|
||||||
show_display,
|
show_display,
|
||||||
@ -161,9 +162,9 @@ def get_address(
|
|||||||
protocol_magic = cardano.PROTOCOL_MAGICS["testnet"]
|
protocol_magic = cardano.PROTOCOL_MAGICS["testnet"]
|
||||||
network_id = cardano.NETWORK_IDS["testnet"]
|
network_id = cardano.NETWORK_IDS["testnet"]
|
||||||
|
|
||||||
staking_key_hash_bytes = None
|
staking_key_hash_bytes = cardano.parse_optional_bytes(staking_key_hash)
|
||||||
if staking_key_hash:
|
script_payment_hash_bytes = cardano.parse_optional_bytes(script_payment_hash)
|
||||||
staking_key_hash_bytes = bytes.fromhex(staking_key_hash)
|
script_staking_hash_bytes = cardano.parse_optional_bytes(script_staking_hash)
|
||||||
|
|
||||||
address_parameters = cardano.create_address_parameters(
|
address_parameters = cardano.create_address_parameters(
|
||||||
address_type,
|
address_type,
|
||||||
@ -173,6 +174,8 @@ def get_address(
|
|||||||
block_index,
|
block_index,
|
||||||
tx_index,
|
tx_index,
|
||||||
certificate_index,
|
certificate_index,
|
||||||
|
script_payment_hash_bytes,
|
||||||
|
script_staking_hash_bytes,
|
||||||
)
|
)
|
||||||
|
|
||||||
return cardano.get_address(
|
return cardano.get_address(
|
||||||
|
@ -1962,6 +1962,8 @@ class CardanoAddressParametersType(protobuf.MessageType):
|
|||||||
3: protobuf.Field("address_n_staking", "uint32", repeated=True, required=False),
|
3: protobuf.Field("address_n_staking", "uint32", repeated=True, required=False),
|
||||||
4: protobuf.Field("staking_key_hash", "bytes", repeated=False, required=False),
|
4: protobuf.Field("staking_key_hash", "bytes", repeated=False, required=False),
|
||||||
5: protobuf.Field("certificate_pointer", "CardanoBlockchainPointerType", 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__(
|
def __init__(
|
||||||
@ -1972,12 +1974,16 @@ class CardanoAddressParametersType(protobuf.MessageType):
|
|||||||
address_n_staking: Optional[List["int"]] = None,
|
address_n_staking: Optional[List["int"]] = None,
|
||||||
staking_key_hash: Optional["bytes"] = None,
|
staking_key_hash: Optional["bytes"] = None,
|
||||||
certificate_pointer: Optional["CardanoBlockchainPointerType"] = None,
|
certificate_pointer: Optional["CardanoBlockchainPointerType"] = None,
|
||||||
|
script_payment_hash: Optional["bytes"] = None,
|
||||||
|
script_staking_hash: Optional["bytes"] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.address_n = address_n if address_n is not None else []
|
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_n_staking = address_n_staking if address_n_staking is not None else []
|
||||||
self.address_type = address_type
|
self.address_type = address_type
|
||||||
self.staking_key_hash = staking_key_hash
|
self.staking_key_hash = staking_key_hash
|
||||||
self.certificate_pointer = certificate_pointer
|
self.certificate_pointer = certificate_pointer
|
||||||
|
self.script_payment_hash = script_payment_hash
|
||||||
|
self.script_staking_hash = script_staking_hash
|
||||||
|
|
||||||
|
|
||||||
class CardanoGetAddress(protobuf.MessageType):
|
class CardanoGetAddress(protobuf.MessageType):
|
||||||
|
@ -16,7 +16,12 @@
|
|||||||
|
|
||||||
import pytest
|
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.messages import CardanoAddressType
|
||||||
from trezorlib.tools import parse_path
|
from trezorlib.tools import parse_path
|
||||||
|
|
||||||
@ -34,6 +39,7 @@ pytestmark = [
|
|||||||
"cardano/get_address_byron.slip39.json",
|
"cardano/get_address_byron.slip39.json",
|
||||||
"cardano/get_base_address.json",
|
"cardano/get_base_address.json",
|
||||||
"cardano/get_base_address_with_staking_key_hash.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_enterprise_address.json",
|
||||||
"cardano/get_pointer_address.json",
|
"cardano/get_pointer_address.json",
|
||||||
"cardano/get_reward_address.json",
|
"cardano/get_reward_address.json",
|
||||||
@ -45,16 +51,22 @@ def test_cardano_get_address(client, parameters, result):
|
|||||||
address_type=getattr(
|
address_type=getattr(
|
||||||
CardanoAddressType, parameters["address_type"].upper()
|
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"))
|
address_n_staking=parse_path(parameters.get("staking_path"))
|
||||||
if "staking_path" in parameters
|
if "staking_path" in parameters
|
||||||
else None,
|
else None,
|
||||||
staking_key_hash=bytes.fromhex(parameters.get("staking_key_hash"))
|
staking_key_hash=parse_optional_bytes(parameters.get("staking_key_hash")),
|
||||||
if "staking_key_hash" in parameters
|
|
||||||
else None,
|
|
||||||
block_index=parameters.get("block_index"),
|
block_index=parameters.get("block_index"),
|
||||||
tx_index=parameters.get("tx_index"),
|
tx_index=parameters.get("tx_index"),
|
||||||
certificate_index=parameters.get("certificate_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"],
|
protocol_magic=parameters["protocol_magic"],
|
||||||
network_id=parameters["network_id"],
|
network_id=parameters["network_id"],
|
||||||
|
Loading…
Reference in New Issue
Block a user