mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-20 03:35:51 +00:00
Working hybrid async/thread code w/ domain verif
This commit is contained in:
@@ -9,11 +9,147 @@ import re
|
|||||||
import wx
|
import wx
|
||||||
import wx.dataview
|
import wx.dataview
|
||||||
import wx.adv
|
import wx.adv
|
||||||
from wxasync import AsyncBind, WxAsyncApp, StartCoroutine
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from queue import Queue, Empty
|
from queue import Queue, Empty
|
||||||
|
|
||||||
|
|
||||||
|
class XRPLMonitorThread(Thread):
|
||||||
|
"""
|
||||||
|
A worker thread to watch for new ledger events and pass the info back to
|
||||||
|
the main frame to be shown in the UI. Using a thread lets us maintain the
|
||||||
|
responsiveness of the UI while doing work in the background.
|
||||||
|
"""
|
||||||
|
def __init__(self, url, gui, account, loop):
|
||||||
|
Thread.__init__(self, daemon=True)
|
||||||
|
self.gui = gui
|
||||||
|
self.url = url
|
||||||
|
self.account = account
|
||||||
|
#self.client = xrpl.clients.WebsocketClient(self.ws_url, timeout=0.2)
|
||||||
|
#self.jobq = Queue() # for incoming requests from the GUI
|
||||||
|
self.loop = loop
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
asyncio.set_event_loop(self.loop)
|
||||||
|
self.loop.run_forever()
|
||||||
|
|
||||||
|
async def watch_xrpl(self):
|
||||||
|
async with xrpl.asyncio.clients.AsyncWebsocketClient(self.url) as self.client:
|
||||||
|
await self.on_connected()
|
||||||
|
async for message in self.client:
|
||||||
|
mtype = message.get("type")
|
||||||
|
if mtype == "ledgerClosed":
|
||||||
|
wx.CallAfter(self.gui.update_ledger, message)
|
||||||
|
elif mtype == "transaction":
|
||||||
|
wx.CallAfter(self.gui.add_tx_from_sub, message)
|
||||||
|
response = await self.client.request(xrpl.models.requests.AccountInfo(
|
||||||
|
account=self.account,
|
||||||
|
ledger_index=message["ledger_index"]
|
||||||
|
))
|
||||||
|
wx.CallAfter(self.gui.update_account, response.result["account_data"])
|
||||||
|
|
||||||
|
async def on_connected(self):
|
||||||
|
"""
|
||||||
|
Set up initial subscriptions and pass initial responses from the network
|
||||||
|
back to the GUI thread for display.
|
||||||
|
"""
|
||||||
|
response = await self.client.request(xrpl.models.requests.Subscribe(
|
||||||
|
streams=["ledger"],
|
||||||
|
accounts=[self.account]
|
||||||
|
))
|
||||||
|
# The immediate response contains details for the last validated ledger
|
||||||
|
wx.CallAfter(self.gui.update_ledger, response.result)
|
||||||
|
|
||||||
|
# Get starting values for account info, account transaction history
|
||||||
|
response = await self.client.request(xrpl.models.requests.AccountInfo(
|
||||||
|
account=self.account,
|
||||||
|
ledger_index="validated"
|
||||||
|
))
|
||||||
|
wx.CallAfter(self.gui.update_account, response.result["account_data"])
|
||||||
|
response = await self.client.request(xrpl.models.requests.AccountTx(
|
||||||
|
account=self.account
|
||||||
|
))
|
||||||
|
wx.CallAfter(self.gui.update_account_tx, response.result)
|
||||||
|
|
||||||
|
|
||||||
|
async def check_destination(self, destination, dlg):
|
||||||
|
"""
|
||||||
|
Check a potential destination address's details:
|
||||||
|
- Is the account funded?
|
||||||
|
If not, payments below the reserve base will fail
|
||||||
|
- Do they have DisallowXRP enabled?
|
||||||
|
If so, the user should be warned they don't want XRP, but can click
|
||||||
|
through.
|
||||||
|
- Do they have a verified Domain?
|
||||||
|
If so, we want to show the user the associated domain info.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The data to send back to the GUI thread: None for checks that weren't
|
||||||
|
# performed, True/False for actual results except where noted.
|
||||||
|
account_status = {
|
||||||
|
"funded": None,
|
||||||
|
"disallow_xrp": None,
|
||||||
|
"deposit_auth": None,
|
||||||
|
"domain_verified": None,
|
||||||
|
"domain_str": "" # the decoded domain, regardless of verification
|
||||||
|
}
|
||||||
|
|
||||||
|
# Look up account, see if it's even funded
|
||||||
|
try:
|
||||||
|
response = await xrpl.asyncio.account.get_account_info(destination,
|
||||||
|
self.client, ledger_index="validated")
|
||||||
|
account_status["funded"] = True
|
||||||
|
dest_acct = response.result["account_data"]
|
||||||
|
except xrpl.asyncio.clients.exceptions.XRPLRequestFailureException:
|
||||||
|
# Not funded, so the other checks don't apply.
|
||||||
|
account_status["funded"] = False
|
||||||
|
wx.CallAfter(dlg.UpdateDestInfo, account_status)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check DisallowXRP flag
|
||||||
|
lsfDisallowXRP = 0x00080000
|
||||||
|
if dest_acct["Flags"] & lsfDisallowXRP:
|
||||||
|
account_status["disallow_xrp"] = True
|
||||||
|
else:
|
||||||
|
account_status["disallow_xrp"] = False
|
||||||
|
|
||||||
|
# Check domain verification
|
||||||
|
domain, verified = verify_account_domain(dest_acct)
|
||||||
|
account_status["domain_verified"] = verified
|
||||||
|
account_status["domain_str"] = domain
|
||||||
|
|
||||||
|
# TODO: check Deposit Auth
|
||||||
|
|
||||||
|
# Send data back to the main thread.
|
||||||
|
wx.CallAfter(dlg.UpdateDestInfo, account_status)
|
||||||
|
|
||||||
|
async def send_xrp(self, paydata):
|
||||||
|
dtag = paydata.get("dtag")
|
||||||
|
wallet = paydata.get("wallet")
|
||||||
|
if dtag.strip() == "":
|
||||||
|
dtag = None
|
||||||
|
if dtag is not None:
|
||||||
|
try:
|
||||||
|
dtag = int(dtag)
|
||||||
|
if dtag < 0 or dtag > 2**32-1:
|
||||||
|
raise ValueError("Destination tag must be a 32-bit unsigned integer")
|
||||||
|
except ValueError as e:
|
||||||
|
print("Invalid destination tag:", e)
|
||||||
|
print("Canceled sending payment.")
|
||||||
|
return
|
||||||
|
|
||||||
|
tx = xrpl.models.transactions.Payment(
|
||||||
|
account=self.account,
|
||||||
|
sequence=wallet.sequence,
|
||||||
|
destination=paydata["to"],
|
||||||
|
amount=xrpl.utils.xrp_to_drops(paydata["amt"]),
|
||||||
|
destination_tag=dtag
|
||||||
|
)
|
||||||
|
tx_signed = await xrpl.asyncio.transaction.safe_sign_and_autofill_transaction(tx, wallet, self.client)
|
||||||
|
await xrpl.asyncio.transaction.submit_transaction(tx_signed, self.client)
|
||||||
|
wx.CallAfter(self.gui.add_pending_tx, tx_signed)
|
||||||
|
|
||||||
|
|
||||||
class AutoGridBagSizer(wx.GridBagSizer):
|
class AutoGridBagSizer(wx.GridBagSizer):
|
||||||
"""
|
"""
|
||||||
Helper class for adding a bunch of items uniformly to a GridBagSizer.
|
Helper class for adding a bunch of items uniformly to a GridBagSizer.
|
||||||
@@ -58,8 +194,6 @@ class SendXRPDialog(wx.Dialog):
|
|||||||
self.domain_text = wx.StaticText(self, label="")
|
self.domain_text = wx.StaticText(self, label="")
|
||||||
self.domain_verified = wx.StaticBitmap(self, bitmap=bmp_check)
|
self.domain_verified = wx.StaticBitmap(self, bitmap=bmp_check)
|
||||||
self.domain_verified.Hide()
|
self.domain_verified.Hide()
|
||||||
#self.domain_mismatch = wx.StaticBitmap(self, bitmap=bmp_err)
|
|
||||||
#self.domain_mismatch.SetTooltip("Fail to verify domain")
|
|
||||||
|
|
||||||
if max_send <= 0:
|
if max_send <= 0:
|
||||||
max_send = 100000000.0
|
max_send = 100000000.0
|
||||||
@@ -90,11 +224,18 @@ class SendXRPDialog(wx.Dialog):
|
|||||||
self.txt_dtag.Bind(wx.EVT_TEXT, self.onDestTagEdit)
|
self.txt_dtag.Bind(wx.EVT_TEXT, self.onDestTagEdit)
|
||||||
## TODO: why does this only run when the dialog is closed?
|
## TODO: why does this only run when the dialog is closed?
|
||||||
## and is there a fix for AsyncShowDialog causing an invalid ptr deref??
|
## and is there a fix for AsyncShowDialog causing an invalid ptr deref??
|
||||||
AsyncBind(wx.EVT_TEXT, self.onToEdit, self.txt_to)
|
self.txt_to.Bind(wx.EVT_TEXT, self.onToEdit)
|
||||||
|
#AsyncBind(wx.EVT_TEXT, self.onToEdit, self.txt_to)
|
||||||
|
|
||||||
async def onToEdit(self, event):
|
def onToEdit(self, event):
|
||||||
v = self.txt_to.GetValue().strip()
|
v = self.txt_to.GetValue().strip()
|
||||||
|
# Reset warnings / domain verification
|
||||||
err_msg = ""
|
err_msg = ""
|
||||||
|
self.err_to.SetToolTip("")
|
||||||
|
self.err_to.Hide()
|
||||||
|
self.domain_text.SetLabel("")
|
||||||
|
self.domain_verified.Hide()
|
||||||
|
|
||||||
if xrpl.core.addresscodec.is_valid_xaddress(v):
|
if xrpl.core.addresscodec.is_valid_xaddress(v):
|
||||||
cl_addr, tag, is_test = xrpl.core.addresscodec.xaddress_to_classic_address(v)
|
cl_addr, tag, is_test = xrpl.core.addresscodec.xaddress_to_classic_address(v)
|
||||||
self.txt_dtag.ChangeValue(str(tag))
|
self.txt_dtag.ChangeValue(str(tag))
|
||||||
@@ -114,42 +255,9 @@ class SendXRPDialog(wx.Dialog):
|
|||||||
err_msg = "Not a valid address."
|
err_msg = "Not a valid address."
|
||||||
elif v == self.parent.classic_address:
|
elif v == self.parent.classic_address:
|
||||||
err_msg = "Can't send XRP to self."
|
err_msg = "Can't send XRP to self."
|
||||||
|
else:
|
||||||
# Check for Disallow XRP
|
task = asyncio.run_coroutine_threadsafe(self.parent.worker.check_destination(v, self), self.parent.worker_loop)
|
||||||
try:
|
task.result()
|
||||||
response = await xrpl.asyncio.account.get_account_info(v,
|
|
||||||
self.parent.client, ledger_index="validated")
|
|
||||||
dest_funded = True
|
|
||||||
dest_acct = response.result["account_data"]
|
|
||||||
except xrpl.asyncio.clients.exceptions.XRPLRequestFailureException:
|
|
||||||
dest_funded = False
|
|
||||||
|
|
||||||
if dest_funded:
|
|
||||||
lsfDisallowXRP = 0x00080000
|
|
||||||
if dest_acct["Flags"] & lsfDisallowXRP:
|
|
||||||
err_msg = "This account does not want to receive XRP"
|
|
||||||
|
|
||||||
# Domain verification
|
|
||||||
bmp_err = wx.ArtProvider.GetBitmap(wx.ART_ERROR, wx.ART_CMN_DIALOG, size=(16,16))
|
|
||||||
bmp_check = wx.ArtProvider.GetBitmap(wx.ART_TICK_MARK, wx.ART_CMN_DIALOG, size=(16,16))
|
|
||||||
domain, verified = verify_account_domain(dest_acct)
|
|
||||||
if not domain:
|
|
||||||
self.domain_text.Hide()
|
|
||||||
self.domain_verified.Show()
|
|
||||||
elif verified:
|
|
||||||
self.domain_text.SetLabel(domain)
|
|
||||||
self.domain_text.Show()
|
|
||||||
self.domain_verified.SetTooltip("Domain verified")
|
|
||||||
self.domain_verified.SetBitmap(bmp_check)
|
|
||||||
self.domain_verified.Show()
|
|
||||||
else:
|
|
||||||
self.domain_text.SetLabel(domain)
|
|
||||||
self.domain_text.Show()
|
|
||||||
self.domain_verified.SetTooltip("Failed to verify domain")
|
|
||||||
self.domain_verified.SetBitmap(bmp_err)
|
|
||||||
self.domain_verified.Show()
|
|
||||||
|
|
||||||
# TODO: Check for Deposit Auth
|
|
||||||
|
|
||||||
if err_msg:
|
if err_msg:
|
||||||
self.err_to.SetToolTip(err_msg)
|
self.err_to.SetToolTip(err_msg)
|
||||||
@@ -170,29 +278,43 @@ class SendXRPDialog(wx.Dialog):
|
|||||||
"amt": self.txt_amt.GetValue(),
|
"amt": self.txt_amt.GetValue(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def verify_account_domain(account):
|
def UpdateDestInfo(self, dest_status):
|
||||||
"""
|
print("dest_status:", dest_status) #TODO: remove
|
||||||
Verify an account using an xrp-ledger.toml file.
|
# Keep existing error message if there is one
|
||||||
|
err_msg = self.err_to.GetToolTip().GetTip().strip()
|
||||||
|
|
||||||
Params:
|
if not dest_status["funded"]:
|
||||||
account:dict - the AccountRoot object to verify
|
err_msg = ("Warning: this account does not exist. The payment will "
|
||||||
Returns (domain:str, verified:bool)
|
"fail unless you send enough to fund it.")
|
||||||
"""
|
elif dest_status["disallow_xrp"]:
|
||||||
domain_hex = account.get("Domain")
|
err_msg = "This account does not want to receive XRP."
|
||||||
if not domain_hex:
|
|
||||||
return "", False
|
# Domain verification
|
||||||
verified = False
|
bmp_err = wx.ArtProvider.GetBitmap(wx.ART_ERROR, wx.ART_CMN_DIALOG, size=(16,16))
|
||||||
domain = xrpl.utils.hex_to_str(domain_hex)
|
bmp_check = wx.ArtProvider.GetBitmap(wx.ART_TICK_MARK, wx.ART_CMN_DIALOG, size=(16,16))
|
||||||
toml_url = f"https://{domain}/.well-known/xrp-ledger.toml"
|
domain = dest_status["domain_str"]
|
||||||
toml_response = requests.get(toml_url)
|
verified = dest_status["domain_verified"]
|
||||||
if toml_response.ok:
|
if not domain:
|
||||||
parsed_toml = toml.loads(toml_response.text)
|
self.domain_text.Hide()
|
||||||
toml_accounts = parsed_toml.get("ACCOUNTS", [])
|
self.domain_verified.Hide()
|
||||||
for t_a in toml_accounts:
|
elif verified:
|
||||||
if t_a.get("address") == account.get("Account"):
|
self.domain_text.SetLabel(domain)
|
||||||
verified = True
|
self.domain_text.Show()
|
||||||
break
|
self.domain_verified.SetToolTip("Domain verified")
|
||||||
return domain, verified
|
self.domain_verified.SetBitmap(bmp_check)
|
||||||
|
self.domain_verified.Show()
|
||||||
|
else:
|
||||||
|
self.domain_text.SetLabel(domain)
|
||||||
|
self.domain_text.Show()
|
||||||
|
self.domain_verified.SetToolTip("Failed to verify domain")
|
||||||
|
self.domain_verified.SetBitmap(bmp_err)
|
||||||
|
self.domain_verified.Show()
|
||||||
|
|
||||||
|
if err_msg:
|
||||||
|
self.err_to.SetToolTip(err_msg)
|
||||||
|
self.err_to.Show()
|
||||||
|
else:
|
||||||
|
self.err_to.Hide()
|
||||||
|
|
||||||
|
|
||||||
class TWaXLFrame(wx.Frame):
|
class TWaXLFrame(wx.Frame):
|
||||||
@@ -203,6 +325,7 @@ class TWaXLFrame(wx.Frame):
|
|||||||
def __init__(self, url, test_network=True):
|
def __init__(self, url, test_network=True):
|
||||||
wx.Frame.__init__(self, None, title="TWaXL", size=wx.Size(800,400))
|
wx.Frame.__init__(self, None, title="TWaXL", size=wx.Size(800,400))
|
||||||
|
|
||||||
|
self.url = url
|
||||||
self.test_network = test_network
|
self.test_network = test_network
|
||||||
|
|
||||||
self.tabs = wx.Notebook(self, style=wx.BK_DEFAULT)
|
self.tabs = wx.Notebook(self, style=wx.BK_DEFAULT)
|
||||||
@@ -214,7 +337,8 @@ class TWaXLFrame(wx.Frame):
|
|||||||
self.acct_info_area = wx.StaticBox(main_panel, label="Account Info")
|
self.acct_info_area = wx.StaticBox(main_panel, label="Account Info")
|
||||||
|
|
||||||
lbl_address = wx.StaticText(self.acct_info_area, label="Classic Address:")
|
lbl_address = wx.StaticText(self.acct_info_area, label="Classic Address:")
|
||||||
self.st_classic_address = wx.StaticText(self.acct_info_area, label="TBD")
|
self.tc_classic_address = wx.TextCtrl(self.acct_info_area, value="TBD")
|
||||||
|
#self.tc_classic_address.Disable() # Only a text control so user can copy-paste
|
||||||
lbl_xaddress = wx.StaticText(self.acct_info_area, label="X-Address:")
|
lbl_xaddress = wx.StaticText(self.acct_info_area, label="X-Address:")
|
||||||
self.st_x_address = wx.StaticText(self.acct_info_area, label="TBD")
|
self.st_x_address = wx.StaticText(self.acct_info_area, label="TBD")
|
||||||
lbl_xrp_bal = wx.StaticText(self.acct_info_area, label="XRP Balance:")
|
lbl_xrp_bal = wx.StaticText(self.acct_info_area, label="XRP Balance:")
|
||||||
@@ -223,7 +347,7 @@ class TWaXLFrame(wx.Frame):
|
|||||||
self.st_reserve = wx.StaticText(self.acct_info_area, label="TBD")
|
self.st_reserve = wx.StaticText(self.acct_info_area, label="TBD")
|
||||||
|
|
||||||
aia_sizer = AutoGridBagSizer(self.acct_info_area)
|
aia_sizer = AutoGridBagSizer(self.acct_info_area)
|
||||||
aia_sizer.BulkAdd( ((lbl_address, self.st_classic_address),
|
aia_sizer.BulkAdd( ((lbl_address, self.tc_classic_address),
|
||||||
(lbl_xaddress, self.st_x_address),
|
(lbl_xaddress, self.st_x_address),
|
||||||
(lbl_xrp_bal, self.st_xrp_balance),
|
(lbl_xrp_bal, self.st_xrp_balance),
|
||||||
(lbl_reserve, self.st_reserve)) )
|
(lbl_reserve, self.st_reserve)) )
|
||||||
@@ -283,10 +407,14 @@ class TWaXLFrame(wx.Frame):
|
|||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
# Attach handlers and start bg thread for updates from the ledger ------
|
# Attach handlers and start bg thread for updates from the ledger ------
|
||||||
# self.Bind(wx.EVT_BUTTON, self.send_xrp, source=self.sxb)
|
self.Bind(wx.EVT_BUTTON, self.click_send_xrp, source=self.sxb)
|
||||||
AsyncBind(wx.EVT_BUTTON, self.send_xrp, self.sxb)
|
#AsyncBind(wx.EVT_BUTTON, self.send_xrp, self.sxb)
|
||||||
self.url = url
|
|
||||||
StartCoroutine(self.monitor_xrpl, self)
|
#StartCoroutine(self.monitor_xrpl, self)
|
||||||
|
self.worker_loop = asyncio.new_event_loop()
|
||||||
|
self.worker = XRPLMonitorThread(url, self, self.classic_address, self.worker_loop)
|
||||||
|
self.worker.start()
|
||||||
|
task = asyncio.run_coroutine_threadsafe(self.worker.watch_xrpl(), self.worker_loop)
|
||||||
|
|
||||||
def toggle_dialog_style(self, event):
|
def toggle_dialog_style(self, event):
|
||||||
"""
|
"""
|
||||||
@@ -337,44 +465,10 @@ class TWaXLFrame(wx.Frame):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
exit(1)
|
exit(1)
|
||||||
self.st_classic_address.SetLabel(self.classic_address)
|
#self.st_classic_address.SetLabel(self.classic_address)
|
||||||
|
self.tc_classic_address.ChangeValue(self.classic_address)
|
||||||
self.st_x_address.SetLabel(self.xaddress)
|
self.st_x_address.SetLabel(self.xaddress)
|
||||||
|
|
||||||
async def monitor_xrpl(self):
|
|
||||||
"""
|
|
||||||
Coroutine to set up XRPL API subscriptions & handle incoming messages,
|
|
||||||
without making the GUI non-responsive while it waits for the network.
|
|
||||||
"""
|
|
||||||
async with xrpl.asyncio.clients.AsyncWebsocketClient(self.url) as self.client:
|
|
||||||
response = await self.client.request(xrpl.models.requests.Subscribe(
|
|
||||||
streams=["ledger"],
|
|
||||||
accounts=[self.classic_address]
|
|
||||||
))
|
|
||||||
# The immediate response contains details for the last validated ledger
|
|
||||||
self.update_ledger(response.result)
|
|
||||||
|
|
||||||
# Get starting values for account info, account transaction history
|
|
||||||
response = await self.client.request(xrpl.models.requests.AccountInfo(
|
|
||||||
account=self.classic_address,
|
|
||||||
ledger_index="validated"
|
|
||||||
))
|
|
||||||
self.update_account(response.result["account_data"])
|
|
||||||
response = await self.client.request(xrpl.models.requests.AccountTx(
|
|
||||||
account=self.classic_address
|
|
||||||
))
|
|
||||||
self.update_account_tx(response.result)
|
|
||||||
|
|
||||||
async for message in self.client:
|
|
||||||
mtype = message.get("type")
|
|
||||||
if mtype == "ledgerClosed":
|
|
||||||
self.update_ledger(message)
|
|
||||||
elif mtype == "transaction":
|
|
||||||
self.add_tx_from_sub(message)
|
|
||||||
response = await self.client.request(xrpl.models.requests.AccountInfo(
|
|
||||||
account=self.classic_address,
|
|
||||||
ledger_index=message["ledger_index"]
|
|
||||||
))
|
|
||||||
self.update_account(response.result["account_data"])
|
|
||||||
|
|
||||||
def update_ledger(self, message):
|
def update_ledger(self, message):
|
||||||
"""
|
"""
|
||||||
@@ -535,7 +629,7 @@ class TWaXLFrame(wx.Frame):
|
|||||||
self.tx_list.PrependItem(cols)
|
self.tx_list.PrependItem(cols)
|
||||||
self.pending_tx_rows[tx_hash] = self.tx_list.RowToItem(0)
|
self.pending_tx_rows[tx_hash] = self.tx_list.RowToItem(0)
|
||||||
|
|
||||||
async def send_xrp(self, event):
|
def click_send_xrp(self, event):
|
||||||
"""
|
"""
|
||||||
Pop up a dialog for the user to input how much XRP to send where, and
|
Pop up a dialog for the user to input how much XRP to send where, and
|
||||||
send the transaction (if the user doesn't cancel).
|
send the transaction (if the user doesn't cancel).
|
||||||
@@ -551,38 +645,41 @@ class TWaXLFrame(wx.Frame):
|
|||||||
return
|
return
|
||||||
|
|
||||||
paydata = dlg.GetPaymentData()
|
paydata = dlg.GetPaymentData()
|
||||||
dtag = paydata.get("dtag")
|
paydata["wallet"] = self.wallet #TODO: is this threadsafe???
|
||||||
if dtag.strip() == "":
|
task = asyncio.run_coroutine_threadsafe(self.worker.send_xrp(paydata), self.worker_loop)
|
||||||
dtag = None
|
task.result()
|
||||||
if dtag is not None:
|
|
||||||
try:
|
|
||||||
dtag = int(dtag)
|
|
||||||
if dtag < 0 or dtag > 2**32-1:
|
|
||||||
raise ValueError("Destination tag must be a 32-bit unsigned integer")
|
|
||||||
except ValueError as e:
|
|
||||||
print("Invalid destination tag:", e)
|
|
||||||
print("Canceled sending payment.")
|
|
||||||
return
|
|
||||||
|
|
||||||
tx = xrpl.models.transactions.Payment(
|
def verify_account_domain(account):
|
||||||
account=self.classic_address,
|
"""
|
||||||
sequence=self.wallet.sequence,
|
Verify an account using an xrp-ledger.toml file.
|
||||||
destination=paydata["to"],
|
|
||||||
amount=xrpl.utils.xrp_to_drops(paydata["amt"]),
|
|
||||||
destination_tag=dtag
|
|
||||||
)
|
|
||||||
tx_signed = await xrpl.asyncio.transaction.safe_sign_and_autofill_transaction(tx, self.wallet, self.client)
|
|
||||||
self.add_pending_tx(tx_signed)
|
|
||||||
await xrpl.asyncio.transaction.submit_transaction(tx_signed, self.client)
|
|
||||||
|
|
||||||
|
Params:
|
||||||
|
account:dict - the AccountRoot object to verify
|
||||||
|
Returns (domain:str, verified:bool)
|
||||||
|
"""
|
||||||
|
domain_hex = account.get("Domain")
|
||||||
|
if not domain_hex:
|
||||||
|
return "", False
|
||||||
|
verified = False
|
||||||
|
domain = xrpl.utils.hex_to_str(domain_hex)
|
||||||
|
toml_url = f"https://{domain}/.well-known/xrp-ledger.toml"
|
||||||
|
toml_response = requests.get(toml_url)
|
||||||
|
if toml_response.ok:
|
||||||
|
parsed_toml = toml.loads(toml_response.text)
|
||||||
|
toml_accounts = parsed_toml.get("ACCOUNTS", [])
|
||||||
|
for t_a in toml_accounts:
|
||||||
|
if t_a.get("address") == account.get("Account"):
|
||||||
|
verified = True
|
||||||
|
break
|
||||||
|
return domain, verified
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
#JSON_RPC_URL = "https://s.altnet.rippletest.net:51234/"
|
#JSON_RPC_URL = "https://s.altnet.rippletest.net:51234/"
|
||||||
#JSON_RPC_URL = "http://localhost:5005/"
|
#JSON_RPC_URL = "http://localhost:5005/"
|
||||||
WS_URL = "wss://s.altnet.rippletest.net:51233"
|
#WS_URL = "wss://s.altnet.rippletest.net:51233"
|
||||||
|
WS_URL= "wss://xrplcluster.com"
|
||||||
|
|
||||||
app = WxAsyncApp()
|
app = wx.App()
|
||||||
frame = TWaXLFrame(WS_URL)
|
frame = TWaXLFrame(WS_URL, test_network=False)
|
||||||
frame.Show()
|
frame.Show()
|
||||||
loop = asyncio.events.get_event_loop()
|
app.MainLoop()
|
||||||
loop.run_until_complete(app.MainLoop())
|
|
||||||
|
|||||||
40
content/_code-samples/build-a-wallet/py/x_verify_domain.py
Normal file
40
content/_code-samples/build-a-wallet/py/x_verify_domain.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
import requests
|
||||||
|
import toml
|
||||||
|
import xrpl
|
||||||
|
|
||||||
|
def verify_account_domain(account):
|
||||||
|
"""
|
||||||
|
Verify an account using an xrp-ledger.toml file.
|
||||||
|
|
||||||
|
Params:
|
||||||
|
account:dict - the AccountRoot object to verify
|
||||||
|
Returns (domain:str, verified:bool)
|
||||||
|
"""
|
||||||
|
domain_hex = account.get("Domain")
|
||||||
|
if not domain_hex:
|
||||||
|
return "", False
|
||||||
|
verified = False
|
||||||
|
domain = xrpl.utils.hex_to_str(domain_hex)
|
||||||
|
toml_url = f"https://{domain}/.well-known/xrp-ledger.toml"
|
||||||
|
toml_response = requests.get(toml_url)
|
||||||
|
if toml_response.ok:
|
||||||
|
parsed_toml = toml.loads(toml_response.text)
|
||||||
|
toml_accounts = parsed_toml.get("ACCOUNTS", [])
|
||||||
|
for t_a in toml_accounts:
|
||||||
|
if t_a.get("address") == account.get("Account"):
|
||||||
|
verified = True
|
||||||
|
break
|
||||||
|
return domain, verified
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
parser = ArgumentParser()
|
||||||
|
parser.add_argument("address", type=str,
|
||||||
|
help="Classic address to check domain verification of")
|
||||||
|
args = parser.parse_args()
|
||||||
|
client = xrpl.clients.JsonRpcClient("https://xrplcluster.com")
|
||||||
|
r = xrpl.account.get_account_info(args.address, client,
|
||||||
|
ledger_index="validated")
|
||||||
|
print(verify_account_domain(r.result["account_data"]))
|
||||||
Reference in New Issue
Block a user