diff options
author | Péter Szilágyi <peterke@gmail.com> | 2016-04-28 17:00:11 +0800 |
---|---|---|
committer | Péter Szilágyi <peterke@gmail.com> | 2016-05-02 21:20:21 +0800 |
commit | 586eddfd09558bfd71f23c2e50c270d2ca665d49 (patch) | |
tree | 4015e542e0397c606d1f14ef23d15463881be554 /release/contract.sol | |
parent | d46da273c6731512b4114393856a96be06505797 (diff) | |
download | dexon-586eddfd09558bfd71f23c2e50c270d2ca665d49.tar dexon-586eddfd09558bfd71f23c2e50c270d2ca665d49.tar.gz dexon-586eddfd09558bfd71f23c2e50c270d2ca665d49.tar.bz2 dexon-586eddfd09558bfd71f23c2e50c270d2ca665d49.tar.lz dexon-586eddfd09558bfd71f23c2e50c270d2ca665d49.tar.xz dexon-586eddfd09558bfd71f23c2e50c270d2ca665d49.tar.zst dexon-586eddfd09558bfd71f23c2e50c270d2ca665d49.zip |
release, all: integrate the release service into geth
Diffstat (limited to 'release/contract.sol')
-rw-r--r-- | release/contract.sol | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/release/contract.sol b/release/contract.sol new file mode 100644 index 000000000..fedf646c0 --- /dev/null +++ b/release/contract.sol @@ -0,0 +1,249 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. + +// ReleaseOracle is an Ethereum contract to store the current and previous +// versions of the go-ethereum implementation. Its goal is to allow Geth to +// check for new releases automatically without the need to consult a central +// repository. +// +// The contract takes a vote based approach on both assigning authorised signers +// as well as signing off on new Geth releases. +// +// Note, when a signer is demoted, the currently pending release is auto-nuked. +// The reason is to prevent suprises where a demotion actually tilts the votes +// in favor of one voter party and pushing out a new release as a consequence of +// a simple demotion. +contract ReleaseOracle { + // Votes is an internal data structure to count votes on a specific proposal + struct Votes { + address[] pass; // List of signers voting to pass a proposal + address[] fail; // List of signers voting to fail a proposal + } + + // Version is the version details of a particular Geth release + struct Version { + uint32 major; // Major version component of the release + uint32 minor; // Minor version component of the release + uint32 patch; // Patch version component of the release + bytes20 commit; // Git SHA1 commit hash of the release + + uint64 time; // Timestamp of the release approval + Votes votes; // Votes that passed this release + } + + // Oracle authorization details + mapping(address => bool) authorised; // Set of accounts allowed to vote on updating the contract + address[] voters; // List of addresses currently accepted as signers + + // Various proposals being voted on + mapping(address => Votes) authProps; // Currently running user authorization proposals + address[] authPend; // List of addresses being voted on (map indexes) + + Version verProp; // Currently proposed release being voted on + Version[] releases; // All the positively voted releases + + // isSigner is a modifier to authorize contract transactions. + modifier isSigner() { + if (authorised[msg.sender]) { + _ + } + } + + // Constructor to assign the initial set of signers. + function ReleaseOracle(address[] signers) { + // If no signers were specified, assign the creator as the sole signer + if (signers.length == 0) { + authorised[msg.sender] = true; + voters.push(msg.sender); + return; + } + // Otherwise assign the individual signers one by one + for (uint i = 0; i < signers.length; i++) { + authorised[signers[i]] = true; + voters.push(signers[i]); + } + } + + // signers is an accessor method to retrieve all te signers (public accessor + // generates an indexed one, not a retreive-all version). + function signers() constant returns(address[]) { + return voters; + } + + // authProposals retrieves the list of addresses that authorization proposals + // are currently being voted on. + function authProposals() constant returns(address[]) { + return authPend; + } + + // authVotes retrieves the current authorization votes for a particular user + // to promote him into the list of signers, or demote him from there. + function authVotes(address user) constant returns(address[] promote, address[] demote) { + return (authProps[user].pass, authProps[user].fail); + } + + // currentVersion retrieves the semantic version, commit hash and release time + // of the currently votec active release. + function currentVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, uint time) { + if (releases.length == 0) { + return (0, 0, 0, 0, 0); + } + var release = releases[releases.length - 1]; + + return (release.major, release.minor, release.patch, release.commit, release.time); + } + + // proposedVersion retrieves the semantic version, commit hash and the current + // votes for the next proposed release. + function proposedVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, address[] pass, address[] fail) { + return (verProp.major, verProp.minor, verProp.patch, verProp.commit, verProp.votes.pass, verProp.votes.fail); + } + + // promote pitches in on a voting campaign to promote a new user to a signer + // position. + function promote(address user) { + updateSigner(user, true); + } + + // demote pitches in on a voting campaign to demote an authorised user from + // its signer position. + function demote(address user) { + updateSigner(user, false); + } + + // release votes for a particular version to be included as the next release. + function release(uint32 major, uint32 minor, uint32 patch, bytes20 commit) { + updateRelease(major, minor, patch, commit, true); + } + + // nuke votes for the currently proposed version to not be included as the next + // release. Nuking doesn't require a specific version number for simplicity. + function nuke() { + updateRelease(0, 0, 0, 0, false); + } + + // updateSigner marks a vote for changing the status of an Ethereum user, either + // for or against the user being an authorised signer. + function updateSigner(address user, bool authorize) internal isSigner { + // Gather the current votes and ensure we don't double vote + Votes votes = authProps[user]; + for (uint i = 0; i < votes.pass.length; i++) { + if (votes.pass[i] == msg.sender) { + return; + } + } + for (i = 0; i < votes.fail.length; i++) { + if (votes.fail[i] == msg.sender) { + return; + } + } + // If no authorization proposal is open, add the user to the index for later lookups + if (votes.pass.length == 0 && votes.fail.length == 0) { + authPend.push(user); + } + // Cast the vote and return if the proposal cannot be resolved yet + if (authorize) { + votes.pass.push(msg.sender); + if (votes.pass.length <= voters.length / 2) { + return; + } + } else { + votes.fail.push(msg.sender); + if (votes.fail.length <= voters.length / 2) { + return; + } + } + // Proposal resolved in our favor, execute whatever we voted on + if (authorize && !authorised[user]) { + authorised[user] = true; + voters.push(user); + } else if (!authorize && authorised[user]) { + authorised[user] = false; + + for (i = 0; i < voters.length; i++) { + if (voters[i] == user) { + voters[i] = voters[voters.length - 1]; + voters.length--; + + delete verProp; // Nuke any version proposal (no suprise releases!) + break; + } + } + } + // Finally delete the resolved proposal, index and garbage collect + delete authProps[user]; + + for (i = 0; i < authPend.length; i++) { + if (authPend[i] == user) { + authPend[i] = authPend[authPend.length - 1]; + authPend.length--; + break; + } + } + } + + // updateRelease votes for a particular version to be included as the next release, + // or for the currently proposed release to be nuked out. + function updateRelease(uint32 major, uint32 minor, uint32 patch, bytes20 commit, bool release) internal isSigner { + // Skip nuke votes if no proposal is pending + if (!release && verProp.votes.pass.length == 0) { + return; + } + // Mark a new release if no proposal is pending + if (verProp.votes.pass.length == 0) { + verProp.major = major; + verProp.minor = minor; + verProp.patch = patch; + verProp.commit = commit; + } + // Make sure positive votes match the current proposal + if (release && (verProp.major != major || verProp.minor != minor || verProp.patch != patch || verProp.commit != commit)) { + return; + } + // Gather the current votes and ensure we don't double vote + Votes votes = verProp.votes; + for (uint i = 0; i < votes.pass.length; i++) { + if (votes.pass[i] == msg.sender) { + return; + } + } + for (i = 0; i < votes.fail.length; i++) { + if (votes.fail[i] == msg.sender) { + return; + } + } + // Cast the vote and return if the proposal cannot be resolved yet + if (release) { + votes.pass.push(msg.sender); + if (votes.pass.length <= voters.length / 2) { + return; + } + } else { + votes.fail.push(msg.sender); + if (votes.fail.length <= voters.length / 2) { + return; + } + } + // Proposal resolved in our favor, execute whatever we voted on + if (release) { + verProp.time = uint64(now); + releases.push(verProp); + delete verProp; + } else { + delete verProp; + } + } +} |