async def payment_pre_processing(self, other_address, seq, command, payment): """An async method to let VASP perform custom business logic to a successsful (sequenced & ACKed) command prior to normal processing. For example it can be used to check whether the payment is in terminal status. The command could have originated either from the other VASP or this VASP (see `command.origin` to determine this). Args: other_address (str): the encoded Diem Blockchain address of the other VASP. seq (int): the sequence number into the shared command sequence. command (ProtocolCommand): the command that lead to the new or updated payment. payment (PaymentObject): the payment resulting from this command. Returns None or a context objext that will be passed on the other business context functions. """ if (payment.sender.status.as_status() == Status.ready_for_settlement and payment.receiver.status.as_status() == Status.ready_for_settlement): if self.is_sender(payment): ref_id = payment.reference_id transaction_id = get_transaction_id_from_reference_id(ref_id) transaction = get_single_transaction(transaction_id) add_metadata_signature(ref_id, payment.recipient_signature) if transaction.status == TransactionStatus.COMPLETED: return None if transaction.status == TransactionStatus.READY_FOR_ON_CHAIN: start_settle_offchain(transaction_id=transaction.id) # TODO: What should happen in this case? if transaction.status == TransactionStatus.CANCELED: raise ValueError("what should happen in this case?") else: receiver_subaddress = LibraAddress.from_encoded_str( payment.receiver.address).get_subaddress_hex() txn_id = add_transaction( amount=payment.action.amount, currency=DiemCurrency(payment.action.currency), payment_type=TransactionType.OFFCHAIN, status=TransactionStatus.READY_FOR_ON_CHAIN, source_id=None, source_address=LibraAddress.from_encoded_str( payment.sender.address).get_onchain_address_hex(), source_subaddress=LibraAddress.from_encoded_str( payment.sender.address).get_subaddress_hex(), destination_id=get_account_id_from_subaddr( receiver_subaddress), destination_address=LibraAddress.from_encoded_str( payment.receiver.address).get_onchain_address_hex(), destination_subaddress=receiver_subaddress, sequence=None, blockchain_version=None, reference_id=payment.reference_id, metadata_signature=payment.recipient_signature, )
def get_peer_base_url(self, other_addr: LibraAddress) -> str: """Get the base URL that manages off-chain communications of the other VASP. Returns: str: The base url of the other VASP. """ return self.context.get_vasp_base_url( other_addr.get_onchain_address_hex())
def make_payment( sender: LibraAddress, receiver: LibraAddress, amount: int, currency=testnet.TEST_CURRENCY_CODE, action="charge", ) -> PaymentObject: action = PaymentAction(amount, currency, action, int(time.time())) status = StatusObject(Status.none) sender_actor = PaymentActor(sender.as_str(), status, []) receiver_actor = PaymentActor(receiver.as_str(), status, []) return PaymentObject( sender_actor, receiver_actor, f"{sender.get_onchain().as_str()}_abc", None, "payment description", action, )
def get_account_id(self, payment) -> int: my_actor = payment.sender if self.is_sender( payment) else payment.receiver address = LibraAddress.from_encoded_str(my_actor.address) subaddress = address.get_subaddress_hex() account_id = get_account_id_from_subaddr(subaddress) if account_id is None: role = self.get_my_role(payment) raise BusinessForceAbort( OffChainErrorCode.payment_invalid_libra_subaddress, f"Subaccount {subaddress} does not exist in {role}.", ) return account_id
def get_new_offchain_reference_id(sender_address_hex: str) -> str: address = LibraAddress.from_hex(context.get().config.diem_address_hrp(), sender_address_hex, None) id = uuid.uuid1().hex return f"{address.as_str()}_{id}"
def vasp_diem_address(self) -> LibraAddress: return LibraAddress.from_hex(self.diem_address_hrp(), self.vasp_address, None)
def external_offchain_transaction( sender_id: int, receiver_address: str, receiver_subaddress: str, amount: int, currency: DiemCurrency, payment_type: TransactionType, original_payment_reference_id: Optional[str] = None, description: Optional[str] = None, ) -> Transaction: sender_onchain_address = context.get().config.vasp_address logger.info( f"=================Start external_offchain_transaction " f"{sender_onchain_address}, {sender_id}, {receiver_address}, {receiver_subaddress}, " f"{amount}, {currency}, {payment_type}") if not validate_balance(sender_id, amount, currency): raise BalanceError( f"Balance {account_service.get_account_balance_by_id(account_id=sender_id).total[currency]} " f"is less than amount needed {amount}") sender_subaddress = account_service.generate_new_subaddress( account_id=sender_id) logger.info(f"======sender_subaddress: {sender_subaddress}") ref_id = get_new_offchain_reference_id(sender_onchain_address) transaction = add_transaction( amount=amount, currency=currency, payment_type=payment_type, status=TransactionStatus.PENDING, source_id=sender_id, source_address=sender_onchain_address, source_subaddress=sender_subaddress, destination_id=None, destination_address=receiver_address, destination_subaddress=receiver_subaddress, reference_id=ref_id, ) # off-chain logic sender_address = LibraAddress.from_bytes( context.get().config.diem_address_hrp(), bytes.fromhex(sender_onchain_address), bytes.fromhex(sender_subaddress), ) logger.info( f"sender address: {sender_onchain_address}, {sender_address.as_str()}, {sender_address.get_onchain().as_str()}" ) receiver_address = LibraAddress.from_bytes( context.get().config.diem_address_hrp(), bytes.fromhex(receiver_address), bytes.fromhex(receiver_subaddress), ) logger.info( f"receiver address: {receiver_address.as_str()}, {receiver_address.get_onchain().as_str()}", ) sender = PaymentActor( sender_address.as_str(), StatusObject(Status.needs_kyc_data), [], ) receiver = PaymentActor(receiver_address.as_str(), StatusObject(Status.none), []) action = PaymentAction(amount, currency, "charge", int(time())) reference_id = get_reference_id_from_transaction_id(transaction.id) payment = PaymentObject( sender=sender, receiver=receiver, reference_id=reference_id, original_payment_reference_id=original_payment_reference_id, description=description, action=action, ) cmd = PaymentCommand(payment) if not get_transaction_status(transaction.id) == TransactionStatus.PENDING: logger.info( "In external_offchain_transaction, payment status is not PENDING, abort" ) return update_transaction(transaction.id, TransactionStatus.OFF_CHAIN_STARTED) logger.info( "In external_offchain_transaction: Updated status to OFF_CHAIN_STARTED" ) result = (offchain_client.get().new_command(receiver_address.get_onchain(), cmd).result(timeout=300)) logger.info(f"Offchain Result: {result}") return transaction
def get_peer_compliance_verification_key(self, other_addr: str) -> ComplianceKey: address = LibraAddress.from_encoded_str( other_addr).get_onchain_address_hex() return self.context.get_vasp_public_compliance_key(address)
def user_address(self, user_subaddress_hex: str) -> LibraAddress: return LibraAddress.from_hex( self.context.config.diem_address_hrp(), self.vasp_address.get_onchain_address_hex(), user_subaddress_hex, )
def actor_to_libra_address(actor: PaymentActor) -> LibraAddress: return LibraAddress.from_encoded_str(actor.address)