|
24 | 24 | KEY_TYPE_IDS = tuple(k.value.replace("-", "_") for k in KEY_TYPES) |
25 | 25 | # pyrefly: ignore # no-matching-overload |
26 | 26 | OUT_FORMATS = tuple(clusterlib.OutputFormat) |
27 | | -OUT_FORMAT_IDS = (k.value.replace("-", "_") for k in OUT_FORMATS) |
| 27 | +OUT_FORMAT_IDS = tuple(k.value.replace("-", "_") for k in OUT_FORMATS) |
28 | 28 |
|
29 | 29 | # A small embedded list of *valid* BIP39 words (subset). |
30 | 30 | # Enough to build syntactically plausible phrases without importing anything. |
@@ -98,7 +98,7 @@ class BadMnemonicCase: |
98 | 98 | bad_token = st.one_of( |
99 | 99 | ascii_word, # Unknown word |
100 | 100 | st.text(min_size=1, max_size=8).filter( |
101 | | - lambda s: any(c for c in s if not c.isalpha()) |
| 101 | + lambda s: any(not c.isalpha() for c in s) |
102 | 102 | ), # Punctuation/digits |
103 | 103 | st.sampled_from(["Über", "naïve", "résumé", "café"]), # Diacritics / non-ASCII |
104 | 104 | st.sampled_from(["🚀", "🔥", "🙂"]), # Emoji |
@@ -381,6 +381,41 @@ def test_golden_deriv( |
381 | 381 |
|
382 | 382 | assert helpers.checksum(filename=key_file) == helpers.checksum(filename=golden_key_file) |
383 | 383 |
|
| 384 | + @allure.link(helpers.get_vcs_link()) |
| 385 | + @pytest.mark.parametrize("key_type", KEY_TYPES, ids=KEY_TYPE_IDS) |
| 386 | + @common.hypothesis_settings(max_examples=300) |
| 387 | + @hypothesis.given(account_number=st.integers(min_value=0, max_value=2**31 - 1)) |
| 388 | + @hypothesis.example(account_number=0) |
| 389 | + @hypothesis.example(account_number=2**31 - 1) |
| 390 | + def test_derive_account_key_number_property( |
| 391 | + self, |
| 392 | + cluster: clusterlib.ClusterLib, |
| 393 | + key_type: clusterlib.KeyType, |
| 394 | + account_number: int, |
| 395 | + ) -> None: |
| 396 | + """Test that `derive-from-mnemonic` accepts any valid account_number in [0, 2^31-1]. |
| 397 | +
|
| 398 | + For payment/stake keys, pass the same value as key_number, otherwise omit key_number. |
| 399 | + """ |
| 400 | + temp_template = f"{common.get_test_id(cluster)}_{common.unique_time_str()}" |
| 401 | + mnemonic_file = DATA_DIR / "gold_[0-bech32-payment-24]_mnemonic" |
| 402 | + |
| 403 | + key_number = ( |
| 404 | + account_number |
| 405 | + if key_type in (clusterlib.KeyType.PAYMENT, clusterlib.KeyType.STAKE) |
| 406 | + else None |
| 407 | + ) |
| 408 | + |
| 409 | + key_file = cluster.g_key.derive_from_mnemonic( |
| 410 | + key_name=f"{temp_template}_derived", |
| 411 | + key_type=key_type, |
| 412 | + mnemonic_file=mnemonic_file, |
| 413 | + account_number=account_number, |
| 414 | + key_number=key_number, |
| 415 | + out_format=clusterlib.OutputFormat.BECH32, |
| 416 | + ) |
| 417 | + assert key_file.exists() |
| 418 | + |
384 | 419 |
|
385 | 420 | @common.SKIPIF_WRONG_ERA |
386 | 421 | class TestNegativeMnemonic: |
@@ -435,3 +470,46 @@ def test_rejects_noncompliant_mnemonics( |
435 | 470 | "Error reading mnemonic file" in err_value |
436 | 471 | or "Error converting the mnemonic into a key" in err_value |
437 | 472 | ) |
| 473 | + |
| 474 | + @allure.link(helpers.get_vcs_link()) |
| 475 | + @pytest.mark.parametrize("key_type", KEY_TYPES, ids=KEY_TYPE_IDS) |
| 476 | + @common.hypothesis_settings(max_examples=300) |
| 477 | + @hypothesis.given( |
| 478 | + bad_value=st.one_of( |
| 479 | + # Integers outside the valid range |
| 480 | + st.integers(max_value=-1), |
| 481 | + st.integers(min_value=2**31), |
| 482 | + # Non-integer types |
| 483 | + st.floats(allow_nan=True, allow_infinity=True), |
| 484 | + st.text( |
| 485 | + alphabet=st.characters(blacklist_categories=["C", "Nd"]), min_size=1, max_size=5 |
| 486 | + ), |
| 487 | + st.sampled_from([3.14, object(), [], {}]), |
| 488 | + ) |
| 489 | + ) |
| 490 | + @hypothesis.example(bad_value=-1) |
| 491 | + @hypothesis.example(bad_value=2**31) |
| 492 | + def test_reject_invalid_account_or_key_number( |
| 493 | + self, |
| 494 | + cluster: clusterlib.ClusterLib, |
| 495 | + key_type: clusterlib.KeyType, |
| 496 | + bad_value: object, |
| 497 | + ) -> None: |
| 498 | + """Property: derive_from_mnemonic rejects out-of-range or non-int account/key numbers.""" |
| 499 | + temp_template = f"{common.get_test_id(cluster)}_{common.unique_time_str()}" |
| 500 | + mnemonic_file = DATA_DIR / "gold_[0-bech32-payment-24]_mnemonic" |
| 501 | + |
| 502 | + kwargs: dict[str, tp.Any] = { |
| 503 | + "key_name": f"{temp_template}_derived", |
| 504 | + "key_type": key_type, |
| 505 | + "mnemonic_file": mnemonic_file, |
| 506 | + "account_number": bad_value, |
| 507 | + } |
| 508 | + |
| 509 | + if key_type in (clusterlib.KeyType.PAYMENT, clusterlib.KeyType.STAKE): |
| 510 | + kwargs["key_number"] = bad_value |
| 511 | + |
| 512 | + with pytest.raises(clusterlib.CLIError) as excinfo: |
| 513 | + cluster.g_key.derive_from_mnemonic(**kwargs) |
| 514 | + err_value = str(excinfo.value) |
| 515 | + assert "unexpected" in err_value or "Error converting the mnemonic into a key" in err_value |
0 commit comments