|
6 | 6 |
|
7 | 7 | from hiero_sdk_python.account.account_id import AccountId |
8 | 8 | from hiero_sdk_python.consensus.topic_update_transaction import TopicUpdateTransaction |
| 9 | +from hiero_sdk_python.crypto.key import Key |
9 | 10 | from hiero_sdk_python.crypto.private_key import PrivateKey |
| 11 | +from hiero_sdk_python.crypto.public_key import PublicKey |
10 | 12 | from hiero_sdk_python.Duration import Duration |
11 | 13 | from hiero_sdk_python.hapi.services import ( |
12 | 14 | response_header_pb2, |
|
26 | 28 | pytestmark = pytest.mark.unit |
27 | 29 |
|
28 | 30 |
|
29 | | -# This test uses fixtures (mock_account_ids, topic_id) as parameters |
| 31 | +def create_key(key_type: str, use_private: bool) -> PrivateKey | PublicKey: |
| 32 | + """ |
| 33 | + Create a key based on type and whether to use private or public. |
| 34 | +
|
| 35 | + Args: |
| 36 | + key_type: "ed25519" or "ecdsa" |
| 37 | + use_private: True for PrivateKey, False for PublicKey |
| 38 | +
|
| 39 | + Returns: |
| 40 | + The created key (PrivateKey or PublicKey) |
| 41 | + """ |
| 42 | + if key_type == "ed25519": |
| 43 | + private_key = PrivateKey.generate_ed25519() |
| 44 | + elif key_type == "ecdsa": |
| 45 | + private_key = PrivateKey.generate("ecdsa") |
| 46 | + else: |
| 47 | + raise ValueError(f"Unsupported key_type: {key_type!r}") |
| 48 | + return private_key if use_private else private_key.public_key() |
| 49 | + |
| 50 | + |
| 51 | +def get_expected_public_key(key: Key) -> PublicKey: |
| 52 | + """ |
| 53 | + Get the public key from either PrivateKey or PublicKey. |
| 54 | +
|
| 55 | + Args: |
| 56 | + key: PrivateKey or PublicKey |
| 57 | +
|
| 58 | + Returns: |
| 59 | + PublicKey |
| 60 | + """ |
| 61 | + return key if isinstance(key, PublicKey) else key.public_key() |
| 62 | + |
| 63 | + |
| 64 | +def verify_key_in_proto(proto_key, expected_public_key: PublicKey, key_type: str) -> None: |
| 65 | + """Verify the proto key matches expected public key.""" |
| 66 | + if key_type == "ed25519": |
| 67 | + assert proto_key.ed25519 == expected_public_key.to_bytes_raw() |
| 68 | + elif key_type == "ecdsa": # ecdsa |
| 69 | + assert proto_key.HasField("ECDSA_secp256k1") |
| 70 | + assert proto_key.ECDSA_secp256k1 == expected_public_key.to_bytes_raw() |
| 71 | + else: |
| 72 | + raise ValueError(f"Unsupported key_type: {key_type!r}") |
| 73 | + |
| 74 | + |
| 75 | +@pytest.mark.parametrize( |
| 76 | + "key_type,use_private", |
| 77 | + [ |
| 78 | + ("ed25519", True), |
| 79 | + ("ed25519", False), |
| 80 | + ("ecdsa", True), |
| 81 | + ("ecdsa", False), |
| 82 | + ], |
| 83 | +) |
| 84 | +def test_topic_update_setters_return_self(key_type, use_private, topic_id): |
| 85 | + """Fluent setters must return self for the Key-typed API.""" |
| 86 | + key = create_key(key_type, use_private) |
| 87 | + tx = TopicUpdateTransaction() |
| 88 | + |
| 89 | + assert tx.set_topic_id(topic_id) is tx |
| 90 | + assert tx.set_admin_key(key) is tx |
| 91 | + assert tx.set_submit_key(key) is tx |
| 92 | + assert tx.set_fee_schedule_key(key) is tx |
| 93 | + assert tx.set_fee_exempt_keys([key]) is tx |
| 94 | + |
| 95 | + |
| 96 | +@pytest.mark.parametrize( |
| 97 | + "key_type,use_private", |
| 98 | + [ |
| 99 | + ("ed25519", True), |
| 100 | + ("ed25519", False), |
| 101 | + ("ecdsa", True), |
| 102 | + ("ecdsa", False), |
| 103 | + ], |
| 104 | +) |
| 105 | +def test_build_topic_update_transaction_body_with_all_key_types(mock_account_ids, topic_id, key_type, use_private): |
| 106 | + """Test building a TopicUpdateTransaction body with different key types.""" |
| 107 | + _, _, node_account_id, _, _ = mock_account_ids |
| 108 | + |
| 109 | + admin_key = create_key(key_type, use_private) |
| 110 | + submit_key = create_key(key_type, use_private) |
| 111 | + fee_schedule_key = create_key(key_type, use_private) |
| 112 | + fee_exempt_keys = [ |
| 113 | + create_key(key_type, use_private), |
| 114 | + create_key(key_type, use_private), |
| 115 | + ] |
| 116 | + |
| 117 | + expected_admin_public = get_expected_public_key(admin_key) |
| 118 | + expected_submit_public = get_expected_public_key(submit_key) |
| 119 | + expected_fee_schedule_public = get_expected_public_key(fee_schedule_key) |
| 120 | + expected_fee_exempt_publics = [get_expected_public_key(key) for key in fee_exempt_keys] |
| 121 | + |
| 122 | + tx = TopicUpdateTransaction( |
| 123 | + topic_id=topic_id, |
| 124 | + memo="Updated Memo", |
| 125 | + admin_key=admin_key, |
| 126 | + submit_key=submit_key, |
| 127 | + custom_fees=[CustomFixedFee(1000, fee_collector_account_id=AccountId(0, 0, 9876))], |
| 128 | + fee_schedule_key=fee_schedule_key, |
| 129 | + fee_exempt_keys=fee_exempt_keys, |
| 130 | + ) |
| 131 | + |
| 132 | + tx.operator_account_id = AccountId(0, 0, 2) |
| 133 | + tx.node_account_id = node_account_id |
| 134 | + |
| 135 | + transaction_body = tx.build_transaction_body() |
| 136 | + |
| 137 | + assert transaction_body.consensusUpdateTopic.topicID.topicNum == 1234 |
| 138 | + assert transaction_body.consensusUpdateTopic.memo.value == "Updated Memo" |
| 139 | + verify_key_in_proto(transaction_body.consensusUpdateTopic.adminKey, expected_admin_public, key_type) |
| 140 | + verify_key_in_proto(transaction_body.consensusUpdateTopic.submitKey, expected_submit_public, key_type) |
| 141 | + verify_key_in_proto(transaction_body.consensusUpdateTopic.fee_schedule_key, expected_fee_schedule_public, key_type) |
| 142 | + assert len(transaction_body.consensusUpdateTopic.fee_exempt_key_list.keys) == 2 |
| 143 | + verify_key_in_proto( |
| 144 | + transaction_body.consensusUpdateTopic.fee_exempt_key_list.keys[0], expected_fee_exempt_publics[0], key_type |
| 145 | + ) |
| 146 | + verify_key_in_proto( |
| 147 | + transaction_body.consensusUpdateTopic.fee_exempt_key_list.keys[1], expected_fee_exempt_publics[1], key_type |
| 148 | + ) |
| 149 | + assert len(transaction_body.consensusUpdateTopic.custom_fees.fees) == 1 |
| 150 | + |
| 151 | + |
| 152 | +@pytest.mark.parametrize( |
| 153 | + "key_type,use_private", |
| 154 | + [ |
| 155 | + ("ed25519", True), |
| 156 | + ("ed25519", False), |
| 157 | + ("ecdsa", True), |
| 158 | + ("ecdsa", False), |
| 159 | + ], |
| 160 | +) |
| 161 | +def test_build_scheduled_topic_update_body_with_all_key_types(topic_id, key_type, use_private): |
| 162 | + """Test building scheduled body for TopicUpdateTransaction with different key types.""" |
| 163 | + admin_key = create_key(key_type, use_private) |
| 164 | + submit_key = create_key(key_type, use_private) |
| 165 | + fee_schedule_key = create_key(key_type, use_private) |
| 166 | + fee_exempt_keys = [ |
| 167 | + create_key(key_type, use_private), |
| 168 | + create_key(key_type, use_private), |
| 169 | + ] |
| 170 | + |
| 171 | + expected_admin_public = get_expected_public_key(admin_key) |
| 172 | + expected_submit_public = get_expected_public_key(submit_key) |
| 173 | + expected_fee_schedule_public = get_expected_public_key(fee_schedule_key) |
| 174 | + expected_fee_exempt_publics = [get_expected_public_key(key) for key in fee_exempt_keys] |
| 175 | + |
| 176 | + tx = TopicUpdateTransaction() |
| 177 | + tx.set_topic_id(topic_id) |
| 178 | + tx.set_memo("Scheduled Topic Update") |
| 179 | + tx.set_admin_key(admin_key) |
| 180 | + tx.set_submit_key(submit_key) |
| 181 | + tx.set_auto_renew_period(Duration(8000000)) |
| 182 | + tx.set_auto_renew_account(AccountId(0, 0, 9876)) |
| 183 | + tx.set_custom_fees([CustomFixedFee(1000, fee_collector_account_id=AccountId(0, 0, 9876))]) |
| 184 | + tx.set_fee_schedule_key(fee_schedule_key) |
| 185 | + tx.set_fee_exempt_keys(fee_exempt_keys) |
| 186 | + |
| 187 | + schedulable_body = tx.build_scheduled_body() |
| 188 | + |
| 189 | + assert isinstance(schedulable_body, SchedulableTransactionBody) |
| 190 | + assert schedulable_body.HasField("consensusUpdateTopic") |
| 191 | + assert schedulable_body.consensusUpdateTopic.topicID.topicNum == 1234 |
| 192 | + assert schedulable_body.consensusUpdateTopic.memo.value == "Scheduled Topic Update" |
| 193 | + verify_key_in_proto(schedulable_body.consensusUpdateTopic.adminKey, expected_admin_public, key_type) |
| 194 | + verify_key_in_proto(schedulable_body.consensusUpdateTopic.submitKey, expected_submit_public, key_type) |
| 195 | + verify_key_in_proto(schedulable_body.consensusUpdateTopic.fee_schedule_key, expected_fee_schedule_public, key_type) |
| 196 | + assert len(schedulable_body.consensusUpdateTopic.fee_exempt_key_list.keys) == 2 |
| 197 | + verify_key_in_proto( |
| 198 | + schedulable_body.consensusUpdateTopic.fee_exempt_key_list.keys[0], expected_fee_exempt_publics[0], key_type |
| 199 | + ) |
| 200 | + verify_key_in_proto( |
| 201 | + schedulable_body.consensusUpdateTopic.fee_exempt_key_list.keys[1], expected_fee_exempt_publics[1], key_type |
| 202 | + ) |
| 203 | + assert len(schedulable_body.consensusUpdateTopic.custom_fees.fees) == 1 |
| 204 | + |
| 205 | + |
30 | 206 | def test_build_topic_update_transaction_body(mock_account_ids, topic_id): |
31 | 207 | """Test building a TopicUpdateTransaction body with valid topic ID and memo.""" |
32 | 208 | _, _, node_account_id, _, _ = mock_account_ids |
|
0 commit comments