Merge branch 'master' of github.com:Giveth/liquidpledging

This commit is contained in:
Jordi Baylina 2017-07-13 19:21:53 +02:00
commit c0063a1e3b
No known key found for this signature in database
GPG Key ID: 7480C80C1BE43112
3 changed files with 115 additions and 77 deletions

View File

@ -2,7 +2,9 @@ pragma solidity ^0.4.11;
contract ILiquidPledging { contract ILiquidPledging {
enum NoteManagerType { Donor, Delegate, Project } // TODO: make this enum its own contract... or at least make it so that an owner
// can add a new NoteManagerType
enum NoteManagerType { Donor, Delegate, Project}
enum PaymentState {NotPaid, Paying, Paid} enum PaymentState {NotPaid, Paying, Paid}
function numberOfNotes() constant returns (uint); function numberOfNotes() constant returns (uint);

View File

@ -5,22 +5,24 @@ import "./LiquidPledgingBase.sol";
contract LiquidPledging is LiquidPledgingBase { contract LiquidPledging is LiquidPledgingBase {
uint constant MAX_DELEGATES = 20;
uint constant MAX_SUBPROJECT_LEVEL = 20;
////// //////
// Constructor // Constructor
////// //////
// This constructor actualy also calls the constructor for the
// `LiquidPledgingBase` contract
function LiquidPledging(address _vault) LiquidPledgingBase(_vault) { function LiquidPledging(address _vault) LiquidPledgingBase(_vault) {
} }
/// @notice This is the main entry of Ether to the system. Ethether goes to /// @notice This is how value enters into the system which creates notes. The
/// the vault and then the Note for the donor withou delegates is increased. /// token of value goes into the vault and then the amount in the Note
/// relevant to this donor without delegates is increased.
/// After that, a normal transfer is done to the idReceiver. /// After that, a normal transfer is done to the idReceiver.
/// @param idDonor Id of the donor thats donating. /// @param idDonor Identifier of the donor thats donating.
/// @param idReceiver To who it's transfered. Can ve the same donnor, another /// @param idReceiver To whom it's transfered. Can be the same donor, another
/// donor, a delegate or a project /// donor, a delegate or a project
function donate(uint64 idDonor, uint64 idReceiver) payable { function donate(uint64 idDonor, uint64 idReceiver) payable {// TODO change to `pledge()`
NoteManager storage sender = findManager(idDonor); NoteManager storage sender = findManager(idDonor);
require(sender.managerType == NoteManagerType.Donor); require(sender.managerType == NoteManagerType.Donor);
@ -30,10 +32,10 @@ contract LiquidPledging is LiquidPledgingBase {
require(amount > 0); require(amount > 0);
vault.transfer(amount); vault.transfer(amount); // transfers the baseToken to the Vault
uint64 idNote = findNote( uint64 idNote = findNote(
idDonor, idDonor,
new uint64[](0), new uint64[](0), //what is new
0, 0,
0, 0,
0, 0,
@ -49,13 +51,13 @@ contract LiquidPledging is LiquidPledgingBase {
} }
/// @notice This is the main function to move Ether from one Note to the other /// @notice This is the main function to move value from one Note to the other
/// @param idSender Id of the donor, delegate or project manager that is transferin /// @param idSender ID of the donor, delegate or project manager that is transfering
/// the funds from Note to note. This manager must have permisions to move the Ether /// the funds from Note to Note. This manager must have permisions to move the value
/// @param idNote Id of the note that's moving the Ether. /// @param idNote Id of the note that's moving the value
/// @param amount Quantity of Ether that's moving. /// @param amount Quantity of value that's being moved
/// @param idReceiver Destination of the Ether, can be a donor to move Ether between donors, /// @param idReceiver Destination of the value, can be a donor sending to a donor or
/// A delegate to delegate that Ether, or a project to commit or precommit it to that project. /// a delegate, a delegate to another delegate or a project to precommit it to that project
function transfer(uint64 idSender, uint64 idNote, uint amount, uint64 idReceiver) { function transfer(uint64 idSender, uint64 idNote, uint amount, uint64 idReceiver) {
idNote = normalizeNote(idNote); idNote = normalizeNote(idNote);
@ -85,7 +87,7 @@ contract LiquidPledging is LiquidPledgingBase {
uint senderDIdx = getDelegateIdx(n, idSender); uint senderDIdx = getDelegateIdx(n, idSender);
if (senderDIdx != NOTFOUND) { if (senderDIdx != NOTFOUND) {
// If the receiver is another doner // If the receiver is another donor
if (receiver.managerType == NoteManagerType.Donor) { if (receiver.managerType == NoteManagerType.Donor) {
// Only accept to change to the original donor to remove all delegates // Only accept to change to the original donor to remove all delegates
assert(n.owner == idReceiver); assert(n.owner == idReceiver);
@ -96,23 +98,32 @@ contract LiquidPledging is LiquidPledgingBase {
// If the receiver is another delegate // If the receiver is another delegate
if (receiver.managerType == NoteManagerType.Delegate) { if (receiver.managerType == NoteManagerType.Delegate) {
uint receiverDIdx = getDelegateIdx(n, idReceiver); uint receiverDIdx = getDelegateIdx(n, idReceiver);
// If the receiver is not in the delegate list // If the receiver is not in the delegate list
if (receiverDIdx == NOTFOUND) { if (receiverDIdx == NOTFOUND) {
undelegate(idNote, amount, n.delegationChain.length - senderDIdx - 1); undelegate(idNote, amount, n.delegationChain.length - senderDIdx - 1);
appendDelegate(idNote, amount, idReceiver); appendDelegate(idNote, amount, idReceiver);
// If the receiver is after the delegate list and is not the next one.
// Canccel delegations an redelegate // If the receiver is already part of the delegate chain and is
// after the sender, then all of the other delegates after the sender are
// removed and the receiver is appended at the end of the delegation chain
} else if (receiverDIdx > senderDIdx) { } else if (receiverDIdx > senderDIdx) {
undelegate(idNote, amount, n.delegationChain.length - senderDIdx - 1); undelegate(idNote, amount, n.delegationChain.length - senderDIdx - 1);
appendDelegate(idNote, amount, idReceiver); appendDelegate(idNote, amount, idReceiver);
// If it's before the list cancel thelegations until him
// If the receiver is already part of the delegate chain and is
// before the sender, then the sender and all of the other
// delegates after the RECEIVER are revomved from the chain,
// this is interesting because the delegate undelegates from the
// delegates that delegated to this delegate... game theory issues? should this be allowed
} else if (receiverDIdx <= senderDIdx) { } else if (receiverDIdx <= senderDIdx) {
undelegate(idNote, amount, n.delegationChain.length - receiverDIdx -1); undelegate(idNote, amount, n.delegationChain.length - receiverDIdx -1);
} }
return; return;
} }
// If the delegate chose a project to assign // If the delegate wants to support a project, they undelegate all
// the delegates after them in the chain and choose a project
if (receiver.managerType == NoteManagerType.Project) { if (receiver.managerType == NoteManagerType.Project) {
undelegate(idNote, amount, n.delegationChain.length - senderDIdx - 1); undelegate(idNote, amount, n.delegationChain.length - senderDIdx - 1);
proposeAssignProject(idNote, amount, idReceiver); proposeAssignProject(idNote, amount, idReceiver);
@ -123,8 +134,8 @@ contract LiquidPledging is LiquidPledgingBase {
} }
/// @notice This method is used to withdraw Ether from the system. This can be used /// @notice This method is used to withdraw value from the system. This can be used
/// from the doonurs to rollback a not commited donation or by project manager to use /// by the donors to avoid committing the donation or by project manager to use
/// the Ether. /// the Ether.
/// @param idNote Id of the note that wants to be withdrawed. /// @param idNote Id of the note that wants to be withdrawed.
/// @param amount Quantity of Ether that wants to be withdrawed. /// @param amount Quantity of Ether that wants to be withdrawed.
@ -183,7 +194,7 @@ contract LiquidPledging is LiquidPledgingBase {
function cancelPayment(uint64 idNote, uint amount) onlyVault { function cancelPayment(uint64 idNote, uint amount) onlyVault {
Note storage n = findNote(idNote); Note storage n = findNote(idNote);
require(n.paymentState == PaymentState.Paying); require(n.paymentState == PaymentState.Paying); //TODO change to revert
// When a payment is cacnceled, never is assigned to a project. // When a payment is cacnceled, never is assigned to a project.
uint64 oldNote = findNote( uint64 oldNote = findNote(
@ -212,6 +223,8 @@ contract LiquidPledging is LiquidPledgingBase {
// Multi note methods // Multi note methods
//////// ////////
// This set of functions makes moving a lot of notes around much more
// efficient (saves gas) than calling these functions in series
uint constant D64 = 0x10000000000000000; uint constant D64 = 0x10000000000000000;
function mTransfer(uint64 idSender, uint[] notesAmounts, uint64 idReceiver) { function mTransfer(uint64 idSender, uint[] notesAmounts, uint64 idReceiver) {
for (uint i = 0; i < notesAmounts.length; i++ ) { for (uint i = 0; i < notesAmounts.length; i++ ) {
@ -253,7 +266,8 @@ contract LiquidPledging is LiquidPledgingBase {
// Private methods // Private methods
/////// ///////
// this function is obvious, but it can also be called to undelegate everyone
// by setting your self as teh idReceiver
function transferOwnershipToProject(uint64 idNote, uint amount, uint64 idReceiver) internal { function transferOwnershipToProject(uint64 idNote, uint amount, uint64 idReceiver) internal {
Note storage n = findNote(idNote); Note storage n = findNote(idNote);
@ -265,24 +279,19 @@ contract LiquidPledging is LiquidPledgingBase {
0, 0,
n.oldNote, n.oldNote,
PaymentState.NotPaid); PaymentState.NotPaid);
// If the owner does not change, then just let it this way.
if (n.owner == idReceiver) return;
uint64 toNote = findNote( uint64 toNote = findNote(
idReceiver, idReceiver,
new uint64[](0), new uint64[](0),
0, 0,
0, 0,
oldNote, oldNote,
PaymentState.NotPaid); PaymentState.NotPaid);
doTransfer(idNote, toNote, amount); doTransfer(idNote, toNote, amount);
} }
function transferOwnershipToDonor(uint64 idNote, uint amount, uint64 idReceiver) internal { function transferOwnershipToDonor(uint64 idNote, uint amount, uint64 idReceiver) internal {
// If the owner does not change, then just let it this way.
Note storage n = findNote(idNote); Note storage n = findNote(idNote);
if (n.owner == idReceiver) return;
uint64 toNote = findNote( uint64 toNote = findNote(
idReceiver, idReceiver,
new uint64[](0), new uint64[](0),
@ -296,12 +305,15 @@ contract LiquidPledging is LiquidPledgingBase {
function appendDelegate(uint64 idNote, uint amount, uint64 idReceiver) internal { function appendDelegate(uint64 idNote, uint amount, uint64 idReceiver) internal {
Note storage n= findNote(idNote); Note storage n= findNote(idNote);
require(n.delegationChain.length < MAX_DELEGATES); require(n.delegationChain.length < MAX_DELEGATES); //TODO change to revert and say the error
uint64[] memory newDelegationChain = new uint64[](n.delegationChain.length + 1); uint64[] memory newDelegationChain = new uint64[](n.delegationChain.length + 1);
for (uint i=0; i<n.delegationChain.length; i++) { for (uint i=0; i<n.delegationChain.length; i++) {
newDelegationChain[i] = n.delegationChain[i]; newDelegationChain[i] = n.delegationChain[i];
} }
// Make the last item in the array the idReceiver
newDelegationChain[n.delegationChain.length] = idReceiver; newDelegationChain[n.delegationChain.length] = idReceiver;
uint64 toNote = findNote( uint64 toNote = findNote(
n.owner, n.owner,
newDelegationChain, newDelegationChain,
@ -330,7 +342,7 @@ contract LiquidPledging is LiquidPledgingBase {
} }
function proposeAssignProject(uint64 idNote, uint amount, uint64 idReceiver) internal { function proposeAssignProject(uint64 idNote, uint amount, uint64 idReceiver) internal {// Todo rename
Note storage n = findNote(idNote); Note storage n = findNote(idNote);
require(getProjectLevel(n) < MAX_SUBPROJECT_LEVEL); require(getProjectLevel(n) < MAX_SUBPROJECT_LEVEL);
@ -358,8 +370,16 @@ contract LiquidPledging is LiquidPledgingBase {
Transfer(from, to, amount); Transfer(from, to, amount);
} }
// This function does 2 things, #1: it checks to make sure that the pledges are correct
// if the a pledged project has already been commited then it changes the owner
// to be the proposed project (Note that the UI will have to read the commit time and manually
// do what this function does to the note for the end user at the expiration of the committime)
// #2: It checks to make sure that if there has been a cancellation in the chain of projects,
// then it adjusts the note's owner appropriately.
function normalizeNote(uint64 idNote) internal returns(uint64) { function normalizeNote(uint64 idNote) internal returns(uint64) {
Note storage n = findNote(idNote); Note storage n = findNote(idNote);
// Check to make sure this note hasnt already been used or is in the process of being used
if (n.paymentState != PaymentState.NotPaid) return idNote; if (n.paymentState != PaymentState.NotPaid) return idNote;
// First send to a project if it's proposed and commited // First send to a project if it's proposed and commited
@ -383,7 +403,7 @@ contract LiquidPledging is LiquidPledgingBase {
n = findNote(idNote); n = findNote(idNote);
} }
toNote = getOldestNoteNotCanceled(idNote); toNote = getOldestNoteNotCanceled(idNote);// TODO toNote is note defined
if (toNote != idNote) { if (toNote != idNote) {
doTransfer(idNote, toNote, n.amount); doTransfer(idNote, toNote, n.amount);
} }

View File

@ -6,14 +6,18 @@ contract Vault {
contract LiquidPledgingBase { contract LiquidPledgingBase {
enum NoteManagerType { Donor, Delegate, Project } uint constant MAX_DELEGATES = 20;
enum PaymentState {NotPaid, Paying, Paid} uint constant MAX_SUBPROJECT_LEVEL = 20;
struct NoteManager { enum NoteManagerType { Donor, Delegate, Project }// todo change name
enum PaymentState { NotPaid, Paying, Paid }
// This struct defines the details of each the NoteManager, these NoteManagers can create
struct NoteManager {// change manager
NoteManagerType managerType; NoteManagerType managerType;
address addr; address addr;
string name; string name;
uint64 commitTime; // Only used in donors and campaigns uint64 commitTime; // Only used in donors and projects, its the precommitment time
address reviewer; // Only for project address reviewer; // Only for project
bool canceled; // Only for project bool canceled; // Only for project
} }
@ -21,19 +25,19 @@ contract LiquidPledgingBase {
struct Note { struct Note {
uint amount; uint amount;
uint64 owner; uint64 owner;
uint64[] delegationChain; uint64[] delegationChain; //index numbers!!!!!
uint64 proposedProject; uint64 proposedProject; // TODO change the name only used for when delegates are precommiting to a project
uint64 commitTime; // At what time the upcoming time will become an owner. uint64 commitTime; // At what time the upcoming time will become an owner.
uint64 oldNote; uint64 oldNote; // this points to the Note[] index that the Note was derived from
PaymentState paymentState; PaymentState paymentState;
} }
Note[] notes; Note[] notes;
NoteManager[] managers; NoteManager[] managers; // the list of all the note managers 0 is reserved for no manager
Vault public vault; Vault public vault;
// this mapping allows you to search for a specific note's index number by the hash of that note
mapping (bytes32 => uint64) hNote2ddx; mapping (bytes32 => uint64) hNote2ddx;//TODO Fix typo
///// /////
@ -51,8 +55,8 @@ contract LiquidPledgingBase {
////// //////
function LiquidPledgingBase(address _vault) { function LiquidPledgingBase(address _vault) {
managers.length = 1; managers.length = 1; // we reserve the 0 manager
notes.length = 1; notes.length = 1; // we reserve the 0 note
vault = Vault(_vault); vault = Vault(_vault);
} }
@ -61,7 +65,7 @@ contract LiquidPledgingBase {
// Managers functions // Managers functions
////// //////
function addDonor(string name, uint64 commitTime) { function addDonor(string name, uint64 commitTime) {//Todo return idManager
managers.push(NoteManager( managers.push(NoteManager(
NoteManagerType.Donor, NoteManagerType.Donor,
msg.sender, msg.sender,
@ -73,7 +77,7 @@ contract LiquidPledgingBase {
DonorAdded(uint64(managers.length-1)); DonorAdded(uint64(managers.length-1));
} }
event DonorAdded(uint64 indexed idMember); event DonorAdded(uint64 indexed idDonor);
function updateDonor( function updateDonor(
uint64 idDonor, uint64 idDonor,
@ -90,9 +94,9 @@ contract LiquidPledgingBase {
DonorUpdated(idDonor); DonorUpdated(idDonor);
} }
event DonorUpdated(uint64 indexed idMember); event DonorUpdated(uint64 indexed idDonor);
function addDelegate(string name) { function addDelegate(string name) { //TODO return index number
managers.push(NoteManager( managers.push(NoteManager(
NoteManagerType.Delegate, NoteManagerType.Delegate,
msg.sender, msg.sender,
@ -104,7 +108,7 @@ contract LiquidPledgingBase {
DeegateAdded(uint64(managers.length-1)); DeegateAdded(uint64(managers.length-1));
} }
event DeegateAdded(uint64 indexed idMember); event DeegateAdded(uint64 indexed idDelegate);
function updateDelegate(uint64 idDelegate, address newAddr, string newName) { function updateDelegate(uint64 idDelegate, address newAddr, string newName) {
NoteManager storage delegate = findManager(idDelegate); NoteManager storage delegate = findManager(idDelegate);
@ -115,7 +119,7 @@ contract LiquidPledgingBase {
DelegateUpdated(idDelegate); DelegateUpdated(idDelegate);
} }
event DelegateUpdated(uint64 indexed idMember); event DelegateUpdated(uint64 indexed idDelegate);
function addProject(string name, address reviewer, uint64 commitTime) { function addProject(string name, address reviewer, uint64 commitTime) {
managers.push(NoteManager( managers.push(NoteManager(
@ -129,7 +133,7 @@ contract LiquidPledgingBase {
ProjectAdded(uint64(managers.length-1)); ProjectAdded(uint64(managers.length-1));
} }
event ProjectAdded(uint64 indexed idMember); event ProjectAdded(uint64 indexed idProject);
function updateProject(uint64 idProject, address newAddr, string newName, uint64 newCommitTime) { function updateProject(uint64 idProject, address newAddr, string newName, uint64 newCommitTime) {
NoteManager storage project = findManager(idProject); NoteManager storage project = findManager(idProject);
@ -141,7 +145,7 @@ contract LiquidPledgingBase {
ProjectUpdated(idProject); ProjectUpdated(idProject);
} }
function updateProjectCanceler(uint64 idProject, address newReviewer) { function updateProjectReviewer(uint64 idProject, address newReviewer) {
NoteManager storage project = findManager(idProject); NoteManager storage project = findManager(idProject);
require(project.managerType == NoteManagerType.Project); require(project.managerType == NoteManagerType.Project);
require(project.reviewer == msg.sender); require(project.reviewer == msg.sender);
@ -149,7 +153,7 @@ contract LiquidPledgingBase {
ProjectUpdated(idProject); ProjectUpdated(idProject);
} }
event ProjectUpdated(uint64 indexed idMember); event ProjectUpdated(uint64 indexed idManager);
////////// //////////
@ -179,7 +183,7 @@ contract LiquidPledgingBase {
oldNote = n.oldNote; oldNote = n.oldNote;
paymentState = n.paymentState; paymentState = n.paymentState;
} }
// This is to return the delegates one by one, because you can not return an array
function getNoteDelegate(uint64 idNote, uint idxDelegate) constant returns( function getNoteDelegate(uint64 idNote, uint idxDelegate) constant returns(
uint64 idDelegate, uint64 idDelegate,
address addr, address addr,
@ -217,6 +221,9 @@ contract LiquidPledgingBase {
// Private methods // Private methods
/////// ///////
// All notes exist... but if the note hasn't been created in this system yet then it wouldn't
// be in the hash array hNoteddx[]
// this function creates a balloon if one is not created already... this ballon has 0 for the amount
function findNote( function findNote(
uint64 owner, uint64 owner,
uint64[] delegationChain, uint64[] delegationChain,
@ -245,12 +252,35 @@ contract LiquidPledgingBase {
return notes[idNote]; return notes[idNote];
} }
function getOldestNoteNotCanceled(uint64 idNote) internal constant returns(uint64) { // a constant for the case that a delegate is requested that is not a delegate in the system
uint64 constant NOTFOUND = 0xFFFFFFFFFFFFFFFF;
// helper function that searches the delegationChain fro a specific delegate and
// level of delegation returns their idx in the delegation cahin which reflect their level of authority
function getDelegateIdx(Note n, uint64 idDelegate) internal returns(uint64) {
for (uint i=0; i<n.delegationChain.length; i++) {
if (n.delegationChain[i] == idDelegate) return uint64(i);
}
return NOTFOUND;
}
// helper function that returns the project level solely to check that there
// are not too many Projects that violate MAX_SUBPROJECT_LEVEL
function getProjectLevel(Note n) internal returns(uint) {
if (n.oldNote == 0) return 0;//changed
Note oldN = findNote(n.oldNote);
return getProjectLevel(oldN) + 1;
}
// this makes it easy to cancel projects
// @param idNote the note that may or may not be cancelled
function getOldestNoteNotCanceled(uint64 idNote) internal constant returns(uint64) { //todo rename
if (idNote == 0) return 0; if (idNote == 0) return 0;
Note storage n = findNote(idNote); Note storage n = findNote(idNote);
NoteManager storage owner = findManager(n.owner); NoteManager storage owner = findManager(n.owner);
if (owner.managerType == NoteManagerType.Donor) return idNote; if (owner.managerType == NoteManagerType.Donor) return idNote;
// This function calls itself to iterate up the chain to check which
// projects are cancelled, confirming that it is returning the Oldest valid Note
uint64 parentProject = getOldestNoteNotCanceled(n.oldNote); uint64 parentProject = getOldestNoteNotCanceled(n.oldNote);
if (owner.canceled) { // Current project is canceled. if (owner.canceled) { // Current project is canceled.
@ -262,18 +292,4 @@ contract LiquidPledgingBase {
} }
} }
uint64 constant NOTFOUND = 0xFFFFFFFFFFFFFFFF;
function getDelegateIdx(Note n, uint64 idDelegate) internal returns(uint64) {
for (uint i=0; i<n.delegationChain.length; i++) {
if (n.delegationChain[i] == idDelegate) return uint64(i);
}
return NOTFOUND;
}
function getProjectLevel(Note n) internal returns(uint) {
if (n.oldNote == 0) return 1;
Note storage oldN = findNote(n.oldNote);
return getProjectLevel(oldN) + 1;
}
} }