contract.sol 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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. pragma solidity ^0.4.18;
  17. // ReleaseOracle is an Ethereum contract to store the current and previous
  18. // versions of the go-ethereum 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. // Version is the version details of a particular Geth release
  36. struct Version {
  37. uint32 major; // Major version component of the release
  38. uint32 minor; // Minor version component of the release
  39. uint32 patch; // Patch version component of the release
  40. bytes20 commit; // Git SHA1 commit hash of the release
  41. uint64 time; // Timestamp of the release approval
  42. Votes votes; // Votes that passed this release
  43. }
  44. // Oracle authorization details
  45. mapping(address => bool) authorised; // Set of accounts allowed to vote on updating the contract
  46. address[] voters; // List of addresses currently accepted as signers
  47. // Various proposals being voted on
  48. mapping(address => Votes) authProps; // Currently running user authorization proposals
  49. address[] authPend; // List of addresses being voted on (map indexes)
  50. Version verProp; // Currently proposed release being voted on
  51. Version[] releases; // All the positively voted releases
  52. // isSigner is a modifier to authorize contract transactions.
  53. modifier isSigner() {
  54. if (authorised[msg.sender]) {
  55. _;
  56. }
  57. }
  58. // Constructor to assign the initial set of signers.
  59. function ReleaseOracle(address[] signers) {
  60. // If no signers were specified, assign the creator as the sole signer
  61. if (signers.length == 0) {
  62. authorised[msg.sender] = true;
  63. voters.push(msg.sender);
  64. return;
  65. }
  66. // Otherwise assign the individual signers one by one
  67. for (uint i = 0; i < signers.length; i++) {
  68. authorised[signers[i]] = true;
  69. voters.push(signers[i]);
  70. }
  71. }
  72. // signers is an accessor method to retrieve all the signers (public accessor
  73. // generates an indexed one, not a retrieve-all version).
  74. function signers() constant returns(address[]) {
  75. return voters;
  76. }
  77. // authProposals retrieves the list of addresses that authorization proposals
  78. // are currently being voted on.
  79. function authProposals() constant returns(address[]) {
  80. return authPend;
  81. }
  82. // authVotes retrieves the current authorization votes for a particular user
  83. // to promote him into the list of signers, or demote him from there.
  84. function authVotes(address user) constant returns(address[] promote, address[] demote) {
  85. return (authProps[user].pass, authProps[user].fail);
  86. }
  87. // currentVersion retrieves the semantic version, commit hash and release time
  88. // of the currently votec active release.
  89. function currentVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, uint time) {
  90. if (releases.length == 0) {
  91. return (0, 0, 0, 0, 0);
  92. }
  93. var release = releases[releases.length - 1];
  94. return (release.major, release.minor, release.patch, release.commit, release.time);
  95. }
  96. // proposedVersion retrieves the semantic version, commit hash and the current
  97. // votes for the next proposed release.
  98. function proposedVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, address[] pass, address[] fail) {
  99. return (verProp.major, verProp.minor, verProp.patch, verProp.commit, verProp.votes.pass, verProp.votes.fail);
  100. }
  101. // promote pitches in on a voting campaign to promote a new user to a signer
  102. // position.
  103. function promote(address user) {
  104. updateSigner(user, true);
  105. }
  106. // demote pitches in on a voting campaign to demote an authorised user from
  107. // its signer position.
  108. function demote(address user) {
  109. updateSigner(user, false);
  110. }
  111. // release votes for a particular version to be included as the next release.
  112. function release(uint32 major, uint32 minor, uint32 patch, bytes20 commit) {
  113. updateRelease(major, minor, patch, commit, true);
  114. }
  115. // nuke votes for the currently proposed version to not be included as the next
  116. // release. Nuking doesn't require a specific version number for simplicity.
  117. function nuke() {
  118. updateRelease(0, 0, 0, 0, false);
  119. }
  120. // updateSigner marks a vote for changing the status of an Ethereum user, either
  121. // for or against the user being an authorised signer.
  122. function updateSigner(address user, bool authorize) internal isSigner {
  123. // Gather the current votes and ensure we don't double vote
  124. Votes votes = authProps[user];
  125. for (uint i = 0; i < votes.pass.length; i++) {
  126. if (votes.pass[i] == msg.sender) {
  127. return;
  128. }
  129. }
  130. for (i = 0; i < votes.fail.length; i++) {
  131. if (votes.fail[i] == msg.sender) {
  132. return;
  133. }
  134. }
  135. // If no authorization proposal is open, add the user to the index for later lookups
  136. if (votes.pass.length == 0 && votes.fail.length == 0) {
  137. authPend.push(user);
  138. }
  139. // Cast the vote and return if the proposal cannot be resolved yet
  140. if (authorize) {
  141. votes.pass.push(msg.sender);
  142. if (votes.pass.length <= voters.length / 2) {
  143. return;
  144. }
  145. } else {
  146. votes.fail.push(msg.sender);
  147. if (votes.fail.length <= voters.length / 2) {
  148. return;
  149. }
  150. }
  151. // Proposal resolved in our favor, execute whatever we voted on
  152. if (authorize && !authorised[user]) {
  153. authorised[user] = true;
  154. voters.push(user);
  155. } else if (!authorize && authorised[user]) {
  156. authorised[user] = false;
  157. for (i = 0; i < voters.length; i++) {
  158. if (voters[i] == user) {
  159. voters[i] = voters[voters.length - 1];
  160. voters.length--;
  161. delete verProp; // Nuke any version proposal (no surprise releases!)
  162. break;
  163. }
  164. }
  165. }
  166. // Finally delete the resolved proposal, index and garbage collect
  167. delete authProps[user];
  168. for (i = 0; i < authPend.length; i++) {
  169. if (authPend[i] == user) {
  170. authPend[i] = authPend[authPend.length - 1];
  171. authPend.length--;
  172. break;
  173. }
  174. }
  175. }
  176. // updateRelease votes for a particular version to be included as the next release,
  177. // or for the currently proposed release to be nuked out.
  178. function updateRelease(uint32 major, uint32 minor, uint32 patch, bytes20 commit, bool release) internal isSigner {
  179. // Skip nuke votes if no proposal is pending
  180. if (!release && verProp.votes.pass.length == 0) {
  181. return;
  182. }
  183. // Mark a new release if no proposal is pending
  184. if (verProp.votes.pass.length == 0) {
  185. verProp.major = major;
  186. verProp.minor = minor;
  187. verProp.patch = patch;
  188. verProp.commit = commit;
  189. }
  190. // Make sure positive votes match the current proposal
  191. if (release && (verProp.major != major || verProp.minor != minor || verProp.patch != patch || verProp.commit != commit)) {
  192. return;
  193. }
  194. // Gather the current votes and ensure we don't double vote
  195. Votes votes = verProp.votes;
  196. for (uint i = 0; i < votes.pass.length; i++) {
  197. if (votes.pass[i] == msg.sender) {
  198. return;
  199. }
  200. }
  201. for (i = 0; i < votes.fail.length; i++) {
  202. if (votes.fail[i] == msg.sender) {
  203. return;
  204. }
  205. }
  206. // Cast the vote and return if the proposal cannot be resolved yet
  207. if (release) {
  208. votes.pass.push(msg.sender);
  209. if (votes.pass.length <= voters.length / 2) {
  210. return;
  211. }
  212. } else {
  213. votes.fail.push(msg.sender);
  214. if (votes.fail.length <= voters.length / 2) {
  215. return;
  216. }
  217. }
  218. // Proposal resolved in our favor, execute whatever we voted on
  219. if (release) {
  220. verProp.time = uint64(now);
  221. releases.push(verProp);
  222. delete verProp;
  223. } else {
  224. delete verProp;
  225. }
  226. }
  227. }