contract_extender.sol 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. pragma solidity ^0.5.3;
  2. contract ContractExtender {
  3. //target details - what, who and when to extend
  4. address public creator;
  5. string public targetRecipientPTMKey;
  6. address public contractToExtend;
  7. //list of wallet addresses that can cast votes
  8. address[] public walletAddressesToVote;
  9. uint256 public totalNumberOfVoters;
  10. mapping(address => bool) walletAddressesToVoteMap;
  11. uint256 numberOfVotesSoFar;
  12. mapping(address => bool) hasVotedMapping;
  13. mapping(address => bool) public votes;
  14. //contains the total outcome of voting
  15. //true if ALL nodes vote true, false if ANY node votes false
  16. bool public voteOutcome;
  17. //the hash of the shared payload
  18. string public sharedDataHash;
  19. string[] uuids;
  20. //if creator cancelled this extension
  21. bool public isFinished;
  22. // General housekeeping
  23. event NewContractExtensionContractCreated(address toExtend, string recipientPTMKey, address recipientAddress); //to tell nodes a new extension is happening
  24. event AllNodesHaveAccepted(bool outcome); //when all nodes have voted
  25. event CanPerformStateShare(); //when all nodes have voted & the recipient has accepted
  26. event ExtensionFinished(); //if the extension is cancelled or completed
  27. event NewVote(bool vote, address voter); // when someone voted (either true or false)
  28. event StateShared(address toExtend, string tesserahash, string uuid); //when the state is shared and can be replayed into the database
  29. event UpdateMembers(address toExtend, string uuid); //to update the original transaction hash for the new party member
  30. constructor(address contractAddress, address recipientAddress, string memory recipientPTMKey) public {
  31. creator = msg.sender;
  32. targetRecipientPTMKey = recipientPTMKey;
  33. contractToExtend = contractAddress;
  34. walletAddressesToVote.push(msg.sender);
  35. walletAddressesToVote.push(recipientAddress);
  36. sharedDataHash = "";
  37. voteOutcome = true;
  38. numberOfVotesSoFar = 0;
  39. for (uint256 i = 0; i < walletAddressesToVote.length; i++) {
  40. walletAddressesToVoteMap[walletAddressesToVote[i]] = true;
  41. }
  42. totalNumberOfVoters = walletAddressesToVote.length;
  43. emit NewContractExtensionContractCreated(contractAddress, recipientPTMKey, recipientAddress);
  44. }
  45. /////////////////////////////////////////////////////////////////////////////////////
  46. //modifiers
  47. /////////////////////////////////////////////////////////////////////////////////////
  48. modifier notFinished() {
  49. require(!isFinished, "extension has been marked as finished");
  50. _;
  51. }
  52. modifier onlyCreator() {
  53. require(msg.sender == creator, "only leader may perform this action");
  54. _;
  55. }
  56. /////////////////////////////////////////////////////////////////////////////////////
  57. //main
  58. /////////////////////////////////////////////////////////////////////////////////////
  59. function haveAllNodesVoted() public view returns (bool) {
  60. return walletAddressesToVote.length == numberOfVotesSoFar;
  61. }
  62. // returns true if the sender address has already voted on the
  63. // extension contracts
  64. function checkIfVoted() public view returns (bool) {
  65. return hasVotedMapping[msg.sender];
  66. }
  67. // returns true if the contract extension is finished
  68. function checkIfExtensionFinished() public view returns (bool) {
  69. return isFinished;
  70. }
  71. // single node vote to either extend or not
  72. // can't have voted before
  73. function doVote(bool vote, string memory nextuuid) public notFinished() {
  74. cast(vote);
  75. if (vote) {
  76. setUuid(nextuuid);
  77. }
  78. // check if voting has finished
  79. checkVotes();
  80. emit NewVote(vote, msg.sender);
  81. }
  82. // this event is emitted to tell each node to use this tx as the original tx
  83. // only if they voted for it
  84. function updatePartyMembers() public {
  85. for(uint256 i = 0; i < uuids.length; i++) {
  86. emit UpdateMembers(contractToExtend, uuids[i]);
  87. }
  88. }
  89. //state has been shared off chain via a private transaction, the hash the PTM generated is set here
  90. function setSharedStateHash(string memory hash) public onlyCreator() notFinished() {
  91. bytes memory hashAsBytes = bytes(sharedDataHash);
  92. bytes memory incomingAsBytes = bytes(hash);
  93. require(incomingAsBytes.length != 0, "new hash cannot be empty");
  94. require(hashAsBytes.length == 0, "state hash already set");
  95. sharedDataHash = hash;
  96. for(uint256 i = 0; i < uuids.length; i++) {
  97. emit StateShared(contractToExtend, sharedDataHash, uuids[i]);
  98. }
  99. finish();
  100. }
  101. //close the contract to further modifications
  102. function finish() public notFinished() onlyCreator() {
  103. setFinished();
  104. }
  105. //this sets a unique code that only the sending node has access to, that can be referred to later
  106. function setUuid(string memory nextuuid) public notFinished() {
  107. uuids.push(nextuuid);
  108. }
  109. // Internal methods
  110. function setFinished() internal {
  111. isFinished = true;
  112. emit ExtensionFinished();
  113. }
  114. // checks if all the conditions for voting have been met
  115. // either all voted true and target accepted, or someone voted false
  116. function checkVotes() internal {
  117. if (!voteOutcome) {
  118. emit AllNodesHaveAccepted(false);
  119. setFinished();
  120. return;
  121. }
  122. if (haveAllNodesVoted()) {
  123. emit AllNodesHaveAccepted(true);
  124. emit CanPerformStateShare();
  125. }
  126. }
  127. function cast(bool vote) internal {
  128. require(!isFinished, "extension process completed. cannot vote");
  129. require(walletAddressesToVoteMap[msg.sender], "not allowed to vote");
  130. require(!hasVotedMapping[msg.sender], "already voted");
  131. require(voteOutcome, "voting already declined");
  132. hasVotedMapping[msg.sender] = true;
  133. votes[msg.sender] = vote;
  134. numberOfVotesSoFar++;
  135. voteOutcome = voteOutcome && vote;
  136. }
  137. }