def _command_transaction_status( command: offchain.PaymentCommand, default: TransactionStatus) -> TransactionStatus: if command.is_both_ready(): return TransactionStatus.OFF_CHAIN_READY elif command.is_abort(): return TransactionStatus.CANCELED return default
def _send_kyc_data_and_receipient_signature( command: offchain.PaymentCommand, ) -> offchain.PaymentCommand: sig_msg = command.travel_rule_metadata_signature_message(_hrp()) user_id = get_account_id_from_subaddr( command.receiver_subaddress(_hrp()).hex()) return command.new_command( recipient_signature=_compliance_private_key().sign(sig_msg).hex(), kyc_data=_user_kyc_data(user_id), status=offchain.Status.ready_for_settlement, )
def _kyc_data_result( self, action: str, ret: ActionResultType, command: offchain.PaymentCommand ) -> offchain.Command: if ret == ActionResult.PASS: if command.is_receiver(): return self._send_kyc_data_and_receipient_signature(command) return command.new_command(status=offchain.Status.ready_for_settlement) return command.new_command( status=offchain.Status.abort, abort_code=offchain.AbortCode.reject_kyc_data, abort_message=f"{action}: {ret}", )
def _send_kyc_data_and_receipient_signature( self, command: offchain.PaymentCommand, ) -> offchain.PaymentCommand: sig_msg = command.travel_rule_metadata_signature_message(self.hrp) subaddress = command.receiver_subaddress(self.hrp) user = self._find_user_by_subaddress(subaddress) return command.new_command( recipient_signature=self.compliance_key.sign(sig_msg).hex(), kyc_data=user.kyc_data(), status=offchain.Status.ready_for_settlement, )
def _create_payment_command(self, account_id: str, cmd: offchain.PaymentCommand, validate: bool = False) -> None: self.app.store.create( PaymentCommand, before_create=lambda _: cmd.validate(None) if validate else None, account_id=account_id, is_sender=cmd.is_sender(), reference_id=cmd.reference_id(), is_inbound=cmd.is_inbound(), cid=cmd.id(), payment_object=asdict(cmd.payment), )
def _ready_for_settlement( self, account_id: str, cmd: offchain.PaymentCommand) -> offchain.Command: if cmd.is_sender(): return cmd.new_command(status=Status.ready_for_settlement) sig_msg = cmd.travel_rule_metadata_signature_message( self.app.diem_account.hrp) sig = self.app.diem_account.sign_by_compliance_key(sig_msg).hex() kyc_data = self.app.store.find(Account, id=account_id).kyc_data_object() return cmd.new_command(recipient_signature=sig, kyc_data=kyc_data, status=Status.ready_for_settlement)
def _offchain_action_evaluate_kyc_data( self, account_id: str, cmd: offchain.PaymentCommand) -> offchain.Command: op_kyc_data = cmd.counterparty_actor_obj().kyc_data if op_kyc_data is None or self.app.kyc_sample.match_kyc_data( "reject", op_kyc_data): return self._new_reject_kyc_data(cmd, "KYC data is rejected") elif self.app.kyc_sample.match_any_kyc_data( ["soft_match", "soft_reject"], op_kyc_data): return cmd.new_command(status=Status.soft_match) elif self.app.kyc_sample.match_kyc_data("minimum", op_kyc_data): return self._ready_for_settlement(account_id, cmd) else: return self._new_reject_kyc_data(cmd, "KYC data is not from samples")
def _update_payment_command(self, cmd: PaymentCommand, offchain_cmd: offchain.PaymentCommand, validate: bool = False) -> None: prior = cmd.to_offchain_command() self.app.store.update( cmd, before_update=lambda _: offchain_cmd.validate(prior) if validate else None, cid=offchain_cmd.id(), is_inbound=offchain_cmd.is_inbound(), is_abort=offchain_cmd.is_abort(), is_ready=offchain_cmd.is_both_ready(), payment_object=asdict(offchain_cmd.payment), )
def _new_payment_command_transaction(command: offchain.PaymentCommand, status: TransactionStatus) -> Transaction: payment = command.payment sender_address, source_subaddress = _account_address_and_subaddress( payment.sender.address) destination_address, destination_subaddress = _account_address_and_subaddress( payment.receiver.address) source_id = get_account_id_from_subaddr(source_subaddress) destination_id = get_account_id_from_subaddr(destination_subaddress) return Transaction( type=TransactionType.OFFCHAIN, status=status, amount=payment.action.amount, currency=payment.action.currency, created_timestamp=datetime.utcnow(), source_id=source_id, source_address=sender_address, source_subaddress=source_subaddress, destination_id=destination_id, destination_address=destination_address, destination_subaddress=destination_subaddress, reference_id=command.reference_id(), command_json=offchain.to_json(command), )
def _offchain_action_review_kyc_data( self, account_id: str, cmd: offchain.PaymentCommand) -> offchain.Command: op_kyc_data = cmd.counterparty_actor_obj().kyc_data if op_kyc_data is None or self.app.kyc_sample.match_kyc_data( "soft_reject", op_kyc_data): return self._new_reject_kyc_data( cmd, "KYC data review result is reject") return self._ready_for_settlement(account_id, cmd)
def _offchain_action_clear_soft_match( self, account_id: str, cmd: offchain.PaymentCommand) -> offchain.Command: account = self.app.store.find(Account, id=account_id) if account.reject_additional_kyc_data_request: return self._new_reject_kyc_data(cmd, "no need additional KYC data") return cmd.new_command(additional_kyc_data="{%r: %r}" % ("account_id", account_id))
def _lock_and_save_inbound_command( command: offchain.PaymentCommand) -> Transaction: def validate_and_save(txn: Optional[Transaction]) -> Transaction: if txn: prior = _txn_payment_command(txn) if command == prior: return command.validate(prior) txn.command_json = offchain.to_json(command) txn.status = _command_transaction_status( command, TransactionStatus.OFF_CHAIN_INBOUND) else: txn = _new_payment_command_transaction( command, TransactionStatus.OFF_CHAIN_INBOUND) return txn return lock_for_update(command.reference_id(), validate_and_save)
def travel_metadata(self, cmd: offchain.PaymentCommand) -> Tuple[bytes, bytes]: metadata = cmd.travel_rule_metadata(self.hrp) return (metadata, bytes.fromhex(str(cmd.payment.recipient_signature)))
def _enqueue_follow_up_action(self, command: offchain.PaymentCommand) -> None: if command.follow_up_action(): self.task_queue.append(lambda app: app._offchain_business_action( command.reference_id()))
def _new_reject_kyc_data(self, cmd: offchain.PaymentCommand, msg: str) -> offchain.Command: return cmd.new_command(status=Status.abort, abort_code=AbortCode.reject_kyc_data, abort_message=msg)