-
Notifications
You must be signed in to change notification settings - Fork 31
Description
Issue: Unauthorized Error during Local Testing with CCIPLocalSimulator in CrossChainNameService Project
Description:
When I clone the smartcontractkit/ccip-cross-chain-name-service
repository: https://github.com/smartcontractkit/ccip-cross-chain-name-service into VSCode and follow the documentation Hardhat and Foundry Integration, I install Foundry in the Hardhat project using:
npm install --save-dev @nomicfoundation/hardhat-foundry
I wanted to use CCIPLocalSimulator
for local testing of the CrossChainNameService project. After running:
forge install smartcontractkit/chainlink-local --no-commit
I wrote the following test code:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {CrossChainNameServiceLookup} from "contracts/CrossChainNameServiceLookup.sol";
import {CrossChainNameServiceReceiver} from "contracts/CrossChainNameServiceReceiver.sol";
import {CrossChainNameServiceRegister} from "contracts/CrossChainNameServiceRegister.sol";
import {Test, console} from "lib/forge-std/src/Test.sol";
import {CCIPLocalSimulator} from "lib/chainlink-local/src/ccip/CCIPLocalSimulator.sol";
import {
IRouterClient, WETH9, LinkToken, BurnMintERC677Helper
} from "lib/chainlink-local/src/ccip/CCIPLocalSimulator.sol";
contract CrossChainNameServiceTest is Test {
CCIPLocalSimulator public ccipLocalSimulator;
CrossChainNameServiceRegister public ccnsRegister;
CrossChainNameServiceReceiver public ccnsReveiver;
CrossChainNameServiceLookup public ccnsLookup;
uint256 public constant GAS_LIMIT = 200000;
uint256 public constant REQUEST_LINK_AMOUT = 1 ether;
address public Alice = makeAddr("Alice");
uint64 public chainSelector;
IRouterClient public sourceRouter;
IRouterClient public destinationRouter;
WETH9 public wrappedNative;
LinkToken public linkToken;
BurnMintERC677Helper public ccipBnM;
BurnMintERC677Helper public ccipLnM;
function setUp() public {
ccipLocalSimulator = new CCIPLocalSimulator();
(chainSelector, sourceRouter, destinationRouter, wrappedNative, linkToken, ccipBnM, ccipLnM) =
ccipLocalSimulator.configuration();
ccnsLookup = new CrossChainNameServiceLookup();
ccnsRegister = new CrossChainNameServiceRegister(address(sourceRouter), address(ccnsLookup));
ccnsReveiver = new CrossChainNameServiceReceiver(address(destinationRouter), address(ccnsLookup), chainSelector);
ccnsLookup.setCrossChainNameServiceAddress(address(ccnsReveiver));
console.log("Authorized address set to:", address(ccnsReveiver));
}
function testCCNSSuccess() public {
// Arrange
ccnsRegister.enableChain(chainSelector, address(ccnsReveiver), GAS_LIMIT);
ccipLocalSimulator.requestLinkFromFaucet(address(ccnsRegister), REQUEST_LINK_AMOUT);
// ACT
vm.prank(Alice);
ccnsRegister.register("alice.ccns");
address expectAddress = ccnsLookup.lookup("alice.ccns");
// Assert
assert(expectAddress == Alice);
}
}
Problem:
While using CCIPLocalSimulator
for local testing, it seems that both sending and receiving occur on the same chain, simulating cross-chain behavior. When the following part of the test is executed:
vm.prank(Alice);
ccnsRegister.register("alice.ccns");
An issue arises.
The CrossChainNameServiceLookup
contract's register
function has a modifier onlyCrossChainNameService
, and we need to set:
ccnsLookup.setCrossChainNameServiceAddress(address(ccnsReveiver));
However, during the execution of ccnsRegister.register("alice.ccns");
, the CrossChainNameServiceRegister
contract first calls the MockCCIPRouter
to send the message. The CrossChainNameServiceReceiver
contract’s ccipReceive
function receives the message. The CrossChainNameServiceReceiver
then calls the CrossChainNameServiceLookup
contract's register
function to map the domain name and user address to the destination chain.
Once the registration is completed on all destination chains, it tries to call i_lookup.register(_name, msg.sender)
on the source chain. At this point, the CrossChainNameServiceAddress
is still set to CrossChainNameServiceReceiver (ccnsReveiver)
, not CrossChainNameServiceRegister (ccnsRegister)
, causing the function to revert with an Unauthorized()
error.
Addition:
If I remove the onlyCrossChainNameService
modifier from the CrossChainNameServiceLookup
contract’s register
function and run the test again, it throws a [Revert] AlreadyTaken()
error, proving that the issue exists.
Expected Solution:
Cross-chain registration should be split into two functions: one for source chain registration and another for handling cross-chain registration. Alternatively, the authorization mechanism should be adjusted to accommodate the multi-chain registration process.