Skip to content

Commit

Permalink
Merge pull request #424 from icon-project/fix/cluster-connection-audit
Browse files Browse the repository at this point in the history
fix: cluster connection audit
  • Loading branch information
gcranju authored Dec 13, 2024
2 parents 8e26a88 + b53fabe commit 04ccaa5
Show file tree
Hide file tree
Showing 6 changed files with 443 additions and 298 deletions.
98 changes: 71 additions & 27 deletions contracts/evm/contracts/adapters/ClusterConnection.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@
pragma solidity >=0.8.0;
pragma abicoder v2;

import {console2 } from "forge-std/Test.sol";
import {console2} from "forge-std/Test.sol";

import "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import "@xcall/utils/Types.sol";
import "@xcall/contracts/xcall/interfaces/IConnection.sol";
import "@iconfoundation/xcall-solidity-library/interfaces/ICallService.sol";
import "@iconfoundation/xcall-solidity-library/utils/RLPEncode.sol";
import "@iconfoundation/xcall-solidity-library/utils/RLPEncode.sol";
import "@iconfoundation/xcall-solidity-library/utils/Strings.sol";
import "@iconfoundation/xcall-solidity-library/utils/Integers.sol";

/// @custom:oz-upgrades-from contracts/adapters/ClusterConnectionV1.sol:ClusterConnectionV1
contract ClusterConnection is Initializable, IConnection {

using RLPEncode for bytes;
using RLPEncode for string;
using RLPEncode for uint256;

using Strings for bytes;
using Integers for uint256;

mapping(string => uint256) private messageFees;
mapping(string => uint256) private responseFees;
mapping(string => mapping(uint256 => bool)) receipts;
Expand Down Expand Up @@ -51,12 +56,18 @@ contract ClusterConnection is Initializable, IConnection {
return validators;
}

function updateValidators(bytes[] memory _validators, uint8 _threshold) external onlyAdmin {
function updateValidators(
bytes[] memory _validators,
uint8 _threshold
) external onlyAdmin {
delete validators;
for (uint i = 0; i < _validators.length; i++) {
address validators_address = publicKeyToAddress(_validators[i]);
if(!isValidator(validators_address) && validators_address != address(0)) {
validators.push(validators_address);
if (
!isValidator(validators_address) &&
validators_address != address(0)
) {
validators.push(validators_address);
}
}
require(validators.length >= _threshold, "Not enough validators");
Expand Down Expand Up @@ -143,40 +154,65 @@ contract ClusterConnection is Initializable, IConnection {
bytes calldata _msg,
bytes[] calldata _signedMessages
) public onlyRelayer {
require(_signedMessages.length >= validatorsThreshold, "Not enough signatures passed");
bytes32 messageHash = getMessageHash(srcNetwork, _connSn, _msg);
require(
_signedMessages.length >= validatorsThreshold,
"Not enough signatures passed"
);

string memory dstNetwork = ICallService(xCall).getNetworkId();

bytes32 messageHash = getMessageHash(
srcNetwork,
_connSn,
_msg,
dstNetwork
);
uint signerCount = 0;
address[] memory collectedSigners = new address[](_signedMessages.length);

address[] memory collectedSigners = new address[](
_signedMessages.length
);

for (uint i = 0; i < _signedMessages.length; i++) {
address signer = recoverSigner(messageHash, _signedMessages[i]);
require(signer != address(0), "Invalid signature");
if (!isValidatorProcessed(collectedSigners, signer) && existsInValidators(signer)){
if (
!isValidatorProcessed(collectedSigners, signer) &&
existsInValidators(signer)
) {
collectedSigners[signerCount] = signer;
signerCount++;
}
}
require(signerCount >= validatorsThreshold,"Not enough valid signatures passed");
recvMessage(srcNetwork,_connSn,_msg);
require(
signerCount >= validatorsThreshold,
"Not enough valid signatures passed"
);
recvMessage(srcNetwork, _connSn, _msg);
}

function existsInValidators(address signer) internal view returns (bool) {
for (uint i = 0; i < validators.length; i++){
for (uint i = 0; i < validators.length; i++) {
if (validators[i] == signer) return true;
}
return false;
}

function isValidatorProcessed(address[] memory processedSigners, address signer) public pure returns (bool) {
function isValidatorProcessed(
address[] memory processedSigners,
address signer
) public pure returns (bool) {
for (uint i = 0; i < processedSigners.length; i++) {
if (processedSigners[i] == signer) {
return true;
}
}
return false;
}
}

function recoverSigner(bytes32 messageHash, bytes memory signature) public pure returns (address) {
function recoverSigner(
bytes32 messageHash,
bytes memory signature
) public pure returns (address) {
require(signature.length == 65, "Invalid signature length");
bytes32 r;
bytes32 s;
Expand Down Expand Up @@ -240,7 +276,7 @@ contract ClusterConnection is Initializable, IConnection {
adminAddress = _address;
}

/**
/**
@notice Set the address of the relayer.
@param _address The address of the relayer.
*/
Expand Down Expand Up @@ -268,24 +304,33 @@ contract ClusterConnection is Initializable, IConnection {
@notice Set the required signature count for verification.
@param _count The desired count.
*/
function setRequiredValidatorCount(uint8 _count) external onlyAdmin() {
function setRequiredValidatorCount(uint8 _count) external onlyAdmin {
validatorsThreshold = _count;
}

function getRequiredValidatorCount() external view returns (uint8) {
return validatorsThreshold;
}

function getMessageHash(string memory srcNetwork, uint256 _connSn, bytes calldata _msg) internal pure returns (bytes32) {
bytes memory rlp = abi.encodePacked(
srcNetwork.encodeString(),
_connSn.encodeUint(),
_msg.encodeBytes()
).encodeList();
return keccak256(rlp);
function getMessageHash(
string memory srcNetwork,
uint256 _connSn,
bytes calldata _msg,
string memory dstNetwork
) internal pure returns (bytes32) {
bytes memory encoded = abi
.encodePacked(
srcNetwork,
_connSn.toString(),
_msg,
dstNetwork
);
return keccak256(encoded);
}

function publicKeyToAddress(bytes memory publicKey) internal pure returns (address addr) {
function publicKeyToAddress(
bytes memory publicKey
) internal pure returns (address addr) {
require(publicKey.length == 65, "Invalid public key length");

bytes32 hash;
Expand All @@ -298,5 +343,4 @@ contract ClusterConnection is Initializable, IConnection {

addr = address(uint160(uint256(hash)));
}

}
2 changes: 1 addition & 1 deletion contracts/evm/library/xcall/utils/Strings.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ library Strings {
converted[i * 2 + 1] = _base[uint8(buffer[i]) & 0xf];
}

return string(abi.encodePacked("0x", converted));
return string(converted);
}

/**
Expand Down
Loading

0 comments on commit 04ccaa5

Please sign in to comment.