2017-06-06 17:40:14 +00:00
pragma solidity ^ 0 . 4 . 11 ;
2017-09-13 12:41:08 +00:00
import " ./ILiquidPledgingPlugin.sol " ;
2017-09-29 10:11:13 +00:00
/// @dev This is declares a few functions from `Vault` so that the
/// `LiquidPledgingBase` contract can interface with the `Vault` contract
2017-07-13 17:12:45 +00:00
contract Vault {
function authorizePayment ( bytes32 _ref , address _dest , uint _amount ) ;
2017-08-13 11:23:00 +00:00
function ( ) payable ;
2017-07-13 17:12:45 +00:00
}
2017-06-06 17:40:14 +00:00
2017-06-26 17:54:28 +00:00
contract LiquidPledgingBase {
2017-10-03 10:20:23 +00:00
// Limits inserted to prevent large loops that could prevent canceling
2017-07-13 17:21:53 +00:00
uint constant MAX_DELEGATES = 20 ;
2017-10-03 11:37:45 +00:00
uint constant MAX_SUBCAMPAIGN_LEVEL = 20 ;
uint constant MAX_INTERCAMPAIGN_LEVEL = 20 ;
2017-06-06 17:40:14 +00:00
2017-10-03 12:42:21 +00:00
enum PledgeManagerType { Giver , Delegate , Campaign }
2017-09-29 10:11:13 +00:00
enum PaymentState { NotPaid , Paying , Paid } // TODO name change NotPaid
2017-10-03 12:42:21 +00:00
/// @dev This struct defines the details of each the PledgeManager, these
/// PledgeManagers can own pledges and act as delegates
struct PledgeManager { // TODO name change PledgeManager
PledgeManagerType managerType ; // Giver, Delegate or Campaign
2017-09-29 10:11:13 +00:00
address addr ; // account or contract address for admin
2017-10-03 10:20:23 +00:00
string name ;
2017-09-29 10:11:13 +00:00
uint64 commitTime ; // In seconds, used for Givers' & Delegates' vetos
2017-10-03 11:37:45 +00:00
uint64 parentCampaign ; // Only for campaigns
2017-09-29 10:11:13 +00:00
bool canceled ; //Always false except for canceled campaigns
ILiquidPledgingPlugin plugin ; // if the plugin is 0x0 then nothing happens if its a contract address than that smart contract is called via the milestone contract
2017-06-06 17:40:14 +00:00
}
2017-10-03 12:42:21 +00:00
struct Pledge {
2017-06-06 17:40:14 +00:00
uint amount ;
2017-10-03 12:42:21 +00:00
uint64 owner ; // PledgeManager
2017-09-29 10:11:13 +00:00
uint64 [ ] delegationChain ; // list of index numbers
2017-10-03 11:37:45 +00:00
uint64 proposedCampaign ; // TODO change the name only used for when delegates are precommiting to a campaign
uint64 commitTime ; // When the proposedCampaign will become the owner
2017-10-03 12:42:21 +00:00
uint64 oldPledge ; // this points to the Pledge[] index that the Pledge was derived from
2017-06-06 17:40:14 +00:00
PaymentState paymentState ;
}
2017-10-03 12:42:21 +00:00
Pledge [ ] pledges ;
PledgeManager [ ] managers ; //The list of pledgeManagers 0 means there is no manager
2017-06-06 17:40:14 +00:00
Vault public vault ;
2017-10-03 12:42:21 +00:00
// this mapping allows you to search for a specific pledge's index number by the hash of that pledge
mapping ( bytes32 => uint64 ) hPledge2idx ; //TODO Fix typo
2017-06-06 17:40:14 +00:00
/////
// Modifiers
/////
modifier onlyVault ( ) {
2017-07-13 17:12:45 +00:00
require ( msg . sender == address ( vault ) ) ;
2017-06-06 17:40:14 +00:00
_ ;
}
//////
// Constructor
//////
2017-09-29 10:11:13 +00:00
/// @notice The Constructor creates the `LiquidPledgingBase` on the blockchain
/// @param _vault Where the ETH is stored that the pledges represent
2017-06-06 17:40:14 +00:00
function LiquidPledgingBase ( address _vault ) {
2017-07-09 17:04:02 +00:00
managers . length = 1 ; // we reserve the 0 manager
2017-10-03 12:42:21 +00:00
pledges . length = 1 ; // we reserve the 0 pledge
2017-06-06 17:40:14 +00:00
vault = Vault ( _vault ) ;
}
///////
// Managers functions
//////
2017-10-03 10:20:23 +00:00
/// @notice Creates a giver.
function addGiver ( string name , uint64 commitTime , ILiquidPledgingPlugin plugin
) returns ( uint64 idGiver ) {
2017-09-14 18:06:58 +00:00
2017-10-03 10:20:23 +00:00
idGiver = uint64 ( managers . length ) ;
2017-09-14 18:06:58 +00:00
2017-10-03 12:42:21 +00:00
managers . push ( PledgeManager (
PledgeManagerType . Giver ,
2017-06-06 17:40:14 +00:00
msg . sender ,
name ,
commitTime ,
2017-08-18 16:47:22 +00:00
0 ,
2017-09-13 12:41:08 +00:00
false ,
plugin ) ) ;
2017-09-28 15:49:10 +00:00
2017-10-03 10:20:23 +00:00
GiverAdded ( idGiver ) ;
2017-06-06 17:40:14 +00:00
}
2017-10-03 10:20:23 +00:00
event GiverAdded ( uint64 indexed idGiver ) ;
2017-06-06 17:40:14 +00:00
2017-10-03 10:20:23 +00:00
///@notice Changes the address, name or commitTime associated with a specific giver
function updateGiver (
uint64 idGiver ,
2017-06-06 17:40:14 +00:00
address newAddr ,
string newName ,
uint64 newCommitTime )
{
2017-10-03 12:42:21 +00:00
PledgeManager storage giver = findManager ( idGiver ) ;
require ( giver . managerType == PledgeManagerType . Giver ) ; //Must be a Giver
require ( giver . addr == msg . sender ) ; //current addr had to originate this tx
2017-10-03 10:20:23 +00:00
giver . addr = newAddr ;
giver . name = newName ;
giver . commitTime = newCommitTime ;
GiverUpdated ( idGiver ) ;
2017-06-06 17:40:14 +00:00
}
2017-10-03 10:20:23 +00:00
event GiverUpdated ( uint64 indexed idGiver ) ;
2017-06-06 17:40:14 +00:00
2017-09-29 10:11:13 +00:00
/// @notice Creates a new Delegate
2017-09-14 18:06:58 +00:00
function addDelegate ( string name , uint64 commitTime , ILiquidPledgingPlugin plugin ) returns ( uint64 idDelegate ) { //TODO return index number
idDelegate = uint64 ( managers . length ) ;
2017-10-03 12:42:21 +00:00
managers . push ( PledgeManager (
PledgeManagerType . Delegate ,
2017-06-06 17:40:14 +00:00
msg . sender ,
name ,
2017-09-13 12:41:08 +00:00
commitTime ,
2017-06-06 17:40:14 +00:00
0 ,
2017-09-13 12:41:08 +00:00
false ,
plugin ) ) ;
2017-06-06 17:40:14 +00:00
2017-09-28 15:50:57 +00:00
DelegateAdded ( idDelegate ) ;
2017-06-06 17:40:14 +00:00
}
2017-09-28 15:49:10 +00:00
event DelegateAdded ( uint64 indexed idDelegate ) ;
2017-06-06 17:40:14 +00:00
2017-09-29 10:11:13 +00:00
///@notice Changes the address, name or commitTime associated with a specific delegate
2017-09-13 12:41:08 +00:00
function updateDelegate (
uint64 idDelegate ,
address newAddr ,
string newName ,
2017-09-18 13:43:09 +00:00
uint64 newCommitTime ) {
2017-10-03 12:42:21 +00:00
PledgeManager storage delegate = findManager ( idDelegate ) ;
require ( delegate . managerType == PledgeManagerType . Delegate ) ;
2017-07-13 17:12:45 +00:00
require ( delegate . addr == msg . sender ) ;
2017-06-06 17:40:14 +00:00
delegate . addr = newAddr ;
delegate . name = newName ;
2017-09-13 12:41:08 +00:00
delegate . commitTime = newCommitTime ;
2017-06-06 17:40:14 +00:00
DelegateUpdated ( idDelegate ) ;
}
2017-07-09 17:04:02 +00:00
event DelegateUpdated ( uint64 indexed idDelegate ) ;
2017-06-06 17:40:14 +00:00
2017-09-29 10:11:13 +00:00
/// @notice Creates a new Campaign
2017-10-03 11:37:45 +00:00
function addCampaign ( string name , address campaignManager , uint64 parentCampaign , uint64 commitTime , ILiquidPledgingPlugin plugin ) returns ( uint64 idCampaign ) {
if ( parentCampaign != 0 ) {
2017-10-03 12:42:21 +00:00
PledgeManager storage pm = findManager ( parentCampaign ) ;
require ( pm . managerType == PledgeManagerType . Campaign ) ;
2017-08-18 16:47:22 +00:00
require ( pm . addr == msg . sender ) ;
2017-10-03 11:37:45 +00:00
require ( getCampaignLevel ( pm ) < MAX_SUBCAMPAIGN_LEVEL ) ;
2017-08-18 16:47:22 +00:00
}
2017-09-14 18:06:58 +00:00
2017-10-03 11:37:45 +00:00
idCampaign = uint64 ( managers . length ) ;
2017-09-14 18:06:58 +00:00
2017-10-03 12:42:21 +00:00
managers . push ( PledgeManager (
PledgeManagerType . Campaign ,
2017-10-03 11:37:45 +00:00
campaignManager ,
2017-06-06 17:40:14 +00:00
name ,
commitTime ,
2017-10-03 11:37:45 +00:00
parentCampaign ,
2017-09-13 12:41:08 +00:00
false ,
plugin ) ) ;
2017-06-06 17:40:14 +00:00
2017-10-03 11:37:45 +00:00
CampaignAdded ( idCampaign ) ;
2017-06-06 17:40:14 +00:00
}
2017-10-03 11:37:45 +00:00
event CampaignAdded ( uint64 indexed idCampaign ) ;
2017-06-06 17:40:14 +00:00
2017-09-29 10:11:13 +00:00
///@notice Changes the address, name or commitTime associated with a specific Campaign
2017-10-03 11:37:45 +00:00
function updateCampaign (
uint64 idCampaign ,
2017-09-18 13:43:09 +00:00
address newAddr ,
string newName ,
uint64 newCommitTime )
{
2017-10-03 12:42:21 +00:00
PledgeManager storage campaign = findManager ( idCampaign ) ;
require ( campaign . managerType == PledgeManagerType . Campaign ) ;
2017-10-03 11:37:45 +00:00
require ( campaign . addr == msg . sender ) ;
campaign . addr = newAddr ;
campaign . name = newName ;
campaign . commitTime = newCommitTime ;
CampaignUpdated ( idCampaign ) ;
2017-06-06 17:40:14 +00:00
}
2017-10-03 11:37:45 +00:00
event CampaignUpdated ( uint64 indexed idManager ) ;
2017-06-06 17:40:14 +00:00
//////////
// Public constant functions
//////////
2017-10-03 12:42:21 +00:00
/// @notice Public constant that states how many pledgess are in the system
function numberOfPledges ( ) constant returns ( uint ) {
return pledges . length - 1 ;
2017-06-06 17:40:14 +00:00
}
2017-10-03 12:42:21 +00:00
/// @notice Public constant that states the details of the specified Pledge
function getPledge ( uint64 idPledge ) constant returns (
2017-06-06 17:40:14 +00:00
uint amount ,
uint64 owner ,
uint64 nDelegates ,
2017-10-03 11:37:45 +00:00
uint64 proposedCampaign ,
2017-06-27 11:08:23 +00:00
uint64 commitTime ,
2017-10-03 12:42:21 +00:00
uint64 oldPledge ,
2017-06-06 17:40:14 +00:00
PaymentState paymentState
) {
2017-10-03 12:42:21 +00:00
Pledge storage n = findPledge ( idPledge ) ;
2017-06-06 17:40:14 +00:00
amount = n . amount ;
owner = n . owner ;
nDelegates = uint64 ( n . delegationChain . length ) ;
2017-10-03 11:37:45 +00:00
proposedCampaign = n . proposedCampaign ;
2017-06-27 11:08:23 +00:00
commitTime = n . commitTime ;
2017-10-03 12:42:21 +00:00
oldPledge = n . oldPledge ;
2017-06-06 17:40:14 +00:00
paymentState = n . paymentState ;
}
2017-09-29 10:11:13 +00:00
/// @notice Public constant that states the delegates one by one, because
/// an array cannot be returned
2017-10-03 12:42:21 +00:00
function getPledgeDelegate ( uint64 idPledge , uint idxDelegate ) constant returns (
2017-06-06 17:40:14 +00:00
uint64 idDelegate ,
address addr ,
string name
) {
2017-10-03 12:42:21 +00:00
Pledge storage n = findPledge ( idPledge ) ;
2017-06-26 17:54:28 +00:00
idDelegate = n . delegationChain [ idxDelegate - 1 ] ;
2017-10-03 12:42:21 +00:00
PledgeManager storage delegate = findManager ( idDelegate ) ;
2017-06-06 17:40:14 +00:00
addr = delegate . addr ;
name = delegate . name ;
}
2017-09-29 10:11:13 +00:00
/// @notice Public constant that states the number of admins in the system
2017-10-03 12:42:21 +00:00
function numberOfPledgeManagers ( ) constant returns ( uint ) {
2017-06-06 17:40:14 +00:00
return managers . length - 1 ;
}
2017-10-03 10:20:23 +00:00
/// @notice Public constant that states the details of the specified admin
2017-10-03 12:42:21 +00:00
function getPledgeManager ( uint64 idManager ) constant returns (
PledgeManagerType managerType ,
2017-06-06 17:40:14 +00:00
address addr ,
string name ,
uint64 commitTime ,
2017-10-03 11:37:45 +00:00
uint64 parentCampaign ,
2017-09-26 19:06:45 +00:00
bool canceled ,
address plugin )
2017-06-06 17:40:14 +00:00
{
2017-10-03 12:42:21 +00:00
PledgeManager storage m = findManager ( idManager ) ;
2017-06-06 17:40:14 +00:00
managerType = m . managerType ;
addr = m . addr ;
name = m . name ;
commitTime = m . commitTime ;
2017-10-03 11:37:45 +00:00
parentCampaign = m . parentCampaign ;
2017-06-06 17:40:14 +00:00
canceled = m . canceled ;
2017-09-26 19:26:26 +00:00
plugin = address ( m . plugin ) ;
2017-06-06 17:40:14 +00:00
}
////////
// Private methods
///////
2017-10-03 12:42:21 +00:00
/// @notice All pledges technically exist... but if the pledge hasn't been
2017-09-29 10:11:13 +00:00
/// created in this system yet then it wouldn't be in the hash array
2017-10-03 12:42:21 +00:00
/// hPledge2idx[]; this creates a Pledge with and amount of 0 if one is not
2017-10-03 10:20:23 +00:00
/// created already...
2017-10-03 12:42:21 +00:00
function findPledge (
2017-06-06 17:40:14 +00:00
uint64 owner ,
uint64 [ ] delegationChain ,
2017-10-03 11:37:45 +00:00
uint64 proposedCampaign ,
2017-06-27 11:08:23 +00:00
uint64 commitTime ,
2017-10-03 12:42:21 +00:00
uint64 oldPledge ,
2017-06-06 17:40:14 +00:00
PaymentState paid
) internal returns ( uint64 )
{
2017-10-03 12:42:21 +00:00
bytes32 hPledge = sha3 ( owner , delegationChain , proposedCampaign , commitTime , oldPledge , paid ) ;
uint64 idx = hPledge2idx [ hPledge ] ;
2017-06-06 17:40:14 +00:00
if ( idx > 0 ) return idx ;
2017-10-03 12:42:21 +00:00
idx = uint64 ( pledges . length ) ;
hPledge2idx [ hPledge ] = idx ;
pledges . push ( Pledge ( 0 , owner , delegationChain , proposedCampaign , commitTime , oldPledge , paid ) ) ;
2017-06-06 17:40:14 +00:00
return idx ;
}
2017-10-03 12:42:21 +00:00
function findManager ( uint64 idManager ) internal returns ( PledgeManager storage ) {
2017-07-13 17:12:45 +00:00
require ( idManager < managers . length ) ;
2017-06-06 17:40:14 +00:00
return managers [ idManager ] ;
}
2017-10-03 12:42:21 +00:00
function findPledge ( uint64 idPledge ) internal returns ( Pledge storage ) {
require ( idPledge < pledges . length ) ;
return pledges [ idPledge ] ;
2017-06-06 17:40:14 +00:00
}
2017-07-09 17:04:02 +00:00
// a constant for the case that a delegate is requested that is not a delegate in the system
uint64 constant NOTFOUND = 0xFFFFFFFFFFFFFFFF ;
2017-07-13 17:21:53 +00:00
2017-07-09 17:04:02 +00:00
// helper function that searches the delegationChain fro a specific delegate and
2017-09-29 10:11:13 +00:00
// level of delegation returns their idx in the delegation chain which reflect their level of authority
2017-10-03 12:42:21 +00:00
function getDelegateIdx ( Pledge n , uint64 idDelegate ) internal returns ( uint64 ) {
2017-07-09 17:04:02 +00:00
for ( uint i = 0 ; i < n . delegationChain . length ; i ++ ) {
if ( n . delegationChain [ i ] == idDelegate ) return uint64 ( i ) ;
}
return NOTFOUND ;
}
2017-10-03 12:42:21 +00:00
// helper function that returns the pledge level solely to check that transfers
2017-10-03 11:37:45 +00:00
// between Campaigns not violate MAX_INTERCAMPAIGN_LEVEL
2017-10-03 12:42:21 +00:00
function getPledgeLevel ( Pledge n ) internal returns ( uint ) {
if ( n . oldPledge == 0 ) return 0 ; //changed
Pledge storage oldN = findPledge ( n . oldPledge ) ;
return getPledgeLevel ( oldN ) + 1 ;
2017-07-09 17:04:02 +00:00
}
2017-08-18 16:47:22 +00:00
2017-09-13 12:41:08 +00:00
// helper function that returns the max commit time of the owner and all the
// delegates
2017-10-03 12:42:21 +00:00
function maxCommitTime ( Pledge n ) internal returns ( uint commitTime ) {
PledgeManager storage m = findManager ( n . owner ) ;
2017-09-13 12:41:08 +00:00
commitTime = m . commitTime ;
for ( uint i = 0 ; i < n . delegationChain . length ; i ++ ) {
m = findManager ( n . delegationChain [ i ] ) ;
if ( m . commitTime > commitTime ) commitTime = m . commitTime ;
}
}
2017-08-18 16:47:22 +00:00
2017-10-03 11:37:45 +00:00
// helper function that returns the campaign level solely to check that there
// are not too many Campaigns that violate MAX_SUBCAMPAIGNS_LEVEL
2017-10-03 12:42:21 +00:00
function getCampaignLevel ( PledgeManager m ) internal returns ( uint ) {
assert ( m . managerType == PledgeManagerType . Campaign ) ;
2017-10-03 11:37:45 +00:00
if ( m . parentCampaign == 0 ) return ( 1 ) ;
2017-10-03 12:42:21 +00:00
PledgeManager storage parentNM = findManager ( m . parentCampaign ) ;
2017-10-03 11:37:45 +00:00
return getCampaignLevel ( parentNM ) ;
2017-08-18 16:47:22 +00:00
}
2017-10-03 11:37:45 +00:00
function isCampaignCanceled ( uint64 campaignId ) constant returns ( bool ) {
2017-10-03 12:42:21 +00:00
PledgeManager storage m = findManager ( campaignId ) ;
if ( m . managerType == PledgeManagerType . Giver ) return false ;
assert ( m . managerType == PledgeManagerType . Campaign ) ;
2017-08-18 16:47:22 +00:00
if ( m . canceled ) return true ;
2017-10-03 11:37:45 +00:00
if ( m . parentCampaign == 0 ) return false ;
return isCampaignCanceled ( m . parentCampaign ) ;
2017-08-18 16:47:22 +00:00
}
2017-10-03 11:37:45 +00:00
// @notice A helper function for canceling campaigns
2017-10-03 12:42:21 +00:00
// @param idPledge the pledge that may or may not be canceled
function getOldestPledgeNotCanceled ( uint64 idPledge ) internal constant returns ( uint64 ) { //todo rename
if ( idPledge == 0 ) return 0 ;
Pledge storage n = findPledge ( idPledge ) ;
PledgeManager storage manager = findManager ( n . owner ) ;
if ( manager . managerType == PledgeManagerType . Giver ) return idPledge ;
2017-08-18 16:47:22 +00:00
2017-10-03 12:42:21 +00:00
assert ( manager . managerType == PledgeManagerType . Campaign ) ;
2017-08-18 16:47:22 +00:00
2017-10-03 12:42:21 +00:00
if ( ! isCampaignCanceled ( n . owner ) ) return idPledge ;
2017-08-18 16:47:22 +00:00
2017-10-03 12:42:21 +00:00
return getOldestPledgeNotCanceled ( n . oldPledge ) ;
2017-06-06 17:40:14 +00:00
}
2017-10-03 12:42:21 +00:00
function checkManagerOwner ( PledgeManager m ) internal constant {
2017-09-18 13:43:09 +00:00
require ( ( msg . sender == m . addr ) || ( msg . sender == address ( m . plugin ) ) ) ;
}
2017-06-06 17:40:14 +00:00
}