Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: updates to match aname contract-V2 #215

Merged
merged 17 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion python/examples/13-agent-name-service/agent1.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Message(Model):
my_wallet = LocalWallet.from_unsafe_seed("registration test wallet")
name_service_contract = get_name_service_contract(test=True)
faucet = get_faucet()
DOMAIN = "agent"
DOMAIN = "example.agent"

faucet.get_wealth(my_wallet.address())

Expand Down
4 changes: 3 additions & 1 deletion python/examples/13-agent-name-service/agent2.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ class Message(Model):
endpoint=["http://localhost:8000/submit"],
)

DOMAIN = "example.agent"


@alice.on_interval(period=5)
async def alice_interval_handler(ctx: Context):
bob_name = "bob-0.agent"
bob_name = "bob-0" + "." + DOMAIN
ctx.logger.info(f"Sending message to {bob_name}...")
await ctx.send(bob_name, Message(message="Hello there bob."))

Expand Down
124 changes: 100 additions & 24 deletions python/src/uagents/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import asyncio
from datetime import datetime, timedelta
from typing import Any, Optional, Dict, List
from typing import Any, Optional, Dict, List, Union

from cosmpy.aerial.contract import LedgerContract
from cosmpy.aerial.client import (
Expand Down Expand Up @@ -80,6 +80,29 @@ def add_testnet_funds(wallet_address: str):
)


def parse_record_config(
record: Optional[Union[str, List[str], Dict[str, dict]]]
) -> List[Dict[str, Any]]:
"""
Parse the user-provided record configuration.

Returns:
List[Dict[str, Any]]: The parsed record configuration in correct format.
"""
if isinstance(record, dict):
records = [
{"address": val[0], "weight": val[1].get("weight") or 1}
for val in record.items()
]
elif isinstance(record, list):
records = [{"address": val, "weight": 1} for val in record]
elif isinstance(record, str):
records = [{"address": record, "weight": 1}]
else:
records = None
return records


async def wait_for_tx_to_complete(
tx_hash: str,
ledger: LedgerClient,
Expand Down Expand Up @@ -345,14 +368,36 @@ def is_domain_public(self, domain: str):
Returns:
bool: True if the domain is public, False otherwise.
"""
res = self.query({"domain_record": {"domain": f".{domain}"}})
return res["is_public"]
res = self.query({"query_domain_flags": {"domain": domain.split(".")[-1]}}).get(
"domain_flags"
)
if res:
return res["web3_flags"]["is_public"]
return False

def get_previous_records(self, name: str, domain: str):
"""
Retrieve the previous records for a given name within a specified domain.

Args:
name (str): The name whose records are to be retrieved.
domain (str): The domain within which the name is registered.

Returns:
A list of dictionaries, where each dictionary contains
details of a record associated with the given name.
"""
query_msg = {"domain_record": {"domain": f"{name}.{domain}"}}
result = self.query(query_msg)
if result["record"] is not None:
return result["record"]["records"][0]["agent_address"]["records"]
return []

def get_registration_tx(
self,
name: str,
wallet_address: str,
agent_address: str,
agent_records: List[Dict[str, Any]],
domain: str,
test: bool,
):
Expand All @@ -370,24 +415,36 @@ def get_registration_tx(
Optional[Transaction]: The registration transaction, or None if the name is not
available or not owned by the wallet address.
"""
if not self.is_name_available(name, domain) and not self.is_owner(
name, domain, wallet_address
):
transaction = Transaction()

contract = (
TESTNET_CONTRACT_NAME_SERVICE if test else MAINNET_CONTRACT_NAME_SERVICE
)

if self.is_name_available(name, domain):
price_per_second = self.query({"contract_state": {}})["price_per_second"]
amount = int(price_per_second["amount"]) * 86400
denom = price_per_second["denom"]

registration_msg = {"register": {"domain": f"{name}.{domain}"}}

transaction.add_message(
create_cosmwasm_execute_msg(
wallet_address, contract, registration_msg, funds=f"{amount}{denom}"
)
)
elif not self.is_owner(name, domain, wallet_address):
return None

registration_msg = {
"register": {
record_msg = {
"update_record": {
"domain": f"{name}.{domain}",
"agent_address": agent_address,
"agent_records": agent_records,
}
}

contract = (
TESTNET_CONTRACT_NAME_SERVICE if test else MAINNET_CONTRACT_NAME_SERVICE
)
transaction = Transaction()
transaction.add_message(
create_cosmwasm_execute_msg(wallet_address, contract, registration_msg)
create_cosmwasm_execute_msg(wallet_address, contract, record_msg)
)

return transaction
Expand All @@ -396,9 +453,10 @@ async def register(
self,
ledger: LedgerClient,
wallet: LocalWallet,
agent_address: str,
agent_records: Optional[Union[str, List[str], Dict[str, dict]]],
name: str,
domain: str,
overwrite: bool = True,
):
"""
Register a name within a domain using the NameService contract.
Expand All @@ -409,28 +467,46 @@ async def register(
agent_address (str): The address of the agent.
name (str): The name to be registered.
domain (str): The domain in which the name is registered.
overwrite (bool, optional): Specifies whether to overwrite any existing
addresses registered to the domain. If False, the address will be
appended to the previous records. Defaults to True.
"""
logger.info("Registering name...")
chain_id = ledger.query_chain_id()

if not get_almanac_contract(chain_id == "dorado-1").is_registered(
agent_address
):
logger.warning(
f"Agent {name} needs to be registered in almanac contract to register its name"
)
return
records = parse_record_config(agent_records)
agent_addresses = [val.get("address") for val in records]

for agent_address in agent_addresses:
if not get_almanac_contract(chain_id == "dorado-1").is_registered(
agent_address
):
logger.warning(
"Address %s needs to be registered in almanac contract "
jrriehl marked this conversation as resolved.
Show resolved Hide resolved
"to be registered in a domain",
agent_address,
)
return

if not self.is_domain_public(domain):
logger.warning(
f"Domain {domain} is not public, please select a public domain"
)
return

if not overwrite:
previous_records = self.get_previous_records(name, domain)
records = list(
{
f"{rec['address']}_{rec['weight']}": rec
for rec in previous_records + records
}.values()
)

transaction = self.get_registration_tx(
name,
str(wallet.address()),
agent_address,
records,
domain,
chain_id == "dorado-1",
)
Expand Down
12 changes: 9 additions & 3 deletions python/src/uagents/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,15 @@ def get_agent_address(name: str, test: bool) -> str:
query_msg = {"domain_record": {"domain": f"{name}"}}
result = get_name_service_contract(test).query(query_msg)
if result["record"] is not None:
registered_address = result["record"]["records"][0]["agent_address"]["records"]
if len(registered_address) > 0:
return registered_address[0]["address"]
registered_records = result["record"]["records"][0]["agent_address"]["records"]
if len(registered_records) > 0:
addresses = [val.get("address") for val in registered_records]
weights = [val.get("weight") for val in registered_records]
selected_address_list = weighted_random_sample(addresses, weights=weights)
selected_address = (
selected_address_list[0] if selected_address_list else None
)
return selected_address
return None


Expand Down
Loading