github.com/Elemental-core/elementalcore@v0.0.0-20191206075037-63891242267a/contracts/release/contract.sol (about) 1 // Copyright 2016 The elementalcore Authors 2 // This file is part of the elementalcore library. 3 // 4 // The elementalcore library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The elementalcore library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the elementalcore library. If not, see <http://www.gnu.org/licenses/>. 16 17 // ReleaseOracle is an Ethereum contract to store the current and previous 18 // versions of the elementalcore implementation. Its goal is to allow Geth to 19 // check for new releases automatically without the need to consult a central 20 // repository. 21 // 22 // The contract takes a vote based approach on both assigning authorised signers 23 // as well as signing off on new Geth releases. 24 // 25 // Note, when a signer is demoted, the currently pending release is auto-nuked. 26 // The reason is to prevent suprises where a demotion actually tilts the votes 27 // in favor of one voter party and pushing out a new release as a consequence of 28 // a simple demotion. 29 contract ReleaseOracle { 30 // Votes is an internal data structure to count votes on a specific proposal 31 struct Votes { 32 address[] pass; // List of signers voting to pass a proposal 33 address[] fail; // List of signers voting to fail a proposal 34 } 35 36 // Version is the version details of a particular Geth release 37 struct Version { 38 uint32 major; // Major version component of the release 39 uint32 minor; // Minor version component of the release 40 uint32 patch; // Patch version component of the release 41 bytes20 commit; // Git SHA1 commit hash of the release 42 43 uint64 time; // Timestamp of the release approval 44 Votes votes; // Votes that passed this release 45 } 46 47 // Oracle authorization details 48 mapping(address => bool) authorised; // Set of accounts allowed to vote on updating the contract 49 address[] voters; // List of addresses currently accepted as signers 50 51 // Various proposals being voted on 52 mapping(address => Votes) authProps; // Currently running user authorization proposals 53 address[] authPend; // List of addresses being voted on (map indexes) 54 55 Version verProp; // Currently proposed release being voted on 56 Version[] releases; // All the positively voted releases 57 58 // isSigner is a modifier to authorize contract transactions. 59 modifier isSigner() { 60 if (authorised[msg.sender]) { 61 _ 62 } 63 } 64 65 // Constructor to assign the initial set of signers. 66 function ReleaseOracle(address[] signers) { 67 // If no signers were specified, assign the creator as the sole signer 68 if (signers.length == 0) { 69 authorised[msg.sender] = true; 70 voters.push(msg.sender); 71 return; 72 } 73 // Otherwise assign the individual signers one by one 74 for (uint i = 0; i < signers.length; i++) { 75 authorised[signers[i]] = true; 76 voters.push(signers[i]); 77 } 78 } 79 80 // signers is an accessor method to retrieve all te signers (public accessor 81 // generates an indexed one, not a retrieve-all version). 82 function signers() constant returns(address[]) { 83 return voters; 84 } 85 86 // authProposals retrieves the list of addresses that authorization proposals 87 // are currently being voted on. 88 function authProposals() constant returns(address[]) { 89 return authPend; 90 } 91 92 // authVotes retrieves the current authorization votes for a particular user 93 // to promote him into the list of signers, or demote him from there. 94 function authVotes(address user) constant returns(address[] promote, address[] demote) { 95 return (authProps[user].pass, authProps[user].fail); 96 } 97 98 // currentVersion retrieves the semantic version, commit hash and release time 99 // of the currently votec active release. 100 function currentVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, uint time) { 101 if (releases.length == 0) { 102 return (0, 0, 0, 0, 0); 103 } 104 var release = releases[releases.length - 1]; 105 106 return (release.major, release.minor, release.patch, release.commit, release.time); 107 } 108 109 // proposedVersion retrieves the semantic version, commit hash and the current 110 // votes for the next proposed release. 111 function proposedVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, address[] pass, address[] fail) { 112 return (verProp.major, verProp.minor, verProp.patch, verProp.commit, verProp.votes.pass, verProp.votes.fail); 113 } 114 115 // promote pitches in on a voting campaign to promote a new user to a signer 116 // position. 117 function promote(address user) { 118 updateSigner(user, true); 119 } 120 121 // demote pitches in on a voting campaign to demote an authorised user from 122 // its signer position. 123 function demote(address user) { 124 updateSigner(user, false); 125 } 126 127 // release votes for a particular version to be included as the next release. 128 function release(uint32 major, uint32 minor, uint32 patch, bytes20 commit) { 129 updateRelease(major, minor, patch, commit, true); 130 } 131 132 // nuke votes for the currently proposed version to not be included as the next 133 // release. Nuking doesn't require a specific version number for simplicity. 134 function nuke() { 135 updateRelease(0, 0, 0, 0, false); 136 } 137 138 // updateSigner marks a vote for changing the status of an Ethereum user, either 139 // for or against the user being an authorised signer. 140 function updateSigner(address user, bool authorize) internal isSigner { 141 // Gather the current votes and ensure we don't double vote 142 Votes votes = authProps[user]; 143 for (uint i = 0; i < votes.pass.length; i++) { 144 if (votes.pass[i] == msg.sender) { 145 return; 146 } 147 } 148 for (i = 0; i < votes.fail.length; i++) { 149 if (votes.fail[i] == msg.sender) { 150 return; 151 } 152 } 153 // If no authorization proposal is open, add the user to the index for later lookups 154 if (votes.pass.length == 0 && votes.fail.length == 0) { 155 authPend.push(user); 156 } 157 // Cast the vote and return if the proposal cannot be resolved yet 158 if (authorize) { 159 votes.pass.push(msg.sender); 160 if (votes.pass.length <= voters.length / 2) { 161 return; 162 } 163 } else { 164 votes.fail.push(msg.sender); 165 if (votes.fail.length <= voters.length / 2) { 166 return; 167 } 168 } 169 // Proposal resolved in our favor, execute whatever we voted on 170 if (authorize && !authorised[user]) { 171 authorised[user] = true; 172 voters.push(user); 173 } else if (!authorize && authorised[user]) { 174 authorised[user] = false; 175 176 for (i = 0; i < voters.length; i++) { 177 if (voters[i] == user) { 178 voters[i] = voters[voters.length - 1]; 179 voters.length--; 180 181 delete verProp; // Nuke any version proposal (no surprise releases!) 182 break; 183 } 184 } 185 } 186 // Finally delete the resolved proposal, index and garbage collect 187 delete authProps[user]; 188 189 for (i = 0; i < authPend.length; i++) { 190 if (authPend[i] == user) { 191 authPend[i] = authPend[authPend.length - 1]; 192 authPend.length--; 193 break; 194 } 195 } 196 } 197 198 // updateRelease votes for a particular version to be included as the next release, 199 // or for the currently proposed release to be nuked out. 200 function updateRelease(uint32 major, uint32 minor, uint32 patch, bytes20 commit, bool release) internal isSigner { 201 // Skip nuke votes if no proposal is pending 202 if (!release && verProp.votes.pass.length == 0) { 203 return; 204 } 205 // Mark a new release if no proposal is pending 206 if (verProp.votes.pass.length == 0) { 207 verProp.major = major; 208 verProp.minor = minor; 209 verProp.patch = patch; 210 verProp.commit = commit; 211 } 212 // Make sure positive votes match the current proposal 213 if (release && (verProp.major != major || verProp.minor != minor || verProp.patch != patch || verProp.commit != commit)) { 214 return; 215 } 216 // Gather the current votes and ensure we don't double vote 217 Votes votes = verProp.votes; 218 for (uint i = 0; i < votes.pass.length; i++) { 219 if (votes.pass[i] == msg.sender) { 220 return; 221 } 222 } 223 for (i = 0; i < votes.fail.length; i++) { 224 if (votes.fail[i] == msg.sender) { 225 return; 226 } 227 } 228 // Cast the vote and return if the proposal cannot be resolved yet 229 if (release) { 230 votes.pass.push(msg.sender); 231 if (votes.pass.length <= voters.length / 2) { 232 return; 233 } 234 } else { 235 votes.fail.push(msg.sender); 236 if (votes.fail.length <= voters.length / 2) { 237 return; 238 } 239 } 240 // Proposal resolved in our favor, execute whatever we voted on 241 if (release) { 242 verProp.time = uint64(now); 243 releases.push(verProp); 244 delete verProp; 245 } else { 246 delete verProp; 247 } 248 } 249 }