Skip to content

Commit

Permalink
feat: added Interchain Router (#105)
Browse files Browse the repository at this point in the history
* Added the eternal storage here as well and using it for the InterchainRouter.

* Removed Eternal Storage

* Added a few tests

* Simplified storage and added more tests

* made lint happy.

* Fixed a warnong

* addressed some comments

* made slither happy

* Added string storage library and a test file to make slither happy.

* fix testInterchainRouterProxy

* Added delete in string storage, using it in the interchain router and added tests

* Rename interchain router to interchain address tracker

* changed names of StringStorage functions

* refactor names

* address comments

* bump npm version

* addressed comments

---------

Co-authored-by: Milap Sheth <[email protected]>
  • Loading branch information
Foivos and milapsheth authored Oct 31, 2023
1 parent 6160466 commit b64ba1c
Show file tree
Hide file tree
Showing 11 changed files with 555 additions and 3 deletions.
59 changes: 59 additions & 0 deletions contracts/interfaces/IInterchainAddressTracker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IContractIdentifier } from './IContractIdentifier.sol';

/**
* @title IInterchainAddressTracker
* @dev Manages trusted addresses by chain, keeps track of addresses supported by the Axelar gateway contract
*/
interface IInterchainAddressTracker is IContractIdentifier {
error ZeroAddress();
error LengthMismatch();
error ZeroStringLength();
error UntrustedChain();

event TrustedAddressSet(string chain, string address_);
event TrustedAddressRemoved(string chain);

/**
* @dev Gets the name of the chain this is deployed at
*/
function chainName() external view returns (string memory);

/**
* @dev Gets the trusted address at a remote chain
* @param chain Chain name of the remote chain
* @return trustedAddress_ The trusted address for the chain. Returns '' if the chain is untrusted
*/
function trustedAddress(string memory chain) external view returns (string memory trustedAddress_);

/**
* @dev Gets the trusted address hash for a chain
* @param chain Chain name
* @return trustedAddressHash_ the hash of the trusted address for that chain
*/
function trustedAddressHash(string memory chain) external view returns (bytes32 trustedAddressHash_);

/**
* @dev Checks whether the interchain sender is a trusted address
* @param chain Chain name of the sender
* @param address_ Address of the sender
* @return bool true if the sender chain/address are trusted, false otherwise
*/
function isTrustedAddress(string calldata chain, string calldata address_) external view returns (bool);

/**
* @dev Sets the trusted address for the specified chain
* @param chain Chain name to be trusted
* @param address_ Trusted address to be added for the chain
*/
function setTrustedAddress(string memory chain, string memory address_) external;

/**
* @dev Remove the trusted address of the chain.
* @param chain Chain name that should be made untrusted
*/
function removeTrustedAddress(string calldata chain) external;
}
27 changes: 27 additions & 0 deletions contracts/libs/StringStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

library StringStorage {
struct Wrapper {
string value;
}

function set(bytes32 slot, string memory value) internal {
_getStorageStruct(slot).value = value;
}

function get(bytes32 slot) internal view returns (string memory value) {
value = _getStorageStruct(slot).value;
}

function clear(bytes32 slot) internal {
delete _getStorageStruct(slot).value;
}

function _getStorageStruct(bytes32 slot) internal pure returns (Wrapper storage wrapper) {
assembly {
wrapper.slot := slot
}
}
}
19 changes: 19 additions & 0 deletions contracts/test/libs/TestStringStorage.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { StringStorage } from '../../libs/StringStorage.sol';

contract TestStringStorage {
function set(bytes32 slot, string calldata value) external {
StringStorage.set(slot, value);
}

function get(bytes32 slot) external view returns (string memory value) {
value = StringStorage.get(slot);
}

function clear(bytes32 slot) external {
StringStorage.clear(slot);
}
}
16 changes: 16 additions & 0 deletions contracts/test/utils/TestInterchainAddressTracker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { InterchainAddressTracker } from '../../utils/InterchainAddressTracker.sol';

contract TestInterchainAddressTracker is InterchainAddressTracker {
string public name = 'Test'; // Dummy var for a different bytecode

error Invalid();

constructor(string memory chainName_) InterchainAddressTracker(chainName_) {
if (_CHAIN_NAME_SLOT != bytes32(uint256(keccak256('interchain-address-tracker-chain-name')) - 1))
revert Invalid();
}
}
15 changes: 15 additions & 0 deletions contracts/test/utils/TestInterchainAddressTrackerProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { InterchainAddressTrackerProxy } from '../../utils/InterchainAddressTrackerProxy.sol';

contract TestInterchainAddressTrackerProxy is InterchainAddressTrackerProxy {
constructor(
address implementationAddress,
address owner,
bytes memory params
) InterchainAddressTrackerProxy(implementationAddress, owner, params) {
contractId();
}
}
156 changes: 156 additions & 0 deletions contracts/utils/InterchainAddressTracker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { IInterchainAddressTracker } from '../interfaces/IInterchainAddressTracker.sol';
import { StringStorage } from '../libs/StringStorage.sol';
import { Upgradable } from '../upgradable/Upgradable.sol';

/**
* @title InterchainAddressTracker
* @dev Manages and validates trusted interchain addresses of an application.
*/
contract InterchainAddressTracker is IInterchainAddressTracker, Upgradable {
bytes32 internal constant PREFIX_ADDRESS_MAPPING = keccak256('interchain-address-tracker-address-mapping');
bytes32 internal constant PREFIX_ADDRESS_HASH_MAPPING =
keccak256('interchain-address-tracker-address-hash-mapping');
// bytes32(uint256(keccak256('interchain-address-tracker-chain-name')) - 1)
bytes32 internal constant _CHAIN_NAME_SLOT = 0x0e2c162a1f4b5cff9fdbd6b34678a9bcb9898a0b9fbca695b112d61688d8b2ac;

bytes32 private constant CONTRACT_ID = keccak256('interchain-address-tracker');

/**
* @dev Constructs the InterchainAddressTracker contract, both array parameters must be equal in length.
* @param chainName_ The name of the current chain.
*/
constructor(string memory chainName_) {
if (bytes(chainName_).length == 0) revert ZeroStringLength();

StringStorage.set(_CHAIN_NAME_SLOT, chainName_);
}

/**
* @notice Getter for the contract id.
*/
function contractId() external pure returns (bytes32) {
return CONTRACT_ID;
}

function _setup(bytes calldata params) internal override {
(string[] memory trustedChainNames, string[] memory trustedAddresses) = abi.decode(
params,
(string[], string[])
);
uint256 length = trustedChainNames.length;

if (length != trustedAddresses.length) revert LengthMismatch();

for (uint256 i; i < length; ++i) {
setTrustedAddress(trustedChainNames[i], trustedAddresses[i]);
}
}

/**
* @dev Gets the name of the chain this is deployed at
*/
function chainName() external view returns (string memory chainName_) {
chainName_ = StringStorage.get(_CHAIN_NAME_SLOT);
}

/**
* @dev Gets the key for the trusted address at a remote chain
* @param chain Chain name of the remote chain
* @return slot the slot to store the trusted address in
*/
function _getTrustedAddressSlot(string memory chain) internal pure returns (bytes32 slot) {
slot = keccak256(abi.encode(PREFIX_ADDRESS_MAPPING, chain));
}

/**
* @dev Gets the key for the trusted address at a remote chain
* @param chain Chain name of the remote chain
* @return slot the slot to store the trusted address hash in
*/
function _getTrustedAddressHashSlot(string memory chain) internal pure returns (bytes32 slot) {
slot = keccak256(abi.encode(PREFIX_ADDRESS_HASH_MAPPING, chain));
}

/**
* @dev Sets the trusted address and its hash for a remote chain
* @param chain Chain name of the remote chain
* @param trustedAddress_ the string representation of the trusted address
*/
function _setTrustedAddress(string memory chain, string memory trustedAddress_) internal {
StringStorage.set(_getTrustedAddressSlot(chain), trustedAddress_);

bytes32 slot = _getTrustedAddressHashSlot(chain);
bytes32 addressHash = keccak256(bytes(trustedAddress_));
assembly {
sstore(slot, addressHash)
}
}

/**
* @dev Gets the trusted address at a remote chain
* @param chain Chain name of the remote chain
* @return trustedAddress_ The trusted address for the chain. Returns '' if the chain is untrusted
*/
function trustedAddress(string memory chain) public view returns (string memory trustedAddress_) {
trustedAddress_ = StringStorage.get(_getTrustedAddressSlot(chain));
}

/**
* @dev Gets the trusted address hash for a chain
* @param chain Chain name
* @return trustedAddressHash_ the hash of the trusted address for that chain
*/
function trustedAddressHash(string memory chain) public view returns (bytes32 trustedAddressHash_) {
bytes32 slot = _getTrustedAddressHashSlot(chain);
assembly {
trustedAddressHash_ := sload(slot)
}
}

/**
* @dev Checks whether the interchain sender is a trusted address
* @param chain Chain name of the sender
* @param address_ Address of the sender
* @return bool true if the sender chain/address are trusted, false otherwise
*/
function isTrustedAddress(string calldata chain, string calldata address_) external view returns (bool) {
bytes32 addressHash = keccak256(bytes(address_));

return addressHash == trustedAddressHash(chain);
}

/**
* @dev Sets the trusted address for the specified chain
* @param chain Chain name to be trusted
* @param address_ Trusted address to be added for the chain
*/
function setTrustedAddress(string memory chain, string memory address_) public onlyOwner {
if (bytes(chain).length == 0) revert ZeroStringLength();
if (bytes(address_).length == 0) revert ZeroStringLength();

_setTrustedAddress(chain, address_);

emit TrustedAddressSet(chain, address_);
}

/**
* @dev Remove the trusted address of the chain.
* @param chain Chain name that should be made untrusted
*/
function removeTrustedAddress(string calldata chain) external onlyOwner {
if (bytes(chain).length == 0) revert ZeroStringLength();

StringStorage.clear(_getTrustedAddressSlot(chain));

bytes32 slot = _getTrustedAddressHashSlot(chain);
assembly {
sstore(slot, 0)
}

emit TrustedAddressRemoved(chain);
}
}
33 changes: 33 additions & 0 deletions contracts/utils/InterchainAddressTrackerProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { Proxy } from '../upgradable/Proxy.sol';

/**
* @title InterchainAddressTrackerProxy
* @dev Proxy contract for the InterchainAddressTracker contract. Inherits from the Proxy contract.
*/
contract InterchainAddressTrackerProxy is Proxy {
bytes32 private constant CONTRACT_ID = keccak256('interchain-address-tracker');

/**
* @dev Constructs the InterchainAddressTrackerProxy contract.
* @param implementationAddress Address of the InterchainAddressTracker implementation
* @param owner Address of the owner of the proxy
* @param params The params to be passed to the _setup function of the implementation.
*/
constructor(
address implementationAddress,
address owner,
bytes memory params
) Proxy(implementationAddress, owner, params) {}

/**
* @dev Override for the `contractId` function in Proxy. Returns a unique identifier for this contract.
* @return bytes32 Identifier for this contract.
*/
function contractId() internal pure override returns (bytes32) {
return CONTRACT_ID;
}
}
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@axelar-network/axelar-gmp-sdk-solidity",
"version": "5.5.0",
"version": "5.6.0",
"description": "Solidity GMP SDK and utilities provided by Axelar for cross-chain development",
"main": "index.js",
"scripts": {
Expand Down
Loading

0 comments on commit b64ba1c

Please sign in to comment.