def get_result(self, request: Request): version = request.operation.get(GET_TXN_AUTHOR_AGREEMENT_AML_VERSION) timestamp = request.operation.get( GET_TXN_AUTHOR_AGREEMENT_AML_TIMESTAMP) if version is not None: path = StaticTAAHelper.state_path_taa_aml_version(version) data, proof = self._get_value_from_state(path, with_proof=True) return self._return_txn_author_agreement_aml(request, proof, data=data) if timestamp is not None: head_hash = self.database_manager.ts_store.get_equal_or_prev( timestamp, CONFIG_LEDGER_ID) if head_hash is None: return self._return_txn_author_agreement_aml(request, None) head_hash = head_hash if head_hash else self.state.committedHeadHash data, proof = self._get_value_from_state( StaticTAAHelper.state_path_taa_aml_latest(), head_hash, with_proof=True) return self._return_txn_author_agreement_aml(request, proof, data=data) path = StaticTAAHelper.state_path_taa_aml_latest() data, proof = self._get_value_from_state(path, with_proof=True) return self._return_txn_author_agreement_aml(request, proof, data=data)
def _validate_update_taa(self, request, digest): ledger_taa = self.get_from_state(StaticTAAHelper.state_path_taa_digest(digest))[0] # check TAA text taa_text = ledger_taa.get(TXN_AUTHOR_AGREEMENT_TEXT) if request.operation.get(TXN_AUTHOR_AGREEMENT_TEXT, taa_text) != taa_text: raise InvalidClientRequest(request.identifier, request.reqId, "Changing a text of existing transaction author agreement is forbidden") # check TAA ratification timestamp taa_ratified = ledger_taa.get(TXN_AUTHOR_AGREEMENT_RATIFICATION_TS) if request.operation.get(TXN_AUTHOR_AGREEMENT_RATIFICATION_TS, taa_ratified) != taa_ratified: raise InvalidClientRequest(request.identifier, request.reqId, "Changing ratification date of existing " "transaction author agreement is forbidden") # TODO: Following code assumes that the only reason for updating TAA is changing its retirement date. # If this is no longer the case this needs to be changed. Also this cries for adding separate transaction # for TAA retirement # check if TAA enforcement is disabled last_taa_digest = StaticTAAHelper.get_latest_taa(self.state) if last_taa_digest is None: raise InvalidClientRequest(request.identifier, request.reqId, "Retirement date cannot be changed when TAA enforcement is disabled.") # check if we are trying to modify latest TAA if last_taa_digest == digest: raise InvalidClientRequest(request.identifier, request.reqId, "The latest transaction author agreement cannot be retired.")
def get_result(self, request: Request): version = request.operation.get(GET_TXN_AUTHOR_AGREEMENT_VERSION) digest = request.operation.get(GET_TXN_AUTHOR_AGREEMENT_DIGEST) timestamp = request.operation.get(GET_TXN_AUTHOR_AGREEMENT_TIMESTAMP) if version is not None: path = StaticTAAHelper.state_path_taa_version(version) digest, proof = self._get_value_from_state(path, with_proof=True) return self._return_txn_author_agreement(request, proof, digest=digest) if digest is not None: path = StaticTAAHelper.state_path_taa_digest(digest) data, proof = self._get_value_from_state(path, with_proof=True) return self._return_txn_author_agreement(request, proof, data=data) if timestamp is not None: head_hash = self.database_manager.ts_store.get_equal_or_prev( timestamp, CONFIG_LEDGER_ID) if head_hash is None: return self._return_txn_author_agreement(request, None) path = StaticTAAHelper.state_path_taa_latest() digest, proof = self._get_value_from_state(path, head_hash, with_proof=True) return self._return_txn_author_agreement(request, proof, head_hash=head_hash, digest=digest) path = StaticTAAHelper.state_path_taa_latest() digest, proof = self._get_value_from_state(path, with_proof=True) return self._return_txn_author_agreement(request, proof, digest=digest)
def update_state(self, txn, prev_result, request, is_committed=False): self._validate_txn_type(txn) seq_no = get_seq_no(txn) txn_time = get_txn_time(txn) _, taa_list = self.state.generate_state_proof_for_keys_with_prefix( StaticTAAHelper.state_path_taa_digest(""), serialize=False, get_value=True) for encode_key, encode_data in taa_list.items(): taa = rlp_decode(encode_data) taa, last_seq_no, last_update_time = self._decode_state_value( taa[0]) digest = StaticTAAHelper.get_digest_from_state_key(encode_key) if TXN_AUTHOR_AGREEMENT_RETIREMENT_TS not in taa or taa.get( TXN_AUTHOR_AGREEMENT_RETIREMENT_TS, 0) > txn_time: self._set_taa_to_state( digest, seq_no, txn_time, taa[TXN_AUTHOR_AGREEMENT_TEXT], taa[TXN_AUTHOR_AGREEMENT_VERSION], taa.get(TXN_AUTHOR_AGREEMENT_RATIFICATION_TS, last_update_time), retirement_ts=txn_time) self.state.remove(StaticTAAHelper.state_path_taa_latest())
def _add_taa_to_state(self, digest, seq_no, txn_time, text, version, ratification_ts): self._set_taa_to_state(digest, seq_no, txn_time, text, version, ratification_ts) self.state.set(StaticTAAHelper.state_path_taa_version(version), digest) self.state.set(StaticTAAHelper.state_path_taa_latest(), digest)
def test_update_state(txn_author_agreement_handler, taa_request): seq_no = 1 txn_time = 1560241033 txn_id = "id" txn = reqToTxn(taa_request) payload = get_payload_data(txn) text = payload[TXN_AUTHOR_AGREEMENT_TEXT] version = payload[TXN_AUTHOR_AGREEMENT_VERSION] digest = StaticTAAHelper.taa_digest(text, version) append_txn_metadata(txn, seq_no, txn_time, txn_id) state_value = { TXN_AUTHOR_AGREEMENT_TEXT: text, TXN_AUTHOR_AGREEMENT_VERSION: version } txn_author_agreement_handler.update_state(txn, None, taa_request) assert txn_author_agreement_handler.get_from_state( StaticTAAHelper.state_path_taa_digest(digest)) == (state_value, seq_no, txn_time) assert txn_author_agreement_handler.state.get( StaticTAAHelper.state_path_taa_latest()) == digest assert txn_author_agreement_handler.state.get( StaticTAAHelper.state_path_taa_version(version)) == digest
def update_state(self, txn, prev_result, request, is_committed=False): self._validate_txn_type(txn) payload = get_payload_data(txn) seq_no = get_seq_no(txn) txn_time = get_txn_time(txn) serialized_data = encode_state_value(payload, seq_no, txn_time, serializer=config_state_serializer) version = payload[AML_VERSION] self.state.set(StaticTAAHelper.state_path_taa_aml_latest(), serialized_data) self.state.set(StaticTAAHelper.state_path_taa_aml_version(version), serialized_data)
def _update_txn_author_agreement_acceptance_mechanisms( self, payload, seq_no, txn_time): serialized_data = encode_state_value( payload, seq_no, txn_time, serializer=config_state_serializer) version = payload[AML_VERSION] self.state.set(StaticTAAHelper.state_path_taa_aml_latest(), serialized_data) self.state.set(StaticTAAHelper.state_path_taa_aml_version(version), serialized_data)
def dynamic_validation(self, request: Request): self._validate_request_type(request) self.authorize(request) operation, identifier, req_id = request.operation, request.identifier, request.reqId if self.state.get(StaticTAAHelper.state_path_taa_aml_latest()) is None: raise InvalidClientRequest(identifier, req_id, "TAA txn is forbidden until TAA AML is set. Send TAA AML first.") version = operation[TXN_AUTHOR_AGREEMENT_VERSION] if StaticTAAHelper.get_taa_digest(self.state, version, isCommitted=False) is not None: raise InvalidClientRequest(identifier, req_id, "Changing existing version of transaction author agreement is forbidden")
def test_update_state(txn_author_agreement_disable_handler, taa_disable_request, txn_author_agreement_handler, tconf, domain_state, taa_pp_time): # create TAAs taa_txns = [] taa_digests = [] taa_state_datas = [] for _ in list(range(5)): txn, digest, state_data = create_taa_txn( taa_request(tconf, domain_state, taa_pp_time), taa_pp_time) taa_txns.append(txn) taa_digests.append(digest) taa_state_datas.append(state_data) assert taa_txns # create a disable txn disable_seq_no = 1 disable_txn_time = get_utc_epoch() taa_disable_txn = reqToTxn(taa_disable_request) append_txn_metadata(taa_disable_txn, disable_seq_no, disable_txn_time) # set a TAAs for index, taa_txn in enumerate(taa_txns): txn_author_agreement_handler.update_state(taa_txn, None, None) check_taa_in_state( handler=txn_author_agreement_handler, digest=taa_digests[index], version=taa_state_datas[index][0][TXN_AUTHOR_AGREEMENT_VERSION], state_data=taa_state_datas[index]) assert txn_author_agreement_disable_handler.state.get( StaticTAAHelper.state_path_taa_latest(), isCommitted=False) == taa_digests[index].encode() # disable TAAs txn_author_agreement_disable_handler.update_state(taa_disable_txn, None, None) assert txn_author_agreement_disable_handler.state.get( StaticTAAHelper.state_path_taa_latest(), isCommitted=False) is None # set a TAAs for index, state_data in enumerate(taa_state_datas): state_value = state_data[0] state_value[TXN_AUTHOR_AGREEMENT_RETIREMENT_TS] = disable_txn_time check_taa_in_state(handler=txn_author_agreement_handler, digest=taa_digests[index], version=state_value[TXN_AUTHOR_AGREEMENT_VERSION], state_data=(state_data[0], disable_seq_no, disable_txn_time))
def _update_txn_author_agreement(self, seq_no, txn_time, text, version): digest = StaticTAAHelper.taa_digest(text, version) data = encode_state_value( { TXN_AUTHOR_AGREEMENT_TEXT: text, TXN_AUTHOR_AGREEMENT_VERSION: version }, seq_no, txn_time, serializer=config_state_serializer) self.state.set(StaticTAAHelper.state_path_taa_digest(digest), data) self.state.set(StaticTAAHelper.state_path_taa_latest(), digest) self.state.set(StaticTAAHelper.state_path_taa_version(version), digest)
def update_state(self, txn, prev_result, request, is_committed=False): self._validate_txn_type(txn) payload = get_payload_data(txn) text = payload.get(TXN_AUTHOR_AGREEMENT_TEXT) version = payload[TXN_AUTHOR_AGREEMENT_VERSION] retired = payload.get(TXN_AUTHOR_AGREEMENT_RETIREMENT_TS) ratified = payload.get(TXN_AUTHOR_AGREEMENT_RATIFICATION_TS) seq_no = get_seq_no(txn) txn_time = get_txn_time(txn) digest = StaticTAAHelper.get_taa_digest(self.state, version, isCommitted=False) if digest is None: digest = StaticTAAHelper.taa_digest(text, version) self._add_taa_to_state(digest, seq_no, txn_time, text, version, ratified) else: self._update_taa_to_state(digest, seq_no, txn_time, retired)
def expected_data(data: TaaData): return { TXN_AUTHOR_AGREEMENT_TEXT: data.text, TXN_AUTHOR_AGREEMENT_VERSION: data.version, TXN_AUTHOR_AGREEMENT_DIGEST: StaticTAAHelper.taa_digest(data.text, data.version), TXN_AUTHOR_AGREEMENT_RATIFICATION_TS: data.txn_time }, data.seq_no, data.txn_time
def additional_dynamic_validation(self, request: Request, req_pp_time: Optional[int]): if not self.state.get(StaticTAAHelper.state_path_taa_latest(), isCommitted=False): raise InvalidClientRequest( request.identifier, request.reqId, "Transaction author agreement is already disabled.")
def update_state(self, txn, prev_result, request, is_committed=False): self._validate_txn_type(txn) payload = get_payload_data(txn) text = payload[TXN_AUTHOR_AGREEMENT_TEXT] version = payload[TXN_AUTHOR_AGREEMENT_VERSION] seq_no = get_seq_no(txn) txn_time = get_txn_time(txn) digest = StaticTAAHelper.taa_digest(text, version) data = encode_state_value({ TXN_AUTHOR_AGREEMENT_TEXT: text, TXN_AUTHOR_AGREEMENT_VERSION: version }, seq_no, txn_time, serializer=config_state_serializer) self.state.set(StaticTAAHelper.state_path_taa_digest(digest), data) self.state.set(StaticTAAHelper.state_path_taa_latest(), digest) self.state.set(StaticTAAHelper.state_path_taa_version(version), digest)
def set_aml(txn_author_agreement_handler): txn_author_agreement_handler.state.set( StaticTAAHelper.state_path_taa_aml_latest(), encode_state_value("value", "seqNo", "txnTime", serializer=config_state_serializer))
def dynamic_validation(self, request: Request, req_pp_time: Optional[int]): self._validate_request_type(request) self.authorize(request) operation, identifier, req_id = request.operation, request.identifier, request.reqId aml_latest, _, _ = self.get_from_state(StaticTAAHelper.state_path_taa_aml_latest()) if aml_latest is None: raise InvalidClientRequest(identifier, req_id, "TAA txn is forbidden until TAA AML is set. Send TAA AML first.") version = operation[TXN_AUTHOR_AGREEMENT_VERSION] digest = StaticTAAHelper.get_taa_digest(self.state, version, isCommitted=False) if digest is None: if req_pp_time is None: raise LogicError("Cannot validate TAA transaction outside of normal ordering") self._validate_add_taa(request, req_pp_time) else: self._validate_update_taa(request, digest)
def taa_input_data(): input_data = [] for n in range(10): text, version = gen_random_txn_author_agreement(32, 8) input_data.append( TaaData(text, version, n, n + 10, StaticTAAHelper.taa_digest(text, version))) return input_data
def dynamic_validation(self, request: Request): self._validate_request_type(request) self.authorize(request) operation, identifier, req_id = request.operation, request.identifier, request.reqId version = operation.get(AML_VERSION) if StaticTAAHelper.get_taa_aml_data(self.state, version, isCommitted=False) is not None: raise InvalidClientRequest(identifier, req_id, "Version of TAA AML must be unique and it cannot be modified")
def additional_dynamic_validation(self, request: Request, req_pp_time: Optional[int]): operation, identifier, req_id = request.operation, request.identifier, request.reqId version = operation.get(AML_VERSION) if StaticTAAHelper.get_taa_aml_data(self.state, version, isCommitted=False) is not None: raise InvalidClientRequest( identifier, req_id, "Version of TAA AML must be unique and it cannot be modified")
def _update_taa_to_state(self, digest, seq_no, txn_time, retirement_ts=None): ledger_data = self.get_from_state(StaticTAAHelper.state_path_taa_digest(digest)) if ledger_data is None: return ledger_taa, last_seq_no, last_update_time = ledger_data ratification_ts = ledger_taa.get(TXN_AUTHOR_AGREEMENT_RATIFICATION_TS, last_update_time) text = ledger_taa.get(TXN_AUTHOR_AGREEMENT_TEXT) version = ledger_taa.get(TXN_AUTHOR_AGREEMENT_VERSION) self._set_taa_to_state(digest, seq_no, txn_time, text, version, ratification_ts, retirement_ts)
def test_update_state(txn_author_agreement_aml_handler, domain_state, aml_request): seq_no = 1 txn_time = 1560241033 txn_id = "id" txn = reqToTxn(aml_request) payload = get_payload_data(txn) version = payload[AML_VERSION] append_txn_metadata(txn, seq_no, txn_time, txn_id) txn_author_agreement_aml_handler.update_state(txn, None, aml_request) assert txn_author_agreement_aml_handler.get_from_state( StaticTAAHelper.state_path_taa_aml_latest()) == (payload, seq_no, txn_time) assert txn_author_agreement_aml_handler.get_from_state( StaticTAAHelper.state_path_taa_aml_version(version)) == (payload, seq_no, txn_time)
def test_dynamic_validation_with_not_unique_aml( txn_author_agreement_aml_handler, aml_request): version = aml_request.operation[AML_VERSION] txn_author_agreement_aml_handler.state.set( StaticTAAHelper.state_path_taa_aml_version(version), "{}") with pytest.raises( InvalidClientRequest, match="Version of TAA AML must be unique and it cannot be modified" ): txn_author_agreement_aml_handler.dynamic_validation(aml_request)
def expected_state_data(data: TaaData) -> Dict: return { 'lsn': data.seq_no, 'lut': data.txn_time, 'val': { TXN_AUTHOR_AGREEMENT_TEXT: data.text, TXN_AUTHOR_AGREEMENT_VERSION: data.version, TXN_AUTHOR_AGREEMENT_DIGEST: StaticTAAHelper.taa_digest(data.text, data.version), TXN_AUTHOR_AGREEMENT_RATIFICATION_TS: data.txn_time } }
def test_dynamic_validation_with_not_unique_version( txn_author_agreement_handler, taa_request, set_aml): version = taa_request.operation[TXN_AUTHOR_AGREEMENT_VERSION] txn_author_agreement_handler.state.set( StaticTAAHelper.state_path_taa_version(version), "{}".encode()) with pytest.raises( InvalidClientRequest, match= "Changing existing version of transaction author agreement is forbidden" ): txn_author_agreement_handler.dynamic_validation(taa_request)
def test_fill_ts_store_for_config_after_catchup(txnPoolNodeSet, looper, sdk_pool_handle, sdk_wallet_trustee, tconf, tdir, allPluginsPath, set_txn_author_agreement_aml): sdk_send_txn_author_agreement(looper, sdk_pool_handle, sdk_wallet_trustee, *create_random_taa(), ratified=get_utc_epoch() - 600) node_to_disconnect = txnPoolNodeSet[-1] disconnect_node_and_ensure_disconnected(looper, txnPoolNodeSet, node_to_disconnect) looper.removeProdable(name=node_to_disconnect.name) sdk_reply = sdk_send_txn_author_agreement(looper, sdk_pool_handle, sdk_wallet_trustee, *create_random_taa(), ratified=get_utc_epoch() - 600) node_to_disconnect = start_stopped_node(node_to_disconnect, looper, tconf, tdir, allPluginsPath) txnPoolNodeSet[-1] = node_to_disconnect looper.run(checkNodesConnected(txnPoolNodeSet)) waitNodeDataEquality(looper, node_to_disconnect, *txnPoolNodeSet, exclude_from_check=['check_last_ordered_3pc_backup']) req_handler = node_to_disconnect.read_manager.request_handlers[ GET_TXN_AUTHOR_AGREEMENT] last_digest = StaticTAAHelper.get_taa_digest(req_handler.state) key = StaticTAAHelper.state_path_taa_digest(last_digest) root_hash = req_handler.database_manager.ts_store.get_equal_or_prev( get_txn_time(sdk_reply[1]['result'])) assert root_hash from_state = req_handler.state.get_for_root_hash(root_hash=root_hash, key=key) assert config_state_serializer.deserialize(from_state)['val']['text'] == \ get_payload_data(sdk_reply[1]['result'])['text']
def test_update_state(txn_author_agreement_handler, taa_request, taa_pp_time): txn, digest, state_data = create_taa_txn(taa_request, taa_pp_time) txn_author_agreement_handler.update_state(txn, None, taa_request) check_taa_in_state(handler=txn_author_agreement_handler, digest=digest, version=state_data[0][TXN_AUTHOR_AGREEMENT_VERSION], state_data=state_data) assert txn_author_agreement_handler.state.get( StaticTAAHelper.state_path_taa_latest(), isCommitted=False) == digest.encode()
def authorize(self, request): version = request.operation.get(TXN_AUTHOR_AGREEMENT_VERSION) if StaticTAAHelper.get_taa_digest(self.state, version, isCommitted=False) is None: self.write_req_validator.validate(request, [AuthActionAdd(txn_type=self.txn_type, field='*', value='*')]) else: self.write_req_validator.validate(request, [AuthActionEdit(txn_type=self.txn_type, field='*', old_value='*', new_value='*')])
def set_txn_author_agreement( looper, sdk_pool_handle, sdk_wallet, text: str, version: str, ratified: int, retired: Optional[int] ) -> TaaData: reply = sdk_send_txn_author_agreement(looper, sdk_pool_handle, sdk_wallet, version, text, ratified=ratified, retired=retired)[1] assert reply[OP_FIELD_NAME] == REPLY result = reply[f.RESULT.nm] return TaaData( text, version, seq_no=result[TXN_METADATA][TXN_METADATA_SEQ_NO], txn_time=result[TXN_METADATA][TXN_METADATA_TIME], # TODO: Add ratified? digest=StaticTAAHelper.taa_digest(text, version) )
def test_dynamic_validation_update_with_retired_taa_off( txn_author_agreement_handler, domain_state, taa_request, taa_pp_time, set_aml, retired_time): txn, digest, state_data = create_taa_txn(taa_request, taa_pp_time) txn_author_agreement_handler.update_state(txn, None, taa_request) txn_author_agreement_handler.state.remove( StaticTAAHelper.state_path_taa_latest()) if retired_time != "without": taa_request.operation[ TXN_AUTHOR_AGREEMENT_RETIREMENT_TS] = retired_time with pytest.raises( InvalidClientRequest, match= "Retirement date cannot be changed when TAA enforcement is disabled." ): txn_author_agreement_handler.dynamic_validation( taa_request, taa_pp_time)