feat(cardano): allow address_index in staking paths

pull/3345/head
David Misiak 9 months ago committed by matejcik
parent 8cb6b08377
commit 90d3035590

@ -2005,6 +2005,50 @@
"error_message": "Invalid certificate path"
}
},
{
"description": "Stake deregistration address index larger than 1_000_000",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"validity_interval_start": null,
"certificates": [
{
"type": 1,
"path": "m/1852'/1815'/0'/2/1234567"
}
],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"path": "m/1852'/1815'/190'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
"amount": "1"
}
],
"mint": [],
"script_data_hash": null,
"collateral_inputs": [],
"required_signers": [],
"collateral_return": null,
"total_collateral": null,
"reference_inputs": [],
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"error_message": "Invalid certificate"
}
},
{
"description": "Repeated asset name in multiasset token group",
"parameters": {

@ -2721,6 +2721,169 @@
}
]
}
},
{
"description": "Ordinary transaction with non-zero address index in change output staking path",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"validity_interval_start": null,
"certificates": [],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
"amount": "1"
},
{
"addressType": 0,
"path": "m/1852'/1815'/0'/0/0",
"stakingPath": "m/1852'/1815'/0'/2/1",
"amount": "7120787"
}
],
"mint": [],
"script_data_hash": null,
"collateral_inputs": [],
"required_signers": [],
"collateral_return": null,
"total_collateral": null,
"reference_inputs": [],
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"tx_hash": "f2c5b7bd408add0234e2302d1b46cc72e0af8a88e6d12add95d230c51febdb04",
"witnesses": [
{
"type": 1,
"pub_key": "5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1",
"signature": "0000edbd08c560a00b41f19c23328a8865fd6d37a7e63f99f2c7b6f25dfe524f48b9ec1763a73c6e83c8454828fab1b698998a7a9befbaf27814e2dfea904702",
"chain_code": null
}
]
}
},
{
"description": "Ordinary transaction with non-zero address index in stake registration certificate",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"validity_interval_start": null,
"certificates": [
{
"type": 0,
"path": "m/1852'/1815'/0'/2/1"
}
],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
"amount": "1"
}
],
"mint": [],
"script_data_hash": null,
"collateral_inputs": [],
"required_signers": [],
"collateral_return": null,
"total_collateral": null,
"reference_inputs": [],
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"tx_hash": "62baf68499258a35809faab713420d7d609dd0a1a3bbc5f6e3332917cf5ddece",
"witnesses": [
{
"type": 1,
"pub_key": "5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1",
"signature": "c59fcfbe4973fa0095b2eb3cdc937127664ad9fbf2b70e4eeac63f8b5de1d6b157b2ef105051762bf6132ba425822de30d8c9974c4edd41418d287b46eef6b0f",
"chain_code": null
}
]
}
},
{
"description": "Ordinary transaction with non-zero address index in withdrawal",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"validity_interval_start": null,
"certificates": [],
"withdrawals": [
{
"path": "m/1852'/1815'/0'/2/1",
"amount": "1000"
}
],
"auxiliary_data": null,
"inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
"amount": "1"
}
],
"mint": [],
"script_data_hash": null,
"collateral_inputs": [],
"required_signers": [],
"collateral_return": null,
"total_collateral": null,
"reference_inputs": [],
"signing_mode": "ORDINARY_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"tx_hash": "ac4bdaaf2288a0108654919e7f89811b32274d54da834162e407635db39de4f4",
"witnesses": [
{
"type": 1,
"pub_key": "5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1",
"signature": "de92697a49b52c0bdeb98278198a3679e50f50cdc310ab03dcdb794d72d86a2791a85eaaaa04700996050c7ff787a704d53873cc95a165c83e2a9947a60ecd04",
"chain_code": null
},
{
"type": 1,
"pub_key": "3963e13782d53ac894789a145db59830231d11f84d2df25c7f38db320de20cdc",
"signature": "7293fd496e70730531406830e06ac3d412411fd7568ad33469bbd5d0efa0e0bfe465acf2c2329d97ee35cc32aad5d3d3bb5088d1c8c9808e8b09d1e8810a230a",
"chain_code": null
}
]
}
}
]
}

@ -1103,6 +1103,60 @@
]
}
},
{
"description": "Plutus transaction with base address device-owned output with non-zero staking path address_index",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"validity_interval_start": null,
"certificates": [],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"addressType": 0,
"path": "m/1852'/1815'/0'/0/0",
"stakingPath": "m/1852'/1815'/0'/2/1",
"amount": "7120787"
}
],
"mint": [],
"script_data_hash": "d593fd793c377ac50a3169bb8378ffc257c944da31aa8f355dfa5a4f6ff89e02",
"collateral_inputs": [
{
"path": "m/1852'/1815'/0'/0/0",
"prev_hash": "1af8fa0b754ff99253d983894e63a2b09cbb56c833ba18c3384210163f63dcfc",
"prev_index": 0
}
],
"required_signers": [],
"collateral_return": null,
"total_collateral": null,
"reference_inputs": [],
"signing_mode": "PLUTUS_TRANSACTION",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"tx_hash": "2399f1743e4074d3e18a742898c3e4d5eac66a7284a949ddcd1eac004498720f",
"witnesses": [
{
"type": 1,
"pub_key": "5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1",
"signature": "8bb3d58bff5d999d1fe2fbfe818395be2a4a9e1373ff785148ba925e0aefec9f1dbf43c962ebc14c38b29b5f67935527080b9a2eaa837d9fda8fdce66c922503",
"chain_code": null
}
]
}
},
{
"description": "Plutus transaction with BASE_KEY_SCRIPT address device-owned output",
"parameters": {

@ -493,7 +493,7 @@
"include_network_id": false
},
"result": {
"error_message": "Stakepool registration transaction can only contain staking witnesses"
"error_message": "Stakepool registration transaction can only contain the pool owner witness request"
}
},
{
@ -1080,7 +1080,7 @@
"include_network_id": false
},
"result": {
"error_message": "Stakepool registration transaction can only contain staking witnesses"
"error_message": "Stakepool registration transaction can only contain the pool owner witness request"
}
},
{
@ -1177,7 +1177,7 @@
"include_network_id": false
},
"result": {
"error_message": "Stakepool registration transaction can only contain staking witnesses"
"error_message": "Stakepool registration transaction can only contain the pool owner witness request"
}
},
{
@ -2068,6 +2068,103 @@
"result": {
"error_message": "Invalid tx signing request"
}
},
{
"description": "Sample stake pool registration certificate with additional staking witness request not matching the owner path",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"validity_interval_start": null,
"certificates": [
{
"type": 3,
"pool_parameters": {
"pool_id": "f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973",
"vrf_key_hash": "198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640",
"pledge": 500000000,
"cost": 340000000,
"margin": {
"numerator": 1,
"denominator": 2
},
"reward_account": "stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
"owners": [
{
"staking_key_path": "m/1852'/1815'/0'/2/0"
},
{
"staking_key_hash": "3a7f09d3df4cf66a7399c2b05bfa234d5a29560c311fc5db4c490711"
}
],
"relays": [
{
"type": 0,
"ipv4_address": "192.168.0.1",
"ipv6_address": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"port": 1234
},
{
"type": 0,
"ipv6_address": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"port": 1234
},
{
"type": 0,
"ipv4_address": "192.168.0.1",
"port": 1234
},
{
"type": 1,
"host_name": "www.test.test",
"port": 1234
},
{
"type": 2,
"host_name": "www.test2.test"
}
],
"metadata": {
"url": "https://www.test.test",
"hash": "914c57c1f12bbf4a82b12d977d4f274674856a11ed4b9b95bd70f5d41c5064a6"
}
}
}
],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"path": null,
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
"amount": "1"
}
],
"mint": [],
"script_data_hash": null,
"collateral_inputs": [],
"required_signers": [],
"collateral_return": null,
"total_collateral": null,
"reference_inputs": [],
"signing_mode": "POOL_REGISTRATION_AS_OWNER",
"additional_witness_requests": [
{
"path": "m/1852'/1815'/0'/2/1"
}
],
"include_network_id": false
},
"result": {
"error_message": "Stakepool registration transaction can only contain the pool owner witness request"
}
}
]
}

@ -546,6 +546,107 @@
}
]
}
},
{
"description": "Sample stake pool registration certificate with a non-zero owner staking path address index",
"parameters": {
"protocol_magic": 764824073,
"network_id": 1,
"fee": 42,
"ttl": 10,
"validity_interval_start": null,
"certificates": [
{
"type": 3,
"pool_parameters": {
"pool_id": "f61c42cbf7c8c53af3f520508212ad3e72f674f957fe23ff0acb4973",
"vrf_key_hash": "198890ad6c92e80fbdab554dda02da9fb49d001bbd96181f3e07f7a6ab0d0640",
"pledge": 500000000,
"cost": 340000000,
"margin": {
"numerator": 1,
"denominator": 2
},
"reward_account": "stake1uya87zwnmax0v6nnn8ptqkl6ydx4522kpsc3l3wmf3yswygwx45el",
"owners": [
{
"staking_key_path": "m/1852'/1815'/0'/2/1"
},
{
"staking_key_hash": "3a7f09d3df4cf66a7399c2b05bfa234d5a29560c311fc5db4c490711"
}
],
"relays": [
{
"type": 0,
"ipv4_address": "192.168.0.1",
"ipv6_address": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"port": 1234
},
{
"type": 0,
"ipv6_address": "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
"port": 1234
},
{
"type": 0,
"ipv4_address": "192.168.0.1",
"port": 1234
},
{
"type": 1,
"host_name": "www.test.test",
"port": 1234
},
{
"type": 2,
"host_name": "www.test2.test"
}
],
"metadata": {
"url": "https://www.test.test",
"hash": "914c57c1f12bbf4a82b12d977d4f274674856a11ed4b9b95bd70f5d41c5064a6"
}
}
}
],
"withdrawals": [],
"auxiliary_data": null,
"inputs": [
{
"path": null,
"prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7",
"prev_index": 0
}
],
"outputs": [
{
"address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r",
"amount": "1"
}
],
"mint": [],
"script_data_hash": null,
"collateral_inputs": [],
"required_signers": [],
"collateral_return": null,
"total_collateral": null,
"reference_inputs": [],
"signing_mode": "POOL_REGISTRATION_AS_OWNER",
"additional_witness_requests": [],
"include_network_id": false
},
"result": {
"tx_hash": "36d7e5b9acc9487b9190c3bbe8fa0ec60a6fd66b3c0af7cfd8a015a18b00d765",
"witnesses": [
{
"type": 1,
"pub_key": "3963e13782d53ac894789a145db59830231d11f84d2df25c7f38db320de20cdc",
"signature": "b9c1131865991dbec67da4db2ff315afbf84965c823a893e4821382630d669e7e1b95197743213f24effaf8fee174a4369cfb5db417f20dce4ed40fd42450e0a",
"chain_code": null
}
]
}
}
]
}

@ -91,7 +91,7 @@ Since Alonzo era, network id may be included as an item in the transaction body.
## Key types
In Shelley two types of keys are used. Payment key and staking key. Payment keys are derived from _m/1852'/1815'/x/[0,1]/y_ paths and are used for holding/transferring funds. Staking keys are derived from _m/1852'/1815'/x/2/0_ paths, thus there is only one staking key per account. They are used for staking operations - certificates, withdrawals. Shelley addresses are built from the combination of hashes of these keys.
In Shelley two types of keys are used. Payment key and staking key. Payment keys are derived from _m/1852'/1815'/x/[0,1]/y_ paths and are used for holding/transferring funds. Staking keys are derived from _m/1852'/1815'/x/2/y_ paths (in the past, the only allowed value of `y` was `0`). They are used for staking operations - certificates, withdrawals. Shelley addresses are built from the combination of hashes of these keys.
[Multi-sig paths (1854')](https://cips.cardano.org/cips/cip1854/) are used to generate keys which should be used in native scripts and also to sign multi-sig transactions. [Minting paths (1855')](https://cips.cardano.org/cips/cip1855/) are used for creating minting policies and for witnessing minting transactions.

@ -220,5 +220,7 @@ def _do_base_address_credentials_match(
from .paths import CHAIN_STAKING_KEY
from .utils import to_account_path
# Note: This checks that the account matches and the staking path address_index is 0.
# (Even though other values are allowed, we want to display them to the user.)
path_to_staking_path = to_account_path(address_n) + [CHAIN_STAKING_KEY, 0]
return address_n_staking == path_to_staking_path

@ -15,14 +15,17 @@ SCHEMA_PUBKEY = PathSchema.parse("m/[44,1852,1854]'/coin_type'/account'/*", _SLI
SCHEMA_MINT = PathSchema.parse(f"m/1855'/coin_type'/[0-{HARDENED - 1}]'", _SLIP44_ID)
SCHEMA_PAYMENT = PathSchema.parse("m/[44,1852]'/coin_type'/account'/[0,1]/address_index", _SLIP44_ID)
# staking is only allowed on Shelley paths with suffix /2/0
SCHEMA_STAKING = PathSchema.parse("m/1852'/coin_type'/account'/2/0", _SLIP44_ID)
SCHEMA_STAKING_ANY_ACCOUNT = PathSchema.parse(f"m/1852'/coin_type'/[0-{HARDENED - 1}]'/2/0", _SLIP44_ID)
SCHEMA_STAKING = PathSchema.parse("m/1852'/coin_type'/account'/2/address_index", _SLIP44_ID)
SCHEMA_STAKING_ANY_ACCOUNT = PathSchema.parse(f"m/1852'/coin_type'/[0-{HARDENED - 1}]'/2/address_index", _SLIP44_ID)
# fmt: on
ACCOUNT_PATH_INDEX = const(2)
ACCOUNT_PATH_LENGTH = const(3)
CHAIN_STAKING_KEY = const(2)
ADDRESS_INDEX_PATH_INDEX = const(4)
RECOMMENDED_ADDRESS_INDEX = const(0) # https://cips.cardano.org/cips/cip11/
CHANGE_OUTPUT_PATH_NAME = "Change output path"
CHANGE_OUTPUT_STAKING_PATH_NAME = "Change output staking path"
CERTIFICATE_PATH_NAME = "Certificate path"

@ -707,12 +707,19 @@ async def confirm_withdrawal(
def _format_stake_credential(
path: list[int], script_hash: bytes | None, key_hash: bytes | None
) -> tuple[str, str]:
from .helpers.utils import to_account_path
from .helpers.paths import ADDRESS_INDEX_PATH_INDEX, RECOMMENDED_ADDRESS_INDEX
if path:
account_number = format_account_number(path)
address_index = path[ADDRESS_INDEX_PATH_INDEX]
if address_index == RECOMMENDED_ADDRESS_INDEX:
return (
f"for account {account_number}:",
address_n_to_str(path),
)
return (
f"for account {format_account_number(path)}:",
address_n_to_str(to_account_path(path)),
f"for account {account_number} and index {address_index}:",
address_n_to_str(path),
)
elif key_hash:
return ("for key hash:", bech32.encode(bech32.HRP_STAKE_KEY_HASH, key_hash))

@ -78,9 +78,12 @@ class PoolOwnerSigner(Signer):
from ..helpers.paths import SCHEMA_STAKING_ANY_ACCOUNT
super()._validate_witness_request(witness_request)
if not SCHEMA_STAKING_ANY_ACCOUNT.match(witness_request.path):
if not (
SCHEMA_STAKING_ANY_ACCOUNT.match(witness_request.path)
and witness_request.path == self.pool_owner_path
):
raise ProcessError(
"Stakepool registration transaction can only contain staking witnesses"
"Stakepool registration transaction can only contain the pool owner witness request"
)
def _is_network_id_verifiable(self) -> bool:

@ -88,6 +88,9 @@ class Signer:
self.account_path_checker = AccountPathChecker()
# There should be at most one pool owner given as a path.
self.pool_owner_path = None
# Inputs, outputs and fee are mandatory, count the number of optional fields present.
tx_dict_items_count = 3 + sum(
(
@ -747,6 +750,7 @@ class Signer:
if owner.staking_key_path:
owners_as_path_count += 1
self.pool_owner_path = owner.staking_key_path
certificates.assert_cond(owners_as_path_count == 1)

Loading…
Cancel
Save