pragma solidity ^0.4.24; // Author: Bruno Block // Version: 0.5 interface contractInterface { function balanceOf(address _owner) external constant returns (uint256 balance); function transfer(address _to, uint256 _value) external; } contract DualSig { address public directorA; address public directorB; address public mediator; address public maliciousDirector; address public proposalAuthor; address public proposalContract; address public proposalDestination; uint256 public proposalAmount; uint256 public proposalBlock; uint256 public proposalNonce; uint256 public overrideBlock; uint256 public lastAcceptBlock; uint256 public transferSafety; event Proposal(uint256 _nonce, address _author, address _contract, uint256 _amount, address _destination, uint256 _timestamp); event Accept(uint256 _nonce); event NewDirectorA(address _director); event NewDirectorB(address _director); event NewMediator(address _mediator); event ReplaceDirectorProposal(address _maliciousDirector); event ReplaceDirectorAccept(address _maliciousDirector); modifier onlyDirectors { require(msg.sender == directorA || msg.sender == directorB); _; } modifier onlyMediator { require(msg.sender == mediator); _; } constructor() public { overrideBlock = (60*60*24*30)/15;// One month override interval assuming 15 second blocks (172,800 blocks) proposalNonce = 0; transferSafety = 1 ether; directorA = msg.sender; directorB = msg.sender; mediator = msg.sender; lastAcceptBlock = 0; maliciousDirector = 0x0; reset(); } function () public payable {} function proposal(address proposalContractSet, uint256 proposalAmountSet, address proposalDestinationSet) public onlyDirectors { proposalNonce++; proposalAuthor = msg.sender; proposalContract = proposalContractSet; proposalAmount = proposalAmountSet; proposalDestination = proposalDestinationSet; proposalBlock = block.number + overrideBlock; emit Proposal(proposalNonce, proposalAuthor, proposalContract, proposalAmount, proposalDestination, proposalBlock); } function reset() public onlyDirectors { proposalNonce++; if (proposalNonce > 1000000) { proposalNonce = 0; } proposalAuthor = 0x0; proposalContract = 0x0; proposalAmount = 0; proposalDestination = 0x0; proposalBlock = 0; } function accept(uint256 acceptNonce) public onlyDirectors { require(proposalNonce == acceptNonce); require(proposalAmount > 0); require(proposalDestination != 0x0); require(proposalAuthor != msg.sender || block.number >= proposalBlock); address localContract = proposalContract; address localDestination = proposalDestination; uint256 localAmount = proposalAmount; reset(); if (localContract==0x0) { require(localAmount <= address(this).balance); localDestination.transfer(localAmount); } else { contractInterface tokenContract = contractInterface(localContract); tokenContract.transfer(localDestination, localAmount); } lastAcceptBlock = block.number; emit Accept(acceptNonce); } function transferDirectorA(address newDirectorA) public payable { require(msg.sender==directorA); require(msg.value==transferSafety);// Prevents accidental transfer directorA.transfer(transferSafety);// Reimburse safety deposit reset(); if (maliciousDirector == directorA) { maliciousDirector = newDirectorA; } directorA = newDirectorA; emit NewDirectorA(directorA); } function transferDirectorB(address newDirectorB) public payable { require(msg.sender==directorB); require(msg.value==transferSafety);// Prevents accidental transfer directorB.transfer(transferSafety);// Reimburse safety deposit reset(); if (maliciousDirector == directorB) { maliciousDirector = newDirectorB; } directorB = newDirectorB; emit NewDirectorB(directorB); } function transferMediator(address newMediator) public onlyMediator payable { require(msg.value==transferSafety);// Prevents accidental transfer mediator.transfer(transferSafety);// Reimburse safety deposit reset(); mediator = newMediator; emit NewMediator(mediator); } function replaceDirectorProposal(address _maliciousDirector) public onlyMediator { require(lastAcceptBlock != 0 && block.number > lastAcceptBlock + 2*overrideBlock);// confirms that directorA and directorB didn't accept any proposal for 2*overrideBlock blocks require(_maliciousDirector == directorA || _maliciousDirector == directorB); maliciousDirector = _maliciousDirector; emit ReplaceDirectorProposal(maliciousDirector); } function replaceDirectorAccept(address _maliciousDirector) public onlyDirectors { require(maliciousDirector == directorA || maliciousDirector == directorB); require(maliciousDirector == _maliciousDirector); emit ReplaceDirectorAccept(_maliciousDirector); if (_maliciousDirector == directorA) { directorA = mediator; emit NewDirectorA(mediator); } else if (_maliciousDirector == directorB) { directorB = mediator; emit NewDirectorB(mediator); } maliciousDirector = 0x0; lastAcceptBlock = 0; // in case a malicious mediator somehow manages to become director, at least 1 accepted proposal needed for him to take over } }
0.4.24