Skip to content

Add validations for TAA_ACCEPTANCE_TIME and timestamps in the TAA txn #1483

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions plenum/server/client_error_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class Rejects:
"Txn Author Agreement acceptance time is inappropriate: provided {}, expected in [{}, {}]")
TAA_AML_INVALID = ClientError(206,
"Txn Author Agreement acceptance mechanism is inappropriate: provided {}, expected one of {}")
TAA_INCORRECT_ACCEPTANCE_TIME_FORMAT = ClientError(207, "TAA_ACCEPTANCE_TIME = {} is out of range")


class Nacks:
Expand Down
17 changes: 16 additions & 1 deletion plenum/server/request_handlers/txn_author_agreement_handler.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime
from typing import Optional

from common.exceptions import LogicError
Expand All @@ -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)
Expand Down Expand Up @@ -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))
8 changes: 7 additions & 1 deletion plenum/server/request_managers/write_request_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,13 @@ 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,
Rejects.TAA_INCORRECT_ACCEPTANCE_TIME_FORMAT.reason.format(r_taa_a_ts),
Rejects.TAA_INCORRECT_ACCEPTANCE_TIME_FORMAT.code)
if datetime_r_taa.time() != time(0):
raise InvalidClientTaaAcceptanceError(
request.identifier, request.reqId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
23 changes: 23 additions & 0 deletions plenum/test/txn_author_agreement/test_txn_author_agreement.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down