From cec39ea18ee0cbed9904d2a58b255e70ed047585 Mon Sep 17 00:00:00 2001 From: Jordi Baylina Date: Tue, 27 Jun 2017 13:08:23 +0200 Subject: [PATCH] NormalOperation tests completed --- contracts/LiquidPledging.sol | 11 +- contracts/LiquidPledgingBase.sol | 34 +++--- package-lock.json | 140 ++++++++++++++++++++++ package.json | 4 + test/NormalOperation.js | 197 ++++++++++++++++++++++++++++--- test/helpers/assertFail.js | 9 ++ 6 files changed, 354 insertions(+), 41 deletions(-) create mode 100644 test/helpers/assertFail.js diff --git a/contracts/LiquidPledging.sol b/contracts/LiquidPledging.sol index 711d25d..d2276a7 100644 --- a/contracts/LiquidPledging.sol +++ b/contracts/LiquidPledging.sol @@ -142,7 +142,8 @@ contract LiquidPledging is LiquidPledgingBase { if (n.paymentState != PaymentState.Paying) throw; - if (isProjectCanceled(n.owner)) throw; + // Check the project is not canceled in the while. + if (getOldestNoteNotCanceled(idNote) != idNote) throw; uint64 idNewNote = findNote( n.owner, @@ -178,9 +179,7 @@ contract LiquidPledging is LiquidPledgingBase { function cancelProject(uint64 idProject) { NoteManager project = findManager(idProject); - if ( (project.reviewer != msg.sender) - &&(project.addr != msg.sender)) - throw; + require((project.reviewer == msg.sender) || (project.addr == msg.sender)); project.canceled = true; } @@ -317,7 +316,7 @@ contract LiquidPledging is LiquidPledgingBase { if (n.paymentState != PaymentState.NotPaid) return idNote; // First send to a project if it's proposed and commited - if ((n.proposedProject > 0) && ( getTime() > n.commmitTime)) { + if ((n.proposedProject > 0) && ( getTime() > n.commitTime)) { uint64 oldNote = findNote( n.owner, n.delegationChain, @@ -334,6 +333,7 @@ contract LiquidPledging is LiquidPledgingBase { PaymentState.NotPaid); doTransfer(idNote, toNote, n.amount); idNote = toNote; + n = findNote(idNote); } toNote = getOldestNoteNotCanceled(idNote); @@ -343,7 +343,6 @@ contract LiquidPledging is LiquidPledgingBase { return toNote; } - ///////////// // Test functions ///////////// diff --git a/contracts/LiquidPledgingBase.sol b/contracts/LiquidPledgingBase.sol index be9a475..207c634 100644 --- a/contracts/LiquidPledgingBase.sol +++ b/contracts/LiquidPledgingBase.sol @@ -21,7 +21,7 @@ contract LiquidPledgingBase { uint64 owner; uint64[] delegationChain; uint64 proposedProject; - uint64 commmitTime; // At what time the upcoming time will become an owner. + uint64 commitTime; // At what time the upcoming time will become an owner. uint64 oldNote; PaymentState paymentState; } @@ -164,7 +164,7 @@ contract LiquidPledgingBase { uint64 owner, uint64 nDelegates, uint64 proposedProject, - uint64 commmitTime, + uint64 commitTime, uint64 oldNote, PaymentState paymentState ) { @@ -173,7 +173,7 @@ contract LiquidPledgingBase { owner = n.owner; nDelegates = uint64(n.delegationChain.length); proposedProject = n.proposedProject; - commmitTime = n.commmitTime; + commitTime = n.commitTime; oldNote = n.oldNote; paymentState = n.paymentState; } @@ -219,17 +219,17 @@ contract LiquidPledgingBase { uint64 owner, uint64[] delegationChain, uint64 proposedProject, - uint64 commmitTime, + uint64 commitTime, uint64 oldNote, PaymentState paid ) internal returns (uint64) { - bytes32 hNote = sha3(owner, delegationChain, proposedProject, commmitTime, oldNote, paid); + bytes32 hNote = sha3(owner, delegationChain, proposedProject, commitTime, oldNote, paid); uint64 idx = hNote2ddx[hNote]; if (idx > 0) return idx; idx = uint64(notes.length); hNote2ddx[hNote] = idx; - notes.push(Note(0, owner, delegationChain, proposedProject, commmitTime, oldNote, paid)); + notes.push(Note(0, owner, delegationChain, proposedProject, commitTime, oldNote, paid)); return idx; } @@ -243,25 +243,23 @@ contract LiquidPledgingBase { return notes[idNote]; } - function getOldestNoteNotCanceled(uint64 idProject) internal constant returns(uint64) { - if (idProject == 0) return 0; - Note n = findNote(idProject); + function getOldestNoteNotCanceled(uint64 idNote) internal constant returns(uint64) { + if (idNote == 0) return 0; + Note n = findNote(idNote); NoteManager owner = findManager(n.owner); - if (owner.managerType == NoteManagerType.Donor) return idProject; + if (owner.managerType == NoteManagerType.Donor) return idNote; uint64 parentProject = getOldestNoteNotCanceled(n.oldNote); - if (parentProject == n.oldNote) { - return idProject; - } else { + + if (owner.canceled) { // Current project is canceled. + return parentProject; + } else if (parentProject == n.oldNote) { // None of the top projects is canceled + return idNote; + } else { // Current is not canceled but some ont the top yes return parentProject; } } - function isProjectCanceled(uint64 idProject) internal constant returns(bool){ - uint parentProject = getOldestNoteNotCanceled(idProject); - return (parentProject != idProject); - } - uint64 constant NOTFOUND = 0xFFFFFFFFFFFFFFFF; function getDelegateIdx(Note n, uint64 idDelegate) internal returns(uint64) { for (uint i=0; i { + const note = { + delegates: [], + }; + const res = await liquidPledging.getNote(idNote); + note.amount = res[0]; + note.owner = res[1]; + for (let i=1; i <= res[2].toNumber(); i += 1) { + const delegate = {}; + const resd = await liquidPledging.getNoteDelegate(idNote, i); + delegate.id = resd[0].toNumber(); + delegate.addr = resd[1]; + delegate.name = resd[2]; + note.delegates.push(delegate); + } + if (res[3].toNumber()) { + note.proposedProject = res[3].toNumber(); + note.commmitTime = res[4].toNumber(); + } + if (res[5].toNumber()) { + note.oldProject = res[5].toNumber(); + } + if (res[6].toNumber() == 0) { + note.paymentState = "NotPaid"; + } else if (res[6].toNumber() == 1) { + note.paymentState = "Paying"; + } else if (res[6].toNumber() == 2) { + note.paymentState = "Paid"; + } else { + note.paymentState = "Unknown"; + } + return note; +}; + +const getManager = async (liquidPledging, idManager) => { + const manager = {}; + const res = await liquidPledging.getNoteManager(idManager); + if (res[0].toNumber() == 0) { + manager.paymentState = "Donor"; + } else if (res[0].toNumber() === 1) { + manager.paymentState = "Delegate"; + } else if (res[0].toNumber() === 2) { + manager.paymentState = "Project"; + } else { + manager.paymentState = "Unknown"; + } + manager.addr = res[1]; + manager.name = res[2]; + manager.commitTime = res[3].toNumber(); + if (manager.paymentState == "Project") { + manager.reviewer = res[4]; + manager.canceled = res[5]; + } + return manager; +}; + +const getState = async (liquidPledging) => { + const st = { + notes: [null], + managers: [null], + }; + const nNotes = await liquidPledging.numberOfNotes(); + for (let i=1; i <= nNotes; i += 1) { + const note = await getNote(liquidPledging, i); + st.notes.push(note); + } + + const nManagers = await liquidPledging.numberOfNoteManagers(); + for (let i=1; i <= nManagers; i += 1) { + const manager = await getManager(liquidPledging, i); + st.managers.push(manager); + } + return st; +}; + +const printState = async(liquidPledging) => { + const st = await getState(liquidPledging); + console.log(JSON.stringify(st, null, 2)); +}; + +const printBalances = async(liquidPledging) => { + const st = await getState(liquidPledging); + assert.equal(st.notes.length, 13); + for (let i=1; i<=12; i++) { + console.log(i, web3.fromWei(st.notes[ i ].amount).toNumber() ) + } +}; + contract("LiquidPledging", (accounts) => { let liquidPledging; let vault; - let donor1 = accounts[0]; - let delegate1 = accounts[1]; - let adminProject1 = accounts[2]; - let adminProject2 = accounts[3]; - let reviewer = accounts[4]; + let donor1 = accounts[1]; + let delegate1 = accounts[2]; + let adminProject1 = accounts[3]; + let adminProject2 = accounts[4]; + let adminProject2a = accounts[5]; + let delegate2 = accounts[6]; + let reviewer = accounts[7]; it("Should deploy LiquidPledgin contract", async () => { vault = await Vault.new(); liquidPledging = await LiquidPledging.new(vault.address); @@ -40,7 +132,7 @@ contract("LiquidPledging", (accounts) => { assert.equal(res[2], "Delegate1"); }); it("Donor should delegate on the delegate ", async () => { - await liquidPledging.transfer(1, 1, web3.toWei(0.5), 2); + await liquidPledging.transfer(1, 1, web3.toWei(0.5), 2, {from: donor1}); const nNotes = await liquidPledging.numberOfNotes(); assert.equal(nNotes.toNumber(), 2); const res1 = await liquidPledging.getNote(1); @@ -151,34 +243,105 @@ contract("LiquidPledging", (accounts) => { assert.equal(res7[4], 0); // commit time assert.equal(res7[5].toNumber(), 2); // Old Node assert.equal(res7[6].toNumber(), 2); // Peinding paid Paid - }); it("Reviewer should be able to cancel project1", async () => { - + await liquidPledging.cancelProject(3, {from: reviewer}); + const st = await getState(liquidPledging); + assert.equal(st.managers[3].canceled , true); + }); + it("Should not allow to withdraw from a canceled project", async () => { + const st = await getState(liquidPledging); + assert.equal(web3.fromWei(st.notes[5].amount).toNumber(), 0.05); + await assertFail(async function() { + await liquidPledging.withdraw(5, web3.toWei(0.01), {from: adminProject1}); + }); }); it("Delegate should send part of this ETH to project2", async () => { - + await liquidPledging.transfer(2, 5, web3.toWei(0.03), 4, {from: delegate1}); + const st = await getState(liquidPledging); + assert.equal(st.notes.length, 9); + assert.equal(web3.fromWei(st.notes[ 8 ].amount).toNumber(), 0.03); + assert.equal(st.notes[8].owner, 1); + assert.equal(st.notes[8].delegates.length, 1); + assert.equal(st.notes[8].delegates[0].id, 2); + assert.equal(st.notes[8].proposedProject, 4); }); - it("Owner should be able to send the remaining to project2", async () => { - + it("Donor should be able to send the remaining to project2", async () => { + await liquidPledging.transfer(1, 5, web3.toWei(0.02), 4, {from: donor1}); + const st = await getState(liquidPledging); + assert.equal(st.notes.length, 9); + assert.equal(web3.fromWei(st.notes[ 5 ].amount).toNumber(), 0); + assert.equal(web3.fromWei(st.notes[ 4 ].amount).toNumber(), 0.12); }); it("A subproject 2a and a delegate2 is created", async () => { - + await liquidPledging.addProject("Project2a", reviewer, 86400, {from: adminProject2a}); + await liquidPledging.addDelegate("Delegate2", {from: delegate2}); + const nManagers = await liquidPledging.numberOfNoteManagers(); + assert.equal(nManagers, 6); }); it("Project 2 delegate in delegate2", async () => { - + await liquidPledging.transfer(4, 4, web3.toWei(0.02), 6, {from: adminProject2}); + const st = await getState(liquidPledging); + assert.equal(st.notes.length, 10); + assert.equal(web3.fromWei(st.notes[ 9 ].amount).toNumber(), 0.02); + assert.equal(web3.fromWei(st.notes[ 4 ].amount).toNumber(), 0.1); }); it("delegate2 assigns to projec2a", async () => { - + await liquidPledging.transfer(6, 9, web3.toWei(0.01), 5, {from: delegate2}); + const st = await getState(liquidPledging); + assert.equal(st.notes.length, 11); + assert.equal(web3.fromWei(st.notes[ 9 ].amount).toNumber(), 0.01); + assert.equal(web3.fromWei(st.notes[ 10 ].amount).toNumber(), 0.01); }); - it("project2a spends on a while", async () => { - + it("project2a authorize to spend a little", async () => { + const n = Math.floor(new Date().getTime() / 1000); + await liquidPledging.setMockedTime(n + 86401*3); + await liquidPledging.withdraw(10, web3.toWei(0.005), {from: adminProject2a}); + const st = await getState(liquidPledging); + assert.equal(st.notes.length, 13); + assert.equal(web3.fromWei(st.notes[ 10 ].amount).toNumber(), 0); + assert.equal(web3.fromWei(st.notes[ 11 ].amount).toNumber(), 0.005); + assert.equal(web3.fromWei(st.notes[ 12 ].amount).toNumber(), 0.005); }); it("project2 is canceled", async () => { - + await liquidPledging.cancelProject(4, {from: reviewer}); + }); + it("project2 should not be able to confirm payment", async () => { + await assertFail(async function() { + await vault.confirmPayment(1); + }); + }); + it("Should not be able to withdraw it", async() => { + await assertFail(async function() { + await liquidPledging.withdraw(12, web3.toWei(0.005), {from: donor1}); + }); + }); + it("Should not be able to cancel payment", async() => { + await vault.cancelPayment(1); + const st = await getState(liquidPledging); + assert.equal(st.notes.length, 13); + assert.equal(web3.fromWei(st.notes[ 2 ].amount).toNumber(), 0.31); + assert.equal(web3.fromWei(st.notes[ 11 ].amount).toNumber(), 0); + assert.equal(web3.fromWei(st.notes[ 12 ].amount).toNumber(), 0); }); it("original owner should recover the remaining funds", async () => { + const st = await getState(liquidPledging); + await liquidPledging.withdraw(1, web3.toWei(0.5), {from: donor1}); + await liquidPledging.withdraw(2, web3.toWei(0.31), {from: donor1}); + await liquidPledging.withdraw(4, web3.toWei(0.1), {from: donor1}); + + await liquidPledging.withdraw(8, web3.toWei(0.03), {from: donor1}); + const test = await liquidPledging.test(); + await liquidPledging.withdraw(9, web3.toWei(0.01), {from: donor1}); + + const initialBalance = await web3.eth.getBalance(donor1); + await vault.multiConfirm([2,3,4,5,6]); + + const finalBalance = await web3.eth.getBalance(donor1); + const collected = web3.fromWei(finalBalance.sub(initialBalance)).toNumber(); + + assert.equal(collected, 0.95); }); }); diff --git a/test/helpers/assertFail.js b/test/helpers/assertFail.js new file mode 100644 index 0000000..ccd6e0e --- /dev/null +++ b/test/helpers/assertFail.js @@ -0,0 +1,9 @@ +module.exports = async function(callback) { + let web3_error_thrown = false; + try { + await callback(); + } catch (error) { + if (error.message.search("invalid opcode")) web3_error_thrown = true; + } + assert.ok(web3_error_thrown, "Transaction should fail"); +};