diff --git a/indy_node/server/request_handlers/domain_req_handlers/attribute_handler.py b/indy_node/server/request_handlers/domain_req_handlers/attribute_handler.py index d0204ab15..6ea4a839e 100644 --- a/indy_node/server/request_handlers/domain_req_handlers/attribute_handler.py +++ b/indy_node/server/request_handlers/domain_req_handlers/attribute_handler.py @@ -1,6 +1,7 @@ from indy_common.state import domain from indy_common.constants import ATTRIB +from indy_node.server.request_handlers.utils import validate_attrib_keys from plenum.common.constants import DOMAIN_LEDGER_ID, RAW, ENC, HASH, TARGET_NYM from plenum.common.exceptions import InvalidClientRequest, UnauthorizedClientRequest @@ -19,7 +20,7 @@ def static_validation(self, request: Request): self._validate_request_type(request) identifier, req_id, operation = get_request_data(request) - if not self._validate_attrib_keys(operation): + if not validate_attrib_keys(operation): raise InvalidClientRequest(identifier, req_id, '{} should have one and only one of ' '{}, {}, {}' @@ -66,8 +67,3 @@ def _update_state_with_single_txn(self, txn, is_committed=True) -> None: def __has_nym(self, nym, isCommitted: bool = True): return self.database_manager.idr_cache.hasNym(nym, isCommitted=isCommitted) - - @staticmethod - def _validate_attrib_keys(operation): - data_keys = {RAW, ENC, HASH}.intersection(set(operation.keys())) - return len(data_keys) == 1 diff --git a/indy_node/server/request_handlers/domain_req_handlers/claim_def_handler.py b/indy_node/server/request_handlers/domain_req_handlers/claim_def_handler.py index 1b8702a5d..90ad418bb 100644 --- a/indy_node/server/request_handlers/domain_req_handlers/claim_def_handler.py +++ b/indy_node/server/request_handlers/domain_req_handlers/claim_def_handler.py @@ -41,10 +41,10 @@ def dynamic_validation(self, request: Request): "Mentioned seqNo ({}) isn't seqNo of the schema.".format(ref)) # only owner can update claim_def, # because his identifier is the primary key of claim_def - self.write_req_validator.validate(request, - [AuthActionAdd(txn_type=CLAIM_DEF, - field='*', - value='*')]) + self.write_request_validator.validate(request, + [AuthActionAdd(txn_type=CLAIM_DEF, + field='*', + value='*')]) def gen_txn_path(self, txn): self._validate_txn_type(txn) diff --git a/indy_node/server/request_handlers/domain_req_handlers/nym_handler.py b/indy_node/server/request_handlers/domain_req_handlers/nym_handler.py index f604399fa..b62a13b2a 100644 --- a/indy_node/server/request_handlers/domain_req_handlers/nym_handler.py +++ b/indy_node/server/request_handlers/domain_req_handlers/nym_handler.py @@ -92,14 +92,14 @@ def _update_state_with_single_txn(self, txn, is_committed=True): def _validate_new_nym(self, request, operation): role = operation.get(ROLE) - self.write_req_validator.validate(request, - [AuthActionAdd(txn_type=NYM, - field=ROLE, - value=role)]) + self.write_request_validator.validate(request, + [AuthActionAdd(txn_type=NYM, + field=ROLE, + value=role)]) def _validate_existing_nym(self, request, operation, nym_data): origin = request.identifier - owner = self.idrCache.getOwnerFor(operation[TARGET_NYM], isCommitted=False) + owner = self.database_manager.idr_cache.getOwnerFor(operation[TARGET_NYM], isCommitted=False) is_owner = origin == owner updateKeys = [ROLE, VERKEY] @@ -107,9 +107,9 @@ def _validate_existing_nym(self, request, operation, nym_data): if key in operation: newVal = operation[key] oldVal = nym_data.get(key) - self.write_req_validator.validate(request, - [AuthActionEdit(txn_type=NYM, - field=key, - old_value=oldVal, - new_value=newVal, - is_owner=is_owner)]) + self.write_request_validator.validate(request, + [AuthActionEdit(txn_type=NYM, + field=key, + old_value=oldVal, + new_value=newVal, + is_owner=is_owner)]) diff --git a/indy_node/server/request_handlers/domain_req_handlers/revoc_reg_entry_handler.py b/indy_node/server/request_handlers/domain_req_handlers/revoc_reg_entry_handler.py index f73cc8db5..bc6b85507 100644 --- a/indy_node/server/request_handlers/domain_req_handlers/revoc_reg_entry_handler.py +++ b/indy_node/server/request_handlers/domain_req_handlers/revoc_reg_entry_handler.py @@ -1,3 +1,5 @@ +from typing import Dict, Callable + from indy_common.state import domain from indy_common.constants import REVOC_REG_ENTRY, REVOC_REG_DEF_ID, VALUE, ISSUANCE_TYPE @@ -14,10 +16,10 @@ class RevocRegEntryHandler(WriteRequestHandler): def __init__(self, database_manager: DatabaseManager, get_revoc_reg_entry: GetRevocRegHandler, - revocation_strategy_map: dict): + get_revocation_strategy: Callable): super().__init__(database_manager, REVOC_REG_ENTRY, DOMAIN_LEDGER_ID) self.get_revoc_reg_entry = get_revoc_reg_entry - self.revocation_strategy_map = revocation_strategy_map + self.get_revocation_strategy = get_revocation_strategy def static_validation(self, request: Request): pass @@ -50,6 +52,3 @@ def _update_state_with_single_txn(self, txn, isCommitted=False): revoc_def[VALUE][ISSUANCE_TYPE]) writer = writer_cls(self.state) writer.write(current_entry, txn) - - def get_revocation_strategy(self, type): - return self.revocation_strategy_map.get(type, None) diff --git a/indy_node/server/request_handlers/domain_req_handlers/schema_handler.py b/indy_node/server/request_handlers/domain_req_handlers/schema_handler.py index a462df787..14d8751d8 100644 --- a/indy_node/server/request_handlers/domain_req_handlers/schema_handler.py +++ b/indy_node/server/request_handlers/domain_req_handlers/schema_handler.py @@ -34,18 +34,18 @@ def dynamic_validation(self, request: Request): schema_version = get_write_schema_version(request) schema, _, _, _ = self.get_schema_handler.get_schema( author=identifier, - schemaName=schema_name, - schemaVersion=schema_version, + schema_name=schema_name, + schema_version=schema_version, with_proof=False) if schema: raise InvalidClientRequest(identifier, req_id, '{} can have one and only one SCHEMA with ' 'name {} and version {}' .format(identifier, schema_name, schema_version)) - self.write_req_validator.validate(request, - [AuthActionAdd(txn_type=SCHEMA, - field='*', - value='*')]) + self.write_request_validator.validate(request, + [AuthActionAdd(txn_type=SCHEMA, + field='*', + value='*')]) def gen_txn_path(self, txn): self._validate_txn_type(txn) diff --git a/indy_node/server/request_handlers/read_req_handlers/get_attribute_handler.py b/indy_node/server/request_handlers/read_req_handlers/get_attribute_handler.py index f40dd8c10..ad883378a 100644 --- a/indy_node/server/request_handlers/read_req_handlers/get_attribute_handler.py +++ b/indy_node/server/request_handlers/read_req_handlers/get_attribute_handler.py @@ -1,5 +1,76 @@ +from indy_common.state import domain + +from indy_common.constants import ATTRIB, GET_ATTR + from indy_node.server.request_handlers.read_request_handler import ReadRequestHandler +from indy_node.server.request_handlers.utils import validate_attrib_keys +from plenum.common.constants import RAW, ENC, HASH, TARGET_NYM, DOMAIN_LEDGER_ID +from plenum.common.exceptions import InvalidClientRequest +from plenum.common.request import Request +from plenum.common.txn_util import get_request_data +from plenum.server.database_manager import DatabaseManager +from stp_core.common.log import getlogger + +logger = getlogger() class GetAttributeHandler(ReadRequestHandler): - pass + + def __init__(self, database_manager: DatabaseManager): + super().__init__(database_manager, GET_ATTR, DOMAIN_LEDGER_ID) + + def get_result(self, request: Request): + self._validate_request_type(request) + identifier, req_id, operation = get_request_data(request) + if not validate_attrib_keys(operation): + raise InvalidClientRequest(identifier, req_id, + '{} should have one and only one of ' + '{}, {}, {}' + .format(ATTRIB, RAW, ENC, HASH)) + nym = operation[TARGET_NYM] + if RAW in operation: + attr_type = RAW + elif ENC in operation: + # If attribute is encrypted, it will be queried by its hash + attr_type = ENC + else: + attr_type = HASH + attr_key = operation[attr_type] + value, last_seq_no, last_update_time, proof = \ + self.get_attr(did=nym, key=attr_key, attr_type=attr_type) + attr = None + if value is not None: + if HASH in operation: + attr = attr_key + else: + attr = value + return self.make_result(request=request, + data=attr, + last_seq_no=last_seq_no, + update_time=last_update_time, + proof=proof) + + def get_attr(self, + did: str, + key: str, + attr_type, + is_committed=True) -> (str, int, int, list): + assert did is not None + assert key is not None + path = domain.make_state_path_for_attr(did, key, attr_type == HASH) + try: + hashed_val, last_seq_no, last_update_time, proof = \ + self.lookup(path, is_committed, with_proof=True) + except KeyError: + return None, None, None, None + if not hashed_val or hashed_val == '': + # Its a HASH attribute + return hashed_val, last_seq_no, last_update_time, proof + else: + try: + value = self.database_manager.attribute_store.get(hashed_val) + except KeyError: + logger.error('Could not get value from attribute store for {}' + .format(hashed_val)) + return None, None, None, None + return value, last_seq_no, last_update_time, proof diff --git a/indy_node/server/request_handlers/read_req_handlers/get_claim_def_handler.py b/indy_node/server/request_handlers/read_req_handlers/get_claim_def_handler.py index a3ecba571..5c4a666f3 100644 --- a/indy_node/server/request_handlers/read_req_handlers/get_claim_def_handler.py +++ b/indy_node/server/request_handlers/read_req_handlers/get_claim_def_handler.py @@ -1,5 +1,51 @@ +from indy_common.state import domain + +from indy_common.constants import CLAIM_DEF_SIGNATURE_TYPE, GET_CLAIM_DEF +from indy_common.req_utils import get_read_claim_def_from, get_read_claim_def_signature_type, \ + get_read_claim_def_schema_ref, get_read_claim_def_tag + from indy_node.server.request_handlers.read_request_handler import ReadRequestHandler +from plenum.common.constants import DOMAIN_LEDGER_ID +from plenum.common.request import Request +from plenum.server.database_manager import DatabaseManager class GetClaimDefHandler(ReadRequestHandler): - pass + + def __init__(self, database_manager: DatabaseManager): + super().__init__(database_manager, GET_CLAIM_DEF, DOMAIN_LEDGER_ID) + + def get_result(self, request: Request): + self._validate_request_type(request) + frm = get_read_claim_def_from(request) + signature_type = get_read_claim_def_signature_type(request) + schema_ref = get_read_claim_def_schema_ref(request) + tag = get_read_claim_def_tag(request) + keys, last_seq_no, last_update_time, proof = self.get_claim_def( + author=frm, + schema_seq_no=schema_ref, + signature_type=signature_type, + tag=tag + ) + result = self.make_result(request=request, + data=keys, + last_seq_no=last_seq_no, + update_time=last_update_time, + proof=proof) + result[CLAIM_DEF_SIGNATURE_TYPE] = signature_type + return result + + def get_claim_def(self, + author: str, + schema_seq_no: str, + signature_type, + tag, + is_committed=True) -> (str, int, int, list): + assert author is not None + assert schema_seq_no is not None + path = domain.make_state_path_for_claim_def(author, schema_seq_no, signature_type, tag) + try: + keys, seq_no, last_update_time, proof = self.lookup(path, is_committed, with_proof=True) + return keys, seq_no, last_update_time, proof + except KeyError: + return None, None, None, None diff --git a/indy_node/server/request_handlers/read_req_handlers/get_nym_handler.py b/indy_node/server/request_handlers/read_req_handlers/get_nym_handler.py index f9ab2668a..385c31588 100644 --- a/indy_node/server/request_handlers/read_req_handlers/get_nym_handler.py +++ b/indy_node/server/request_handlers/read_req_handlers/get_nym_handler.py @@ -1,5 +1,42 @@ +from indy_common.constants import GET_NYM + +from common.serializers.serialization import domain_state_serializer +from indy_common.state import domain from indy_node.server.request_handlers.read_request_handler import ReadRequestHandler +from plenum.common.constants import TARGET_NYM, TXN_TIME, DOMAIN_LEDGER_ID +from plenum.common.request import Request +from plenum.common.types import f +from plenum.server.database_manager import DatabaseManager + class GetNymHandler(ReadRequestHandler): - pass + + def __init__(self, database_manager: DatabaseManager): + super().__init__(database_manager, GET_NYM, DOMAIN_LEDGER_ID) + + def get_result(self, request: Request): + self._validate_request_type(request) + nym = request.operation[TARGET_NYM] + path = domain.make_state_path_for_nym(nym) + nym_data, proof = self.get_value_from_state(path, with_proof=True) + if nym_data: + nym_data = domain_state_serializer.deserialize(nym_data) + nym_data[TARGET_NYM] = nym + data = domain_state_serializer.serialize(nym_data) + seq_no = nym_data[f.SEQ_NO.nm] + update_time = nym_data[TXN_TIME] + else: + data = None + seq_no = None + update_time = None + + # TODO: add update time here! + result = self.make_result(request=request, + data=data, + last_seq_no=seq_no, + update_time=update_time, + proof=proof) + + result.update(request.operation) + return result diff --git a/indy_node/server/request_handlers/read_req_handlers/get_revoc_reg_def_handler.py b/indy_node/server/request_handlers/read_req_handlers/get_revoc_reg_def_handler.py index 291f83699..8422c94d6 100644 --- a/indy_node/server/request_handlers/read_req_handlers/get_revoc_reg_def_handler.py +++ b/indy_node/server/request_handlers/read_req_handlers/get_revoc_reg_def_handler.py @@ -1,5 +1,27 @@ +from indy_common.constants import ID, GET_REVOC_REG_DEF + from indy_node.server.request_handlers.read_request_handler import ReadRequestHandler +from plenum.common.constants import DOMAIN_LEDGER_ID +from plenum.common.request import Request +from plenum.server.database_manager import DatabaseManager class GetRevocRegDefHandler(ReadRequestHandler): - pass + + def __init__(self, database_manager: DatabaseManager): + super().__init__(database_manager, GET_REVOC_REG_DEF, DOMAIN_LEDGER_ID) + + def get_result(self, request: Request): + self._validate_request_type(request) + state_path = request.operation.get(ID, None) + assert state_path + try: + keys, last_seq_no, last_update_time, proof = self.lookup(state_path, isCommitted=True, with_proof=True) + except KeyError: + keys, last_seq_no, last_update_time, proof = None, None, None, None + result = self.make_result(request=request, + data=keys, + last_seq_no=last_seq_no, + update_time=last_update_time, + proof=proof) + return result diff --git a/indy_node/server/request_handlers/read_req_handlers/get_revoc_reg_delta_handler.py b/indy_node/server/request_handlers/read_req_handlers/get_revoc_reg_delta_handler.py index da2ded99a..6a8fd116b 100644 --- a/indy_node/server/request_handlers/read_req_handlers/get_revoc_reg_delta_handler.py +++ b/indy_node/server/request_handlers/read_req_handlers/get_revoc_reg_delta_handler.py @@ -1,5 +1,141 @@ +from collections import Callable + +from indy_common.state import domain + +from indy_common.constants import FROM, TO, REVOC_REG_DEF_ID, ISSUANCE_TYPE, REVOKED, ISSUED, VALUE, REVOC_TYPE, \ + ACCUM_TO, STATE_PROOF_FROM, ACCUM_FROM, GET_REVOC_REG_DELTA + from indy_node.server.request_handlers.read_request_handler import ReadRequestHandler +from indy_node.server.request_handlers.utils import StateValue +from plenum.common.constants import DOMAIN_LEDGER_ID +from plenum.common.request import Request +from plenum.server.database_manager import DatabaseManager class GetRevocRegDeltaHandler(ReadRequestHandler): - pass + + def __init__(self, database_manager: DatabaseManager, + get_revocation_strategy: Callable): + super().__init__(database_manager, GET_REVOC_REG_DELTA, DOMAIN_LEDGER_ID) + self.get_revocation_strategy = get_revocation_strategy + + def get_result(self, request: Request): + """ + For getting reply we need: + 1. Get REVOC_REG_ENTRY by "TO" timestamp from state + 2. If FROM is given in request, then Get REVOC_REG_ENTRY by "FROM" timestamp from state + 3. Get ISSUANCE_TYPE for REVOC_REG_DEF (revoked/issued strategy) + 4. Compute issued and revoked indices by corresponding strategy + 5. Make result + 5.1 Now, if "FROM" is presented in request, then STATE_PROOF_FROM and ACCUM (revocation entry for "FROM" timestamp) + will added into data section + 5.2 If not, then only STATE_PROOF for "TO" revocation entry will added + :param request: + :return: Reply + """ + self._validate_request_type(request) + req_ts_from = request.operation.get(FROM, None) + req_ts_to = request.operation.get(TO) + revoc_reg_def_id = request.operation.get(REVOC_REG_DEF_ID) + reply = None + """ + Get root hash for "to" timestamp + Get REVOC_REG_ENTRY and ACCUM record for timestamp "to" + """ + path_to_reg_entry = domain.make_state_path_for_revoc_reg_entry(revoc_reg_def_id=revoc_reg_def_id) + path_to_reg_entry_accum = domain.make_state_path_for_revoc_reg_entry_accum(revoc_reg_def_id=revoc_reg_def_id) + + entry_to = self._get_reg_entry_by_timestamp(req_ts_to, path_to_reg_entry) + accum_to = self._get_reg_entry_accum_by_timestamp(req_ts_to, path_to_reg_entry_accum) + entry_from = StateValue() + accum_from = StateValue() + + if accum_to.value and entry_to.value: + """Get issuance type from REVOC_REG_DEF""" + encoded_revoc_reg_def = self.state.get_for_root_hash(entry_to.root_hash, + revoc_reg_def_id) + if encoded_revoc_reg_def: + revoc_reg_def, _, _ = domain.decode_state_value(encoded_revoc_reg_def) + strategy_cls = self.get_revocation_strategy(revoc_reg_def[VALUE][ISSUANCE_TYPE]) + issued_to = entry_to.value[VALUE].get(ISSUED, []) + revoked_to = entry_to.value[VALUE].get(REVOKED, []) + if req_ts_from: + """Get REVOC_REG_ENTRY and ACCUM records for timestamp from if exist""" + entry_from = self._get_reg_entry_by_timestamp(req_ts_from, path_to_reg_entry) + accum_from = self._get_reg_entry_accum_by_timestamp(req_ts_from, path_to_reg_entry_accum) + if req_ts_from and entry_from.value and accum_from.value: + """Compute issued/revoked lists corresponding with ISSUANCE_TYPE strategy""" + issued_from = entry_from.value[VALUE].get(ISSUED, []) + revoked_from = entry_from.value[VALUE].get(REVOKED, []) + result_issued, result_revoked = strategy_cls.get_delta({ISSUED: issued_to, + REVOKED: revoked_to}, + {ISSUED: issued_from, + REVOKED: revoked_from}) + else: + result_issued, result_revoked = strategy_cls.get_delta({ISSUED: issued_to, + REVOKED: revoked_to}, + None) + reply = { + REVOC_REG_DEF_ID: revoc_reg_def_id, + REVOC_TYPE: revoc_reg_def.get(REVOC_TYPE), + VALUE: { + ACCUM_TO: accum_to.value if entry_from.value else entry_to.value, + ISSUED: result_issued, + REVOKED: result_revoked + } + + } + """If we got "from" timestamp, then add state proof into "data" section of reply""" + if req_ts_from and accum_from.value: + reply[STATE_PROOF_FROM] = accum_from.proof + reply[VALUE][ACCUM_FROM] = accum_from.value + + if accum_to and entry_to: + seq_no = accum_to.seq_no if entry_from.value else entry_to.seq_no + update_time = accum_to.update_time if entry_from.value else entry_to.update_time + proof = accum_to.proof if entry_from.value else entry_to.proof + else: + seq_no = None + update_time = None + proof = None + + return self.make_result(request=request, + data=reply, + last_seq_no=seq_no, + update_time=update_time, + proof=proof) + + def _get_reg_entry_by_timestamp(self, timestamp, path_to_reg_entry): + reg_entry = None + seq_no = None + last_update_time = None + reg_entry_proof = None + past_root = self.ts_store.get_equal_or_prev(timestamp) + if past_root: + encoded_entry, reg_entry_proof = self.get_value_from_state(path_to_reg_entry, + head_hash=past_root, + with_proof=True) + if encoded_entry: + reg_entry, seq_no, last_update_time = domain.decode_state_value(encoded_entry) + return StateValue(root_hash=past_root, + value=reg_entry, + seq_no=seq_no, + update_time=last_update_time, + proof=reg_entry_proof) + + def _get_reg_entry_accum_by_timestamp(self, timestamp, path_to_reg_entry_accum): + reg_entry_accum = None + seq_no = None + last_update_time = None + reg_entry_accum_proof = None + past_root = self.ts_store.get_equal_or_prev(timestamp) + if past_root: + encoded_entry, reg_entry_accum_proof = self.get_value_from_state( + path_to_reg_entry_accum, head_hash=past_root, with_proof=True) + if encoded_entry: + reg_entry_accum, seq_no, last_update_time = domain.decode_state_value(encoded_entry) + return StateValue(root_hash=past_root, + value=reg_entry_accum, + seq_no=seq_no, + update_time=last_update_time, + proof=reg_entry_accum_proof) diff --git a/indy_node/server/request_handlers/read_req_handlers/get_revoc_reg_handler.py b/indy_node/server/request_handlers/read_req_handlers/get_revoc_reg_handler.py index b49416b5f..315dbd90f 100644 --- a/indy_node/server/request_handlers/read_req_handlers/get_revoc_reg_handler.py +++ b/indy_node/server/request_handlers/read_req_handlers/get_revoc_reg_handler.py @@ -1,14 +1,52 @@ +from indy_common.constants import TIMESTAMP, REVOC_REG_DEF_ID, GET_REVOC_REG + from indy_common.state import domain from indy_node.server.request_handlers.read_request_handler import ReadRequestHandler +from indy_node.server.request_handlers.utils import StateValue +from plenum.common.constants import DOMAIN_LEDGER_ID from plenum.common.exceptions import InvalidClientRequest +from plenum.common.request import Request +from plenum.server.database_manager import DatabaseManager class GetRevocRegHandler(ReadRequestHandler): + + def __init__(self, database_manager: DatabaseManager): + super().__init__(database_manager, GET_REVOC_REG, DOMAIN_LEDGER_ID) + + def get_result(self, request: Request): + self._validate_request_type(request) + req_ts = request.operation.get(TIMESTAMP) + revoc_reg_def_id = request.operation.get(REVOC_REG_DEF_ID) + # Get root hash corresponding with given timestamp + past_root = self.database_manager.ts_store.get_equal_or_prev(req_ts) + # Path to corresponding ACCUM record in state + path = domain.make_state_path_for_revoc_reg_entry_accum(revoc_reg_def_id=revoc_reg_def_id) + entry_state = StateValue() + if past_root is not None: + encoded_entry, proof = self.get_value_from_state(path, + head_hash=past_root, + with_proof=True) + entry_state.proof = proof + if encoded_entry: + revoc_reg_entry_accum, seq_no, last_update_time = domain.decode_state_value(encoded_entry) + entry_state = StateValue(root_hash=past_root, + value=revoc_reg_entry_accum, + seq_no=seq_no, + update_time=last_update_time, + proof=proof) + + return self.make_result(request=request, + data=entry_state.value, + last_seq_no=entry_state.seq_no, + update_time=entry_state.update_time, + proof=entry_state.proof) + def get_current_revoc_entry_and_revoc_def(self, author_did, revoc_reg_def_id, req_id): assert revoc_reg_def_id - current_entry, _, _, _ = self.getRevocDefEntry(revoc_reg_def_id=revoc_reg_def_id, - isCommitted=False) + current_entry, _, _, _ = self._get_revoc_def_entry(revoc_reg_def_id=revoc_reg_def_id, + isCommitted=False) revoc_def, _, _, _ = self.lookup(revoc_reg_def_id, isCommitted=False, with_proof=False) if revoc_def is None: raise InvalidClientRequest(author_did, @@ -16,9 +54,9 @@ def get_current_revoc_entry_and_revoc_def(self, author_did, revoc_reg_def_id, re "There is no any REVOC_REG_DEF by path: {}".format(revoc_reg_def_id)) return current_entry, revoc_def - def getRevocDefEntry(self, - revoc_reg_def_id, - isCommitted=True) -> (str, int, int, list): + def _get_revoc_def_entry(self, + revoc_reg_def_id, + isCommitted=True) -> (str, int, int, list): assert revoc_reg_def_id path = domain.make_state_path_for_revoc_reg_entry(revoc_reg_def_id=revoc_reg_def_id) try: diff --git a/indy_node/server/request_handlers/read_req_handlers/get_schema_handler.py b/indy_node/server/request_handlers/read_req_handlers/get_schema_handler.py index a0ed7a2a1..5cd5ae67d 100644 --- a/indy_node/server/request_handlers/read_req_handlers/get_schema_handler.py +++ b/indy_node/server/request_handlers/read_req_handlers/get_schema_handler.py @@ -1,22 +1,55 @@ +from indy_common.constants import SCHEMA_NAME, SCHEMA_VERSION, GET_SCHEMA +from indy_common.req_utils import get_read_schema_from, get_read_schema_name, get_read_schema_version from indy_common.state import domain from indy_node.server.request_handlers.read_request_handler import ReadRequestHandler +from plenum.common.constants import DOMAIN_LEDGER_ID +from plenum.common.request import Request +from plenum.server.database_manager import DatabaseManager class GetSchemaHandler(ReadRequestHandler): + def __init__(self, database_manager: DatabaseManager): + super().__init__(database_manager, GET_SCHEMA, DOMAIN_LEDGER_ID) + + def get_result(self, request: Request): + self._validate_request_type(request) + author_did = get_read_schema_from(request) + schema_name = get_read_schema_name(request) + schema_version = get_read_schema_version(request) + schema, last_seq_no, last_update_time, proof = self.get_schema( + author=author_did, + schema_name=schema_name, + schema_version=schema_version, + with_proof=True + ) + # TODO: we have to do this since SCHEMA has a bit different format than other txns + # (it has NAME and VERSION inside DATA, and it's not part of the state value, but state key) + if schema is None: + schema = {} + schema.update({ + SCHEMA_NAME: schema_name, + SCHEMA_VERSION: schema_version + }) + return self.make_result(request=request, + data=schema, + last_seq_no=last_seq_no, + update_time=last_update_time, + proof=proof) + def get_schema(self, author: str, - schemaName: str, - schemaVersion: str, - isCommitted=True, + schema_name: str, + schema_version: str, + is_committed=True, with_proof=True) -> (str, int, int, list): assert author is not None - assert schemaName is not None - assert schemaVersion is not None - path = domain.make_state_path_for_schema(author, schemaName, schemaVersion) + assert schema_name is not None + assert schema_version is not None + path = domain.make_state_path_for_schema(author, schema_name, schema_version) try: - keys, seqno, lastUpdateTime, proof = self.lookup(path, isCommitted, with_proof=with_proof) - return keys, seqno, lastUpdateTime, proof + keys, seq_no, last_update_time, proof = self.lookup(path, is_committed, with_proof=with_proof) + return keys, seq_no, last_update_time, proof except KeyError: return None, None, None, None diff --git a/indy_node/server/request_handlers/read_request_handler.py b/indy_node/server/request_handlers/read_request_handler.py index 8cafef50d..1a6d45d3e 100644 --- a/indy_node/server/request_handlers/read_request_handler.py +++ b/indy_node/server/request_handlers/read_request_handler.py @@ -1,4 +1,7 @@ from indy_common.state import domain +from plenum.common.constants import DATA, TXN_TIME, STATE_PROOF +from plenum.common.plenum_protocol_version import PlenumProtocolVersion +from plenum.common.types import f from plenum.server.request_handlers.handler_interfaces.read_request_handler import \ ReadRequestHandler as PReadRequestHandler @@ -6,19 +9,35 @@ class ReadRequestHandler(PReadRequestHandler): - def lookup(self, path, isCommitted=True, with_proof=False) -> (str, int): + def lookup(self, path, is_committed=True, with_proof=False) -> (str, int): """ Queries state for data on specified path :param path: path to data - :param isCommitted: queries the committed state root if True else the uncommitted root + :param is_committed: queries the committed state root if True else the uncommitted root :param with_proof: creates proof if True :return: data """ assert path is not None - head_hash = self.state.committedHeadHash if isCommitted else self.state.headHash - encoded, proof = self.get_value_from_state(path, head_hash, with_proof=with_proof) + head_hash = self.state.committedHeadHash if is_committed else self.state.headHash + encoded, proof = self._get_value_from_state(path, head_hash, with_proof=with_proof) if encoded: value, last_seq_no, last_update_time = domain.decode_state_value(encoded) return value, last_seq_no, last_update_time, proof return None, None, None, proof + + @staticmethod + def make_result(request, data, last_seq_no, update_time, proof): + result = {**request.operation, **{ + DATA: data, + f.IDENTIFIER.nm: request.identifier, + f.REQ_ID.nm: request.reqId, + f.SEQ_NO.nm: last_seq_no, + TXN_TIME: update_time + }} + if proof and request.protocolVersion and \ + request.protocolVersion >= PlenumProtocolVersion.STATE_PROOF_SUPPORT.value: + result[STATE_PROOF] = proof + + # Do not inline please, it makes debugging easier + return result diff --git a/indy_node/server/request_handlers/utils.py b/indy_node/server/request_handlers/utils.py new file mode 100644 index 000000000..32af80787 --- /dev/null +++ b/indy_node/server/request_handlers/utils.py @@ -0,0 +1,15 @@ +from plenum.common.constants import RAW, ENC, HASH + + +class StateValue: + def __init__(self, root_hash=None, value=None, seq_no=None, update_time=None, proof=None): + self.root_hash = root_hash + self.value = value + self.seq_no = seq_no + self.update_time = update_time + self.proof = proof + + +def validate_attrib_keys(operation): + data_keys = {RAW, ENC, HASH}.intersection(set(operation.keys())) + return len(data_keys) == 1 diff --git a/indy_node/test/request_handlers/helper.py b/indy_node/test/request_handlers/helper.py index 34fbefe7b..008c2bceb 100644 --- a/indy_node/test/request_handlers/helper.py +++ b/indy_node/test/request_handlers/helper.py @@ -1,5 +1,6 @@ import random +from plenum.common.exceptions import UnauthorizedClientRequest from plenum.common.txn_util import get_seq_no from plenum.common.util import randomString from plenum.test.testing_utils import FakeSomething @@ -23,3 +24,13 @@ def get_fake_ledger(): ledger.appendTxns = lambda txns: ledger.txn_list.update({get_seq_no(txn): txn for txn in txns}) return ledger + + +def get_exception(is_exception): + def exception(request, action_list): + if is_exception: + raise UnauthorizedClientRequest(None, None) + else: + pass + + return exception diff --git a/indy_node/test/request_handlers/test_claim_def_handler.py b/indy_node/test/request_handlers/test_claim_def_handler.py index 1c496f07c..ff0078a69 100644 --- a/indy_node/test/request_handlers/test_claim_def_handler.py +++ b/indy_node/test/request_handlers/test_claim_def_handler.py @@ -2,7 +2,7 @@ from indy_common.constants import NYM, CLAIM_DEF, REF from indy_node.server.request_handlers.domain_req_handlers.claim_def_handler import ClaimDefHandler -from indy_node.test.request_handlers.helper import add_to_idr +from indy_node.test.request_handlers.helper import add_to_idr, get_exception from plenum.common.constants import STEWARD from plenum.common.exceptions import InvalidClientRequest, UnauthorizedClientRequest, UnknownIdentifier @@ -10,11 +10,14 @@ from plenum.common.txn_util import reqToTxn, get_seq_no from plenum.common.util import randomString +from plenum.test.testing_utils import FakeSomething @pytest.fixture(scope="module") def claim_def_handler(db_manager): - return ClaimDefHandler(db_manager) + f = FakeSomething() + f.validate = lambda request, action_list: True + return ClaimDefHandler(db_manager, f) @pytest.fixture(scope="module") @@ -35,8 +38,7 @@ def claim_def_request(creator, schema_request): def test_claim_def_dynamic_validation_for_new_claim_def(claim_def_request, schema_request, - claim_def_handler: ClaimDefHandler, - creator): + claim_def_handler: ClaimDefHandler): schema = reqToTxn(schema_request) claim_def_request.operation[REF] = get_seq_no(schema) claim_def_handler.ledger.appendTxns([schema]) @@ -44,8 +46,8 @@ def test_claim_def_dynamic_validation_for_new_claim_def(claim_def_request, schem def test_claim_def_dynamic_validation_without_permission(claim_def_request, schema_request, - claim_def_handler: ClaimDefHandler, - creator): + claim_def_handler: ClaimDefHandler): + claim_def_handler.write_request_validator.validate = get_exception(True) schema = reqToTxn(schema_request) claim_def_request.operation[REF] = get_seq_no(schema) claim_def_handler.ledger.appendTxns([schema]) @@ -59,13 +61,11 @@ def test_claim_def_dynamic_validation_without_permission(claim_def_request, sche operation=claim_def_request.operation) with pytest.raises(UnauthorizedClientRequest) as e: claim_def_handler.dynamic_validation(request) - assert "cannot add claim def" \ - in e._excinfo[1].args[0] def test_claim_def_dynamic_validation_for_unknown_identifier(claim_def_request, schema_request, - claim_def_handler: ClaimDefHandler, - creator): + claim_def_handler: ClaimDefHandler): + claim_def_handler.write_request_validator.validate = get_exception(True) test_identifier = randomString() schema = reqToTxn(schema_request) claim_def_request.operation[REF] = get_seq_no(schema) @@ -73,13 +73,12 @@ def test_claim_def_dynamic_validation_for_unknown_identifier(claim_def_request, request = Request(identifier=test_identifier, reqId=claim_def_request.reqId, operation=claim_def_request.operation) - with pytest.raises(UnknownIdentifier) as e: + with pytest.raises(UnauthorizedClientRequest) as e: claim_def_handler.dynamic_validation(request) - assert 'unknown identifier' in e._excinfo[1].reason def test_claim_def_dynamic_validation_without_schema(claim_def_request, schema_request, - claim_def_handler: ClaimDefHandler, creator): + claim_def_handler: ClaimDefHandler): with pytest.raises(InvalidClientRequest) as e: claim_def_handler.dynamic_validation(claim_def_request) assert "Mentioned seqNo ({}) doesn't exist.".format(claim_def_request.operation[REF]) \ diff --git a/indy_node/test/request_handlers/test_nym_handler.py b/indy_node/test/request_handlers/test_nym_handler.py index f9e85b718..064944a5b 100644 --- a/indy_node/test/request_handlers/test_nym_handler.py +++ b/indy_node/test/request_handlers/test_nym_handler.py @@ -4,16 +4,19 @@ from indy_common.constants import NYM from indy_node.server.request_handlers.domain_req_handlers.nym_handler import NymHandler -from indy_node.test.request_handlers.helper import add_to_idr +from indy_node.test.request_handlers.helper import add_to_idr, get_exception from plenum.common.constants import STEWARD from plenum.common.exceptions import InvalidClientRequest, UnauthorizedClientRequest from plenum.common.request import Request from plenum.common.util import randomString +from plenum.test.testing_utils import FakeSomething @pytest.fixture(scope="module") def nym_handler(db_manager): - return NymHandler(None, db_manager) + f = FakeSomething() + f.validate = lambda request, action_list: True + return NymHandler(db_manager, f) @pytest.fixture(scope="function") @@ -75,17 +78,21 @@ def test_nym_static_validation_not_authorized_random(nym_request, nym_handler: N def test_nym_dynamic_validation_for_new_nym(nym_request, nym_handler: NymHandler, creator): - for role in Authoriser.ValidRoles: - nym_request.operation['role'] = role - with pytest.raises(UnauthorizedClientRequest): - nym_handler.dynamic_validation(nym_request) + nym_handler.write_request_validator.validate = get_exception(False) add_to_idr(nym_handler.database_manager.idr_cache, creator, STEWARD) nym_handler.dynamic_validation(nym_request) + nym_handler.write_request_validator.validate = get_exception(True) + with pytest.raises(UnauthorizedClientRequest): + nym_handler.dynamic_validation(nym_request) + def test_nym_dynamic_validation_for_existing_nym(nym_request: Request, nym_handler: NymHandler, creator): add_to_idr(nym_handler.database_manager.idr_cache, nym_request.operation['dest'], None) + nym_handler.write_request_validator.validate = get_exception(False) + add_to_idr(nym_handler.database_manager.idr_cache, creator, STEWARD) + nym_handler.dynamic_validation(nym_request) + + nym_handler.write_request_validator.validate = get_exception(True) with pytest.raises(UnauthorizedClientRequest): nym_handler.dynamic_validation(nym_request) - nym_request._identifier = nym_request.operation['dest'] - nym_handler.dynamic_validation(nym_request) diff --git a/indy_node/test/request_handlers/test_revoc_reg_entry_handler.py b/indy_node/test/request_handlers/test_revoc_reg_entry_handler.py index 96aab3af8..e03d46ce5 100644 --- a/indy_node/test/request_handlers/test_revoc_reg_entry_handler.py +++ b/indy_node/test/request_handlers/test_revoc_reg_entry_handler.py @@ -19,16 +19,15 @@ def __init__(self, state): def validate(self, current_entry, request): pass - revocation_strategy_map = { - ISSUANCE_BY_DEFAULT: Validator - } + def get_revocation_strategy(type): + return Validator def get_current_revoc_entry_and_revoc_def(author_did, revoc_reg_def_id, req_id): return True, {VALUE: {ISSUANCE_TYPE: ISSUANCE_BY_DEFAULT}} f = FakeSomething() f.get_current_revoc_entry_and_revoc_def = get_current_revoc_entry_and_revoc_def - return RevocRegEntryHandler(db_manager, f, revocation_strategy_map) + return RevocRegEntryHandler(db_manager, f, get_revocation_strategy) @pytest.fixture(scope="function") @@ -46,9 +45,9 @@ def test_revoc_reg_entry_dynamic_validation_passes(revoc_reg_entry_handler, def test_revoc_reg_entry_dynamic_validation_fails(revoc_reg_entry_handler, revoc_reg_entry_request): - def validate(self, current_entry, request): + def validate(sample): raise InvalidClientRequest('sample', 'sample') - revoc_reg_entry_handler.revocation_strategy_map[ISSUANCE_BY_DEFAULT].validate = validate + revoc_reg_entry_handler.get_revocation_strategy = validate with pytest.raises(InvalidClientRequest): revoc_reg_entry_handler.dynamic_validation(revoc_reg_entry_request) diff --git a/indy_node/test/request_handlers/test_schema_handler.py b/indy_node/test/request_handlers/test_schema_handler.py index fae5bae18..0db6eefa6 100644 --- a/indy_node/test/request_handlers/test_schema_handler.py +++ b/indy_node/test/request_handlers/test_schema_handler.py @@ -1,7 +1,7 @@ import pytest from indy_node.server.request_handlers.domain_req_handlers.schema_handler import SchemaHandler -from indy_node.test.request_handlers.helper import add_to_idr +from indy_node.test.request_handlers.helper import add_to_idr, get_exception from plenum.common.constants import TRUSTEE from plenum.common.exceptions import InvalidClientRequest, UnknownIdentifier, UnauthorizedClientRequest from plenum.test.testing_utils import FakeSomething @@ -11,11 +11,13 @@ def schema_handler(db_manager): f = FakeSomething() make_schema_exist(f, False) - return SchemaHandler(db_manager, f) + f_validator = FakeSomething() + f_validator.validate = lambda request, action_list: True + return SchemaHandler(db_manager, f, f_validator) def make_schema_exist(handler, is_exist: bool): - def exist(author, schemaName, schemaVersion, with_proof): + def exist(author, schema_name, schema_version, with_proof): return is_exist, None, None, None handler.get_schema = exist @@ -27,13 +29,8 @@ def test_schema_dynamic_validation_failed_existing_schema(schema_request, schema schema_handler.dynamic_validation(schema_request) -def test_schema_dynamic_validation_failed_ident_not_exist(schema_request, schema_handler): - make_schema_exist(schema_handler.get_schema_handler, False) - with pytest.raises(UnknownIdentifier): - schema_handler.dynamic_validation(schema_request) - - def test_schema_dynamic_validation_failed_not_authorised(schema_request, schema_handler): + schema_handler.write_request_validator.validate = get_exception(True) make_schema_exist(schema_handler.get_schema_handler, False) add_to_idr(schema_handler.database_manager.idr_cache, schema_request.identifier, None) with pytest.raises(UnauthorizedClientRequest): @@ -41,6 +38,7 @@ def test_schema_dynamic_validation_failed_not_authorised(schema_request, schema_ def test_schema_dynamic_validation_passes(schema_request, schema_handler): + schema_handler.write_request_validator.validate = get_exception(False) make_schema_exist(schema_handler.get_schema_handler, False) add_to_idr(schema_handler.database_manager.idr_cache, schema_request.identifier, TRUSTEE) schema_handler.dynamic_validation(schema_request)