def payment(lrw1: offchain_business.LRW, lrw2: offchain_business.LRW, user1, user2) -> PaymentObject: user1address = generate_new_subaddress(account_id=user1.account_id) user2address = generate_new_subaddress(account_id=user2.account_id) return make_payment(lrw1.user_address(user1address), lrw2.user_address(user2address), 1000)
def send_fake_tx(amount=100, send_to_self=False) -> Tuple[int, Transaction]: user = OneUser.run(db_session, account_amount=100_000_000_000, account_currency=DiemCurrency.XUS) account_id = user.account_id amount = amount payment_type = types.TransactionType.EXTERNAL currency = diem_utils.types.currencies.DiemCurrency.XUS destination_address = "receiver_address" destination_subaddress = "receiver_subaddress" if send_to_self: destination_address = account_address_hex( context.get().config.vasp_address) destination_subaddress = generate_new_subaddress(account_id) send_tx = send_transaction( sender_id=account_id, amount=amount, currency=currency, payment_type=payment_type, destination_address=destination_address, destination_subaddress=destination_subaddress, ) return account_id, get_transaction(send_tx.id) if send_tx else None
def save_outbound_transaction( sender_id: int, destination_address: str, destination_subaddress: str, amount: int, currency: DiemCurrency, ) -> Transaction: sender_onchain_address = context.get().config.vasp_address sender_subaddress = account.generate_new_subaddress(account_id=sender_id) return commit_transaction( _new_payment_command_transaction( offchain.PaymentCommand.init( identifier.encode_account( sender_onchain_address, sender_subaddress, _hrp() ), _user_kyc_data(sender_id), identifier.encode_account( destination_address, destination_subaddress, _hrp() ), amount, currency.value, ), TransactionStatus.OFF_CHAIN_OUTBOUND, ) )
async def test_check_account_existence_receiver_not_exist(lrw1, lrw2, user1): user1address = generate_new_subaddress(account_id=user1.account_id) user2address = "cf64428bdeb62af2" payment = make_payment(lrw1.user_address(user1address), lrw2.user_address(user2address), 1000) await lrw1.check_account_existence(payment) with pytest.raises(BusinessValidationFailure): await lrw2.check_account_existence(payment)
async def test_get_extended_kyc_unknown_user_subaddress(lrw1, lrw2, user2): user1address = "cf64428bdeb62af2" user2address = generate_new_subaddress(account_id=user2.account_id) payment = make_payment(lrw1.user_address(user1address), lrw2.user_address(user2address), 1000) with pytest.raises(BusinessForceAbort): await lrw1.get_extended_kyc(payment) assert await lrw2.get_extended_kyc(payment)
def internal_transaction( sender_id: int, receiver_id: int, amount: int, currency: DiemCurrency, payment_type: TransactionType, ) -> Transaction: """Transfer transaction between accounts in the LRW internal ledger.""" log_execution("Enter internal_transaction") 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(sender_id) receiver_subaddress = account_service.generate_new_subaddress(receiver_id) internal_vasp_address = context.get().config.vasp_address transaction = add_transaction( amount=amount, currency=currency, payment_type=payment_type, status=TransactionStatus.COMPLETED, source_id=sender_id, source_address=internal_vasp_address, source_subaddress=sender_subaddress, destination_id=receiver_id, destination_address=internal_vasp_address, destination_subaddress=receiver_subaddress, ) log_execution( f"Transfer from {sender_id} to {receiver_id} started with transaction id {transaction.id}" ) add_transaction_log(transaction.id, "Transfer completed") return transaction
def handle_outgoing_transaction(sender_sub_address): if sender_sub_address: sender_sub_record = SubAddress.query.filter_by( address=sender_sub_address ).first() if sender_sub_record: source_id = sender_sub_record.account_id else: source_id = Account.query.filter_by(name=INVENTORY_ACCOUNT_NAME).first().id else: source_id = Account.query.filter_by(name=INVENTORY_ACCOUNT_NAME).first().id sender_sub_address = generate_new_subaddress(source_id) return sender_sub_address, source_id
def test_process_inbound_command(monkeypatch): hrp = context.get().config.diem_address_hrp() user = OneUser.run(db_session, account_amount=100_000_000_000, account_currency=currency) amount = 10_000_000_000 sender = LocalAccount.generate() sender_subaddress = identifier.gen_subaddress() receiver_subaddress = generate_new_subaddress(user.account_id) cmd = offchain.PaymentCommand.init( identifier.encode_account(sender.account_address, sender_subaddress, hrp), _user_kyc_data(user.account_id), identifier.encode_account(context.get().config.vasp_address, receiver_subaddress, hrp), amount, currency.value, ) with monkeypatch.context() as m: client = context.get().offchain_client m.setattr( client, "process_inbound_request", lambda _, cmd: client.create_inbound_payment_command( cmd.cid, cmd.payment), ) code, resp = process_inbound_command(cmd.payment.sender.address, cmd) assert code == 200 assert resp txn = get_transaction_by_reference_id(cmd.reference_id()) assert txn assert txn.status == TransactionStatus.OFF_CHAIN_INBOUND cmd = _txn_payment_command(txn) assert cmd.is_inbound(), str(cmd) with monkeypatch.context() as m: m.setattr( context.get().offchain_client, "send_command", lambda cmd, _: offchain.reply_request(cmd.cid), ) process_offchain_tasks() db_session.refresh(txn) assert txn.status == TransactionStatus.OFF_CHAIN_OUTBOUND
def handle_incoming_transaction(receiver_sub_address): if receiver_sub_address: sub_address_record = SubAddress.query.filter_by( address=receiver_sub_address).first() if sub_address_record: destination_id = sub_address_record.account_id else: destination_id = (Account.query.filter_by( name=INVENTORY_ACCOUNT_NAME).first().id) else: destination_id = Account.query.filter_by( name=INVENTORY_ACCOUNT_NAME).first().id receiver_sub_address = generate_new_subaddress(destination_id) return destination_id, receiver_sub_address
def external_transaction( sender_id: int, receiver_address: str, receiver_subaddress: str, amount: int, currency: DiemCurrency, payment_type: TransactionType, original_txn_id: int, ) -> Transaction: logger.info( f"external_transaction {sender_id} to receiver {receiver_address}, " f"receiver subaddress {receiver_subaddress}, amount {amount}") 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_onchain_address = context.get().config.vasp_address sender_subaddress = None if (payment_type == TransactionType.EXTERNAL or payment_type == TransactionType.REFUND): sender_subaddress = account_service.generate_new_subaddress( account_id=sender_id) 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, original_txn_id=original_txn_id, ) if services.run_bg_tasks(): from ..background_tasks.background import async_external_transaction async_external_transaction.send(transaction.id) else: submit_onchain(transaction_id=transaction.id) return transaction
def test_process_incoming_travel_rule_txn() -> None: account = create_account("fake_account") sender_addr = "46db232847705e05525db0336fd9f337" receiver_addr = "lrw_vasp" sender_subaddr = generate_new_subaddress(account.id) amount = 1000 * 1_000_000 sender = account_address(sender_addr) sequence = 1 currency = DiemCurrency.XUS blockchain_version = 1 off_chain_reference_id = "off_chain_reference_id" metadata, _ = travel_rule(off_chain_reference_id, sender, amount) storage.add_transaction( amount=amount, currency=currency, payment_type=TransactionType.OFFCHAIN, status=TransactionStatus.OFF_CHAIN_READY, source_id=account.id, source_address=sender_addr, source_subaddress=sender_subaddr, destination_address=receiver_addr, reference_id=off_chain_reference_id, ) process_incoming_transaction( sender_address=sender_addr, receiver_address=receiver_addr, sequence=sequence, amount=amount, currency=currency, metadata=diem_types.Metadata__TravelRuleMetadata.bcs_deserialize( metadata), blockchain_version=blockchain_version, ) # successfully parse meta and sequence tx = storage.get_transaction_by_details(source_address=sender_addr, source_subaddress=sender_subaddr, sequence=sequence) assert tx is not None assert tx.sequence == sequence assert tx.blockchain_version == blockchain_version
def test_process_incoming_refund_txn() -> None: initial_sender_account = create_account("fake_account") initial_sender_subaddr = generate_new_subaddress(initial_sender_account.id) initial_sender_addr = "lrw_vasp" initial_receiver_addr = "46db232847705e05525db0336fd9f337" meta = refund_metadata( original_transaction_version=1, reason=diem_types.RefundReason__InvalidSubaddress(), ) initial_tx = storage.add_transaction( amount=500, currency=DiemCurrency.XUS, payment_type=TransactionType.EXTERNAL, status=TransactionStatus.COMPLETED, source_id=initial_sender_account.id, source_address=initial_sender_addr, source_subaddress=initial_sender_subaddr, destination_address=initial_receiver_addr, blockchain_version=1, ) assert initial_tx is not None assert initial_tx.blockchain_version == 1 assert storage.get_transaction_by_blockchain_version(1) is not None process_incoming_transaction( sender_address=initial_receiver_addr, receiver_address=initial_sender_addr, sequence=1, amount=500, currency=DiemCurrency.XUS, metadata=diem_types.Metadata__RefundMetadata.bcs_deserialize(meta), blockchain_version=2, ) tx = storage.get_transaction_by_blockchain_version(2) assert tx is not None assert tx.type == TransactionType.REFUND assert tx.original_txn_id == initial_tx.id
def test_process_incoming_general_txn() -> None: account = create_account("fake_account") sender_addr = "46db232847705e05525db0336fd9f337" subaddr = generate_new_subaddress(account.id) meta = general_metadata(to_subaddress=sub_address(subaddr)) process_incoming_transaction( sender_address=sender_addr, receiver_address="lrw_vasp", sequence=1, amount=100, currency=DiemCurrency.XUS, metadata=diem_types.Metadata__GeneralMetadata.bcs_deserialize(meta), blockchain_version=1, ) # successfully parse meta and sequence tx = storage.get_transaction_by_details(source_address=sender_addr, source_subaddress=None, sequence=1) assert tx is not None
def test_send_payment_between_vasps(lrw1, lrw2, vasp1, vasp2, user1, user2): sender_address = lrw1.context.config.vasp_diem_address() receiver_address = lrw2.context.config.vasp_diem_address() receiver_subaddress = generate_new_subaddress(account_id=user2.account_id) # setup global environment as lrw1 app context.set(lrw1.context) client.set(vasp1) txn = send_transaction( sender_id=user1.account_id, amount=2_000_000_000, currency=DiemCurrency.Coin1, destination_address=receiver_address.get_onchain_address_hex(), destination_subaddress=receiver_subaddress, ) assert txn assert txn.off_chain assert len(txn.off_chain) == 1 assert txn.off_chain[0].reference_id reference_id = txn.off_chain[0].reference_id num_tries = 20 while num_tries > 1: txn = get_single_transaction(txn.id) if txn.status == TransactionStatus.COMPLETED: break num_tries -= 1 time.sleep(1) payment = vasp1.get_payment_by_ref(reference_id) assert payment.sender.status.as_status() == Status.ready_for_settlement assert payment.receiver.status.as_status() == Status.ready_for_settlement payment = vasp2.get_payment_by_ref(reference_id) assert payment.sender.status.as_status() == Status.ready_for_settlement assert payment.receiver.status.as_status() == Status.ready_for_settlement
def test_subaddr_map() -> None: account = create_account(account_name="fake_account") subaddr = generate_new_subaddress(account.id) assert get_account_id_from_subaddr(subaddr) == account.id
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