From fc21bb61af915644b8f9555bf4ac338813aec998 Mon Sep 17 00:00:00 2001 From: Artem Payvin Date: Fri, 26 Aug 2022 10:40:02 +0100 Subject: [PATCH 01/12] Add chain support in timelimits --- proxy/contracts/schain/CommunityLocker.sol | 61 ++++++++++++++----- .../TokenManagers/TokenManagerERC1155.sol | 4 +- .../TokenManagers/TokenManagerERC20.sol | 2 +- .../TokenManagers/TokenManagerERC721.sol | 2 +- .../schain/TokenManagers/TokenManagerEth.sol | 2 +- proxy/package.json | 2 +- proxy/test/CommunityLocker.ts | 6 +- proxy/test/TokenManagerERC721.ts | 2 +- proxy/test/TokenManagerERC721WithMetadata.ts | 2 +- proxy/yarn.lock | 8 +-- 10 files changed, 62 insertions(+), 29 deletions(-) diff --git a/proxy/contracts/schain/CommunityLocker.sol b/proxy/contracts/schain/CommunityLocker.sol index b853ed679..72c502cf2 100644 --- a/proxy/contracts/schain/CommunityLocker.sol +++ b/proxy/contracts/schain/CommunityLocker.sol @@ -28,13 +28,17 @@ import "@skalenetwork/ima-interfaces/schain/ICommunityLocker.sol"; import "../Messages.sol"; +interface ICommunityLockerInitializer is ICommunityLocker { + function initializeTimestamp() external; +} + /** * @title CommunityLocker * @dev Contract contains logic to perform automatic reimbursement * of gas fees for sent messages */ -contract CommunityLocker is ICommunityLocker, AccessControlEnumerableUpgradeable { +contract CommunityLocker is ICommunityLockerInitializer, AccessControlEnumerableUpgradeable { /** * @dev Mainnet identifier. @@ -75,7 +79,8 @@ contract CommunityLocker is ICommunityLocker, AccessControlEnumerableUpgradeable * @dev Amount of seconds after message sending * when next message cannot be sent. */ - uint public timeLimitPerMessage; + // slither-disable-next-line constable-states uninitialized-state + uint private _deprecatedTimeLimitPerMessage; /** * @dev Mapping of users who are allowed to send a message. @@ -93,6 +98,10 @@ contract CommunityLocker is ICommunityLocker, AccessControlEnumerableUpgradeable uint256 public gasPriceTimestamp; + mapping(bytes32 => uint) public timeLimitPerMessage; + + mapping(bytes32 => mapping(address => uint)) public lastMessageTimeStampToSchain; + /** * @dev Emitted when a user becomes active. */ @@ -118,6 +127,19 @@ contract CommunityLocker is ICommunityLocker, AccessControlEnumerableUpgradeable uint newValue ); + modifier checkUserBeforeTransfer(bytes32 chainHash, address user) { + uint256 lastTimestamp = lastMessageTimeStampToSchain[chainHash][user]; + if (chainHash == MAINNET_HASH) { + require(activeUsers[user], "Recipient must be active"); + lastTimestamp = lastMessageTimeStamp[user]; + } + require( + lastTimestamp + timeLimitPerMessage[chainHash] < block.timestamp, + "Trying to send messages too often" + ); + _; + } + /** * @dev Allows MessageProxy to post operational message from mainnet * or SKALE chains. @@ -163,21 +185,24 @@ contract CommunityLocker is ICommunityLocker, AccessControlEnumerableUpgradeable * - Previous message sent by {receiver} must be sent earlier then {timeLimitPerMessage} seconds before current time * or there are no messages sent by {receiver}. */ - function checkAllowedToSendMessage(address receiver) external override { + function checkAllowedToSendMessage(bytes32 chainHash, address receiver) + external + checkUserBeforeTransfer(chainHash, receiver) + override + { require( tokenManagerLinker.hasTokenManager(ITokenManager(msg.sender)), "Sender is not registered token manager" ); - require(activeUsers[receiver], "Recipient must be active"); - require( - lastMessageTimeStamp[receiver] + timeLimitPerMessage < block.timestamp, - "Trying to send messages too often" - ); - lastMessageTimeStamp[receiver] = block.timestamp; + if (chainHash == MAINNET_HASH) { + lastMessageTimeStamp[receiver] = block.timestamp; + } else { + lastMessageTimeStampToSchain[chainHash][receiver] = block.timestamp; + } } /** - * @dev Set value of {timeLimitPerMessage}. + * @dev Set value of {timeLimitPerMessage} of given chain. * * Requirements: * @@ -185,14 +210,16 @@ contract CommunityLocker is ICommunityLocker, AccessControlEnumerableUpgradeable * * Emits a {ConstantUpdated} event. */ - function setTimeLimitPerMessage(uint newTimeLimitPerMessage) external override { + function setTimeLimitPerMessage(string memory chainName, uint newTimeLimitPerMessage) external override { require(hasRole(CONSTANT_SETTER_ROLE, msg.sender), "Not enough permissions to set constant"); + bytes32 chainHash = keccak256(abi.encodePacked(chainName)); + require(chainHash != schainHash, "Incorrect chain"); emit ConstantUpdated( keccak256(abi.encodePacked("TimeLimitPerMessage")), - timeLimitPerMessage, + timeLimitPerMessage[chainHash], newTimeLimitPerMessage ); - timeLimitPerMessage = newTimeLimitPerMessage; + timeLimitPerMessage[chainHash] = newTimeLimitPerMessage; } /** @@ -247,7 +274,13 @@ contract CommunityLocker is ICommunityLocker, AccessControlEnumerableUpgradeable messageProxy = newMessageProxy; tokenManagerLinker = newTokenManagerLinker; schainHash = keccak256(abi.encodePacked(newSchainName)); - timeLimitPerMessage = 5 minutes; + timeLimitPerMessage[MAINNET_HASH] = 5 minutes; communityPool = newCommunityPool; } + + function initializeTimestamp() external override { + require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Incorrect sender"); + // slither-disable-next-line uninitialized-state + timeLimitPerMessage[MAINNET_HASH] = _deprecatedTimeLimitPerMessage; + } } diff --git a/proxy/contracts/schain/TokenManagers/TokenManagerERC1155.sol b/proxy/contracts/schain/TokenManagers/TokenManagerERC1155.sol index dc463d18b..c0a7caa41 100644 --- a/proxy/contracts/schain/TokenManagers/TokenManagerERC1155.sol +++ b/proxy/contracts/schain/TokenManagers/TokenManagerERC1155.sol @@ -111,7 +111,7 @@ contract TokenManagerERC1155 is external override { - communityLocker.checkAllowedToSendMessage(msg.sender); + communityLocker.checkAllowedToSendMessage(MAINNET_HASH, msg.sender); _exit(MAINNET_HASH, depositBox, contractOnMainnet, msg.sender, id, amount); } @@ -128,7 +128,7 @@ contract TokenManagerERC1155 is external override { - communityLocker.checkAllowedToSendMessage(msg.sender); + communityLocker.checkAllowedToSendMessage(MAINNET_HASH, msg.sender); _exitBatch(MAINNET_HASH, depositBox, contractOnMainnet, msg.sender, ids, amounts); } diff --git a/proxy/contracts/schain/TokenManagers/TokenManagerERC20.sol b/proxy/contracts/schain/TokenManagers/TokenManagerERC20.sol index 6d72b1fdb..4d05af1af 100644 --- a/proxy/contracts/schain/TokenManagers/TokenManagerERC20.sol +++ b/proxy/contracts/schain/TokenManagers/TokenManagerERC20.sol @@ -99,7 +99,7 @@ contract TokenManagerERC20 is TokenManager, ITokenManagerERC20 { external override { - communityLocker.checkAllowedToSendMessage(msg.sender); + communityLocker.checkAllowedToSendMessage(MAINNET_HASH, msg.sender); _exit(MAINNET_HASH, depositBox, contractOnMainnet, msg.sender, amount); } diff --git a/proxy/contracts/schain/TokenManagers/TokenManagerERC721.sol b/proxy/contracts/schain/TokenManagers/TokenManagerERC721.sol index 3f815e833..d4067fe4f 100644 --- a/proxy/contracts/schain/TokenManagers/TokenManagerERC721.sol +++ b/proxy/contracts/schain/TokenManagers/TokenManagerERC721.sol @@ -98,7 +98,7 @@ contract TokenManagerERC721 is TokenManager, ITokenManagerERC721 { external override { - communityLocker.checkAllowedToSendMessage(msg.sender); + communityLocker.checkAllowedToSendMessage(MAINNET_HASH, msg.sender); _exit(MAINNET_HASH, depositBox, contractOnMainnet, msg.sender, tokenId); } diff --git a/proxy/contracts/schain/TokenManagers/TokenManagerEth.sol b/proxy/contracts/schain/TokenManagers/TokenManagerEth.sol index 28f3aaf03..56fe9565e 100644 --- a/proxy/contracts/schain/TokenManagers/TokenManagerEth.sol +++ b/proxy/contracts/schain/TokenManagers/TokenManagerEth.sol @@ -54,7 +54,7 @@ contract TokenManagerEth is TokenManager, ITokenManagerEth { * EthErc20 tokens are burned on schain and ETH are unlocked on mainnet for {to} address. */ function exitToMain(uint256 amount) external override { - communityLocker.checkAllowedToSendMessage(msg.sender); + communityLocker.checkAllowedToSendMessage(MAINNET_HASH, msg.sender); _exit(MAINNET_HASH, depositBox, msg.sender, amount); } diff --git a/proxy/package.json b/proxy/package.json index e6daef673..3e9da932f 100644 --- a/proxy/package.json +++ b/proxy/package.json @@ -26,7 +26,7 @@ "@openzeppelin/contracts-upgradeable": "^4.7.1", "@openzeppelin/hardhat-upgrades": "^1.9.0", "@skalenetwork/etherbase-interfaces": "^0.0.1-develop.20", - "@skalenetwork/ima-interfaces": "1.0.0-develop.20", + "@skalenetwork/ima-interfaces": "1.0.0-timelimit-s2s-transfers-interface.1", "@skalenetwork/skale-manager-interfaces": "1.0.0", "axios": "^0.21.4", "dotenv": "^10.0.0", diff --git a/proxy/test/CommunityLocker.ts b/proxy/test/CommunityLocker.ts index 9d75f5913..d9799a49b 100644 --- a/proxy/test/CommunityLocker.ts +++ b/proxy/test/CommunityLocker.ts @@ -107,11 +107,11 @@ describe("CommunityLocker", () => { }); it("should set time limit per message", async () => { - await communityLocker.setTimeLimitPerMessage(0) + await communityLocker.setTimeLimitPerMessage("Mainnet", 0) .should.be.eventually.rejectedWith("Not enough permissions to set constant"); await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); - await communityLocker.setTimeLimitPerMessage(0); - expect(BigNumber.from(await communityLocker.timeLimitPerMessage()).toString()).to.be.equal(BigNumber.from(0).toString()); + await communityLocker.setTimeLimitPerMessage("Mainnet", 0); + expect(BigNumber.from(await communityLocker.timeLimitPerMessage(mainnetHash)).toString()).to.be.equal(BigNumber.from(0).toString()); }); it("should set gasprice", async () => { diff --git a/proxy/test/TokenManagerERC721.ts b/proxy/test/TokenManagerERC721.ts index 19cc6971e..f08ec0dec 100644 --- a/proxy/test/TokenManagerERC721.ts +++ b/proxy/test/TokenManagerERC721.ts @@ -120,7 +120,7 @@ describe("TokenManagerERC721", () => { await messageProxyForSchain.connect(deployer).grantRole(extraContractRegistrarRole, deployer.address); await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); - await communityLocker.setTimeLimitPerMessage(0); + await communityLocker.setTimeLimitPerMessage("Mainnet", 0); }); it("should change depositBox address", async () => { diff --git a/proxy/test/TokenManagerERC721WithMetadata.ts b/proxy/test/TokenManagerERC721WithMetadata.ts index 1aea17527..6da0a4d4b 100644 --- a/proxy/test/TokenManagerERC721WithMetadata.ts +++ b/proxy/test/TokenManagerERC721WithMetadata.ts @@ -121,7 +121,7 @@ describe("TokenManagerERC721WithMetadata", () => { await messageProxyForSchain.connect(deployer).grantRole(extraContractRegistrarRole, deployer.address); await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); - await communityLocker.setTimeLimitPerMessage(0); + await communityLocker.setTimeLimitPerMessage("Mainnet", 0); }); it("should change depositBox address", async () => { diff --git a/proxy/yarn.lock b/proxy/yarn.lock index 1732af0fa..ac9642022 100644 --- a/proxy/yarn.lock +++ b/proxy/yarn.lock @@ -757,10 +757,10 @@ resolved "https://registry.yarnpkg.com/@skalenetwork/etherbase-interfaces/-/etherbase-interfaces-0.0.1-develop.20.tgz#33f61e18d695fd47063aa39dce4df335d26b9528" integrity sha512-j3xnuQtOtjvjAoUMJgSUFxRa9/Egkg1RyA8r6PjcEb33VksE4LWLBy0PNFUFehLZv48595JROTcViGeXXwg5HQ== -"@skalenetwork/ima-interfaces@1.0.0-develop.20": - version "1.0.0-develop.20" - resolved "https://registry.yarnpkg.com/@skalenetwork/ima-interfaces/-/ima-interfaces-1.0.0-develop.20.tgz#e4ae92f9832c08d289690f080b22363fa8f3a339" - integrity sha512-OV6lHSg/UiezfLHM/STawOlu2P0IxAXXVL0LilyunuOAtSg3lLfRRyTJKD3D+JePfhgIc25B8DUxJiRyQg4UIg== +"@skalenetwork/ima-interfaces@1.0.0-timelimit-s2s-transfers-interface.1": + version "1.0.0-timelimit-s2s-transfers-interface.1" + resolved "https://registry.yarnpkg.com/@skalenetwork/ima-interfaces/-/ima-interfaces-1.0.0-timelimit-s2s-transfers-interface.1.tgz#d87743ab6101f6fdf2f9138c47f9221a98bf6d46" + integrity sha512-B95rBp7YVZSnyRsUuqOG0v6AWPeoN11QRzeEh/v67+HuzxBFb9gVkLBOmgvHKNyjS4qolMPkg3CWQuXrMoOePA== dependencies: "@skalenetwork/skale-manager-interfaces" "^0.1.2" From ae87d7ed5eca7532777db54fdebe19b7563a05ea Mon Sep 17 00:00:00 2001 From: Artem Payvin Date: Fri, 26 Aug 2022 12:05:05 +0100 Subject: [PATCH 02/12] Add slither detector to exclude --- proxy/contracts/schain/CommunityLocker.sol | 2 +- proxy/slither.config.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/proxy/contracts/schain/CommunityLocker.sol b/proxy/contracts/schain/CommunityLocker.sol index 72c502cf2..fea6bc841 100644 --- a/proxy/contracts/schain/CommunityLocker.sol +++ b/proxy/contracts/schain/CommunityLocker.sol @@ -79,7 +79,7 @@ contract CommunityLocker is ICommunityLockerInitializer, AccessControlEnumerable * @dev Amount of seconds after message sending * when next message cannot be sent. */ - // slither-disable-next-line constable-states uninitialized-state + // slither-disable-next-line uninitialized-state uint private _deprecatedTimeLimitPerMessage; /** diff --git a/proxy/slither.config.json b/proxy/slither.config.json index 2ec3eac25..8eb8c4111 100644 --- a/proxy/slither.config.json +++ b/proxy/slither.config.json @@ -1,4 +1,4 @@ { - "detectors_to_exclude": "uninitialized-local,calls-loop,reentrancy-events,assembly,solc-version,timestamp,unused-return,variable-scope,dead-code,erc20-interface", + "detectors_to_exclude": "uninitialized-local,calls-loop,reentrancy-events,assembly,solc-version,timestamp,unused-return,variable-scope,dead-code,erc20-interface,constable-states", "filter_paths": "test|@openzeppelin/contracts|thirdparty" } \ No newline at end of file From bf96e1dd4a858dc9bfd38eecabe4157753942016 Mon Sep 17 00:00:00 2001 From: Artem Payvin Date: Fri, 26 Aug 2022 12:34:47 +0100 Subject: [PATCH 03/12] Fix predeployed --- .../contracts/community_locker.py | 17 ++++++++++++----- .../test/contracts/community_locker.py | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/proxy/predeployed/src/ima_predeployed/contracts/community_locker.py b/proxy/predeployed/src/ima_predeployed/contracts/community_locker.py index 850d40944..e0bcf901f 100644 --- a/proxy/predeployed/src/ima_predeployed/contracts/community_locker.py +++ b/proxy/predeployed/src/ima_predeployed/contracts/community_locker.py @@ -15,6 +15,7 @@ class CommunityLockerGenerator(AccessControlEnumerableGenerator): META_FILENAME = 'CommunityLocker.meta.json' DEFAULT_ADMIN_ROLE = (0).to_bytes(32, 'big') DEFAULT_TIME_LIMIT_SEC = 5 * 60 + MAINNET_HASH = Web3.solidityKeccak(['string'], ['Mainnet']) # ---------- storage ---------- # --------Initializable-------- @@ -42,9 +43,13 @@ class CommunityLockerGenerator(AccessControlEnumerableGenerator): # 202: tokenManagerLinker # 203: communityPool # 204: schainHash - # 205: timeLimitPerMessage - # 206: _unfrozenUsers - # 207: _lastMessageTimeStamp + # 205: _deprecatedTimeLimitPerMessage + # 206: activeUsers + # 207: lastMessageTimeStamp + # 208: mainnetGasPrice + # 209: gasPriceTimestamp + # 210: timeLimitPerMessage + # 211: lastMessageTimeStampToSchain INITIALIZED_SLOT = 0 ROLES_SLOT = 101 @@ -53,7 +58,7 @@ class CommunityLockerGenerator(AccessControlEnumerableGenerator): TOKEN_MANAGER_LINKER_SLOT = AccessControlEnumerableGenerator.next_slot(MESSAGE_PROXY_SLOT) COMMUNITY_POOL_SLOT = AccessControlEnumerableGenerator.next_slot(TOKEN_MANAGER_LINKER_SLOT) SCHAIN_HASH_SLOT = AccessControlEnumerableGenerator.next_slot(COMMUNITY_POOL_SLOT) - TIME_LIMIT_PER_MESSAGE_SLOT = AccessControlEnumerableGenerator.next_slot(SCHAIN_HASH_SLOT) + TIME_LIMIT_PER_MESSAGE_SLOT = 210 def __init__(self): generator = CommunityLockerGenerator.from_hardhat_artifact( @@ -77,7 +82,9 @@ def generate_storage(cls, **kwargs) -> Dict[str, str]: cls._write_address(storage, cls.COMMUNITY_POOL_SLOT, community_pool_address) cls._write_bytes32(storage, cls.SCHAIN_HASH_SLOT, Web3.solidityKeccak(['string'], [schain_name])) - cls._write_uint256(storage, cls.TIME_LIMIT_PER_MESSAGE_SLOT, cls.DEFAULT_TIME_LIMIT_SEC) + time_limit_per_message_slot = Generator.calculate_mapping_value_slot( + cls.TIME_LIMIT_PER_MESSAGE_SLOT, cls.MAINNET_HASH, 'bytes32') + cls._write_uint256(storage, time_limit_per_message_slot, cls.DEFAULT_TIME_LIMIT_SEC) return storage diff --git a/proxy/predeployed/test/contracts/community_locker.py b/proxy/predeployed/test/contracts/community_locker.py index e5a53eb86..a1707e98b 100644 --- a/proxy/predeployed/test/contracts/community_locker.py +++ b/proxy/predeployed/test/contracts/community_locker.py @@ -14,4 +14,4 @@ def check_community_locker(deployer_address: str, schain_name: str, community_po if not community_locker.functions.tokenManagerLinker().call() == TOKEN_MANAGER_LINKER_ADDRESS: raise AssertionError if not community_locker.functions.communityPool().call() == community_pool_address: raise AssertionError if not community_locker.functions.schainHash().call() == w3.solidityKeccak(['string'], [schain_name]): raise AssertionError - if not community_locker.functions.timeLimitPerMessage().call() == CommunityLockerGenerator.DEFAULT_TIME_LIMIT_SEC: raise AssertionError + if not community_locker.functions.timeLimitPerMessage(CommunityLockerGenerator.MAINNET_HASH).call() == CommunityLockerGenerator.DEFAULT_TIME_LIMIT_SEC: raise AssertionError From 2761e9b86f030424367089debcb8bf32808bbb97 Mon Sep 17 00:00:00 2001 From: Artem Payvin Date: Fri, 26 Aug 2022 15:13:35 +0100 Subject: [PATCH 04/12] Fix predeployed generator for community locker --- .../src/ima_predeployed/contracts/community_locker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/predeployed/src/ima_predeployed/contracts/community_locker.py b/proxy/predeployed/src/ima_predeployed/contracts/community_locker.py index e0bcf901f..9dff37b2c 100644 --- a/proxy/predeployed/src/ima_predeployed/contracts/community_locker.py +++ b/proxy/predeployed/src/ima_predeployed/contracts/community_locker.py @@ -82,7 +82,7 @@ def generate_storage(cls, **kwargs) -> Dict[str, str]: cls._write_address(storage, cls.COMMUNITY_POOL_SLOT, community_pool_address) cls._write_bytes32(storage, cls.SCHAIN_HASH_SLOT, Web3.solidityKeccak(['string'], [schain_name])) - time_limit_per_message_slot = Generator.calculate_mapping_value_slot( + time_limit_per_message_slot = AccessControlEnumerableGenerator.calculate_mapping_value_slot( cls.TIME_LIMIT_PER_MESSAGE_SLOT, cls.MAINNET_HASH, 'bytes32') cls._write_uint256(storage, time_limit_per_message_slot, cls.DEFAULT_TIME_LIMIT_SEC) From 1f616ee184661544cd5d3cb1a89174b76f01b94a Mon Sep 17 00:00:00 2001 From: Artem Payvin Date: Fri, 26 Aug 2022 16:12:16 +0100 Subject: [PATCH 05/12] Add agent support and test support --- agent/cli.js | 5 +++ agent/main.js | 4 ++- npms/skale-ima/index.js | 2 ++ proxy/scripts/index.js | 78 ++++++++++++++++++++++++++++++++++++++++ test/agent-test.js | 2 ++ test/tools/blockchain.py | 2 +- 6 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 proxy/scripts/index.js diff --git a/agent/cli.js b/agent/cli.js index 7ac2729d0..93bf784da 100644 --- a/agent/cli.js +++ b/agent/cli.js @@ -483,6 +483,11 @@ function parse( joExternalHandlers, argv ) { imaState.strChainName_s_chain = joArg.value; continue; } + if( joArg.name == "id-origin-chain" ) { + owaspUtils.verifyArgumentWithNonEmptyValue( joArg ); + imaState.strChainName_origin_chain = joArg.value; + continue; + } if( joArg.name == "id-t-chain" ) { owaspUtils.verifyArgumentWithNonEmptyValue( joArg ); imaState.strChainName_t_chain = joArg.value; diff --git a/agent/main.js b/agent/main.js index 0b37c7ea9..76ff680e0 100644 --- a/agent/main.js +++ b/agent/main.js @@ -120,6 +120,7 @@ global.imaState = { "strChainName_main_net": ( process.env.CHAIN_NAME_ETHEREUM || "Mainnet" ).toString().trim(), "strChainName_s_chain": ( process.env.CHAIN_NAME_SCHAIN || "id-S-chain" ).toString().trim(), + "strChainName_origin_chain": ( process.env.CHAIN_NAME_SCHAIN_ORIGIN || "Mainnet" ).toString().trim(), "strChainName_t_chain": ( process.env.CHAIN_NAME_SCHAIN_TARGET || "id-T-chain" ).toString().trim(), "cid_main_net": owaspUtils.toInteger( process.env.CID_ETHEREUM ) || -4, "cid_s_chain": owaspUtils.toInteger( process.env.CID_SCHAIN ) || -4, @@ -1652,7 +1653,7 @@ if( haveReimbursementCommands ) { } if( imaState.nReimbursementRange >= 0 ) { imaState.arrActions.push( { - "name": "Gas Reimbursement - Set Minimal time interval from S2M transfers", + "name": "Gas Reimbursement - Set Minimal time interval from S2M and S2S transfers", "fn": async function() { await IMA.reimbursement_set_range( imaState.w3_s_chain, @@ -1661,6 +1662,7 @@ if( imaState.nReimbursementRange >= 0 ) { imaState.strChainName_s_chain, imaState.cid_s_chain, imaState.tc_s_chain, + imaState.strChainName_origin_chain, imaState.nReimbursementRange ); return true; diff --git a/npms/skale-ima/index.js b/npms/skale-ima/index.js index 308013118..38c76b818 100644 --- a/npms/skale-ima/index.js +++ b/npms/skale-ima/index.js @@ -2089,6 +2089,7 @@ async function reimbursement_set_range( strChainName_s_chain, cid_s_chain, tc_s_chain, + strChainName_origin_chain, nReimbursementRange ) { const details = log.createMemoryStream(); @@ -2108,6 +2109,7 @@ async function reimbursement_set_range( // const methodWithArguments = jo_community_locker.methods.setTimeLimitPerMessage( // call params, last is destination account on S-chain + strChainName_origin_chain, "0x" + w3_s_chain.utils.toBN( nReimbursementRange ).toString( 16 ) ); const dataTx = methodWithArguments.encodeABI(); // the encoded ABI of the method diff --git a/proxy/scripts/index.js b/proxy/scripts/index.js new file mode 100644 index 000000000..786e9740f --- /dev/null +++ b/proxy/scripts/index.js @@ -0,0 +1,78 @@ +require( "dotenv" ).config(); +const endpoint = process.env.ENDPOINT; +const privateKey = process.env.PRIVATE_KEY; +const addressG = process.env.ADDRESS; +const ethereumjs_tx = require( "ethereumjs-tx" ); +const crypto = require("crypto") + +const Web3 = require( "web3" ); + +const web3 = new Web3( new Web3.providers.HttpProvider( endpoint ) ); + +const DIFFICULTY = new web3.utils.BN( 1 ); + +async function safe_send_signed_transaction( serializedTx ) { + const strTX = "0x" + serializedTx.toString( "hex" ); // strTX is string starting from "0x" + let joReceipt = null; + let bHaveReceipt = false; + try { + joReceipt = await web3.eth.sendSignedTransaction( strTX ); + bHaveReceipt = ( joReceipt != null ); + } catch ( err ) { + const s = strLogPrefix + cc.fatal( "WARNING:" ) + cc.warning( " first attempt to send signed transaction failure during : " ) + cc.sunny( err ) + "\n"; + } + if( !bHaveReceipt ) { + try { + joReceipt = await web3.eth.sendSignedTransaction( strTX ); + } catch ( err ) { + const s = strLogPrefix + cc.fatal( "CRITICAL ERROR:" ) + cc.error( " second attempt to send signed transaction failure during : " ) + cc.error( err ) + "\n"; + throw err; + } + } + return joReceipt; +} + +function mineFreeGas( gasAmount, address, nonce ) { + console.log( "Mining free gas: ", gasAmount ); + const nonceHash = new web3.utils.BN( web3.utils.soliditySha3( nonce ).slice( 2 ), 16 ); + const addressHash = new web3.utils.BN( web3.utils.soliditySha3( address ).slice( 2 ), 16 ); + const nonceAddressXOR = nonceHash.xor( addressHash ); + const maxNumber = new web3.utils.BN( 2 ).pow( new web3.utils.BN( 256 ) ).sub( new web3.utils.BN( 1 ) ); + const divConstant = maxNumber.div( DIFFICULTY ); + let candidate; + while( true ) { + candidate = new web3.utils.BN( crypto.randomBytes( 32 ).toString( "hex" ), 16 ); + const candidateHash = new web3.utils.BN( web3.utils.soliditySha3( candidate ).slice( 2 ), 16 ); + const resultHash = nonceAddressXOR.xor( candidateHash ); + const externalGas = divConstant.div( resultHash ).toNumber(); + console.log(externalGas); + if( externalGas >= gasAmount ) { + break; + } + } + return candidate.toString(); +} + +async function sendTX() { + // const dataTx = "0x68eb2022000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000000000001"; + const dataTx = "0x"; + const tcnt = await web3.eth.getTransactionCount(web3.utils.toChecksumAddress(addressG)); + console.log("OK"); + const minedGasPrice = mineFreeGas( 100000, addressG, tcnt ); + const rawTx = { + chainId: "0x79f99296", + nonce: tcnt, + gasPrice: minedGasPrice, + // gasLimit: estimatedGas, + gas: 100000, // gas is optional here + to: "0xD2aAA00500000000000000000000000000000000", // contract address + data: dataTx + }; + const tx = new ethereumjs_tx( rawTx ); + const key = Buffer.from( privateKey, "hex" ); // convert private key to buffer + tx.sign( key ); + const serializedTx = tx.serialize(); + await safe_send_signed_transaction(serializedTx); +} + +sendTX(); \ No newline at end of file diff --git a/test/agent-test.js b/test/agent-test.js index da922465a..2ff55e927 100644 --- a/test/agent-test.js +++ b/test/agent-test.js @@ -117,6 +117,7 @@ global.imaState = { "strChainName_main_net": ( process.env.CHAIN_NAME_ETHEREUM || "Mainnet" ).toString().trim(), "strChainName_s_chain": ( process.env.CHAIN_NAME_SCHAIN || "Bob" ).toString().trim(), + "strChainName_origin_chain": ( process.env.CHAIN_NAME_ETHEREUM || "Mainnet" ).toString().trim(), "strChainName_t_chain": ( process.env.CHAIN_NAME_SCHAIN_TARGET || "Alice" ).toString().trim(), "cid_main_net": owaspUtils.toInteger( process.env.CID_ETHEREUM ) || -4, "cid_s_chain": owaspUtils.toInteger( process.env.CID_SCHAIN ) || -4, @@ -661,6 +662,7 @@ describe( "CLI", function() { "--url-s-chain=" + imaState.strURL_s_chain, "--id-main-net=" + imaState.strChainName_main_net, "--id-s-chain=" + imaState.strChainName_s_chain, + "--id-origin-chain=" + imaState.strChainName_origin_chain, "--cid-main-net=" + imaState.cid_main_net, "--cid-s-chain=" + imaState.cid_s_chain, "--address-main-net=" + imaState.joAccount_main_net.address(), diff --git a/test/tools/blockchain.py b/test/tools/blockchain.py index bb00268aa..16f7c2147 100644 --- a/test/tools/blockchain.py +++ b/test/tools/blockchain.py @@ -250,7 +250,7 @@ def recharge_user_wallet(self, from_key, schainName, amount_wei): def set_time_limit_per_message(self, from_key, time_limit): sender_address = self.key_to_address(from_key) community_locker = self._get_contract_on_schain('community_locker') - time_limit_abi = community_locker.encodeABI(fn_name="setTimeLimitPerMessage", args=[time_limit]) + time_limit_abi = community_locker.encodeABI(fn_name="setTimeLimitPerMessage", args=["Mainnet", time_limit]) signed_txn = self.web3_schain.eth.account.signTransaction(dict( nonce=self.web3_schain.eth.getTransactionCount(sender_address), gasPrice=self.web3_schain.eth.gasPrice, From 97ed348fcbb00d7b88f5173bf6f1e55322d6a6d6 Mon Sep 17 00:00:00 2001 From: Artem Payvin Date: Fri, 26 Aug 2022 18:21:33 +0100 Subject: [PATCH 06/12] Add tests and add check to S2S --- .../TokenManagers/TokenManagerERC1155.sol | 2 + .../TokenManagers/TokenManagerERC20.sol | 1 + .../TokenManagers/TokenManagerERC721.sol | 1 + proxy/test/CommunityLocker.ts | 12 ++ proxy/test/TokenManagerERC1155.ts | 175 ++++++++++++++++++ proxy/test/TokenManagerERC20.ts | 102 ++++++++++ proxy/test/TokenManagerERC721.ts | 101 ++++++++++ proxy/test/TokenManagerERC721WithMetadata.ts | 101 ++++++++++ 8 files changed, 495 insertions(+) diff --git a/proxy/contracts/schain/TokenManagers/TokenManagerERC1155.sol b/proxy/contracts/schain/TokenManagers/TokenManagerERC1155.sol index c0a7caa41..ae7e62ae8 100644 --- a/proxy/contracts/schain/TokenManagers/TokenManagerERC1155.sol +++ b/proxy/contracts/schain/TokenManagers/TokenManagerERC1155.sol @@ -149,6 +149,7 @@ contract TokenManagerERC1155 is rightTransaction(targetSchainName, msg.sender) { bytes32 targetSchainHash = keccak256(abi.encodePacked(targetSchainName)); + communityLocker.checkAllowedToSendMessage(targetSchainHash, msg.sender); _exit(targetSchainHash, tokenManagers[targetSchainHash], contractOnMainnet, msg.sender, id, amount); } @@ -169,6 +170,7 @@ contract TokenManagerERC1155 is rightTransaction(targetSchainName, msg.sender) { bytes32 targetSchainHash = keccak256(abi.encodePacked(targetSchainName)); + communityLocker.checkAllowedToSendMessage(targetSchainHash, msg.sender); _exitBatch(targetSchainHash, tokenManagers[targetSchainHash], contractOnMainnet, msg.sender, ids, amounts); } diff --git a/proxy/contracts/schain/TokenManagers/TokenManagerERC20.sol b/proxy/contracts/schain/TokenManagers/TokenManagerERC20.sol index 4d05af1af..a502b90fd 100644 --- a/proxy/contracts/schain/TokenManagers/TokenManagerERC20.sol +++ b/proxy/contracts/schain/TokenManagers/TokenManagerERC20.sol @@ -119,6 +119,7 @@ contract TokenManagerERC20 is TokenManager, ITokenManagerERC20 { rightTransaction(targetSchainName, msg.sender) { bytes32 targetSchainHash = keccak256(abi.encodePacked(targetSchainName)); + communityLocker.checkAllowedToSendMessage(targetSchainHash, msg.sender); _exit(targetSchainHash, tokenManagers[targetSchainHash], contractOnMainnet, msg.sender, amount); } diff --git a/proxy/contracts/schain/TokenManagers/TokenManagerERC721.sol b/proxy/contracts/schain/TokenManagers/TokenManagerERC721.sol index d4067fe4f..34c6fe4ee 100644 --- a/proxy/contracts/schain/TokenManagers/TokenManagerERC721.sol +++ b/proxy/contracts/schain/TokenManagers/TokenManagerERC721.sol @@ -118,6 +118,7 @@ contract TokenManagerERC721 is TokenManager, ITokenManagerERC721 { rightTransaction(targetSchainName, msg.sender) { bytes32 targetSchainHash = keccak256(abi.encodePacked(targetSchainName)); + communityLocker.checkAllowedToSendMessage(targetSchainHash, msg.sender); _exit(targetSchainHash, tokenManagers[targetSchainHash], contractOnMainnet, msg.sender, tokenId); } diff --git a/proxy/test/CommunityLocker.ts b/proxy/test/CommunityLocker.ts index d9799a49b..1fd3682e3 100644 --- a/proxy/test/CommunityLocker.ts +++ b/proxy/test/CommunityLocker.ts @@ -107,6 +107,7 @@ describe("CommunityLocker", () => { }); it("should set time limit per message", async () => { + expect(BigNumber.from(await communityLocker.timeLimitPerMessage(mainnetHash)).toString()).to.be.equal(BigNumber.from(300).toString()); await communityLocker.setTimeLimitPerMessage("Mainnet", 0) .should.be.eventually.rejectedWith("Not enough permissions to set constant"); await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); @@ -114,6 +115,17 @@ describe("CommunityLocker", () => { expect(BigNumber.from(await communityLocker.timeLimitPerMessage(mainnetHash)).toString()).to.be.equal(BigNumber.from(0).toString()); }); + it("should set time limit per message for schain", async () => { + const schainName = "Schain Sierra"; + const schainHash = ethers.utils.id(schainName); + expect(BigNumber.from(await communityLocker.timeLimitPerMessage(schainHash)).toString()).to.be.equal(BigNumber.from(0).toString()); + await communityLocker.setTimeLimitPerMessage(schainName, 0) + .should.be.eventually.rejectedWith("Not enough permissions to set constant"); + await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); + await communityLocker.setTimeLimitPerMessage(schainName, 1200); + expect(BigNumber.from(await communityLocker.timeLimitPerMessage(schainHash)).toString()).to.be.equal(BigNumber.from(1200).toString()); + }); + it("should set gasprice", async () => { const newBLSSignature: [BigNumber, BigNumber] = [ BigNumber.from("0x2dedd4eaeac95881fbcaa4146f95a438494545c607bd57d560aa1d13d2679db8"), diff --git a/proxy/test/TokenManagerERC1155.ts b/proxy/test/TokenManagerERC1155.ts index 18e46ca82..8ac48c9d5 100644 --- a/proxy/test/TokenManagerERC1155.ts +++ b/proxy/test/TokenManagerERC1155.ts @@ -35,6 +35,7 @@ import { } from "../typechain"; import { randomString, stringValue } from "./utils/helper"; +import { skipTime } from "./utils/time"; chai.should(); chai.use((chaiAsPromised as any)); @@ -248,6 +249,93 @@ describe("TokenManagerERC1155", () => { outgoingMessagesCounter.should.be.deep.equal(BigNumber.from(1)); }); + it("should reject `transferToSchainERC1155` when executing earlier then allowed", async () => { + // add connected chain: + await messageProxyForSchain.connect(deployer).grantRole(await messageProxyForSchain.CHAIN_CONNECTOR_ROLE(), deployer.address); + await messageProxyForSchain.connect(deployer).addConnectedChain(newSchainName); + + await erc1155OnOriginChain.connect(deployer).mint(user.address, id, 5, "0x"); + await erc1155OnOriginChain.connect(user).setApprovalForAll(tokenManagerERC1155.address, true); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1) + .should.be.eventually.rejectedWith("Incorrect Token Manager address"); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155("Mainnet", erc1155OnOriginChain.address, id, 1) + .should.be.eventually.rejectedWith("This function is not for transferring to Mainnet"); + + await tokenManagerERC1155.addTokenManager(newSchainName, tokenManagerERC11552.address); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(1)); + + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 100); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await skipTime(90); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await skipTime(20); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(3)); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 0); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 100); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); + + await skipTime(110); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(5)); + }); + it("should invoke `transferToSchainERC1155` and receive tokens without mistakes", async () => { // add connected chain: await messageProxyForSchain.connect(deployer).grantRole(await messageProxyForSchain.CHAIN_CONNECTOR_ROLE(), deployer.address); @@ -1203,6 +1291,93 @@ describe("TokenManagerERC1155", () => { outgoingMessagesCounter.should.be.deep.equal(BigNumber.from(1)); }); + it("should reject `transferToSchainERC1155` when executing earlier then allowed", async () => { + // add connected chain: + await messageProxyForSchain.connect(deployer).grantRole(await messageProxyForSchain.CHAIN_CONNECTOR_ROLE(), deployer.address); + await messageProxyForSchain.connect(deployer).addConnectedChain(newSchainName); + + await erc1155OnOriginChain.connect(deployer).mintBatch(user.address, ids, [5, 5, 5, 5], "0x"); + await erc1155OnOriginChain.connect(user).setApprovalForAll(tokenManagerERC1155.address, true); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]) + .should.be.eventually.rejectedWith("Incorrect Token Manager address"); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155Batch("Mainnet", erc1155OnOriginChain.address, ids, [1, 1, 1, 1]) + .should.be.eventually.rejectedWith("This function is not for transferring to Mainnet"); + + await tokenManagerERC1155.addTokenManager(newSchainName, tokenManagerERC11552.address); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(1)); + + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 100); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await skipTime(90); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await skipTime(20); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(3)); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 0); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 100); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); + + await skipTime(110); + + await tokenManagerERC1155 + .connect(user) + .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(5)); + }); + it("should invoke `transferToSchainERC1155` and receive tokens without mistakes", async () => { // add connected chain: await messageProxyForSchain.connect(deployer).grantRole(await messageProxyForSchain.CHAIN_CONNECTOR_ROLE(), deployer.address); diff --git a/proxy/test/TokenManagerERC20.ts b/proxy/test/TokenManagerERC20.ts index 2e327d982..f5f593677 100644 --- a/proxy/test/TokenManagerERC20.ts +++ b/proxy/test/TokenManagerERC20.ts @@ -52,6 +52,7 @@ import { BigNumber } from "ethers"; import { assert, expect } from "chai"; import { deployKeyStorageMock } from "./utils/deploy/test/keyStorageMock"; +import { skipTime } from "./utils/time"; describe("TokenManagerERC20", () => { let deployer: SignerWithAddress; @@ -304,6 +305,107 @@ describe("TokenManagerERC20", () => { outgoingMessagesCounter.should.be.deep.equal(BigNumber.from(1)); }); + it("should reject `transferToSchainERC20` when executing earlier then allowed", async () => { + const amount = "20000000000000000"; + await messageProxyForSchain.registerExtraContract(newSchainName, tokenManagerErc20.address); + + // add connected chain: + await messageProxyForSchain.connect(deployer).grantRole(await messageProxyForSchain.CHAIN_CONNECTOR_ROLE(), deployer.address); + await messageProxyForSchain.connect(deployer).addConnectedChain(newSchainName); + + await erc20OnOriginChain.connect(deployer).mint(user.address, amount); + await erc20OnOriginChain.connect(user).approve(tokenManagerErc20.address, amount); + + await tokenManagerErc20 + .connect(user) + .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount) + .should.be.eventually.rejectedWith("Incorrect Token Manager address"); + + await tokenManagerErc20 + .connect(user) + .transferToSchainERC20("Mainnet", erc20OnOriginChain.address, amount) + .should.be.eventually.rejectedWith("This function is not for transferring to Mainnet"); + + await tokenManagerErc20.addTokenManager(newSchainName, tokenManagerErc202.address); + + await tokenManagerErc20 + .connect(user) + .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(1)); + + await erc20OnOriginChain.connect(deployer).mint(user.address, amount); + await erc20OnOriginChain.connect(user).approve(tokenManagerErc20.address, amount); + + await tokenManagerErc20 + .connect(user) + .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 100); + + await erc20OnOriginChain.connect(deployer).mint(user.address, amount); + await erc20OnOriginChain.connect(user).approve(tokenManagerErc20.address, amount); + + await tokenManagerErc20 + .connect(user) + .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await skipTime(90); + + await tokenManagerErc20 + .connect(user) + .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await skipTime(20); + + await tokenManagerErc20 + .connect(user) + .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(3)); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 0); + + await erc20OnOriginChain.connect(deployer).mint(user.address, amount); + await erc20OnOriginChain.connect(user).approve(tokenManagerErc20.address, amount); + + await tokenManagerErc20 + .connect(user) + .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 100); + + await erc20OnOriginChain.connect(deployer).mint(user.address, amount); + await erc20OnOriginChain.connect(user).approve(tokenManagerErc20.address, amount); + + await tokenManagerErc20 + .connect(user) + .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); + + await skipTime(110); + + await tokenManagerErc20 + .connect(user) + .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(5)); + }); + it("should invoke `transferToSchainERC20` and receive tokens without mistakes", async () => { const amount = "20000000000000000"; await messageProxyForSchain.registerExtraContract(newSchainName, tokenManagerErc20.address); diff --git a/proxy/test/TokenManagerERC721.ts b/proxy/test/TokenManagerERC721.ts index f08ec0dec..40fb3b704 100644 --- a/proxy/test/TokenManagerERC721.ts +++ b/proxy/test/TokenManagerERC721.ts @@ -35,6 +35,7 @@ import { } from "../typechain"; import { randomString, stringValue } from "./utils/helper"; +import { skipTime } from "./utils/time"; chai.should(); chai.use((chaiAsPromised as any)); @@ -259,6 +260,106 @@ describe("TokenManagerERC721", () => { outgoingMessagesCounter.should.be.deep.equal(BigNumber.from(1)); }); + it("should reject `transferToSchainERC721` when executing earlier then allowed", async () => { + await messageProxyForSchain.registerExtraContract(newSchainName, tokenManagerERC721.address); + + // add connected chain: + await messageProxyForSchain.connect(deployer).grantRole(await messageProxyForSchain.CHAIN_CONNECTOR_ROLE(), deployer.address); + await messageProxyForSchain.connect(deployer).addConnectedChain(newSchainName); + + await erc721OnOriginChain.connect(deployer).mint(user.address, 1); + await erc721OnOriginChain.connect(user).approve(tokenManagerERC721.address, 1); + + await tokenManagerERC721 + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 1) + .should.be.eventually.rejectedWith("Incorrect Token Manager address"); + + await tokenManagerERC721 + .connect(user) + .transferToSchainERC721("Mainnet", erc721OnOriginChain.address, 1) + .should.be.eventually.rejectedWith("This function is not for transferring to Mainnet"); + + await tokenManagerERC721.addTokenManager(newSchainName, tokenManagerERC7212.address); + + await tokenManagerERC721 + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 1); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(1)); + + await erc721OnOriginChain.connect(deployer).mint(user.address, 2); + await erc721OnOriginChain.connect(user).approve(tokenManagerERC721.address, 2); + + await tokenManagerERC721 + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 2); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 100); + + await erc721OnOriginChain.connect(deployer).mint(user.address, 3); + await erc721OnOriginChain.connect(user).approve(tokenManagerERC721.address, 3); + + await tokenManagerERC721 + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 3) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await skipTime(90); + + await tokenManagerERC721 + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 3) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await skipTime(20); + + await tokenManagerERC721 + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 3); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(3)); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 0); + + await erc721OnOriginChain.connect(deployer).mint(user.address, 4); + await erc721OnOriginChain.connect(user).approve(tokenManagerERC721.address, 4); + + await tokenManagerERC721 + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 4); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 100); + + await erc721OnOriginChain.connect(deployer).mint(user.address, 5); + await erc721OnOriginChain.connect(user).approve(tokenManagerERC721.address, 5); + + await tokenManagerERC721 + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 5) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); + + await skipTime(110); + + await tokenManagerERC721 + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 5); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(5)); + }); + it("should invoke `transferToSchainERC721` and receive tokens without mistakes", async () => { await messageProxyForSchain.registerExtraContract(newSchainName, tokenManagerERC721.address); diff --git a/proxy/test/TokenManagerERC721WithMetadata.ts b/proxy/test/TokenManagerERC721WithMetadata.ts index 6da0a4d4b..a159ad68c 100644 --- a/proxy/test/TokenManagerERC721WithMetadata.ts +++ b/proxy/test/TokenManagerERC721WithMetadata.ts @@ -35,6 +35,7 @@ import { } from "../typechain"; import { randomString, stringValue } from "./utils/helper"; +import { skipTime } from "./utils/time"; chai.should(); chai.use((chaiAsPromised as any)); @@ -265,6 +266,106 @@ describe("TokenManagerERC721WithMetadata", () => { outgoingMessagesCounter.should.be.deep.equal(BigNumber.from(1)); }); + it("should reject `transferToSchainERC721` when executing earlier then allowed", async () => { + await messageProxyForSchain.registerExtraContract(newSchainName, tokenManagerERC721WithMetadata.address); + + // add connected chain: + await messageProxyForSchain.connect(deployer).grantRole(await messageProxyForSchain.CHAIN_CONNECTOR_ROLE(), deployer.address); + await messageProxyForSchain.connect(deployer).addConnectedChain(newSchainName); + + await erc721OnOriginChain.connect(deployer).mint(user.address, 1); + await erc721OnOriginChain.connect(user).approve(tokenManagerERC721WithMetadata.address, 1); + + await tokenManagerERC721WithMetadata + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 1) + .should.be.eventually.rejectedWith("Incorrect Token Manager address"); + + await tokenManagerERC721WithMetadata + .connect(user) + .transferToSchainERC721("Mainnet", erc721OnOriginChain.address, 1) + .should.be.eventually.rejectedWith("This function is not for transferring to Mainnet"); + + await tokenManagerERC721WithMetadata.addTokenManager(newSchainName, tokenManagerERC721WithMetadata2.address); + + await tokenManagerERC721WithMetadata + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 1); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(1)); + + await erc721OnOriginChain.connect(deployer).mint(user.address, 2); + await erc721OnOriginChain.connect(user).approve(tokenManagerERC721WithMetadata.address, 2); + + await tokenManagerERC721WithMetadata + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 2); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 100); + + await erc721OnOriginChain.connect(deployer).mint(user.address, 3); + await erc721OnOriginChain.connect(user).approve(tokenManagerERC721WithMetadata.address, 3); + + await tokenManagerERC721WithMetadata + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 3) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await skipTime(90); + + await tokenManagerERC721WithMetadata + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 3) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); + + await skipTime(20); + + await tokenManagerERC721WithMetadata + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 3); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(3)); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 0); + + await erc721OnOriginChain.connect(deployer).mint(user.address, 4); + await erc721OnOriginChain.connect(user).approve(tokenManagerERC721WithMetadata.address, 4); + + await tokenManagerERC721WithMetadata + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 4); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); + + await communityLocker.setTimeLimitPerMessage(newSchainName, 100); + + await erc721OnOriginChain.connect(deployer).mint(user.address, 5); + await erc721OnOriginChain.connect(user).approve(tokenManagerERC721WithMetadata.address, 5); + + await tokenManagerERC721WithMetadata + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 5) + .should.be.eventually.rejectedWith("Trying to send messages too often"); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); + + await skipTime(110); + + await tokenManagerERC721WithMetadata + .connect(user) + .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 5); + + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(5)); + }); + it("should invoke `transferToSchainERC721` and receive tokens without mistakes", async () => { await messageProxyForSchain.registerExtraContract(newSchainName, tokenManagerERC721WithMetadata.address); From 4b7a73c5210839536fc6026d19c218f36924a99e Mon Sep 17 00:00:00 2001 From: Artem Payvin Date: Fri, 26 Aug 2022 18:29:02 +0100 Subject: [PATCH 07/12] Fix and extend tests --- proxy/test/CommunityLocker.ts | 10 ++++++---- proxy/test/TokenManagerERC1155.ts | 4 ++-- proxy/test/TokenManagerERC20.ts | 2 +- proxy/test/TokenManagerERC721.ts | 2 +- proxy/test/TokenManagerERC721WithMetadata.ts | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/proxy/test/CommunityLocker.ts b/proxy/test/CommunityLocker.ts index 1fd3682e3..cd3227061 100644 --- a/proxy/test/CommunityLocker.ts +++ b/proxy/test/CommunityLocker.ts @@ -116,13 +116,15 @@ describe("CommunityLocker", () => { }); it("should set time limit per message for schain", async () => { - const schainName = "Schain Sierra"; - const schainHash = ethers.utils.id(schainName); + const anotherSchainName = "Schain Sierra"; + const schainHash = ethers.utils.id(anotherSchainName); expect(BigNumber.from(await communityLocker.timeLimitPerMessage(schainHash)).toString()).to.be.equal(BigNumber.from(0).toString()); - await communityLocker.setTimeLimitPerMessage(schainName, 0) + await communityLocker.setTimeLimitPerMessage(anotherSchainName, 1200) .should.be.eventually.rejectedWith("Not enough permissions to set constant"); await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); - await communityLocker.setTimeLimitPerMessage(schainName, 1200); + await communityLocker.setTimeLimitPerMessage(schainName, 1200) + .should.be.eventually.rejectedWith("Incorrect chain"); + await communityLocker.setTimeLimitPerMessage(anotherSchainName, 1200); expect(BigNumber.from(await communityLocker.timeLimitPerMessage(schainHash)).toString()).to.be.equal(BigNumber.from(1200).toString()); }); diff --git a/proxy/test/TokenManagerERC1155.ts b/proxy/test/TokenManagerERC1155.ts index 8ac48c9d5..d727348dc 100644 --- a/proxy/test/TokenManagerERC1155.ts +++ b/proxy/test/TokenManagerERC1155.ts @@ -307,7 +307,7 @@ describe("TokenManagerERC1155", () => { await tokenManagerERC1155 .connect(user) .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1); - + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(3)); await communityLocker.setTimeLimitPerMessage(newSchainName, 0); @@ -1349,7 +1349,7 @@ describe("TokenManagerERC1155", () => { await tokenManagerERC1155 .connect(user) .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]); - + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(3)); await communityLocker.setTimeLimitPerMessage(newSchainName, 0); diff --git a/proxy/test/TokenManagerERC20.ts b/proxy/test/TokenManagerERC20.ts index f5f593677..ea6bdbe4f 100644 --- a/proxy/test/TokenManagerERC20.ts +++ b/proxy/test/TokenManagerERC20.ts @@ -371,7 +371,7 @@ describe("TokenManagerERC20", () => { await tokenManagerErc20 .connect(user) .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount); - + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(3)); await communityLocker.setTimeLimitPerMessage(newSchainName, 0); diff --git a/proxy/test/TokenManagerERC721.ts b/proxy/test/TokenManagerERC721.ts index 40fb3b704..14f864355 100644 --- a/proxy/test/TokenManagerERC721.ts +++ b/proxy/test/TokenManagerERC721.ts @@ -325,7 +325,7 @@ describe("TokenManagerERC721", () => { await tokenManagerERC721 .connect(user) .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 3); - + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(3)); await communityLocker.setTimeLimitPerMessage(newSchainName, 0); diff --git a/proxy/test/TokenManagerERC721WithMetadata.ts b/proxy/test/TokenManagerERC721WithMetadata.ts index a159ad68c..8242d4376 100644 --- a/proxy/test/TokenManagerERC721WithMetadata.ts +++ b/proxy/test/TokenManagerERC721WithMetadata.ts @@ -331,7 +331,7 @@ describe("TokenManagerERC721WithMetadata", () => { await tokenManagerERC721WithMetadata .connect(user) .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 3); - + BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(3)); await communityLocker.setTimeLimitPerMessage(newSchainName, 0); From 1174a3aabc46b7b0dd921f0c43f7a86be6fef686 Mon Sep 17 00:00:00 2001 From: Artem Payvin Date: Wed, 14 Sep 2022 11:39:51 +0100 Subject: [PATCH 08/12] Fixed suggested changes --- proxy/contracts/schain/CommunityLocker.sol | 21 +++++++++++++++-- proxy/migrations/upgradeSchain.ts | 26 ++++++++++++++++++++-- proxy/slither.config.json | 2 +- proxy/test/CommunityLocker.ts | 16 ++++++------- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/proxy/contracts/schain/CommunityLocker.sol b/proxy/contracts/schain/CommunityLocker.sol index fea6bc841..9c5812eaf 100644 --- a/proxy/contracts/schain/CommunityLocker.sol +++ b/proxy/contracts/schain/CommunityLocker.sol @@ -79,7 +79,7 @@ contract CommunityLocker is ICommunityLockerInitializer, AccessControlEnumerable * @dev Amount of seconds after message sending * when next message cannot be sent. */ - // slither-disable-next-line uninitialized-state + // slither-disable-next-line constable-states uint private _deprecatedTimeLimitPerMessage; /** @@ -94,12 +94,28 @@ contract CommunityLocker is ICommunityLockerInitializer, AccessControlEnumerable // user address => timestamp of last message mapping(address => uint) public lastMessageTimeStamp; + /** + * @dev mainnet gas price(baseFee) value + */ uint256 public mainnetGasPrice; + /** + * @dev Timestamp of previous set of mainnet gas price + */ uint256 public gasPriceTimestamp; + /** + * @dev Amount of seconds after message sending + * when next message cannot be sent. + */ + // schainHash => time limit mapping(bytes32 => uint) public timeLimitPerMessage; + /** + * @dev Timestamp of previous sent message by user during + * schain to schain transfers + */ + // schainHash => user => timestamp mapping(bytes32 => mapping(address => uint)) public lastMessageTimeStampToSchain; /** @@ -135,7 +151,7 @@ contract CommunityLocker is ICommunityLockerInitializer, AccessControlEnumerable } require( lastTimestamp + timeLimitPerMessage[chainHash] < block.timestamp, - "Trying to send messages too often" + "Exceeded message rate limit" ); _; } @@ -282,5 +298,6 @@ contract CommunityLocker is ICommunityLockerInitializer, AccessControlEnumerable require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Incorrect sender"); // slither-disable-next-line uninitialized-state timeLimitPerMessage[MAINNET_HASH] = _deprecatedTimeLimitPerMessage; + delete _deprecatedTimeLimitPerMessage; } } diff --git a/proxy/migrations/upgradeSchain.ts b/proxy/migrations/upgradeSchain.ts index f54d9a7f7..3f4438a42 100644 --- a/proxy/migrations/upgradeSchain.ts +++ b/proxy/migrations/upgradeSchain.ts @@ -1,6 +1,11 @@ -import { contracts } from "./deploySchain"; +import { getManifestAdmin } from "@openzeppelin/hardhat-upgrades/dist/admin"; +import { CommunityLocker } from "../typechain/CommunityLocker"; +import { contracts, getContractKeyInAbiFile } from "./deploySchain"; import { manifestSetup } from "./generateManifest"; +import { encodeTransaction } from "./tools/multiSend"; import { upgrade } from "./upgrade"; +import chalk from "chalk"; +import { ethers } from "hardhat"; function stringValue(value: string | undefined) { if (value) { @@ -20,7 +25,24 @@ async function main() { // deploying of new contracts }, async( safeTransactions, abi ) => { - // initialization + const communityLockerName = "CommunityLocker"; + const communityLockerFactory = await ethers.getContractFactory(communityLockerName); + const communityLockerAddress = abi[getContractKeyInAbiFile(communityLockerName) + "_address"]; + let communityLocker; + if (communityLockerAddress) { + communityLocker = communityLockerFactory.attach(communityLockerAddress) as CommunityLocker; + console.log(chalk.yellow("Prepare transaction to initialize timestamp")); + safeTransactions.push(encodeTransaction( + 0, + communityLockerAddress, + 0, + communityLocker.interface.encodeFunctionData("initializeTimestamp") + )); + } else { + console.log(chalk.red("CommunityLocker was not found!")); + console.log(chalk.red("Check your abi!!!")); + process.exit(1); + } }, "proxySchain" ); diff --git a/proxy/slither.config.json b/proxy/slither.config.json index 8eb8c4111..2ec3eac25 100644 --- a/proxy/slither.config.json +++ b/proxy/slither.config.json @@ -1,4 +1,4 @@ { - "detectors_to_exclude": "uninitialized-local,calls-loop,reentrancy-events,assembly,solc-version,timestamp,unused-return,variable-scope,dead-code,erc20-interface,constable-states", + "detectors_to_exclude": "uninitialized-local,calls-loop,reentrancy-events,assembly,solc-version,timestamp,unused-return,variable-scope,dead-code,erc20-interface", "filter_paths": "test|@openzeppelin/contracts|thirdparty" } \ No newline at end of file diff --git a/proxy/test/CommunityLocker.ts b/proxy/test/CommunityLocker.ts index cd3227061..23d3f62d4 100644 --- a/proxy/test/CommunityLocker.ts +++ b/proxy/test/CommunityLocker.ts @@ -107,25 +107,25 @@ describe("CommunityLocker", () => { }); it("should set time limit per message", async () => { - expect(BigNumber.from(await communityLocker.timeLimitPerMessage(mainnetHash)).toString()).to.be.equal(BigNumber.from(300).toString()); + expect(await communityLocker.timeLimitPerMessage(mainnetHash)).to.be.equal(300); await communityLocker.setTimeLimitPerMessage("Mainnet", 0) .should.be.eventually.rejectedWith("Not enough permissions to set constant"); await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); await communityLocker.setTimeLimitPerMessage("Mainnet", 0); - expect(BigNumber.from(await communityLocker.timeLimitPerMessage(mainnetHash)).toString()).to.be.equal(BigNumber.from(0).toString()); + expect(await communityLocker.timeLimitPerMessage(mainnetHash)).to.be.equal(0); }); it("should set time limit per message for schain", async () => { const anotherSchainName = "Schain Sierra"; const schainHash = ethers.utils.id(anotherSchainName); - expect(BigNumber.from(await communityLocker.timeLimitPerMessage(schainHash)).toString()).to.be.equal(BigNumber.from(0).toString()); + expect(await communityLocker.timeLimitPerMessage(schainHash)).to.be.equal(0); await communityLocker.setTimeLimitPerMessage(anotherSchainName, 1200) .should.be.eventually.rejectedWith("Not enough permissions to set constant"); await communityLocker.grantRole(await communityLocker.CONSTANT_SETTER_ROLE(), deployer.address); await communityLocker.setTimeLimitPerMessage(schainName, 1200) .should.be.eventually.rejectedWith("Incorrect chain"); await communityLocker.setTimeLimitPerMessage(anotherSchainName, 1200); - expect(BigNumber.from(await communityLocker.timeLimitPerMessage(schainHash)).toString()).to.be.equal(BigNumber.from(1200).toString()); + expect(await communityLocker.timeLimitPerMessage(schainHash)).to.be.equal(1200); }); it("should set gasprice", async () => { @@ -142,8 +142,8 @@ describe("CommunityLocker", () => { const time = await currentTime(); await communityLocker.setGasPrice(100, time + 200, sign).should.be.eventually.rejectedWith("Timestamp should not be in the future"); await communityLocker.setGasPrice(100, time, sign); - expect(BigNumber.from(await communityLocker.mainnetGasPrice()).toString()).to.be.equal(BigNumber.from(100).toString()); - expect(BigNumber.from(await communityLocker.gasPriceTimestamp()).toString()).to.be.equal(BigNumber.from(time).toString()); + expect(await communityLocker.mainnetGasPrice()).to.be.equal(100); + expect(await communityLocker.gasPriceTimestamp()).to.be.equal(time); skipTime(60); @@ -151,7 +151,7 @@ describe("CommunityLocker", () => { await communityLocker.setGasPrice(101, time, sign).should.be.eventually.rejectedWith("Gas price timestamp already updated"); await communityLocker.setGasPrice(101, time + 70, sign).should.be.eventually.rejectedWith("Timestamp should not be in the future"); await communityLocker.setGasPrice(101, time + 40, sign); - expect(BigNumber.from(await communityLocker.mainnetGasPrice()).toString()).to.be.equal(BigNumber.from(101).toString()); - expect(BigNumber.from(await communityLocker.gasPriceTimestamp()).toString()).to.be.equal(BigNumber.from(time + 40).toString()); + expect(await communityLocker.mainnetGasPrice()).to.be.equal(101); + expect(await communityLocker.gasPriceTimestamp()).to.be.equal(time + 40); }); }); From 71a4e6791f8679d1ce419d258290eec5faefd897 Mon Sep 17 00:00:00 2001 From: Artem Payvin Date: Wed, 14 Sep 2022 12:39:59 +0100 Subject: [PATCH 09/12] Rename revert reason in test --- proxy/test/TokenManagerERC1155.ts | 12 ++++++------ proxy/test/TokenManagerERC20.ts | 6 +++--- proxy/test/TokenManagerERC721.ts | 6 +++--- proxy/test/TokenManagerERC721WithMetadata.ts | 6 +++--- proxy/test/TokenManagerEth.ts | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/proxy/test/TokenManagerERC1155.ts b/proxy/test/TokenManagerERC1155.ts index d727348dc..92b3425ae 100644 --- a/proxy/test/TokenManagerERC1155.ts +++ b/proxy/test/TokenManagerERC1155.ts @@ -289,7 +289,7 @@ describe("TokenManagerERC1155", () => { await tokenManagerERC1155 .connect(user) .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); @@ -298,7 +298,7 @@ describe("TokenManagerERC1155", () => { await tokenManagerERC1155 .connect(user) .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); @@ -323,7 +323,7 @@ describe("TokenManagerERC1155", () => { await tokenManagerERC1155 .connect(user) .transferToSchainERC1155(newSchainName, erc1155OnOriginChain.address, id, 1) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); @@ -1331,7 +1331,7 @@ describe("TokenManagerERC1155", () => { await tokenManagerERC1155 .connect(user) .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); @@ -1340,7 +1340,7 @@ describe("TokenManagerERC1155", () => { await tokenManagerERC1155 .connect(user) .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); @@ -1365,7 +1365,7 @@ describe("TokenManagerERC1155", () => { await tokenManagerERC1155 .connect(user) .transferToSchainERC1155Batch(newSchainName, erc1155OnOriginChain.address, ids, [1, 1, 1, 1]) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); diff --git a/proxy/test/TokenManagerERC20.ts b/proxy/test/TokenManagerERC20.ts index ea6bdbe4f..9058e1c3a 100644 --- a/proxy/test/TokenManagerERC20.ts +++ b/proxy/test/TokenManagerERC20.ts @@ -353,7 +353,7 @@ describe("TokenManagerERC20", () => { await tokenManagerErc20 .connect(user) .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); @@ -362,7 +362,7 @@ describe("TokenManagerERC20", () => { await tokenManagerErc20 .connect(user) .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); @@ -393,7 +393,7 @@ describe("TokenManagerERC20", () => { await tokenManagerErc20 .connect(user) .transferToSchainERC20(newSchainName, erc20OnOriginChain.address, amount) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); diff --git a/proxy/test/TokenManagerERC721.ts b/proxy/test/TokenManagerERC721.ts index 14f864355..29190ef7c 100644 --- a/proxy/test/TokenManagerERC721.ts +++ b/proxy/test/TokenManagerERC721.ts @@ -307,7 +307,7 @@ describe("TokenManagerERC721", () => { await tokenManagerERC721 .connect(user) .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 3) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); @@ -316,7 +316,7 @@ describe("TokenManagerERC721", () => { await tokenManagerERC721 .connect(user) .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 3) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); @@ -347,7 +347,7 @@ describe("TokenManagerERC721", () => { await tokenManagerERC721 .connect(user) .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 5) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); diff --git a/proxy/test/TokenManagerERC721WithMetadata.ts b/proxy/test/TokenManagerERC721WithMetadata.ts index 8242d4376..653f8cb94 100644 --- a/proxy/test/TokenManagerERC721WithMetadata.ts +++ b/proxy/test/TokenManagerERC721WithMetadata.ts @@ -313,7 +313,7 @@ describe("TokenManagerERC721WithMetadata", () => { await tokenManagerERC721WithMetadata .connect(user) .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 3) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); @@ -322,7 +322,7 @@ describe("TokenManagerERC721WithMetadata", () => { await tokenManagerERC721WithMetadata .connect(user) .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 3) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(2)); @@ -353,7 +353,7 @@ describe("TokenManagerERC721WithMetadata", () => { await tokenManagerERC721WithMetadata .connect(user) .transferToSchainERC721(newSchainName, erc721OnOriginChain.address, 5) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); BigNumber.from(await messageProxyForSchain.getOutgoingMessagesCounter(newSchainName)).should.be.deep.equal(BigNumber.from(4)); diff --git a/proxy/test/TokenManagerEth.ts b/proxy/test/TokenManagerEth.ts index 635d73d0e..f77e79eb9 100644 --- a/proxy/test/TokenManagerEth.ts +++ b/proxy/test/TokenManagerEth.ts @@ -217,7 +217,7 @@ describe("TokenManagerEth", () => { await messageProxyForSchain.postMessage(communityLocker.address, mainnetHash, fakeCommunityPool, data1); await tokenManagerEth.connect(user).exitToMain(amountTo) - .should.be.eventually.rejectedWith("Trying to send messages too often"); + .should.be.eventually.rejectedWith("Exceeded message rate limit"); }); From e2b663eb34e079379167c0c4e0df54c93820319d Mon Sep 17 00:00:00 2001 From: Artem Payvin Date: Wed, 14 Sep 2022 15:34:53 +0100 Subject: [PATCH 10/12] Fixed suggested changes --- proxy/contracts/schain/CommunityLocker.sol | 7 ++ proxy/package.json | 2 +- proxy/scripts/index.js | 78 ---------------------- 3 files changed, 8 insertions(+), 79 deletions(-) delete mode 100644 proxy/scripts/index.js diff --git a/proxy/contracts/schain/CommunityLocker.sol b/proxy/contracts/schain/CommunityLocker.sol index 9c5812eaf..70513665d 100644 --- a/proxy/contracts/schain/CommunityLocker.sol +++ b/proxy/contracts/schain/CommunityLocker.sol @@ -294,8 +294,15 @@ contract CommunityLocker is ICommunityLockerInitializer, AccessControlEnumerable communityPool = newCommunityPool; } + /** + * @dev Initialize timestamp after upgrade and should be removed after upgrade + * + * Requirements: + * Should be called only by address which hold DEFAULT_ADMIN_ROLE role + */ function initializeTimestamp() external override { require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Incorrect sender"); + // Disable slither check due to moving data to the new data structure // slither-disable-next-line uninitialized-state timeLimitPerMessage[MAINNET_HASH] = _deprecatedTimeLimitPerMessage; delete _deprecatedTimeLimitPerMessage; diff --git a/proxy/package.json b/proxy/package.json index d496f3c08..fd7126b2f 100644 --- a/proxy/package.json +++ b/proxy/package.json @@ -26,7 +26,7 @@ "@openzeppelin/contracts-upgradeable": "^4.7.1", "@openzeppelin/hardhat-upgrades": "^1.9.0", "@skalenetwork/etherbase-interfaces": "^0.0.1-develop.20", - "@skalenetwork/ima-interfaces": "1.0.0-timelimit-s2s-transfers-interface.2", + "@skalenetwork/ima-interfaces": "1.0.0-develop.23", "@skalenetwork/skale-manager-interfaces": "1.0.0", "axios": "^0.21.4", "dotenv": "^10.0.0", diff --git a/proxy/scripts/index.js b/proxy/scripts/index.js deleted file mode 100644 index 786e9740f..000000000 --- a/proxy/scripts/index.js +++ /dev/null @@ -1,78 +0,0 @@ -require( "dotenv" ).config(); -const endpoint = process.env.ENDPOINT; -const privateKey = process.env.PRIVATE_KEY; -const addressG = process.env.ADDRESS; -const ethereumjs_tx = require( "ethereumjs-tx" ); -const crypto = require("crypto") - -const Web3 = require( "web3" ); - -const web3 = new Web3( new Web3.providers.HttpProvider( endpoint ) ); - -const DIFFICULTY = new web3.utils.BN( 1 ); - -async function safe_send_signed_transaction( serializedTx ) { - const strTX = "0x" + serializedTx.toString( "hex" ); // strTX is string starting from "0x" - let joReceipt = null; - let bHaveReceipt = false; - try { - joReceipt = await web3.eth.sendSignedTransaction( strTX ); - bHaveReceipt = ( joReceipt != null ); - } catch ( err ) { - const s = strLogPrefix + cc.fatal( "WARNING:" ) + cc.warning( " first attempt to send signed transaction failure during : " ) + cc.sunny( err ) + "\n"; - } - if( !bHaveReceipt ) { - try { - joReceipt = await web3.eth.sendSignedTransaction( strTX ); - } catch ( err ) { - const s = strLogPrefix + cc.fatal( "CRITICAL ERROR:" ) + cc.error( " second attempt to send signed transaction failure during : " ) + cc.error( err ) + "\n"; - throw err; - } - } - return joReceipt; -} - -function mineFreeGas( gasAmount, address, nonce ) { - console.log( "Mining free gas: ", gasAmount ); - const nonceHash = new web3.utils.BN( web3.utils.soliditySha3( nonce ).slice( 2 ), 16 ); - const addressHash = new web3.utils.BN( web3.utils.soliditySha3( address ).slice( 2 ), 16 ); - const nonceAddressXOR = nonceHash.xor( addressHash ); - const maxNumber = new web3.utils.BN( 2 ).pow( new web3.utils.BN( 256 ) ).sub( new web3.utils.BN( 1 ) ); - const divConstant = maxNumber.div( DIFFICULTY ); - let candidate; - while( true ) { - candidate = new web3.utils.BN( crypto.randomBytes( 32 ).toString( "hex" ), 16 ); - const candidateHash = new web3.utils.BN( web3.utils.soliditySha3( candidate ).slice( 2 ), 16 ); - const resultHash = nonceAddressXOR.xor( candidateHash ); - const externalGas = divConstant.div( resultHash ).toNumber(); - console.log(externalGas); - if( externalGas >= gasAmount ) { - break; - } - } - return candidate.toString(); -} - -async function sendTX() { - // const dataTx = "0x68eb2022000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000000000001"; - const dataTx = "0x"; - const tcnt = await web3.eth.getTransactionCount(web3.utils.toChecksumAddress(addressG)); - console.log("OK"); - const minedGasPrice = mineFreeGas( 100000, addressG, tcnt ); - const rawTx = { - chainId: "0x79f99296", - nonce: tcnt, - gasPrice: minedGasPrice, - // gasLimit: estimatedGas, - gas: 100000, // gas is optional here - to: "0xD2aAA00500000000000000000000000000000000", // contract address - data: dataTx - }; - const tx = new ethereumjs_tx( rawTx ); - const key = Buffer.from( privateKey, "hex" ); // convert private key to buffer - tx.sign( key ); - const serializedTx = tx.serialize(); - await safe_send_signed_transaction(serializedTx); -} - -sendTX(); \ No newline at end of file From 57d381c5944fb81b86d56aebad0e26ce6a1b927b Mon Sep 17 00:00:00 2001 From: Artem Payvin Date: Wed, 14 Sep 2022 15:35:38 +0100 Subject: [PATCH 11/12] Add yarn lock --- proxy/yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proxy/yarn.lock b/proxy/yarn.lock index 33565bc81..6f2dff714 100644 --- a/proxy/yarn.lock +++ b/proxy/yarn.lock @@ -757,10 +757,10 @@ resolved "https://registry.yarnpkg.com/@skalenetwork/etherbase-interfaces/-/etherbase-interfaces-0.0.1-develop.20.tgz#33f61e18d695fd47063aa39dce4df335d26b9528" integrity sha512-j3xnuQtOtjvjAoUMJgSUFxRa9/Egkg1RyA8r6PjcEb33VksE4LWLBy0PNFUFehLZv48595JROTcViGeXXwg5HQ== -"@skalenetwork/ima-interfaces@1.0.0-timelimit-s2s-transfers-interface.2": - version "1.0.0-timelimit-s2s-transfers-interface.2" - resolved "https://registry.yarnpkg.com/@skalenetwork/ima-interfaces/-/ima-interfaces-1.0.0-timelimit-s2s-transfers-interface.2.tgz#10a3e145bc61c023fab2073d88389d94052d8b60" - integrity sha512-EQkDhX1gj4RAk6nJ7BiCP7w2oCPfqc0MbTdPveBNgCUyN6cSGBM3RxHDAMmpGp72ISeyiunwnKzXMUm5YmTnmg== +"@skalenetwork/ima-interfaces@1.0.0-develop.23": + version "1.0.0-develop.23" + resolved "https://registry.yarnpkg.com/@skalenetwork/ima-interfaces/-/ima-interfaces-1.0.0-develop.23.tgz#49f144e98ecac7f7daaa4e03d8a8a84269fe808c" + integrity sha512-Cd50gu0mtSuZgoxSUiwrFWgYbRdagJL0qM0nYX12t1HKY5OatMOGtc+Ok21NrIFEKQSDDFLpC2gF+pIklV8fXQ== dependencies: "@skalenetwork/skale-manager-interfaces" "^0.1.2" From b42041601d87bf6a05bfdaca6642c9fa8e8c9302 Mon Sep 17 00:00:00 2001 From: Artem Payvin Date: Wed, 14 Sep 2022 15:52:05 +0100 Subject: [PATCH 12/12] Add coment above slither disable --- proxy/contracts/schain/CommunityLocker.sol | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/proxy/contracts/schain/CommunityLocker.sol b/proxy/contracts/schain/CommunityLocker.sol index 70513665d..52683d27d 100644 --- a/proxy/contracts/schain/CommunityLocker.sol +++ b/proxy/contracts/schain/CommunityLocker.sol @@ -75,10 +75,9 @@ contract CommunityLocker is ICommunityLockerInitializer, AccessControlEnumerable */ bytes32 public schainHash; - /** - * @dev Amount of seconds after message sending - * when next message cannot be sent. - */ + // Disable slither check due to variable depreciation + // and unavailability of making it constant because + // it breaks upgradeability pattern. // slither-disable-next-line constable-states uint private _deprecatedTimeLimitPerMessage;