forked from cerc-io/plugeth
1d7d1a3499
This change also deletes generator.go, moving the only interesting line in it into release.go. The binding has been regenerated with abigen from develop and solc v0.3.6.
250 lines
9.0 KiB
Solidity
250 lines
9.0 KiB
Solidity
// 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;
|
|
}
|
|
}
|
|
}
|