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  }