Exemplo n.º 1
0
 def __init__(self, ledger, state, idrCache: IdrCache, upgrader: Upgrader,
              poolManager, poolCfg: PoolConfig, write_req_validator):
     super().__init__(ledger, state)
     self.idrCache = idrCache
     self.upgrader = upgrader
     self.poolManager = poolManager
     self.poolCfg = poolCfg
     self.write_req_validator = write_req_validator
     self.constraint_serializer = ConstraintsSerializer(
         domain_state_serializer)
Exemplo n.º 2
0
 def __init__(self, ledger, state, domain_state, idrCache: IdrCache,
              upgrader: Upgrader, poolManager, poolCfg: PoolConfig,
              write_req_validator, bls_store=None, ts_store: Optional[StateTsDbStorage] = None):
     super().__init__(ledger, state, domain_state, bls_store, ts_store)
     self.idrCache = idrCache
     self.upgrader = upgrader
     self.poolManager = poolManager
     self.poolCfg = poolCfg
     self.write_req_validator = write_req_validator
     self.constraint_serializer = ConstraintsSerializer(config_state_serializer)
     self._add_query_handler(GET_AUTH_RULE, self.handle_get_auth_rule)
Exemplo n.º 3
0
class AbstractAuthRuleHandler(WriteRequestHandler, metaclass=ABCMeta):

    def __init__(self, database_manager: DatabaseManager,
                 write_req_validator: WriteRequestValidator, txn_type):
        super().__init__(database_manager, txn_type, CONFIG_LEDGER_ID)
        self.write_req_validator = write_req_validator
        self.constraint_serializer = ConstraintsSerializer(domain_state_serializer)

    def _static_validation_for_rule(self, operation, identifier, req_id):
        try:
            ConstraintCreator.create_constraint(operation.get(CONSTRAINT))
        except (ValueError, KeyError) as exp:
            raise InvalidClientRequest(identifier,
                                       req_id,
                                       exp)
        StaticAuthRuleHelper.check_auth_key(operation, identifier, req_id, self.write_req_validator.auth_map)

    def _update_auth_constraint(self, auth_key: str, constraint):
        self.state.set(AbstractAuthRuleHandler.make_state_path_for_auth_rule(auth_key),
                       self.constraint_serializer.serialize(constraint))

    def _decode_state_value(self, encoded):
        if encoded:
            return config_state_serializer.deserialize(encoded)
        return None

    @staticmethod
    def make_state_path_for_auth_rule(action_id) -> bytes:
        return "{MARKER}:{ACTION_ID}" \
            .format(MARKER=MARKER_AUTH_RULE,
                    ACTION_ID=action_id).encode()
Exemplo n.º 4
0
 def _init_write_request_validator(self):
     constraint_serializer = ConstraintsSerializer(domain_state_serializer)
     config_state = self.node.states[CONFIG_LEDGER_ID]
     self.node.write_req_validator = WriteRequestValidator(config=self.node.config,
                                                           auth_map=auth_map,
                                                           cache=self.node.idrCache,
                                                           config_state=config_state,
                                                           state_serializer=constraint_serializer,
                                                           metrics=self.node.metrics)
Exemplo n.º 5
0
 def _init_write_request_validator(self):
     constraint_serializer = ConstraintsSerializer(domain_state_serializer)
     config_state = self.states[CONFIG_LEDGER_ID]
     self.write_req_validator = WriteRequestValidator(config=self.config,
                                                      auth_map=auth_map,
                                                      cache=self.getIdrCache(),
                                                      config_state=config_state,
                                                      state_serializer=constraint_serializer,
                                                      anyone_can_write_map=anyone_can_write_map,)
Exemplo n.º 6
0
def test_constraint_serialization(constraints):
    constraint_serializer = ConstraintsSerializer(domain_state_serializer)
    for constraint in constraints:
        serialized = constraint_serializer.serialize(constraint)
        deserialized = constraint_serializer.deserialize(serialized)
        assert constraint == deserialized
        assert constraint_serializer.serialize(constraint) == \
            constraint_serializer.serialize(deserialized)
Exemplo n.º 7
0
def test_catching_up_auth_rule_txn(looper, txnPoolNodeSet, sdk_wallet_trustee,
                                   sdk_wallet_steward, sdk_pool_handle):
    delayed_node = txnPoolNodeSet[-1]
    wh, _ = sdk_wallet_trustee
    new_steward_did, new_steward_verkey = create_verkey_did(looper, wh)
    changed_constraint = AuthConstraint(role=STEWARD, sig_count=1)
    action = AuthActionAdd(txn_type=NYM, field=ROLE, value=STEWARD)
    with pytest.raises(RequestRejectedException,
                       match="Not enough TRUSTEE signatures"):
        sdk_add_new_nym(looper,
                        sdk_pool_handle,
                        sdk_wallet_steward,
                        'newSteward2',
                        STEWARD_STRING,
                        dest=new_steward_did,
                        verkey=new_steward_verkey)
    with delay_rules_without_processing(delayed_node.nodeIbStasher, cDelay(),
                                        pDelay(), ppDelay()):
        sdk_send_and_check_auth_rule_request(
            looper,
            sdk_pool_handle,
            sdk_wallet_trustee,
            auth_action=ADD_PREFIX,
            auth_type=action.txn_type,
            field=action.field,
            new_value=action.value,
            old_value=None,
            constraint=changed_constraint.as_dict)
        sdk_add_new_nym(looper, sdk_pool_handle, sdk_wallet_trustee,
                        'newSteward2')
        delayed_node.start_catchup()
        looper.run(
            eventually(
                lambda: assertExp(delayed_node.mode == Mode.participating)))
    sdk_add_new_nym(looper,
                    sdk_pool_handle,
                    sdk_wallet_steward,
                    'newSteward3',
                    STEWARD_STRING,
                    dest=new_steward_did,
                    verkey=new_steward_verkey)
    ensure_all_nodes_have_same_data(looper, txnPoolNodeSet)
    config_state = delayed_node.states[CONFIG_LEDGER_ID]
    from_state = config_state.get(config.make_state_path_for_auth_rule(
        action.get_action_id()),
                                  isCommitted=True)
    assert changed_constraint == ConstraintsSerializer(
        config_state_serializer).deserialize(from_state)
def request_handler(bls_store):
    state = PruningState(KeyValueStorageInMemory())
    config_state = PruningState(KeyValueStorageInMemory())
    state_serializer = ConstraintsSerializer(domain_state_serializer)
    cache = IdrCache('Cache', KeyValueStorageInMemory())
    attr_store = AttributeStore(KeyValueStorageInMemory())
    write_req_validator = WriteRequestValidator(
        config=FakeSomething(authPolicy=CONFIG_LEDGER_AUTH_POLICY),
        auth_map=auth_map,
        cache=cache,
        config_state=config_state,
        state_serializer=state_serializer)
    return DomainReqHandler(ledger=None,
                            state=state,
                            config=None,
                            requestProcessor=None,
                            idrCache=cache,
                            attributeStore=attr_store,
                            bls_store=bls_store,
                            write_req_validator=write_req_validator,
                            ts_store=None)
Exemplo n.º 9
0
 def __init__(self, database_manager: DatabaseManager,
              write_req_validator: WriteRequestValidator, txn_type):
     super().__init__(database_manager, txn_type, CONFIG_LEDGER_ID)
     self.write_req_validator = write_req_validator
     self.constraint_serializer = ConstraintsSerializer(domain_state_serializer)
Exemplo n.º 10
0
class ConfigReqHandler(PConfigReqHandler):
    write_types = \
        {POOL_UPGRADE, NODE_UPGRADE, POOL_CONFIG, AUTH_RULE, AUTH_RULES} | \
        PConfigReqHandler.write_types
    query_types = \
        {GET_AUTH_RULE, } | \
        PConfigReqHandler.query_types

    def __init__(self,
                 ledger,
                 state,
                 domain_state,
                 idrCache: IdrCache,
                 upgrader: Upgrader,
                 poolManager,
                 poolCfg: PoolConfig,
                 write_req_validator,
                 bls_store=None,
                 ts_store: Optional[StateTsDbStorage] = None):
        super().__init__(ledger, state, domain_state, bls_store, ts_store)
        self.idrCache = idrCache
        self.upgrader = upgrader
        self.poolManager = poolManager
        self.poolCfg = poolCfg
        self.write_req_validator = write_req_validator
        self.constraint_serializer = ConstraintsSerializer(
            config_state_serializer)
        self._add_query_handler(GET_AUTH_RULE, self.handle_get_auth_rule)

    def doStaticValidation(self, request: Request):
        super().doStaticValidation(request)

        identifier, req_id, operation = request.identifier, request.reqId, request.operation
        if operation[TXN_TYPE] == POOL_UPGRADE:
            self._doStaticValidationPoolUpgrade(identifier, req_id, operation)
        elif operation[TXN_TYPE] == POOL_CONFIG:
            self._doStaticValidationPoolConfig(identifier, req_id, operation)
        elif operation[TXN_TYPE] == AUTH_RULE:
            self._doStaticValidationAuthRule(identifier, req_id, operation)
        elif operation[TXN_TYPE] == AUTH_RULES:
            self._doStaticValidationAuthRules(identifier, req_id, operation)
        elif operation[TXN_TYPE] == GET_AUTH_RULE:
            self._doStaticValidationGetAuthRule(identifier, req_id, operation)

    def _doStaticValidationPoolConfig(self, identifier, reqId, operation):
        pass

    def _doStaticValidationAuthRules(self, identifier, reqId, operation):
        for rule in operation[RULES]:
            self._doStaticValidationAuthRule(identifier, reqId, rule)

    def _doStaticValidationAuthRule(self, identifier, reqId, operation):
        try:
            ConstraintCreator.create_constraint(operation.get(CONSTRAINT))
        except ValueError as exp:
            raise InvalidClientRequest(identifier, reqId, exp)

        action = operation.get(AUTH_ACTION, None)

        if OLD_VALUE not in operation and action == EDIT_PREFIX:
            raise InvalidClientRequest(
                identifier, reqId, "Transaction for change authentication "
                "rule for {}={} must contain field {}".format(
                    AUTH_ACTION, EDIT_PREFIX, OLD_VALUE))

        if OLD_VALUE in operation and action == ADD_PREFIX:
            raise InvalidClientRequest(
                identifier, reqId, "Transaction for change authentication "
                "rule for {}={} must not contain field {}".format(
                    AUTH_ACTION, ADD_PREFIX, OLD_VALUE))
        self._check_auth_key(operation, identifier, reqId)

    def _doStaticValidationGetAuthRule(self, identifier, req_id, operation):
        required_fields = list(dict(ClientGetAuthRuleOperation.schema).keys())
        required_fields.remove(OLD_VALUE)
        if len(operation) > 1:
            if not set(required_fields).issubset(set(operation.keys())):
                raise InvalidClientRequest(
                    identifier, req_id,
                    "Not enough fields to build an auth key.")
            self._check_auth_key(operation, identifier, req_id)

    def _doStaticValidationPoolUpgrade(self, identifier, reqId, operation):
        action = operation.get(ACTION)
        if action not in (START, CANCEL):
            raise InvalidClientRequest(identifier, reqId,
                                       "{} not a valid action".format(action))
        if action == START:
            schedule = operation.get(SCHEDULE, {})
            force = operation.get(FORCE)
            force = str(force) == 'True'
            isValid, msg = self.upgrader.isScheduleValid(
                schedule, self.poolManager.getNodesServices(), force)
            if not isValid:
                raise InvalidClientRequest(
                    identifier, reqId,
                    "{} not a valid schedule since {}".format(schedule, msg))

        # TODO: Check if cancel is submitted before start

    def validate(self, req: Request):
        super().validate(req)

        status = '*'
        operation = req.operation
        typ = operation.get(TXN_TYPE)
        if typ not in self.write_types:
            return
        if typ == POOL_UPGRADE:
            pkg_to_upgrade = req.operation.get(PACKAGE,
                                               getConfig().UPGRADE_ENTRY)
            targetVersion = req.operation[VERSION]
            reinstall = req.operation.get(REINSTALL, False)
            # check package name
            if not pkg_to_upgrade:
                raise InvalidClientRequest(req.identifier, req.reqId,
                                           "Upgrade package name is empty")

            try:
                res = self.upgrader.check_upgrade_possible(
                    pkg_to_upgrade, targetVersion, reinstall)
            except Exception as exc:
                res = str(exc)

            if res:
                raise InvalidClientRequest(req.identifier, req.reqId, res)

            action = operation.get(ACTION)
            # TODO: Some validation needed for making sure name and version
            # present
            txn = self.upgrader.get_upgrade_txn(
                lambda txn: get_payload_data(txn).get(NAME, None) == req.
                operation.get(NAME, None) and get_payload_data(txn).get(
                    VERSION) == req.operation.get(VERSION),
                reverse=True)
            if txn:
                status = get_payload_data(txn).get(ACTION, '*')

            if status == START and action == START:
                raise InvalidClientRequest(
                    req.identifier, req.reqId,
                    "Upgrade '{}' is already scheduled".format(
                        req.operation.get(NAME)))
            if status == '*':
                auth_action = AuthActionAdd(txn_type=POOL_UPGRADE,
                                            field=ACTION,
                                            value=action)
            else:
                auth_action = AuthActionEdit(txn_type=POOL_UPGRADE,
                                             field=ACTION,
                                             old_value=status,
                                             new_value=action)
            self.write_req_validator.validate(req, [auth_action])
        elif typ == POOL_CONFIG:
            action = '*'
            status = '*'
            self.write_req_validator.validate(req, [
                AuthActionEdit(txn_type=typ,
                               field=ACTION,
                               old_value=status,
                               new_value=action)
            ])
        elif typ == AUTH_RULE:
            self.write_req_validator.validate(req, [
                AuthActionEdit(
                    txn_type=typ, field="*", old_value="*", new_value="*")
            ])
        elif typ == AUTH_RULES:
            self.write_req_validator.validate(req, [
                AuthActionEdit(
                    txn_type=typ, field="*", old_value="*", new_value="*")
            ])
        elif typ == TXN_AUTHOR_AGREEMENT:
            self.write_req_validator.validate(
                req, [AuthActionAdd(txn_type=typ, field="*", value="*")])
        elif typ == TXN_AUTHOR_AGREEMENT_AML:
            self.write_req_validator.validate(
                req, [AuthActionAdd(txn_type=typ, field='*', value='*')])

    def authorize(self, req: Request):
        # We don't need authorization from plenum since we have auth map in node
        pass

    def apply(self, req: Request, cons_time):
        txn = append_txn_metadata(reqToTxn(req), txn_time=cons_time)
        self.ledger.append_txns_metadata([txn])
        (start, _), _ = self.ledger.appendTxns([txn])
        self.updateState([txn], isCommitted=False)
        return start, txn

    def commit(self, txnCount, stateRoot, txnRoot, ppTime) -> List:
        committedTxns = super().commit(txnCount, stateRoot, txnRoot, ppTime)
        for txn in committedTxns:
            # Handle POOL_UPGRADE or POOL_CONFIG transaction here
            # only in case it is not forced.
            # If it is forced then it was handled earlier
            # in applyForced method.
            if not is_forced(txn):
                self.upgrader.handleUpgradeTxn(txn)
                self.poolCfg.handleConfigTxn(txn)
        return committedTxns

    def applyForced(self, req: Request):
        super().applyForced(req)
        txn = reqToTxn(req)
        self.upgrader.handleUpgradeTxn(txn)
        self.poolCfg.handleConfigTxn(txn)

    @staticmethod
    def get_auth_key(operation):
        action = operation.get(AUTH_ACTION, None)
        old_value = operation.get(OLD_VALUE, None)
        new_value = operation.get(NEW_VALUE, None)
        auth_type = operation.get(AUTH_TYPE, None)
        field = operation.get(FIELD, None)

        return AuthActionEdit(txn_type=auth_type,
                              field=field,
                              old_value=old_value,
                              new_value=new_value).get_action_id() \
            if action == EDIT_PREFIX else \
            AuthActionAdd(txn_type=auth_type,
                          field=field,
                          value=new_value).get_action_id()

    @staticmethod
    def get_auth_constraint(operation):
        return ConstraintCreator.create_constraint(operation.get(CONSTRAINT))

    def update_auth_constraint(self, auth_key: str, constraint):
        self.state.set(config.make_state_path_for_auth_rule(auth_key),
                       self.constraint_serializer.serialize(constraint))

    def updateStateWithSingleTxn(self, txn, isCommitted=False):
        super().updateStateWithSingleTxn(txn, isCommitted)

        typ = get_type(txn)
        if typ == AUTH_RULE:
            self._update_auth_rule_state(get_payload_data(txn))
        elif typ == AUTH_RULES:
            payload = get_payload_data(txn)
            for rule in payload.get(RULES):
                self._update_auth_rule_state(rule)

    def _update_auth_rule_state(self, payload):
        constraint = self.get_auth_constraint(payload)
        auth_key = self.get_auth_key(payload)
        self.update_auth_constraint(auth_key, constraint)

    def handle_get_auth_rule(self, request: Request):
        proof = None
        operation = request.operation
        if len(operation) >= len(ClientGetAuthRuleOperation.schema) - 1:
            key = self.get_auth_key(operation)
            data, proof = self._get_auth_rule(key)
        else:
            data = self._get_all_auth_rules()
        result = self.make_result(request=request, data=data, proof=proof)
        result.update(request.operation)
        return result

    def _get_auth_rule(self, key):
        multi_sig = None
        if self._bls_store:
            root_hash = self.state.committedHeadHash
            encoded_root_hash = state_roots_serializer.serialize(
                bytes(root_hash))
            multi_sig = self._bls_store.get(encoded_root_hash)
        path = config.make_state_path_for_auth_rule(key)
        map_data, proof = self.get_value_from_state(path,
                                                    with_proof=True,
                                                    multi_sig=multi_sig)

        if map_data:
            data = self.constraint_serializer.deserialize(map_data)
        else:
            data = self.write_req_validator.auth_map[key]
        action_obj = split_action_id(key)
        return [self.make_get_auth_rule_result(data, action_obj)], proof

    def _get_all_auth_rules(self):
        data = self.write_req_validator.auth_map.copy()
        result = []
        for key in self.write_req_validator.auth_map:
            path = config.make_state_path_for_auth_rule(key)
            state_constraint, _ = self.get_value_from_state(path)
            if state_constraint:
                value = self.constraint_serializer.deserialize(
                    state_constraint)
            else:
                value = data[key]
            action_obj = split_action_id(key)
            result.append(self.make_get_auth_rule_result(value, action_obj))
        return result

    def _check_auth_key(self, operation, identifier, req_id):
        auth_key = self.get_auth_key(operation)
        if auth_key not in self.write_req_validator.auth_map:
            raise InvalidClientRequest(
                identifier, req_id,
                "Unknown authorization rule: key '{}' is not "
                "found in authorization map.".format(auth_key))

    @staticmethod
    def make_get_auth_rule_result(constraint, action_obj):
        result = {
            CONSTRAINT: constraint.as_dict,
            AUTH_TYPE: action_obj.txn_type,
            AUTH_ACTION: action_obj.prefix,
            FIELD: action_obj.field,
            NEW_VALUE: action_obj.new_value,
        }
        if action_obj.prefix == EDIT_PREFIX:
            result[OLD_VALUE] = action_obj.old_value
        return result
Exemplo n.º 11
0
def constraint_serializer():
    return ConstraintsSerializer(domain_state_serializer)
Exemplo n.º 12
0
class GetAuthRuleHandler(ReadRequestHandler):
    def __init__(self, database_manager: DatabaseManager,
                 write_req_validator: WriteRequestValidator):
        self.write_req_validator = write_req_validator
        self.constraint_serializer = ConstraintsSerializer(
            domain_state_serializer)
        super().__init__(database_manager, GET_AUTH_RULE, CONFIG_LEDGER_ID)

    def static_validation(self, request: Request):
        self._validate_request_type(request)
        identifier, req_id, operation = get_request_data(request)

        required_fields = list(dict(ClientGetAuthRuleOperation.schema).keys())
        required_fields.remove(OLD_VALUE)
        if len(operation) > 1:
            if not set(required_fields).issubset(set(operation.keys())):
                raise InvalidClientRequest(
                    identifier, req_id,
                    "Not enough fields to build an auth key.")
            StaticAuthRuleHelper.check_auth_key(
                operation, identifier, req_id,
                self.write_req_validator.auth_map)

    def get_result(self, request: Request):
        self._validate_request_type(request)
        operation = request.operation
        proof = None
        if len(operation) >= len(ClientGetAuthRuleOperation.schema) - 1:
            path = StaticAuthRuleHelper.get_auth_key(operation)
            data, proof = self._get_auth_rule(path)
        else:
            data = self._get_all_auth_rules()
        result = self.make_result(request=request, data=data, proof=proof)
        result.update(operation)
        return result

    def _get_auth_rule(self, key):
        path = config.make_state_path_for_auth_rule(key)
        map_data, proof = self._get_value_from_state(path, with_proof=True)

        if map_data:
            data = self.constraint_serializer.deserialize(map_data)
        else:
            data = self.write_req_validator.auth_map[key]
        action_obj = split_action_id(key)
        return [self.make_auth_rule_data(data, action_obj)], proof

    def _get_all_auth_rules(self):
        data = self.write_req_validator.auth_map.copy()
        result = []
        for key in self.write_req_validator.auth_map:
            path = AbstractAuthRuleHandler.make_state_path_for_auth_rule(key)
            state_constraint, _ = self._get_value_from_state(path)
            if state_constraint:
                value = self.constraint_serializer.deserialize(
                    state_constraint)
            else:
                value = data[key]
            action_obj = split_action_id(key)
            result.append(self.make_auth_rule_data(value, action_obj))
        return result

    @staticmethod
    def make_auth_rule_data(constraint, action_obj):
        result = {
            CONSTRAINT: constraint.as_dict if constraint is not None else {},
            AUTH_TYPE: action_obj.txn_type,
            AUTH_ACTION: action_obj.prefix,
            FIELD: action_obj.field,
            NEW_VALUE: action_obj.new_value,
        }
        if action_obj.prefix == EDIT_PREFIX:
            result[OLD_VALUE] = action_obj.old_value
        return result
Exemplo n.º 13
0
class ConfigReqHandler(LedgerRequestHandler):
    write_types = {POOL_UPGRADE, NODE_UPGRADE, POOL_CONFIG, AUTH_RULE}

    def __init__(self, ledger, state, idrCache: IdrCache, upgrader: Upgrader,
                 poolManager, poolCfg: PoolConfig, write_req_validator):
        super().__init__(ledger, state)
        self.idrCache = idrCache
        self.upgrader = upgrader
        self.poolManager = poolManager
        self.poolCfg = poolCfg
        self.write_req_validator = write_req_validator
        self.constraint_serializer = ConstraintsSerializer(
            domain_state_serializer)

    def doStaticValidation(self, request: Request):
        identifier, req_id, operation = request.identifier, request.reqId, request.operation
        if operation[TXN_TYPE] == POOL_UPGRADE:
            self._doStaticValidationPoolUpgrade(identifier, req_id, operation)
        elif operation[TXN_TYPE] == POOL_CONFIG:
            self._doStaticValidationPoolConfig(identifier, req_id, operation)
        elif operation[TXN_TYPE] == AUTH_RULE:
            self._doStaticValidationAuthRule(identifier, req_id, operation)

    def _doStaticValidationPoolConfig(self, identifier, reqId, operation):
        pass

    def _doStaticValidationAuthRule(self, identifier, reqId, operation):
        constraint = operation.get(CONSTRAINT)
        ConstraintCreator.create_constraint(constraint)
        action = operation.get(AUTH_ACTION, None)

        if OLD_VALUE not in operation and action == EDIT_PREFIX:
            raise InvalidClientRequest(
                identifier, reqId, "Transaction for change authentication "
                "rule for {}={} must contain field {}".format(
                    AUTH_ACTION, EDIT_PREFIX, OLD_VALUE))

        if OLD_VALUE in operation and action == ADD_PREFIX:
            raise InvalidClientRequest(
                identifier, reqId, "Transaction for change authentication "
                "rule for {}={} must not contain field {}".format(
                    AUTH_ACTION, ADD_PREFIX, OLD_VALUE))

        auth_key = self.get_auth_key(operation)

        if auth_key not in self.write_req_validator.auth_map and \
                auth_key not in self.write_req_validator.anyone_can_write_map:
            raise InvalidClientRequest(
                identifier, reqId,
                "Unknown authorization rule: key '{}' is not "
                "found in authorization map".format(auth_key))

    def _doStaticValidationPoolUpgrade(self, identifier, reqId, operation):
        action = operation.get(ACTION)
        if action not in (START, CANCEL):
            raise InvalidClientRequest(identifier, reqId,
                                       "{} not a valid action".format(action))
        if action == START:
            schedule = operation.get(SCHEDULE, {})
            force = operation.get(FORCE)
            force = str(force) == 'True'
            isValid, msg = self.upgrader.isScheduleValid(
                schedule, self.poolManager.getNodesServices(), force)
            if not isValid:
                raise InvalidClientRequest(
                    identifier, reqId,
                    "{} not a valid schedule since {}".format(schedule, msg))

        # TODO: Check if cancel is submitted before start

    def curr_pkt_info(self, pkg_name):
        if pkg_name == APP_NAME:
            return Upgrader.getVersion(), [APP_NAME]
        return NodeControlUtil.curr_pkt_info(pkg_name)

    def validate(self, req: Request):
        status = '*'
        operation = req.operation
        typ = operation.get(TXN_TYPE)
        if typ not in [POOL_UPGRADE, POOL_CONFIG, AUTH_RULE]:
            return
        if typ == POOL_UPGRADE:
            pkt_to_upgrade = req.operation.get(PACKAGE,
                                               getConfig().UPGRADE_ENTRY)
            if pkt_to_upgrade:
                currentVersion, cur_deps = self.curr_pkt_info(pkt_to_upgrade)
                if not currentVersion:
                    raise InvalidClientRequest(
                        req.identifier, req.reqId,
                        "Packet {} is not installed and cannot be upgraded".
                        format(pkt_to_upgrade))
                if all([APP_NAME not in d for d in cur_deps]):
                    raise InvalidClientRequest(
                        req.identifier, req.reqId,
                        "Packet {} doesn't belong to pool".format(
                            pkt_to_upgrade))
            else:
                raise InvalidClientRequest(req.identifier, req.reqId,
                                           "Upgrade packet name is empty")

            targetVersion = req.operation[VERSION]
            reinstall = req.operation.get(REINSTALL, False)
            if not Upgrader.is_version_upgradable(currentVersion,
                                                  targetVersion, reinstall):
                # currentVersion > targetVersion
                raise InvalidClientRequest(req.identifier, req.reqId,
                                           "Version is not upgradable")

            action = operation.get(ACTION)
            # TODO: Some validation needed for making sure name and version
            # present
            txn = self.upgrader.get_upgrade_txn(
                lambda txn: get_payload_data(txn).get(NAME, None) == req.
                operation.get(NAME, None) and get_payload_data(txn).get(
                    VERSION) == req.operation.get(VERSION),
                reverse=True)
            if txn:
                status = get_payload_data(txn).get(ACTION, '*')

            if status == START and action == START:
                raise InvalidClientRequest(
                    req.identifier, req.reqId,
                    "Upgrade '{}' is already scheduled".format(
                        req.operation.get(NAME)))
            if status == '*':
                auth_action = AuthActionAdd(txn_type=POOL_UPGRADE,
                                            field=ACTION,
                                            value=action)
            else:
                auth_action = AuthActionEdit(txn_type=POOL_UPGRADE,
                                             field=ACTION,
                                             old_value=status,
                                             new_value=action)
            self.write_req_validator.validate(req, [auth_action])
        elif typ == POOL_CONFIG:
            action = '*'
            status = '*'
            self.write_req_validator.validate(req, [
                AuthActionEdit(txn_type=typ,
                               field=ACTION,
                               old_value=status,
                               new_value=action)
            ])
        elif typ == AUTH_RULE:
            self.write_req_validator.validate(req, [
                AuthActionEdit(
                    txn_type=typ, field="*", old_value="*", new_value="*")
            ])

    def apply(self, req: Request, cons_time):
        txn = append_txn_metadata(reqToTxn(req), txn_time=cons_time)
        self.ledger.append_txns_metadata([txn])
        (start, _), _ = self.ledger.appendTxns([txn])
        self.updateState([txn], isCommitted=False)
        return start, txn

    def commit(self, txnCount, stateRoot, txnRoot, ppTime) -> List:
        committedTxns = super().commit(txnCount, stateRoot, txnRoot, ppTime)
        for txn in committedTxns:
            # Handle POOL_UPGRADE or POOL_CONFIG transaction here
            # only in case it is not forced.
            # If it is forced then it was handled earlier
            # in applyForced method.
            if not is_forced(txn):
                self.upgrader.handleUpgradeTxn(txn)
                self.poolCfg.handleConfigTxn(txn)
        return committedTxns

    def applyForced(self, req: Request):
        super().applyForced(req)
        txn = reqToTxn(req)
        self.upgrader.handleUpgradeTxn(txn)
        self.poolCfg.handleConfigTxn(txn)

    @staticmethod
    def get_auth_key(operation):
        action = operation.get(AUTH_ACTION, None)
        old_value = operation.get(OLD_VALUE, None)
        new_value = operation.get(NEW_VALUE, None)
        auth_type = operation.get(AUTH_TYPE, None)
        field = operation.get(FIELD, None)

        return AuthActionEdit(txn_type=auth_type,
                              field=field,
                              old_value=old_value,
                              new_value=new_value).get_action_id() \
            if action == EDIT_PREFIX else \
            AuthActionAdd(txn_type=auth_type,
                          field=field,
                          value=new_value).get_action_id()

    @staticmethod
    def get_auth_constraint(operation):
        return ConstraintCreator.create_constraint(operation.get(CONSTRAINT))

    def update_auth_constraint(self, auth_key, constraint):
        self.state.set(auth_key.encode(),
                       self.constraint_serializer.serialize(constraint))

    def updateState(self, txns, isCommitted=False):
        for txn in txns:
            typ = get_type(txn)
            if typ == AUTH_RULE:
                payload = get_payload_data(txn)
                constraint = self.get_auth_constraint(payload)
                auth_key = self.get_auth_key(payload)
                self.update_auth_constraint(auth_key, constraint)