Skip to content

Commit

Permalink
feat: update resolver logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Alejandro-Morales committed Oct 4, 2023
1 parent 83418ca commit faa7577
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 76 deletions.
4 changes: 2 additions & 2 deletions python/examples/06-send-tokens/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class TransactionInfo(Model):
@alice.on_interval(period=10.0)
async def request_funds(ctx: Context):
await ctx.send(
bob.identifier,
bob.address,
PaymentRequest(
wallet_address=str(ctx.wallet.address()), amount=AMOUNT, denom=DENOM
),
Expand Down Expand Up @@ -56,7 +56,7 @@ async def send_payment(ctx: Context, sender: str, msg: PaymentRequest):
)

# send the tx hash so alice can confirm
await ctx.send(alice.identifier, TransactionInfo(tx_hash=transaction.tx_hash))
await ctx.send(alice.address, TransactionInfo(tx_hash=transaction.tx_hash))


bureau = Bureau()
Expand Down
4 changes: 2 additions & 2 deletions python/examples/07-msg-verification/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async def send_message(ctx: Context):
msg = "Hello there bob."
digest = encode(msg)
await ctx.send(
bob.identifier,
bob.address,
Message(message=msg, digest=digest.hex(), signature=alice.sign_digest(digest)),
)

Expand Down Expand Up @@ -53,7 +53,7 @@ async def bob_rx_message(ctx: Context, sender: str, msg: Message):

# send the response
await ctx.send(
alice.identifier,
alice.address,
Message(message=msg, digest=digest.hex(), signature=bob.sign_digest(digest)),
)

Expand Down
4 changes: 3 additions & 1 deletion python/examples/09-booking-protocol-demo/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
from uagents.setup import fund_agent_if_low


RESTAURANT_ADDRESS = "agent1qfpqn9jhvp9cg33f27q6jvmuv52dgyg9rfuu37rmxrletlqe7lewwjed5gy"
RESTAURANT_ADDRESS = (
"test-agent://agent1qfpqn9jhvp9cg33f27q6jvmuv52dgyg9rfuu37rmxrletlqe7lewwjed5gy"
)

user = Agent(
name="user",
Expand Down
4 changes: 3 additions & 1 deletion python/examples/10-cleaning-demo/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
from uagents.setup import fund_agent_if_low


CLEANER_ADDRESS = "agent1qdfdx6952trs028fxyug7elgcktam9f896ays6u9art4uaf75hwy2j9m87w"
CLEANER_ADDRESS = (
"test-agent://agent1qdfdx6952trs028fxyug7elgcktam9f896ays6u9art4uaf75hwy2j9m87w"
)

user = Agent(
name="user",
Expand Down
4 changes: 2 additions & 2 deletions python/src/uagents/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
AGENT_PREFIX = "agent"
LEDGER_PREFIX = "fetch"
USER_PREFIX = "user"
TESTNET_PREFIX = "test-agent://"
MAINNET_PREFIX = "agent://"
TESTNET_PREFIX = "test-agent"
MAINNET_PREFIX = "agent"

CONTRACT_ALMANAC = "fetch1tjagw8g8nn4cwuw00cf0m5tl4l6wfw9c0ue507fhx9e3yrsck8zs0l3q4w"
TESTNET_CONTRACT_ALMANAC = (
Expand Down
4 changes: 2 additions & 2 deletions python/src/uagents/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from uagents.dispatch import JsonStr, dispatcher
from uagents.envelope import Envelope
from uagents.models import ErrorMessage, Model
from uagents.resolver import Resolver, extract_agent_address
from uagents.resolver import Resolver, split_destination
from uagents.storage import KeyValueStore


Expand Down Expand Up @@ -425,7 +425,7 @@ async def send_raw(
)

# Destination without ledger prefix
destination_address = extract_agent_address(destination)
_, destination_address = split_destination(destination)

if destination_address is not None:
# Handle local dispatch of messages
Expand Down
109 changes: 59 additions & 50 deletions python/src/uagents/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,55 @@
)
from uagents.network import get_almanac_contract, get_name_service_contract

# pylint: disable=arguments-differ

def is_valid_address(address: str) -> bool:
"""
Check if the given string is a valid address.
Args:
address (str): The address to be checked.
def extract_agent_address(destination: str) -> str:
Returns:
bool: True if the address is valid; False otherwise.
"""
Extract the agent address from the provided destination.
return len(address) == 65 and address.startswith(AGENT_PREFIX)


def is_valid_prefix(prefix: str) -> bool:
"""
Check if the given string is a valid prefix.
Args:
destination (str): The destination address to check and extract.
prefix (str): The prefix to be checked.
Returns:
str: The extracted agent address if valid, or None if not valid.
bool: True if the prefix is valid; False otherwise.
"""
valid_prefixes = [TESTNET_PREFIX, MAINNET_PREFIX, ""]
return prefix in valid_prefixes

address = destination.split("://")[-1].split("/")[-1]
expected_length = 65

if len(address) == expected_length and address.startswith(AGENT_PREFIX):
return address
def split_destination(destination: str) -> tuple:
"""
Split a destination string into prefix and remainder.
return None
If the destination contains "://", it splits the prefix and remainder based on that.
If not, it assumes an empty prefix and returns the entire destination as the remainder.
Args:
destination (str): The destination string to be split.
Returns:
tuple: A tuple containing the prefix and remainder as strings.
"""

if "://" in destination:
prefix, remainder = destination.split("://", 1)
remainder = remainder.split("/", 1)[-1]
else:
return "", destination

return prefix, remainder


def query_record(agent_address: str, service: str, test: bool) -> dict:
Expand Down Expand Up @@ -73,29 +101,6 @@ def get_agent_address(name: str, test: bool) -> str:
return None


def is_agent_address(address) -> tuple:
"""
Check if the provided address is a valid agent address.
Args:
address: The address to check.
Returns:
bool: True if the address is a valid agent address, False otherwise.
"""
if not isinstance(address, str):
return False

prefixes = [TESTNET_PREFIX, MAINNET_PREFIX, ""]
expected_length = 65

for prefix in prefixes:
if address.startswith(prefix) and len(address) == expected_length + len(prefix):
return (True, prefix)

return (False, None)


class Resolver(ABC):
@abstractmethod
# pylint: disable=unnecessary-pass
Expand Down Expand Up @@ -136,14 +141,18 @@ async def resolve(self, destination: str) -> Tuple[Optional[str], List[str]]:
Returns:
Tuple[Optional[str], List[str]]: The address (if available) and resolved endpoints.
"""
is_address, prefix = is_agent_address(destination)
if is_address:
return await self._almanc_resolver.resolve(
destination[len(prefix) :], not prefix == MAINNET_PREFIX

prefix, remainder = split_destination(destination)

if is_valid_prefix(prefix):
resolver = (
self._almanc_resolver
if is_valid_address(remainder)
else self._name_service_resolver
)
return await self._name_service_resolver.resolve(
destination, not prefix == MAINNET_PREFIX
)
return await resolver.resolve(destination)

return None, []


class AlmanacResolver(Resolver):
Expand All @@ -156,9 +165,7 @@ def __init__(self, max_endpoints: Optional[int] = None):
"""
self._max_endpoints = max_endpoints or DEFAULT_MAX_ENDPOINTS

async def resolve(
self, destination: str, test: bool
) -> Tuple[Optional[str], List[str]]:
async def resolve(self, destination: str) -> Tuple[Optional[str], List[str]]:
"""
Resolve the destination using the Almanac contract.
Expand All @@ -168,7 +175,9 @@ async def resolve(
Returns:
Tuple[str, List[str]]: The address and resolved endpoints.
"""
result = query_record(destination, "service", test)
prefix, address = split_destination(destination)
is_testnet = prefix != MAINNET_PREFIX
result = query_record(address, "service", is_testnet)
if result is not None:
record = result.get("record") or {}
endpoint_list = (
Expand All @@ -178,7 +187,7 @@ async def resolve(
if len(endpoint_list) > 0:
endpoints = [val.get("url") for val in endpoint_list]
weights = [val.get("weight") for val in endpoint_list]
return destination, random.choices(
return address, random.choices(
endpoints,
weights=weights,
k=min(self._max_endpoints, len(endpoints)),
Expand All @@ -198,9 +207,7 @@ def __init__(self, max_endpoints: Optional[int] = None):
self._max_endpoints = max_endpoints or DEFAULT_MAX_ENDPOINTS
self._almanac_resolver = AlmanacResolver(max_endpoints=self._max_endpoints)

async def resolve(
self, destination: str, test: bool
) -> Tuple[Optional[str], List[str]]:
async def resolve(self, destination: str) -> Tuple[Optional[str], List[str]]:
"""
Resolve the destination using the NameService contract.
Expand All @@ -210,9 +217,11 @@ async def resolve(
Returns:
Tuple[Optional[str], List[str]]: The address (if available) and resolved endpoints.
"""
address = get_agent_address(destination, test)
prefix, name = split_destination(destination)
is_testnet = prefix != MAINNET_PREFIX
address = get_agent_address(name, is_testnet)
if address is not None:
return await self._almanac_resolver.resolve(address, test)
return await self._almanac_resolver.resolve(address)
return None, []


Expand Down
31 changes: 15 additions & 16 deletions python/tests/test_agent_address.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from uagents import Agent
from uagents.crypto import Identity
from uagents.resolver import extract_agent_address
from uagents.resolver import split_destination, is_valid_address, is_valid_prefix


class TestAgentAdress(unittest.TestCase):
Expand Down Expand Up @@ -31,30 +31,29 @@ def test_agent_generate(self):
def test_extract_valid_address(self):
valid_addresses = [
"agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
"some-prefix://agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
"other-prefix://agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
"prefix://name/agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
"agent_name/agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
"test-agent://agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
"agent://agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
"test-agent://name/agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
]

for val in valid_addresses:
self.assertEqual(
extract_agent_address(val)
== "agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
True,
)
prefix, address = split_destination(val)
self.assertEqual(is_valid_address(address),True,)
self.assertEqual(is_valid_prefix(prefix),True,)

def test_extract_invalid_address(self):
invalid_addresses = [
"other1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
"some-prefix:agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
"other-prefix://agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skes",
"some-prefix://agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess/name",
"some-prefix::agent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
"p://other1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
"prefix://myagent1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skes",
"other-prefix://address1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skes",
"some-prefix://name/alice1qfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess/name",
"some-prefix://bobqfl32tdwlyjatc7f9tjng6sm9y7yzapy6awx4h9rrwenputzmnv5g6skess",
]

for val in invalid_addresses:
self.assertEqual(extract_agent_address(val) is None, True)
prefix, address = split_destination(val)
self.assertEqual(is_valid_address(address), False)
self.assertEqual(is_valid_prefix(prefix), False)


if __name__ == "__main__":
Expand Down

0 comments on commit faa7577

Please sign in to comment.