-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
370 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,370 @@ | ||
pragma solidity ^0.4.24; | ||
|
||
// DELEGATION SC v1.1 | ||
// (c) SecureVote 2018 | ||
// Author: Max Kaye <max@secure.vote> | ||
// Released under MIT licence | ||
|
||
// the most up-to-date version of the contract lives at delegate.secvote.eth | ||
|
||
|
||
import "./SVCommon.sol"; // particularly for ERC20Interface | ||
|
||
|
||
// Main delegation contract v1.1 | ||
contract SVDelegationV0101_1 { | ||
|
||
address public owner; | ||
|
||
// in the last version we didn't include enough data - this makes it trivial to traverse off-chain | ||
struct Delegation { | ||
uint64 thisDelegationId; | ||
uint64 prevDelegationId; | ||
uint64 setAtBlock; | ||
address delegatee; | ||
address delegator; | ||
address tokenContract; | ||
} | ||
|
||
// easy lookups | ||
mapping (address => mapping (address => Delegation)) tokenDlgts; | ||
mapping (address => Delegation) globalDlgts; | ||
|
||
// track which token contracts we know about for easy traversal + backwards compatibility | ||
mapping (address => bool) knownTokenContracts; | ||
address[] logTokenContracts; | ||
|
||
// track all delegations via an indexed map | ||
mapping (uint64 => Delegation) historicalDelegations; | ||
uint64 public totalDelegations = 0; | ||
|
||
// reference to v1.0 of contract | ||
SVDelegation prevSVDelegation; | ||
|
||
// pretty straight forward - events | ||
event SetGlobalDelegation(address voter, address delegate); | ||
event SetTokenDelegation(address voter, address tokenContract, address delegate); | ||
|
||
// main constructor - requires the prevDelegationSC address | ||
function SVDelegationV0101_1(address prevDelegationSC) public { | ||
owner = msg.sender; | ||
|
||
prevSVDelegation = SVDelegation(prevDelegationSC); | ||
|
||
// commit the genesis historical delegation to history (like genesis block) - somewhere to point back to | ||
createDelegation(address(0), 0, address(0)); | ||
} | ||
|
||
// internal function to handle inserting delegates into state | ||
function createDelegation(address dlgtAddress, uint64 prevDelegationId, address tokenContract) internal returns(Delegation) { | ||
// use this to log known tokenContracts | ||
if (!knownTokenContracts[tokenContract]) { | ||
logTokenContracts.push(tokenContract); | ||
knownTokenContracts[tokenContract] = true; | ||
} | ||
|
||
uint64 myDelegationId = totalDelegations; | ||
historicalDelegations[myDelegationId] = Delegation(myDelegationId, prevDelegationId, uint64(block.number), dlgtAddress, msg.sender, tokenContract); | ||
totalDelegations += 1; | ||
|
||
return historicalDelegations[myDelegationId]; | ||
} | ||
|
||
// get previous delegation, create new delegation via function and then commit to globalDlgts | ||
function setGlobalDelegation(address dlgtAddress) public { | ||
uint64 prevDelegationId = globalDlgts[msg.sender].thisDelegationId; | ||
globalDlgts[msg.sender] = createDelegation(dlgtAddress, prevDelegationId, address(0)); | ||
emit SetGlobalDelegation(msg.sender, dlgtAddress); | ||
} | ||
|
||
// get previous delegation, create new delegation via function and then commit to tokenDlgts | ||
function setTokenDelegation(address tokenContract, address dlgtAddress) public { | ||
uint64 prevDelegationId = tokenDlgts[tokenContract][msg.sender].thisDelegationId; | ||
tokenDlgts[tokenContract][msg.sender] = createDelegation(dlgtAddress, prevDelegationId, tokenContract); | ||
emit SetTokenDelegation(msg.sender, tokenContract, dlgtAddress); | ||
} | ||
|
||
// given some voter and token address, get the delegation id - failover to global on 0 address | ||
function getDelegationID(address voter, address tokenContract) public constant returns(uint64) { | ||
// default to token resolution but use global if no delegation | ||
Delegation memory _tokenDlgt = tokenDlgts[tokenContract][voter]; | ||
if (tokenContract == address(0)) { | ||
_tokenDlgt = globalDlgts[voter]; | ||
} | ||
|
||
// default to 0 if we don't have a valid delegation | ||
if (_validDelegation(_tokenDlgt)) { | ||
return _tokenDlgt.thisDelegationId; | ||
} | ||
return 0; | ||
} | ||
|
||
function resolveDelegation(address voter, address tokenContract) public constant returns(uint64, uint64, uint64, address, address, address) { | ||
Delegation memory _tokenDlgt = tokenDlgts[tokenContract][voter]; | ||
|
||
// if we have a delegation in this contract return it | ||
if (_validDelegation(_tokenDlgt)) { | ||
return _dlgtRet(_tokenDlgt); | ||
} | ||
|
||
// otherwise try the global delegation | ||
Delegation memory _globalDlgt = globalDlgts[voter]; | ||
if (_validDelegation(_globalDlgt)) { | ||
return _dlgtRet(_globalDlgt); | ||
} | ||
|
||
// CHANGE: Potential bug here: if (v1, t1) -> d1 on the old contract, and | ||
// there's a global v1 -> d2 on this smartcontract, then this function | ||
// resovleDelegation(v1, t1) -> d2 instead of d1. Oops. As it happens this | ||
// doesn't affect any delegations AFAIK, but is something to be aware of. | ||
// ------ | ||
// but if we don't have a delegation in this contract then resolve according the prev contract | ||
address _dlgt; | ||
uint256 meh; | ||
(meh, _dlgt, meh, meh) = prevSVDelegation.resolveDelegation(voter, tokenContract); | ||
return (0, 0, 0, _dlgt, voter, tokenContract); | ||
} | ||
|
||
// returns 2 lists: first of voter addresses, second of token contracts | ||
function findPossibleDelegatorsOf(address delegate) public view returns(address[] memory, address[] memory) { | ||
// not meant to be run on-chain, but off-chain via API, mostly convenience | ||
address[] memory voters; | ||
address[] memory tokenContracts; | ||
Delegation memory _delegation; | ||
|
||
|
||
// first loop through delegations in this contract | ||
uint64 i; | ||
// start at 1 because the first delegation is a "genesis" delegation in constructor | ||
for (i = 1; i < totalDelegations; i++) { | ||
_delegation = historicalDelegations[i]; | ||
if (_delegation.delegatee == delegate) { | ||
// since `.push` isn't available on memory arrays, use their length as the next index location | ||
voters = _appendMemArray(voters, _delegation.delegator); | ||
tokenContracts = _appendMemArray(tokenContracts, _delegation.tokenContract); | ||
} | ||
} | ||
|
||
return resolveOlderDelegations(delegate, voters, tokenContracts); | ||
} | ||
|
||
// CHANGE: BROKE THIS OUT INTO IT'S OWN FUNCTION | ||
// this allows it to scan all known voters and tokens in the | ||
// past contract | ||
// --- | ||
// just get older delegations | ||
function resolveOlderDelegations(address delegate, address[] memory _voters, address[] memory _tcs) internal view returns (address[] memory voters, address[] memory tokenContracts) { | ||
// all the senders who participated in v1.0 of the contract prior to block 5203500 | ||
address[43] memory oldSenders = | ||
[ 0xE8193Bc3D5F3F482406706F843A5f161563F37Bf | ||
, 0x7A933c8a0Eb99e8Bdb07E1b42Aa10872845394B7 | ||
, 0x88341191EfA40Cd031F46138817830A5D3545Ba9 | ||
, 0xB6dc48E8583C8C6e320DaF918CAdef65f2d85B46 | ||
, 0xF02d417c8c6736Dbc7Eb089DC6738b950c2F444e | ||
, 0xF66fE29Ad1E87104A8816AD1A8427976d83CB033 | ||
, 0xfd5955bf412B7537873CBB77eB1E39871e20e142 | ||
, 0xe83Efc57d9C487ACc55a7B62896dA43928E64C3E | ||
, 0xd0c41588b27E64576ddA4e6a08452c59F5A2B2dD | ||
, 0x640370126072f6B890d4Ca2E893103e9363DbE8B | ||
, 0x887dbaCD9a0e58B46065F93cc1f82a52DEfDb979 | ||
, 0xe223771699665bCB0AAf7930277C35d3deC573aF | ||
, 0x364B503B0e86b20B7aC1484c247DE50f10DfD8cf | ||
, 0x4512F5867d91D6B0131427b89Bdb7b460fF30397 | ||
, 0xF5fBff477F5Bf5a950F661B70F6b5364875A1bD7 | ||
, 0x9EbB758483Da174DC3d411386B75afd093CEfCf1 | ||
, 0x499B36A6B92F91524A6B5b8Ff321740e84a2B57e | ||
, 0x05D6e87fd6326F977a2d8c67b9F3EcC030527261 | ||
, 0x7f679053a1679dE7913885F0Db1278e91e8927Ca | ||
, 0xF9CD08d36e972Bb070bBD2C1598D21045259AB0D | ||
, 0xa5617800B8FD754fB81F47A65dc49A60acCc3432 | ||
, 0xa9F6238B83fcb65EcA3c3189a0dce8689e275D57 | ||
, 0xa30F92F9cc478562e0dde73665f1B7ADddDC2dCd | ||
, 0x70278C15A29f0Ef62A845e1ac31AE41988F24C10 | ||
, 0xd42622471946CCFf9F7b9246e8D786c74410bFcC | ||
, 0xd65955EF0f8890D7996f5a7b7b5b05B80605C06a | ||
, 0xB46F4eBDD6404686D785EDACE37D66f815ED7cF8 | ||
, 0xf4d3aa8091D23f97706177CDD94b8dF4c7e4C2FB | ||
, 0x4Fe584FFc9C755BF6Aa9354323e97166958475c9 | ||
, 0xB4802f497Bf6238A29e043103EE6eeae1331BFde | ||
, 0x3EeE0f8Fadc1C29bFB782E70067a8D91B4ddeD56 | ||
, 0x46381F606014C5D68B38aD5C7e8f9401149FAa75 | ||
, 0xC81Be3496d053364255f9cb052F81Ca9e84A9cF3 | ||
, 0xa632837B095d8fa2ef46a22099F91Fe10B3F0538 | ||
, 0x19FA94aEbD4bC694802B566Ae65aEd8F07B992f7 | ||
, 0xE9Ef7664d36191Ad7aB001b9BB0aAfAcD260277F | ||
, 0x17DAB6BB606f32447aff568c1D0eEDC3649C101C | ||
, 0xaBA96c77E3dd7EEa16cc5EbdAAA05483CDD0FF89 | ||
, 0x57d36B0B5f5E333818b1ce072A6D84218E734deC | ||
, 0x59E7612706DFB1105220CcB97aaF3cBF304cD608 | ||
, 0xCf7EC4dcA84b5c8Dc7896c38b4834DC6379BB73D | ||
, 0x5Ed1Da246EA52F302FFf9391e56ec64b9c14cce1 | ||
, 0x4CabFD1796Ec9EAd77457768e5cA782a1A9e576F | ||
]; | ||
|
||
uint i = 0; | ||
|
||
// there were no global delegations in v1.0 of contract | ||
address oldToken = 0x9e88613418cF03dCa54D6a2cf6Ad934A78C7A17A; | ||
|
||
address[] memory allPossibleSenders = new address[](43 + _voters.length); | ||
for (i = 0; i < allPossibleSenders.length; i++) { | ||
if (i < 43) { | ||
allPossibleSenders[i] = oldSenders[i]; | ||
} else { | ||
allPossibleSenders[i] = _voters[i - 43]; | ||
} | ||
} | ||
|
||
// loop through delegations in the previous contract | ||
for (i = 0; i < (allPossibleSenders.length); i++) { | ||
for (uint j = 0; j < logTokenContracts.length + 1; j++) { | ||
uint256 _oldId; | ||
address _oldDlgt; | ||
uint256 _oldSetAtBlock; | ||
uint256 _oldPrevId; | ||
address tc = j == 0 ? oldToken : logTokenContracts[j-1]; | ||
if (tc == address(0)) { | ||
(_oldId, _oldDlgt, _oldSetAtBlock, _oldPrevId) = prevSVDelegation._rawGetGlobalDelegation(allPossibleSenders[i]); | ||
} else { | ||
(_oldId, _oldDlgt, _oldSetAtBlock, _oldPrevId) = prevSVDelegation._rawGetTokenDelegation(allPossibleSenders[i], tc); | ||
} | ||
|
||
if (_oldDlgt == delegate) { | ||
voters = _appendMemArray(voters, allPossibleSenders[i]); | ||
tokenContracts = _appendMemArray(tokenContracts, tc); | ||
} | ||
} | ||
} | ||
|
||
return (_concatMemArray(voters, _voters), _concatMemArray(tokenContracts, _tcs)); | ||
} | ||
|
||
// give access to historicalDelegations | ||
function getHistoricalDelegation(uint64 delegationId) public constant returns(uint64, uint64, uint64, address, address, address) { | ||
return _dlgtRet(historicalDelegations[delegationId]); | ||
} | ||
|
||
// access the globalDelegation map | ||
function _rawGetGlobalDelegation(address _voter) public constant returns(uint64, uint64, uint64, address, address, address) { | ||
return _dlgtRet(globalDlgts[_voter]); | ||
} | ||
|
||
// access the tokenDelegation map | ||
function _rawGetTokenDelegation(address _voter, address _tokenContract) public constant returns(uint64, uint64, uint64, address, address, address) { | ||
return _dlgtRet(tokenDlgts[_tokenContract][_voter]); | ||
} | ||
|
||
// access our log list of token contracts | ||
function _getLogTokenContract(uint256 i) public constant returns(address) { | ||
return logTokenContracts[i]; | ||
} | ||
|
||
// convenience function to turn Delegations into a returnable structure | ||
function _dlgtRet(Delegation d) internal pure returns(uint64, uint64, uint64, address, address, address) { | ||
return (d.thisDelegationId, d.prevDelegationId, d.setAtBlock, d.delegatee, d.delegator, d.tokenContract); | ||
} | ||
|
||
// internal function to test if a delegation is valid or revoked / nonexistent | ||
function _validDelegation(Delegation d) internal pure returns(bool) { | ||
// probs simplest test to check if we have a valid delegation - important to check if delegation is set to 0x00 | ||
// to avoid counting a revocation (which is done by delegating to 0x00) | ||
return d.setAtBlock > 0 && d.delegatee != address(0); | ||
} | ||
|
||
function _appendMemArray(address[] memory arr, address toAppend) internal pure returns(address[] memory arr2) { | ||
arr2 = new address[](arr.length + 1); | ||
|
||
for (uint k = 0; k < arr.length; k++) { | ||
arr2[k] = arr[k]; | ||
} | ||
|
||
arr2[arr.length] = toAppend; | ||
} | ||
|
||
function _concatMemArray(address[] memory a1, address[] memory a2) internal pure returns (address[] memory a3) { | ||
a3 = new address[](a1.length + a2.length); | ||
for (uint i = 0; i < a3.length; i++) { | ||
if (i < a1.length) { | ||
a3[i] = a1[i]; | ||
} else { | ||
a3[i] = a2[i - a1.length]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
// Include previous contract in this one so we can access the various components. Not all things needed are accessible | ||
// through functions - e.g. `historicalDelegations` mapping. | ||
contract SVDelegation { | ||
|
||
address public owner; | ||
|
||
struct Delegation { | ||
uint256 thisDelegationId; | ||
address dlgt; | ||
uint256 setAtBlock; | ||
uint256 prevDelegation; | ||
} | ||
|
||
mapping (address => mapping (address => Delegation)) tokenDlgts; | ||
mapping (address => Delegation) globalDlgts; | ||
|
||
mapping (uint256 => Delegation) public historicalDelegations; | ||
uint256 public totalDelegations = 0; | ||
|
||
event SetGlobalDelegation(address voter, address delegate); | ||
event SetTokenDelegation(address voter, address tokenContract, address delegate); | ||
|
||
function SVDelegation() public { | ||
owner = msg.sender; | ||
|
||
// commit the genesis historical delegation to history (like genesis block) | ||
createDelegation(address(0), 0); | ||
} | ||
|
||
function createDelegation(address dlgtAddress, uint256 prevDelegationId) internal returns(Delegation) { | ||
uint256 myDelegationId = totalDelegations; | ||
historicalDelegations[myDelegationId] = Delegation(myDelegationId, dlgtAddress, block.number, prevDelegationId); | ||
totalDelegations += 1; | ||
|
||
return historicalDelegations[myDelegationId]; | ||
} | ||
|
||
// get previous delegation, create new delegation via function and then commit to globalDlgts | ||
function setGlobalDelegation(address dlgtAddress) public { | ||
uint256 prevDelegationId = globalDlgts[msg.sender].thisDelegationId; | ||
globalDlgts[msg.sender] = createDelegation(dlgtAddress, prevDelegationId); | ||
emit SetGlobalDelegation(msg.sender, dlgtAddress); | ||
} | ||
|
||
// get previous delegation, create new delegation via function and then commit to tokenDlgts | ||
function setTokenDelegation(address tokenContract, address dlgtAddress) public { | ||
uint256 prevDelegationId = tokenDlgts[tokenContract][msg.sender].thisDelegationId; | ||
tokenDlgts[tokenContract][msg.sender] = createDelegation(dlgtAddress, prevDelegationId); | ||
emit SetTokenDelegation(msg.sender, tokenContract, dlgtAddress); | ||
} | ||
|
||
function resolveDelegation(address voter, address tokenContract) public constant returns(uint256, address, uint256, uint256) { | ||
Delegation memory _tokenDlgt = tokenDlgts[tokenContract][voter]; | ||
|
||
// probs simplest test to check if we have a valid delegation | ||
if (_tokenDlgt.setAtBlock > 0) { | ||
return _dlgtRet(_tokenDlgt); | ||
} else { | ||
return _dlgtRet(globalDlgts[voter]); | ||
} | ||
} | ||
|
||
function _rawGetGlobalDelegation(address _voter) public constant returns(uint256, address, uint256, uint256) { | ||
return _dlgtRet(globalDlgts[_voter]); | ||
} | ||
|
||
function _rawGetTokenDelegation(address _voter, address _tokenContract) public constant returns(uint256, address, uint256, uint256) { | ||
return _dlgtRet(tokenDlgts[_tokenContract][_voter]); | ||
} | ||
|
||
function _dlgtRet(Delegation d) internal pure returns(uint256, address, uint256, uint256) { | ||
return (d.thisDelegationId, d.dlgt, d.setAtBlock, d.prevDelegation); | ||
} | ||
} |