fix(cardano): add map key canonical order validation

vdovhanych/arm-emu-deploy
David Misiak 2 years ago committed by matejcik
parent 143af80aea
commit 1388912762

@ -923,48 +923,6 @@
"error_message": "Invalid withdrawal"
}
},
{
"description": "Duplicate withdrawal",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"certificates": [],
"withdrawals": [
{
"path": "m/1852'/1815'/0'/2/0",
"amount": "1000"
},
{
"path": "m/1852'/1815'/0'/2/0",
"amount": "2000"
}
],
"auxiliary_data": null,
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "Ae2tdPwUPEZCanmBz5g2GEwFqKTKpNJcGYPKfDxoNeKZ8bRHr8366kseiK2",
"amount": "3003112"
}
],
"mint": [],
"script_data_hash": null,
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"error_message": "Duplicate withdrawals"
}
},
{
"description": "Auxiliary data hash has incorrect length",
"parameters": {
@ -1514,6 +1472,331 @@
"error_message": "Invalid token bundle in output"
}
},
{
"description": "Repeated asset name in mint token group",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"certificates": [],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "Ae2tdPwUPEZCanmBz5g2GEwFqKTKpNJcGYPKfDxoNeKZ8bRHr8366kseiK2",
"amount": "3003112"
}
],
"mint": [
{
"policy_id": "95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696e",
"mint_amount": "7878754"
},
{
"asset_name_bytes": "74652474436f696e",
"mint_amount": "1234"
}
]
}
],
"script_data_hash": null,
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"error_message": "Invalid mint token bundle"
}
},
{
"description": "Repeated policyId in mint",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"certificates": [],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "Ae2tdPwUPEZCanmBz5g2GEwFqKTKpNJcGYPKfDxoNeKZ8bRHr8366kseiK2",
"amount": "3003112"
}
],
"mint": [
{
"policy_id": "95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696e",
"mint_amount": "7878754"
}
]
},
{
"policy_id": "95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696f",
"mint_amount": "7878754"
}
]
}
],
"script_data_hash": null,
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"error_message": "Invalid mint token bundle"
}
},
{
"description": "Asset names in multiasset token group in wrong order",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"certificates": [],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "Ae2tdPwUPEZCanmBz5g2GEwFqKTKpNJcGYPKfDxoNeKZ8bRHr8366kseiK2",
"amount": "3003112",
"token_bundle": [
{
"policy_id": "95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696e",
"amount": "7878754"
},
{
"asset_name_bytes": "76652474436f696e",
"amount": "1234"
},
{
"asset_name_bytes": "75652474436f696e",
"amount": "1234"
}
]
}
]
}
],
"mint": [],
"script_data_hash": null,
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"error_message": "Invalid token bundle in output"
}
},
{
"description": "PolicyIds in multiasset output in wrong order",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"certificates": [],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "Ae2tdPwUPEZCanmBz5g2GEwFqKTKpNJcGYPKfDxoNeKZ8bRHr8366kseiK2",
"amount": "3003112",
"token_bundle": [
{
"policy_id": "95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696d",
"amount": "7878754"
}
]
},
{
"policy_id": "9aa292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696e",
"amount": "7878754"
}
]
},
{
"policy_id": "96a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696f",
"amount": "7878754"
}
]
}
]
}
],
"mint": [],
"script_data_hash": null,
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"error_message": "Invalid token bundle in output"
}
},
{
"description": "Asset names in mint token group in wrong order",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"certificates": [],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "Ae2tdPwUPEZCanmBz5g2GEwFqKTKpNJcGYPKfDxoNeKZ8bRHr8366kseiK2",
"amount": "3003112"
}
],
"mint": [
{
"policy_id": "95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696e",
"mint_amount": "7878754"
},
{
"asset_name_bytes": "76652474436f696e",
"mint_amount": "1234"
},
{
"asset_name_bytes": "75652474436f696e",
"mint_amount": "1234"
}
]
}
],
"script_data_hash": null,
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"error_message": "Invalid mint token bundle"
}
},
{
"description": "PolicyIds in mint in wrong order",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"certificates": [],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "Ae2tdPwUPEZCanmBz5g2GEwFqKTKpNJcGYPKfDxoNeKZ8bRHr8366kseiK2",
"amount": "3003112"
}
],
"mint": [
{
"policy_id": "95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "14652474436f696d",
"mint_amount": "7878754"
}
]
},
{
"policy_id": "9aa292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "24652474436f696e",
"mint_amount": "7878754"
}
]
},
{
"policy_id": "96a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "34652474436f696f",
"mint_amount": "7878754"
}
]
}
],
"script_data_hash": null,
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"error_message": "Invalid mint token bundle"
}
},
{
"description": "Additional witness requests in ORDINARY_TRANSACTION",
"parameters": {

@ -1707,6 +1707,144 @@
}
]
}
},
{
"description": "Ordinary transaction with multiple correctly ordered tokens",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"validity_interval_start": 47,
"certificates": [],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
"amount": "2000000",
"token_bundle": [
{
"policy_id": "95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "f565",
"amount": "9878754"
},
{
"asset_name_bytes": "f5652474436f69",
"amount": "7878754"
},
{
"asset_name_bytes": "74652474436f696e",
"amount": "7878754"
},
{
"asset_name_bytes": "75652474436f696e",
"amount": "1234"
}
]
},
{
"policy_id": "96a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696e",
"amount": "7878754"
}
]
},
{
"policy_id": "d6a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696e",
"amount": "7878754"
}
]
}
]
},
{
"address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
"amount": "2000000",
"token_bundle": [
{
"policy_id": "a6a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696e",
"amount": "7878754"
}
]
}
]
}
],
"mint": [
{
"policy_id": "95a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "f565",
"mint_amount": "9878754"
},
{
"asset_name_bytes": "f5652474436f69",
"mint_amount": "7878754"
},
{
"asset_name_bytes": "74652474436f696e",
"mint_amount": "-7878754"
},
{
"asset_name_bytes": "75652474436f696e",
"mint_amount": "-1234"
}
]
},
{
"policy_id": "96a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696e",
"mint_amount": "7878754"
}
]
},
{
"policy_id": "d6a292ffee938be03e9bae5657982a74e9014eb4960108c9e23a5b39",
"tokens": [
{
"asset_name_bytes": "74652474436f696e",
"mint_amount": "7878754"
}
]
}
],
"script_data_hash": null,
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"tx_hash": "f1bda77315626ce61c784f3e60a74128f29b7d00a0c8baca030464293da2d7c4",
"witnesses": [
{
"type": 1,
"pub_key": "5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1",
"signature": "4254a6cae8156771ac8e1e75ceb8003f8370a7f6b08013fda32174b56025fcb01bc9d7f4e1eed10bc2c0ff1020eda47475f728cc824ef98a109fd9fe6a1c4105",
"chain_code": null
}
]
}
}
]
}

@ -131,6 +131,92 @@
"error_message": "Invalid certificate"
}
},
{
"description": "Multisig transaction with repeated withdrawal",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"certificates": [],
"withdrawals": [
{
"script_hash": "19fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd",
"amount": "1000"
},
{
"script_hash": "29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd",
"amount": "3000"
},
{
"script_hash": "29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd",
"amount": "1000"
}
],
"auxiliary_data": null,
"inputs": [
{
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1w9rhu54nz94k9l5v6d9rzfs47h7dv7xffcwkekuxcx3evnqpvuxu0",
"amount": "1"
}
],
"mint": [],
"script_data_hash": null,
"signing_mode": "MULTISIG_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"error_message": "Invalid withdrawal"
}
},
{
"description": "Multisig transaction with wthdrawal addresses in wrong order",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"certificates": [],
"withdrawals": [
{
"script_hash": "39fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd",
"amount": "3000"
},
{
"script_hash": "29fb5fd4aa8cadd6705acc8263cee0fc62edca5ac38db593fec2f9fd",
"amount": "1000"
}
],
"auxiliary_data": null,
"inputs": [
{
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1w9rhu54nz94k9l5v6d9rzfs47h7dv7xffcwkekuxcx3evnqpvuxu0",
"amount": "1"
}
],
"mint": [],
"script_data_hash": null,
"signing_mode": "MULTISIG_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"error_message": "Invalid withdrawal"
}
},
{
"description": "Multisig transaction with 1852 multisig witness request",
"parameters": {

@ -421,15 +421,13 @@ async def _process_asset_groups(
should_show_tokens: bool,
) -> None:
"""Read, validate and serialize the asset groups of an output."""
# until the CIP with canonical CBOR is finalized storing the seen_policy_ids is the only way we can check for
# duplicate policy_ids
seen_policy_ids: set[bytes] = set()
previous_policy_id: bytes = b""
for _ in range(asset_groups_count):
asset_group: CardanoAssetGroup = await ctx.call(
CardanoTxItemAck(), CardanoAssetGroup
)
_validate_asset_group(asset_group, seen_policy_ids)
seen_policy_ids.add(asset_group.policy_id)
_validate_asset_group(asset_group, previous_policy_id)
previous_policy_id = asset_group.policy_id
tokens: HashBuilderDict[bytes, int] = HashBuilderDict(asset_group.tokens_count)
with asset_groups_dict.add(asset_group.policy_id, tokens):
@ -450,13 +448,11 @@ async def _process_tokens(
should_show_tokens: bool,
) -> None:
"""Read, validate, confirm and serialize the tokens of an asset group."""
# until the CIP with canonical CBOR is finalized storing the seen_asset_name_bytes is the only way we can check for
# duplicate tokens
seen_asset_name_bytes: set[bytes] = set()
previous_asset_name_bytes: bytes = b""
for _ in range(tokens_count):
token: CardanoToken = await ctx.call(CardanoTxItemAck(), CardanoToken)
_validate_token(token, seen_asset_name_bytes)
seen_asset_name_bytes.add(token.asset_name_bytes)
_validate_token(token, previous_asset_name_bytes)
previous_asset_name_bytes = token.asset_name_bytes
if should_show_tokens:
await confirm_sending_token(ctx, policy_id, token)
@ -577,32 +573,26 @@ async def _process_withdrawals(
if withdrawals_count == 0:
return
# until the CIP with canonical CBOR is finalized storing the seen_withdrawals is the only way we can check for
# duplicate withdrawals
seen_withdrawals: set[tuple[int, ...] | bytes] = set()
previous_reward_address: bytes = b""
for _ in range(withdrawals_count):
withdrawal: CardanoTxWithdrawal = await ctx.call(
CardanoTxItemAck(), CardanoTxWithdrawal
)
_validate_withdrawal(
withdrawal, seen_withdrawals, signing_mode, account_path_checker
)
await confirm_withdrawal(ctx, withdrawal)
reward_address_type = (
CardanoAddressType.REWARD
if withdrawal.path
else CardanoAddressType.REWARD_SCRIPT
)
reward_address = derive_address_bytes(
keychain,
CardanoAddressParametersType(
address_type=reward_address_type,
address_n_staking=withdrawal.path,
script_staking_hash=withdrawal.script_hash,
),
withdrawal,
signing_mode,
protocol_magic,
network_id,
account_path_checker,
previous_reward_address,
)
reward_address = _derive_withdrawal_reward_address_bytes(
keychain, withdrawal, protocol_magic, network_id
)
previous_reward_address = reward_address
await confirm_withdrawal(ctx, withdrawal)
withdrawals_dict.add(reward_address, withdrawal.amount)
@ -649,15 +639,13 @@ async def _process_minting(
await show_warning_tx_contains_mint(ctx)
# until the CIP with canonical CBOR is finalized storing the seen_policy_ids is the only way we can check for
# duplicate policy_ids
seen_policy_ids: set[bytes] = set()
previous_policy_id: bytes = b""
for _ in range(token_minting.asset_groups_count):
asset_group: CardanoAssetGroup = await ctx.call(
CardanoTxItemAck(), CardanoAssetGroup
)
_validate_asset_group(asset_group, seen_policy_ids, is_mint=True)
seen_policy_ids.add(asset_group.policy_id)
_validate_asset_group(asset_group, previous_policy_id, is_mint=True)
previous_policy_id = asset_group.policy_id
tokens: HashBuilderDict[bytes, int] = HashBuilderDict(asset_group.tokens_count)
with minting_dict.add(asset_group.policy_id, tokens):
@ -676,13 +664,11 @@ async def _process_minting_tokens(
tokens_count: int,
) -> None:
"""Read, validate, confirm and serialize the tokens of an asset group."""
# until the CIP with canonical CBOR is finalized storing the seen_asset_name_bytes is the only way we can check for
# duplicate tokens
seen_asset_name_bytes: set[bytes] = set()
previous_asset_name_bytes: bytes = b""
for _ in range(tokens_count):
token: CardanoToken = await ctx.call(CardanoTxItemAck(), CardanoToken)
_validate_token(token, seen_asset_name_bytes, is_mint=True)
seen_asset_name_bytes.add(token.asset_name_bytes)
_validate_token(token, previous_asset_name_bytes, is_mint=True)
previous_asset_name_bytes = token.asset_name_bytes
await confirm_token_minting(ctx, policy_id, token)
assert token.mint_amount is not None # _validate_token
@ -890,7 +876,7 @@ async def _show_output(
def _validate_asset_group(
asset_group: CardanoAssetGroup, seen_policy_ids: set[bytes], is_mint: bool = False
asset_group: CardanoAssetGroup, previous_policy_id: bytes, is_mint: bool = False
) -> None:
INVALID_TOKEN_BUNDLE = (
INVALID_TOKEN_BUNDLE_MINT if is_mint else INVALID_TOKEN_BUNDLE_OUTPUT
@ -900,12 +886,12 @@ def _validate_asset_group(
raise INVALID_TOKEN_BUNDLE
if asset_group.tokens_count == 0:
raise INVALID_TOKEN_BUNDLE
if asset_group.policy_id in seen_policy_ids:
if not cbor.are_canonically_ordered(previous_policy_id, asset_group.policy_id):
raise INVALID_TOKEN_BUNDLE
def _validate_token(
token: CardanoToken, seen_asset_name_bytes: set[bytes], is_mint: bool = False
token: CardanoToken, previous_asset_name_bytes: bytes, is_mint: bool = False
) -> None:
INVALID_TOKEN_BUNDLE = (
INVALID_TOKEN_BUNDLE_MINT if is_mint else INVALID_TOKEN_BUNDLE_OUTPUT
@ -920,7 +906,9 @@ def _validate_token(
if len(token.asset_name_bytes) > MAX_ASSET_NAME_LENGTH:
raise INVALID_TOKEN_BUNDLE
if token.asset_name_bytes in seen_asset_name_bytes:
if not cbor.are_canonically_ordered(
previous_asset_name_bytes, token.asset_name_bytes
):
raise INVALID_TOKEN_BUNDLE
@ -943,10 +931,13 @@ async def _show_certificate(
def _validate_withdrawal(
keychain: seed.Keychain,
withdrawal: CardanoTxWithdrawal,
seen_withdrawals: set[tuple[int, ...] | bytes],
signing_mode: CardanoTxSigningMode,
protocol_magic: int,
network_id: int,
account_path_checker: AccountPathChecker,
previous_reward_address: bytes,
) -> None:
validate_stake_credential(
withdrawal.path, withdrawal.script_hash, signing_mode, INVALID_WITHDRAWAL
@ -958,10 +949,11 @@ def _validate_withdrawal(
credential = tuple(withdrawal.path) if withdrawal.path else withdrawal.script_hash
assert credential # validate_stake_credential
if credential in seen_withdrawals:
raise wire.ProcessError("Duplicate withdrawals")
else:
seen_withdrawals.add(credential)
reward_address = _derive_withdrawal_reward_address_bytes(
keychain, withdrawal, protocol_magic, network_id
)
if not cbor.are_canonically_ordered(previous_reward_address, reward_address):
raise INVALID_WITHDRAWAL
account_path_checker.add_withdrawal(withdrawal)
@ -971,6 +963,29 @@ def _validate_script_data_hash(script_data_hash: bytes) -> None:
raise INVALID_SCRIPT_DATA_HASH
def _derive_withdrawal_reward_address_bytes(
keychain: seed.Keychain,
withdrawal: CardanoTxWithdrawal,
protocol_magic: int,
network_id: int,
) -> bytes:
reward_address_type = (
CardanoAddressType.REWARD
if withdrawal.path
else CardanoAddressType.REWARD_SCRIPT
)
return derive_address_bytes(
keychain,
CardanoAddressParametersType(
address_type=reward_address_type,
address_n_staking=withdrawal.path,
script_staking_hash=withdrawal.script_hash,
),
protocol_magic,
network_id,
)
def _get_output_address(
keychain: seed.Keychain,
protocol_magic: int,

@ -318,3 +318,13 @@ def create_array_header(size: int) -> bytes:
def create_map_header(size: int) -> bytes:
return _header(_CBOR_MAP, size)
def are_canonically_ordered(previous: Value, current: Value) -> bool:
"""
Returns True if `previous` is smaller than `current` with regards to
the cbor map key ordering as defined in
https://datatracker.ietf.org/doc/html/rfc7049#section-3.9
"""
u, v = encode(previous), encode(current)
return len(u) < len(v) or (len(u) == len(v) and u < v)

Loading…
Cancel
Save