def add_outgoing_transaction_to_blockchain( patch_blockchain, sender_sub_address, amount, receiver_address, sequence, version, ): metadata = general_metadata( from_subaddress=bytes.fromhex(sender_sub_address)) transaction = mock_transaction( metadata=metadata.hex(), amount=amount, receiver_address=receiver_address, sequence_number=sequence, sender_address=VASP_ADDRESS, version=version, ) event = mock_event( metadata=metadata.hex(), amount=amount, receiver_address=receiver_address, sender_address=VASP_ADDRESS, events_key=SENT_EVENTS_KEY, sequence_number=sequence, version=version, ) patch_blockchain.add_account_transactions(addr_hex=VASP_ADDRESS, txs=[transaction]) patch_blockchain.add_events(SENT_EVENTS_KEY, [event])
def test_custodial_to_non_custodial(): client = testnet.create_client() faucet = testnet.Faucet(client) sender_custodial = CustodialApp.create(faucet.gen_account()) receiver = faucet.gen_account() amount = 1_000_000 currency_code = testnet.TEST_CURRENCY_CODE script = stdlib.encode_peer_to_peer_with_metadata_script( currency=utils.currency_code(currency_code), payee=utils.account_address(receiver.account_address), amount=amount, metadata=txnmetadata.general_metadata( sender_custodial.find_user_sub_address_by_id(0), None), metadata_signature=b"", # only travel rule metadata requires signature ) sender = sender_custodial.available_child_vasp() seq_num = client.get_account_sequence(sender.account_address) txn = create_transaction(sender, seq_num, script, currency_code) signed_txn = sender.sign(txn) client.submit(signed_txn) executed_txn = client.wait_for_transaction(signed_txn) assert executed_txn is not None
def test_custodial_to_custodial_under_threshold(): client = testnet.create_client() faucet = testnet.Faucet(client) sender_custodial = CustodialApp.create(faucet.gen_account()) receiver_custodial = CustodialApp.create(faucet.gen_account()) intent_id = receiver_custodial.payment(user_id=0, amount=1_000_000) intent = identifier.decode_intent(intent_id, identifier.TDM) script = stdlib.encode_peer_to_peer_with_metadata_script( currency=utils.currency_code(intent.currency_code), payee=utils.account_address(intent.account_address), amount=intent.amount, metadata=txnmetadata.general_metadata( sender_custodial.find_user_sub_address_by_id(0), intent.sub_address), metadata_signature=b"", # only travel rule metadata requires signature ) sender = sender_custodial.available_child_vasp() seq_num = client.get_account_sequence(sender.account_address) txn = create_transaction(sender, seq_num, script, intent.currency_code) signed_txn = sender.sign(txn) client.submit(signed_txn) executed_txn = client.wait_for_transaction(signed_txn) assert executed_txn is not None
def add_incoming_transaction_to_blockchain( patch_blockchain, receiver_sub_address, amount, sender_address, sequence, version, ): metadata_1 = general_metadata( to_subaddress=bytes.fromhex(receiver_sub_address)) transaction = mock_transaction( metadata=metadata_1.hex(), amount=amount, receiver_address=VASP_ADDRESS, sequence_number=sequence, sender_address=sender_address, version=version, ) event = mock_event( metadata=metadata_1.hex(), amount=amount, receiver_address=VASP_ADDRESS, sender_address=sender_address, events_key=RECEIVED_EVENTS_KEY, sequence_number=sequence, version=version, ) patch_blockchain.add_account_transactions(addr_hex=VASP_ADDRESS, txs=[transaction]) patch_blockchain.add_events(RECEIVED_EVENTS_KEY, [event]) return transaction
def test_receive_payment_with_general_metadata_and_invalid_to_subaddress( stub_client: RestClient, target_client: RestClient, currency: str, hrp: str, stub_config: AppConfig, diem_client: jsonrpc.Client, invalid_to_subaddress: Optional[bytes], ) -> None: """When received a payment with general metadata and invalid to subaddress, receiver should refund the payment by using RefundMetadata with reason `invalid subaddress`. Test Plan: 1. Generate a valid account identifier from receiver account as payee. 2. Create a general metadata with valid from subaddress and invalid to subaddress. 3. Send payment transaction from sender to receiver on-chain account. 4. Wait for the transaction executed successfully. 5. Assert sender account received a payment transaction with refund metadata. 6. Assert receiver account does not receive funds. """ amount = 1_000_000 sender_account = stub_client.create_account(balances={currency: amount}) receiver_account = target_client.create_account() try: receiver_account_identifier = receiver_account.generate_account_identifier( ) receiver_account_address: diem_types.AccountAddress = identifier.decode_account_address( receiver_account_identifier, hrp) sender_account_identifier = sender_account.generate_account_identifier( ) valid_from_subaddress = identifier.decode_account_subaddress( sender_account_identifier, hrp) invalid_metadata = txnmetadata.general_metadata( valid_from_subaddress, invalid_to_subaddress) original_payment_txn: jsonrpc.Transaction = stub_config.account.submit_and_wait_for_txn( diem_client, stdlib.encode_peer_to_peer_with_metadata_script( currency=utils.currency_code(currency), amount=amount, payee=receiver_account_address, metadata=invalid_metadata, metadata_signature=b"", ), ) sender_account.wait_for_event( "created_transaction", status=Transaction.Status.completed, refund_diem_txn_version=original_payment_txn.version, refund_reason=RefundReason.invalid_subaddress, ) assert receiver_account.balance(currency) == 0 finally: receiver_account.log_events() sender_account.log_events()
def test_refund_transaction_of_custodial_to_custodial_under_threshold(): client = testnet.create_client() faucet = testnet.Faucet(client) sender_custodial = CustodialApp.create(faucet.gen_account(), client) receiver_custodial = CustodialApp.create(faucet.gen_account(), client) # create a payment transaction intent_id = receiver_custodial.payment(user_id=0, amount=1_000_000) intent = identifier.decode_intent(intent_id, identifier.TDM) receiver_address = utils.account_address(intent.account_address) script = stdlib.encode_peer_to_peer_with_metadata_script( currency=utils.currency_code(intent.currency_code), payee=receiver_address, amount=intent.amount, metadata=txnmetadata.general_metadata( sender_custodial.find_user_sub_address_by_id(0), intent.sub_address), metadata_signature=b"", # only travel rule metadata requires signature ) sender = sender_custodial.available_child_vasp() txn = sender_custodial.create_transaction(sender, script, intent.currency_code) signed_txn = sender.sign(txn) client.submit(signed_txn) executed_txn = client.wait_for_transaction(signed_txn) # start to refund the transaction # find the event for the receiver, a p2p transaction may contains multiple receivers # in the future. event = txnmetadata.find_refund_reference_event(executed_txn, receiver_address) assert event is not None amount = event.data.amount.amount currency_code = event.data.amount.currency refund_receiver_address = utils.account_address(event.data.sender) metadata = txnmetadata.refund_metadata_from_event(event) refund_txn_script = stdlib.encode_peer_to_peer_with_metadata_script( currency=utils.currency_code(currency_code), payee=refund_receiver_address, amount=amount, metadata=metadata, metadata_signature=b"", # only travel rule metadata requires signature ) # receiver is sender of refund txn sender = receiver_custodial.available_child_vasp() txn = receiver_custodial.create_transaction(sender, refund_txn_script, currency_code) refund_executed_txn = receiver_custodial.submit_and_wait(sender.sign(txn)) assert refund_executed_txn is not None
def p2p_by_general( self, currency: str, amount: int, receiver_vasp_address: str, receiver_sub_address: str, sender_sub_address: str, ) -> jsonrpc.Transaction: metadata = txnmetadata.general_metadata( from_subaddress=bytes.fromhex(sender_sub_address), to_subaddress=bytes.fromhex(receiver_sub_address), ) return self._p2p_transfer(currency, amount, receiver_vasp_address, metadata, b"")
def test_receive_payment_with_general_metadata_and_invalid_subaddresses( sender_account: AccountResource, receiver_account: AccountResource, currency: str, hrp: str, stub_config: AppConfig, diem_client: jsonrpc.Client, invalid_to_subaddress: Optional[bytes], invalid_from_subaddress: Optional[bytes], pending_income_account: AccountResource, ) -> None: """When received a payment with general metadata and invalid subaddresses, it is considered same with the case received invalid to subaddress, and receiver should refund the payment. Test Plan: 1. Generate a valid account identifier from receiver account as payee. 2. Create a general metadata with invalid from subaddress and invalid to subaddress. 3. Send payment transaction from sender to receiver on-chain account. 4. Wait for the transaction executed successfully. 5. Assert sender account received a payment transaction with refund metadata. 6. Assert receiver account does not receive funds. """ receiver_account_identifier = receiver_account.generate_account_identifier( ) receiver_account_address = identifier.decode_account_address( receiver_account_identifier, hrp) invalid_metadata = txnmetadata.general_metadata(invalid_from_subaddress, invalid_to_subaddress) original_payment_txn: jsonrpc.Transaction = stub_config.account.submit_and_wait_for_txn( diem_client, stdlib.encode_peer_to_peer_with_metadata_script( currency=utils.currency_code(currency), amount=amount, payee=receiver_account_address, metadata=invalid_metadata, metadata_signature=b"", ), ) pending_income_account.wait_for_event( "created_transaction", status=Transaction.Status.completed, refund_diem_txn_version=original_payment_txn.version, refund_reason=RefundReason.invalid_subaddress, ) assert receiver_account.balance(currency) == 0
def test_receive_payment_with_general_metadata_and_invalid_from_subaddress( stub_client: RestClient, target_client: RestClient, currency: str, hrp: str, stub_config: AppConfig, diem_client: jsonrpc.Client, invalid_from_subaddress: Optional[bytes], ) -> None: """When received a payment with general metadata and invalid from subaddress, receiver is not required to take any action on it as long as to subaddress is valid. Test Plan: 1. Generate a valid account identifier from receiver account as payee. 2. Create a general metadata with invalid from subaddress and valid to subaddress. 3. Send payment transaction from sender to receiver on-chain account. 4. Wait for the transaction executed successfully. 5. Assert receiver account received funds eventually. """ amount = 1_000_000 sender_account = stub_client.create_account(balances={currency: amount}) receiver_account = target_client.create_account() try: receiver_account_identifier = receiver_account.generate_account_identifier( ) receiver_account_address = identifier.decode_account_address( receiver_account_identifier, hrp) valid_to_subaddress = identifier.decode_account_subaddress( receiver_account_identifier, hrp) invalid_metadata = txnmetadata.general_metadata( invalid_from_subaddress, valid_to_subaddress) stub_config.account.submit_and_wait_for_txn( diem_client, stdlib.encode_peer_to_peer_with_metadata_script( currency=utils.currency_code(currency), amount=amount, payee=receiver_account_address, metadata=invalid_metadata, metadata_signature=b"", ), ) receiver_account.wait_for_balance(currency, amount) finally: receiver_account.log_events() sender_account.log_events()
def mint( self, authkey_hex: str, amount: int, identifier: str, session: Optional[requests.Session] = None, timeout: Optional[Union[float, Tuple[float, float]]] = None, ) -> int: dd_address_hex = account_address_hex(DESIGNATED_DEALER_ADDRESS) account = BlockchainMock.get_account_resource(dd_address_hex) sequence = account.sequence_number version = BlockchainMock.blockchain.version decoded_addr, decoded_subaddr = decode_account(authkey_hex) metadata = general_metadata(to_subaddress=decoded_subaddr) address_hex_bytes = account_address_bytes(decoded_addr) process_incoming_transaction( sender_address=dd_address_hex, receiver_address=authkey_hex, sequence=sequence, amount=amount, currency=DiemCurrency.Coin1, metadata=metadata, ) BlockchainMock.blockchain.version += 1 tx = MockSignedTransaction( sender=bytes.fromhex(ASSOC_AUTHKEY), amount=amount, currency=identifier, receiver=address_hex_bytes, metadata=metadata, sequence=account.sequence_number, version=version, ) account.sequence_number += 1 account.transactions[sequence] = tx BlockchainMock.blockchain.transactions[version] = tx return sequence
def test_refund_metadata_from_event_that_has_from_subaddress(): from_sub_address = "8f8b82153010a1bd" reference_event_seq = 324 metadata = txnmetadata.general_metadata( utils.sub_address(from_sub_address)) event = jsonrpc.Event( data=jsonrpc.EventData(metadata=metadata.hex(), ), sequence_number=reference_event_seq, ) ret = txnmetadata.refund_metadata_from_event(event) assert ret is not None gm = diem_types.Metadata__GeneralMetadata.bcs_deserialize(ret) assert gm is not None assert gm.value.value.from_subaddress is None assert gm.value.value.to_subaddress.hex() == from_sub_address assert int(gm.value.value.referenced_event) == reference_event_seq
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_receive_payment_with_general_metadata_and_invalid_from_subaddress( sender_account: AccountResource, receiver_account: AccountResource, currency: str, hrp: str, stub_config: AppConfig, diem_client: jsonrpc.Client, invalid_from_subaddress: Optional[bytes], ) -> None: """When received a payment with general metadata and invalid from subaddress, receiver is not required to take any action on it as long as to subaddress is valid. Test Plan: 1. Generate a valid payment URI from the receiver account. 2. Create a general metadata with invalid from subaddress and valid to subaddress. 3. Send payment transaction from sender to receiver on-chain account. 4. Wait for the transaction executed successfully. 5. Assert receiver account received funds eventually. """ receiver_uri = receiver_account.generate_payment_uri() receiver_account_address: diem_types.AccountAddress = receiver_uri.intent( hrp).account_address valid_to_subaddress = receiver_uri.intent(hrp).subaddress invalid_metadata = txnmetadata.general_metadata(invalid_from_subaddress, valid_to_subaddress) stub_config.account.submit_and_wait_for_txn( diem_client, stdlib.encode_peer_to_peer_with_metadata_script( currency=utils.currency_code(currency), amount=amount, payee=receiver_account_address, metadata=invalid_metadata, metadata_signature=b"", ), ) receiver_account.wait_for_balance(currency, amount)
def send_transaction( self, currency: DiemCurrency, amount: int, dest_vasp_address: str, dest_sub_address: str, source_sub_address: str = None, ) -> Tuple[int, int]: account_info = self.fetch_account_info() if not account_info: raise RuntimeError(f"Could not find account {self.address_str}") if source_sub_address is None: source_sub_address = secrets.token_hex( identifier.DIEM_SUBADDRESS_SIZE) meta = txnmetadata.general_metadata( from_subaddress=bytes.fromhex(source_sub_address), to_subaddress=bytes.fromhex(dest_sub_address), ) script = stdlib.encode_peer_to_peer_with_metadata_script( currency=utils.currency_code(currency.value), payee=utils.account_address(dest_vasp_address), amount=amount, metadata=meta, metadata_signature=b"", ) tx = self._custody.create_transaction( self._custody_account_name, account_info.sequence_number, script, currency.value, ) self._diem_client.submit(tx) onchain_tx = self._diem_client.wait_for_transaction(tx, 30) return onchain_tx.version, account_info.sequence_number
async def test_p2p_under_threshold_by_subaddress(): client = create_client() faucet = Faucet(client) sender = await faucet.gen_account() sender_subaddress = identifier.gen_subaddress() receiver = await faucet.gen_account() receiver_subaddress = identifier.gen_subaddress() amount = 2_000_000 payload = stdlib.encode_peer_to_peer_with_metadata_script_function( currency=utils.currency_code(XUS), payee=receiver.account_address, amount=amount, metadata=txnmetadata.general_metadata(sender_subaddress, receiver_subaddress), metadata_signature=b"", # only travel rule metadata requires signature ) seq_num = await client.get_account_sequence(sender.account_address) txn = diem_types.RawTransaction( sender=sender.account_address, sequence_number=seq_num, payload=payload, max_gas_amount=1_000_000, gas_unit_price=0, gas_currency_code=XUS, expiration_timestamp_secs=int(time.time()) + 30, chain_id=chain_ids.TESTNET, ) signed_txn = sender.sign(txn) await client.submit(signed_txn) executed_txn = await client.wait_for_transaction(signed_txn) assert executed_txn is not None
def trade_and_execute( self, quote_id: QuoteId, direction: Direction, diem_deposit_address: Optional[str] = None, tx_version: Optional[int] = None, ) -> TradeId: quote = LpClientMock.QUOTES[quote_id] trade_id = TradeId(uuid4()) metadata = diem_types.Metadata__Undefined() if diem_deposit_address is not None: addr, subaddr = identifier.decode_account(diem_deposit_address, "tdm") metadata = general_metadata(to_subaddress=subaddr) if direction == Direction.Buy: process_incoming_transaction( sender_address="", receiver_address=diem_deposit_address, sequence=1, amount=quote.amount, currency=quote.rate.pair.base.value, metadata=metadata, blockchain_version=1, ) trade_data = TradeData( trade_id=trade_id, direction=direction, pair=quote.rate.pair, amount=quote.amount, status=TradeStatus.Complete, quote=quote, tx_version=1, ) LpClientMock.TRADES[trade_id] = trade_data return trade_id
def test_new_general_metadata_from_to_sub_address(): from_sub_address = utils.sub_address("8f8b82153010a1bd") to_sub_address = utils.sub_address("111111153010a111") ret = txnmetadata.general_metadata(from_sub_address, to_sub_address) assert ret.hex() == "01000108111111153010a11101088f8b82153010a1bd00"
def test_new_general_metadata_to_sub_address(): sub_address = utils.sub_address("8f8b82153010a1bd") ret = txnmetadata.general_metadata(None, sub_address) assert ret.hex() == "010001088f8b82153010a1bd0000"
def test_new_general_metadata_for_nones(): ret = txnmetadata.general_metadata(None, None) assert ret == b""
# use DD private key to send P2P transaction to the watched account sender_account = LocalAccount( Ed25519PrivateKey.from_private_bytes( bytes.fromhex(private_key))) sender_account_info = diem_client.get_account( sender_account.account_address) sender_account_addr_hex = utils.account_address_hex( sender_account.account_address) script = stdlib.encode_peer_to_peer_with_metadata_script( currency=utils.currency_code(currency), payee=watched_account_addr, amount=refill_amount, metadata=txnmetadata.general_metadata( from_subaddress=utils.account_address_bytes( sender_account.account_address), to_subaddress=utils.account_address_bytes( watched_account_addr), ), metadata_signature=b"", ) raw_tx = diem_types.RawTransaction( sender=sender_account.account_address, sequence_number=sender_account_info.sequence_number, payload=diem_types.TransactionPayload__Script(script), max_gas_amount=1_000_000, gas_unit_price=0, gas_currency_code=currency, expiration_timestamp_secs=int(time()) + 30, chain_id=diem_types.ChainId.from_int(network_chainid), ) tx = sender_account.sign(raw_tx)
def general_metadata(self, from_subaddress: bytes, payee: str) -> Tuple[bytes, bytes]: to_account, to_subaddress = identifier.decode_account(payee, self.hrp) return (txnmetadata.general_metadata(from_subaddress, to_subaddress), b"")
def test_new_general_metadata_for_nones(): ret = txnmetadata.general_metadata(None, None) assert ret.hex() == "0100000000"