From d84040b767c0d778a1f01b2ed98343f9d2c7004b Mon Sep 17 00:00:00 2001 From: Ivo Georgiev Date: Wed, 3 Apr 2019 02:18:17 +0300 Subject: [PATCH] full eslint, fix #51 --- contracts/Identity.sol | 2 +- migrations/1_initial_migration.js | 2 +- package.json | 10 +- test/TestAdExCore.js | 132 ++++++++---- test/TestIdentity.js | 334 +++++++++++++++++++----------- test/TestRegistry.js | 22 +- test/index.js | 29 +-- truffle-config.js | 145 ++++++------- 8 files changed, 425 insertions(+), 251 deletions(-) diff --git a/contracts/Identity.sol b/contracts/Identity.sol index 7757c075..de86733a 100644 --- a/contracts/Identity.sol +++ b/contracts/Identity.sol @@ -200,7 +200,7 @@ contract Identity { internal { assembly { - let result := call(sub(gas, 10000), to, value, add(data, 0x20), mload(data), 0, 0) + let result := call(gas, to, value, add(data, 0x20), mload(data), 0, 0) switch result case 0 { let size := returndatasize diff --git a/migrations/1_initial_migration.js b/migrations/1_initial_migration.js index 04645c4f..d9c3fd5a 100644 --- a/migrations/1_initial_migration.js +++ b/migrations/1_initial_migration.js @@ -4,4 +4,4 @@ const AdExCore = artifacts.require('./AdExCore.sol') module.exports = function(deployer) { deployer.deploy(AdExCore) deployer.deploy(Migrations) -}; +} diff --git a/package.json b/package.json index 27e5ee8b..b6fac541 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,17 @@ "solc": "^0.5.7", "truffle": "^5.0.10" }, - "devDependencies": {}, + "devDependencies": { + "eslint": "^5.16.0", + "eslint-config-airbnb-base": "^13.1.0", + "eslint-config-prettier": "^4.1.0", + "eslint-plugin-import": "^2.16.0", + "eslint-plugin-prettier": "^3.0.1", + "prettier": "^1.16.4" + }, "scripts": { "test": "truffle test", + "lint": "eslint . -c .eslintrc.js --fix", "preversion": "mkdir -p temp && solc --abi contracts/AdExCore.sol -o temp && mkdir -p abi && mv temp/AdExCore.abi abi/AdExCore.json && rm -r temp/" }, "repository": { diff --git a/test/TestAdExCore.js b/test/TestAdExCore.js index 753ea03d..62556194 100644 --- a/test/TestAdExCore.js +++ b/test/TestAdExCore.js @@ -1,13 +1,16 @@ +const promisify = require('util').promisify +const { providers, Contract } = require('ethers') + const AdExCore = artifacts.require('AdExCore') const MockToken = artifacts.require('./mocks/Token') const MockLibs = artifacts.require('./mocks/Libs') const { moveTime, sampleChannel, expectEVMError } = require('./') -const promisify = require('util').promisify + const ethSign = promisify(web3.eth.sign.bind(web3)) const { ChannelState, Channel, MerkleTree, splitSig } = require('../js') -const { providers, Contract } = require('ethers') + const web3Provider = new providers.Web3Provider(web3.currentProvider) contract('AdExCore', function(accounts) { @@ -16,7 +19,7 @@ contract('AdExCore', function(accounts) { let libMock const tokens = 2000 - const userAcc = accounts[0] + const userAcc = accounts[0] before(async function() { const tokenWeb3 = await MockToken.new() @@ -34,33 +37,50 @@ contract('AdExCore', function(accounts) { it('SignatureValidator', async function() { const hash = '0x0202020202020202020202020202020202020202020202020202020202020202' const sig = splitSig(await ethSign(hash, userAcc)) - assert.isTrue(await libMock.isValidSig(hash, userAcc, sig), 'isValidSig returns true for the signer') - assert.isNotTrue(await libMock.isValidSig(hash, accounts[1], sig), 'isValidSig returns true for a non-signer') + assert.isTrue( + await libMock.isValidSig(hash, userAcc, sig), + 'isValidSig returns true for the signer' + ) + assert.isNotTrue( + await libMock.isValidSig(hash, accounts[1], sig), + 'isValidSig returns true for a non-signer' + ) }) it('channelOpen', async function() { const blockTime = (await web3.eth.getBlock('latest')).timestamp - const channelWrongCreator = sampleChannel(accounts, token.address, accounts[1], tokens, blockTime+50, 0) + const channelWrongCreator = sampleChannel( + accounts, + token.address, + accounts[1], + tokens, + blockTime + 50, + 0 + ) await expectEVMError(core.channelOpen(channelWrongCreator.toSolidityTuple()), 'INVALID_CREATOR') - const channel = sampleChannel(accounts, token.address, userAcc, tokens, blockTime+50, 0) + const channel = sampleChannel(accounts, token.address, userAcc, tokens, blockTime + 50, 0) const receipt = await (await core.channelOpen(channel.toSolidityTuple())).wait() - const ev = receipt.events.find(x => x.event === 'LogChannelOpen') + const ev = receipt.events.find(x => x.event === 'LogChannelOpen') assert.ok(ev, 'has LogChannelOpen event') assert.equal(await token.balanceOf(userAcc), 0, 'account balance is 0') assert.equal(await token.balanceOf(core.address), tokens, 'contract balance is correct') assert.equal(ev.args.channelId, channel.hashHex(core.address), 'channel hash matches') - assert.equal(await core.states(channel.hash(core.address)), ChannelState.Active, 'channel state is correct') + assert.equal( + await core.states(channel.hash(core.address)), + ChannelState.Active, + 'channel state is correct' + ) await expectEVMError(core.channelOpen(channel.toSolidityTuple()), 'INVALID_STATE') }) it('channelWithdrawExpired', async function() { const blockTime = (await web3.eth.getBlock('latest')).timestamp - const channel = sampleChannel(accounts, token.address, userAcc, tokens, blockTime+50, 1) + const channel = sampleChannel(accounts, token.address, userAcc, tokens, blockTime + 50, 1) await (await core.channelOpen(channel.toSolidityTuple())).wait() const initialBal = await token.balanceOf(userAcc) @@ -72,9 +92,20 @@ contract('AdExCore', function(accounts) { // Ensure we can do this when the time comes await moveTime(web3, 100) const receipt = await (await channelWithdrawExpired()).wait() - assert.ok(receipt.events.find(x => x.event === 'LogChannelWithdrawExpired'), 'has LogChannelWihtdrawExpired event') - assert.equal(await core.states(channel.hash(core.address)), ChannelState.Expired, 'channel state is correct') - assert.equal(await token.balanceOf(userAcc), initialBal.toNumber() + tokens, 'funds are returned') + assert.ok( + receipt.events.find(x => x.event === 'LogChannelWithdrawExpired'), + 'has LogChannelWihtdrawExpired event' + ) + assert.equal( + await core.states(channel.hash(core.address)), + ChannelState.Expired, + 'channel state is correct' + ) + assert.equal( + await token.balanceOf(userAcc), + initialBal.toNumber() + tokens, + 'funds are returned' + ) // cannot do it again await expectEVMError(channelWithdrawExpired(), 'INVALID_STATE') @@ -83,21 +114,22 @@ contract('AdExCore', function(accounts) { it('channelWithdraw', async function() { const blockTime = (await web3.eth.getBlock('latest')).timestamp const totalDeposit = tokens - const channel = sampleChannel(accounts, token.address, userAcc, totalDeposit, blockTime+50, 2) + const channel = sampleChannel(accounts, token.address, userAcc, totalDeposit, blockTime + 50, 2) const channelWithdraw = core.channelWithdraw.bind(core, channel.toSolidityTuple()) await (await core.channelOpen(channel.toSolidityTuple())).wait() // Prepare the tree and sign the state root - const userLeafAmnt = totalDeposit/2 + const userLeafAmnt = totalDeposit / 2 const [stateRoot, validSigs, proof] = await balanceTreeToWithdrawArgs( channel, { [userAcc]: userLeafAmnt }, - userAcc, userLeafAmnt + userAcc, + userLeafAmnt ) // Can't withdraw an amount that is not in the tree await expectEVMError( - channelWithdraw(stateRoot, validSigs, proof, userLeafAmnt+1), + channelWithdraw(stateRoot, validSigs, proof, userLeafAmnt + 1), 'BALANCELEAF_NOT_FOUND' ) @@ -112,27 +144,43 @@ contract('AdExCore', function(accounts) { const validWithdraw = () => channelWithdraw(stateRoot, validSigs, proof, userLeafAmnt) const tx = await validWithdraw() const receipt = await tx.wait() - assert.ok(receipt.events.find(x => x.event === 'LogChannelWithdraw'), 'has LogChannelWithdraw event') + assert.ok( + receipt.events.find(x => x.event === 'LogChannelWithdraw'), + 'has LogChannelWithdraw event' + ) assert.equal(await token.balanceOf(userAcc), userLeafAmnt, 'user has a proper token balance') const channelId = channel.hash(core.address) - assert.equal(await core.withdrawn(channelId), userLeafAmnt, 'channel has the right withdrawn value') - assert.equal(await core.withdrawnPerUser(channelId, userAcc), userLeafAmnt, 'channel has right withdrawnPerUser') + assert.equal( + await core.withdrawn(channelId), + userLeafAmnt, + 'channel has the right withdrawn value' + ) + assert.equal( + await core.withdrawnPerUser(channelId, userAcc), + userLeafAmnt, + 'channel has right withdrawnPerUser' + ) // if we try with less, it won't work const decWithdrawArgs = await balanceTreeToWithdrawArgs( channel, - { [userAcc]: userLeafAmnt-1 }, - userAcc, userLeafAmnt-1 + { [userAcc]: userLeafAmnt - 1 }, + userAcc, + userLeafAmnt - 1 ) - await expectEVMError(channelWithdraw.apply(null, decWithdrawArgs)) + await expectEVMError(channelWithdraw(...decWithdrawArgs)) // we can do it again, but it's not gonna give us more tokens const receipt2 = await (await validWithdraw()).wait() const withdrawEvent = receipt2.events.find(x => x.event === 'LogChannelWithdraw') assert.ok(withdrawEvent, 'has LogChannelWithdraw event') assert.equal(withdrawEvent.args.amount, 0, 'withdrawn amount is 0') - assert.equal(await core.withdrawn(channelId), userLeafAmnt, 'channel has the right withdrawn value') + assert.equal( + await core.withdrawn(channelId), + userLeafAmnt, + 'channel has the right withdrawn value' + ) // add more balances and withdraw; make sure that only the difference (to the last withdrawal) is withdrawn // also, test a tree that has more elements @@ -142,39 +190,50 @@ contract('AdExCore', function(accounts) { { [userAcc]: incUserLeafAmnt, [accounts[1]]: 10, - [accounts[2]]: 10, + [accounts[2]]: 10 }, - userAcc, incUserLeafAmnt + userAcc, + incUserLeafAmnt ) - const receipt3 = await (await channelWithdraw.apply(null, incWithdrawArgs)).wait() + const receipt3 = await (await channelWithdraw(...incWithdrawArgs)).wait() const incWithdrawEvent = receipt3.events.find(x => x.event === 'LogChannelWithdraw') assert.ok(incWithdrawEvent, 'has LogChannelWithdraw event') assert.equal(incWithdrawEvent.args.amount, 10, 'withdrawn amount is 10') - assert.equal(await core.withdrawn(channelId), incUserLeafAmnt, 'channel has the right withdrawn value') + assert.equal( + await core.withdrawn(channelId), + incUserLeafAmnt, + 'channel has the right withdrawn value' + ) assert.equal(await token.balanceOf(userAcc), incUserLeafAmnt, 'user has the right token amount') await moveTime(web3, 100) await expectEVMError(validWithdraw(), 'EXPIRED') // Now we withdrawExpired, and we can only get the rest - const expiredReceipt = await (await core.channelWithdrawExpired(channel.toSolidityTuple())).wait() + const expiredReceipt = await (await core.channelWithdrawExpired( + channel.toSolidityTuple() + )).wait() const expiredEv = expiredReceipt.events.find(x => x.event === 'LogChannelWithdrawExpired') - assert.equal(expiredEv.args.amount.toNumber() + incUserLeafAmnt, totalDeposit, 'withdrawExpired returned the rest of the funds') + assert.equal( + expiredEv.args.amount.toNumber() + incUserLeafAmnt, + totalDeposit, + 'withdrawExpired returned the rest of the funds' + ) assert.equal(await token.balanceOf(userAcc), totalDeposit, 'totalDeposit is returned') }) it('channelWithdraw: cannot withdraw more than the channel', async function() { const blockTime = (await web3.eth.getBlock('latest')).timestamp const totalDeposit = tokens - const channel = sampleChannel(accounts, token.address, userAcc, totalDeposit, blockTime+50, 3) - const channelWithdraw = core.channelWithdraw.bind(core, channel.toSolidityTuple()) + const channel = sampleChannel(accounts, token.address, userAcc, totalDeposit, blockTime + 50, 3) await (await core.channelOpen(channel.toSolidityTuple())).wait() const leafAmnt = totalDeposit + 1 const [stateRoot, validSigs, proof] = await balanceTreeToWithdrawArgs( channel, { [userAcc]: leafAmnt }, - userAcc, leafAmnt + userAcc, + leafAmnt ) await expectEVMError( core.channelWithdraw(channel.toSolidityTuple(), stateRoot, validSigs, proof, leafAmnt), @@ -182,13 +241,13 @@ contract('AdExCore', function(accounts) { ) }) - // Bench: creating these: (elem1, elem2, elem3, tree, proof, stateRoot, hashToSignHex, sig1), 1000 times, takes ~6000ms // Bench: creating these: (elem1, elem2, elem3, tree, proof, stateRoot, hashtoSignHex), 1000 times, takes ~300ms // Bench: creating these: (tree, proof, stateRoot, hashtoSignHex), 1000 times, takes ~300ms async function balanceTreeToWithdrawArgs(channel, balances, acc, amnt) { - const elements = Object.entries(balances) - .map(([ acc, amnt ]) => Channel.getBalanceLeaf(acc, amnt)) + const elements = Object.entries(balances).map(([leafAcc, leafAmnt]) => + Channel.getBalanceLeaf(leafAcc, leafAmnt) + ) const tree = new MerkleTree(elements) const elemToWithdraw = Channel.getBalanceLeaf(acc, amnt) const proof = tree.proof(elemToWithdraw) @@ -199,4 +258,3 @@ contract('AdExCore', function(accounts) { return [stateRoot, [sig1, sig2], proof, amnt] } }) - diff --git a/test/TestIdentity.js b/test/TestIdentity.js index 1f5ff2a7..b4193e5d 100644 --- a/test/TestIdentity.js +++ b/test/TestIdentity.js @@ -1,3 +1,8 @@ +const promisify = require('util').promisify +const { providers, Contract } = require('ethers') +const { Interface, randomBytes, getAddress } = require('ethers').utils +const { generateAddress2 } = require('ethereumjs-util') + const AdExCore = artifacts.require('AdExCore') const Identity = artifacts.require('Identity') const IdentityFactory = artifacts.require('IdentityFactory') @@ -8,15 +13,18 @@ const { moveTime, sampleChannel, expectEVMError } = require('./') const { Transaction, RoutineAuthorization, Channel, splitSig, MerkleTree } = require('../js') const { getProxyDeployTx, getStorageSlotsFromArtifact } = require('../js/IdentityProxyDeploy') -const promisify = require('util').promisify const ethSign = promisify(web3.eth.sign.bind(web3)) -const { providers, Contract, ContractFactory } = require('ethers') -const { Interface, randomBytes, getAddress } = require('ethers').utils const web3Provider = new providers.Web3Provider(web3.currentProvider) const DAY_SECONDS = 24 * 60 * 60 +// WARNING +// READ THIS! +// gasLimit must be hardcoded cause ganache cannot estimate it properly +// that's cause of the call() that we do here; see https://github.com/AdExNetwork/adex-protocol-eth/issues/55 +const gasLimit = 300000 + const coreInterface = new Interface(AdExCore._json.abi) contract('Identity', function(accounts) { @@ -62,23 +70,22 @@ contract('Identity', function(accounts) { // Generating a proxy deploy transaction const deployTx = getProxyDeployTx( id.address, - token.address, relayerAddr, feeAmnt, + token.address, + relayerAddr, + feeAmnt, registryAddr, [[userAcc, 3]], // Using this option is fine if the token.address is a token that reverts on failures - { unsafeERC20: true, ...getStorageSlotsFromArtifact(Identity) }, - //{ safeERC20Artifact: artifacts.require('SafeERC20'), ...getStorageSlotsFromArtifact(Identity) }, + { unsafeERC20: true, ...getStorageSlotsFromArtifact(Identity) } + // { safeERC20Artifact: artifacts.require('SafeERC20'), ...getStorageSlotsFromArtifact(Identity) }, ) - const salt = '0x'+Buffer.from(randomBytes(32)).toString('hex') - const { generateAddress2 } = require('ethereumjs-util') - const expectedAddr = getAddress('0x'+generateAddress2(identityFactory.address, salt, deployTx.data).toString('hex')) - - const deploy = identityFactory.deploy.bind( - identityFactory, - deployTx.data, - salt, + const salt = `0x${Buffer.from(randomBytes(32)).toString('hex')}` + const expectedAddr = getAddress( + `0x${generateAddress2(identityFactory.address, salt, deployTx.data).toString('hex')}` ) + + const deploy = identityFactory.deploy.bind(identityFactory, deployTx.data, salt, { gasLimit }) // Without any tokens to pay for the fee, we should revert await expectEVMError(deploy(), 'FAILED_DEPLOYING') @@ -92,13 +99,17 @@ contract('Identity', function(accounts) { const deployEv = deployReceipt.events.find(x => x.event === 'LogDeployed') assert.ok(deployEv, 'has deployedEv') assert.equal(expectedAddr, deployEv.args.addr, 'counterfactual contract address matches') - + // privilege level is OK - const newIdentity = new Contract(expectedAddr, Identity._json.abi, web3Provider.getSigner(relayerAddr)) + const newIdentity = new Contract( + expectedAddr, + Identity._json.abi, + web3Provider.getSigner(relayerAddr) + ) assert.equal(await newIdentity.privileges(userAcc), 3, 'privilege level is OK') - //console.log('deploy cost', deployReceipt.gasUsed.toString(10)) - //id = newIdentity + // console.log('deploy cost', deployReceipt.gasUsed.toString(10)) + // id = newIdentity // check if deploy fee is paid out assert.equal(await token.balanceOf(relayerAddr), feeAmnt, 'fee is paid out') }) @@ -108,25 +119,33 @@ contract('Identity', function(accounts) { // Generating a proxy deploy transaction const deployTx = getProxyDeployTx( id.address, - token.address, relayerAddr, 0, + token.address, + relayerAddr, + 0, registryAddr, [[userAcc, 3]], - { unsafeERC20: true, ...getStorageSlotsFromArtifact(Identity) }, + { unsafeERC20: true, ...getStorageSlotsFromArtifact(Identity) } ) - const salt = '0x'+Buffer.from(randomBytes(32)).toString('hex') - + const salt = `0x${Buffer.from(randomBytes(32)).toString('hex')}` const deployAndFund = identityFactory.deployAndFund.bind( identityFactory, - deployTx.data, salt, - token.address, fundAmnt, + deployTx.data, + salt, + token.address, + fundAmnt, + { gasLimit } ) // Only relayer can call const userSigner = web3Provider.getSigner(userAcc) - const identityFactoryUser = new Contract(identityFactory.address, IdentityFactory._json.abi, userSigner) + const identityFactoryUser = new Contract( + identityFactory.address, + IdentityFactory._json.abi, + userSigner + ) await expectEVMError( - identityFactoryUser.deployAndFund(deployTx.data, salt, token.address, fundAmnt), + identityFactoryUser.deployAndFund(deployTx.data, salt, token.address, fundAmnt, { gasLimit }), 'ONLY_RELAYER' ) @@ -140,7 +159,11 @@ contract('Identity', function(accounts) { const receipt = await (await deployAndFund()).wait() const deployedEv = receipt.events.find(x => x.event === 'LogDeployed') assert.ok(deployedEv, 'has deployedEv') - assert.equal(await token.balanceOf(deployedEv.args.addr), fundAmnt, 'deployed contract has received the funding amount') + assert.equal( + await token.balanceOf(deployedEv.args.addr), + fundAmnt, + 'deployed contract has received the funding amount' + ) }) it('relay a tx', async function() { @@ -156,7 +179,7 @@ contract('Identity', function(accounts) { feeTokenAddr: token.address, feeTokenAmount: 25, to: id.address, - data: idInterface.functions.setAddrPrivilege.encode([userAcc, 4]), + data: idInterface.functions.setAddrPrivilege.encode([userAcc, 4]) }) const hash = relayerTx.hashHex() @@ -170,13 +193,22 @@ contract('Identity', function(accounts) { // Do the execute() correctly, verify if it worked const sig = splitSig(await ethSign(hash, userAcc)) - const receipt = await (await id.execute([relayerTx.toSolidityTuple()], [sig])).wait() + const receipt = await (await id.execute([relayerTx.toSolidityTuple()], [sig], { + gasLimit + })).wait() assert.equal(await id.privileges(userAcc), 4, 'privilege level changed') - assert.equal(await token.balanceOf(relayerAddr), initialBal.toNumber() + relayerTx.feeTokenAmount.toNumber(), 'relayer has received the tx fee') - assert.ok(receipt.events.find(x => x.event == 'LogPrivilegeChanged'), 'LogPrivilegeChanged event found') - assert.equal((await id.nonce()).toNumber(), initialNonce+1, 'nonce has increased with 1') - //console.log('relay cost', receipt.gasUsed.toString(10)) + assert.equal( + await token.balanceOf(relayerAddr), + initialBal.toNumber() + relayerTx.feeTokenAmount.toNumber(), + 'relayer has received the tx fee' + ) + assert.ok( + receipt.events.find(x => x.event === 'LogPrivilegeChanged'), + 'LogPrivilegeChanged event found' + ) + assert.equal((await id.nonce()).toNumber(), initialNonce + 1, 'nonce has increased with 1') + // console.log('relay cost', receipt.gasUsed.toString(10)) // setAddrPrivilege can only be invoked by the contract await expectEVMError(id.setAddrPrivilege(userAcc, 0), 'ONLY_IDENTITY_CAN_CALL') @@ -191,11 +223,14 @@ contract('Identity', function(accounts) { feeTokenAddr: token.address, feeTokenAmount: 5, to: id.address, - data: idInterface.functions.setAddrPrivilege.encode([userAcc, 1]), + data: idInterface.functions.setAddrPrivilege.encode([userAcc, 1]) }) const newHash = relayerNextTx.hashHex() const newSig = splitSig(await ethSign(newHash, userAcc)) - await expectEVMError(id.execute([relayerNextTx.toSolidityTuple()], [newSig]), 'PRIVILEGE_NOT_DOWNGRADED') + await expectEVMError( + id.execute([relayerNextTx.toSolidityTuple()], [newSig]), + 'PRIVILEGE_NOT_DOWNGRADED' + ) // Try to run a TX from an acc with insufficient privilege const relayerTxEvil = new Transaction({ @@ -204,11 +239,14 @@ contract('Identity', function(accounts) { feeTokenAddr: token.address, feeTokenAmount: 25, to: id.address, - data: idInterface.functions.setAddrPrivilege.encode([evilAcc, 4]), + data: idInterface.functions.setAddrPrivilege.encode([evilAcc, 4]) }) const hashEvil = relayerTxEvil.hashHex() const sigEvil = splitSig(await ethSign(hashEvil, evilAcc)) - await expectEVMError(id.execute([relayerTxEvil.toSolidityTuple()], [sigEvil]), 'INSUFFICIENT_PRIVILEGE_TRANSACTION') + await expectEVMError( + id.execute([relayerTxEvil.toSolidityTuple()], [sigEvil]), + 'INSUFFICIENT_PRIVILEGE_TRANSACTION' + ) }) // Relay two transactions @@ -216,39 +254,51 @@ contract('Identity', function(accounts) { it('relay multiple transactions', async function() { const getTuples = txns => txns.map(tx => new Transaction(tx).toSolidityTuple()) const getSigs = function(txns) { - return Promise.all(txns.map(args => { - const tx = new Transaction(args) - const hash = tx.hashHex() - return ethSign(hash, userAcc).then(sig => splitSig(sig)) - })) + return Promise.all( + txns.map(args => { + const tx = new Transaction(args) + const hash = tx.hashHex() + return ethSign(hash, userAcc).then(sig => splitSig(sig)) + }) + ) } const initialBal = await token.balanceOf(relayerAddr) const initialNonce = (await id.nonce()).toNumber() - const txns = [100, 200].map((n, i) => - ({ - identityContract: id.address, - nonce: initialNonce + i, - feeTokenAddr: token.address, - feeTokenAmount: 5, - to: id.address, - data: idInterface.functions.setAddrPrivilege.encode([userAcc, 4]), - }) - ) - const totalFee = txns.map(x => x.feeTokenAmount).reduce((a, b) => a+b, 0) + const txns = [100, 200].map((n, i) => ({ + identityContract: id.address, + nonce: initialNonce + i, + feeTokenAddr: token.address, + feeTokenAmount: 5, + to: id.address, + data: idInterface.functions.setAddrPrivilege.encode([userAcc, 4]) + })) + const totalFee = txns.map(x => x.feeTokenAmount).reduce((a, b) => a + b, 0) // Cannot use an invalid identityContract const invalidTxns1 = [txns[0], { ...txns[1], identityContract: token.address }] - await expectEVMError(id.execute(getTuples(invalidTxns1), await getSigs(invalidTxns1)), 'TRANSACTION_NOT_FOR_CONTRACT') + await expectEVMError( + id.execute(getTuples(invalidTxns1), await getSigs(invalidTxns1)), + 'TRANSACTION_NOT_FOR_CONTRACT' + ) // Cannot use a different fee token const invalidTxns2 = [txns[0], { ...txns[1], feeTokenAddr: accounts[8] }] - await expectEVMError(id.execute(getTuples(invalidTxns2), await getSigs(invalidTxns2)), 'EXECUTE_NEEDS_SINGLE_TOKEN') + await expectEVMError( + id.execute(getTuples(invalidTxns2), await getSigs(invalidTxns2)), + 'EXECUTE_NEEDS_SINGLE_TOKEN' + ) - const receipt = await (await id.execute(getTuples(txns), await getSigs(txns))).wait() + const receipt = await (await id.execute(getTuples(txns), await getSigs(txns), { + gasLimit + })).wait() // 2 times LogPrivilegeChanged, 1 transfer (fee) assert.equal(receipt.events.length, 3, 'has the right events length') - assert.equal(receipt.events.filter(x => x.event === 'LogPrivilegeChanged').length, 2, 'LogPrivilegeChanged happened twice') + assert.equal( + receipt.events.filter(x => x.event === 'LogPrivilegeChanged').length, + 2, + 'LogPrivilegeChanged happened twice' + ) assert.equal( await token.balanceOf(relayerAddr), initialBal.toNumber() + totalFee, @@ -264,15 +314,24 @@ contract('Identity', function(accounts) { feeTokenAddr: token.address, feeTokenAmount: 0, to: id.address, - data: idInterface.functions.setAddrPrivilege.encode([userAcc, 4]), + data: idInterface.functions.setAddrPrivilege.encode([userAcc, 4]) }) - await expectEVMError(id.executeBySender([relayerTx.toSolidityTuple()]), 'INSUFFICIENT_PRIVILEGE_SENDER') + await expectEVMError( + id.executeBySender([relayerTx.toSolidityTuple()]), + 'INSUFFICIENT_PRIVILEGE_SENDER' + ) - const idWithSender = new Contract(id.address, Identity._json.abi, web3Provider.getSigner(userAcc)) - const receipt = await (await idWithSender.executeBySender([relayerTx.toSolidityTuple()])).wait() + const idWithSender = new Contract( + id.address, + Identity._json.abi, + web3Provider.getSigner(userAcc) + ) + const receipt = await (await idWithSender.executeBySender([relayerTx.toSolidityTuple()], { + gasLimit + })).wait() assert.equal(receipt.events.length, 1, 'right number of events emitted') - assert.equal((await id.nonce()).toNumber(), initialNonce+1, 'nonce has increased with 1') + assert.equal((await id.nonce()).toNumber(), initialNonce + 1, 'nonce has increased with 1') }) it('relay routine operations', async function() { @@ -286,38 +345,51 @@ contract('Identity', function(accounts) { outpace: coreAddr, validUntil: blockTime + DAY_SECONDS, feeTokenAddr: token.address, - feeTokenAmount: fee, + feeTokenAmount: fee }) const hash = auth.hashHex() const sig = splitSig(await ethSign(hash, userAcc)) - const op = [ - 2, - RoutineAuthorization.encodeWithdraw(token.address, userAcc, toWithdraw), - ] + const op = [2, RoutineAuthorization.encodeWithdraw(token.address, userAcc, toWithdraw)] const initialUserBal = await token.balanceOf(userAcc) const initialRelayerBal = await token.balanceOf(relayerAddr) - const execRoutines = id.executeRoutines.bind( - id, - auth.toSolidityTuple(), - sig, - [op], - ) + const execRoutines = id.executeRoutines.bind(id, auth.toSolidityTuple(), sig, [op], { + gasLimit + }) const receipt = await (await execRoutines()).wait() - //console.log(receipt.gasUsed.toString(10)) + // console.log(receipt.gasUsed.toString(10)) // Transfer (withdraw), Transfer (fee) assert.equal(receipt.events.length, 2, 'has right number of events') - assert.equal(await token.balanceOf(userAcc), initialUserBal.toNumber() + toWithdraw, 'user has the right balance after withdrawal') - assert.equal(await token.balanceOf(relayerAddr), initialRelayerBal.toNumber() + fee, 'relayer has received the fee') + assert.equal( + await token.balanceOf(userAcc), + initialUserBal.toNumber() + toWithdraw, + 'user has the right balance after withdrawal' + ) + assert.equal( + await token.balanceOf(relayerAddr), + initialRelayerBal.toNumber() + fee, + 'relayer has received the fee' + ) // Do it again to make sure the fee is not paid out twice await (await execRoutines()).wait() - assert.equal(await token.balanceOf(userAcc), initialUserBal.toNumber() + toWithdraw*2, 'user has the right balance after second withdrawal') - assert.equal(await token.balanceOf(relayerAddr), initialRelayerBal.toNumber() + fee, 'relayer has received the fee only once') + assert.equal( + await token.balanceOf(userAcc), + initialUserBal.toNumber() + toWithdraw * 2, + 'user has the right balance after second withdrawal' + ) + assert.equal( + await token.balanceOf(relayerAddr), + initialRelayerBal.toNumber() + fee, + 'relayer has received the fee only once' + ) // Does not work with an invalid sig const invalidSig = splitSig(await ethSign(hash, evilAcc)) - await expectEVMError(id.executeRoutines(auth.toSolidityTuple(), invalidSig, [op]), 'INSUFFICIENT_PRIVILEGE') + await expectEVMError( + id.executeRoutines(auth.toSolidityTuple(), invalidSig, [op]), + 'INSUFFICIENT_PRIVILEGE' + ) // Does not allow withdrawals to an unauthorized addr const evilOp = [2, RoutineAuthorization.encodeWithdraw(token.address, evilAcc, toWithdraw)] @@ -329,13 +401,10 @@ contract('Identity', function(accounts) { // We can't tamper with authentication params (outpace in this case) const evilTuple = auth.toSolidityTuple() evilTuple[2] = token.address // set any other address - await expectEVMError( - id.executeRoutines(evilTuple, sig, [op]), - 'INSUFFICIENT_PRIVILEGE' - ) + await expectEVMError(id.executeRoutines(evilTuple, sig, [op]), 'INSUFFICIENT_PRIVILEGE') // We can no longer call after the authorization has expired - await moveTime(web3, DAY_SECONDS+10) + await moveTime(web3, DAY_SECONDS + 10) await expectEVMError( id.executeRoutines(auth.toSolidityTuple(), sig, [op]), 'AUTHORIZATION_EXPIRED' @@ -348,24 +417,33 @@ contract('Identity', function(accounts) { // WARNING: for some reason the latest block timestamp here is not updated after the last test... // so we need to workaround with + DAY_SECONDS const blockTime = (await web3.eth.getBlock('latest')).timestamp + DAY_SECONDS - const channel = sampleChannel(accounts, token.address, id.address, tokenAmnt, blockTime+DAY_SECONDS, 0) + const channel = sampleChannel( + accounts, + token.address, + id.address, + tokenAmnt, + blockTime + DAY_SECONDS, + 0 + ) const relayerTx = new Transaction({ identityContract: id.address, nonce: (await id.nonce()).toNumber(), feeTokenAddr: token.address, feeTokenAmount: 0, to: coreAddr, - data: coreInterface.functions.channelOpen.encode([channel.toSolidityTuple()]), + data: coreInterface.functions.channelOpen.encode([channel.toSolidityTuple()]) }) const hash = relayerTx.hashHex() const sig = splitSig(await ethSign(hash, userAcc)) - const receipt = await (await id.execute([relayerTx.toSolidityTuple()], [sig])).wait() + await (await id.execute([relayerTx.toSolidityTuple()], [sig], { + gasLimit + })).wait() // getting this far, we should have a channel open; now let's withdraw from it - //console.log(receipt.gasUsed.toString(10)) + // console.log(receipt.gasUsed.toString(10)) // Prepare all the data needed for withdrawal const elem1 = Channel.getBalanceLeaf(id.address, tokenAmnt) - const tree = new MerkleTree([ elem1 ]) + const tree = new MerkleTree([elem1]) const proof = tree.proof(elem1) const stateRoot = tree.getRoot() const hashToSignHex = channel.hashToSignHex(coreAddr, stateRoot) @@ -378,7 +456,7 @@ contract('Identity', function(accounts) { outpace: coreAddr, validUntil: blockTime + DAY_SECONDS, feeTokenAddr: token.address, - feeTokenAmount: 0, + feeTokenAmount: 0 }) const balBefore = (await token.balanceOf(userAcc)).toNumber() const authSig = splitSig(await ethSign(auth.hashHex(), userAcc)) @@ -386,10 +464,16 @@ contract('Identity', function(accounts) { auth.toSolidityTuple(), authSig, [ - getChannelWithdrawOp([channel.toSolidityTuple(), stateRoot, [vsig1, vsig2], proof, tokenAmnt]), - // @TODO: op1, withdraw expired - [ 2, RoutineAuthorization.encodeWithdraw(token.address, userAcc, tokenAmnt) ], + getChannelWithdrawOp([ + channel.toSolidityTuple(), + stateRoot, + [vsig1, vsig2], + proof, + tokenAmnt + ]), + [2, RoutineAuthorization.encodeWithdraw(token.address, userAcc, tokenAmnt)] ], + { gasLimit } )).wait() const balAfter = (await token.balanceOf(userAcc)).toNumber() assert.equal(balAfter - balBefore, tokenAmnt, 'token amount withdrawn is right') @@ -397,13 +481,17 @@ contract('Identity', function(accounts) { assert.equal(routineReceipt.events.length, 3, 'right number of events') // wrongWithdrawArgs: flipped the signatures - const wrongWithdrawArgs = [channel.toSolidityTuple(), stateRoot, [vsig2, vsig1], proof, tokenAmnt] + const wrongWithdrawArgs = [ + channel.toSolidityTuple(), + stateRoot, + [vsig2, vsig1], + proof, + tokenAmnt + ] await expectEVMError( - id.executeRoutines( - auth.toSolidityTuple(), - authSig, - [getChannelWithdrawOp(wrongWithdrawArgs)], - ), + id.executeRoutines(auth.toSolidityTuple(), authSig, [ + getChannelWithdrawOp(wrongWithdrawArgs) + ]), 'NOT_SIGNED_BY_VALIDATORS' ) }) @@ -417,41 +505,50 @@ contract('Identity', function(accounts) { identityContract: id.address, relayer: relayerAddr, outpace: coreAddr, - validUntil: blockTime + DAY_SECONDS*4, + validUntil: blockTime + DAY_SECONDS * 4, feeTokenAddr: token.address, - feeTokenAmount: 0, + feeTokenAmount: 0 }) const authSig = splitSig(await ethSign(auth.hashHex(), userAcc)) const executeRoutines = id.executeRoutines.bind(id, auth.toSolidityTuple(), authSig) // a channel with non-whitelisted validators - const channelEvil = sampleChannel([allowedValidator1, accounts[2]], token.address, id.address, tokenAmnt, blockTime+DAY_SECONDS, 0) + const channelEvil = sampleChannel( + [allowedValidator1, accounts[2]], + token.address, + id.address, + tokenAmnt, + blockTime + DAY_SECONDS, + 0 + ) await expectEVMError( - executeRoutines([ - getChannelOpenOp([channelEvil.toSolidityTuple()]), - ]), + executeRoutines([getChannelOpenOp([channelEvil.toSolidityTuple()])]), 'VALIDATOR_NOT_WHITELISTED' ) // we can open a channel with the whitelisted validators - const channel = sampleChannel([allowedValidator1, allowedValidator2], token.address, id.address, tokenAmnt, blockTime+DAY_SECONDS, 0) - const receipt = await (await executeRoutines( - [getChannelOpenOp([channel.toSolidityTuple()])], - )).wait() + const channel = sampleChannel( + [allowedValidator1, allowedValidator2], + token.address, + id.address, + tokenAmnt, + blockTime + DAY_SECONDS, + 0 + ) + const receipt = await (await executeRoutines([getChannelOpenOp([channel.toSolidityTuple()])], { + gasLimit + })).wait() // events should be: transfer, channelOpen assert.ok(receipt.events.length, 2, 'Transfer, ChannelOpen events emitted') // withdrawExpired should work const withdrawExpiredOp = getChannelWithdrawExpiredOp([channel.toSolidityTuple()]) // ensure we report the underlying OUTPACE error properly - await expectEVMError( - executeRoutines([withdrawExpiredOp]), - 'NOT_EXPIRED' - ) + await expectEVMError(executeRoutines([withdrawExpiredOp]), 'NOT_EXPIRED') // move time, withdrawExpired successfully and check results - await moveTime(web3, DAY_SECONDS*3) - const expiredReceipt = await (await executeRoutines([withdrawExpiredOp])).wait() + await moveTime(web3, DAY_SECONDS * 3) + const expiredReceipt = await (await executeRoutines([withdrawExpiredOp], { gasLimit })).wait() // LogWithdrawExpired and Transfer assert.equal(expiredReceipt.events.length, 2, 'right event count') assert.equal(await token.balanceOf(id.address), tokenAmnt, 'full deposit refunded') @@ -460,15 +557,14 @@ contract('Identity', function(accounts) { // @TODO is there a more elegant way to remove the SELECTOR than .slice(10)? function getChannelWithdrawOp(args) { - const data = '0x'+coreInterface.functions.channelWithdraw.encode(args).slice(10) + const data = `0x${coreInterface.functions.channelWithdraw.encode(args).slice(10)}` return [0, data] } function getChannelWithdrawExpiredOp(args) { - const data = '0x'+coreInterface.functions.channelWithdrawExpired.encode(args).slice(10) + const data = `0x${coreInterface.functions.channelWithdrawExpired.encode(args).slice(10)}` return [1, data] } function getChannelOpenOp(args) { - const data = '0x'+coreInterface.functions.channelOpen.encode(args).slice(10) + const data = `0x${coreInterface.functions.channelOpen.encode(args).slice(10)}` return [3, data] } - diff --git a/test/TestRegistry.js b/test/TestRegistry.js index f2a08460..9d653792 100644 --- a/test/TestRegistry.js +++ b/test/TestRegistry.js @@ -1,7 +1,9 @@ +const { providers, Contract } = require('ethers') + const Registry = artifacts.require('Registry') const { expectEVMError } = require('./') -const { providers, Contract } = require('ethers') + const web3Provider = new providers.Web3Provider(web3.currentProvider) contract('Registry', function(accounts) { @@ -11,15 +13,23 @@ contract('Registry', function(accounts) { before(async function() { const registryWeb3 = await Registry.new() - registry = new Contract(registryWeb3.address, Registry._json.abi, web3Provider.getSigner(ownerAddr)) - registryUser = new Contract(registry.address, Registry._json.abi, web3Provider.getSigner(accounts[2])) + registry = new Contract( + registryWeb3.address, + Registry._json.abi, + web3Provider.getSigner(ownerAddr) + ) + registryUser = new Contract( + registry.address, + Registry._json.abi, + web3Provider.getSigner(accounts[2]) + ) }) it('whitelist', async function() { const validator = accounts[1] // Another user cannot invoke - await expectEVMError(registryUser.setWhitelisted(validator, true), 'ONLY_OWNER'); + await expectEVMError(registryUser.setWhitelisted(validator, true), 'ONLY_OWNER') // shold be false to start with assert.equal(await registry.whitelisted(validator), false) @@ -30,10 +40,8 @@ contract('Registry', function(accounts) { await (await registry.setWhitelisted(validator, false)).wait() assert.equal(await registry.whitelisted(validator), false) }) - + it('changing ownership', async function() { - // Another user cannot invoke - const registryUser = new Contract(registry.address, Registry._json.abi, web3Provider.getSigner(accounts[2])) await expectEVMError(registryUser.changeOwner(accounts[2]), 'ONLY_OWNER') await (await registry.changeOwner(accounts[2])).wait() diff --git a/test/index.js b/test/index.js index 1ae3b1ef..de7c8c1b 100644 --- a/test/index.js +++ b/test/index.js @@ -2,18 +2,18 @@ const { Channel } = require('../js') async function expectEVMError(promise, errString) { try { - await promise; - assert.isOk(false, 'should have failed with '+errString) - } catch(e) { - const expectedString = errString ? - 'VM Exception while processing transaction: revert '+errString + await promise + assert.isOk(false, `should have failed with ${errString}`) + } catch (e) { + const expectedString = errString + ? `VM Exception while processing transaction: revert ${errString}` : 'VM Exception while processing transaction: revert' assert.equal(e.message, expectedString, 'error message is incorrect') } } function sampleChannel(accounts, tokenAddr, creator, amount, validUntil, nonce) { - const spec = new Buffer(32) + const spec = Buffer.alloc(32) spec.writeUInt32BE(nonce) return new Channel({ creator, @@ -21,17 +21,20 @@ function sampleChannel(accounts, tokenAddr, creator, amount, validUntil, nonce) tokenAmount: amount, validUntil, validators: [accounts[0], accounts[1]], - spec, + spec }) } function moveTime(web3, time) { return new Promise(function(resolve, reject) { - web3.currentProvider.send({ - jsonrpc: '2.0', - method: 'evm_increaseTime', - params: [time], - id: 0, - }, (err, res) => err ? reject(err) : resolve(res)) + web3.currentProvider.send( + { + jsonrpc: '2.0', + method: 'evm_increaseTime', + params: [time], + id: 0 + }, + (err, res) => (err ? reject(err) : resolve(res)) + ) }) } module.exports = { expectEVMError, sampleChannel, moveTime } diff --git a/truffle-config.js b/truffle-config.js index 8f5c80b1..97026772 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -29,82 +29,83 @@ // const mnemonic = fs.readFileSync(".secret").toString().trim(); module.exports = { - /** - * Networks define how you connect to your ethereum client and let you set the - * defaults web3 uses to send transactions. If you don't specify one truffle - * will spin up a development blockchain for you on port 9545 when you - * run `develop` or `test`. You can ask a truffle command to use a specific - * network from the command line, e.g - * - * $ truffle test --network - */ + /** + * Networks define how you connect to your ethereum client and let you set the + * defaults web3 uses to send transactions. If you don't specify one truffle + * will spin up a development blockchain for you on port 9545 when you + * run `develop` or `test`. You can ask a truffle command to use a specific + * network from the command line, e.g + * + * $ truffle test --network + */ - networks: { - // Useful for testing. The `development` name is special - truffle uses it by default - // if it's defined here and no other network is specified at the command line. - // You should run a client (like ganache-cli, geth or parity) in a separate terminal - // tab if you use this network and you must also set the `host`, `port` and `network_id` - // options below to some value. - // - //development: { - // host: 'localhost', - // port: 8545, - // network_id: '*', // Match any network id - // gas: 4500000, - // gasPrice: 10000000000, - //}, - // development: { - // host: "127.0.0.1", // Localhost (default: none) - // port: 8545, // Standard Ethereum port (default: none) - // network_id: "*", // Any network (default: none) - // }, + networks: { + // Useful for testing. The `development` name is special - truffle uses it by default + // if it's defined here and no other network is specified at the command line. + // You should run a client (like ganache-cli, geth or parity) in a separate terminal + // tab if you use this network and you must also set the `host`, `port` and `network_id` + // options below to some value. + // + // development: { + // host: 'localhost', + // port: 8545, + // network_id: '*', // Match any network id + // gas: 4500000, + // gasPrice: 10000000000, + // }, + // development: { + // host: "127.0.0.1", // Localhost (default: none) + // port: 8545, // Standard Ethereum port (default: none) + // network_id: "*", // Any network (default: none) + // }, - // Another network with more advanced options... - advanced: { - // port: 8777, // Custom port - // network_id: 1342, // Custom network - // gas: 8500000, // Gas sent with each transaction (default: ~6700000) - // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) - // from:
, // Account to send txs from (default: accounts[0]) - // websockets: true // Enable EventEmitter interface for web3 (default: false) - }, + // Another network with more advanced options... + advanced: { + // port: 8777, // Custom port + // network_id: 1342, // Custom network + // gas: 8500000, // Gas sent with each transaction (default: ~6700000) + // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) + // from:
, // Account to send txs from (default: accounts[0]) + // websockets: true // Enable EventEmitter interface for web3 (default: false) + }, - // Useful for deploying to a public network. - // NB: It's important to wrap the provider as a function. - ropsten: { - // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/${infuraKey}`), - // network_id: 3, // Ropsten's id - // gas: 5500000, // Ropsten has a lower block limit than mainnet - // confirmations: 2, // # of confs to wait between deployments. (default: 0) - // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) - // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) - }, + // Useful for deploying to a public network. + // NB: It's important to wrap the provider as a function. + ropsten: { + // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/${infuraKey}`), + // network_id: 3, // Ropsten's id + // gas: 5500000, // Ropsten has a lower block limit than mainnet + // confirmations: 2, // # of confs to wait between deployments. (default: 0) + // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) + // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) + }, - // Useful for private networks - private: { - // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), - // network_id: 2111, // This network is yours, in the cloud. - // production: true // Treats this network as if it was a public net. (default: false) - } - }, + // Useful for private networks + private: { + // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), + // network_id: 2111, // This network is yours, in the cloud. + // production: true // Treats this network as if it was a public net. (default: false) + } + }, - // Set default mocha options here, use special reporters etc. - mocha: { - // timeout: 100000 - }, + // Set default mocha options here, use special reporters etc. + mocha: { + // timeout: 100000 + }, - // Configure your compilers - compilers: { - solc: { - version: "0.5.6", // Fetch exact version from solc-bin (default: truffle's version) - // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) - settings: { // See the solidity docs for advice about optimization and evmVersion - // optimizer: { - // enabled: false, - // runs: 200 - // }, - evmVersion: "constantinople" - } - } - } + // Configure your compilers + compilers: { + solc: { + version: '0.5.6', // Fetch exact version from solc-bin (default: truffle's version) + // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) + settings: { + // See the solidity docs for advice about optimization and evmVersion + // optimizer: { + // enabled: false, + // runs: 200 + // }, + evmVersion: 'constantinople' + } + } + } }