diff --git a/common/protob/messages-cardano.proto b/common/protob/messages-cardano.proto index 5b8e6c31d..f7e5c6496 100644 --- a/common/protob/messages-cardano.proto +++ b/common/protob/messages-cardano.proto @@ -206,6 +206,7 @@ message CardanoSignTxInit { required uint32 minting_asset_groups_count = 13; required CardanoDerivationType derivation_type = 14; optional bool include_network_id = 15 [default=false]; // network id included as tx body item + optional bytes script_data_hash = 16; } /** @@ -226,6 +227,7 @@ message CardanoTxOutput { optional CardanoAddressParametersType address_parameters = 2; // parameters used to derive the address required uint64 amount = 3; // amount to spend required uint32 asset_groups_count = 4; + optional bytes datum_hash = 5; } /** diff --git a/common/tests/fixtures/cardano/sign_tx.failed.json b/common/tests/fixtures/cardano/sign_tx.failed.json index eec5e1802..de2a09153 100644 --- a/common/tests/fixtures/cardano/sign_tx.failed.json +++ b/common/tests/fixtures/cardano/sign_tx.failed.json @@ -28,6 +28,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -60,6 +61,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -92,6 +94,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -124,6 +127,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -156,6 +160,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -188,6 +193,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -225,6 +231,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -257,6 +264,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -289,6 +297,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -321,6 +330,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -353,6 +363,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -385,6 +396,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -417,6 +429,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -449,6 +462,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -484,6 +498,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -521,6 +536,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -558,6 +574,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -595,6 +612,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -633,6 +651,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -671,6 +690,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -708,6 +728,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -745,6 +766,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -782,6 +804,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -819,6 +842,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -857,6 +881,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -898,6 +923,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -932,6 +958,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -974,6 +1001,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1018,6 +1046,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1026,6 +1055,143 @@ "error_message": "Invalid auxiliary data" } }, + { + "description": "Output datum hash has incorrect length", + "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": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r", + "amount": "1", + "datum_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3" + } + ], + "mint": [], + "script_data_hash": null, + "signing_mode": "ORDINARY_TRANSACTION", + "additional_witness_requests": [], + "include_network_id": false + }, + "result": { + "error_message": "Invalid output datum hash" + } + }, + { + "description": "Output datum hash with non-script address", + "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": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r", + "amount": "1", + "datum_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7" + } + ], + "mint": [], + "script_data_hash": null, + "signing_mode": "ORDINARY_TRANSACTION", + "additional_witness_requests": [], + "include_network_id": false + }, + "result": { + "error_message": "Invalid output" + } + }, + { + "description": "Output datum hash with non-script address parameters", + "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": [ + { + "addressType": 2, + "path": "m/1852'/1815'/0'/0/0", + "scriptStakingHash": "8d7bebc7a58f1c7b5fb7c9391071ecd3b51b032695522f8c555343a9", + "amount": "7120787", + "datum_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7" + } + ], + "mint": [], + "script_data_hash": null, + "signing_mode": "ORDINARY_TRANSACTION", + "additional_witness_requests": [], + "include_network_id": false + }, + "result": { + "error_message": "Invalid output" + } + }, + { + "description": "Script data hash has incorrect length", + "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": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r", + "amount": "3003112" + } + ], + "mint": [], + "script_data_hash": "d593fd793c377ac50a3169bb8378ffc257c944da31aa8f355dfa5a4f", + "signing_mode": "ORDINARY_TRANSACTION", + "additional_witness_requests": [], + "include_network_id": false + }, + "result": { + "error_message": "Invalid script data hash" + } + }, { "description": "Change output path larger than 100", "parameters": { @@ -1056,6 +1222,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1094,6 +1261,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1132,6 +1300,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1164,6 +1333,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1201,6 +1371,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1248,6 +1419,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1300,6 +1472,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1332,6 +1505,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [ { @@ -1368,6 +1542,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [ { @@ -1406,6 +1581,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1486,6 +1662,7 @@ ] } ], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [ { @@ -1523,6 +1700,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [ { @@ -1611,6 +1789,7 @@ ] } ], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [ { @@ -1653,6 +1832,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1690,6 +1870,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1727,6 +1908,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1770,6 +1952,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1813,6 +1996,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1855,6 +2039,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1891,6 +2076,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1924,6 +2110,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false diff --git a/common/tests/fixtures/cardano/sign_tx.json b/common/tests/fixtures/cardano/sign_tx.json index f55304ea2..0e894265c 100644 --- a/common/tests/fixtures/cardano/sign_tx.json +++ b/common/tests/fixtures/cardano/sign_tx.json @@ -28,6 +28,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -73,6 +74,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -119,6 +121,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -174,6 +177,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -238,6 +242,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -285,6 +290,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -332,6 +338,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -378,6 +385,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -426,6 +434,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -471,6 +480,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -516,6 +526,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -556,6 +567,7 @@ ], "outputs": [], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -606,6 +618,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -657,6 +670,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -713,6 +727,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -765,6 +780,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -813,6 +829,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -864,6 +881,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -918,6 +936,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -976,6 +995,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1047,6 +1067,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1088,6 +1109,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1138,6 +1160,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1190,6 +1213,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1237,6 +1261,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1375,6 +1400,7 @@ } }, "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -1490,6 +1516,7 @@ ] } ], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [ { @@ -1540,6 +1567,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": true @@ -1555,6 +1583,130 @@ } ] } + }, + { + "description": "Ordinary transaction with output datum hash", + "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": "addr1w9rhu54nz94k9l5v6d9rzfs47h7dv7xffcwkekuxcx3evnqpvuxu0", + "amount": "1", + "datum_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7" + } + ], + "mint": [], + "script_data_hash": null, + "signing_mode": "ORDINARY_TRANSACTION", + "additional_witness_requests": [], + "include_network_id": false + }, + "result": { + "tx_hash": "8ea2765f1e46d84f02d8b25a5f0cf445aaeaadcab913e17e59388a4f898ca812", + "witnesses": [ + { + "type": 1, + "pub_key": "5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1", + "signature": "ccc4e3c2adbf63561881212c8dffd42a02850460256da9b393aaed2cbd131fbb2798a92a2adf59c31d22e1e33c3dad011d91e09aa2d5b15ba64fa995bf241900", + "chain_code": null + } + ] + } + }, + { + "description": "Ordinary transaction with script address but no datum hash", + "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": "addr1w9rhu54nz94k9l5v6d9rzfs47h7dv7xffcwkekuxcx3evnqpvuxu0", + "amount": "1" + } + ], + "mint": [], + "script_data_hash": null, + "signing_mode": "ORDINARY_TRANSACTION", + "additional_witness_requests": [], + "include_network_id": false + }, + "result": { + "tx_hash": "06d6587ddd79e000b6922c54069b78bb931b0cba06d97ba4171f3ce6590438d2", + "witnesses": [ + { + "type": 1, + "pub_key": "5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1", + "signature": "4b7e694b8a31905918561eedc376aaa2f7827f19e228a9e28b5f56ee55ea61725752c50c2f1c67b79de747aeb8e5f1559ce211107a399938b954121d4c085f07", + "chain_code": null + } + ] + } + }, + { + "description": "Ordinary transaction with script data hash", + "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": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r", + "amount": "1" + } + ], + "mint": [], + "script_data_hash": "d593fd793c377ac50a3169bb8378ffc257c944da31aa8f355dfa5a4f6ff89e02", + "signing_mode": "ORDINARY_TRANSACTION", + "additional_witness_requests": [], + "include_network_id": false + }, + "result": { + "tx_hash": "8606e5b69b5c40bd359d7bad6ed6f77810b8e8acba6cbca298c13f92b11178d4", + "witnesses": [ + { + "type": 1, + "pub_key": "5d010cf16fdeff40955633d6c565f3844a288a24967cf6b76acbeb271b4f13c1", + "signature": "c8676cf593554f347e2d6a5ffc6a1371638156eaaddb3e7f94ae9a7488a6adb01c3660d8aff1da08e01c4899615158e69c5c797841ef7747740ab56d3b452f0c", + "chain_code": null + } + ] + } } ] } diff --git a/common/tests/fixtures/cardano/sign_tx.multisig.failed.json b/common/tests/fixtures/cardano/sign_tx.multisig.failed.json index d73f78cf4..6f4caa07e 100644 --- a/common/tests/fixtures/cardano/sign_tx.multisig.failed.json +++ b/common/tests/fixtures/cardano/sign_tx.multisig.failed.json @@ -32,6 +32,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { @@ -72,6 +73,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { @@ -116,6 +118,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { @@ -151,6 +154,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { @@ -188,6 +192,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { @@ -223,6 +228,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { @@ -310,6 +316,7 @@ ] } ], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { diff --git a/common/tests/fixtures/cardano/sign_tx.multisig.json b/common/tests/fixtures/cardano/sign_tx.multisig.json index 075c488f2..e0f16a6c9 100644 --- a/common/tests/fixtures/cardano/sign_tx.multisig.json +++ b/common/tests/fixtures/cardano/sign_tx.multisig.json @@ -79,6 +79,7 @@ ] } ], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { @@ -136,6 +137,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { @@ -189,6 +191,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { @@ -246,6 +249,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { @@ -308,6 +312,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { @@ -407,6 +412,7 @@ ] } ], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [ { @@ -444,6 +450,95 @@ } ] } + }, + { + "description": "Multisig transaction with output datum hash", + "parameters": { + "protocol_magic": 764824073, + "network_id": 1, + "fee": 42, + "ttl": 10, + "certificates": [], + "withdrawals": [], + "auxiliary_data": null, + "inputs": [ + { + "prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7", + "prev_index": 0 + } + ], + "outputs": [ + { + "address": "addr1w9rhu54nz94k9l5v6d9rzfs47h7dv7xffcwkekuxcx3evnqpvuxu0", + "amount": "1", + "datum_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7" + } + ], + "mint": [], + "script_data_hash": null, + "signing_mode": "MULTISIG_TRANSACTION", + "additional_witness_requests": [ + { + "path": "m/1854'/1815'/0'/0/0" + } + ], + "include_network_id": false + }, + "result": { + "tx_hash": "8ea2765f1e46d84f02d8b25a5f0cf445aaeaadcab913e17e59388a4f898ca812", + "witnesses": [ + { + "type": 1, + "pub_key": "b10be5c0d11ad8292bbe69e220ca0cfbe154610b3041a8e72f9d515c226ab3b1", + "signature": "186bd74158d42d1703ac97171f09c50052a6ce6e9dad38520aa38c25c94df6857a6be3c930464ba64884cc8cdd01d05c533d9e9026b2ba50afbe93752bfd0106", + "chain_code": null + } + ] + } + }, + { + "description": "Multisig transaction with script data hash", + "parameters": { + "protocol_magic": 764824073, + "network_id": 1, + "fee": 42, + "ttl": 10, + "certificates": [], + "withdrawals": [], + "auxiliary_data": null, + "inputs": [ + { + "prev_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7", + "prev_index": 0 + } + ], + "outputs": [ + { + "address": "addr1q84sh2j72ux0l03fxndjnhctdg7hcppsaejafsa84vh7lwgmcs5wgus8qt4atk45lvt4xfxpjtwfhdmvchdf2m3u3hlsd5tq5r", + "amount": "1" + } + ], + "mint": [], + "script_data_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7", + "signing_mode": "MULTISIG_TRANSACTION", + "additional_witness_requests": [ + { + "path": "m/1854'/1815'/0'/0/0" + } + ], + "include_network_id": false + }, + "result": { + "tx_hash": "ee065ce429d8dbcc2432aa81706877f2eee4fc5031d1e1ae06fe993b9987b1be", + "witnesses": [ + { + "type": 1, + "pub_key": "b10be5c0d11ad8292bbe69e220ca0cfbe154610b3041a8e72f9d515c226ab3b1", + "signature": "88ce2f590b662688ee626dd2ee471ec6e55cfac467c12645ef10c582a539dd85a1bac7ff7b95a59354576f604a031a28de8817c51eff88ec7d43cb00e824d90b", + "chain_code": null + } + ] + } } ] } diff --git a/common/tests/fixtures/cardano/sign_tx.slip39.json b/common/tests/fixtures/cardano/sign_tx.slip39.json index 7f00d9872..696cc9c93 100644 --- a/common/tests/fixtures/cardano/sign_tx.slip39.json +++ b/common/tests/fixtures/cardano/sign_tx.slip39.json @@ -32,6 +32,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -77,6 +78,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -122,6 +124,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false diff --git a/common/tests/fixtures/cardano/sign_tx_stake_pool_registration.failed.json b/common/tests/fixtures/cardano/sign_tx_stake_pool_registration.failed.json index 45e002f8d..2e8853fb3 100644 --- a/common/tests/fixtures/cardano/sign_tx_stake_pool_registration.failed.json +++ b/common/tests/fixtures/cardano/sign_tx_stake_pool_registration.failed.json @@ -49,6 +49,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -105,6 +106,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -158,6 +160,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -211,6 +214,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -285,6 +289,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -321,6 +326,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -379,6 +385,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -432,6 +439,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -484,6 +492,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -536,6 +545,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -622,6 +632,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -708,6 +719,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "ORDINARY_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -794,6 +806,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "MULTISIG_TRANSACTION", "additional_witness_requests": [], "include_network_id": false @@ -880,6 +893,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [ { @@ -970,6 +984,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [ { @@ -1087,6 +1102,7 @@ ] } ], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [ { @@ -1179,6 +1195,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -1186,6 +1203,181 @@ "result": { "error_message": "Invalid output" } + }, + { + "description": "Sample stake pool registration certificate with output datum hash", + "parameters": { + "protocol_magic": 764824073, + "network_id": 1, + "fee": 42, + "ttl": 10, + "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": "addr1w9rhu54nz94k9l5v6d9rzfs47h7dv7xffcwkekuxcx3evnqpvuxu0", + "amount": "1", + "datum_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7" + } + ], + "mint": [], + "script_data_hash": null, + "signing_mode": "POOL_REGISTRATION_AS_OWNER", + "additional_witness_requests": [], + "include_network_id": false + }, + "result": { + "error_message": "Invalid output" + } + }, + { + "description": "Sample stake pool registration certificate with script data hash", + "parameters": { + "protocol_magic": 764824073, + "network_id": 1, + "fee": 42, + "ttl": 10, + "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": "addr1w9rhu54nz94k9l5v6d9rzfs47h7dv7xffcwkekuxcx3evnqpvuxu0", + "amount": "1" + } + ], + "mint": [], + "script_data_hash": "3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7", + "signing_mode": "POOL_REGISTRATION_AS_OWNER", + "additional_witness_requests": [], + "include_network_id": false + }, + "result": { + "error_message": "Invalid tx signing request" + } } ] } diff --git a/common/tests/fixtures/cardano/sign_tx_stake_pool_registration.json b/common/tests/fixtures/cardano/sign_tx_stake_pool_registration.json index 98e4d329e..c0362f7a6 100644 --- a/common/tests/fixtures/cardano/sign_tx_stake_pool_registration.json +++ b/common/tests/fixtures/cardano/sign_tx_stake_pool_registration.json @@ -82,6 +82,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -176,6 +177,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -271,6 +273,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -332,6 +335,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -393,6 +397,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false @@ -489,6 +494,7 @@ } ], "mint": [], + "script_data_hash": null, "signing_mode": "POOL_REGISTRATION_AS_OWNER", "additional_witness_requests": [], "include_network_id": false diff --git a/core/src/apps/cardano/address.py b/core/src/apps/cardano/address.py index 0765e1d40..c278d1d0a 100644 --- a/core/src/apps/cardano/address.py +++ b/core/src/apps/cardano/address.py @@ -39,6 +39,13 @@ ADDRESS_TYPES_SHELLEY = ( CardanoAddressType.REWARD_SCRIPT, ) +ADDRESS_TYPES_PAYMENT_SCRIPT = ( + CardanoAddressType.BASE_SCRIPT_KEY, + CardanoAddressType.BASE_SCRIPT_SCRIPT, + CardanoAddressType.POINTER_SCRIPT, + CardanoAddressType.ENTERPRISE_SCRIPT, +) + MIN_ADDRESS_BYTES_LENGTH = 29 MAX_ADDRESS_BYTES_LENGTH = 65 @@ -240,7 +247,7 @@ def _validate_address_and_get_type( raise INVALID_ADDRESS address_bytes = get_address_bytes_unsafe(address) - address_type = _get_address_type(address_bytes) + address_type = get_address_type(address_bytes) if address_type == CardanoAddressType.BYRON: validate_byron_address(address_bytes, protocol_magic) @@ -281,14 +288,14 @@ def get_address_bytes_unsafe(address: str) -> bytes: return address_bytes -def _get_address_type(address: bytes) -> CardanoAddressType: +def get_address_type(address: bytes) -> CardanoAddressType: return address[0] >> 4 # type: ignore [int-into-enum] def _validate_shelley_address( address_str: str, address_bytes: bytes, network_id: int ) -> None: - address_type = _get_address_type(address_bytes) + address_type = get_address_type(address_bytes) _validate_address_size(address_bytes) _validate_address_bech32_hrp(address_str, address_type, network_id) @@ -352,7 +359,7 @@ def derive_human_readable_address( def encode_human_readable_address(address_bytes: bytes) -> str: - address_type = _get_address_type(address_bytes) + address_type = get_address_type(address_bytes) if address_type == CardanoAddressType.BYRON: return base58.encode(address_bytes) elif address_type in ADDRESS_TYPES_SHELLEY: diff --git a/core/src/apps/cardano/helpers/__init__.py b/core/src/apps/cardano/helpers/__init__.py index 9b30e0c57..9bedb9ed2 100644 --- a/core/src/apps/cardano/helpers/__init__.py +++ b/core/src/apps/cardano/helpers/__init__.py @@ -18,7 +18,11 @@ INVALID_STAKEPOOL_REGISTRATION_TX_WITNESSES = wire.ProcessError( INVALID_WITNESS_REQUEST = wire.ProcessError("Invalid witness request") INVALID_NATIVE_SCRIPT = wire.ProcessError("Invalid native script") INVALID_TOKEN_BUNDLE_MINT = wire.ProcessError("Invalid mint token bundle") +INVALID_OUTPUT_DATUM_HASH = wire.ProcessError("Invalid output datum hash") +INVALID_SCRIPT_DATA_HASH = wire.ProcessError("Invalid script data hash") LOVELACE_MAX_SUPPLY = 45_000_000_000 * 1_000_000 ADDRESS_KEY_HASH_SIZE = 28 SCRIPT_HASH_SIZE = 28 +OUTPUT_DATUM_HASH_SIZE = 32 +SCRIPT_DATA_HASH_SIZE = 32 diff --git a/core/src/apps/cardano/helpers/bech32.py b/core/src/apps/cardano/helpers/bech32.py index 5b70ad38f..46ebe07ae 100644 --- a/core/src/apps/cardano/helpers/bech32.py +++ b/core/src/apps/cardano/helpers/bech32.py @@ -12,6 +12,8 @@ HRP_JORMUN_PUBLIC_KEY = "ed25519_pk" HRP_SCRIPT_HASH = "script" HRP_KEY_HASH = "addr_vkh" HRP_SHARED_KEY_HASH = "addr_shared_vkh" +HRP_OUTPUT_DATUM_HASH = "datum" +HRP_SCRIPT_DATA_HASH = "script_data" def encode(hrp: str, data: bytes) -> str: diff --git a/core/src/apps/cardano/helpers/utils.py b/core/src/apps/cardano/helpers/utils.py index 54b4b4b98..bf43ef06c 100644 --- a/core/src/apps/cardano/helpers/utils.py +++ b/core/src/apps/cardano/helpers/utils.py @@ -76,6 +76,14 @@ def format_key_hash(key_hash: bytes, is_shared_key: bool) -> str: return bech32.encode(hrp, key_hash) +def format_output_datum_hash(output_datum_hash: bytes) -> str: + return bech32.encode(bech32.HRP_OUTPUT_DATUM_HASH, output_datum_hash) + + +def format_script_data_hash(script_data_hash: bytes) -> str: + return bech32.encode(bech32.HRP_SCRIPT_DATA_HASH, script_data_hash) + + 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() diff --git a/core/src/apps/cardano/layout.py b/core/src/apps/cardano/layout.py index 6f4b14c18..cc66b5ed2 100644 --- a/core/src/apps/cardano/layout.py +++ b/core/src/apps/cardano/layout.py @@ -31,6 +31,8 @@ from .helpers.utils import ( format_asset_fingerprint, format_key_hash, format_optional_int, + format_output_datum_hash, + format_script_data_hash, format_script_hash, format_stake_pool_id, to_account_path, @@ -332,6 +334,34 @@ async def show_warning_tx_output_contains_tokens(ctx: wire.Context) -> None: ) +async def show_warning_tx_output_contains_datum_hash( + ctx: wire.Context, datum_hash: bytes +) -> None: + await confirm_properties( + ctx, + "confirm_datum_hash", + title="Confirm transaction", + props=[ + ( + "The following transaction output contains datum hash:", + format_output_datum_hash(datum_hash), + ), + ("\nContinue?", None), + ], + br_code=ButtonRequestType.Other, + ) + + +async def show_warning_tx_output_no_datum_hash(ctx: wire.Context) -> None: + await confirm_metadata( + ctx, + "confirm_no_datum_hash", + title="Confirm transaction", + content="The following transaction output contains a script address, but does not contain a datum hash.", + br_code=ButtonRequestType.Other, + ) + + async def confirm_witness_request( ctx: wire.Context, witness_path: list[int], @@ -654,6 +684,16 @@ async def show_warning_tx_network_unverifiable(ctx: wire.Context) -> None: ) +async def confirm_script_data_hash(ctx: wire.Context, script_data_hash: bytes) -> None: + await confirm_properties( + ctx, + "confirm_script_data_hash", + title="Confirm transaction", + props=[("Script data hash:", format_script_data_hash(script_data_hash))], + br_code=ButtonRequestType.Other, + ) + + async def show_cardano_address( ctx: wire.Context, address_parameters: CardanoAddressParametersType, diff --git a/core/src/apps/cardano/native_script.py b/core/src/apps/cardano/native_script.py index a6df35060..54106f1f1 100644 --- a/core/src/apps/cardano/native_script.py +++ b/core/src/apps/cardano/native_script.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING from trezor.crypto import hashlib -from trezor.enums import CardanoAddressType, CardanoNativeScriptType +from trezor.enums import CardanoNativeScriptType from apps.cardano.helpers import ( ADDRESS_KEY_HASH_SIZE, @@ -21,15 +21,6 @@ if TYPE_CHECKING: from apps.common.cbor import CborSequence -SCRIPT_ADDRESS_TYPES = ( - CardanoAddressType.BASE_SCRIPT_KEY, - CardanoAddressType.BASE_KEY_SCRIPT, - CardanoAddressType.BASE_SCRIPT_SCRIPT, - CardanoAddressType.POINTER_SCRIPT, - CardanoAddressType.ENTERPRISE_SCRIPT, - CardanoAddressType.REWARD_SCRIPT, -) - def validate_native_script(script: CardanoNativeScript | None) -> None: if not script: diff --git a/core/src/apps/cardano/sign_tx.py b/core/src/apps/cardano/sign_tx.py index 710946123..9f94d6510 100644 --- a/core/src/apps/cardano/sign_tx.py +++ b/core/src/apps/cardano/sign_tx.py @@ -35,9 +35,11 @@ from apps.common import cbor, safety_checks from . import seed from .address import ( + ADDRESS_TYPES_PAYMENT_SCRIPT, derive_address_bytes, derive_human_readable_address, get_address_bytes_unsafe, + get_address_type, validate_output_address, validate_output_address_parameters, ) @@ -59,6 +61,8 @@ from .certificates import ( ) from .helpers import ( INVALID_OUTPUT, + INVALID_OUTPUT_DATUM_HASH, + INVALID_SCRIPT_DATA_HASH, INVALID_STAKE_POOL_REGISTRATION_TX_STRUCTURE, INVALID_STAKEPOOL_REGISTRATION_TX_WITNESSES, INVALID_TOKEN_BUNDLE_MINT, @@ -67,6 +71,8 @@ from .helpers import ( INVALID_WITHDRAWAL, INVALID_WITNESS_REQUEST, LOVELACE_MAX_SUPPLY, + OUTPUT_DATUM_HASH_SIZE, + SCRIPT_DATA_HASH_SIZE, network_ids, protocol_magics, ) @@ -87,6 +93,7 @@ from .helpers.paths import ( from .helpers.utils import derive_public_key, validate_stake_credential from .layout import ( confirm_certificate, + confirm_script_data_hash, confirm_sending, confirm_sending_token, confirm_stake_pool_metadata, @@ -102,7 +109,9 @@ from .layout import ( show_warning_path, show_warning_tx_contains_mint, show_warning_tx_network_unverifiable, + show_warning_tx_output_contains_datum_hash, show_warning_tx_output_contains_tokens, + show_warning_tx_output_no_datum_hash, ) from .seed import is_byron_path, is_multisig_path, is_shelley_path @@ -124,6 +133,7 @@ TX_BODY_KEY_WITHDRAWALS = const(5) TX_BODY_KEY_AUXILIARY_DATA = const(7) TX_BODY_KEY_VALIDITY_INTERVAL_START = const(8) TX_BODY_KEY_MINT = const(9) +TX_BODY_KEY_SCRIPT_DATA_HASH = const(11) TX_BODY_KEY_NETWORK_ID = const(15) POOL_REGISTRATION_CERTIFICATE_ITEMS_COUNT = 10 @@ -147,6 +157,7 @@ async def sign_tx( msg.validity_interval_start is not None, msg.minting_asset_groups_count > 0, msg.include_network_id, + msg.script_data_hash is not None, ) ) @@ -203,6 +214,12 @@ async def _validate_tx_signing_request( else: raise INVALID_TX_SIGNING_REQUEST + if msg.script_data_hash is not None and msg.signing_mode not in ( + CardanoTxSigningMode.ORDINARY_TRANSACTION, + CardanoTxSigningMode.MULTISIG_TRANSACTION, + ): + raise INVALID_TX_SIGNING_REQUEST + return is_network_id_verifiable @@ -284,6 +301,9 @@ async def _process_transaction( with tx_dict.add(TX_BODY_KEY_MINT, minting_dict): await _process_minting(ctx, minting_dict) + if msg.script_data_hash is not None: + await _process_script_data_hash(ctx, tx_dict, msg.script_data_hash) + if msg.include_network_id: tx_dict.add(TX_BODY_KEY_NETWORK_ID, msg.network_id) @@ -360,13 +380,15 @@ async def _process_outputs( keychain, protocol_magic, network_id, output ) - if output.asset_groups_count == 0: - outputs_list.append((output_address, output.amount)) - else: - # output structure is: [address, [amount, asset_groups]] - output_list: HashBuilderList = HashBuilderList(2) - with outputs_list.append(output_list): - output_list.append(output_address) + has_datum_hash = output.datum_hash is not None + output_list: HashBuilderList = HashBuilderList(2 + int(has_datum_hash)) + with outputs_list.append(output_list): + output_list.append(output_address) + if output.asset_groups_count == 0: + # output structure is: [address, amount, datum_hash?] + output_list.append(output.amount) + else: + # output structure is: [address, [amount, asset_groups], datum_hash?] output_value_list: HashBuilderList = HashBuilderList(2) with output_list.append(output_value_list): output_value_list.append(output.amount) @@ -380,6 +402,8 @@ async def _process_outputs( output.asset_groups_count, should_show_output, ) + if has_datum_hash: + output_list.append(output.datum_hash) total_amount += output.amount @@ -662,6 +686,19 @@ async def _process_minting_tokens( tokens.add(token.asset_name_bytes, token.mint_amount) +async def _process_script_data_hash( + ctx: wire.Context, + tx_body_builder_dict: HashBuilderDict, + script_data_hash: bytes, +) -> None: + """Validate, confirm and serialize the script data hash.""" + _validate_script_data_hash(script_data_hash) + + await confirm_script_data_hash(ctx, script_data_hash) + + tx_body_builder_dict.add(TX_BODY_KEY_SCRIPT_DATA_HASH, script_data_hash) + + async def _process_witness_requests( ctx: wire.Context, keychain: seed.Keychain, @@ -762,6 +799,18 @@ def _validate_output( else: raise INVALID_OUTPUT + if output.datum_hash is not None: + if signing_mode not in ( + CardanoTxSigningMode.ORDINARY_TRANSACTION, + CardanoTxSigningMode.MULTISIG_TRANSACTION, + ): + raise INVALID_OUTPUT + if len(output.datum_hash) != OUTPUT_DATUM_HASH_SIZE: + raise INVALID_OUTPUT_DATUM_HASH + address_type = _get_output_address_type(output) + if address_type not in ADDRESS_TYPES_PAYMENT_SCRIPT: + raise INVALID_OUTPUT + account_path_checker.add_output(output) @@ -769,6 +818,16 @@ def _should_show_output( output: CardanoTxOutput, signing_mode: CardanoTxSigningMode, ) -> bool: + if output.datum_hash: + # none of the reasons for hiding below should be reachable when datum hash + # is present, but let's make sure + return True + + address_type = _get_output_address_type(output) + if output.datum_hash is None and address_type in ADDRESS_TYPES_PAYMENT_SCRIPT: + # Plutus script address without a datum hash is unspendable, we must show a warning + return True + 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. @@ -789,9 +848,16 @@ async def _show_output( protocol_magic: int, network_id: int, ) -> None: + if output.datum_hash: + await show_warning_tx_output_contains_datum_hash(ctx, output.datum_hash) + if output.asset_groups_count > 0: await show_warning_tx_output_contains_tokens(ctx) + address_type = _get_output_address_type(output) + if output.datum_hash is None and address_type in ADDRESS_TYPES_PAYMENT_SCRIPT: + await show_warning_tx_output_no_datum_hash(ctx) + is_change_output: bool if address_parameters := output.address_parameters: is_change_output = True @@ -892,6 +958,11 @@ def _validate_withdrawal( account_path_checker.add_withdrawal(withdrawal) +def _validate_script_data_hash(script_data_hash: bytes) -> None: + if len(script_data_hash) != SCRIPT_DATA_HASH_SIZE: + raise INVALID_SCRIPT_DATA_HASH + + def _get_output_address( keychain: seed.Keychain, protocol_magic: int, @@ -907,6 +978,13 @@ def _get_output_address( return get_address_bytes_unsafe(output.address) +def _get_output_address_type(output: CardanoTxOutput) -> CardanoAddressType: + if output.address_parameters: + return output.address_parameters.address_type + assert output.address is not None # _validate_output + return get_address_type(get_address_bytes_unsafe(output.address)) + + def _sign_tx_hash( keychain: seed.Keychain, tx_body_hash: bytes, path: list[int] ) -> bytes: diff --git a/core/src/trezor/messages.py b/core/src/trezor/messages.py index ed88875fa..422d4fe81 100644 --- a/core/src/trezor/messages.py +++ b/core/src/trezor/messages.py @@ -1332,6 +1332,7 @@ if TYPE_CHECKING: minting_asset_groups_count: "int" derivation_type: "CardanoDerivationType" include_network_id: "bool" + script_data_hash: "bytes | None" def __init__( self, @@ -1351,6 +1352,7 @@ if TYPE_CHECKING: ttl: "int | None" = None, validity_interval_start: "int | None" = None, include_network_id: "bool | None" = None, + script_data_hash: "bytes | None" = None, ) -> None: pass @@ -1379,6 +1381,7 @@ if TYPE_CHECKING: address_parameters: "CardanoAddressParametersType | None" amount: "int" asset_groups_count: "int" + datum_hash: "bytes | None" def __init__( self, @@ -1387,6 +1390,7 @@ if TYPE_CHECKING: asset_groups_count: "int", address: "str | None" = None, address_parameters: "CardanoAddressParametersType | None" = None, + datum_hash: "bytes | None" = None, ) -> None: pass diff --git a/python/src/trezorlib/cardano.py b/python/src/trezorlib/cardano.py index 708b120a5..2861f9cb0 100644 --- a/python/src/trezorlib/cardano.py +++ b/python/src/trezorlib/cardano.py @@ -172,27 +172,27 @@ def parse_output(output: dict) -> OutputWithAssetGroups: if not (contains_address or contains_address_type): raise ValueError(INCOMPLETE_OUTPUT_ERROR_MESSAGE) - address = None - address_parameters = None - token_bundle = [] - - if contains_address: - address = output["address"] + address = output.get("address") + address_parameters = None if contains_address_type: address_parameters = _parse_address_parameters( output, INCOMPLETE_OUTPUT_ERROR_MESSAGE ) + token_bundle = [] if "token_bundle" in output: token_bundle = _parse_token_bundle(output["token_bundle"], is_mint=False) + datum_hash = parse_optional_bytes(output.get("datum_hash")) + return ( messages.CardanoTxOutput( address=address, address_parameters=address_parameters, amount=int(output["amount"]), asset_groups_count=len(token_bundle), + datum_hash=datum_hash, ), token_bundle, ) @@ -537,6 +537,10 @@ def parse_mint(mint: Iterable[dict]) -> List[AssetGroupWithTokens]: return _parse_token_bundle(mint, is_mint=True) +def parse_script_data_hash(script_data_hash: Optional[str]) -> Optional[bytes]: + return parse_optional_bytes(script_data_hash) + + def parse_additional_witness_request( additional_witness_request: dict, ) -> Path: @@ -688,6 +692,7 @@ def sign_tx( network_id: int = NETWORK_IDS["mainnet"], auxiliary_data: Optional[messages.CardanoTxAuxiliaryData] = None, mint: Sequence[AssetGroupWithTokens] = (), + script_data_hash: Optional[bytes] = None, additional_witness_requests: Sequence[Path] = (), derivation_type: messages.CardanoDerivationType = messages.CardanoDerivationType.ICARUS, include_network_id: bool = False, @@ -712,6 +717,7 @@ def sign_tx( network_id=network_id, has_auxiliary_data=auxiliary_data is not None, minting_asset_groups_count=len(mint), + script_data_hash=script_data_hash, witness_requests_count=len(witness_requests), derivation_type=derivation_type, include_network_id=include_network_id, diff --git a/python/src/trezorlib/cli/cardano.py b/python/src/trezorlib/cli/cardano.py index 697f4b5c7..85004dd04 100644 --- a/python/src/trezorlib/cli/cardano.py +++ b/python/src/trezorlib/cli/cardano.py @@ -87,6 +87,9 @@ def sign_tx( ] auxiliary_data = cardano.parse_auxiliary_data(transaction.get("auxiliary_data")) mint = cardano.parse_mint(transaction.get("mint", ())) + script_data_hash = cardano.parse_script_data_hash( + transaction.get("script_data_hash") + ) additional_witness_requests = [ cardano.parse_additional_witness_request(p) for p in transaction["additional_witness_requests"] @@ -107,6 +110,7 @@ def sign_tx( network_id, auxiliary_data, mint, + script_data_hash, additional_witness_requests, derivation_type=derivation_type, include_network_id=include_network_id, diff --git a/python/src/trezorlib/messages.py b/python/src/trezorlib/messages.py index 9d852c544..1b894b3ca 100644 --- a/python/src/trezorlib/messages.py +++ b/python/src/trezorlib/messages.py @@ -2245,6 +2245,7 @@ class CardanoSignTxInit(protobuf.MessageType): 13: protobuf.Field("minting_asset_groups_count", "uint32", repeated=False, required=True), 14: protobuf.Field("derivation_type", "CardanoDerivationType", repeated=False, required=True), 15: protobuf.Field("include_network_id", "bool", repeated=False, required=False), + 16: protobuf.Field("script_data_hash", "bytes", repeated=False, required=False), } def __init__( @@ -2265,6 +2266,7 @@ class CardanoSignTxInit(protobuf.MessageType): ttl: Optional["int"] = None, validity_interval_start: Optional["int"] = None, include_network_id: Optional["bool"] = False, + script_data_hash: Optional["bytes"] = None, ) -> None: self.signing_mode = signing_mode self.protocol_magic = protocol_magic @@ -2281,6 +2283,7 @@ class CardanoSignTxInit(protobuf.MessageType): self.ttl = ttl self.validity_interval_start = validity_interval_start self.include_network_id = include_network_id + self.script_data_hash = script_data_hash class CardanoTxInput(protobuf.MessageType): @@ -2307,6 +2310,7 @@ class CardanoTxOutput(protobuf.MessageType): 2: protobuf.Field("address_parameters", "CardanoAddressParametersType", repeated=False, required=False), 3: protobuf.Field("amount", "uint64", repeated=False, required=True), 4: protobuf.Field("asset_groups_count", "uint32", repeated=False, required=True), + 5: protobuf.Field("datum_hash", "bytes", repeated=False, required=False), } def __init__( @@ -2316,11 +2320,13 @@ class CardanoTxOutput(protobuf.MessageType): asset_groups_count: "int", address: Optional["str"] = None, address_parameters: Optional["CardanoAddressParametersType"] = None, + datum_hash: Optional["bytes"] = None, ) -> None: self.amount = amount self.asset_groups_count = asset_groups_count self.address = address self.address_parameters = address_parameters + self.datum_hash = datum_hash class CardanoAssetGroup(protobuf.MessageType): diff --git a/tests/device_tests/cardano/test_sign_tx.py b/tests/device_tests/cardano/test_sign_tx.py index 316a9e52c..f5f915381 100644 --- a/tests/device_tests/cardano/test_sign_tx.py +++ b/tests/device_tests/cardano/test_sign_tx.py @@ -45,6 +45,7 @@ def test_cardano_sign_tx(client: Client, parameters, result): withdrawals = [cardano.parse_withdrawal(w) for w in parameters["withdrawals"]] auxiliary_data = cardano.parse_auxiliary_data(parameters["auxiliary_data"]) mint = cardano.parse_mint(parameters["mint"]) + script_data_hash = cardano.parse_script_data_hash(parameters["script_data_hash"]) additional_witness_requests = [ cardano.parse_additional_witness_request(p) for p in parameters["additional_witness_requests"] @@ -72,6 +73,7 @@ def test_cardano_sign_tx(client: Client, parameters, result): network_id=parameters["network_id"], auxiliary_data=auxiliary_data, mint=mint, + script_data_hash=script_data_hash, additional_witness_requests=additional_witness_requests, include_network_id=parameters["include_network_id"], ) @@ -93,6 +95,7 @@ def test_cardano_sign_tx_failed(client: Client, parameters, result): withdrawals = [cardano.parse_withdrawal(w) for w in parameters["withdrawals"]] auxiliary_data = cardano.parse_auxiliary_data(parameters["auxiliary_data"]) mint = cardano.parse_mint(parameters["mint"]) + script_data_hash = cardano.parse_script_data_hash(parameters["script_data_hash"]) additional_witness_requests = [ cardano.parse_additional_witness_request(p) for p in parameters["additional_witness_requests"] @@ -121,6 +124,7 @@ def test_cardano_sign_tx_failed(client: Client, parameters, result): network_id=parameters["network_id"], auxiliary_data=auxiliary_data, mint=mint, + script_data_hash=script_data_hash, additional_witness_requests=additional_witness_requests, include_network_id=parameters["include_network_id"], )