Skip to content

Commit

Permalink
Merge pull request #1254 from skalenetwork/enhancement/add-timelimit-s2s
Browse files Browse the repository at this point in the history
Enhancement/add timelimit s2s
  • Loading branch information
payvint authored Sep 14, 2022
2 parents 08ed0cc + b420416 commit 396e27f
Show file tree
Hide file tree
Showing 21 changed files with 641 additions and 48 deletions.
5 changes: 5 additions & 0 deletions agent/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion agent/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions npms/skale-ima/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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
Expand Down
92 changes: 74 additions & 18 deletions proxy/contracts/schain/CommunityLocker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -71,11 +75,11 @@ contract CommunityLocker is ICommunityLocker, AccessControlEnumerableUpgradeable
*/
bytes32 public schainHash;

/**
* @dev Amount of seconds after message sending
* when next message cannot be sent.
*/
uint public timeLimitPerMessage;
// 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;

/**
* @dev Mapping of users who are allowed to send a message.
Expand All @@ -89,10 +93,30 @@ contract CommunityLocker is ICommunityLocker, AccessControlEnumerableUpgradeable
// 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;

/**
* @dev Emitted when a user becomes active.
*/
Expand All @@ -118,6 +142,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,
"Exceeded message rate limit"
);
_;
}

/**
* @dev Allows MessageProxy to post operational message from mainnet
* or SKALE chains.
Expand Down Expand Up @@ -163,36 +200,41 @@ 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:
*
* - Function caller has to be granted with {CONSTANT_SETTER_ROLE}.
*
* 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;
}

/**
Expand Down Expand Up @@ -247,7 +289,21 @@ contract CommunityLocker is ICommunityLocker, AccessControlEnumerableUpgradeable
messageProxy = newMessageProxy;
tokenManagerLinker = newTokenManagerLinker;
schainHash = keccak256(abi.encodePacked(newSchainName));
timeLimitPerMessage = 5 minutes;
timeLimitPerMessage[MAINNET_HASH] = 5 minutes;
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;
}
}
6 changes: 4 additions & 2 deletions proxy/contracts/schain/TokenManagers/TokenManagerERC1155.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand All @@ -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);
}

Expand All @@ -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);
}

Expand Down
3 changes: 2 additions & 1 deletion proxy/contracts/schain/TokenManagers/TokenManagerERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand Down
3 changes: 2 additions & 1 deletion proxy/contracts/schain/TokenManagers/TokenManagerERC721.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion proxy/contracts/schain/TokenManagers/TokenManagerEth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
26 changes: 24 additions & 2 deletions proxy/migrations/upgradeSchain.ts
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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"
);
Expand Down
2 changes: 1 addition & 1 deletion proxy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.22",
"@skalenetwork/ima-interfaces": "1.0.0-develop.23",
"@skalenetwork/skale-manager-interfaces": "1.0.0",
"axios": "^0.21.4",
"dotenv": "^10.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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--------
Expand Down Expand Up @@ -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
Expand All @@ -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(
Expand All @@ -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 = 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)

return storage

Expand Down
2 changes: 1 addition & 1 deletion proxy/predeployed/test/contracts/community_locker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit 396e27f

Please sign in to comment.