diff --git a/plenum/server/request_handlers/txn_author_agreement_handler.py b/plenum/server/request_handlers/txn_author_agreement_handler.py index 5c8a829e8c..c7fb6d11c2 100644 --- a/plenum/server/request_handlers/txn_author_agreement_handler.py +++ b/plenum/server/request_handlers/txn_author_agreement_handler.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import Optional from common.exceptions import LogicError @@ -19,7 +20,12 @@ def __init__(self, database_manager: DatabaseManager): super().__init__(database_manager, TXN_AUTHOR_AGREEMENT, CONFIG_LEDGER_ID) def static_validation(self, request: Request): - pass + self._validate_request_type(request) + operation, identifier, req_id = request.operation, request.identifier, request.reqId + self._validate_ts(operation.get(TXN_AUTHOR_AGREEMENT_RETIREMENT_TS), + identifier, req_id, TXN_AUTHOR_AGREEMENT_RETIREMENT_TS) + self._validate_ts(operation.get(TXN_AUTHOR_AGREEMENT_RATIFICATION_TS), + identifier, req_id, TXN_AUTHOR_AGREEMENT_RATIFICATION_TS) def dynamic_validation(self, request: Request, req_pp_time: Optional[int]): self._validate_request_type(request) @@ -134,3 +140,12 @@ def _validate_update_taa(self, request, digest): if last_taa_digest == digest: raise InvalidClientRequest(request.identifier, request.reqId, "The latest transaction author agreement cannot be retired.") + + def _validate_ts(self, ts, identifier, req_id, field_name): + if not ts: + return + try: + datetime.utcfromtimestamp(ts) + except ValueError: + raise InvalidClientRequest(identifier, req_id, + "{} = {} is out of range.".format(field_name, ts)) diff --git a/plenum/server/request_managers/write_request_manager.py b/plenum/server/request_managers/write_request_manager.py index 37c6768377..bf2c796703 100644 --- a/plenum/server/request_managers/write_request_manager.py +++ b/plenum/server/request_managers/write_request_manager.py @@ -346,7 +346,12 @@ def do_taa_validation(self, request: Request, req_pp_time: int, config): ) r_taa_a_ts = request.taaAcceptance[f.TAA_ACCEPTANCE_TIME.nm] - datetime_r_taa = datetime.utcfromtimestamp(r_taa_a_ts) + try: + datetime_r_taa = datetime.utcfromtimestamp(r_taa_a_ts) + except ValueError: + raise InvalidClientTaaAcceptanceError( + request.identifier, request.reqId, + "TAA_ACCEPTANCE_TIME = {} is out of range".format(r_taa_a_ts)) if datetime_r_taa.time() != time(0): raise InvalidClientTaaAcceptanceError( request.identifier, request.reqId, diff --git a/plenum/test/txn_author_agreement/acceptance/test_taa_acceptance_validation.py b/plenum/test/txn_author_agreement/acceptance/test_taa_acceptance_validation.py index 68ed60ca41..2a400b4638 100644 --- a/plenum/test/txn_author_agreement/acceptance/test_taa_acceptance_validation.py +++ b/plenum/test/txn_author_agreement/acceptance/test_taa_acceptance_validation.py @@ -106,6 +106,21 @@ def test_taa_acceptance_mechanism_inappropriate( validate_taa_acceptance(request_dict) +def test_taa_acceptance_with_incorrect_time( + validate_taa_acceptance, validation_error, + request_dict +): + request_dict[f.TAA_ACCEPTANCE.nm][f.TAA_ACCEPTANCE_TIME.nm] *= 1000 + with pytest.raises( + validation_error, + match=( + r"TAA_ACCEPTANCE_TIME = {} is " + r"out of range.".format(request_dict[f.TAA_ACCEPTANCE.nm][f.TAA_ACCEPTANCE_TIME.nm]) + ) + ): + validate_taa_acceptance(request_dict) + + def test_taa_acceptance_time_near_lower_threshold( tconf, txnPoolNodeSet, validate_taa_acceptance, validation_error, turn_off_freshness_state_update, max_last_accepted_pre_prepare_time, diff --git a/plenum/test/txn_author_agreement/test_txn_author_agreement.py b/plenum/test/txn_author_agreement/test_txn_author_agreement.py index 31d32dd91e..c0f36ae484 100644 --- a/plenum/test/txn_author_agreement/test_txn_author_agreement.py +++ b/plenum/test/txn_author_agreement/test_txn_author_agreement.py @@ -97,6 +97,29 @@ def test_create_txn_author_agreement_with_ratified_from_future_fails(looper, set ratified=get_utc_epoch() + 600) +def test_create_txn_author_agreement_with_milliseconds_ratified_fails(looper, set_txn_author_agreement_aml, + sdk_pool_handle, sdk_wallet_trustee): + ratified = get_utc_epoch() * 1000 + with pytest.raises(RequestNackedException, + match="{} = {} is out of range.".format(TXN_AUTHOR_AGREEMENT_RATIFICATION_TS, ratified)): + sdk_send_txn_author_agreement(looper, sdk_pool_handle, sdk_wallet_trustee, + version=randomString(16), + text=randomString(1024), + ratified=ratified) + + +def test_create_txn_author_agreement_with_milliseconds_retired_fails(looper, set_txn_author_agreement_aml, + sdk_pool_handle, sdk_wallet_trustee): + retired = get_utc_epoch() * 1000 + with pytest.raises(RequestNackedException, + match="{} = {} is out of range.".format(TXN_AUTHOR_AGREEMENT_RETIREMENT_TS, retired)): + sdk_send_txn_author_agreement(looper, sdk_pool_handle, sdk_wallet_trustee, + version=randomString(16), + text=randomString(1024), + ratified=get_utc_epoch() - 600, + retired=retired) + + @pytest.mark.parametrize('retired_offset', [-600, 600]) def test_create_txn_author_agreement_with_retired_date_fails(looper, set_txn_author_agreement_aml, sdk_pool_handle, sdk_wallet_trustee,