Skip to content

Commit 306717b

Browse files
authored
fix: prevent duplicate signatures in _signature_map (hiero-ledger#2219)
Signed-off-by: Mohit Yadav <ymohit799057@gmail.com>
1 parent cf653c3 commit 306717b

2 files changed

Lines changed: 49 additions & 2 deletions

File tree

src/hiero_sdk_python/transaction/transaction.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,12 @@ def sign(self, private_key: PrivateKey) -> Transaction:
192192
# We initialize the signature map for this body_bytes if it doesn't exist yet
193193
self._signature_map.setdefault(body_bytes, basic_types_pb2.SignatureMap())
194194

195-
# Append the signature pair to the signature map for this transaction body
196-
self._signature_map[body_bytes].sigPair.append(sig_pair)
195+
# deduplication check
196+
already_signed = any(sp.pubKeyPrefix == public_key_bytes for sp in self._signature_map[body_bytes].sigPair)
197+
198+
# append only if not already signed
199+
if not already_signed:
200+
self._signature_map[body_bytes].sigPair.append(sig_pair)
197201

198202
return self
199203

tests/unit/transaction_test.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import pytest
44

55
from hiero_sdk_python.account.account_create_transaction import AccountCreateTransaction
6+
from hiero_sdk_python.account.account_id import AccountId
67
from hiero_sdk_python.crypto.private_key import PrivateKey
78
from hiero_sdk_python.exceptions import ReceiptStatusError
89
from hiero_sdk_python.hapi.services import (
@@ -14,6 +15,9 @@
1415
transaction_response_pb2,
1516
)
1617
from hiero_sdk_python.response_code import ResponseCode
18+
from hiero_sdk_python.tokens.token_id import TokenId
19+
from hiero_sdk_python.tokens.token_mint_transaction import TokenMintTransaction
20+
from hiero_sdk_python.transaction.transaction_id import TransactionId
1721
from hiero_sdk_python.transaction.transaction_receipt import TransactionReceipt
1822
from hiero_sdk_python.transaction.transaction_response import TransactionResponse
1923
from tests.unit.mock_server import mock_hedera_servers
@@ -117,3 +121,42 @@ def test_execute_returns_receipt_without_error_when_validation_disabled():
117121
receipt = tx.execute(client)
118122

119123
assert receipt.status == ResponseCode.INVALID_SIGNATURE
124+
125+
126+
def test_duplicate_signature_not_added():
127+
tx = TokenMintTransaction()
128+
tx.set_transaction_id(TransactionId.generate(AccountId(0, 0, 1234)))
129+
tx.set_node_account_id(AccountId(0, 0, 3))
130+
tx.set_token_id(TokenId(0, 0, 1))
131+
tx.set_amount(100)
132+
key = PrivateKey.generate_ed25519()
133+
tx.freeze()
134+
tx.sign(key)
135+
tx.sign(key)
136+
assert tx._signature_map, "signature_map should not be empty after freeze+sign" # ← ADD HERE
137+
body_bytes = next(iter(tx._signature_map.keys()))
138+
sig_pairs = tx._signature_map[body_bytes].sigPair
139+
assert len(sig_pairs) == 1, "Expected 1 signature for duplicate key"
140+
141+
142+
def test_multiple_keys_still_work():
143+
tx = TokenMintTransaction()
144+
tx.set_transaction_id(TransactionId.generate(AccountId(0, 0, 1234)))
145+
tx.set_node_account_id(AccountId(0, 0, 3))
146+
tx.set_token_id(TokenId(0, 0, 1))
147+
tx.set_amount(100)
148+
key1 = PrivateKey.generate_ed25519()
149+
key2 = PrivateKey.generate_ed25519()
150+
tx.freeze()
151+
tx.sign(key1)
152+
tx.sign(key2)
153+
assert tx._signature_map, "signature_map should not be empty after freeze+sign"
154+
body_bytes = next(iter(tx._signature_map.keys()))
155+
sig_pairs = tx._signature_map[body_bytes].sigPair
156+
assert len(sig_pairs) == 2, "Expected 2 signatures for different keys"
157+
pubkey_prefixes = {sp.pubKeyPrefix for sp in sig_pairs}
158+
expected_prefixes = {
159+
key1.public_key().to_bytes_raw(),
160+
key2.public_key().to_bytes_raw(),
161+
}
162+
assert pubkey_prefixes == expected_prefixes, "Signatures should match key1 and key2 exactly"

0 commit comments

Comments
 (0)