|
8 | 8 |
|
9 | 9 | from hiero_sdk_python.account.account_id import AccountId |
10 | 10 | from hiero_sdk_python.consensus.topic_message_submit_transaction import TopicMessageSubmitTransaction |
| 11 | +from hiero_sdk_python.crypto.private_key import PrivateKey |
11 | 12 | from hiero_sdk_python.exceptions import PrecheckError, ReceiptStatusError |
12 | 13 | from hiero_sdk_python.hapi.services import ( |
13 | 14 | response_header_pb2, |
|
22 | 23 | ) |
23 | 24 | from hiero_sdk_python.response_code import ResponseCode |
24 | 25 | from hiero_sdk_python.transaction.custom_fee_limit import CustomFeeLimit |
| 26 | +from hiero_sdk_python.transaction.transaction import Transaction |
25 | 27 | from hiero_sdk_python.transaction.transaction_id import TransactionId |
26 | 28 | from hiero_sdk_python.transaction.transaction_receipt import TransactionReceipt |
27 | 29 | from hiero_sdk_python.transaction.transaction_response import TransactionResponse |
@@ -566,3 +568,165 @@ def test_execute_raises_when_message_is_empty(topic_id, mock_client): |
566 | 568 |
|
567 | 569 | with pytest.raises(ValueError, match="Missing required fields: message"): |
568 | 570 | transaction.freeze_with(mock_client) |
| 571 | + |
| 572 | + |
| 573 | +def test_frezee_with_client_generate_all_transaction_body_bytes(topic_id, mock_client): |
| 574 | + """Test freeze_with() generate all required transaction body bytes.""" |
| 575 | + message = "A" * 40 # will create 4 chunks |
| 576 | + |
| 577 | + transaction = TopicMessageSubmitTransaction().set_topic_id(topic_id).set_chunk_size(10).set_message(message) |
| 578 | + |
| 579 | + assert len(transaction._transaction_ids) == 0 |
| 580 | + assert not transaction._transaction_body_bytes |
| 581 | + |
| 582 | + transaction.freeze_with(mock_client) |
| 583 | + |
| 584 | + # generated transaction ids == number of chunks i.e 4 (40/10) |
| 585 | + assert len(transaction._transaction_ids) == 4 |
| 586 | + |
| 587 | + transaction_ids = transaction._transaction_ids |
| 588 | + initial_transaction = transaction_ids[0] |
| 589 | + |
| 590 | + for i, _ in enumerate(transaction_ids): |
| 591 | + assert transaction_ids[i].account_id == initial_transaction.account_id |
| 592 | + assert transaction_ids[i].valid_start.nanos == initial_transaction.valid_start.nanos + i |
| 593 | + |
| 594 | + # create body bytes for each transaction_id and every node_account_ids |
| 595 | + assert len(transaction._transaction_body_bytes) == 4 |
| 596 | + for transaction_id in transaction._transaction_ids: |
| 597 | + assert len(transaction._transaction_body_bytes[transaction_id]) == len(mock_client.network.nodes) |
| 598 | + |
| 599 | + |
| 600 | +def test_manual_frezee_generate_all_transaction_body_bytes(topic_id): |
| 601 | + """Test freeze() generate all required transaction body bytes.""" |
| 602 | + transaction_id = TransactionId.generate(AccountId(0, 0, 1)) |
| 603 | + node_account_ids = [AccountId(0, 0, 4), AccountId(0, 0, 5)] |
| 604 | + |
| 605 | + message = "A" * 20 # will create 2 chunks |
| 606 | + |
| 607 | + transaction = ( |
| 608 | + TopicMessageSubmitTransaction() |
| 609 | + .set_topic_id(topic_id) |
| 610 | + .set_chunk_size(10) |
| 611 | + .set_message(message) |
| 612 | + .set_transaction_id(transaction_id) |
| 613 | + .set_node_account_ids(node_account_ids) |
| 614 | + ) |
| 615 | + |
| 616 | + # single transaction_id which is set |
| 617 | + assert len(transaction._transaction_ids) == 1 |
| 618 | + assert not transaction._transaction_body_bytes |
| 619 | + |
| 620 | + transaction.freeze() |
| 621 | + |
| 622 | + # generated transaction ids == number of chunks i.e 2 (20/10) |
| 623 | + assert len(transaction._transaction_ids) == 2 |
| 624 | + |
| 625 | + transaction_ids = transaction._transaction_ids |
| 626 | + |
| 627 | + assert transaction_ids[0] == transaction_id |
| 628 | + |
| 629 | + for i, _ in enumerate(transaction_ids): |
| 630 | + assert transaction_ids[i].account_id == transaction_id.account_id |
| 631 | + assert transaction_ids[i].valid_start.nanos == transaction_id.valid_start.nanos + i |
| 632 | + |
| 633 | + # create body bytes for each transaction_id and every node_account_ids |
| 634 | + assert len(transaction._transaction_body_bytes) == 2 |
| 635 | + for transaction_id in transaction._transaction_ids: |
| 636 | + assert len(transaction._transaction_body_bytes[transaction_id]) == len(node_account_ids) |
| 637 | + |
| 638 | + |
| 639 | +def test_serialize_chunk_transaction_preserve_signature_map(topic_id): |
| 640 | + """Test serialize chunk transaction preserve signature maps.""" |
| 641 | + transaction_id = TransactionId.generate(AccountId(0, 0, 1)) |
| 642 | + node_account_ids = [AccountId(0, 0, 4), AccountId(0, 0, 5)] |
| 643 | + key = PrivateKey.generate_ecdsa() |
| 644 | + |
| 645 | + message = "A" * 20 # will create 2 chunks |
| 646 | + |
| 647 | + tx1 = ( |
| 648 | + TopicMessageSubmitTransaction() |
| 649 | + .set_topic_id(topic_id) |
| 650 | + .set_chunk_size(10) |
| 651 | + .set_message(message) |
| 652 | + .set_transaction_id(transaction_id) |
| 653 | + .set_node_account_ids(node_account_ids) |
| 654 | + .freeze() |
| 655 | + .sign(key) |
| 656 | + ) |
| 657 | + |
| 658 | + assert tx1._signature_map |
| 659 | + |
| 660 | + for transaction_id in tx1._transaction_ids: |
| 661 | + for node_id in node_account_ids: |
| 662 | + body_bytes = tx1._transaction_body_bytes[transaction_id][node_id] |
| 663 | + sig_pairs = tx1._signature_map[body_bytes].sigPair |
| 664 | + |
| 665 | + assert len(sig_pairs) == 1 |
| 666 | + |
| 667 | + pubkey_prefixes = {sp.pubKeyPrefix for sp in sig_pairs} |
| 668 | + assert pubkey_prefixes == {key.public_key().to_bytes_raw()} |
| 669 | + |
| 670 | + # will create transaction_bytes like |
| 671 | + # {tx_id1: {node_id1: bytes, node_id2: bytes}, tx_id2: {node_id1: bytes, node_id2: bytes}} |
| 672 | + |
| 673 | + tx_bytes = tx1.to_bytes() |
| 674 | + tx2 = Transaction.from_bytes(tx_bytes) |
| 675 | + |
| 676 | + assert isinstance(tx2, TopicMessageSubmitTransaction) |
| 677 | + |
| 678 | + assert tx2._signature_map |
| 679 | + |
| 680 | + for transaction_id in tx2._transaction_ids: |
| 681 | + for node_id in node_account_ids: |
| 682 | + body_bytes = tx2._transaction_body_bytes[transaction_id][node_id] |
| 683 | + sig_pairs = tx2._signature_map[body_bytes].sigPair |
| 684 | + |
| 685 | + assert len(sig_pairs) == 1 |
| 686 | + |
| 687 | + pubkey_prefixes = {sp.pubKeyPrefix for sp in sig_pairs} |
| 688 | + assert pubkey_prefixes == {key.public_key().to_bytes_raw()} |
| 689 | + |
| 690 | + |
| 691 | +def test_signing_serialize_chunk_transaction_sign_all_available_bytes(topic_id): |
| 692 | + """Test that signing the serialize chunk transaction sign all available bytes.""" |
| 693 | + transaction_id = TransactionId.generate(AccountId(0, 0, 1)) |
| 694 | + node_account_ids = [AccountId(0, 0, 4), AccountId(0, 0, 5)] |
| 695 | + |
| 696 | + message = "A" * 20 # will create 2 chunks |
| 697 | + |
| 698 | + tx1 = ( |
| 699 | + TopicMessageSubmitTransaction() |
| 700 | + .set_topic_id(topic_id) |
| 701 | + .set_chunk_size(10) |
| 702 | + .set_message(message) |
| 703 | + .set_transaction_id(transaction_id) |
| 704 | + .set_node_account_ids(node_account_ids) |
| 705 | + .freeze() |
| 706 | + ) |
| 707 | + |
| 708 | + assert not tx1._signature_map |
| 709 | + |
| 710 | + # will create transaction_bytes like |
| 711 | + # {tx_id1: {node_id1: bytes, node_id2: bytes}, tx_id2: {node_id1: bytes, node_id2: bytes}} |
| 712 | + |
| 713 | + tx_bytes = tx1.to_bytes() |
| 714 | + tx2 = Transaction.from_bytes(tx_bytes) |
| 715 | + |
| 716 | + assert isinstance(tx2, TopicMessageSubmitTransaction) |
| 717 | + |
| 718 | + key = PrivateKey.generate_ecdsa() |
| 719 | + |
| 720 | + tx2.sign(key) |
| 721 | + |
| 722 | + assert tx2._signature_map |
| 723 | + |
| 724 | + for transaction_id in tx2._transaction_ids: |
| 725 | + for node_id in node_account_ids: |
| 726 | + body_bytes = tx2._transaction_body_bytes[transaction_id][node_id] |
| 727 | + sig_pairs = tx2._signature_map[body_bytes].sigPair |
| 728 | + |
| 729 | + assert len(sig_pairs) == 1 |
| 730 | + |
| 731 | + pubkey_prefixes = {sp.pubKeyPrefix for sp in sig_pairs} |
| 732 | + assert pubkey_prefixes == {key.public_key().to_bytes_raw()} |
0 commit comments