diff --git a/.env.ganache b/.env.ganache index 449b4daf..c3d4940f 100644 --- a/.env.ganache +++ b/.env.ganache @@ -9,7 +9,7 @@ PROVIDER_HOST=127.0.0.1 PROVIDER_PORT=8545 # Schelling Coin Address (if want to re-use already deployed instance) -SCHELLING_COIN_ADDRESS=0xF4c08930165661f13CEb5671CB0B0EC372b08151 +SCHELLING_COIN_ADDRESS= # List of Stakers STAKER_ADDRESSES=0x1D68ad204637173b2d8656B7972c57dcE41Bc80e,0x9FF5085aa345C019cDF2A427B02Bd6746DeF549B,0xc4695904751Ad8414c75798d6Ff2579f55e61522,0x40d57C3F5c3BAbac3033E2D50AB7C6886A595F46,0xa2B827aCF6073f5D9e2350cbf0646Ba2535a5B0C diff --git a/.gitignore b/.gitignore index c411563f..329b943c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ deployed/ganache .env.goerli node_modules/ .DS_Store -.previous-deployment-addresses \ No newline at end of file +.previous-deployment-addresses +.contract-deployment.tmp.json \ No newline at end of file diff --git a/.solhint.json b/.solhint.json index 77d7d634..f418cc30 100644 --- a/.solhint.json +++ b/.solhint.json @@ -6,9 +6,7 @@ "const-name-snakecase": "error", "contract-name-camelcase": "error", "event-name-camelcase": "error", - "expression-indent": "error", "func-name-mixedcase": "error", - "func-order": "error", "func-visibility": ["error", { "ignoreConstructors": true }], "imports-on-top": "warn", "mark-callable-contracts": "off", diff --git a/contracts/Core/BlockManager.sol b/contracts/Core/BlockManager.sol index d3721ebf..64432763 100644 --- a/contracts/Core/BlockManager.sol +++ b/contracts/Core/BlockManager.sol @@ -8,10 +8,11 @@ import "./interface/IJobManager.sol"; import "./storage/BlockStorage.sol"; import "../lib/Constants.sol"; import "../lib/Random.sol"; +import "../Initializable.sol"; import "./ACL.sol"; -contract BlockManager is ACL, BlockStorage { +contract BlockManager is Initializable, ACL, BlockStorage { IStakeManager public stakeManager; IStateManager public stateManager; @@ -50,17 +51,17 @@ contract BlockManager is ACL, BlockStorage { _; } - function init( - address _stakeManagerAddress, - address _stateManagerAddress, - address _voteManagerAddress, - address _jobManagerAddress - ) external + function initialize ( + address stakeManagerAddress, + address stateManagerAddress, + address voteManagerAddress, + address jobManagerAddress + ) external initializer onlyRole(DEFAULT_ADMIN_ROLE) { - stakeManager = IStakeManager(_stakeManagerAddress); - stateManager = IStateManager(_stateManagerAddress); - voteManager = IVoteManager(_voteManagerAddress); - jobManager = IJobManager(_jobManagerAddress); + stakeManager = IStakeManager(stakeManagerAddress); + stateManager = IStateManager(stateManagerAddress); + voteManager = IVoteManager(voteManagerAddress); + jobManager = IJobManager(jobManagerAddress); } function getBlock(uint256 epoch) external view returns(Structs.Block memory _block) { @@ -132,10 +133,9 @@ contract BlockManager is ACL, BlockStorage { uint256[] memory higherCutoffs, uint256 iteration, uint256 biggestStakerId - ) public checkEpoch(epoch) checkState(Constants.propose()) + ) public initialized checkEpoch(epoch) checkState(Constants.propose()) { uint256 proposerId = stakeManager.getStakerId(msg.sender); - // SchellingCoin sch = SchellingCoin(schAddress); require(isElectedProposer(iteration, biggestStakerId, proposerId), "not elected"); require( stakeManager.getStaker(proposerId).stake >= Constants.minStake(), @@ -176,6 +176,7 @@ contract BlockManager is ACL, BlockStorage { uint256[] memory sorted ) public + initialized checkEpoch(epoch) checkState(Constants.dispute()) { @@ -214,13 +215,13 @@ contract BlockManager is ACL, BlockStorage { // //if any mistake made during giveSorted, resetDispute and start again function resetDispute( uint256 epoch - ) public checkEpoch(epoch) checkState(Constants.dispute()) + ) public initialized checkEpoch(epoch) checkState(Constants.dispute()) { disputes[epoch][msg.sender] = Structs.Dispute(0, 0, 0, 0, 0, 0); } function finalizeDispute (uint256 epoch, uint256 blockId) - public checkEpoch(epoch) checkState(Constants.dispute()) { + public initialized checkEpoch(epoch) checkState(Constants.dispute()) { uint256 assetId = disputes[epoch][msg.sender].assetId; require( disputes[epoch][msg.sender].accWeight == voteManager.getTotalStakeRevealed(epoch, assetId), @@ -242,7 +243,7 @@ contract BlockManager is ACL, BlockStorage { } } - function confirmBlock() public onlyRole(Constants.getBlockConfirmerHash()) { + function confirmBlock() public initialized onlyRole(Constants.getBlockConfirmerHash()) { uint256 epoch = stateManager.getEpoch(); for (uint8 i=0; i < proposedBlocks[epoch - 1].length; i++) { @@ -273,7 +274,8 @@ contract BlockManager is ACL, BlockStorage { uint256 stakerId ) public - view + view + initialized returns (bool) { // generating pseudo random number (range 0..(totalstake - 1)), add (+1) to the result, diff --git a/contracts/Core/JobManager.sol b/contracts/Core/JobManager.sol index 70b5ece2..cd8336d9 100644 --- a/contracts/Core/JobManager.sol +++ b/contracts/Core/JobManager.sol @@ -37,11 +37,10 @@ contract JobManager is ACL, JobStorage { uint256 timestamp ); - //disable after init. - function init(address _stateManagerAddress) external { - stateManager = IStateManager(_stateManagerAddress); + constructor(address stateManagerAddress) { + stateManager = IStateManager(stateManagerAddress); } - + function createJob( string calldata url, string calldata selector, diff --git a/contracts/Core/StakeManager.sol b/contracts/Core/StakeManager.sol index 1ff44f49..20b3a29f 100644 --- a/contracts/Core/StakeManager.sol +++ b/contracts/Core/StakeManager.sol @@ -7,6 +7,7 @@ import "./interface/IVoteManager.sol"; import "./storage/StakeStorage.sol"; import "../lib/Constants.sol"; import "../lib/Constants.sol"; +import "../Initializable.sol"; import "../SchellingCoin.sol"; import "./ACL.sol"; @@ -15,7 +16,7 @@ import "./ACL.sol"; /// @notice StakeManager handles stake, unstake, withdraw, reward, functions /// for stakers -contract StakeManager is ACL, StakeStorage { +contract StakeManager is Initializable, ACL, StakeStorage { SchellingCoin public sch; IVoteManager public voteManager; @@ -82,22 +83,21 @@ contract StakeManager is ACL, StakeStorage { blockReward = _blockReward; } - /// @param _schAddress The address of the Schelling token ERC20 contract - /// @param _voteManagerAddress The address of the VoteManager contract - /// @param _blockManagerAddress The address of the BlockManager contract - /// @param _stateManagerAddress The address of the StateManager contract - /// todo disable after init - function init ( - address _schAddress, - address _voteManagerAddress, - address _blockManagerAddress, - address _stateManagerAddress - ) external + /// @param schAddress The address of the Schelling token ERC20 contract + /// @param voteManagersAddress The address of the VoteManager contract + /// @param blockManagerAddress The address of the BlockManager contract + /// @param stateManagerAddress The address of the StateManager contract + function initialize ( + address schAddress, + address voteManagersAddress, + address blockManagerAddress, + address stateManagerAddress + ) external initializer onlyRole(DEFAULT_ADMIN_ROLE) { - sch = SchellingCoin(_schAddress); - voteManager = IVoteManager(_voteManagerAddress); - blockManager = IBlockManager(_blockManagerAddress); - stateManager = IStateManager(_stateManagerAddress); + sch = SchellingCoin(schAddress); + voteManager = IVoteManager(voteManagersAddress); + blockManager = IBlockManager(blockManagerAddress); + stateManager = IStateManager(stateManagerAddress); } /// @param _id The ID of the staker @@ -105,7 +105,7 @@ contract StakeManager is ACL, StakeStorage { function setStakerEpochLastRevealed( uint256 _id, uint256 _epochLastRevealed - ) external onlyRole(Constants.getStakerActivityUpdaterHash()) + ) external initialized onlyRole(Constants.getStakerActivityUpdaterHash()) { stakers[_id].epochLastRevealed = _epochLastRevealed; } @@ -113,7 +113,7 @@ contract StakeManager is ACL, StakeStorage { /// @param stakerId The ID of the staker function updateCommitmentEpoch( uint256 stakerId - ) external onlyRole(Constants.getStakerActivityUpdaterHash()) + ) external initialized onlyRole(Constants.getStakerActivityUpdaterHash()) { stakers[stakerId].epochLastCommitted = stateManager.getEpoch(); } @@ -132,6 +132,7 @@ contract StakeManager is ACL, StakeStorage { uint256 amount ) external + initialized checkEpoch(epoch) checkState(Constants.commit()) { // not allowed during reveal period @@ -163,7 +164,7 @@ contract StakeManager is ACL, StakeStorage { /// @notice staker must call unstake() and should wait for Constants.WITHDRAW_LOCK_PERIOD /// after which she can call withdraw() to finally Withdraw /// @param epoch The Epoch value for which staker is requesting to unstake - function unstake (uint256 epoch) external checkEpoch(epoch) checkState(Constants.commit()) { + function unstake (uint256 epoch) external initialized checkEpoch(epoch) checkState(Constants.commit()) { uint256 stakerId = stakerIds[msg.sender]; Structs.Staker storage staker = stakers[stakerId]; require(staker.id != 0, "staker.id = 0"); @@ -177,7 +178,7 @@ contract StakeManager is ACL, StakeStorage { /// @notice Helps stakers withdraw their stake if previously unstaked /// @param epoch The Epoch value for which staker is requesting a withdraw - function withdraw (uint256 epoch) external checkEpoch(epoch) checkState(Constants.commit()) { + function withdraw (uint256 epoch) external initialized checkEpoch(epoch) checkState(Constants.commit()) { uint256 stakerId = stakerIds[msg.sender]; Structs.Staker storage staker = stakers[stakerId]; require(staker.id != 0, "staker doesnt exist"); @@ -210,7 +211,7 @@ contract StakeManager is ACL, StakeStorage { function givePenalties( uint256 stakerId, uint256 epoch - ) external onlyRole(Constants.getStakeModifierHash()) + ) external initialized onlyRole(Constants.getStakeModifierHash()) { _givePenalties(stakerId, epoch); } @@ -248,7 +249,7 @@ contract StakeManager is ACL, StakeStorage { function giveRewards( uint256 stakerId, uint256 epoch - ) external onlyRole(Constants.getStakeModifierHash()) + ) external initialized onlyRole(Constants.getStakeModifierHash()) { if (stakeGettingReward == 0) return; Structs.Staker memory thisStaker = stakers[stakerId]; diff --git a/contracts/Core/VoteManager.sol b/contracts/Core/VoteManager.sol index d312f17b..300eb6c7 100644 --- a/contracts/Core/VoteManager.sol +++ b/contracts/Core/VoteManager.sol @@ -7,14 +7,19 @@ import "./interface/IStateManager.sol"; import "./interface/IBlockManager.sol"; import "./storage/VoteStorage.sol"; import "../lib/Constants.sol"; +import "../Initializable.sol"; +import "./ACL.sol"; -contract VoteManager is VoteStorage { +contract VoteManager is Initializable, ACL, VoteStorage { IStakeManager public stakeManager; IStateManager public stateManager; IBlockManager public blockManager; + event Committed(uint256 epoch, uint256 stakerId, bytes32 commitment, uint256 timestamp); + event Revealed(uint256 epoch, uint256 stakerId, uint256 stake, uint256[] values, uint256 timestamp); + modifier checkEpoch (uint256 epoch) { require(epoch == stateManager.getEpoch(), "incorrect epoch"); _; @@ -25,15 +30,19 @@ contract VoteManager is VoteStorage { _; } - function init (address _stakeManagerAddress, address _stateManagerAddress, address _blockManagerAddress) public { - stakeManager = IStakeManager(_stakeManagerAddress); - stateManager = IStateManager(_stateManagerAddress); - blockManager = IBlockManager(_blockManagerAddress); + function initialize ( + address stakeManagerAddress, + address stateManagerAddress, + address blockManagerAddress + ) external initializer onlyRole(DEFAULT_ADMIN_ROLE) + { + stakeManager = IStakeManager(stakeManagerAddress); + stateManager = IStateManager(stateManagerAddress); + blockManager = IBlockManager(blockManagerAddress); } + - event Committed(uint256 epoch, uint256 stakerId, bytes32 commitment, uint256 timestamp); - - function commit(uint256 epoch, bytes32 commitment) public checkEpoch(epoch) checkState(Constants.commit()) { + function commit(uint256 epoch, bytes32 commitment) public initialized checkEpoch(epoch) checkState(Constants.commit()) { uint256 stakerId = stakeManager.getStakerId(msg.sender); require(commitments[epoch][stakerId] == 0x0, "already commited"); Structs.Staker memory thisStaker = stakeManager.getStaker(stakerId); @@ -53,12 +62,18 @@ contract VoteManager is VoteStorage { } } - event Revealed(uint256 epoch, uint256 stakerId, uint256 stake, uint256[] values, uint256 timestamp); - function reveal (uint256 epoch, bytes32 root, uint256[] memory values, - bytes32[][] memory proofs, bytes32 secret, address stakerAddress) - public - checkEpoch(epoch) { + function reveal ( + uint256 epoch, + bytes32 root, + uint256[] memory values, + bytes32[][] memory proofs, bytes32 secret, + address stakerAddress + ) + public + initialized + checkEpoch(epoch) + { uint256 thisStakerId = stakeManager.getStakerId(stakerAddress); require(thisStakerId > 0, "Structs.Staker does not exist"); Structs.Staker memory thisStaker = stakeManager.getStaker(thisStakerId); diff --git a/contracts/Core/interface/IBlockManager.sol b/contracts/Core/interface/IBlockManager.sol index 3f06e65b..ca30adb7 100644 --- a/contracts/Core/interface/IBlockManager.sol +++ b/contracts/Core/interface/IBlockManager.sol @@ -6,13 +6,6 @@ import "../../lib/Structs.sol"; interface IBlockManager { - function init( - address _stakeManagerAddress, - address _stateManagerAddress, - address _voteManagerAddress, - address _jobManagerAddress - ) external; - // elected proposer proposes block. //we use a probabilistic method to elect stakers weighted by stake // protocol works like this. diff --git a/contracts/Core/interface/IStakeManager.sol b/contracts/Core/interface/IStakeManager.sol index 1ef67e93..532f689c 100644 --- a/contracts/Core/interface/IStakeManager.sol +++ b/contracts/Core/interface/IStakeManager.sol @@ -4,8 +4,6 @@ pragma solidity ^0.8.0; import "../../lib/Structs.sol"; interface IStakeManager { - function init (address _schAddress, address _voteManagerAddress, - address _blockManagerAddress, address _stateManagerAddress) external; function setStakerEpochLastRevealed(uint256 _id, uint256 _epochLastRevealed) external; function updateCommitmentEpoch(uint256 stakerId) external; @@ -21,5 +19,4 @@ interface IStakeManager { function getNumStakers() external view returns(uint256); function getRewardPool() external view returns(uint256); function getStakeGettingReward() external view returns(uint256); - } diff --git a/contracts/Core/interface/IVoteManager.sol b/contracts/Core/interface/IVoteManager.sol index 5a31c8a5..3aa8e948 100644 --- a/contracts/Core/interface/IVoteManager.sol +++ b/contracts/Core/interface/IVoteManager.sol @@ -6,8 +6,6 @@ import "../../lib/Structs.sol"; interface IVoteManager { - function init(address _stakeManagerAddress, address _blockManagerAddress) external; - function commit(uint256 epoch, bytes32 commitment) external; function reveal( diff --git a/contracts/Faucet.sol b/contracts/Faucet.sol index 25428a18..120e3d9e 100644 --- a/contracts/Faucet.sol +++ b/contracts/Faucet.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; + import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; diff --git a/contracts/Initializable.sol b/contracts/Initializable.sol new file mode 100644 index 00000000..a09b7e87 --- /dev/null +++ b/contracts/Initializable.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT + +// solhint-disable-next-line compiler-version +pragma solidity ^0.8.0; + +/** + * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed + * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an + * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer + * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. + * + * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as + * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. + * + * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure + * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. + * + * Forked from OZ's (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/b9125001f0a1c44d596ca3a47536f1a467e3a29d/contracts/proxy/utils/Initializable.sol) + */ + +abstract contract Initializable { + + /** + * @dev Indicates that the contract has been initialized. + */ + bool private _initialized; + + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool private _initializing; + + /** + * @dev Modifier to protect an initializer function from being invoked twice. + */ + modifier initializer() { + require(_initializing || !_initialized, "Initializable: contract is already initialized"); + + bool isTopLevelCall = !_initializing; + if (isTopLevelCall) { + _initializing = true; + _initialized = true; + } + + _; + + if (isTopLevelCall) { + _initializing = false; + } + } + + modifier initialized() { + require(_initialized, "Contract should be initialized"); + _; + } +} diff --git a/hardhat.config.js b/hardhat.config.js index b5bd491d..f0a8c5f3 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -40,7 +40,6 @@ module.exports = { }, networks: { hardhat: { - blockGasLimit: 0x1fffffffffffff, chainId: 31337, }, coverage: { diff --git a/migrations/deploy_all.js b/migrations/deploy_all.js index 3a3dd2b3..1b818026 100644 --- a/migrations/deploy_all.js +++ b/migrations/deploy_all.js @@ -1,9 +1,9 @@ /* eslint-disable no-console */ const librariesMigration = require('./src/1_deploy_libraries'); -const blockManagerMigration = require('./src/2_deploy_block_manager'); +const stateManagerMigration = require('./src/2_deploy_state_manager'); const jobManagerMigration = require('./src/3_deploy_job_manager'); const stakeManagerMigration = require('./src/4_deploy_stake_manager'); -const stateManagerMigration = require('./src/5_deploy_state_manager'); +const blockManagerMigration = require('./src/5_deploy_block_manager'); const voteManagerMigration = require('./src/6_deploy_vote_manager'); const delegatorMigration = require('./src/7_deploy_delegator'); const schellingCoinAndFacuetMigration = require('./src/8_deploy_schelling_coin_and_faucet'); @@ -12,10 +12,10 @@ const postDeploymentSetup = require('./src/postDeploymentSetup'); async function main() { // Deploy smart contracts await librariesMigration(); - await blockManagerMigration(); + await stateManagerMigration(); await jobManagerMigration(); await stakeManagerMigration(); - await stateManagerMigration(); + await blockManagerMigration(); await voteManagerMigration(); await delegatorMigration(); await schellingCoinAndFacuetMigration(); diff --git a/migrations/src/5_deploy_state_manager.js b/migrations/src/2_deploy_state_manager.js similarity index 100% rename from migrations/src/5_deploy_state_manager.js rename to migrations/src/2_deploy_state_manager.js diff --git a/migrations/src/3_deploy_job_manager.js b/migrations/src/3_deploy_job_manager.js index 0f5cd42d..7132d354 100644 --- a/migrations/src/3_deploy_job_manager.js +++ b/migrations/src/3_deploy_job_manager.js @@ -1,7 +1,8 @@ -const { deployContract } = require('../migrationHelpers'); +const { deployContract, readDeploymentFile } = require('../migrationHelpers'); const deployJobManager = async () => { - await deployContract('JobManager', ['Constants']); + const { StateManager } = await readDeploymentFile(); + await deployContract('JobManager', ['Constants'], [StateManager]); }; module.exports = async () => { diff --git a/migrations/src/2_deploy_block_manager.js b/migrations/src/5_deploy_block_manager.js similarity index 100% rename from migrations/src/2_deploy_block_manager.js rename to migrations/src/5_deploy_block_manager.js diff --git a/migrations/src/postDeploymentSetup.js b/migrations/src/postDeploymentSetup.js index 9c8e53a7..d6abfadf 100644 --- a/migrations/src/postDeploymentSetup.js +++ b/migrations/src/postDeploymentSetup.js @@ -71,10 +71,9 @@ module.exports = async () => { pendingTransactions.push(await schellingCoin.transfer(faucetAddress, SEED_AMOUNT)); } - pendingTransactions.push(await blockManager.init(stakeManagerAddress, stateManagerAddress, voteManagerAddress, jobManagerAddress)); - pendingTransactions.push(await voteManager.init(stakeManagerAddress, stateManagerAddress, blockManagerAddress)); - pendingTransactions.push(await stakeManager.init(schellingCoinAddress, voteManagerAddress, blockManagerAddress, stateManagerAddress)); - pendingTransactions.push(await jobManager.init(stateManagerAddress)); + pendingTransactions.push(await blockManager.initialize(stakeManagerAddress, stateManagerAddress, voteManagerAddress, jobManagerAddress)); + pendingTransactions.push(await voteManager.initialize(stakeManagerAddress, stateManagerAddress, blockManagerAddress)); + pendingTransactions.push(await stakeManager.initialize(schellingCoinAddress, voteManagerAddress, blockManagerAddress, stateManagerAddress)); pendingTransactions.push(await jobManager.grantRole(await constants.getJobConfirmerHash(), blockManagerAddress)); pendingTransactions.push(await blockManager.grantRole(await constants.getBlockConfirmerHash(), voteManagerAddress)); diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 3477f1a7..4bcebe52 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -22,7 +22,7 @@ cp -r artifacts deployed/$ENV/abi cat .contract-deployment.tmp.json | jq '.' > deployed/$ENV/addresses.json rm -rf .contract-deployment.tmp.json -if [[ -f "deployed/$ENV/addresses.json" ]] +if [[ -f "./.previous-deployment-addresses" ]] then rm -rf .previous-deployment-addresses fi diff --git a/test/ACL.js b/test/ACL.js index 14d1d997..f13f26e6 100644 --- a/test/ACL.js +++ b/test/ACL.js @@ -12,17 +12,19 @@ describe('Access Control Test', async () => { let constants; let jobManager; let stakeManager; + let initializeContracts; const expectedRevertMessage = 'ACL: sender not authorized'; before(async () => { ({ - blockManager, constants, jobManager, stakeManager, + blockManager, constants, jobManager, stakeManager, initializeContracts, } = await setupContracts()); signers = await ethers.getSigners(); }); beforeEach(async () => { snapShotId = await takeSnapshot(); + await Promise.all(await initializeContracts()); }); afterEach(async () => { diff --git a/test/BlockManager.js b/test/BlockManager.js index dc2b5c2b..3f226d9b 100644 --- a/test/BlockManager.js +++ b/test/BlockManager.js @@ -8,6 +8,7 @@ const { assertBNNotEqual, mineToNextEpoch, mineToNextState, + assertRevert, } = require('./helpers/testHelpers'); const { setupContracts } = require('./helpers/testSetup'); const { DEFAULT_ADMIN_ROLE_HASH } = require('./helpers/constants'); @@ -25,14 +26,24 @@ const { utils } = ethers; describe('BlockManager', function () { let signers; let blockManager; + let jobManager; let random; let schellingCoin; let stakeManager; + let stateManager; let voteManager; + let initializeContracts; before(async () => { ({ - blockManager, random, schellingCoin, stakeManager, voteManager, + blockManager, + jobManager, + random, + schellingCoin, + stakeManager, + stateManager, + voteManager, + initializeContracts, } = await setupContracts()); signers = await ethers.getSigners(); }); @@ -43,7 +54,38 @@ describe('BlockManager', function () { assert(isAdminRoleGranted === true, 'Admin role was not Granted'); }); + it('should not be able to stake, commit without initialization', async () => { + const epoch = await getEpoch(); + + const tx1 = stakeManager.connect(signers[6]).stake(epoch, tokenAmount('18000')); + await assertRevert(tx1, 'Contract should be initialized'); + + const votes = [100, 200, 300, 400, 500, 600, 700, 800, 900]; + const tree = merkle('keccak256').sync(votes); + + const root = tree.root(); + const commitment1 = utils.solidityKeccak256( + ['uint256', 'uint256', 'bytes32'], + [epoch, root, '0x727d5c9e6d18ed15ce7ac8d3cce6ec8a0e9c02481415c0823ea49d847ccb9ddd'] + ); + const tx2 = voteManager.connect(signers[5]).commit(epoch, commitment1); + + await assertRevert(tx2, 'Contract should be initialized'); + }); + + it('should not be able to initiliaze BlockManager contract without admin role', async () => { + const tx = blockManager.connect(signers[1]).initialize( + stakeManager.address, + stateManager.address, + voteManager.address, + jobManager.address + ); + await assertRevert(tx, 'ACL: sender not authorized'); + }); + it('should be able to initialize', async () => { + await Promise.all(await initializeContracts()); + await mineToNextEpoch(); await schellingCoin.transfer(signers[5].address, tokenAmount('423000')); await schellingCoin.transfer(signers[6].address, tokenAmount('19000')); diff --git a/test/StakeManager.js b/test/StakeManager.js index ad2fbe5a..221af5d2 100644 --- a/test/StakeManager.js +++ b/test/StakeManager.js @@ -18,11 +18,21 @@ describe('StakeManager', function () { describe('SchellingCoin', async function () { let signers; let schellingCoin; + let blockManager; let stakeManager; + let stateManager; let voteManager; + let initializeContracts; before(async () => { - ({ schellingCoin, stakeManager, voteManager } = await setupContracts()); + ({ + schellingCoin, + blockManager, + stakeManager, + stateManager, + voteManager, + initializeContracts, + } = await setupContracts()); signers = await ethers.getSigners(); }); @@ -31,7 +41,24 @@ describe('StakeManager', function () { assert(isAdminRoleGranted === true, 'Admin role was not Granted'); }); + it('should not be able to stake without initialization', async () => { + const tx = stakeManager.connect(signers[6]).stake(await getEpoch(), tokenAmount('18000')); + await assertRevert(tx, 'Contract should be initialized'); + }); + + it('should not be able to initiliaze StakeManager contract without admin role', async () => { + const tx = stakeManager.connect(signers[1]).initialize( + schellingCoin.address, + voteManager.address, + blockManager.address, + stateManager.address + ); + await assertRevert(tx, 'ACL: sender not authorized'); + }); + it('should be able to initialize', async function () { + await Promise.all(await initializeContracts()); + await mineToNextEpoch(); const stake1 = tokenAmount('443000'); await schellingCoin.transfer(signers[1].address, stake1); diff --git a/test/VoteManager.js b/test/VoteManager.js index c23a6ad5..2ed00bf6 100644 --- a/test/VoteManager.js +++ b/test/VoteManager.js @@ -4,9 +4,11 @@ test cases where nobody votes, too low stake (1-4) */ const merkle = require('@razor-network/merkle'); const { utils } = require('ethers'); +const { DEFAULT_ADMIN_ROLE_HASH } = require('./helpers/constants'); const { assertBNEqual, assertBNLessThan, + assertRevert, mineToNextEpoch, mineToNextState, } = require('./helpers/testHelpers'); @@ -28,16 +30,44 @@ describe('VoteManager', function () { let stakeManager; let stateManager; let voteManager; + let initializeContracts; before(async () => { ({ - blockManager, random, schellingCoin, stakeManager, stateManager, voteManager, + blockManager, random, schellingCoin, stakeManager, stateManager, voteManager, initializeContracts, } = await setupContracts()); signers = await ethers.getSigners(); }); describe('SchellingCoin', async function () { + it('admin role should be granted', async () => { + const isAdminRoleGranted = await stakeManager.hasRole(DEFAULT_ADMIN_ROLE_HASH, signers[0].address); + assert(isAdminRoleGranted === true, 'Admin role was not Granted'); + }); + + it('should not be able to commit without initialization', async () => { + const epoch = await getEpoch(); + const votes = [100, 200, 300, 400, 500, 600, 700, 800, 900]; + const tree = merkle('keccak256').sync(votes); + + const root = tree.root(); + const commitment1 = utils.solidityKeccak256( + ['uint256', 'uint256', 'bytes32'], + [epoch, root, '0x727d5c9e6d18ed15ce7ac8d3cce6ec8a0e9c02481415c0823ea49d847ccb9ddd'] + ); + const tx = voteManager.connect(signers[5]).commit(epoch, commitment1); + + await assertRevert(tx, 'Contract should be initialized'); + }); + + it('should not be able to initiliaze VoteManager contract without admin role', async () => { + const tx = voteManager.connect(signers[1]).initialize(stakeManager.address, stateManager.address, blockManager.address); + await assertRevert(tx, 'ACL: sender not authorized'); + }); + it('should be able to initialize', async function () { + await Promise.all(await initializeContracts()); + await mineToNextEpoch(); await schellingCoin.transfer(signers[3].address, tokenAmount('423000')); await schellingCoin.transfer(signers[4].address, tokenAmount('19000')); diff --git a/test/helpers/testSetup.js b/test/helpers/testSetup.js index 53d01872..b79425fa 100644 --- a/test/helpers/testSetup.js +++ b/test/helpers/testSetup.js @@ -48,11 +48,10 @@ const setupContracts = async () => { }); const blockManager = await BlockManager.deploy(); - + const stateManager = await StateManager.deploy(); const delegator = await Delegator.deploy(); - const jobManager = await JobManager.deploy(); + const jobManager = await JobManager.deploy(stateManager.address); const stakeManager = await StakeManager.deploy(BLOCK_REWARD.toHexString()); - const stateManager = await StateManager.deploy(); const voteManager = await VoteManager.deploy(); const schellingCoin = await SchellingCoin.deploy(); const faucet = await Faucet.deploy(schellingCoin.address); @@ -67,11 +66,10 @@ const setupContracts = async () => { await stateManager.deployed(); await voteManager.deployed(); - await Promise.all([ - blockManager.init(stakeManager.address, stateManager.address, voteManager.address, jobManager.address), - voteManager.init(stakeManager.address, stateManager.address, blockManager.address), - stakeManager.init(schellingCoin.address, voteManager.address, blockManager.address, stateManager.address), - jobManager.init(stateManager.address), + const initializeContracts = async () => [ + blockManager.initialize(stakeManager.address, stateManager.address, voteManager.address, jobManager.address), + voteManager.initialize(stakeManager.address, stateManager.address, blockManager.address), + stakeManager.initialize(schellingCoin.address, voteManager.address, blockManager.address, stateManager.address), jobManager.grantRole(await constants.getJobConfirmerHash(), blockManager.address), blockManager.grantRole(await constants.getBlockConfirmerHash(), voteManager.address), @@ -80,7 +78,7 @@ const setupContracts = async () => { stakeManager.grantRole(await constants.getStakerActivityUpdaterHash(), voteManager.address), delegator.upgradeDelegate(jobManager.address), - ]); + ]; return { blockManager, @@ -94,6 +92,7 @@ const setupContracts = async () => { stateManager, structs, voteManager, + initializeContracts, }; };