diff --git a/contracts/evm/contracts/adapters/ClusterConnection.sol b/contracts/evm/contracts/adapters/ClusterConnection.sol index a5b70e42..58d9c9c8 100644 --- a/contracts/evm/contracts/adapters/ClusterConnection.sol +++ b/contracts/evm/contracts/adapters/ClusterConnection.sol @@ -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 { @@ -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"); @@ -48,11 +51,12 @@ contract ClusterConnection is Initializable, IConnection { return validators; } - function updateValidators(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"); @@ -147,7 +151,7 @@ contract ClusterConnection is Initializable, IConnection { 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++; } @@ -156,6 +160,13 @@ contract ClusterConnection is Initializable, IConnection { recvMessage(srcNetwork,_connSn,_msg); } + function existsInValidators(address signer) internal 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) { @@ -179,14 +190,9 @@ 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); } - function recvMessage( string memory srcNetwork, uint256 _connSn, @@ -264,10 +270,25 @@ contract ClusterConnection is Initializable, IConnection { function getMessageHash(string memory srcNetwork, uint256 _connSn, bytes calldata _msg) internal pure returns (bytes32) { bytes memory rlp = abi.encodePacked( - srcNetwork, - _connSn, - _msg - ); + 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))); + } + } diff --git a/contracts/evm/test/adapters/ClusterConnection.t.sol b/contracts/evm/test/adapters/ClusterConnection.t.sol index e88c4733..5a52bee4 100644 --- a/contracts/evm/test/adapters/ClusterConnection.t.sol +++ b/contracts/evm/test/adapters/ClusterConnection.t.sol @@ -194,7 +194,7 @@ contract ClusterConnectionTest is Test { assert(source_relayer.balance == 10 ether); } - function testRecvMessageWithMultiSignature() public { + function testRecvMessageWithMultiSignatures() public { bytes memory data = bytes("test"); string memory iconDapp = NetworkAddress.networkAddress( nidSource, @@ -212,18 +212,19 @@ contract ClusterConnectionTest is Test { 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); + 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); @@ -246,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; } @@ -278,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.updateValidators(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(); } @@ -315,9 +322,9 @@ 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); + 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); @@ -336,10 +343,10 @@ contract ClusterConnectionTest is Test { function getMessageHash(string memory srcNetwork, uint256 _connSn, bytes memory _msg) internal pure returns (bytes32) { bytes memory rlp = abi.encodePacked( - srcNetwork, - _connSn, - _msg - ); + srcNetwork.encodeString(), + _connSn.encodeUint(), + _msg.encodeBytes() + ).encodeList(); return keccak256(rlp); } }