diff --git a/contracts/extensions/Tasks.sol b/contracts/extensions/Tasks.sol index 72ea57c592..fad5f5840c 100644 --- a/contracts/extensions/Tasks.sol +++ b/contracts/extensions/Tasks.sol @@ -33,6 +33,11 @@ contract Tasks is DSMath { /// @param taskId The newly added task id event TaskAdded(uint256 taskId); + /// @notice Event logged when a task's security status changes (secure vs. managed) + /// @param taskId Id of the task + /// @param secure Boolean of security status (true: secure, false: managed) + event TaskSecuritySet(uint256 indexed taskId, bool secure); + /// @notice Event logged when a task's specification hash changes /// @param taskId Id of the task /// @param specificationHash New specification hash of the task @@ -69,6 +74,7 @@ contract Tasks is DSMath { uint256 dueDate; uint256 completionTimestamp; uint256 changeNonce; + bool secure; } struct Role { @@ -103,6 +109,7 @@ contract Tasks is DSMath { roleAssignmentSigs[bytes4(keccak256("setTaskWorkerRole(uint256,address)"))] = true; // Initialise the task update reviewers + reviewers[bytes4(keccak256("setTaskSecurity(uint256,bool)"))] = [TaskRole.Manager, TaskRole.Worker]; reviewers[bytes4(keccak256("setTaskBrief(uint256,bytes32)"))] = [TaskRole.Manager, TaskRole.Worker]; reviewers[bytes4(keccak256("setTaskDueDate(uint256,uint256)"))] = [TaskRole.Manager, TaskRole.Worker]; reviewers[bytes4(keccak256("setTaskSkill(uint256,uint256)"))] = [TaskRole.Manager, TaskRole.Worker]; @@ -115,8 +122,8 @@ contract Tasks is DSMath { reviewers[bytes4(keccak256("cancelTask(uint256)"))] = [TaskRole.Manager, TaskRole.Worker]; } - modifier self() { - require(address(this) == msg.sender, "task-not-self"); + modifier self(uint256 _id) { + require(managerCanCall(_id) || address(this) == msg.sender, "task-not-self"); _; } @@ -274,7 +281,8 @@ contract Tasks is DSMath { bytes32 _specificationHash, uint256 _domainId, uint256 _skillId, - uint256 _dueDate + uint256 _dueDate, + bool _secure ) public isAdmin(msg.sender, _callerPermissionDomainId, _callerChildSkillIndex, _domainId) @@ -285,9 +293,13 @@ contract Tasks is DSMath { tasks[taskCount].expenditureId = expenditureId; tasks[taskCount].specificationHash = _specificationHash; tasks[taskCount].dueDate = (_dueDate > 0) ? _dueDate : now + 90 days; // Note: can set dueDate in past? + tasks[taskCount].secure = _secure; setTaskRoleUser(taskCount, TaskRole.Manager, msg.sender); - setTaskRoleUser(taskCount, TaskRole.Evaluator, msg.sender); + + if (_secure) { + setTaskRoleUser(taskCount, TaskRole.Evaluator, msg.sender); + } if (_skillId > 0) { this.setTaskSkill(taskCount, _skillId); @@ -355,22 +367,32 @@ contract Tasks is DSMath { return ratingSecrets[_id].secret[_role]; } + function setTaskSecurity(uint256 _id, bool _secure) public self(_id) { + tasks[_id].secure = _secure; + + if (!_secure) { + removeTaskEvaluatorRole(_id); + } + + emit TaskSecuritySet(_id, _secure); + } + // Note: the domain permissions arguments are placed at the end for consistency with the other role change functions function setTaskManagerRole(uint256 _id, address payable _user, uint256 _permissionDomainId, uint256 _childSkillIndex) public - self + self(_id) isAdmin(_user, _permissionDomainId, _childSkillIndex, colony.getExpenditure(tasks[_id].expenditureId).domainId) { setTaskRoleUser(_id, TaskRole.Manager, _user); } - function setTaskEvaluatorRole(uint256 _id, address payable _user) public self { + function setTaskEvaluatorRole(uint256 _id, address payable _user) public self(_id) { // Can only assign role if no one is currently assigned to it require(getTaskRoleUser(_id, TaskRole.Evaluator) == address(0x0), "task-evaluator-role-assigned"); setTaskRoleUser(_id, TaskRole.Evaluator, _user); } - function setTaskWorkerRole(uint256 _id, address payable _user) public self { + function setTaskWorkerRole(uint256 _id, address payable _user) public self(_id) { // Can only assign role if no one is currently assigned to it require(getTaskRoleUser(_id, TaskRole.Worker) == address(0x0), "task-worker-role-assigned"); uint256[] memory skills = colony.getExpenditureSlot(tasks[_id].expenditureId, uint256(TaskRole.Worker)).skills; @@ -378,23 +400,23 @@ contract Tasks is DSMath { setTaskRoleUser(_id, TaskRole.Worker, _user); } - function removeTaskEvaluatorRole(uint256 _id) public self { + function removeTaskEvaluatorRole(uint256 _id) public self(_id) { setTaskRoleUser(_id, TaskRole.Evaluator, address(0x0)); } - function removeTaskWorkerRole(uint256 _id) public self { + function removeTaskWorkerRole(uint256 _id) public self(_id) { setTaskRoleUser(_id, TaskRole.Worker, address(0x0)); } - function setTaskManagerPayout(uint256 _id, address _token, uint256 _amount) public self { + function setTaskManagerPayout(uint256 _id, address _token, uint256 _amount) public self(_id) { colony.setExpenditurePayout(_id, uint256(TaskRole.Manager), _token, _amount); } - function setTaskEvaluatorPayout(uint256 _id, address _token, uint256 _amount) public self { + function setTaskEvaluatorPayout(uint256 _id, address _token, uint256 _amount) public self(_id) { colony.setExpenditurePayout(_id, uint256(TaskRole.Evaluator), _token, _amount); } - function setTaskWorkerPayout(uint256 _id, address _token, uint256 _amount) public self { + function setTaskWorkerPayout(uint256 _id, address _token, uint256 _amount) public self(_id) { colony.setExpenditurePayout(_id, uint256(TaskRole.Worker), _token, _amount); } @@ -421,13 +443,13 @@ contract Tasks is DSMath { this.setTaskWorkerPayout(_id, _token, _workerAmount); } - function setTaskSkill(uint256 _id, uint256 _skillId) public self { + function setTaskSkill(uint256 _id, uint256 _skillId) public self(_id) { colony.setExpenditureSkill(tasks[_id].expenditureId, uint256(TaskRole.Worker), _skillId); } function setTaskBrief(uint256 _id, bytes32 _specificationHash) public - self + self(_id) taskExists(_id) taskNotComplete(_id) { @@ -438,7 +460,7 @@ contract Tasks is DSMath { function setTaskDueDate(uint256 _id, uint256 _dueDate) public - self + self(_id) taskExists(_id) taskNotComplete(_id) { @@ -479,7 +501,7 @@ contract Tasks is DSMath { function cancelTask(uint256 _id) public - self + self(_id) taskExists(_id) taskNotComplete(_id) { @@ -487,11 +509,13 @@ contract Tasks is DSMath { } // Permissions pertain to the Arbitration role here - function finalizeTask(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _id) + function finalizeSecureTask(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _id) public taskExists(_id) taskComplete(_id) { + require(tasks[_id].secure, "task-not-secure"); + colony.finalizeExpenditure(tasks[_id].expenditureId); assignWorkRatings(_id); @@ -510,6 +534,16 @@ contract Tasks is DSMath { } } + function finalizeManagedTask(uint256 _id) + public + taskExists(_id) + confirmTaskRoleIdentity(_id, msg.sender, TaskRole.Manager) + { + require(!tasks[_id].secure, "task-not-managed"); + + colony.finalizeExpenditure(tasks[_id].expenditureId); + } + function getTaskCount() public view returns (uint256) { return taskCount; } @@ -687,4 +721,8 @@ contract Tasks is DSMath { function isTaskComplete(uint256 _id) internal view returns (bool) { return tasks[_id].completionTimestamp > 0; } + + function managerCanCall(uint256 _id) internal view returns (bool) { + return !tasks[_id].secure && getTaskRoleUser(_id, TaskRole.Manager) == msg.sender; + } } diff --git a/test/extensions/tasks.js b/test/extensions/tasks.js index 00ac3ecec9..2ca42ecc27 100644 --- a/test/extensions/tasks.js +++ b/test/extensions/tasks.js @@ -115,7 +115,7 @@ contract("Tasks extension", accounts => { describe("when creating tasks", () => { it("should allow admins to make a task", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const task = await tasks.getTask(taskId); @@ -125,11 +125,11 @@ contract("Tasks extension", accounts => { }); it("should fail if a non-admin user tries to make a task", async () => { - await checkErrorRevert(tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: OTHER }), "colony-task-not-admin"); + await checkErrorRevert(tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: OTHER }), "colony-task-not-admin"); }); it("should set the task creator as the manager and evaluator", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const task = await tasks.getTask(taskId); @@ -148,7 +148,7 @@ contract("Tasks extension", accounts => { const newEvaluator = accounts[1]; expect(newEvaluator).to.not.equal(MANAGER); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); let evaluator = await tasks.getTaskRoleUser(taskId, EVALUATOR_ROLE); @@ -183,7 +183,7 @@ contract("Tasks extension", accounts => { const taskCountBefore = await tasks.getTaskCount(); for (let i = 0; i < 5; i += 1) { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); } const taskCountAfter = await tasks.getTaskCount(); @@ -191,7 +191,7 @@ contract("Tasks extension", accounts => { }); it("should set the task domain correctly", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 2, 0, 0); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 2, 0, 0, true); const taskId = await tasks.getTaskCount(); const task = await tasks.getTask(taskId); @@ -200,14 +200,14 @@ contract("Tasks extension", accounts => { }); it("should log TaskAdded and TaskDueDateSet events", async () => { - await expectAllEvents(tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0), ["TaskAdded", "TaskDueDateSet"]); + await expectAllEvents(tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true), ["TaskAdded", "TaskDueDateSet"]); }); it("should optionally set the skill and due date", async () => { const currTime = await currentBlockTime(); const dueDate = currTime + SECONDS_PER_DAY; - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, dueDate); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, dueDate, true); const taskId = await tasks.getTaskCount(); const task = await tasks.getTask(taskId); @@ -218,7 +218,7 @@ contract("Tasks extension", accounts => { }); it("should set the due date to 90 days from now if unspecified", async () => { - const tx = await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0); + const tx = await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true); const taskId = await tasks.getTaskCount(); const task = await tasks.getTask(taskId); @@ -229,7 +229,7 @@ contract("Tasks extension", accounts => { describe("when updating tasks", () => { it("should not be able to `executeTaskRoleAssignment` on a nonexistent task", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -246,12 +246,12 @@ contract("Tasks extension", accounts => { }); it("should not be able to `executeTaskRoleAssignment` on a finalized task", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); await submitDeliverableAndRatings({ tasks, taskId }); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); await checkErrorRevert( executeSignedRoleAssignment({ @@ -267,7 +267,7 @@ contract("Tasks extension", accounts => { }); it("should not be able to pass unallowed function signature to `executeTaskRoleAssignment`", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -284,7 +284,7 @@ contract("Tasks extension", accounts => { }); it("should not be able to send any ether while assigning a role", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const { sigV, sigR, sigS, txData } = await getSigsAndTransactionData({ @@ -300,7 +300,7 @@ contract("Tasks extension", accounts => { }); it("should not be able to execute task change when the number of signature parts differ", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const { sigV, sigR, sigS, txData } = await getSigsAndTransactionData({ @@ -322,7 +322,7 @@ contract("Tasks extension", accounts => { const newEvaluator = accounts[1]; expect(newEvaluator).to.not.equal(MANAGER); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedTaskChange({ @@ -360,7 +360,7 @@ contract("Tasks extension", accounts => { }); it("should not allow a worker to be assigned if the task has no skill", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -398,7 +398,7 @@ contract("Tasks extension", accounts => { const newEvaluator = accounts[1]; expect(newEvaluator).to.not.equal(MANAGER); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedTaskChange({ @@ -436,7 +436,7 @@ contract("Tasks extension", accounts => { }); it("should not allow role to be assigned if it is already assigned to somebody", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -474,7 +474,7 @@ contract("Tasks extension", accounts => { }); it("should allow role to be unassigned, as long as the current assigned address agrees", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -517,7 +517,7 @@ contract("Tasks extension", accounts => { }); it("should not allow role to be assigned if passed address is not equal to one of the signers", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -534,7 +534,7 @@ contract("Tasks extension", accounts => { }); it("should allow manager to assign themself to a role", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -551,7 +551,7 @@ contract("Tasks extension", accounts => { }); it("should not allow anyone to assign themself to a role with one signature except manager", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -568,7 +568,7 @@ contract("Tasks extension", accounts => { }); it("should allow different modes of signing when assigning roles", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -585,7 +585,7 @@ contract("Tasks extension", accounts => { }); it("should not allow role assignment if none of the signers is manager", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -602,7 +602,7 @@ contract("Tasks extension", accounts => { }); it("should allow to change manager role if the user agrees", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -619,7 +619,7 @@ contract("Tasks extension", accounts => { }); it("should not allow one-signature assignment of manager to a role if signer is not manager", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -636,7 +636,7 @@ contract("Tasks extension", accounts => { }); it("should not allow assignment of manager role if the user does not agree", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -653,7 +653,7 @@ contract("Tasks extension", accounts => { }); it("should not allow assignment of manager role if user is not an admin", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -670,7 +670,7 @@ contract("Tasks extension", accounts => { }); it("should not allow removal of manager role", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -690,7 +690,7 @@ contract("Tasks extension", accounts => { const newEvaluator = accounts[1]; expect(newEvaluator).to.not.equal(MANAGER); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); // Setting the worker @@ -737,10 +737,10 @@ contract("Tasks extension", accounts => { }); it("should correctly increment `taskChangeNonce` for multiple updates on multiple tasks", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId1 = await tasks.getTaskCount(); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId2 = await tasks.getTaskCount(); // Change the task1 brief @@ -784,7 +784,7 @@ contract("Tasks extension", accounts => { }); it("should allow update of task brief signed by manager only when worker has not been assigned", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedTaskChange({ @@ -801,7 +801,7 @@ contract("Tasks extension", accounts => { }); it("should allow update of task brief signed by manager and worker", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -827,7 +827,7 @@ contract("Tasks extension", accounts => { }); it("should allow update of task brief signed by manager and worker using Trezor-style signatures", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -853,7 +853,7 @@ contract("Tasks extension", accounts => { }); it("should allow update of task brief signed by manager and worker if one uses Trezor-style signatures and the other does not", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -879,7 +879,7 @@ contract("Tasks extension", accounts => { }); it("should not allow update of task brief signed by manager twice, with two different signature styles", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -906,7 +906,7 @@ contract("Tasks extension", accounts => { it("should allow update of task due date signed by manager and worker", async () => { const dueDate = await currentBlockTime(); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -932,14 +932,14 @@ contract("Tasks extension", accounts => { }); it("should fail if a non-colony call is made to the task update functions", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert(tasks.setTaskBrief(taskId, SPECIFICATION_HASH_UPDATED, { from: OTHER }), "colony-task-not-self"); }); it("should fail update of task brief signed by a non-registered role", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -956,7 +956,7 @@ contract("Tasks extension", accounts => { }); it("should fail update of task brief signed by manager and evaluator", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -982,7 +982,7 @@ contract("Tasks extension", accounts => { }); it("should fail to execute task change for a non-registered function signature", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -999,7 +999,7 @@ contract("Tasks extension", accounts => { }); it("should fail to execute change of task brief, using an invalid taskId", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const taskCount = await tasks.getTaskCount(); const nonExistentTaskId = taskCount.addn(10); @@ -1034,7 +1034,7 @@ contract("Tasks extension", accounts => { }); it("should fail to execute task changes, when trying to set skill to 0", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -1051,12 +1051,12 @@ contract("Tasks extension", accounts => { }); it("should fail to execute task change, if the task is already finalized", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); await submitDeliverableAndRatings({ tasks, taskId }); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); await checkErrorRevert( executeSignedTaskChange({ @@ -1072,7 +1072,7 @@ contract("Tasks extension", accounts => { }); it("should fail to change task manager, if the task is complete", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await forwardTime(90 * SECONDS_PER_DAY); @@ -1092,7 +1092,7 @@ contract("Tasks extension", accounts => { }); it("should log a TaskBriefSet event, if the task brief gets changed", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await expectEvent( @@ -1109,7 +1109,7 @@ contract("Tasks extension", accounts => { }); it("should log a TaskDueDateSet event, if the task due date gets changed", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const dueDate = await currentBlockTime(); @@ -1127,7 +1127,7 @@ contract("Tasks extension", accounts => { }); it("should fail to execute task change with a non zero value", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const { sigV, sigR, sigS, txData } = await getSigsAndTransactionData({ @@ -1143,7 +1143,7 @@ contract("Tasks extension", accounts => { }); it("should fail to execute task change with a mismatched set of signature parts", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const { sigV, sigR, sigS, txData } = await getSigsAndTransactionData({ @@ -1159,7 +1159,7 @@ contract("Tasks extension", accounts => { }); it("should fail to execute task change send for a task role assignment call (which should be using executeTaskRoleAssignment)", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const { sigV, sigR, sigS, txData } = await getSigsAndTransactionData({ @@ -1175,7 +1175,7 @@ contract("Tasks extension", accounts => { }); it("should fail to execute task change with the wrong signatures, one signer", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const { sigV, sigR, sigS, txData } = await getSigsAndTransactionData({ @@ -1191,7 +1191,7 @@ contract("Tasks extension", accounts => { }); it("should fail to execute task change with the wrong signatures, two signers", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -1218,7 +1218,7 @@ contract("Tasks extension", accounts => { describe("when submitting task deliverable", () => { it("should update task", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, evaluator: EVALUATOR, worker: WORKER }); @@ -1231,7 +1231,7 @@ contract("Tasks extension", accounts => { }); it("should fail if I try to submit work for a task that is complete", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, evaluator: EVALUATOR, worker: WORKER }); @@ -1242,18 +1242,18 @@ contract("Tasks extension", accounts => { }); it("should fail if I try to submit work for a task that is finalized", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); await submitDeliverableAndRatings({ tasks, taskId }); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); await checkErrorRevert(tasks.submitTaskDeliverable(taskId, DELIVERABLE_HASH, { from: WORKER }), "colony-task-complete"); }); it("should succeed if I try to submit work for a task that is past its due date but not yet marked as complete", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1265,14 +1265,14 @@ contract("Tasks extension", accounts => { }); it("should fail if I try to submit work for a task using an invalid id", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert(tasks.submitTaskDeliverable(taskId.addn(1), DELIVERABLE_HASH), "colony-task-does-not-exist"); }); it("should fail if I try to submit work twice", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1282,7 +1282,7 @@ contract("Tasks extension", accounts => { }); it("should fail if I try to mark a taske complete after work is submitted", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1292,7 +1292,7 @@ contract("Tasks extension", accounts => { }); it("should fail if I try to submit work if I'm not the assigned worker", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: OTHER }); @@ -1300,7 +1300,7 @@ contract("Tasks extension", accounts => { }); it("should log a TaskDeliverableSubmitted event", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1308,7 +1308,7 @@ contract("Tasks extension", accounts => { }); it("should fail if I try to complete the task before the due date", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1318,14 +1318,14 @@ contract("Tasks extension", accounts => { describe("when evaluating a task", () => { it("should fail if I try to evaluate before work is submitted", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert(tasks.submitTaskWorkRating(taskId, WORKER_ROLE, RATING_2_SECRET, { from: EVALUATOR }), "colony-task-not-complete"); }); it("should fail if I try to evaluate twice", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1338,7 +1338,7 @@ contract("Tasks extension", accounts => { }); it("should fail if the wrong user tries to rate the wrong role", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1351,7 +1351,7 @@ contract("Tasks extension", accounts => { }); it("can retreive rating secret information", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1368,7 +1368,7 @@ contract("Tasks extension", accounts => { }); it("should fail if the user tries to rate too late", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1383,7 +1383,7 @@ contract("Tasks extension", accounts => { }); it("should not allow a user to reveal after the deadline, with two secrets", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1399,7 +1399,7 @@ contract("Tasks extension", accounts => { }); it("should not allow a user to reveal after the deadline, with one secret", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1414,7 +1414,7 @@ contract("Tasks extension", accounts => { }); it("should not allow a user to reveal during the submission period", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1428,7 +1428,7 @@ contract("Tasks extension", accounts => { }); it("should not allow a user to reveal a non-matching rating", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1443,7 +1443,7 @@ contract("Tasks extension", accounts => { }); it("should not allow a user to reveal a rating of None", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1457,48 +1457,59 @@ contract("Tasks extension", accounts => { describe("when finalizing a task", () => { it('should set the task "status" property to "finalized"', async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); await submitDeliverableAndRatings({ tasks, taskId }); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); const task = await tasks.getTask(taskId); const expenditure = await colony.getExpenditure(task.expenditureId); expect(expenditure.status).to.eq.BN(FINALIZED_TASK_STATE); }); + it.only("should fail if I try to finalize a task twice", async () => { + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); + const taskId = await tasks.getTaskCount(); + + await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); + await submitDeliverableAndRatings({ tasks, taskId }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); + + await checkErrorRevert(tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }), "colony-expenditure-not-active"); + }); + it("should fail if I try to finalize a task that is not complete", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); - await checkErrorRevert(tasks.finalizeTask(1, 0, taskId), "colony-task-not-complete"); + await checkErrorRevert(tasks.finalizeSecureTask(1, 0, taskId), "colony-task-not-complete"); }); it("should fail if the task work ratings have not been assigned and they still have time to be", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); await tasks.submitTaskDeliverable(taskId, SPECIFICATION_HASH, { from: WORKER }); - await checkErrorRevert(tasks.finalizeTask(1, 0, taskId), "colony-task-ratings-not-closed"); + await checkErrorRevert(tasks.finalizeSecureTask(1, 0, taskId), "colony-task-ratings-not-closed"); }); it("should fail if the task work ratings have not been revealed and they still have time to be", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); await tasks.submitTaskDeliverableAndRating(taskId, SPECIFICATION_HASH, RATING_1_SECRET, { from: WORKER }); await tasks.submitTaskWorkRating(taskId, WORKER_ROLE, RATING_2_SECRET, { from: MANAGER }); - await checkErrorRevert(tasks.finalizeTask(1, 0, taskId), "colony-task-ratings-not-closed"); + await checkErrorRevert(tasks.finalizeSecureTask(1, 0, taskId), "colony-task-ratings-not-closed"); }); it("should finalize if the rate and reveal period have elapsed", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1506,26 +1517,26 @@ contract("Tasks extension", accounts => { // No ratings submitted, so must wait for both rate and reveal periods to elapse await forwardTime(SECONDS_PER_DAY * 10 + 1); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); }); it("should finalize if only the reveal period has elapsed after both secrets are submitted", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); await tasks.submitTaskDeliverableAndRating(taskId, SPECIFICATION_HASH, RATING_1_SECRET, { from: WORKER }); await tasks.submitTaskWorkRating(taskId, WORKER_ROLE, RATING_2_SECRET, { from: MANAGER }); - await checkErrorRevert(tasks.finalizeTask(1, 0, taskId), "colony-task-ratings-not-closed"); + await checkErrorRevert(tasks.finalizeSecureTask(1, 0, taskId), "colony-task-ratings-not-closed"); // Both secrets submitted, so we only have to wait for the reveal period to elapse await forwardTime(SECONDS_PER_DAY * 5 + 1); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); }); it("should assign manager and worker maximum rating if unrated", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const task = await tasks.getTask(taskId); @@ -1533,7 +1544,7 @@ contract("Tasks extension", accounts => { await tasks.submitTaskDeliverable(taskId, SPECIFICATION_HASH, { from: WORKER }); forwardTime(SECONDS_PER_DAY * 10 + 1); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); const managerSlot = await colony.getExpenditureSlot(task.expenditureId, MANAGER_ROLE); const evaluatorSlot = await colony.getExpenditureSlot(task.expenditureId, EVALUATOR_ROLE); @@ -1545,7 +1556,7 @@ contract("Tasks extension", accounts => { }); it("should fail if it's not sufficiently funded to support all its payouts", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedTaskChange({ @@ -1560,30 +1571,30 @@ contract("Tasks extension", accounts => { await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); await submitDeliverableAndRatings({ tasks, taskId }); - await checkErrorRevert(tasks.finalizeTask(1, 0, taskId), "colony-expenditure-not-funded"); + await checkErrorRevert(tasks.finalizeSecureTask(1, 0, taskId), "colony-expenditure-not-funded"); }); it("should fail if I try to accept a task that was finalized before", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); await submitDeliverableAndRatings({ tasks, taskId }); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); - await checkErrorRevert(tasks.finalizeTask(1, 0, taskId), "colony-expenditure-not-active"); + await checkErrorRevert(tasks.finalizeSecureTask(1, 0, taskId), "colony-expenditure-not-active"); }); it("should fail if I try to accept a task using an invalid id", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); - await checkErrorRevert(tasks.finalizeTask(1, 0, taskId.addn(1)), "colony-task-does-not-exist"); + await checkErrorRevert(tasks.finalizeSecureTask(1, 0, taskId.addn(1)), "colony-task-does-not-exist"); }); it("should emit two negative reputation updates for a bad worker rating", async () => { await fundColonyWithTokens(colony, token, INITIAL_FUNDING); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const task = await tasks.getTask(taskId); const expenditure = await colony.getExpenditure(task.expenditureId); @@ -1602,7 +1613,7 @@ contract("Tasks extension", accounts => { await forwardTime(SECONDS_PER_DAY * 5 + 1); await tasks.revealTaskWorkRating(taskId, WORKER_ROLE, 1, RATING_2_SALT, { from: MANAGER }); await forwardTime(SECONDS_PER_DAY * 5 + 1); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); const numEntriesAfter = await repCycle.getReputationUpdateLogLength(); expect(numEntriesAfter.sub(numEntriesBefore)).to.eq.BN(2); @@ -1622,7 +1633,7 @@ contract("Tasks extension", accounts => { it("should emit one negative reputation update for a bad manager/evaluator rating", async () => { await fundColonyWithTokens(colony, token, INITIAL_FUNDING); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const task = await tasks.getTask(taskId); const expenditure = await colony.getExpenditure(task.expenditureId); @@ -1635,7 +1646,7 @@ contract("Tasks extension", accounts => { await colony.moveFundsBetweenPots(1, 0, 0, domain1.fundingPotId, expenditure.fundingPotId, WAD, token.address); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); await submitDeliverableAndRatings({ tasks, taskId, managerRating: 1 }); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); const numEntriesAfter = await repCycle.getReputationUpdateLogLength(); expect(numEntriesAfter.sub(numEntriesBefore)).to.eq.BN(1); @@ -1649,7 +1660,7 @@ contract("Tasks extension", accounts => { describe("when cancelling a task", () => { it('should set the task "status" property to "cancelled"', async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedTaskChange({ @@ -1667,7 +1678,7 @@ contract("Tasks extension", accounts => { }); it("should fail if manager tries to cancel a task that was completed", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); @@ -1687,7 +1698,7 @@ contract("Tasks extension", accounts => { }); it("should fail if manager tries to cancel a task with invalid id", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( @@ -1706,7 +1717,7 @@ contract("Tasks extension", accounts => { describe("when funding tasks", () => { it("should be able to set the task payouts for different roles", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -1794,7 +1805,7 @@ contract("Tasks extension", accounts => { }); it("should be able (if manager) to set all payments at once if evaluator and worker are manager or unassigned", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await checkErrorRevert( tasks.setAllTaskPayouts(taskId, ethers.constants.AddressZero, 5000, 1000, 98000, { from: OTHER }), @@ -1813,7 +1824,7 @@ contract("Tasks extension", accounts => { }); it("should not be able to set all payments at once if worker is assigned and is not the manager", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedRoleAssignment({ @@ -1829,7 +1840,7 @@ contract("Tasks extension", accounts => { }); it("should not be able to set all payments at once if evaluator is assigned and is not the manager", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await executeSignedTaskChange({ @@ -1857,7 +1868,7 @@ contract("Tasks extension", accounts => { }); it("should correctly return the current total payout", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await tasks.setAllTaskPayouts(taskId, token.address, MANAGER_PAYOUT, EVALUATOR_PAYOUT, WORKER_PAYOUT); @@ -1871,7 +1882,7 @@ contract("Tasks extension", accounts => { it("should be possible to return funds back to the domain if cancelled", async () => { await fundColonyWithTokens(colony, token, INITIAL_FUNDING); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await tasks.setAllTaskPayouts(taskId, token.address, 0, 0, WAD); @@ -1934,7 +1945,7 @@ contract("Tasks extension", accounts => { it("should payout agreed ether and tokens for a task", async () => { await fundColonyWithTokens(colony, token, INITIAL_FUNDING); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); // Setup payouts @@ -1952,7 +1963,7 @@ contract("Tasks extension", accounts => { // Complete task await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); await submitDeliverableAndRatings({ tasks, taskId }); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); // Claim payouts const workerEtherBalanceBefore = await web3GetBalance(WORKER); @@ -1971,7 +1982,7 @@ contract("Tasks extension", accounts => { it("should disburse nothing for unsatisfactory work, for manager and worker", async () => { await fundColonyWithTokens(colony, token, INITIAL_FUNDING); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await tasks.setAllTaskPayouts(taskId, token.address, MANAGER_PAYOUT, EVALUATOR_PAYOUT, WORKER_PAYOUT); @@ -1983,7 +1994,7 @@ contract("Tasks extension", accounts => { await assignRoles({ tasks, taskId, manager: MANAGER, evaluator: EVALUATOR, worker: WORKER }); await submitDeliverableAndRatings({ tasks, taskId, managerRating: 1, workerRating: 1 }); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); const managerSlot = await colony.getExpenditureSlot(task.expenditureId, MANAGER_ROLE); const evaluatorSlot = await colony.getExpenditureSlot(task.expenditureId, EVALUATOR_ROLE); @@ -2013,7 +2024,7 @@ contract("Tasks extension", accounts => { it("should disburse nothing for unsatisfactory work, for evaluator", async () => { await fundColonyWithTokens(colony, token, INITIAL_FUNDING); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await tasks.setAllTaskPayouts(taskId, token.address, MANAGER_PAYOUT, EVALUATOR_PAYOUT, WORKER_PAYOUT); @@ -2029,7 +2040,7 @@ contract("Tasks extension", accounts => { await forwardTime(SECONDS_PER_DAY * 5 + 1); await tasks.revealTaskWorkRating(taskId, MANAGER_ROLE, MANAGER_RATING, RATING_1_SALT, { from: WORKER }); await forwardTime(SECONDS_PER_DAY * 5 + 1); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); const managerSlot = await colony.getExpenditureSlot(task.expenditureId, MANAGER_ROLE); const evaluatorSlot = await colony.getExpenditureSlot(task.expenditureId, EVALUATOR_ROLE); @@ -2067,7 +2078,7 @@ contract("Tasks extension", accounts => { it("should let funds be reclaimed after unsatisfactory reviews", async () => { await fundColonyWithTokens(colony, token, INITIAL_FUNDING); - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); await tasks.setAllTaskPayouts(taskId, token.address, 0, 0, WORKER_PAYOUT); @@ -2078,18 +2089,106 @@ contract("Tasks extension", accounts => { await assignRoles({ tasks, taskId, manager: MANAGER, worker: WORKER }); await submitDeliverableAndRatings({ tasks, taskId, workerRating: 1 }); - await tasks.finalizeTask(1, 0, taskId, { from: MANAGER }); + await tasks.finalizeSecureTask(1, 0, taskId, { from: MANAGER }); await colony.claimExpenditurePayout(task.expenditureId, WORKER_ROLE, token.address); await colony.moveFundsBetweenPots(1, 0, 0, expenditure.fundingPotId, 1, WORKER_PAYOUT, token.address); }); it("should return error when task is not finalized", async () => { - await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, { from: MANAGER }); + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, true, { from: MANAGER }); const taskId = await tasks.getTaskCount(); const task = await tasks.getTask(taskId); await checkErrorRevert(colony.claimExpenditurePayout(task.expenditureId, MANAGER_ROLE, token.address), "colony-expenditure-not-finalized"); }); }); + + describe.only("managed tasks", () => { + it("should allow admins to make managed tasks", async () => { + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, false, { from: MANAGER }); + const taskId = await tasks.getTaskCount(); + const task = await tasks.getTask(taskId); + expect(task.secure).to.be.false; + }); + + it("should allow managers to edit task attributes without multi-sig", async () => { + const currTime = await currentBlockTime(); + const dueDate = currTime + SECONDS_PER_DAY; + + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, 0, 0, false, { from: MANAGER }); + const taskId = await tasks.getTaskCount(); + + await tasks.setTaskManagerRole(taskId, ADMIN, 1, 0); + await tasks.setTaskManagerRole(taskId, MANAGER, 1, 0, { from: ADMIN }); + + await tasks.setTaskSkill(taskId, GLOBAL_SKILL_ID); + + await tasks.setTaskEvaluatorRole(taskId, EVALUATOR); + await tasks.setTaskWorkerRole(taskId, WORKER); + + await tasks.setTaskBrief(taskId, SPECIFICATION_HASH_UPDATED); + await tasks.setTaskDueDate(taskId, dueDate); + + await tasks.setTaskManagerPayout(taskId, token.address, MANAGER_PAYOUT); + await tasks.setTaskEvaluatorPayout(taskId, token.address, EVALUATOR_PAYOUT); + await tasks.setTaskWorkerPayout(taskId, token.address, WORKER_PAYOUT); + + await tasks.removeTaskEvaluatorRole(taskId); + await tasks.removeTaskWorkerRole(taskId); + + await tasks.cancelTask(taskId); + }); + + it("should allow managers to finalize a task, with an implicit rating of 2", async () => { + await fundColonyWithTokens(colony, token, INITIAL_FUNDING); + + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, false, { from: MANAGER }); + const taskId = await tasks.getTaskCount(); + + const task = await tasks.getTask(taskId); + const expenditure = await colony.getExpenditure(task.expenditureId); + const totalPayout = MANAGER_PAYOUT.add(WORKER_PAYOUT); + await colony.moveFundsBetweenPots(1, 0, 0, domain1.fundingPotId, expenditure.fundingPotId, totalPayout, token.address); + + await tasks.setTaskWorkerRole(taskId, WORKER); + + await tasks.setTaskManagerPayout(taskId, token.address, MANAGER_PAYOUT); + await tasks.setTaskWorkerPayout(taskId, token.address, WORKER_PAYOUT); + + await tasks.finalizeManagedTask(taskId); + }); + + it("should allow managers to convert managed tasks to secure tasks (and back)", async () => { + await tasks.makeTask(1, 0, 1, 0, SPECIFICATION_HASH, 1, GLOBAL_SKILL_ID, 0, false, { from: MANAGER }); + const taskId = await tasks.getTaskCount(); + + let task; + task = await tasks.getTask(taskId); + expect(task.secure).to.be.false; + + await tasks.setTaskWorkerRole(taskId, WORKER); + await tasks.setTaskEvaluatorRole(taskId, EVALUATOR); + + await tasks.setTaskSecurity(taskId, true); + task = await tasks.getTask(taskId); + expect(task.secure).to.be.true; + + await executeSignedTaskChange({ + tasks, + taskId, + functionName: "setTaskSecurity", + signers: [MANAGER, WORKER], + sigTypes: [0, 0], + args: [taskId, false] + }); + + task = await tasks.getTask(taskId); + expect(task.secure).to.be.false; + + // Setting to managed removes the evaluator + const evaluator = await tasks.getTaskRoleUser(taskId, EVALUATOR_ROLE); + expect(evaluator).to.equal(ethers.constants.AddressZero); + }); + }); });