From 73e751ebd43422a8a0c1f4958d18a9bac1c5384b Mon Sep 17 00:00:00 2001 From: Alejandro-Morales <77800944+Alejandro-Morales@users.noreply.github.com> Date: Fri, 23 Jun 2023 07:12:53 -0600 Subject: [PATCH] feat: integrated name service contract (#77) --- .../08-local-network-interaction/agent1.py | 11 +- .../08-local-network-interaction/agent2.py | 4 +- src/uagents/agent.py | 154 +++++++++++------- src/uagents/config.py | 6 +- src/uagents/context.py | 6 +- src/uagents/network.py | 138 +++++++++++++++- src/uagents/query.py | 8 +- src/uagents/resolver.py | 62 +++++-- tests/test_agent.py | 4 +- tests/test_agent_registration.py | 151 +++++++++++++---- 10 files changed, 423 insertions(+), 121 deletions(-) diff --git a/examples/08-local-network-interaction/agent1.py b/examples/08-local-network-interaction/agent1.py index 157cb34f..2dfef5c9 100644 --- a/examples/08-local-network-interaction/agent1.py +++ b/examples/08-local-network-interaction/agent1.py @@ -1,4 +1,5 @@ from uagents.setup import fund_agent_if_low +from uagents.resolver import get_agent_address from uagents import Agent, Context, Model @@ -10,15 +11,21 @@ class Message(Model): bob = Agent( - name="bob", + name="agent bob", port=8001, - seed="bob secret phrase", + seed="agent bob secret phrase", endpoint=["http://127.0.0.1:8001/submit"], ) fund_agent_if_low(bob.wallet.address()) +@bob.on_event("startup") +async def register_name(ctx: Context): + await bob.register_name() + print("agent bob registered address: ", get_agent_address(ctx.name)) + + @bob.on_message(model=Message) async def message_handler(ctx: Context, sender: str, msg: Message): ctx.logger.info(f"Received message from {sender}: {msg.message}") diff --git a/examples/08-local-network-interaction/agent2.py b/examples/08-local-network-interaction/agent2.py index 149bb27b..b7f251b3 100644 --- a/examples/08-local-network-interaction/agent2.py +++ b/examples/08-local-network-interaction/agent2.py @@ -6,8 +6,6 @@ class Message(Model): message: str -RECIPIENT_ADDRESS = "agent1q2kxet3vh0scsf0sm7y2erzz33cve6tv5uk63x64upw5g68kr0chkv7hw50" - alice = Agent( name="alice", port=8000, @@ -20,7 +18,7 @@ class Message(Model): @alice.on_interval(period=2.0) async def send_message(ctx: Context): - await ctx.send(RECIPIENT_ADDRESS, Message(message="Hello there bob.")) + await ctx.send("agent bob", Message(message="Hello there bob.")) @alice.on_message(model=Message) diff --git a/src/uagents/agent.py b/src/uagents/agent.py index 647b0a6c..c558d1ae 100644 --- a/src/uagents/agent.py +++ b/src/uagents/agent.py @@ -5,6 +5,10 @@ from cosmpy.aerial.wallet import LocalWallet, PrivateKey from cosmpy.crypto.address import Address +from cosmpy.aerial.contract.cosmwasm import create_cosmwasm_execute_msg +from cosmpy.aerial.client import prepare_and_broadcast_basic_transaction +from cosmpy.aerial.tx import Transaction + from uagents.asgi import ASGIServer from uagents.context import ( @@ -18,15 +22,21 @@ from uagents.dispatch import Sink, dispatcher, JsonStr from uagents.models import Model, ErrorMessage from uagents.protocol import Protocol -from uagents.resolver import Resolver, AlmanacResolver +from uagents.resolver import Resolver, GlobalResolver from uagents.storage import KeyValueStore, get_or_create_private_keys -from uagents.network import get_ledger, get_reg_contract, wait_for_tx_to_complete +from uagents.network import ( + get_ledger, + get_almanac_contract, + get_service_contract, + wait_for_tx_to_complete, +) from uagents.mailbox import MailboxClient from uagents.config import ( + CONTRACT_ALMANAC, REGISTRATION_FEE, REGISTRATION_DENOM, + MIN_REGISTRATION_TIME, LEDGER_PREFIX, - BLOCK_INTERVAL, parse_endpoint_config, parse_mailbox_config, get_logger, @@ -63,26 +73,11 @@ def __init__( self._name = name self._port = port if port is not None else 8000 self._background_tasks: Set[asyncio.Task] = set() - self._resolver = resolve if resolve is not None else AlmanacResolver() + self._resolver = resolve if resolve is not None else GlobalResolver() self._loop = asyncio.get_event_loop_policy().get_event_loop() # initialize wallet and identity - if seed is None: - if name is None: - self._wallet = LocalWallet.generate() - self._identity = Identity.generate() - else: - identity_key, wallet_key = get_or_create_private_keys(name) - self._wallet = LocalWallet(PrivateKey(wallet_key)) - self._identity = Identity.from_string(identity_key) - else: - self._identity = Identity.from_seed(seed, 0) - self._wallet = LocalWallet( - PrivateKey(derive_key_from_seed(seed, LEDGER_PREFIX, 0)), - prefix=LEDGER_PREFIX, - ) - if name is None: - self._name = self.address[0:16] + self._initialize_wallet_and_identity(seed, name) self._logger = get_logger(self.name) # configure endpoints and mailbox @@ -103,7 +98,8 @@ def __init__( self._mailbox_client = None self._ledger = get_ledger() - self._reg_contract = get_reg_contract() + self._almanac_contract = get_almanac_contract() + self._service_contract = get_service_contract() self._storage = KeyValueStore(self.address[0:16]) self._interval_handlers: List[Tuple[IntervalCallback, float]] = [] self._interval_messages: Set[str] = set() @@ -147,6 +143,24 @@ def __init__( self._port, self._loop, self._queries, logger=self._logger ) + def _initialize_wallet_and_identity(self, seed, name): + if seed is None: + if name is None: + self._wallet = LocalWallet.generate() + self._identity = Identity.generate() + else: + identity_key, wallet_key = get_or_create_private_keys(name) + self._wallet = LocalWallet(PrivateKey(wallet_key)) + self._identity = Identity.from_string(identity_key) + else: + self._identity = Identity.from_seed(seed, 0) + self._wallet = LocalWallet( + PrivateKey(derive_key_from_seed(seed, LEDGER_PREFIX, 0)), + prefix=LEDGER_PREFIX, + ) + if name is None: + self._name = self.address[0:16] + @property def name(self) -> str: return self._name @@ -182,9 +196,10 @@ def sign_digest(self, digest: bytes) -> str: return self._identity.sign_digest(digest) def sign_registration(self) -> str: - assert self._reg_contract.address is not None + assert self._almanac_contract.address is not None return self._identity.sign_registration( - str(self._reg_contract.address), self.get_registration_sequence() + str(self._almanac_contract.address), + self._almanac_contract.get_sequence(self.address), ) def update_endpoints(self, endpoints: List[Dict[str, Any]]): @@ -208,50 +223,56 @@ async def _register(self, ctx: Context): signature = self.sign_registration() - msg = { - "register": { - "record": { - "service": { - "protocols": list( - map(lambda x: x.digest, self.protocols.values()) - ), - "endpoints": self._endpoints, - } - }, - "signature": signature, - "sequence": self.get_registration_sequence(), - "agent_address": self.address, - } - } - - self._logger.info("Registering on Almanac contract...") - transaction = self._reg_contract.execute( - msg, - ctx.wallet, - funds=f"{REGISTRATION_FEE}{REGISTRATION_DENOM}", + self._logger.info("Registering on almanac contract...") + + transaction = Transaction() + + almanac_msg = self._almanac_contract.get_registration_msg( + self.protocols, self._endpoints, signature, self.address + ) + + transaction.add_message( + create_cosmwasm_execute_msg( + ctx.wallet.address(), + CONTRACT_ALMANAC, + almanac_msg, + funds=f"{REGISTRATION_FEE}{REGISTRATION_DENOM}", + ) + ) + + transaction = prepare_and_broadcast_basic_transaction( + ctx.ledger, transaction, ctx.wallet ) await wait_for_tx_to_complete(transaction.tx_hash) - self._logger.info("Registering on Almanac contract...complete") + self._logger.info("Registering on almanac contract...complete") def _schedule_registration(self): - query_msg = {"query_records": {"agent_address": self.address}} - response = self._reg_contract.query(query_msg) + return self._almanac_contract.get_expiry(self.address) - if not response["record"]: - contract_state = self._reg_contract.query({"query_contract_state": {}}) - expiry = contract_state.get("state").get("expiry_height") - return expiry * BLOCK_INTERVAL + async def register_name(self): + self._logger.info("Registering name...") - expiry = response.get("record")[0].get("expiry") - height = response.get("height") + if not self._almanac_contract.is_registered(self.address): + self._logger.warning( + f"Agent {self.name} needs to be registered in almanac contract to register its name" + ) + return - return (expiry - height) * BLOCK_INTERVAL + transaction = self._service_contract.get_registration_tx( + self.name, str(self.wallet.address()), self.address + ) - def get_registration_sequence(self) -> int: - query_msg = {"query_sequence": {"agent_address": self.address}} - sequence = self._reg_contract.query(query_msg)["sequence"] + if transaction is None: + self._logger.error( + f"Please select another name, {self.name} is owned by another address" + ) - return sequence + return + transaction = prepare_and_broadcast_basic_transaction( + self._ledger, transaction, self.wallet + ) + await wait_for_tx_to_complete(transaction.tx_hash) + self._logger.info("Registering name...complete") def on_interval( self, @@ -364,8 +385,21 @@ def start_background_tasks(self): # start the contract registration update loop if self._endpoints is not None: - self._loop.create_task( - _run_interval(self._register, self._ctx, self._schedule_registration()) + if ( + not self._almanac_contract.is_registered(self.address) + or self._schedule_registration() < MIN_REGISTRATION_TIME + or self._endpoints != self._almanac_contract.get_endpoints(self.address) + ): + self._loop.create_task( + _run_interval( + self._register, self._ctx, self._schedule_registration() + ) + ) + else: + self._logger.info("Registration up to date!") + else: + self._logger.warning( + "I have no endpoint and won't be able to receive external messages" ) def run(self): diff --git a/src/uagents/config.py b/src/uagents/config.py index 84ec877f..e46923b2 100644 --- a/src/uagents/config.py +++ b/src/uagents/config.py @@ -16,9 +16,13 @@ class AgentNetwork(Enum): AGENT_PREFIX = "agent" LEDGER_PREFIX = "fetch" USER_PREFIX = "user" -CONTRACT_ALMANAC = "fetch1tjagw8g8nn4cwuw00cf0m5tl4l6wfw9c0ue507fhx9e3yrsck8zs0l3q4w" +CONTRACT_ALMANAC = "fetch1h5rhtj5m6dqjmufj5m3t4mq6l7cnd8dvaxclwmrk6tfdm0gy3lmszksf0s" +CONTRACT_NAME_SERVICE = ( + "fetch1yrf4xpglq02fzj50m9wn44qdq89a5vr0ufa42qa506uhwal4n79s99sp87" +) REGISTRATION_FEE = 500000000000000000 REGISTRATION_DENOM = "atestfet" +MIN_REGISTRATION_TIME = 3600 BLOCK_INTERVAL = 5 AGENT_NETWORK = AgentNetwork.FETCHAI_TESTNET diff --git a/src/uagents/context.py b/src/uagents/context.py index f1970084..b88f779b 100644 --- a/src/uagents/context.py +++ b/src/uagents/context.py @@ -158,7 +158,7 @@ async def send_raw( return # resolve the endpoint - endpoint = await self._resolver.resolve(destination) + destination_address, endpoint = await self._resolver.resolve(destination) if endpoint is None: self._logger.exception( f"Unable to resolve destination endpoint for address {destination}" @@ -172,7 +172,7 @@ async def send_raw( env = Envelope( version=1, sender=self.address, - target=destination, + target=destination_address, session=self._session, schema_digest=schema_digest, protocol_digest=self.get_message_protocol(schema_digest), @@ -189,5 +189,5 @@ async def send_raw( if not success: self._logger.exception( - f"Unable to send envelope to {destination} @ {endpoint}" + f"Unable to send envelope to {destination_address} @ {endpoint}" ) diff --git a/src/uagents/network.py b/src/uagents/network.py index df9dfa99..b12ea86e 100644 --- a/src/uagents/network.py +++ b/src/uagents/network.py @@ -1,6 +1,6 @@ import asyncio from datetime import datetime, timedelta -from typing import Optional +from typing import Optional, Dict, List, Union from cosmpy.aerial.contract import LedgerContract from cosmpy.aerial.client import ( @@ -10,10 +10,132 @@ DEFAULT_QUERY_TIMEOUT_SECS, ) from cosmpy.aerial.exceptions import NotFoundError, QueryTimeoutError +from cosmpy.aerial.contract.cosmwasm import create_cosmwasm_execute_msg from cosmpy.aerial.faucet import FaucetApi from cosmpy.aerial.tx_helpers import TxResponse +from cosmpy.aerial.tx import Transaction -from uagents.config import AgentNetwork, CONTRACT_ALMANAC, AGENT_NETWORK +from uagents.config import ( + AgentNetwork, + CONTRACT_ALMANAC, + CONTRACT_NAME_SERVICE, + AGENT_NETWORK, + BLOCK_INTERVAL, +) + + +class AlmanacContract(LedgerContract): + def is_registered(self, address: str) -> bool: + query_msg = {"query_records": {"agent_address": address}} + response = self.query(query_msg) + + if not response["record"]: + return False + return True + + def get_expiry(self, address: str): + query_msg = {"query_records": {"agent_address": address}} + response = self.query(query_msg) + + if not response["record"]: + contract_state = self.query({"query_contract_state": {}}) + expiry = contract_state.get("state").get("expiry_height") + return expiry * BLOCK_INTERVAL + + expiry = response.get("record")[0].get("expiry") + height = response.get("height") + + return (expiry - height) * BLOCK_INTERVAL + + def get_endpoints(self, address: str): + query_msg = {"query_records": {"agent_address": address}} + response = self.query(query_msg) + + if not response["record"]: + return 0 + return response.get("record")[0]["record"]["service"]["endpoints"] + + def get_registration_msg( + self, + protocols: Dict, + endpoints: Optional[Union[List[str], Dict[str, dict]]], + signature: str, + address: str, + ) -> dict: + return { + "register": { + "record": { + "service": { + "protocols": list(map(lambda x: x.digest, protocols.values())), + "endpoints": endpoints, + } + }, + "signature": signature, + "sequence": self.get_sequence(address), + "agent_address": address, + } + } + + def get_sequence(self, address: str) -> int: + query_msg = {"query_sequence": {"agent_address": address}} + sequence = self.query(query_msg)["sequence"] + + return sequence + + +class NameServiceContract(LedgerContract): + def is_name_available(self, name: str): + query_msg = {"domain_record": {"domain": f"{name}.agent"}} + return self.query(query_msg)["is_available"] + + def is_owner(self, name: str, wallet_address: str): + query_msg = { + "permissions": { + "domain": f"{name}.agent", + "owner": {"address": {"address": wallet_address}}, + } + } + permission = self.query(query_msg)["permissions"] + return permission == "admin" + + def _get_ownership_msg(self, name: str, wallet_address: str): + return { + "update_ownership": { + "domain": f"{name}.agent", + "owner": {"address": {"address": wallet_address}}, + "permissions": "admin", + } + } + + def _get_registration_msg(self, name: str, address: str): + return { + "register": { + "domain": f"{name}.agent", + "agent_address": address, + } + } + + def get_registration_tx(self, name: str, wallet_address: str, agent_address: str): + if not self.is_name_available(name) and not self.is_owner(name, wallet_address): + return None + + transaction = Transaction() + + ownership_msg = self._get_ownership_msg(name, wallet_address) + registration_msg = self._get_registration_msg(name, agent_address) + + transaction.add_message( + create_cosmwasm_execute_msg( + wallet_address, CONTRACT_NAME_SERVICE, ownership_msg + ) + ) + transaction.add_message( + create_cosmwasm_execute_msg( + wallet_address, CONTRACT_NAME_SERVICE, registration_msg + ) + ) + + return transaction if AGENT_NETWORK == AgentNetwork.FETCHAI_TESTNET: @@ -24,7 +146,9 @@ else: raise NotImplementedError -_contract = LedgerContract(None, _ledger, CONTRACT_ALMANAC) + +_almanac_contract = AlmanacContract(None, _ledger, CONTRACT_ALMANAC) +_name_service_contract = NameServiceContract(None, _ledger, CONTRACT_NAME_SERVICE) def get_ledger() -> LedgerClient: @@ -35,8 +159,12 @@ def get_faucet() -> FaucetApi: return _faucet_api -def get_reg_contract() -> LedgerContract: - return _contract +def get_almanac_contract() -> LedgerContract: + return _almanac_contract + + +def get_service_contract() -> LedgerContract: + return _name_service_contract async def wait_for_tx_to_complete( diff --git a/src/uagents/query.py b/src/uagents/query.py index 3db43bb4..52cd3cc3 100644 --- a/src/uagents/query.py +++ b/src/uagents/query.py @@ -9,7 +9,7 @@ from uagents.dispatch import JsonStr from uagents.envelope import Envelope from uagents.models import Model -from uagents.resolver import Resolver, AlmanacResolver +from uagents.resolver import Resolver, GlobalResolver LOGGER = get_logger("query") @@ -22,14 +22,14 @@ async def query( timeout: Optional[int] = 30, ) -> Optional[Envelope]: if resolver is None: - resolver = AlmanacResolver() + resolver = GlobalResolver() # convert the message into object form json_message = message.json() schema_digest = Model.build_schema_digest(message) # resolve the endpoint - endpoint = await resolver.resolve(destination) + destination_address, endpoint = await resolver.resolve(destination) if endpoint is None: LOGGER.exception( f"Unable to resolve destination endpoint for address {destination}" @@ -43,7 +43,7 @@ async def query( env = Envelope( version=1, sender=generate_user_address(), - target=destination, + target=destination_address, session=uuid.uuid4(), schema_digest=schema_digest, expires=expires, diff --git a/src/uagents/resolver.py b/src/uagents/resolver.py index 0b481bad..cb1791ec 100644 --- a/src/uagents/resolver.py +++ b/src/uagents/resolver.py @@ -2,11 +2,11 @@ from typing import Dict, Optional import random -from uagents.network import get_reg_contract +from uagents.network import get_almanac_contract, get_service_contract -def _query_record(agent_address: str, service: str) -> dict: - contract = get_reg_contract() +def query_record(agent_address: str, service: str) -> dict: + contract = get_almanac_contract() query_msg = { "query_record": {"agent_address": agent_address, "record_type": service} } @@ -14,15 +14,51 @@ def _query_record(agent_address: str, service: str) -> dict: return result +def get_agent_address(name: str) -> str: + query_msg = {"domain_record": {"domain": f"{name}.agent"}} + result = get_service_contract().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"] + return 0 + return 1 + + +def is_agent_address(address): + if not isinstance(address, str): + return False + + prefix = "agent" + expected_length = 65 + + return address.startswith(prefix) and len(address) == expected_length + + class Resolver(ABC): @abstractmethod - async def resolve(self, address: str) -> Optional[str]: + async def resolve(self, destination: str) -> Optional[str]: pass +class GlobalResolver(Resolver): + async def resolve(self, destination: str) -> Optional[str]: + almanac_resolver = AlmanacResolver() + name_service_resolver = NameServiceResolver() + address = ( + destination + if is_agent_address(destination) + else await name_service_resolver.resolve(destination) + ) + + if is_agent_address(address): + return await almanac_resolver.resolve(address) + return None, None + + class AlmanacResolver(Resolver): - async def resolve(self, address: str) -> Optional[str]: - result = _query_record(address, "service") + async def resolve(self, destination: str) -> Optional[str]: + result = query_record(destination, "service") if result is not None: record = result.get("record") or {} endpoint_list = ( @@ -32,13 +68,19 @@ async def resolve(self, address: str) -> Optional[str]: if len(endpoint_list) > 0: endpoints = [val.get("url") for val in endpoint_list] weights = [val.get("weight") for val in endpoint_list] - return random.choices(endpoints, weights=weights)[0] - return None + return destination, random.choices(endpoints, weights=weights)[0] + + return None, None + + +class NameServiceResolver(Resolver): + async def resolve(self, destination: str) -> Optional[str]: + return get_agent_address(destination) class RulesBasedResolver(Resolver): def __init__(self, rules: Dict[str, str]): self._rules = rules - async def resolve(self, address: str) -> Optional[str]: - return self._rules.get(address) + async def resolve(self, destination: str) -> Optional[str]: + return self._rules.get(destination) diff --git a/tests/test_agent.py b/tests/test_agent.py index ba76f2a6..36ac435a 100644 --- a/tests/test_agent.py +++ b/tests/test_agent.py @@ -3,7 +3,7 @@ from typing import Callable from uagents import Agent, Context, Model -from uagents.resolver import AlmanacResolver +from uagents.resolver import GlobalResolver class Message(Model): @@ -31,7 +31,7 @@ def test_agent_init(self): self.assertIsNotNone(self.agent._identity) self.assertIsNotNone(self.agent._server) self.assertTrue(self.agent._dispatcher.contains(self.agent.address)) - self.assertTrue(isinstance(self.agent._resolver, AlmanacResolver)) + self.assertTrue(isinstance(self.agent._resolver, GlobalResolver)) def test_agent_on_interval(self): @self.agent.on_interval(period=10) diff --git a/tests/test_agent_registration.py b/tests/test_agent_registration.py index 1fe70947..70c36f14 100644 --- a/tests/test_agent_registration.py +++ b/tests/test_agent_registration.py @@ -3,49 +3,138 @@ from uagents import Agent from uagents.setup import fund_agent_if_low +from uagents.resolver import get_agent_address +from uagents.config import ( + REGISTRATION_FEE, + REGISTRATION_DENOM, +) -class TestVerify(unittest.TestCase): - def test_agent_registration(self): - agent = Agent(name="alice") +class TestRegistration(unittest.TestCase): + def test_alamanc_registration(self): + agent = Agent() - reg_fee = "500000000000000000atestfet" + reg_fee = f"{REGISTRATION_FEE}{REGISTRATION_DENOM}" fund_agent_if_low(agent.wallet.address()) - sequence = agent.get_registration_sequence() + sequence = agent._almanac_contract.get_sequence(agent.address) signature = agent._identity.sign_registration( - agent._reg_contract.address, agent.get_registration_sequence() - ) - - msg = { - "register": { - "record": { - "service": { - "protocols": [], - "endpoints": [ - {"url": "http://127.0.0.1:8000/submit", "weight": 1} - ], - } - }, - "signature": signature, - "sequence": sequence, - "agent_address": agent.address, - } - } - - transaction = agent._reg_contract.execute(msg, agent.wallet, funds=reg_fee) + agent._almanac_contract.address, sequence + ) + + almanac_msg = agent._almanac_contract.get_registration_msg( + {}, [], signature, agent.address + ) + + transaction = agent._almanac_contract.execute( + almanac_msg, agent.wallet, funds=reg_fee + ) + transaction.wait_to_complete() + + self.assertEqual( + agent._almanac_contract.is_registered(agent.address), + True, + "Almanac registration failed", + ) + + def test_alamanc_failed_registration(self): + agent = Agent() + + self.assertEqual( + agent._almanac_contract.is_registered(agent.address), + False, + "Shouldn't be registered on alamanac", + ) + + def test_name_service_ownership(self): + agent = Agent() + fund_agent_if_low(agent.wallet.address()) + + ownership_msg = agent._service_contract._get_ownership_msg( + agent.name, str(agent.wallet.address()) + ) + + transaction = agent._service_contract.execute(ownership_msg, agent.wallet) + transaction.wait_to_complete() - query_msg = {"query_records": {"agent_address": agent.address}} - response = agent._reg_contract.query(query_msg) + is_owner = agent._service_contract.is_owner( + agent.name, str(agent.wallet.address()) + ) + + self.assertEqual(is_owner, True, "Domain ownership failed") + + def test_name_service_failed_ownership(self): + agent = Agent() + + is_owner = agent._service_contract.is_owner( + agent.name, str(agent.wallet.address()) + ) + + self.assertEqual(is_owner, False, "Agent shouldn't own any domain") + + def test_registration(self): + agent = Agent() - is_registered = False - if response["record"] != []: - is_registered = True + reg_fee = f"{REGISTRATION_FEE}{REGISTRATION_DENOM}" - self.assertEqual(is_registered, True, "Registration failed") + fund_agent_if_low(agent.wallet.address()) + + sequence = agent._almanac_contract.get_sequence(agent.address) + + signature = agent._identity.sign_registration( + agent._almanac_contract.address, sequence + ) + + almanac_msg = agent._almanac_contract.get_registration_msg( + {}, [], signature, agent.address + ) + + agent._almanac_contract.execute( + almanac_msg, agent.wallet, funds=reg_fee + ).wait_to_complete() + + self.assertEqual( + agent._almanac_contract.is_registered(agent.address), + True, + "Almanac registration failed", + ) + + is_name_available = agent._service_contract.is_name_available(agent.name) + self.assertEqual(is_name_available, True, "Agent name should be available") + + is_owner = agent._service_contract.is_owner( + agent.name, str(agent.wallet.address()) + ) + self.assertEqual(is_owner, False) + + ownership_msg = agent._service_contract._get_ownership_msg( + agent.name, str(agent.wallet.address()) + ) + registration_msg = agent._service_contract._get_registration_msg( + agent.name, agent.address + ) + + agent._service_contract.execute(ownership_msg, agent.wallet).wait_to_complete() + agent._service_contract.execute( + registration_msg, agent.wallet + ).wait_to_complete() + + is_name_available = agent._service_contract.is_name_available(agent.name) + self.assertEqual(is_name_available, False, "Agent name shouldn't be available") + + is_owner = agent._service_contract.is_owner( + agent.name, str(agent.wallet.address()) + ) + self.assertEqual(is_owner, True, "Domain ownership failed") + + query_address = get_agent_address(agent.name) + + self.assertEqual( + query_address == agent.address, True, "Service contract registration failed" + ) if __name__ == "__main__":