diff --git a/testing/unit/tp/account/test_transfer.py b/testing/unit/tp/account/test_transfer.py index 69de8f77..f4ca5981 100644 --- a/testing/unit/tp/account/test_transfer.py +++ b/testing/unit/tp/account/test_transfer.py @@ -35,6 +35,7 @@ ACCOUNT_FROM_BALANCE = NODE_ACCOUNT_FROM_BALANCE = 10000 ACCOUNT_TO_BALANCE = NODE_ACCOUNT_TO_BALANCE = 1000 +MAX_UINT64 = 2**64-1 ACCOUNT_ADDRESS_FROM = '112007d71fa7e120c60fb392a64fd69de891a60c667d9ea9e5d9d9d617263be6c20202' ACCOUNT_ADDRESS_TO = '1120071db7c02f5731d06df194dc95465e9b277c19e905ce642664a9a0d504a3909e31' @@ -323,6 +324,56 @@ def test_account_transfer_from_address(): assert state_as_dict.get(ACCOUNT_ADDRESS_TO, Account()).balance == expected_account_to_balance +def test_account_transfer_integer_overflow(): + """ + Case: transfer tokens from address to address such that on receiver address will be more then max uint64 tokens. + Expect: exception ValueError is raised. + """ + account_from_balance = 1 + tokens_amount_to_send = 1 + account_to_balance = MAX_UINT64 + expected_account_from_balance = account_from_balance - tokens_amount_to_send + expected_account_to_balance = account_to_balance + tokens_amount_to_send + + transfer_payload = TransferPayload() + transfer_payload.address_to = ACCOUNT_ADDRESS_TO + transfer_payload.value = tokens_amount_to_send + + transaction_payload = TransactionPayload() + transaction_payload.method = AccountMethod.TRANSFER + transaction_payload.data = transfer_payload.SerializeToString() + + serialized_transaction_payload = transaction_payload.SerializeToString() + + transaction_header = TransactionHeader( + signer_public_key=ACCOUNT_FROM_PUBLIC_KEY, + family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get('family_name'), + family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get('family_version'), + inputs=INPUTS, + outputs=OUTPUTS, + dependencies=[], + payload_sha512=hash512(data=serialized_transaction_payload), + batcher_public_key=RANDOM_NODE_PUBLIC_KEY, + nonce=time.time().hex().encode(), + ) + + serialized_header = transaction_header.SerializeToString() + + transaction_request = TpProcessRequest( + header=transaction_header, + payload=serialized_transaction_payload, + signature=create_signer(private_key=ACCOUNT_FROM_PRIVATE_KEY).sign(serialized_header), + ) + + mock_context = create_context(account_from_balance=account_from_balance, account_to_balance=account_to_balance) + + + with pytest.raises(ValueError) as error: + AccountHandler().apply(transaction=transaction_request, context=mock_context) + + assert str(error.value).startswith('Value out of range') + + def test_account_transfer_from_address_zero_amount(): """ Case: transfer zero tokens from address to address. diff --git a/testing/unit/tp/node_account/shared.py b/testing/unit/tp/node_account/shared.py index a5a1a26b..e77515bb 100644 --- a/testing/unit/tp/node_account/shared.py +++ b/testing/unit/tp/node_account/shared.py @@ -27,6 +27,8 @@ 'family_version': NodeAccountHandler().family_versions[0], } +MAX_UINT64 = 2**64-1 + def create_context(account_from_balance, node_state=NodeAccount.NEW, frozen=0, unfrozen=0): """ diff --git a/testing/unit/tp/node_account/test_transfer_from_unfrozne_to_operational.py b/testing/unit/tp/node_account/test_transfer_from_unfrozen_to_operational.py similarity index 78% rename from testing/unit/tp/node_account/test_transfer_from_unfrozne_to_operational.py rename to testing/unit/tp/node_account/test_transfer_from_unfrozen_to_operational.py index 066be621..18f0d1b8 100644 --- a/testing/unit/tp/node_account/test_transfer_from_unfrozne_to_operational.py +++ b/testing/unit/tp/node_account/test_transfer_from_unfrozen_to_operational.py @@ -30,6 +30,7 @@ INPUTS, OUTPUTS, TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS, + MAX_UINT64, create_context, ) @@ -93,6 +94,53 @@ def test_transfer_from_unfrozen_to_operational(): assert node_account.reputation.unfrozen == unfrozen - tokens_to_transfer +def test_transfer_from_unfrozen_to_operational_integer_overflow(): + """ + Case: transfer from unfrozen to operational such that it will cause integer overflow. + Expect: integer overflow exception is raised. + """ + unfrozen = 1 + operational = MAX_UINT64 + tokens_to_transfer = 1 + + internal_transfer_payload = NodeAccountInternalTransferPayload() + internal_transfer_payload.value = tokens_to_transfer + + transaction_payload = TransactionPayload() + transaction_payload.method = NodeAccountMethod.TRANSFER_FROM_UNFROZEN_TO_OPERATIONAL + transaction_payload.data = internal_transfer_payload.SerializeToString() + + serialized_transaction_payload = transaction_payload.SerializeToString() + + transaction_header = TransactionHeader( + signer_public_key=RANDOM_NODE_PUBLIC_KEY, + family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get('family_name'), + family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get('family_version'), + inputs=INPUTS, + outputs=OUTPUTS, + dependencies=[], + payload_sha512=hash512(data=serialized_transaction_payload), + batcher_public_key=RANDOM_NODE_PUBLIC_KEY, + nonce=time.time().hex().encode(), + ) + + serialized_header = transaction_header.SerializeToString() + + transaction_request = TpProcessRequest( + header=transaction_header, + payload=serialized_transaction_payload, + signature=create_signer(private_key=NODE_ACCOUNT_FROM_PRIVATE_KEY).sign(serialized_header), + ) + + mock_context = create_context(account_from_balance=operational, node_state=NodeAccount.OPENED, + unfrozen=unfrozen) + + with pytest.raises(ValueError) as error: + NodeAccountHandler().apply(transaction=transaction_request, context=mock_context) + + assert str(error.value).startswith('Value out of range') + + def test_transfer_invalid_amount_from_unfrozen_to_operational(): """ Case: transfer from unfrozen to operational with more tokens than on unfrozen account. diff --git a/testing/unit/tp/node_account/test_transfer_frozen_to_unfrozen.py b/testing/unit/tp/node_account/test_transfer_frozen_to_unfrozen.py index ed005b34..51b16d92 100644 --- a/testing/unit/tp/node_account/test_transfer_frozen_to_unfrozen.py +++ b/testing/unit/tp/node_account/test_transfer_frozen_to_unfrozen.py @@ -26,6 +26,9 @@ from testing.conftest import create_signer from testing.mocks.stub import StubContext from testing.utils.client import proto_error_msg +from .shared import ( + MAX_UINT64 +) RANDOM_NODE_PUBLIC_KEY = '039d6881f0a71d05659e1f40b443684b93c7b7c504ea23ea8949ef5216a2236940' @@ -178,6 +181,48 @@ def test_transfer_from_frozen_to_unfrozen(): assert node_account_reputation.frozen == FROZEN - transfer_value assert node_account_reputation.unfrozen == UNFROZEN + transfer_value +def test_transfer_from_frozen_to_unfrozen_integer_overflow(): + """ + Case: transfer from frozen to unfrozen such that it will cause integer overflow. + Expect: integer overflow exception is raised. + """ + transfer_value = 1 + + internal_transfer_payload = NodeAccountInternalTransferPayload() + internal_transfer_payload.value = transfer_value + + transaction_payload = TransactionPayload() + transaction_payload.method = NodeAccountMethod.TRANSFER_FROM_FROZEN_TO_UNFROZEN + transaction_payload.data = internal_transfer_payload.SerializeToString() + + serialized_transaction_payload = transaction_payload.SerializeToString() + + transaction_header = TransactionHeader( + signer_public_key=RANDOM_NODE_PUBLIC_KEY, + family_name=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get('family_name'), + family_version=TRANSACTION_REQUEST_ACCOUNT_HANDLER_PARAMS.get('family_version'), + inputs=INPUTS, + outputs=OUTPUTS, + dependencies=[], + payload_sha512=hash512(data=serialized_transaction_payload), + batcher_public_key=RANDOM_NODE_PUBLIC_KEY, + nonce=time.time().hex().encode(), + ) + + serialized_header = transaction_header.SerializeToString() + + transaction_request = TpProcessRequest( + header=transaction_header, + payload=serialized_transaction_payload, + signature=create_signer(private_key=NODE_ACCOUNT_FROM_PRIVATE_KEY).sign(serialized_header), + ) + + mock_context = create_context(account_from_frozen_balance=MINIMUM_STAKE+1, account_to_unfrozen_balance=MAX_UINT64) + + with pytest.raises(ValueError) as error: + NodeAccountHandler().apply(transaction=transaction_request, context=mock_context) + + assert str(error.value).startswith('Value out of range') def test_transfer_from_frozen_to_unfrozen_low_frozen_balance(): """