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