From 6bc051e211deabeec56c1fd9769a4deffcb112ad Mon Sep 17 00:00:00 2001 From: Ricardo Guilherme Schmidt Date: Wed, 20 Mar 2019 22:01:06 -0300 Subject: [PATCH] add tests --- test/delegation.js | 45 +++++- test/proposal.js | 378 ++++++++++++++++++++++++++++++++++++++++++++ utils/merkleTree.js | 133 ++++++++++++++++ utils/testUtils.js | 185 +++++++++++++++++++--- 4 files changed, 718 insertions(+), 23 deletions(-) create mode 100644 test/proposal.js create mode 100644 utils/merkleTree.js diff --git a/test/delegation.js b/test/delegation.js index a7399fa..cd34573 100644 --- a/test/delegation.js +++ b/test/delegation.js @@ -21,6 +21,27 @@ config({ } }); + /** + * Delegations: Root, Sticker Market, ENS Usernames, Version Listings + * Functions: + * - Root + * - Sticker Market + * - change controller : Absolute Majority + * - change rates : Simple Majority + * - purgconte pack : Simple Majority + * - ENS Usernames + * - change controller : Simple Majority + * - migrate registry : + * - change price + * - Version Listing + * - change developer address + * - remove version + * - add release category (android,ios,linux,etc) + * + * + * + */ + contract("DelegationBase", function() { this.timeout(0); var defaultDelegate; @@ -129,6 +150,7 @@ contract("DelegationBase", function() { }) it("Child a2 delegate to a4", async function () { + result = await ChildDelegation.methods.delegate(accounts[4]).send({from: accounts[2]}) const delegateArgs = result.events.Delegate.returnValues; assert.equal(delegateArgs.who, accounts[2]) @@ -137,6 +159,7 @@ contract("DelegationBase", function() { result = await ChildDelegation.methods.delegatedTo(accounts[2]).call() assert.equal(result, accounts[4]) + result = await ChildDelegation.methods.delegationOf(accounts[0]).call() assert.equal(result, defaultDelegate) result = await ChildDelegation.methods.delegationOf(accounts[1]).call() @@ -144,7 +167,27 @@ contract("DelegationBase", function() { result = await ChildDelegation.methods.delegationOf(accounts[2]).call() assert.equal(result, defaultDelegate) - +   }) + it("default delegate should be able to delegate", async function () { + await ChildDelegation.methods.delegate(accounts[6]).send({from: accounts[6]}) + newDelegate = await ChildDelegation.methods.delegationOf(accounts[6]).call() + result = await ChildDelegation.methods.delegate(accounts[6]).send({from: defaultDelegate}) + const delegateArgs = result.events.Delegate.returnValues; + assert.equal(delegateArgs.who, defaultDelegate) + assert.equal(delegateArgs.to, accounts[6]) + + result = await ChildDelegation.methods.delegatedTo(defaultDelegate).call() + assert.equal(result, accounts[6]) + + result = await ChildDelegation.methods.delegationOf(accounts[0]).call() + assert.equal(result, newDelegate) + result = await ChildDelegation.methods.delegationOf(accounts[1]).call() + assert.equal(result, newDelegate) + result = await ChildDelegation.methods.delegationOf(accounts[2]).call() + assert.equal(result, newDelegate) + + + }) }) \ No newline at end of file diff --git a/test/proposal.js b/test/proposal.js new file mode 100644 index 0000000..5f9b618 --- /dev/null +++ b/test/proposal.js @@ -0,0 +1,378 @@ +const utils = require("../utils/testUtils") + + +const DefaultDelegation = require('Embark/contracts/DefaultDelegation'); +const DelegationFactory = require('Embark/contracts/DelegationFactory'); +const MiniMeToken = require('Embark/contracts/MiniMeToken'); +const Delegation = require('Embark/contracts/Delegation'); +const ProposalFactory = require('Embark/contracts/ProposalFactory'); +const Proposal = require('Embark/contracts/Proposal'); +const ProposalBase = require('Embark/contracts/ProposalBase'); + +config({ + contracts: { + "MiniMeTokenFactory": { + }, + "MiniMeToken": { + "args": [ + "$MiniMeTokenFactory", + utils.zeroAddress, + 0, + "TestMiniMeToken", + 18, + "TST", + true + ] + }, + "DefaultDelegation": { + "args": [ "$accounts[5]" ] + }, + "DelegationBase": { + "args": [ utils.zeroAddress ] + }, + "DelegationInit": {}, + "DelegationFactory": { + "args": ["$DelegationBase", "$DelegationInit", utils.zeroAddress] + }, + "ProposalBase": {}, + "ProposalInit": {}, + "ProposalFactory": { + "args": ["$ProposalBase", "$ProposalInit", utils.zeroAddress] + } + + } + }); +function mintTokens(accounts, amount) { + return Promise.all( + accounts.map((account) => { + return MiniMeToken.methods.generateTokens(account, amount).send({from: accounts[0]}); + }) + ); +} + +function newDelegation(topDelegation) { + return new Promise((resolve, reject) => { + DelegationFactory.methods.createDelegation(topDelegation).send().on('receipt', (receipt) => { + resolve(new web3.eth.Contract(Delegation._jsonInterface, receipt.events.InstanceCreated.returnValues[0])); + }).on('error', (error) => { + reject(error); + }); + }); + +} + +async function addGas(call, from, amount=21000) { + return call.send({ + gas: await call.estimateGas({gas: 8000000})+amount, + from: from + }); +} + +async function tabulateDirect(proposal, account) { + return addGas(proposal.methods.tabulateDirect(account), web3.eth.defaultAccount); +} + +async function tabulateDelegated(proposal, account) { + return addGas(proposal.methods.tabulateDelegated(account), web3.eth.defaultAccount); +} + +async function tabulateSigned(proposal, sig) { + return addGas( + proposal.methods.tabulateSigned( + sig.vote, + sig.position, + sig.proof, + sig.signature + ), + web3.eth.defaultAccount + ); +} + + +contract("Proposal", function() { + this.timeout(0); + const initialBalance = 17 * 10 ^ 18; + var defaultDelegate; + var accounts; + var RootDelegation; + var ChildDelegation; + + before(function(done) { + defaultDelegate = DefaultDelegation._address; + web3.eth.getAccounts().then((res) => { + web3.eth.defaultAccount = res[0] + accounts = res; + //let contactList = require("./contacts.json") + //contacts = Object.keys(contactList).map((key) => { return { name: contactList[key].name.en, address: utils.pubKeyToAddress(key), pubKey: key }}) + //res = accounts.concat(contacts.map((obj) => {return obj.address } )); + mintTokens(res, initialBalance).then((mintReceipts) => { + newDelegation(defaultDelegate).then((createdRoot) => { + RootDelegation = createdRoot; + newDelegation(RootDelegation._address).then((createdChild) => { + ChildDelegation = createdChild; + Promise.all([ + // root: 0 -> 1 -> 2 -> 3 (-> 5) + RootDelegation.methods.delegate(accounts[1]).send({from: accounts[0]}), + RootDelegation.methods.delegate(accounts[2]).send({from: accounts[1]}), + RootDelegation.methods.delegate(accounts[3]).send({from: accounts[2]}), + // root: 4 -> 4 + RootDelegation.methods.delegate(accounts[4]).send({from: accounts[4]}), + + // root: 6 -> 7 -> 8 -> 9 -> 6 (circular) + RootDelegation.methods.delegate(accounts[7]).send({from: accounts[6]}), + RootDelegation.methods.delegate(accounts[8]).send({from: accounts[7]}), + RootDelegation.methods.delegate(accounts[9]).send({from: accounts[8]}), + RootDelegation.methods.delegate(accounts[6]).send({from: accounts[9]}), + // child: 5 -> 6 + ChildDelegation.methods.delegate(accounts[7]).send({from: accounts[5]}) + ]).then((delegateReceipts) => { + done(); + }) + }) + }) + }); + }); + }); + + const QUORUM_QUALIFIED = 0; + const QUORUM_MAJORITY = 1; + const QUORUM_SIMPLE = 2; + + const VOTE_NULL = 0; + const VOTE_REJECT = 1; + const VOTE_APPROVE = 2; + + var testProposal; + var blockStart + it("create proposal by factory", async function () { + let tabulationBlockDelay = 50; + blockStart = await web3.eth.getBlockNumber() + 10; + let blockEndDelay = 10; + receipt = await ProposalFactory.methods.createProposal( + MiniMeToken._address, + RootDelegation._address, + "0xDA0", + tabulationBlockDelay, + blockStart, + blockEndDelay, + QUORUM_QUALIFIED + ).send() + testProposal = new web3.eth.Contract(ProposalBase._jsonInterface, receipt.events.InstanceCreated.returnValues[0]); + + }); + + it("rejects signed and direct votes while not voting period ", async function () { + assert(await web3.eth.getBlockNumber() < blockStart, "Wrong block number") + assert(await utils.assertEVMException(testProposal.methods.voteDirect(VOTE_APPROVE).send({from: accounts[0]}), "Voting not started"), "Didnt rejected") + assert(await utils.assertEVMException(testProposal.methods.voteSigned(utils.zeroBytes32).send({from: accounts[0]}), "Voting not started"), "Didnt rejected") + }); + + + it("increases block number to vote block start", async function () { + await utils.setBlockNumber(blockStart); + assert(await web3.eth.getBlockNumber() >= blockStart, "Wrong block number") + }); + + + it("direct vote", async function () { + let receipt = await testProposal.methods.voteDirect(VOTE_APPROVE).send({from: accounts[5]}); + assert.equal(receipt.events.Voted.returnValues[0], VOTE_APPROVE) + assert.equal(receipt.events.Voted.returnValues[1], accounts[5]); + }); + + const { MerkleTree } = require('../utils/merkleTree.js'); + var sigs = []; + + + it("signed vote at voting period", async function () { + let vote = VOTE_APPROVE; + let approveHash = await testProposal.methods.getVoteHash(vote).call(); + let signatures = await Promise.all(accounts.map((address) => { + //in web app should web3.eth.personal.sign(approveHash, address) + return web3.eth.sign(approveHash, address); + })) + let merkleTree = new MerkleTree(signatures); + let merkleRoot = merkleTree.getHexRoot(); + + let receipt = await testProposal.methods.voteSigned(merkleRoot).send({from: accounts[0]}) + let position = 0; + assert.equal(receipt.events.VoteSignatures.returnValues[0], position) + assert.equal(receipt.events.VoteSignatures.returnValues[1], merkleRoot); + for(var i = 0; i < signatures.length; i++) { + sigs.push ({ + position: position, + vote: vote, + signature: signatures[i], + proof: merkleTree.getHexProof(signatures[i]), + signer: accounts[i] + }) + + } + }); + var voteBlockEnd; + it("tests under block end", async function () { + voteBlockEnd = await testProposal.methods.voteBlockEnd().call(); + assert(await web3.eth.getBlockNumber() <= voteBlockEnd, "Current block number is too low for testing"); + }) + + it("reject tabulateDirect when voting not ended", async function () { + assert(await utils.assertEVMException(tabulateDirect(testProposal, accounts[5]), "Block end not reached"), "Didnt rejected") + }); + + it("reject tabulateDelegated when voting not ended", async function () { + assert(await utils.assertEVMException(tabulateDelegated(testProposal, accounts[0]), "Block end not reached"), "Didnt rejected") + }); + + it("reject tabulateSigned when voting not ended", async function () { + assert(await utils.assertEVMException(tabulateSigned(testProposal, sigs[0]), "Block end not reached"), "Didnt rejected") + }); + + it("increases block number to vote block end", async function () { + await utils.setBlockNumber(voteBlockEnd+1); + assert(await web3.eth.getBlockNumber() > voteBlockEnd, "Wrong block number") + }); + + it("rejects direct votes when voting period ended", async function () { + assert(await utils.assertEVMException(testProposal.methods.voteDirect(VOTE_APPROVE).send({from: accounts[1]}), "Voting ended"), "Didnt rejected") + }); + + it("rejects signed votes when voting period ended", async function () { + assert(await utils.assertEVMException(testProposal.methods.voteSigned(utils.zeroBytes32).send({from: accounts[0]}), "Voting ended"), "Didnt rejected") + }); + + it("reject tabulates when no delegate voted", async function () {; + assert(await utils.assertEVMException(tabulateDelegated(testProposal, accounts[4]), "No delegate vote found"), "Didnt rejected 2") + }); + + + it("tabulates influence from self voter", async function () { + let receipt = await tabulateDirect(testProposal, accounts[5]); + assert.equal(receipt.events.Claimed.returnValues[0], VOTE_APPROVE) + assert.equal(receipt.events.Claimed.returnValues[1], accounts[5]); + assert.equal(receipt.events.Claimed.returnValues[2], accounts[5]); + + }); + + it("tabulates influence from default delegate", async function () { + receipt = await tabulateDelegated(testProposal, accounts[3]); + assert.equal(receipt.events.Claimed.returnValues[0], VOTE_APPROVE) + assert.equal(receipt.events.Claimed.returnValues[1], accounts[5]); + assert.equal(receipt.events.Claimed.returnValues[2], accounts[3]); + + }); + + it("tabulates influence from voteSigned ", async function () { + let sig = sigs[2]; + let receipt = await tabulateSigned(testProposal, sig); + assert.equal(receipt.events.Voted.returnValues[0], sig.vote, "emit Voted wrong Vote") + assert.equal(receipt.events.Voted.returnValues[1], sig.signer, "emit Voted wrong address"); + assert.equal(receipt.events.Claimed.returnValues[0], sig.vote, "emit Claimed wrong Vote") + assert.equal(receipt.events.Claimed.returnValues[1], sig.signer, "emit Claimed wrong claimer"); + assert.equal(receipt.events.Claimed.returnValues[2], sig.signer, "emit Claimed wrong source"); + }); + + + it("should not tabulate for delegate if voted ", async function () { + assert(await utils.assertEVMException(tabulateDelegated(testProposal, accounts[2]), "Not delegatable"), "Didnt rejected ") + assert(await utils.assertEVMException(tabulateDelegated(testProposal, accounts[5]), "Not delegatable"), "Didnt rejected 2") + }); + + it("tabulates influence from direct delegate ", async function () { + let receipt = await tabulateDelegated(testProposal, accounts[1]); + assert.equal(receipt.events.Claimed.returnValues[0], VOTE_APPROVE) + assert.equal(receipt.events.Claimed.returnValues[1], accounts[2]); + assert.equal(receipt.events.Claimed.returnValues[2], accounts[1]) + }); + + it("tabulates influence from indirect delegate", async function () { + let receipt = await tabulateDelegated(testProposal, accounts[0]); + assert.equal(receipt.events.Claimed.returnValues[0], VOTE_APPROVE) + assert.equal(receipt.events.Claimed.returnValues[1], accounts[2]); + assert.equal(receipt.events.Claimed.returnValues[2], accounts[0]) + }); + + it("should not tabulate influence from circular delegation chain when none voted", async function () { + assert(await utils.assertEVMException(tabulateDelegated(testProposal, accounts[7]), "revert"), "Didnt rejected ") + }); + + + it("tabulates signature from circular delegation", async function () { + await tabulateSigned(testProposal, sigs[7]); + }) + + it("tabulates influence from circular direct delegate", async function () { + var receipt = await tabulateDelegated(testProposal, accounts[6]); + assert.equal(receipt.events.Claimed.returnValues[0], VOTE_APPROVE) + assert.equal(receipt.events.Claimed.returnValues[1], accounts[7]); + assert.equal(receipt.events.Claimed.returnValues[2], accounts[6]) + }) + + it("tabulates influence from circular indirect delegate", async function () { + receipt = await tabulateDelegated(testProposal, accounts[9]); + assert.equal(receipt.events.Claimed.returnValues[0], VOTE_APPROVE) + assert.equal(receipt.events.Claimed.returnValues[1], accounts[7]); + assert.equal(receipt.events.Claimed.returnValues[2], accounts[9]) + }) + it("tabulates influence from circular delegate of claiming delegate", async function () { + receipt = await tabulateDelegated(testProposal, accounts[8]); + assert.equal(receipt.events.Claimed.returnValues[0], VOTE_APPROVE) + assert.equal(receipt.events.Claimed.returnValues[1], accounts[7]); + assert.equal(receipt.events.Claimed.returnValues[2], accounts[8]) + }) + + it("retabulates influence to self vote", async function () { + sig = sigs[6]; + receipt = await tabulateSigned(testProposal, sig); + assert.equal(receipt.events.Voted.returnValues[0], sig.vote, "emit Voted wrong Vote") + assert.equal(receipt.events.Voted.returnValues[1], sig.signer, "emit Voted wrong address"); + assert.equal(receipt.events.Claimed.returnValues[0], sig.vote, "emit Claimed wrong Vote") + assert.equal(receipt.events.Claimed.returnValues[1], sig.signer, "emit Claimed wrong claimer"); + assert.equal(receipt.events.Claimed.returnValues[2], sig.signer, "emit Claimed wrong source"); + + }); + + it("retabulates influence to indirect delegate", async function () { + receipt = await tabulateDelegated(testProposal, accounts[8]); + assert.equal(receipt.events.Claimed.returnValues[0], VOTE_APPROVE) + assert.equal(receipt.events.Claimed.returnValues[1], accounts[6]); + assert.equal(receipt.events.Claimed.returnValues[2], accounts[8]) + }); + + it("retabulates influence to direct delegate", async function () { + receipt = await tabulateDelegated(testProposal, accounts[9]); + assert.equal(receipt.events.Claimed.returnValues[0], VOTE_APPROVE) + assert.equal(receipt.events.Claimed.returnValues[1], accounts[6]); + assert.equal(receipt.events.Claimed.returnValues[2], accounts[9]) + }); + + it("create proposal by factory", async function () { + let tabulationBlockDelay = 50; + blockStart = await web3.eth.getBlockNumber(); + let blockEndDelay = 10; + receipt = await ProposalFactory.methods.createProposal( + MiniMeToken._address, + ChildDelegation._address, + "0xDA0", + tabulationBlockDelay, + blockStart, + blockEndDelay, + QUORUM_QUALIFIED + ).send() + testProposal = new web3.eth.Contract(ProposalBase._jsonInterface, receipt.events.InstanceCreated.returnValues[0]); + }); + + it("child delegation default parent chain break", async function () { + + }); + it("child delegation indirect parent chain break", async function () { + + }); + it("child delegation parent chain full break", async function () { + + }); + + + + + +}) \ No newline at end of file diff --git a/utils/merkleTree.js b/utils/merkleTree.js new file mode 100644 index 0000000..bdc7e80 --- /dev/null +++ b/utils/merkleTree.js @@ -0,0 +1,133 @@ +const { sha3, bufferToHex } = require('ethereumjs-util'); + +class MerkleTree { + constructor(elements) { + // Filter empty strings and hash elements + this.elements = elements.filter(el => el).map(el => sha3(el)); + + // Deduplicate elements + this.elements = this.bufDedup(this.elements); + // Sort elements + this.elements.sort(Buffer.compare); + + // Create layers + this.layers = this.getLayers(this.elements); + } + + getLayers(elements) { + if (elements.length == 0) { + return [[""]]; + } + + const layers = []; + layers.push(elements); + + // Get next layer until we reach the root + while (layers[layers.length - 1].length > 1) { + layers.push(this.getNextLayer(layers[layers.length - 1])); + } + + return layers; + } + + getNextLayer(elements) { + return elements.reduce((layer, el, idx, arr) => { + if (idx % 2 === 0) { + // Hash the current element with its pair element + layer.push(this.combinedHash(el, arr[idx + 1])); + } + + return layer; + }, []); + } + + combinedHash(first, second) { + if (!first) { return second; } + if (!second) { return first; } + + return sha3(this.sortAndConcat(first, second)); + } + + getRoot() { + return this.layers[this.layers.length - 1][0]; + } + + getHexRoot() { + return bufferToHex(this.getRoot()); + } + + getProof(el) { + let idx = this.bufIndexOf(el, this.elements); + + if (idx === -1) { + throw new Error("Element does not exist in Merkle tree"); + } + + return this.layers.reduce((proof, layer) => { + const pairElement = this.getPairElement(idx, layer); + + if (pairElement) { + proof.push(pairElement); + } + + idx = Math.floor(idx / 2); + + return proof; + }, []); + } + + getHexProof(el) { + const proof = this.getProof(el); + + return this.bufArrToHex(proof); + } + + getPairElement(idx, layer) { + const pairIdx = idx % 2 === 0 ? idx + 1 : idx - 1; + + if (pairIdx < layer.length) { + return layer[pairIdx]; + } else { + return null; + } + } + + bufIndexOf(el, arr) { + let hash; + + // Convert element to 32 byte hash if it is not one already + if (el.length !== 32 || !Buffer.isBuffer(el)) { + hash = sha3(el); + } else { + hash = el; + } + + for (let i = 0; i < arr.length; i++) { + if (hash.equals(arr[i])) { + return i; + } + } + + return -1; + } + + bufDedup(elements) { + return elements.filter((el, idx) => { + return this.bufIndexOf(el, elements) === idx; + }); + } + + bufArrToHex(arr) { + if (arr.some(el => !Buffer.isBuffer(el))) { + throw new Error("Array is not an array of buffers"); + } + + return arr.map(el => '0x' + el.toString('hex')); + } + + sortAndConcat(...args) { + return Buffer.concat([...args].sort(Buffer.compare)); + } +} + +exports.MerkleTree = MerkleTree; \ No newline at end of file diff --git a/utils/testUtils.js b/utils/testUtils.js index fc6fc30..6b64773 100644 --- a/utils/testUtils.js +++ b/utils/testUtils.js @@ -43,6 +43,13 @@ exports.addressToBytes32 = (address) => { return "0x" + stringed.substring(stringed.length - 64, stringed.length); } +exports.pubKeyToAddress = (contactCode) => { + if(contactCode.length != 132 || contactCode.substring(0,4) != "0x04") { + throw "Invalid contact code: " +contactCode; + } + return web3.utils.toChecksumAddress("0x" + web3.utils.soliditySha3({t: 'bytes', v: ("0x" + contactCode.substring(4))}).substring(26)); +} + // OpenZeppelin's expectThrow helper - // Source: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/test/helpers/expectThrow.js @@ -200,6 +207,73 @@ exports.ensureException = function(error) { assert(isException(error), error.toString()); }; +const NO_ERROR = 0; +const FATAL_ERROR = 1; +const OUT_OF_GAS = 2; +const USER_ERROR = 3; + +exports.getEVMException = async function(sendFn) { + var error = null; + try { + await sendFn.estimateGas(); + } catch(e) { + var a = e.toString().split(": "); + if(a[0] != "RuntimeError" || a[1] != "VM Exception while processing transaction") { + throw e; + } + + switch (a[3]){ + case "out of gas": + case "invalid opcode": + case "invalid JUMP": + case "stack overflow": + case "revert": + error = a[3] + break; + default: + if(a[3].startsWith("revert ")){ + error = a[3].substring(7) + } else { + error = a[3]; + } + } + } +} + +exports.assertEVMException = async function(sendFn, error="") { + try { + await sendFn; + return false; + } catch (e) { + let strError = e.toString(); + if(error == ""){ + return strError.includes('invalid opcode') || strError.includes('invalid JUMP') || strError.includes('revert'); + } else { + return strError.includes(error); + } + } + +}; + + + +exports.strictEVMException = async function(sendFn, error="") { + try { + await sendFn; + return false; + } catch (e) { + let strError = e.toString(); + if(error == ""){ + return strError.includes('invalid opcode') || strError.includes('invalid JUMP') || strError.includes('revert'); + } else { + return strError.includes(error); + } + } + +}; + + + function isException(error) { let strError = error.toString(); return strError.includes('invalid opcode') || strError.includes('invalid JUMP') || strError.includes('revert'); @@ -207,33 +281,100 @@ function isException(error) { exports.increaseTime = async (amount) => { return new Promise(function(resolve, reject) { - web3.currentProvider.sendAsync( + web3.currentProvider.send( { jsonrpc: '2.0', method: 'evm_increaseTime', params: [+amount], id: new Date().getSeconds() - }, - async (error) => { - if (error) { - console.log(error); - return reject(err); - } - await web3.currentProvider.sendAsync( - { - jsonrpc: '2.0', - method: 'evm_mine', - params: [], - id: new Date().getSeconds() - }, (error) => { - if (error) { - console.log(error); - return reject(err); - } - resolve(); + }, (error) => { + if (error) { + reject(err); + } else { + web3.currentProvider.send({ + jsonrpc: '2.0', + method: 'evm_mine', + params: [], + id: new Date().getSeconds() + }, (error) => { + if (error) { + reject(err); + }else { + resolve(); + } + }) } - ) - } - ) + }) }); } + +exports.setTime = async (timestamp) => { + return new Promise(function(resolve, reject) { + web3.currentProvider.send({ + jsonrpc: '2.0', + method: 'evm_mine', + params: [+timestamp], + id: new Date().getSeconds() + }, (error,s)=>{ + if(error) { + reject(error); + } else { + resolve(); + } + }) + }) +} + +exports.increaseBlock = (amount) => { + return new Promise(function(resolve, reject) { + web3.currentProvider.send( + { + jsonrpc: '2.0', + method: 'evm_mine', + params: [], + id: new Date().getSeconds() + }, + (error,s)=>{ + if(error) { + reject(error); + } else { + if(amount == 1) { + resolve() + }else { + exports.increaseBlock(amount-1).then(() => { + resolve(); + }) + } + + } + }) + }) +} + +exports.setBlockNumber = (newBlockNumber) => { + return new Promise((resolve, reject) => { + web3.eth.getBlockNumber().then((blockNumber) => { + if(blockNumber > newBlockNumber) { + reject("Cannot go back"); + } else if (blockNumber < newBlockNumber) { + exports.increaseBlock(newBlockNumber - blockNumber).then(()=>{ + resolve(); + }) + } else { + resolve(); + } + }) + }) +} + +exports.addGas = (call, from, amount=1010) => { + return new Promise((resolve, reject) => { + call.estimateGas().then((gas) => { + call.send({ + gas: gas+amount, + from: from + }).on('error', reject).then(resolve); + }) + }) +} +