refactor(core/cardano): decouple address parameters validation

pull/1666/head
gabrielkerekes 3 years ago committed by matejcik
parent d6776d988e
commit 597402eab8

@ -8,7 +8,6 @@
"parameters": { "parameters": {
"path": "m/1852'/1815'/0'/0/0", "path": "m/1852'/1815'/0'/0/0",
"address_type": "enterprise", "address_type": "enterprise",
"staking_key_hash": "1bc428e4720702ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff",
"network_id": 1, "network_id": 1,
"protocol_magic": 764824073 "protocol_magic": 764824073
}, },
@ -20,7 +19,6 @@
"parameters": { "parameters": {
"path": "m/1852'/1815'/0'/0/0", "path": "m/1852'/1815'/0'/0/0",
"address_type": "enterprise", "address_type": "enterprise",
"staking_key_hash": "1bc428e4720702ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff",
"network_id": 0, "network_id": 0,
"protocol_magic": 42 "protocol_magic": 42
}, },

@ -230,7 +230,7 @@
] ]
}, },
"result": { "result": {
"error_message": "Output address network mismatch!" "error_message": "Output address network mismatch"
} }
}, },
{ {
@ -258,7 +258,7 @@
] ]
}, },
"result": { "result": {
"error_message": "Output address network mismatch!" "error_message": "Output address network mismatch"
} }
}, },
{ {

@ -1,9 +1,15 @@
from trezor import wire
from trezor.crypto import base58, hashlib from trezor.crypto import base58, hashlib
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
from .helpers import INVALID_ADDRESS, NETWORK_MISMATCH, bech32, network_ids from .helpers import (
ADDRESS_KEY_HASH_SIZE,
INVALID_ADDRESS,
INVALID_ADDRESS_PARAMETERS,
NETWORK_MISMATCH,
bech32,
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 derive_public_key, variable_length_encode
from .seed import is_byron_path, is_shelley_path from .seed import is_byron_path, is_shelley_path
@ -31,6 +37,70 @@ MIN_ADDRESS_BYTES_LENGTH = 29
MAX_ADDRESS_BYTES_LENGTH = 65 MAX_ADDRESS_BYTES_LENGTH = 65
def validate_address_parameters(parameters: CardanoAddressParametersType) -> None:
_validate_address_parameters_structure(parameters)
if parameters.address_type == CardanoAddressType.BYRON:
if not is_byron_path(parameters.address_n):
raise INVALID_ADDRESS_PARAMETERS
elif parameters.address_type in ADDRESS_TYPES_SHELLEY:
if not is_shelley_path(parameters.address_n):
raise INVALID_ADDRESS_PARAMETERS
if parameters.address_type == CardanoAddressType.BASE:
_validate_base_address_staking_info(
parameters.address_n_staking, parameters.staking_key_hash
)
elif parameters.address_type == CardanoAddressType.POINTER:
if parameters.certificate_pointer is None:
raise INVALID_ADDRESS_PARAMETERS
elif parameters.address_type == CardanoAddressType.REWARD:
if not SCHEMA_STAKING_ANY_ACCOUNT.match(parameters.address_n):
raise INVALID_ADDRESS_PARAMETERS
else:
raise INVALID_ADDRESS_PARAMETERS
def _validate_address_parameters_structure(
parameters: CardanoAddressParametersType,
) -> None:
address_n_staking = parameters.address_n_staking
staking_key_hash = parameters.staking_key_hash
certificate_pointer = parameters.certificate_pointer
fields_to_be_empty: tuple = ()
if parameters.address_type in (
CardanoAddressType.BYRON,
CardanoAddressType.REWARD,
CardanoAddressType.ENTERPRISE,
):
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
def _validate_base_address_staking_info(
staking_path: list[int],
staking_key_hash: bytes | None,
) -> None:
if staking_key_hash and staking_path:
raise INVALID_ADDRESS_PARAMETERS
if staking_key_hash:
if len(staking_key_hash) != ADDRESS_KEY_HASH_SIZE:
raise INVALID_ADDRESS_PARAMETERS
elif staking_path:
if not SCHEMA_STAKING_ANY_ACCOUNT.match(staking_path):
raise INVALID_ADDRESS_PARAMETERS
else:
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:
@ -142,7 +212,7 @@ def _get_address_network_id(address: bytes) -> int:
def get_public_key_hash(keychain: seed.Keychain, path: list[int]) -> bytes: def get_public_key_hash(keychain: seed.Keychain, path: list[int]) -> bytes:
public_key = derive_public_key(keychain, path) public_key = derive_public_key(keychain, path)
return hashlib.blake2b(data=public_key, outlen=28).digest() return hashlib.blake2b(data=public_key, outlen=ADDRESS_KEY_HASH_SIZE).digest()
def derive_human_readable_address( def derive_human_readable_address(
@ -180,31 +250,18 @@ def derive_address_bytes(
is_byron_address = parameters.address_type == CardanoAddressType.BYRON is_byron_address = parameters.address_type == CardanoAddressType.BYRON
if is_byron_address: if is_byron_address:
address = _derive_byron_address(keychain, parameters.address_n, protocol_magic) address = derive_byron_address(keychain, parameters.address_n, protocol_magic)
else: else:
address = _derive_shelley_address(keychain, parameters, network_id) address = _derive_shelley_address(keychain, parameters, network_id)
return address return address
def _derive_byron_address(
keychain: seed.Keychain, path: list[int], protocol_magic: int
) -> bytes:
if not is_byron_path(path):
raise wire.DataError("Invalid path for byron address!")
address = derive_byron_address(keychain, path, protocol_magic)
return address
def _derive_shelley_address( def _derive_shelley_address(
keychain: seed.Keychain, keychain: seed.Keychain,
parameters: CardanoAddressParametersType, parameters: CardanoAddressParametersType,
network_id: int, network_id: int,
) -> bytes: ) -> bytes:
if not is_shelley_path(parameters.address_n):
raise wire.DataError("Invalid path for shelley address!")
if parameters.address_type == CardanoAddressType.BASE: if parameters.address_type == CardanoAddressType.BASE:
address = _derive_base_address( address = _derive_base_address(
keychain, keychain,
@ -213,21 +270,22 @@ def _derive_shelley_address(
parameters.staking_key_hash, parameters.staking_key_hash,
network_id, network_id,
) )
elif parameters.address_type == CardanoAddressType.ENTERPRISE:
address = _derive_enterprise_address(keychain, parameters.address_n, network_id)
elif parameters.address_type == CardanoAddressType.POINTER: elif parameters.address_type == CardanoAddressType.POINTER:
if parameters.certificate_pointer is None: # ensured by validate_address_parameters
raise wire.DataError("Certificate pointer data missing!") assert parameters.certificate_pointer is not None
address = _derive_pointer_address( address = _derive_pointer_address(
keychain, keychain,
parameters.address_n, parameters.address_n,
parameters.certificate_pointer, parameters.certificate_pointer,
network_id, network_id,
) )
elif parameters.address_type == CardanoAddressType.ENTERPRISE:
address = _derive_enterprise_address(keychain, parameters.address_n, network_id)
elif parameters.address_type == CardanoAddressType.REWARD: elif parameters.address_type == CardanoAddressType.REWARD:
address = _derive_reward_address(keychain, parameters.address_n, network_id) address = _derive_reward_address(keychain, parameters.address_n, network_id)
else: else:
raise wire.DataError("Invalid address type!") raise INVALID_ADDRESS_PARAMETERS
return address return address
@ -247,27 +305,12 @@ def _derive_base_address(
header = _create_address_header(CardanoAddressType.BASE, network_id) header = _create_address_header(CardanoAddressType.BASE, network_id)
spending_key_hash = get_public_key_hash(keychain, path) spending_key_hash = get_public_key_hash(keychain, path)
_validate_base_address_staking_info(staking_path, staking_key_hash)
if staking_key_hash is None: if staking_key_hash is None:
staking_key_hash = get_public_key_hash(keychain, staking_path) staking_key_hash = get_public_key_hash(keychain, staking_path)
return header + spending_key_hash + staking_key_hash return header + spending_key_hash + staking_key_hash
def _validate_base_address_staking_info(
staking_path: list[int],
staking_key_hash: bytes | None,
) -> None:
if (staking_key_hash is None) == (not staking_path):
raise wire.DataError(
"Base address needs either a staking path or a staking key hash!"
)
if staking_key_hash is None and not SCHEMA_STAKING_ANY_ACCOUNT.match(staking_path):
raise wire.DataError("Invalid staking path!")
def _derive_pointer_address( def _derive_pointer_address(
keychain: seed.Keychain, keychain: seed.Keychain,
path: list[int], path: list[int],
@ -305,9 +348,6 @@ def _derive_reward_address(
path: list[int], path: list[int],
network_id: int, network_id: int,
) -> bytes: ) -> bytes:
if not SCHEMA_STAKING_ANY_ACCOUNT.match(path):
raise wire.DataError("Invalid path for reward address!")
staking_key_hash = get_public_key_hash(keychain, path) staking_key_hash = get_public_key_hash(keychain, path)
return pack_reward_address_bytes(staking_key_hash, network_id) return pack_reward_address_bytes(staking_key_hash, network_id)

@ -4,7 +4,11 @@ from trezor.enums import CardanoAddressType
from apps.common import cbor from apps.common import cbor
from .address import derive_address_bytes, derive_human_readable_address from .address import (
derive_address_bytes,
derive_human_readable_address,
validate_address_parameters,
)
from .helpers import INVALID_AUXILIARY_DATA, bech32 from .helpers import INVALID_AUXILIARY_DATA, bech32
from .helpers.bech32 import HRP_JORMUN_PUBLIC_KEY from .helpers.bech32 import HRP_JORMUN_PUBLIC_KEY
from .helpers.paths import SCHEMA_STAKING_ANY_ACCOUNT from .helpers.paths import SCHEMA_STAKING_ANY_ACCOUNT
@ -36,12 +40,7 @@ METADATA_KEY_CATALYST_REGISTRATION = 61284
METADATA_KEY_CATALYST_REGISTRATION_SIGNATURE = 61285 METADATA_KEY_CATALYST_REGISTRATION_SIGNATURE = 61285
def validate_auxiliary_data( def validate_auxiliary_data(auxiliary_data: CardanoTxAuxiliaryDataType | None) -> None:
keychain: seed.Keychain,
auxiliary_data: CardanoTxAuxiliaryDataType | None,
protocol_magic: int,
network_id: int,
) -> None:
if not auxiliary_data: if not auxiliary_data:
return return
@ -52,10 +51,7 @@ def validate_auxiliary_data(
if auxiliary_data.catalyst_registration_parameters: if auxiliary_data.catalyst_registration_parameters:
fields_provided += 1 fields_provided += 1
_validate_catalyst_registration_parameters( _validate_catalyst_registration_parameters(
keychain, auxiliary_data.catalyst_registration_parameters
auxiliary_data.catalyst_registration_parameters,
protocol_magic,
network_id,
) )
if fields_provided != 1: if fields_provided != 1:
@ -72,10 +68,7 @@ def _validate_auxiliary_data_blob(auxiliary_data_blob: bytes) -> None:
def _validate_catalyst_registration_parameters( def _validate_catalyst_registration_parameters(
keychain: seed.Keychain,
catalyst_registration_parameters: CardanoCatalystRegistrationParametersType, catalyst_registration_parameters: CardanoCatalystRegistrationParametersType,
protocol_magic: int,
network_id: int,
) -> None: ) -> None:
if ( if (
len(catalyst_registration_parameters.voting_public_key) len(catalyst_registration_parameters.voting_public_key)
@ -92,8 +85,7 @@ def _validate_catalyst_registration_parameters(
if address_parameters.address_type == CardanoAddressType.BYRON: if address_parameters.address_type == CardanoAddressType.BYRON:
raise INVALID_AUXILIARY_DATA raise INVALID_AUXILIARY_DATA
# try to derive the address to validate it validate_address_parameters(address_parameters)
derive_address_bytes(keychain, address_parameters, protocol_magic, network_id)
async def show_auxiliary_data( async def show_auxiliary_data(

@ -7,7 +7,7 @@ from .address import (
get_public_key_hash, get_public_key_hash,
validate_reward_address, validate_reward_address,
) )
from .helpers import INVALID_CERTIFICATE, LOVELACE_MAX_SUPPLY from .helpers import ADDRESS_KEY_HASH_SIZE, INVALID_CERTIFICATE, LOVELACE_MAX_SUPPLY
from .helpers.paths import SCHEMA_STAKING_ANY_ACCOUNT from .helpers.paths import SCHEMA_STAKING_ANY_ACCOUNT
if False: if False:
@ -26,7 +26,6 @@ if False:
POOL_HASH_SIZE = 28 POOL_HASH_SIZE = 28
VRF_KEY_HASH_SIZE = 32 VRF_KEY_HASH_SIZE = 32
POOL_METADATA_HASH_SIZE = 32 POOL_METADATA_HASH_SIZE = 32
PUBLIC_KEY_HASH_SIZE = 28
IPV4_ADDRESS_SIZE = 4 IPV4_ADDRESS_SIZE = 4
IPV6_ADDRESS_SIZE = 16 IPV6_ADDRESS_SIZE = 16
@ -140,7 +139,9 @@ def _validate_pool_owners(owners: list[CardanoPoolOwnerType]) -> None:
owner.staking_key_hash is not None or owner.staking_key_path is not None owner.staking_key_hash is not None or owner.staking_key_path is not None
) )
if owner.staking_key_hash is not None: if owner.staking_key_hash is not None:
assert_certificate_cond(len(owner.staking_key_hash) == PUBLIC_KEY_HASH_SIZE) assert_certificate_cond(
len(owner.staking_key_hash) == ADDRESS_KEY_HASH_SIZE
)
if owner.staking_key_path: if owner.staking_key_path:
assert_certificate_cond( assert_certificate_cond(
SCHEMA_STAKING_ANY_ACCOUNT.match(owner.staking_key_path) SCHEMA_STAKING_ANY_ACCOUNT.match(owner.staking_key_path)

@ -5,7 +5,7 @@ from apps.common import paths
from apps.common.layout import address_n_to_str, show_qr from apps.common.layout import address_n_to_str, show_qr
from . import seed from . import seed
from .address import derive_human_readable_address from .address import derive_human_readable_address, validate_address_parameters
from .helpers import protocol_magics, staking_use_cases from .helpers import protocol_magics, staking_use_cases
from .helpers.paths import SCHEMA_ADDRESS from .helpers.paths import SCHEMA_ADDRESS
from .helpers.utils import to_account_path from .helpers.utils import to_account_path
@ -38,6 +38,7 @@ async def get_address(
) )
validate_network_info(msg.network_id, msg.protocol_magic) validate_network_info(msg.network_id, msg.protocol_magic)
validate_address_parameters(address_parameters)
try: try:
address = derive_human_readable_address( address = derive_human_readable_address(

@ -1,7 +1,8 @@
from trezor import wire from trezor import wire
INVALID_ADDRESS = wire.ProcessError("Invalid address") INVALID_ADDRESS = wire.ProcessError("Invalid address")
NETWORK_MISMATCH = wire.ProcessError("Output address network mismatch!") INVALID_ADDRESS_PARAMETERS = wire.ProcessError("Invalid address parameters")
NETWORK_MISMATCH = wire.ProcessError("Output address network mismatch")
INVALID_CERTIFICATE = wire.ProcessError("Invalid certificate") INVALID_CERTIFICATE = wire.ProcessError("Invalid certificate")
INVALID_WITHDRAWAL = wire.ProcessError("Invalid withdrawal") INVALID_WITHDRAWAL = wire.ProcessError("Invalid withdrawal")
INVALID_TOKEN_BUNDLE_OUTPUT = wire.ProcessError("Invalid token bundle in output") INVALID_TOKEN_BUNDLE_OUTPUT = wire.ProcessError("Invalid token bundle in output")
@ -14,3 +15,4 @@ INVALID_STAKEPOOL_REGISTRATION_TX_INPUTS = wire.ProcessError(
) )
LOVELACE_MAX_SUPPLY = 45_000_000_000 * 1_000_000 LOVELACE_MAX_SUPPLY = 45_000_000_000 * 1_000_000
ADDRESS_KEY_HASH_SIZE = 28

@ -17,6 +17,7 @@ from .address import (
derive_address_bytes, derive_address_bytes,
derive_human_readable_address, derive_human_readable_address,
get_address_bytes_unsafe, get_address_bytes_unsafe,
validate_address_parameters,
validate_output_address, validate_output_address,
) )
from .auxiliary_data import ( from .auxiliary_data import (
@ -135,12 +136,10 @@ async def _sign_ordinary_tx(
ctx, keychain, i.address_n, SCHEMA_ADDRESS.match(i.address_n) ctx, keychain, i.address_n, SCHEMA_ADDRESS.match(i.address_n)
) )
_validate_outputs(keychain, msg.outputs, msg.protocol_magic, msg.network_id) _validate_outputs(msg.outputs, msg.protocol_magic, msg.network_id)
_validate_certificates(msg.certificates, msg.protocol_magic, msg.network_id) _validate_certificates(msg.certificates, msg.protocol_magic, msg.network_id)
_validate_withdrawals(msg.withdrawals) _validate_withdrawals(msg.withdrawals)
validate_auxiliary_data( validate_auxiliary_data(msg.auxiliary_data)
keychain, msg.auxiliary_data, msg.protocol_magic, msg.network_id
)
# display the transaction in UI # display the transaction in UI
await _show_standard_tx(ctx, keychain, msg) await _show_standard_tx(ctx, keychain, msg)
@ -166,11 +165,9 @@ async def _sign_stake_pool_registration_tx(
_validate_stake_pool_registration_tx_structure(msg) _validate_stake_pool_registration_tx_structure(msg)
_ensure_no_signing_inputs(msg.inputs) _ensure_no_signing_inputs(msg.inputs)
_validate_outputs(keychain, msg.outputs, msg.protocol_magic, msg.network_id) _validate_outputs(msg.outputs, msg.protocol_magic, msg.network_id)
_validate_certificates(msg.certificates, msg.protocol_magic, msg.network_id) _validate_certificates(msg.certificates, msg.protocol_magic, msg.network_id)
validate_auxiliary_data( validate_auxiliary_data(msg.auxiliary_data)
keychain, msg.auxiliary_data, msg.protocol_magic, msg.network_id
)
await _show_stake_pool_registration_tx(ctx, keychain, msg) await _show_stake_pool_registration_tx(ctx, keychain, msg)
@ -209,7 +206,6 @@ def _validate_stake_pool_registration_tx_structure(msg: CardanoSignTx) -> None:
def _validate_outputs( def _validate_outputs(
keychain: seed.Keychain,
outputs: list[CardanoTxOutputType], outputs: list[CardanoTxOutputType],
protocol_magic: int, protocol_magic: int,
network_id: int, network_id: int,
@ -218,10 +214,7 @@ def _validate_outputs(
for output in outputs: for output in outputs:
total_amount += output.amount total_amount += output.amount
if output.address_parameters: if output.address_parameters:
# try to derive the address to validate it validate_address_parameters(output.address_parameters)
derive_address_bytes(
keychain, output.address_parameters, protocol_magic, network_id
)
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:

@ -8,7 +8,7 @@ from trezor.messages import CardanoBlockchainPointerType
from apps.common import HARDENED, seed from apps.common import HARDENED, seed
if not utils.BITCOIN_ONLY: if not utils.BITCOIN_ONLY:
from apps.cardano.address import derive_human_readable_address from apps.cardano.address import derive_human_readable_address, validate_address_parameters
from apps.cardano.byron_address import _address_hash from apps.cardano.byron_address import _address_hash
from apps.cardano.helpers import network_ids, protocol_magics from apps.cardano.helpers import network_ids, protocol_magics
from apps.cardano.seed import Keychain from apps.cardano.seed import Keychain
@ -335,32 +335,6 @@ class TestCardanoAddress(unittest.TestCase):
self.assertEqual(actual_address, expected_address) self.assertEqual(actual_address, expected_address)
def test_base_address_with_invalid_parameters(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)
# both address_n_staking and staking_key_hash are None
with self.assertRaises(wire.DataError):
address_parameters = CardanoAddressParametersType(
address_type=CardanoAddressType.BASE,
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
address_n_staking=None,
staking_key_hash=None,
)
derive_human_readable_address(keychain, address_parameters, 0, 0)
# address_n_staking is not a staking path
with self.assertRaises(wire.DataError):
address_parameters = CardanoAddressParametersType(
address_type=CardanoAddressType.BASE,
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
staking_key_hash=None,
)
derive_human_readable_address(keychain, address_parameters, 0, 0)
def test_enterprise_address(self): def test_enterprise_address(self):
mnemonic = "test walk nut penalty hip pave soap entry language right filter choice" mnemonic = "test walk nut penalty hip pave soap entry language right filter choice"
passphrase = "" passphrase = ""
@ -404,21 +378,6 @@ class TestCardanoAddress(unittest.TestCase):
self.assertEqual(actual_address, expected_address) self.assertEqual(actual_address, expected_address)
def test_pointer_address_invalid_pointers(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)
# pointer is None
with self.assertRaises(wire.DataError):
address_parameters = CardanoAddressParametersType(
address_type=CardanoAddressType.POINTER,
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
certificate_pointer=None,
)
derive_human_readable_address(keychain, address_parameters, 0, 0)
def test_reward_address(self): def test_reward_address(self):
mnemonic = "test walk nut penalty hip pave soap entry language right filter choice" mnemonic = "test walk nut penalty hip pave soap entry language right filter choice"
passphrase = "" passphrase = ""
@ -440,95 +399,99 @@ class TestCardanoAddress(unittest.TestCase):
self.assertEqual(actual_address, expected_address) self.assertEqual(actual_address, expected_address)
def test_reward_address_with_non_staking_path(self): def test_testnet_byron_address(self):
mnemonic = "test walk nut penalty hip pave soap entry language right filter choice" mnemonic = "all all all all all all all all all all all all"
passphrase = "" passphrase = ""
node = bip32.from_mnemonic_cardano(mnemonic, passphrase) node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
keychain = Keychain(node) keychain = Keychain(node)
with self.assertRaises(wire.DataError): addresses = [
"2657WMsDfac5F3zbgs9BwNWx3dhGAJERkAL93gPa68NJ2i8mbCHm2pLUHWSj8Mfea",
"2657WMsDfac6ezKWszxLFqJjSUgpg9NgxKc1koqi24sVpRaPhiwMaExk4useKn5HA",
"2657WMsDfac7hr1ioJGr6g7r6JRx4r1My8Rj91tcPTeVjJDpfBYKURrPG2zVLx2Sq",
]
for i, expected in enumerate(addresses):
# 44'/1815'/0'/0/i'
address_parameters = CardanoAddressParametersType( address_parameters = CardanoAddressParametersType(
address_type=CardanoAddressType.REWARD, address_type=CardanoAddressType.BYRON,
address_n=[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0] address_n=[0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, i],
) )
derive_human_readable_address(keychain, address_parameters, 0, 0) address = derive_human_readable_address(keychain, address_parameters, protocol_magics.TESTNET, 0)
self.assertEqual(expected, address)
def test_shelley_address_with_byron_namespace(self): def test_validate_address_parameters(self):
""" test_vectors = [
It shouldn't be possible to derive Shelley addresses # base address - both address_n_staking and staking_key_hash are None
(Base, Pointer, Enterprise, Reward) with a Byron namespace (44') CardanoAddressParametersType(
""" address_type=CardanoAddressType.BASE,
mnemonic = "test walk nut penalty hip pave soap entry language right filter choice" address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
passphrase = "" address_n_staking=None,
node = bip32.from_mnemonic_cardano(mnemonic, passphrase) staking_key_hash=None,
keychain = Keychain(node) ),
# base address - both address_n_staking and staking_key_hash are set
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_key_hash=unhexlify("1bc428e4720702ebd5dab4fb175324c192dc9bb76cc5da956e3c8dff"),
),
# base address - staking_key_hash is too short
CardanoAddressParametersType(
address_type=CardanoAddressType.BASE,
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
address_n_staking=None,
staking_key_hash=unhexlify("1bc428e4720702ebd5dab4fb175324c192dc9bb76cc5da956e3c8d"),
),
# base address - address_n_staking is not a staking path
CardanoAddressParametersType(
address_type=CardanoAddressType.BASE,
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
address_n_staking=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
staking_key_hash=None,
),
# pointer address - pointer is None
CardanoAddressParametersType(
address_type=CardanoAddressType.POINTER,
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
certificate_pointer=None,
),
# reward address - non staking path
CardanoAddressParametersType(
address_type=CardanoAddressType.REWARD,
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0]
),
with self.assertRaises(wire.DataError): # Shelley addresses with Byron namespace
address_parameters = 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]
) ),
derive_human_readable_address(keychain, address_parameters, 0, 0) CardanoAddressParametersType(
with self.assertRaises(wire.DataError):
address_parameters = CardanoAddressParametersType(
address_type=CardanoAddressType.POINTER, address_type=CardanoAddressType.POINTER,
address_n=[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0], address_n=[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
certificate_pointer=CardanoBlockchainPointerType(block_index=0, tx_index=0, certificate_index=0) certificate_pointer=CardanoBlockchainPointerType(block_index=0, tx_index=0, certificate_index=0)
) ),
derive_human_readable_address(keychain, address_parameters, 0, 0) CardanoAddressParametersType(
with self.assertRaises(wire.DataError):
address_parameters = CardanoAddressParametersType(
address_type=CardanoAddressType.ENTERPRISE, address_type=CardanoAddressType.ENTERPRISE,
address_n=[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0], address_n=[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
) ),
derive_human_readable_address(keychain, address_parameters, 0, 0) CardanoAddressParametersType(
with self.assertRaises(wire.DataError):
address_parameters = CardanoAddressParametersType(
address_type=CardanoAddressType.REWARD, address_type=CardanoAddressType.REWARD,
address_n=[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0], address_n=[44 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
) ),
derive_human_readable_address(keychain, address_parameters, 0, 0)
def test_byron_address_with_shelley_namespace(self):
"""
It shouldn't be possible to derive Byron addresses
with a Shelley namespace (1852')
"""
mnemonic = "all all all all all all all all all all all all"
passphrase = ""
node = bip32.from_mnemonic_cardano(mnemonic, passphrase)
keychain = Keychain(node)
with self.assertRaises(wire.DataError): # Byron address with Shelley namespace
address_parameters = CardanoAddressParametersType( CardanoAddressParametersType(
address_type=CardanoAddressType.BYRON, address_type=CardanoAddressType.BYRON,
address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0], address_n=[1852 | HARDENED, 1815 | HARDENED, 0 | HARDENED, 0, 0],
) )
derive_human_readable_address(keychain, address_parameters, 0, 0)
def test_testnet_byron_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)
addresses = [
"2657WMsDfac5F3zbgs9BwNWx3dhGAJERkAL93gPa68NJ2i8mbCHm2pLUHWSj8Mfea",
"2657WMsDfac6ezKWszxLFqJjSUgpg9NgxKc1koqi24sVpRaPhiwMaExk4useKn5HA",
"2657WMsDfac7hr1ioJGr6g7r6JRx4r1My8Rj91tcPTeVjJDpfBYKURrPG2zVLx2Sq",
] ]
for i, expected in enumerate(addresses): for address_parameters in test_vectors:
# 44'/1815'/0'/0/i' with self.assertRaises(wire.ProcessError):
address_parameters = CardanoAddressParametersType( validate_address_parameters(address_parameters)
address_type=CardanoAddressType.BYRON,
address_n=[0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, i],
)
address = derive_human_readable_address(keychain, address_parameters, protocol_magics.TESTNET, 0)
self.assertEqual(expected, address)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

Loading…
Cancel
Save