Skip to content

Commit

Permalink
cluster connection specs change
Browse files Browse the repository at this point in the history
  • Loading branch information
gcranju committed Nov 29, 2024
1 parent e9da4c5 commit 98bb0df
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 167 deletions.
66 changes: 45 additions & 21 deletions contracts/evm/contracts/adapters/ClusterConnection.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
pragma solidity >=0.8.0;
pragma abicoder v2;

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";

contract ClusterConnection is Initializable, IConnection {

Expand All @@ -26,7 +29,7 @@ contract ClusterConnection is Initializable, IConnection {
uint8 private validatorsThreshold;

event Message(string targetNetwork, uint256 sn, bytes _msg);
event ValidatorSetAdded(address[] _validator, uint8 _threshold);
event ValidatorSetAdded(bytes[] _validator, uint8 _threshold);

modifier onlyRelayer() {
require(msg.sender == this.relayer(), "OnlyRelayer");
Expand All @@ -48,11 +51,12 @@ contract ClusterConnection is Initializable, IConnection {
return validators;
}

function setValidators(address[] 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++) {
if(!isValidator(_validators[i]) && _validators[i] != address(0)) {
validators.push(_validators[i]);
address validators_address = publicKeyToAddress(_validators[i]);
if(!isValidator(validators_address) && validators_address != address(0)) {
validators.push(validators_address);
}
}
require(validators.length >= _threshold, "Not enough validators");
Expand Down Expand Up @@ -143,10 +147,11 @@ contract ClusterConnection is Initializable, IConnection {
bytes32 messageHash = getMessageHash(srcNetwork, _connSn, _msg);
uint signerCount = 0;
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)){
if (!isValidatorProcessed(collectedSigners, signer) && existsInValidators(signer)){
collectedSigners[signerCount] = signer;
signerCount++;
}
Expand All @@ -155,6 +160,13 @@ contract ClusterConnection is Initializable, IConnection {
recvMessage(srcNetwork,_connSn,_msg);
}

function existsInValidators(address signer) internal view returns (bool) {
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) {
for (uint i = 0; i < processedSigners.length; i++) {
if (processedSigners[i] == signer) {
Expand All @@ -178,25 +190,14 @@ contract ClusterConnection is Initializable, IConnection {
v += 27;
}
require(v == 27 || v == 28, "Invalid signature 'v' value");
return ecrecover(toEthSignedMessageHash(messageHash), v, r, s);
}

function toEthSignedMessageHash(bytes32 _messageHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _messageHash));
return ecrecover(messageHash, v, r, s);
}


/**
@notice Sends the message to a xCall.
@param srcNetwork String ( Network Id )
@param _connSn Integer ( connection message sn )
@param _msg Bytes ( serialized bytes of Service Message )
*/
function recvMessage(
string memory srcNetwork,
uint256 _connSn,
bytes calldata _msg
) public onlyRelayer {
) internal {
require(!receipts[srcNetwork][_connSn], "Duplicate Message");
receipts[srcNetwork][_connSn] = true;
ICallService(xCall).handleMessage(srcNetwork, _msg);
Expand Down Expand Up @@ -232,11 +233,19 @@ contract ClusterConnection is Initializable, IConnection {
}

/**
@notice Set the address of the admin.
@param _address The address of the admin.
*/
function setAdmin(address _address) external onlyAdmin {
adminAddress = _address;
}

/**
@notice Set the address of the relayer.
@param _address The address of the relayer.
*/
function setAdmin(address _address) external onlyRelayer {
adminAddress = _address;
function setRelayer(address _address) external onlyAdmin {
relayerAddress = _address;
}

/**
Expand Down Expand Up @@ -272,7 +281,22 @@ contract ClusterConnection is Initializable, IConnection {
srcNetwork.encodeString(),
_connSn.encodeUint(),
_msg.encodeBytes()
);
).encodeList();
return keccak256(rlp);
}

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

bytes32 hash;

assembly {
let publicKeyStart := add(publicKey, 0x20)
let destinationStart := add(publicKeyStart, 1)
hash := keccak256(destinationStart, 64)
}

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

}
175 changes: 29 additions & 146 deletions contracts/evm/test/adapters/ClusterConnection.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -133,98 +133,6 @@ contract ClusterConnectionTest is Test {
vm.stopPrank();
}

function testRecvMessage() public {
bytes memory data = bytes("test");
string memory iconDapp = NetworkAddress.networkAddress(
nidSource,
"0xa"
);
Types.CSMessageRequestV2 memory request = Types.CSMessageRequestV2(
iconDapp,
ParseAddress.toString(address(dappSource)),
1,
Types.CALL_MESSAGE_TYPE,
data,
new string[](0)
);
Types.CSMessage memory message = Types.CSMessage(
Types.CS_REQUEST,
request.encodeCSMessageRequestV2()
);

vm.startPrank(destination_relayer);
adapterTarget.recvMessage(
nidSource,
1,
RLPEncodeStruct.encodeCSMessage(message)
);
vm.stopPrank();
}

function testRecvMessageUnAuthorized() public {
bytes memory data = bytes("test");
string memory iconDapp = NetworkAddress.networkAddress(
nidSource,
"0xa"
);
Types.CSMessageRequestV2 memory request = Types.CSMessageRequestV2(
iconDapp,
ParseAddress.toString(address(dappSource)),
1,
Types.CALL_MESSAGE_TYPE,
data,
new string[](0)
);
Types.CSMessage memory message = Types.CSMessage(
Types.CS_REQUEST,
request.encodeCSMessageRequestV2()
);

vm.startPrank(user);
vm.expectRevert("OnlyRelayer");
adapterTarget.recvMessage(
nidSource,
1,
RLPEncodeStruct.encodeCSMessage(message)
);
vm.stopPrank();
}

function testRecvMessageDuplicateMsg() public {
bytes memory data = bytes("test");
string memory iconDapp = NetworkAddress.networkAddress(
nidSource,
"0xa"
);
Types.CSMessageRequestV2 memory request = Types.CSMessageRequestV2(
iconDapp,
ParseAddress.toString(address(dappSource)),
1,
Types.CALL_MESSAGE_TYPE,
data,
new string[](0)
);
Types.CSMessage memory message = Types.CSMessage(
Types.CS_REQUEST,
request.encodeCSMessageRequestV2()
);

vm.startPrank(destination_relayer);
adapterTarget.recvMessage(
nidSource,
1,
RLPEncodeStruct.encodeCSMessage(message)
);

vm.expectRevert("Duplicate Message");
adapterTarget.recvMessage(
nidSource,
1,
RLPEncodeStruct.encodeCSMessage(message)
);
vm.stopPrank();
}

function testRevertMessage() public {
vm.startPrank(destination_relayer);
vm.expectRevert("CallRequestNotFound");
Expand Down Expand Up @@ -284,9 +192,9 @@ contract ClusterConnectionTest is Test {
vm.stopPrank();

assert(source_relayer.balance == 10 ether);
}
}

function testGetReceipt() public {
function testRecvMessageWithMultiSignatures() public {
bytes memory data = bytes("test");
string memory iconDapp = NetworkAddress.networkAddress(
nidSource,
Expand All @@ -304,50 +212,19 @@ contract ClusterConnectionTest is Test {
Types.CS_REQUEST,
request.encodeCSMessageRequestV2()
);

assert(adapterTarget.getReceipt(nidSource, 1) == false);

vm.startPrank(destination_relayer);
adapterTarget.recvMessage(
nidSource,
1,
RLPEncodeStruct.encodeCSMessage(message)
);
vm.stopPrank();

assert(adapterTarget.getReceipt(nidSource, 1) == true);
}

function testRecvMessageWithMultiSignature() public {
bytes memory data = bytes("test");
string memory iconDapp = NetworkAddress.networkAddress(
nidSource,
"0xa"
);
Types.CSMessageRequestV2 memory request = Types.CSMessageRequestV2(
iconDapp,
ParseAddress.toString(address(dappSource)),
1,
Types.CALL_MESSAGE_TYPE,
data,
new string[](0)
);
Types.CSMessage memory message = Types.CSMessage(
Types.CS_REQUEST,
request.encodeCSMessageRequestV2()
);
uint256 pk = hexStringToUint256("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80");
uint256 pk2 = hexStringToUint256("47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a");
uint256 pk3 = hexStringToUint256("59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d");
uint256 pk4 = hexStringToUint256("2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6");
uint256 pk = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
uint256 pk2 = 0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a;
uint256 pk3 = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;
uint256 pk4 = 0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6;
bytes32 hash = getMessageHash(nidSource, 1, RLPEncodeStruct.encodeCSMessage(message));
vm.startPrank(owner);
address[] memory validators = new address[](4);
validators[0] = address(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266);
validators[1] = address(0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65);
validators[2] = address(0x70997970C51812dc3A010C7d01b50e0d17dc79C8);
validators[3] = address(0xa0Ee7A142d267C1f36714E4a8F75612F20a79720);
adapterTarget.setValidators(validators, 4);
bytes[] memory validators = new bytes[](4);
validators[0] = bytes(hex"048318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed753547f11ca8696646f2f3acb08e31016afac23e630c5d11f59f61fef57b0d2aa5");
validators[1] = bytes(hex"04bf6ee64a8d2fdc551ec8bb9ef862ef6b4bcb1805cdc520c3aa5866c0575fd3b514c5562c3caae7aec5cd6f144b57135c75b6f6cea059c3d08d1f39a9c227219d");
validators[2] = bytes(hex"04ba5734d8f7091719471e7f7ed6b9df170dc70cc661ca05e688601ad984f068b0d67351e5f06073092499336ab0839ef8a521afd334e53807205fa2f08eec74f4");
validators[3] = bytes(hex"043255458e24278e31d5940f304b16300fdff3f6efd3e2a030b5818310ac67af45e28d057e6a332d07e0c5ab09d6947fd4eed1a646edbf224e2d2fec6f49f90abc");
adapterTarget.updateValidators(validators, 4);
adapterTarget.listValidators();
vm.stopPrank();

vm.startPrank(destination_relayer);
Expand All @@ -370,7 +247,10 @@ contract ClusterConnectionTest is Test {

function signMessage(uint256 pk,bytes32 hash) private pure returns (bytes memory){
(uint8 v, bytes32 r, bytes32 s) = vm.sign(pk, hash);
address signer = vm.addr(pk);
bytes memory signature = combineSignature(r,s,v);

address recoverSigner=ecrecover(hash,v,r,s);
return signature;
}

Expand Down Expand Up @@ -402,11 +282,14 @@ contract ClusterConnectionTest is Test {
function testAddValidator() public {
vm.startPrank(owner);

address[] memory validators = new address[](2);
validators[0] = address(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266);
validators[1] = address(0x976EA74026E726554dB657fA54763abd0C3a0aa9);
adapterTarget.setValidators(validators, 2);
assertEq(2, adapterTarget.listValidators().length);
bytes[] memory validators = new bytes[](4);
validators[0] = bytes(hex"048318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed753547f11ca8696646f2f3acb08e31016afac23e630c5d11f59f61fef57b0d2aa5");
validators[1] = bytes(hex"04bf6ee64a8d2fdc551ec8bb9ef862ef6b4bcb1805cdc520c3aa5866c0575fd3b514c5562c3caae7aec5cd6f144b57135c75b6f6cea059c3d08d1f39a9c227219d");
validators[2] = bytes(hex"04ba5734d8f7091719471e7f7ed6b9df170dc70cc661ca05e688601ad984f068b0d67351e5f06073092499336ab0839ef8a521afd334e53807205fa2f08eec74f4");
validators[3] = bytes(hex"043255458e24278e31d5940f304b16300fdff3f6efd3e2a030b5818310ac67af45e28d057e6a332d07e0c5ab09d6947fd4eed1a646edbf224e2d2fec6f49f90abc");
adapterTarget.updateValidators(validators, 4);
console2.log(adapterTarget.listValidators()[0]);
assertEq(4, adapterTarget.listValidators().length);
vm.stopPrank();
}

Expand Down Expand Up @@ -439,10 +322,10 @@ contract ClusterConnectionTest is Test {
uint256 pk = hexStringToUint256("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80");
bytes32 hash = keccak256(RLPEncodeStruct.encodeCSMessage(message));
vm.startPrank(owner);
address[] memory validators = new address[](2);
validators[0] = address(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266);
validators[1] = address(0x976EA74026E726554dB657fA54763abd0C3a0aa9);
adapterTarget.setValidators(validators, 2);
bytes[] memory validators = new bytes[](2);
validators[0] = bytes(hex"048318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed753547f11ca8696646f2f3acb08e31016afac23e630c5d11f59f61fef57b0d2aa5");
validators[1] = bytes(hex"04bf6ee64a8d2fdc551ec8bb9ef862ef6b4bcb1805cdc520c3aa5866c0575fd3b514c5562c3caae7aec5cd6f144b57135c75b6f6cea059c3d08d1f39a9c227219d");
adapterTarget.updateValidators(validators, 2);
vm.stopPrank();
vm.startPrank(destination_relayer);
vm.expectRevert("Not enough valid signatures passed");
Expand All @@ -463,7 +346,7 @@ contract ClusterConnectionTest is Test {
srcNetwork.encodeString(),
_connSn.encodeUint(),
_msg.encodeBytes()
);
).encodeList();
return keccak256(rlp);
}
}

0 comments on commit 98bb0df

Please sign in to comment.