From fdff074acb4162e33be4adf724812df2e21a46cc Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 7 Jun 2018 17:56:31 -0700 Subject: [PATCH 01/74] Migrations to ES6 --- packages/truffle-migrate/index.js | 207 +++++++++++++++--------------- 1 file changed, 101 insertions(+), 106 deletions(-) diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index b93eb020003..1ab7c020e19 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -1,95 +1,104 @@ -var fs = require("fs"); -var dir = require("node-dir"); -var path = require("path"); -var ResolverIntercept = require("./resolverintercept"); -var Require = require("truffle-require"); -var async = require("async"); -var Web3 = require("web3"); -var expect = require("truffle-expect"); -var Deployer = require("truffle-deployer"); - -function Migration(file) { - this.file = path.resolve(file); - this.number = parseInt(path.basename(file)); -}; +const fs = require("fs"); +const dir = require("node-dir"); +const path = require("path"); +const ResolverIntercept = require("./resolverintercept"); +const Require = require("truffle-require"); +const async = require("async"); +const Web3 = require("web3"); +const expect = require("truffle-expect"); +const Deployer = require("truffle-deployer"); + +class Migration { + + constructor(file){ + this.file = path.resolve(file); + this.number = parseInt(path.basename(file)) + } -Migration.prototype.run = function(options, callback) { - var self = this; - var logger = options.logger; + async run(options, callback) { + const self = this; + const logger = options.logger; + const resolver = new ResolverIntercept(options.resolver); + const web3 = new Web3(); + web3.setProvider(options.provider); - var web3 = new Web3(); - web3.setProvider(options.provider); + logger.log("Running migration: " + path.relative(options.migrations_directory, this.file)); - logger.log("Running migration: " + path.relative(options.migrations_directory, this.file)); + // Initial context. + const context = { + web3: web3 + }; - var resolver = new ResolverIntercept(options.resolver); + const deployer = new Deployer({ + logger: { + log: function(msg) { + logger.log(" " + msg); + } + }, + network: options.network, + network_id: options.network_id, + provider: options.provider, + basePath: path.dirname(this.file) + }); - // Initial context. - var context = { - web3: web3 - }; + const finish = async function(err) { + if (err) return callback(err); - var deployer = new Deployer({ - logger: { - log: function(msg) { - logger.log(" " + msg); - } - }, - network: options.network, - network_id: options.network_id, - provider: options.provider, - basePath: path.dirname(this.file) - }); - - var finish = function(err) { - if (err) return callback(err); - deployer.start().then(function() { - if (options.save === false) return; - - var Migrations = resolver.require("./Migrations.sol"); - - if (Migrations && Migrations.isDeployed()) { - logger.log("Saving successful migration to network..."); - return Migrations.deployed().then(function(migrations) { - return migrations.setCompleted(self.number); - }); - } - }).then(function() { - if (options.save === false) return; - logger.log("Saving artifacts..."); - return options.artifactor.saveAll(resolver.contracts()); - }).then(function() { - // Use process.nextTicK() to prevent errors thrown in the callback from triggering the below catch() - process.nextTick(callback); - }).catch(function(e) { - logger.log("Error encountered, bailing. Network state unknown. Review successful transactions manually."); - callback(e); - }); - }; + try { + await deployer.start(); + + if (options.save === false) return; + + const Migrations = resolver.require("./Migrations.sol"); - web3 - .eth - .getAccounts() - .then(accounts => { + if (Migrations && Migrations.isDeployed()) { + logger.log("Saving successful migration to network..."); + const migrations = await Migrations.deployed(); + await migrations.setCompleted(self.number); + } + + logger.log("Saving artifacts..."); + await options.artifactor.saveAll(resolver.contracts()); + + // Use process.nextTicK() to prevent errors thrown in the + // callback from triggering the below catch() + process.nextTick(callback); + } catch(e) { + logger.log("Error encountered, bailing. Review successful transactions manually."); + callback(e); + }; + }; - Require.file({ + try { + const accounts = await web3.eth.getAccounts(); + const requireOptions = { file: self.file, context: context, resolver: resolver, args: [deployer], - }, function(err, fn) { - if (!fn || !fn.length || fn.length == 0) { - return callback(new Error("Migration " + self.file + " invalid or does not take any parameters")); + } + + Require.file(requireOptions, (err, fn) => { + if (err) return callback(err); + + const unRunnable = !fn || !fn.length || fn.length == 0; + + if (unRunnable){ + const msg = `Migration ${self.file} invalid or does not take any parameters`; + return callback(new Error(msg)); } + fn(deployer, options.network, accounts); finish(); }); - }) - .catch(callback); -}; + } catch(err){ + callback(err) + } + } +} -var Migrate = { +const Migrate = { Migration: Migration, assemble: function(options, callback) { @@ -98,21 +107,15 @@ var Migrate = { options.allowed_extensions = options.allowed_extensions || /^\.(js|es6?)$/; - var migrations = files.filter(function(file) { - return isNaN(parseInt(path.basename(file))) == false; - }).filter(function(file) { - return path.extname(file).match(options.allowed_extensions) != null; - }).map(function(file) { - return new Migration(file, options.network); - }); + let migrations = files + .filter(file => isNaN(parseInt(path.basename(file))) == false) + .filter(file => path.extname(file).match(options.allowed_extensions) != null) + .map(file => new Migration(file, options.network)); // Make sure to sort the prefixes as numbers and not strings. - migrations = migrations.sort(function(a, b) { - if (a.number > b.number) { - return 1; - } else if (a.number < b.number) { - return -1; - } + migrations = migrations.sort((a, b) => { + if (a.number > b.number) return 1; + if (a.number < b.number) return -1; return 0; }); @@ -121,7 +124,7 @@ var Migrate = { }, run: function(options, callback) { - var self = this; + const self = this; expect.options(options, [ "working_directory", @@ -149,23 +152,18 @@ var Migrate = { }, runFrom: function(number, options, callback) { - var self = this; + const self = this; this.assemble(options, function(err, migrations) { if (err) return callback(err); while (migrations.length > 0) { - if (migrations[0].number >= number) { - break; - } - + if (migrations[0].number >= number) break; migrations.shift(); } if (options.to) { - migrations = migrations.filter(function(migration) { - return migration.number <= options.to; - }); + migrations = migrations.filter(migration => migration.number <= options.to); } self.runMigrations(migrations, options, callback); @@ -180,7 +178,7 @@ var Migrate = { // Perform a shallow clone of the options object // so that we can override the provider option without // changing the original options object passed in. - var clone = {}; + const clone = {}; Object.keys(options).forEach(function(key) { clone[key] = options[key]; @@ -204,7 +202,7 @@ var Migrate = { }, wrapProvider: function(provider, logger) { - var printTransaction = function(tx_hash) { + const printTransaction = function(tx_hash) { logger.log(" ... " + tx_hash); }; @@ -226,7 +224,7 @@ var Migrate = { wrapResolver: function(resolver, provider) { return { require: function(import_path, search_path) { - var abstraction = resolver.require(import_path, search_path); + const abstraction = resolver.require(import_path, search_path); abstraction.setProvider(provider); @@ -237,7 +235,7 @@ var Migrate = { }, lastCompletedMigration: function(options, callback) { - var Migrations; + let Migrations; try { Migrations = options.resolver.require("Migrations"); @@ -249,7 +247,7 @@ var Migrate = { return callback(null, 0); } - var migrations = Migrations.deployed(); + const migrations = Migrations.deployed(); Migrations.deployed().then(function(migrations) { // Two possible Migrations.sol's (lintable/unlintable) @@ -263,7 +261,7 @@ var Migrate = { }, needsMigrating: function(options, callback) { - var self = this; + const self = this; if (options.reset == true) { return callback(null, true); @@ -276,10 +274,7 @@ var Migrate = { if (err) return callback(err); while (migrations.length > 0) { - if (migrations[0].number >= number) { - break; - } - + if (migrations[0].number >= number) break; migrations.shift(); } From 61af79f754cb3d3379347d22c33d3ca4f6d782cf Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 7 Jun 2018 20:56:19 -0700 Subject: [PATCH 02/74] Add migrations emitter and reporter draft --- packages/truffle-migrate/index.js | 29 ++- packages/truffle-migrate/package.json | 1 + packages/truffle-migrate/reporter.js | 315 ++++++++++++++++++++++++++ 3 files changed, 335 insertions(+), 10 deletions(-) create mode 100644 packages/truffle-migrate/reporter.js diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 1ab7c020e19..1e5e598f57d 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -1,18 +1,24 @@ const fs = require("fs"); const dir = require("node-dir"); const path = require("path"); -const ResolverIntercept = require("./resolverintercept"); -const Require = require("truffle-require"); + +const Emittery = require('emittery'); const async = require("async"); const Web3 = require("web3"); + const expect = require("truffle-expect"); const Deployer = require("truffle-deployer"); +const Require = require("truffle-require"); + +const ResolverIntercept = require("./resolverintercept"); +const Reporter = require("./reporter"); class Migration { constructor(file){ this.file = path.resolve(file); - this.number = parseInt(path.basename(file)) + this.number = parseInt(path.basename(file)); + this.emitter = new Emittery(); } async run(options, callback) { @@ -22,8 +28,6 @@ class Migration { const web3 = new Web3(); web3.setProvider(options.provider); - logger.log("Running migration: " + path.relative(options.migrations_directory, this.file)); - // Initial context. const context = { web3: web3 @@ -38,9 +42,13 @@ class Migration { network: options.network, network_id: options.network_id, provider: options.provider, - basePath: path.dirname(this.file) + basePath: path.dirname(self.file) }); + const reporter = new Reporter(deployer, self); + const migrationsPath = path.relative(options.migrations_directory, self.file) + await self.emitter.emit('preMigrate', migrationsPath); + const finish = async function(err) { if (err) return callback(err); @@ -52,19 +60,19 @@ class Migration { const Migrations = resolver.require("./Migrations.sol"); if (Migrations && Migrations.isDeployed()) { - logger.log("Saving successful migration to network..."); + await self.emitter.emit('saveMigration', self.number); const migrations = await Migrations.deployed(); await migrations.setCompleted(self.number); } - logger.log("Saving artifacts..."); + await self.emitter.emit('postMigrate'); await options.artifactor.saveAll(resolver.contracts()); // Use process.nextTicK() to prevent errors thrown in the // callback from triggering the below catch() process.nextTick(callback); } catch(e) { - logger.log("Error encountered, bailing. Review successful transactions manually."); + await self.emitter.emit('error'); callback(e); }; }; @@ -240,7 +248,8 @@ const Migrate = { try { Migrations = options.resolver.require("Migrations"); } catch (e) { - return callback(new Error("Could not find built Migrations contract: " + e.message)); + const message = `Could not find built Migrations contract: ${e.message}`; + return callback(new Error()); } if (Migrations.isDeployed() == false) { diff --git a/packages/truffle-migrate/package.json b/packages/truffle-migrate/package.json index 06fe15078f8..9a9950b7982 100644 --- a/packages/truffle-migrate/package.json +++ b/packages/truffle-migrate/package.json @@ -22,6 +22,7 @@ "homepage": "https://github.com/trufflesuite/truffle-migrate#readme", "dependencies": { "async": "2.6.1", + "emittery": "^0.4.0", "node-dir": "0.1.17", "truffle-deployer": "^2.0.7", "truffle-expect": "^0.0.4", diff --git a/packages/truffle-migrate/reporter.js b/packages/truffle-migrate/reporter.js new file mode 100644 index 00000000000..08e2fd3c883 --- /dev/null +++ b/packages/truffle-migrate/reporter.js @@ -0,0 +1,315 @@ +/** + * Example reporter class that emulates the classic logger behavior + */ + +class Reporter { + constructor(deployer, migration){ + this.deployingMany = false; + this.logConfirmations = false; + this.deployer = deployer; + this.migration = migration; + this.separator = '\n'; + this.listen(); + } + + listen(){ + this.migration.emitter.on('preMigrate', this.preMigrate.bind(this)); + this.migration.emitter.on('saveMigration', this.saveMigrate.bind(this)); + this.migration.emitter.on('postMigrate', this.postMigrate.bind(this)); + + this.deployer.emitter.on('preDeploy', this.preDeploy.bind(this)); + this.deployer.emitter.on('postDeploy', this.postDeploy.bind(this)); + this.deployer.emitter.on('preDeployMany', this.preDeployMany.bind(this)); + this.deployer.emitter.on('postDeployMany', this.postDeployMany.bind(this)); + this.deployer.emitter.on('deployFailed', this.deployFailed.bind(this)); + this.deployer.emitter.on('linking', this.linking.bind(this)); + this.deployer.emitter.on('error', this.error.bind(this)); + this.deployer.emitter.on('transactionHash', this.hash.bind(this)); + this.deployer.emitter.on('receipt', this.receipt.bind(this)); + this.deployer.emitter.on('confirmation', this.confirmation.bind(this)); + } + + async preMigrate(payload){ + const message = this.messages('preMigrate', payload); + this.deployer.logger.log(message); + } + + async saveMigrate(payload){ + const message = this.messages('saving', payload); + this.deployer.logger.log(message); + } + + async postMigrate(payload){ + const message = this.messages('postMigrate', payload); + this.deployer.logger.log(message); + } + + async migrationError(payload){ + const message = this.messages('migrateErr', payload); + this.deployer.logger.log(message); + } + + async preDeploy(payload){ + let message; + (payload.deployed) + ? message = this.messages('replacing', payload.contract, payload.deployed) + : message = this.messages('deploying', payload.contract, payload.deployed); + + !this.deployingMany && this.deployer.logger.log(message); + } + + async postDeploy(payload){ + let message; + (payload.deployed) + ? message = this.messages('deployed', payload.contract.contractName, payload.instance.address) + : message = this.messages('notDeployed', payload.contract.contractName, payload.instance.address); + + this.deployer.logger.log(message); + } + + async preDeployMany(batch){ + let message = this.messages('many'); + + this.deployingMany = true; + this.deployer.logger.log(message); + + batch.forEach(item => { + Array.isArray(item) + ? message = this.messages('listMany', item[0].contractName) + : message = this.messages('listMany', item.contractName) + + this.deployer.logger.log(message); + }) + + this.deployer.logger.log(this.separator); + } + + async postDeployMany(){ + this.deployingMany = false; + } + + async deployFailed(payload){ + const message = await this.processDeploymentError(payload); + this.deployer.logger.error(message) + } + + linking(payload){ + let message = this.messages('linking', payload.libraryName, payload.libraryAddress, payload.contractName); + this.deployer.logger.log(message); + } + + async error(payload){ + let message = this.messages(payload.type, payload.contract); + this.deployer.logger.error(message); + } + + async hash(payload){ + let message = this.messages('hash', payload.transactionHash, payload.contractName); + this.deployer.logger.log(message); + } + + async receipt(payload){ + let message = this.messages('receipt', payload.receipt.gasUsed, payload.contractName); + this.deployer.logger.log(message); + } + + async confirmation(payload){ + let message = this.messages('confirmation', payload.num, payload.receipt, payload.contractName); + this.logConfirmations && this.deployer.logger.log(message); + } + + underline(msg){ + const ul = '='.repeat(msg.length); + return `\n${msg}\n${ul}`; + } + + messages(kind, ...args){ + const prefix = '\nError:'; + + args[0] = args[0] || {}; + args[1] = args[1] || {}; + args[2] = args[2] || {}; + args[3] = args[3] || {}; + + const kinds = { + + // --------------------------------------- Errors -------------------------------------------- + migrateErr: `Error encountered, bailing. Review successful transactions manually.\n` + + `${args[0].message}`, + + noLibName: `${prefix} Cannot link a library with no name.\n`, + noLibAddress: `${prefix} "${args[0].contractName}" has no address. Has it been deployed?\n`, + + noBytecode: `${prefix} "${args[0].contractName}" ` + + `is an abstract contract or an interface and cannot be deployed\n` + + ` * Hint: just import the contract into the '.sol' file that uses it.\n`, + + intWithGas: `${prefix} "${args[0].contractName}" ran out of gas ` + + `(using a value you set in your network config or deployment parameters.)\n` + + ` * Block limit: ${args[2]}\n` + + ` * Gas sent: ${args[1]}\n`, + + intNoGas: `${prefix} "${args[0].contractName}" ran out of gas ` + + `(using Truffle's estimate.)\n` + + ` * Block limit: ${args[1]}\n` + + ` * Gas sent: ${args[2]}\n` + + ` * Try:\n` + + ` + Setting a higher gas estimate multiplier for this contract\n` + + ` + Using the solc optimizer settings in 'truffle.js'\n` + + ` + Making your contract smaller\n` + + ` + Making your contract constructor more efficient\n` + + ` + Setting a higher network block limit if you are on a\n` + + ` private network or test client (like ganache).\n`, + + oogNoGas: `${prefix} "${args[0].contractName}" ran out of gas. Something in the constructor ` + + `(ex: infinite loop) caused gas estimation to fail. Try:\n` + + ` * Making your contract constructor more efficient\n` + + ` * Setting the gas manually in your config or a deployment parameter\n` + + ` * Using the solc optimizer settings in 'truffle.js'\n` + + ` * Setting a higher network block limit if you are on a\n` + + ` private network or test client (like ganache).\n`, + + rvtReason: `Revert with string error not implemented yet.`, + asrtReason: `Assert with string error not implemented yet.`, + + rvtNoReason: `${prefix} "${args[0].contractName}" hit a require or revert statement ` + + `somewhere in its constructor. Try:\n` + + ` * Verifying that your constructor params satisfy all require conditions.\n` + + ` * Adding reason strings to your require statements.\n`, + + asrtNoReason: `${prefix} "${args[0].contractName}" hit an invalid opcode while deploying. Try:\n` + + ` * Verifying that your constructor params satisfy all assert conditions.\n` + + ` * Verifying your constructor code doesn't access an array out of bounds.\n` + + ` * Adding reason strings to your assert statements.\n`, + + noMoney: `${prefix} "${args[0].contractName}" could not deploy due to insufficient funds\n` + + ` * Account: ${args[1]}\n` + + ` * Balance: ${args[2]} wei\n` + + ` * Message: ${args[3]}\n` + + ` * Try:\n` + + ` + Using an adequately funded account\n` + + ` + If you are using a local Geth node, verify that your node is synced.\n`, + + blockWithGas: `${prefix} "${args[0].contractName}" exceeded the block limit ` + + `(with a gas value you set).\n` + + ` * Block limit: ${args[2]}\n` + + ` * Gas sent: ${args[1]}\n` + + ` * Try:\n` + + ` + Sending less gas.\n` + + ` + Setting a higher network block limit if you are on a\n` + + ` private network or test client (like ganache).\n`, + + blockNoGas: `${prefix} "${args[0].contractName}" exceeded the block limit ` + + `(using Truffle's estimate).\n` + + ` * Block limit: ${args[1]}\n` + + ` * Report this error in the Truffle issues on Github. It should not happen.\n` + + ` * Try: setting gas manually in 'truffle.js' or as parameter to 'deployer.deploy'\n`, + + nonce: `${prefix} "${args[0].contractName}" received: ${args[1]}.\n` + + ` * This error is common when Infura is under heavy network load.\n` + + ` * Try: setting the 'confirmations' key in your network config\n` + + ` to wait for several block confirmations between each deployment.\n`, + + default: `${prefix} "${args[0].contractName}" -- ${args[1]}.\n`, + + // ------------------------------------ Successes -------------------------------------------- + + deploying: this.underline(`Deploying ${args[0]}.contractName`), + replacing: this.underline(`Replacing ${args[0]}.contractName`), + reusing: this.underline(`Re-using. ${args[0]}.contractName`), + many: this.underline(`Deploying Batch`), + linking: this.underline('Linking') + '\n' + + `${args[2]}`.padEnd(20) + ` > ${args[0]}`.padEnd(25) + `(${args[1]})`, + + preMigrate: `${args[0]}`, + saving: `\nSaving migration number: ${args[0]}`, + postMigrate: `\nSaving artifacts...`, + + listMany: `* ${args[0]}`, + deployed: ` > address:'.padEnd(25)` + args[1], + hash: ` > transaction hash:'.padEnd(25)` + args[0], + receipt: ` > gas usage:'.padEnd(25)` + args[0], + confirmation: ` > confirmation number:'.padEnd(25)` + args[0], + } + + return kinds[kind]; + } + + async processDeploymentError(payload){ + let message; + + const error = payload.estimateError || payload.error; + + const errors = { + INT: error.message.includes('base fee') || error.message.includes('intrinsic'), + OOG: error.message.includes('out of gas'), + RVT: error.message.includes('revert'), + ETH: error.message.includes('funds'), + BLK: error.message.includes('block gas limit'), + NCE: error.message.includes('nonce'), + INV: error.message.includes('invalid opcode'), + } + + let type = Object.keys(errors).find(key => errors[key]); + + switch (type) { + case 'INT': + (payload.gas) + ? message = this.messages('intWithGas', payload.contract, payload.gas, payload.blockLimit) + : message = this.messages('intNoGas', payload.contract, payload.blockLimit, payload.estimate); + + this.deployer.logger.error(message); + break; + + case 'OOG': + (payload.gas) + ? message = this.messages('intWithGas', payload.contract, payload.gas, payload.blockLimit) + : message = this.messages('oogNoGas', payload.contract, payload.blockLimit, payload.estimate); + + this.deployer.logger.error(message); + break; + + case 'RVT': + (payload.reason) + ? message = this.messages('rvtReason', payload.contract, payload.reason) + : message = this.messages('rvtNoReason', payload.contract); + + this.deployer.logger.error(message); + break; + + case 'INV': + (payload.reason) + ? message = this.messages('asrtReason', payload.contract, payload.reason) + : message = this.messages('asrtNoReason', payload.contract); + + this.deployer.logger.error(message); + break; + + case 'BLK': + (payload.gas) + ? message = this.messages('blockWithGas', payload.contract, payload.gas, payload.blockLimit) + : message = this.messages('blockNoGas', payload.contract, payload.blockLimit) + + this.deployer.logger.error(message); + break; + + case 'ETH': + let balance = await payload.contract.web3.eth.getBalance(payload.from); + balance = balance.toString(); + message = this.messages('noMoney', payload.contract, payload.from, balance, error.message); + this.deployer.logger.error(message); + break; + + case 'NCE': + message = this.messages('nonce', payload.contract, error.message); + this.deployer.logger.error(message); + break; + + default: + message = this.messages('default', payload.contract, error.message); + this.deployer.logger.error(message); + } + } +} + +module.exports = Reporter; \ No newline at end of file From f1f96bb0b5b6995b56b125a5c767cba6603fbac6 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Fri, 8 Jun 2018 21:00:07 -0700 Subject: [PATCH 03/74] Add reporter details, formatting --- packages/truffle-migrate/index.js | 23 +- packages/truffle-migrate/reporter.js | 373 ++++++++++++++++----------- 2 files changed, 225 insertions(+), 171 deletions(-) diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 1e5e598f57d..18ab2ada2f2 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -36,7 +36,7 @@ class Migration { const deployer = new Deployer({ logger: { log: function(msg) { - logger.log(" " + msg); + logger.log(msg); } }, network: options.network, @@ -68,8 +68,7 @@ class Migration { await self.emitter.emit('postMigrate'); await options.artifactor.saveAll(resolver.contracts()); - // Use process.nextTicK() to prevent errors thrown in the - // callback from triggering the below catch() + // Prevent errors thrown in the callback from triggering the below catch() process.nextTick(callback); } catch(e) { await self.emitter.emit('error'); @@ -209,21 +208,13 @@ const Migrate = { }, callback); }, - wrapProvider: function(provider, logger) { - const printTransaction = function(tx_hash) { - logger.log(" ... " + tx_hash); - }; - + wrapProvider: function(provider) { return { send: function(payload, callback) { provider.send(payload, function(err, result) { - if (err) return callback(err); - - if (payload.method == "eth_sendTransaction") { - printTransaction(result.result); - } - - callback(err, result); + (err) + ? callback(err) + : callback(err, result); }); } }; @@ -233,9 +224,7 @@ const Migrate = { return { require: function(import_path, search_path) { const abstraction = resolver.require(import_path, search_path); - abstraction.setProvider(provider); - return abstraction; }, resolve: resolver.resolve diff --git a/packages/truffle-migrate/reporter.js b/packages/truffle-migrate/reporter.js index 08e2fd3c883..4dc64b3e986 100644 --- a/packages/truffle-migrate/reporter.js +++ b/packages/truffle-migrate/reporter.js @@ -2,12 +2,17 @@ * Example reporter class that emulates the classic logger behavior */ +const util = require('util'); +const web3Utils = require('web3-utils'); + class Reporter { constructor(deployer, migration){ this.deployingMany = false; this.logConfirmations = false; this.deployer = deployer; this.migration = migration; + this.currentGasTotal = 0; + this.currentCostTotal = new web3Utils.BN(0); this.separator = '\n'; this.listen(); } @@ -29,40 +34,71 @@ class Reporter { this.deployer.emitter.on('confirmation', this.confirmation.bind(this)); } - async preMigrate(payload){ - const message = this.messages('preMigrate', payload); + getGasTotal(){ + const gas = this.currentGasTotal; + const cost = web3Utils.fromWei(this.currentCostTotal, "ether"); + + this.currentGasTotal = 0; + this.currentCostTotal = new web3Utils.BN(0); + + return { + gas: gas, + cost: cost + } + } + + async preMigrate(data){ + const message = this.messages('preMigrate', data); this.deployer.logger.log(message); } - async saveMigrate(payload){ - const message = this.messages('saving', payload); + async saveMigrate(data){ + const message = this.messages('saving', data); this.deployer.logger.log(message); } - async postMigrate(payload){ - const message = this.messages('postMigrate', payload); + async postMigrate(data){ + const message = this.messages('postMigrate', data); this.deployer.logger.log(message); } - async migrationError(payload){ - const message = this.messages('migrateErr', payload); + async migrationError(data){ + const message = this.messages('migrateErr', data); this.deployer.logger.log(message); } - async preDeploy(payload){ + async preDeploy(data){ let message; - (payload.deployed) - ? message = this.messages('replacing', payload.contract, payload.deployed) - : message = this.messages('deploying', payload.contract, payload.deployed); + (data.deployed) + ? message = this.messages('replacing', data) + : message = this.messages('deploying', data); !this.deployingMany && this.deployer.logger.log(message); } - async postDeploy(payload){ + async postDeploy(data){ let message; - (payload.deployed) - ? message = this.messages('deployed', payload.contract.contractName, payload.instance.address) - : message = this.messages('notDeployed', payload.contract.contractName, payload.instance.address); + if (data.deployed){ + const web3 = data.contract.web3; + const tx = await data.contract.web3.eth.getTransaction(data.receipt.transactionHash); + const balance = await data.contract.web3.eth.getBalance(tx.from); + const gasPrice = new web3Utils.BN(tx.gasPrice); + const gas = new web3Utils.BN(data.receipt.gasUsed); + const cost = gasPrice.mul(gas); + + data.gas = data.receipt.gasUsed; + data.from = tx.from; + data.cost = web3Utils.fromWei(cost, 'ether'); + data.balance = web3Utils.fromWei(balance, 'ether'); + + this.currentGasTotal += data.gas; + this.currentCostTotal = this.currentCostTotal.add(cost) + this.currentAddress = this.from; + + message = this.messages('deployed', data); + } else { + message = this.messages('notDeployed', data); + } this.deployer.logger.log(message); } @@ -75,8 +111,8 @@ class Reporter { batch.forEach(item => { Array.isArray(item) - ? message = this.messages('listMany', item[0].contractName) - : message = this.messages('listMany', item.contractName) + ? message = this.messages('listMany', item[0]) + : message = this.messages('listMany', item) this.deployer.logger.log(message); }) @@ -88,157 +124,186 @@ class Reporter { this.deployingMany = false; } - async deployFailed(payload){ - const message = await this.processDeploymentError(payload); + async deployFailed(data){ + const message = await this.processDeploymentError(data); this.deployer.logger.error(message) } - linking(payload){ - let message = this.messages('linking', payload.libraryName, payload.libraryAddress, payload.contractName); + linking(data){ + let message = this.messages('linking', data); this.deployer.logger.log(message); } - async error(payload){ - let message = this.messages(payload.type, payload.contract); + async error(data){ + let message = this.messages(data.type, data); this.deployer.logger.error(message); } - async hash(payload){ - let message = this.messages('hash', payload.transactionHash, payload.contractName); + async hash(data){ + let message = this.messages('hash', data); this.deployer.logger.log(message); } - async receipt(payload){ - let message = this.messages('receipt', payload.receipt.gasUsed, payload.contractName); - this.deployer.logger.log(message); + async receipt(data){ + let message = this.messages('receipt', data); } - async confirmation(payload){ - let message = this.messages('confirmation', payload.num, payload.receipt, payload.contractName); + async confirmation(data){ + let message = this.messages('confirmation', data); this.logConfirmations && this.deployer.logger.log(message); } underline(msg){ + return (typeof msg === 'number') + ? `\n ${'-'.repeat(msg)}` + : `\n ${msg}\n ${'-'.repeat(msg.length)}`; + } + + doubleline(msg){ const ul = '='.repeat(msg.length); return `\n${msg}\n${ul}`; } - messages(kind, ...args){ - const prefix = '\nError:'; - - args[0] = args[0] || {}; - args[1] = args[1] || {}; - args[2] = args[2] || {}; - args[3] = args[3] || {}; + messages(kind, data){ + //console.log('data --> ' + util.format("%O", data)); + const prefix = '\nError:'; const kinds = { // --------------------------------------- Errors -------------------------------------------- - migrateErr: `Error encountered, bailing. Review successful transactions manually.\n` + - `${args[0].message}`, - - noLibName: `${prefix} Cannot link a library with no name.\n`, - noLibAddress: `${prefix} "${args[0].contractName}" has no address. Has it been deployed?\n`, - - noBytecode: `${prefix} "${args[0].contractName}" ` + - `is an abstract contract or an interface and cannot be deployed\n` + - ` * Hint: just import the contract into the '.sol' file that uses it.\n`, - - intWithGas: `${prefix} "${args[0].contractName}" ran out of gas ` + - `(using a value you set in your network config or deployment parameters.)\n` + - ` * Block limit: ${args[2]}\n` + - ` * Gas sent: ${args[1]}\n`, - - intNoGas: `${prefix} "${args[0].contractName}" ran out of gas ` + - `(using Truffle's estimate.)\n` + - ` * Block limit: ${args[1]}\n` + - ` * Gas sent: ${args[2]}\n` + - ` * Try:\n` + - ` + Setting a higher gas estimate multiplier for this contract\n` + - ` + Using the solc optimizer settings in 'truffle.js'\n` + - ` + Making your contract smaller\n` + - ` + Making your contract constructor more efficient\n` + - ` + Setting a higher network block limit if you are on a\n` + - ` private network or test client (like ganache).\n`, - - oogNoGas: `${prefix} "${args[0].contractName}" ran out of gas. Something in the constructor ` + - `(ex: infinite loop) caused gas estimation to fail. Try:\n` + - ` * Making your contract constructor more efficient\n` + - ` * Setting the gas manually in your config or a deployment parameter\n` + - ` * Using the solc optimizer settings in 'truffle.js'\n` + - ` * Setting a higher network block limit if you are on a\n` + - ` private network or test client (like ganache).\n`, - - rvtReason: `Revert with string error not implemented yet.`, - asrtReason: `Assert with string error not implemented yet.`, - - rvtNoReason: `${prefix} "${args[0].contractName}" hit a require or revert statement ` + - `somewhere in its constructor. Try:\n` + - ` * Verifying that your constructor params satisfy all require conditions.\n` + - ` * Adding reason strings to your require statements.\n`, - - asrtNoReason: `${prefix} "${args[0].contractName}" hit an invalid opcode while deploying. Try:\n` + - ` * Verifying that your constructor params satisfy all assert conditions.\n` + - ` * Verifying your constructor code doesn't access an array out of bounds.\n` + - ` * Adding reason strings to your assert statements.\n`, - - noMoney: `${prefix} "${args[0].contractName}" could not deploy due to insufficient funds\n` + - ` * Account: ${args[1]}\n` + - ` * Balance: ${args[2]} wei\n` + - ` * Message: ${args[3]}\n` + - ` * Try:\n` + - ` + Using an adequately funded account\n` + - ` + If you are using a local Geth node, verify that your node is synced.\n`, - - blockWithGas: `${prefix} "${args[0].contractName}" exceeded the block limit ` + - `(with a gas value you set).\n` + - ` * Block limit: ${args[2]}\n` + - ` * Gas sent: ${args[1]}\n` + - ` * Try:\n` + - ` + Sending less gas.\n` + - ` + Setting a higher network block limit if you are on a\n` + - ` private network or test client (like ganache).\n`, - - blockNoGas: `${prefix} "${args[0].contractName}" exceeded the block limit ` + - `(using Truffle's estimate).\n` + - ` * Block limit: ${args[1]}\n` + - ` * Report this error in the Truffle issues on Github. It should not happen.\n` + - ` * Try: setting gas manually in 'truffle.js' or as parameter to 'deployer.deploy'\n`, - - nonce: `${prefix} "${args[0].contractName}" received: ${args[1]}.\n` + - ` * This error is common when Infura is under heavy network load.\n` + - ` * Try: setting the 'confirmations' key in your network config\n` + - ` to wait for several block confirmations between each deployment.\n`, - - default: `${prefix} "${args[0].contractName}" -- ${args[1]}.\n`, + migrateErr: () => + `Error encountered, bailing. Review successful transactions manually.\n` + + `${data.message}`, + + noLibName: () => + `${prefix} Cannot link a library with no name.\n`, + + noLibAddress: () => + `${prefix} "${data.contract.contractName}" has no address. Has it been deployed?\n`, + + noBytecode: () => + `${prefix} "${data.contract.contractName}" ` + + `is an abstract contract or an interface and cannot be deployed\n` + + ` * Hint: just import the contract into the '.sol' file that uses it.\n`, + + intWithGas: () => + `${prefix} "${data.contract.contractName}" ran out of gas ` + + `(using a value you set in your network config or deployment parameters.)\n` + + ` * Block limit: ${data.blockLimit}\n` + + ` * Gas sent: ${data.gas}\n`, + + intNoGas: () => + `${prefix} "${data.contract.contractName}" ran out of gas ` + + `(using Truffle's estimate.)\n` + + ` * Block limit: ${data.blockLimit}\n` + + ` * Gas sent: ${data.estimate}\n` + + ` * Try:\n` + + ` + Setting a higher gas estimate multiplier for this contract\n` + + ` + Using the solc optimizer settings in 'truffle.js'\n` + + ` + Making your contract smaller\n` + + ` + Making your contract constructor more efficient\n` + + ` + Setting a higher network block limit if you are on a\n` + + ` private network or test client (like ganache).\n`, + + oogNoGas: () => + `${prefix} "${data.contract.contractName}" ran out of gas. Something in the constructor ` + + `(ex: infinite loop) caused gas estimation to fail. Try:\n` + + ` * Making your contract constructor more efficient\n` + + ` * Setting the gas manually in your config or a deployment parameter\n` + + ` * Using the solc optimizer settings in 'truffle.js'\n` + + ` * Setting a higher network block limit if you are on a\n` + + ` private network or test client (like ganache).\n`, + + rvtReason: () => + `Revert with string error not implemented yet.`, + + asrtReason: () => + `Assert with string error not implemented yet.`, + + rvtNoReason: () => + `${prefix} "${data.contract.contractName}" hit a require or revert statement ` + + `somewhere in its constructor. Try:\n` + + ` * Verifying that your constructor params satisfy all require conditions.\n` + + ` * Adding reason strings to your require statements.\n`, + + asrtNoReason: () => + `${prefix} "${data.contract.contractName}" hit an invalid opcode while deploying. Try:\n` + + ` * Verifying that your constructor params satisfy all assert conditions.\n` + + ` * Verifying your constructor code doesn't access an array out of bounds.\n` + + ` * Adding reason strings to your assert statements.\n`, + + noMoney: () => + `${prefix} "${data.contract.contractName}" could not deploy due to insufficient funds\n` + + ` * Account: ${data.from}\n` + + ` * Balance: ${data.balance} wei\n` + + ` * Message: ${data.error.message}\n` + + ` * Try:\n` + + ` + Using an adequately funded account\n` + + ` + If you are using a local Geth node, verify that your node is synced.\n`, + + blockWithGas: () => + `${prefix} "${data.contract.contractName}" exceeded the block limit ` + + `(with a gas value you set).\n` + + ` * Block limit: ${data.blockLimit}\n` + + ` * Gas sent: ${data.gas}\n` + + ` * Try:\n` + + ` + Sending less gas.\n` + + ` + Setting a higher network block limit if you are on a\n` + + ` private network or test client (like ganache).\n`, + + blockNoGas: () => + `${prefix} "${data.contract.contractName}" exceeded the block limit ` + + `(using Truffle's estimate).\n` + + ` * Block limit: ${data.blockLimit}\n` + + ` * Report this error in the Truffle issues on Github. It should not happen.\n` + + ` * Try: setting gas manually in 'truffle.js' or as parameter to 'deployer.deploy'\n`, + + nonce: () => + `${prefix} "${data.contract.contractName}" received: ${data.error.message}.\n` + + ` * This error is common when Infura is under heavy network load.\n` + + ` * Try: setting the 'confirmations' key in your network config\n` + + ` to wait for several block confirmations between each deployment.\n`, + + default: () => + `${prefix} "${data.contract.contractName}" -- ${data.error.message}.\n`, // ------------------------------------ Successes -------------------------------------------- - deploying: this.underline(`Deploying ${args[0]}.contractName`), - replacing: this.underline(`Replacing ${args[0]}.contractName`), - reusing: this.underline(`Re-using. ${args[0]}.contractName`), - many: this.underline(`Deploying Batch`), - linking: this.underline('Linking') + '\n' + - `${args[2]}`.padEnd(20) + ` > ${args[0]}`.padEnd(25) + `(${args[1]})`, - - preMigrate: `${args[0]}`, - saving: `\nSaving migration number: ${args[0]}`, - postMigrate: `\nSaving artifacts...`, - - listMany: `* ${args[0]}`, - deployed: ` > address:'.padEnd(25)` + args[1], - hash: ` > transaction hash:'.padEnd(25)` + args[0], - receipt: ` > gas usage:'.padEnd(25)` + args[0], - confirmation: ` > confirmation number:'.padEnd(25)` + args[0], + deploying: () => this.underline(`Deploying '${data.contract.contractName}'`), + replacing: () => this.underline(`Replacing '${data.contract.contractName}'`), + reusing: () => this.underline(`Re-using '${data.contract.contractName}'`), + many: () => this.underline(`Deploying Batch`), + + linking: () => this.underline(`Linking`) + + `\n * Contract: ${data.contractName} <--> Library: ${data.libraryName} `+ + `(at address: ${data.libraryAddress})`, + + preMigrate: () => this.doubleline(`${data}`), + saving: () => `\n * Saving migration #${data}...`, + + postMigrate: () => ` * Saving artifacts...` + + this.underline(`* Migration complete`) + '\n' + + ` > ${'Total cost:'.padEnd(15)} ${this.getGasTotal().cost.padStart(15)} ETH\n`, + + deployed: () => ` > ${'contract address:'.padEnd(20)} ${data.receipt.contractAddress}\n` + + ` > ${'gas used:'.padEnd(20)} ${data.gas}\n` + + ` > ${'cost:'.padEnd(20)} ${data.cost} ETH\n` + + ` > ${'account:'.padEnd(20)} ${data.from}\n` + + ` > ${'balance:'.padEnd(20)} ${data.balance}\n`, + + listMany: () => ` * ${data.contractName}`, + hash: () => ` > ${'transaction hash:'.padEnd(20)}` + data.transactionHash, + receipt: () => ` > ${'gas usage:'.padEnd(20)}` + data.gas, + confirmation: () => ` > ${'confirmation number:'.padEnd(20)}` + data.num, } - return kinds[kind]; + return kinds[kind](); } - async processDeploymentError(payload){ + async processDeploymentError(data){ let message; - const error = payload.estimateError || payload.error; + const error = data.estimateError || data.error; const errors = { INT: error.message.includes('base fee') || error.message.includes('intrinsic'), @@ -254,59 +319,59 @@ class Reporter { switch (type) { case 'INT': - (payload.gas) - ? message = this.messages('intWithGas', payload.contract, payload.gas, payload.blockLimit) - : message = this.messages('intNoGas', payload.contract, payload.blockLimit, payload.estimate); + (data.gas) + ? message = this.messages('intWithGas', data) + : message = this.messages('intNoGas', data); this.deployer.logger.error(message); break; case 'OOG': - (payload.gas) - ? message = this.messages('intWithGas', payload.contract, payload.gas, payload.blockLimit) - : message = this.messages('oogNoGas', payload.contract, payload.blockLimit, payload.estimate); + (data.gas) + ? message = this.messages('intWithGas', data) + : message = this.messages('oogNoGas', data); this.deployer.logger.error(message); break; case 'RVT': - (payload.reason) - ? message = this.messages('rvtReason', payload.contract, payload.reason) - : message = this.messages('rvtNoReason', payload.contract); + (data.reason) + ? message = this.messages('rvtReason', data) + : message = this.messages('rvtNoReason', data); this.deployer.logger.error(message); break; case 'INV': - (payload.reason) - ? message = this.messages('asrtReason', payload.contract, payload.reason) - : message = this.messages('asrtNoReason', payload.contract); + (data.reason) + ? message = this.messages('asrtReason', data) + : message = this.messages('asrtNoReason', data); this.deployer.logger.error(message); break; case 'BLK': - (payload.gas) - ? message = this.messages('blockWithGas', payload.contract, payload.gas, payload.blockLimit) - : message = this.messages('blockNoGas', payload.contract, payload.blockLimit) + (data.gas) + ? message = this.messages('blockWithGas', data) + : message = this.messages('blockNoGas', data) this.deployer.logger.error(message); break; case 'ETH': - let balance = await payload.contract.web3.eth.getBalance(payload.from); - balance = balance.toString(); - message = this.messages('noMoney', payload.contract, payload.from, balance, error.message); + const balance = await data.contract.web3.eth.getBalance(data.from); + data.balance = balance.toString(); + message = this.messages('noMoney', data); this.deployer.logger.error(message); break; case 'NCE': - message = this.messages('nonce', payload.contract, error.message); + message = this.messages('nonce', data); this.deployer.logger.error(message); break; default: - message = this.messages('default', payload.contract, error.message); + message = this.messages('default', data); this.deployer.logger.error(message); } } From 10242e43fa6a637edd3fb90793e5625ff89454f6 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Mon, 11 Jun 2018 19:19:08 -0700 Subject: [PATCH 04/74] Working async/await and basic reporter format --- packages/truffle-migrate/index.js | 52 +++++++-- .../reporter/indentedSpinner.js | 15 +++ .../{ => reporter}/reporter.js | 105 +++++++++++++----- 3 files changed, 134 insertions(+), 38 deletions(-) create mode 100644 packages/truffle-migrate/reporter/indentedSpinner.js rename packages/truffle-migrate/{ => reporter}/reporter.js (80%) diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 18ab2ada2f2..e4098eff489 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -11,7 +11,7 @@ const Deployer = require("truffle-deployer"); const Require = require("truffle-require"); const ResolverIntercept = require("./resolverintercept"); -const Reporter = require("./reporter"); +const Reporter = require("./reporter/reporter"); class Migration { @@ -19,6 +19,8 @@ class Migration { this.file = path.resolve(file); this.number = parseInt(path.basename(file)); this.emitter = new Emittery(); + this.isFirst = false; + this.isLast = false; } async run(options, callback) { @@ -37,6 +39,9 @@ class Migration { logger: { log: function(msg) { logger.log(msg); + }, + error: function(msg){ + logger.error(msg); } }, network: options.network, @@ -46,15 +51,25 @@ class Migration { }); const reporter = new Reporter(deployer, self); - const migrationsPath = path.relative(options.migrations_directory, self.file) - await self.emitter.emit('preMigrate', migrationsPath); + const file = path.relative(options.migrations_directory, self.file) - const finish = async function(err) { - if (err) return callback(err); + const preMigrationsData = { + file: file, + isFirst: this.isFirst, + network: options.network, + networkId: options.network_id, + } + await self.emitter.emit('preMigrate', preMigrationsData); + + var finish = async function(migrateFn) { try { await deployer.start(); + if (migrateFn && migrateFn.then !== undefined){ + await deployer.then(() => migrateFn); + } + if (options.save === false) return; const Migrations = resolver.require("./Migrations.sol"); @@ -65,13 +80,23 @@ class Migration { await migrations.setCompleted(self.number); } - await self.emitter.emit('postMigrate'); + await self.emitter.emit('postMigrate', self.isLast); await options.artifactor.saveAll(resolver.contracts()); + deployer.finish(); + if (self.isLast){ + self.emitter.clearListeners(); + } // Prevent errors thrown in the callback from triggering the below catch() process.nextTick(callback); } catch(e) { - await self.emitter.emit('error'); + const payload = { + type: 'migrateErr', + error: e + }; + + await self.emitter.emit('error', payload); + deployer.finish(); callback(e); }; }; @@ -85,7 +110,7 @@ class Migration { args: [deployer], } - Require.file(requireOptions, (err, fn) => { + Require.file(requireOptions, async (err, fn) => { if (err) return callback(err); const unRunnable = !fn || !fn.length || fn.length == 0; @@ -95,8 +120,8 @@ class Migration { return callback(new Error(msg)); } - fn(deployer, options.network, accounts); - finish(); + const migrateFn = fn(deployer, options.network, accounts); + await finish(migrateFn); }); } catch(err){ @@ -200,6 +225,13 @@ const Migrate = { clone.provider = this.wrapProvider(options.provider, clone.logger); clone.resolver = this.wrapResolver(options.resolver, clone.provider); + // Make migrations aware of their position in sequence + const total = migrations.length; + if(total){ + migrations[0].isFirst = true; + migrations[total - 1].isLast = true; + } + async.eachSeries(migrations, function(migration, finished) { migration.run(clone, function(err) { if (err) return finished(err); diff --git a/packages/truffle-migrate/reporter/indentedSpinner.js b/packages/truffle-migrate/reporter/indentedSpinner.js new file mode 100644 index 00000000000..f00ee33224e --- /dev/null +++ b/packages/truffle-migrate/reporter/indentedSpinner.js @@ -0,0 +1,15 @@ +module.exports = { + "interval": 80, + "frames": [ + " ⠋", + " ⠙", + " ⠹", + " ⠸", + " ⠼", + " ⠴", + " ⠦", + " ⠧", + " ⠇", + " ⠏" + ] +} \ No newline at end of file diff --git a/packages/truffle-migrate/reporter.js b/packages/truffle-migrate/reporter/reporter.js similarity index 80% rename from packages/truffle-migrate/reporter.js rename to packages/truffle-migrate/reporter/reporter.js index 4dc64b3e986..bc0bf1835f6 100644 --- a/packages/truffle-migrate/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -4,6 +4,7 @@ const util = require('util'); const web3Utils = require('web3-utils'); +const spinner = require('./indentedSpinner'); class Reporter { constructor(deployer, migration){ @@ -13,6 +14,8 @@ class Reporter { this.migration = migration; this.currentGasTotal = 0; this.currentCostTotal = new web3Utils.BN(0); + this.finalCostTotal = new web3Utils.BN(0); + this.deployments = 0; this.separator = '\n'; this.listen(); } @@ -21,6 +24,7 @@ class Reporter { this.migration.emitter.on('preMigrate', this.preMigrate.bind(this)); this.migration.emitter.on('saveMigration', this.saveMigrate.bind(this)); this.migration.emitter.on('postMigrate', this.postMigrate.bind(this)); + this.migration.emitter.on('error', this.error.bind(this)); this.deployer.emitter.on('preDeploy', this.preDeploy.bind(this)); this.deployer.emitter.on('postDeploy', this.postDeploy.bind(this)); @@ -34,21 +38,29 @@ class Reporter { this.deployer.emitter.on('confirmation', this.confirmation.bind(this)); } - getGasTotal(){ + getTotals(){ const gas = this.currentGasTotal; const cost = web3Utils.fromWei(this.currentCostTotal, "ether"); + this.finalCostTotal = this.finalCostTotal.add(this.currentCostTotal); this.currentGasTotal = 0; this.currentCostTotal = new web3Utils.BN(0); return { gas: gas, - cost: cost + cost: cost, + finalCost: web3Utils.fromWei(this.finalCostTotal, "ether"), + deployments: this.deployments.toString() } } async preMigrate(data){ - const message = this.messages('preMigrate', data); + let message; + if (data.isFirst){ + message = this.messages('firstMigrate', data); + this.deployer.logger.log(message); + } + message = this.messages('preMigrate', data); this.deployer.logger.log(message); } @@ -57,9 +69,14 @@ class Reporter { this.deployer.logger.log(message); } - async postMigrate(data){ - const message = this.messages('postMigrate', data); + async postMigrate(isLast){ + let message = this.messages('postMigrate'); this.deployer.logger.log(message); + + if (isLast){ + message = this.messages('lastMigrate'); + this.deployer.logger.log(message); + } } async migrationError(data){ @@ -82,6 +99,7 @@ class Reporter { const web3 = data.contract.web3; const tx = await data.contract.web3.eth.getTransaction(data.receipt.transactionHash); const balance = await data.contract.web3.eth.getBalance(tx.from); + const gasPrice = new web3Utils.BN(tx.gasPrice); const gas = new web3Utils.BN(data.receipt.gasUsed); const cost = gasPrice.mul(gas); @@ -94,6 +112,7 @@ class Reporter { this.currentGasTotal += data.gas; this.currentCostTotal = this.currentCostTotal.add(cost) this.currentAddress = this.from; + this.deployments++; message = this.messages('deployed', data); } else { @@ -172,8 +191,8 @@ class Reporter { // --------------------------------------- Errors -------------------------------------------- migrateErr: () => - `Error encountered, bailing. Review successful transactions manually.\n` + - `${data.message}`, + `Exiting: Review successful transactions manually by checking the transaction hashes ` + + `above on Etherscan.\n`, noLibName: () => `${prefix} Cannot link a library with no name.\n`, @@ -269,32 +288,62 @@ class Reporter { // ------------------------------------ Successes -------------------------------------------- - deploying: () => this.underline(`Deploying '${data.contract.contractName}'`), - replacing: () => this.underline(`Replacing '${data.contract.contractName}'`), - reusing: () => this.underline(`Re-using '${data.contract.contractName}'`), - many: () => this.underline(`Deploying Batch`), + deploying: () => + this.underline(`Deploying '${data.contract.contractName}'`), + + replacing: () => + this.underline(`Replacing '${data.contract.contractName}'`), + + reusing: () => + this.underline(`Re-using '${data.contract.contractName}'`), + + many: () => + this.underline(`Deploying Batch`), + + linking: () => + this.underline(`Linking`) + + `\n * Contract: ${data.contractName} <--> Library: ${data.libraryName} `+ + `(at address: ${data.libraryAddress})`, + + preMigrate: () => + this.doubleline(`${data.file}`), + + saving: () => + `\n * Saving migration`, + + firstMigrate: () => + this.doubleline(`Starting migrations...`) + '\n' + + `> Network name: '${data.network}'\n` + + `> Network id: ${data.networkId}\n`, + + postMigrate: () => + ` * Saving artifacts` + + this.underline(18) + '\n' + + ` > ${'Total cost:'.padEnd(15)} ${this.getTotals().cost.padStart(15)} ETH\n`, + + lastMigrate: () => + this.doubleline('Summary') + '\n' + + `> ${'TotalDeployments:'.padEnd(20)} ${this.getTotals().deployments}\n` + + `> ${'Final cost:'.padEnd(20)} ${this.getTotals().finalCost} ETH\n`, - linking: () => this.underline(`Linking`) + - `\n * Contract: ${data.contractName} <--> Library: ${data.libraryName} `+ - `(at address: ${data.libraryAddress})`, + deployed: () => + ` > ${'contract address:'.padEnd(20)} ${data.receipt.contractAddress}\n` + + ` > ${'account:'.padEnd(20)} ${data.from}\n` + + ` > ${'cost:'.padEnd(20)} ${data.cost} ETH\n` + + ` > ${'balance:'.padEnd(20)} ${data.balance}\n` + + ` > ${'gas used:'.padEnd(20)} ${data.gas}\n`, - preMigrate: () => this.doubleline(`${data}`), - saving: () => `\n * Saving migration #${data}...`, + listMany: () => + ` * ${data.contractName}`, - postMigrate: () => ` * Saving artifacts...` + - this.underline(`* Migration complete`) + '\n' + - ` > ${'Total cost:'.padEnd(15)} ${this.getGasTotal().cost.padStart(15)} ETH\n`, + hash: () => + ` > ${'transaction hash:'.padEnd(20)} ` + data.transactionHash, - deployed: () => ` > ${'contract address:'.padEnd(20)} ${data.receipt.contractAddress}\n` + - ` > ${'gas used:'.padEnd(20)} ${data.gas}\n` + - ` > ${'cost:'.padEnd(20)} ${data.cost} ETH\n` + - ` > ${'account:'.padEnd(20)} ${data.from}\n` + - ` > ${'balance:'.padEnd(20)} ${data.balance}\n`, + receipt: () => + ` > ${'gas usage:'.padEnd(20)} ` + data.gas, - listMany: () => ` * ${data.contractName}`, - hash: () => ` > ${'transaction hash:'.padEnd(20)}` + data.transactionHash, - receipt: () => ` > ${'gas usage:'.padEnd(20)}` + data.gas, - confirmation: () => ` > ${'confirmation number:'.padEnd(20)}` + data.num, + confirmation: () => + ` > ${'confirmation number:'.padEnd(20)} ` + data.num, } return kinds[kind](); From 3e62aace29ba8aa68bd6a4f62efb88d433cc4f98 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 16 May 2018 15:05:50 -0700 Subject: [PATCH 05/74] Remove unused code --- packages/truffle-deployer/src/actions/exec.js | 27 ------------ packages/truffle-deployer/src/linker.js | 41 ------------------- 2 files changed, 68 deletions(-) delete mode 100644 packages/truffle-deployer/src/actions/exec.js diff --git a/packages/truffle-deployer/src/actions/exec.js b/packages/truffle-deployer/src/actions/exec.js deleted file mode 100644 index b971a8cff0a..00000000000 --- a/packages/truffle-deployer/src/actions/exec.js +++ /dev/null @@ -1,27 +0,0 @@ -// var Require = require("truffle-exec"); -// var path = require("path"); -// -// module.exports = function(file, deployer) { -// return function() { -// if (path.isAbsolute(file) == false) { -// file = path.resolve(path.join(deployer.basePath, file)); -// } -// -// deployer.logger.log("Running " + file + "..."); -// // Evaluate any arguments if they're promises -// return new Promise(function(accept, reject) { -// Require.exec({ -// file: file, -// contracts: Object.keys(deployer.known_contracts).map(function(key) { -// return deployer.known_contracts[key]; -// }), -// network: deployer.network, -// network_id: deployer.network_id, -// provider: deployer.provider -// }, function(err) { -// if (err) return reject(err); -// accept(); -// }); -// }); -// }; -// }; diff --git a/packages/truffle-deployer/src/linker.js b/packages/truffle-deployer/src/linker.js index 311f1ede1e9..9b2788231ce 100644 --- a/packages/truffle-deployer/src/linker.js +++ b/packages/truffle-deployer/src/linker.js @@ -34,45 +34,4 @@ module.exports = { destination.link(library); }); }, - - // autolink: function(contract, available_contracts, logger) { - // // Abstract contract passed. - // if (contract.binary == null) return; - // - // var self = this; - // var regex = /__[^_]+_+/g; - // - // logger = logger || console; - // - // var unlinked_libraries = contract.unlinked_binary.match(regex); - // - // // Nothing to link. - // if (unlinked_libraries == null) return; - // - // if (unlinked_libraries.length == 0) { - // throw new Error("Cannot auto link " + contract.contract_name + "; " + contract.contract_name + " has no library dependencies.") - // } - // - // unlinked_libraries = unlinked_libraries.map(function(name) { - // // Remove underscores - // return name.replace(/_/g, ""); - // }).sort().filter(function(name, index, arr) { - // // Remove duplicates - // if (index + 1 >= arr.length) { - // return true; - // } - // - // return name != arr[index + 1]; - // }); - // - // unlinked_libraries.forEach(function(name) { - // var library = available_contracts[name]; - // - // if (library == null) { - // throw new Error("Cannot auto link " + contract.contract_name + "; " + contract.contract_name + " unknown dependency " + name + ".") - // } - // - // self.link(library, contract, logger); - // }); - // } }; From d6cab546f3f08426339ba4532bee4ffb3262c9aa Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 17 May 2018 17:55:55 -0700 Subject: [PATCH 06/74] Add test fixtures, deployer tests --- packages/truffle-deployer/package.json | 4 +- .../truffle-deployer/src/actions/deploy.js | 90 +++++---- packages/truffle-deployer/src/actions/new.js | 7 +- packages/truffle-deployer/src/linker.js | 13 +- packages/truffle-deployer/test/deployer.js | 185 ++++++++++++------ .../truffle-deployer/test/sources/Example.sol | 7 + .../test/sources/ExampleError.sol | 9 + .../test/sources/IsLibrary.sol | 9 + .../test/sources/Migrations.sol | 23 +++ .../test/sources/UsesExample.sol | 10 + .../test/sources/UsesLibrary.sol | 18 ++ packages/truffle-deployer/test/utils.js | 33 ++++ 12 files changed, 296 insertions(+), 112 deletions(-) create mode 100644 packages/truffle-deployer/test/sources/Example.sol create mode 100644 packages/truffle-deployer/test/sources/ExampleError.sol create mode 100644 packages/truffle-deployer/test/sources/IsLibrary.sol create mode 100644 packages/truffle-deployer/test/sources/Migrations.sol create mode 100644 packages/truffle-deployer/test/sources/UsesExample.sol create mode 100644 packages/truffle-deployer/test/sources/UsesLibrary.sol create mode 100644 packages/truffle-deployer/test/utils.js diff --git a/packages/truffle-deployer/package.json b/packages/truffle-deployer/package.json index 9a15a1dba01..48bceff668c 100644 --- a/packages/truffle-deployer/package.json +++ b/packages/truffle-deployer/package.json @@ -24,12 +24,14 @@ }, "homepage": "https://github.com/trufflesuite/truffle-deployer#readme", "dependencies": { - "truffle-contract": "^3.0.6", + "truffle-contract": "git+https://github.com/trufflesuite/truffle-contract.git#8f90e82bb3e22e2da6b493d8edd217fb49d8906c", "truffle-expect": "^0.0.4" }, "devDependencies": { + "fs-extra": "^6.0.1", "ganache-cli": "6.1.0-beta.4", "mocha": "5.2.0", + "truffle-workflow-compile": "^1.0.3", "web3": "1.0.0-beta.33" }, "publishConfig": { diff --git a/packages/truffle-deployer/src/actions/deploy.js b/packages/truffle-deployer/src/actions/deploy.js index 506e288ec53..e8933439503 100644 --- a/packages/truffle-deployer/src/actions/deploy.js +++ b/packages/truffle-deployer/src/actions/deploy.js @@ -1,42 +1,56 @@ -module.exports = function(contract, args, deployer) { - return function() { - var should_deploy = true; + +const canOverwrite = function(args, isDeployed){ + const lastArg = args[args.length - 1]; + const isObject = typeof lastArg === "object"; + + const overwrite = isObject && + isDeployed && + lastArg.overwrite === false; + + isObject && delete lastArg.overwrite; + return !overwrite; +} + +const deploy = function(contract, args, deployer) { + return async function() { + let instance; + let shouldDeploy = true; + const isDeployed = contract.isDeployed(); // First detect the network to see if it's deployed. - return contract.detectNetwork().then(function() { - // Evaluate any arguments if they're promises (we need to do this in all cases to maintain consistency) - return Promise.all(args) - }).then(function(new_args) { - // Check the last argument. If it's an object that tells us not to overwrite, then lets not. - if (new_args.length > 0) { - var last_arg = new_args[new_args.length - 1]; - if (typeof last_arg == "object" && last_arg.overwrite === false && contract.isDeployed()) { - should_deploy = false; - } - delete last_arg.overwrite; - } - - if (should_deploy == true) { - var prefix = "Deploying "; - if (contract.isDeployed()) { - prefix = "Replacing "; - } - deployer.logger.log(prefix + contract.contract_name + "..."); - return contract.new.apply(contract, new_args); - } else { - return contract.deployed(); - } - }).then(function(instance) { - if (should_deploy == true) { - deployer.logger.log(contract.contract_name + ": " + instance.address); - } else { - deployer.logger.log("Didn't deploy " + contract.contract_name + "; using " + instance.address); - } - - // Ensure the address and tx-hash are set on the contract. - contract.address = instance.address; - contract.transactionHash = instance.transactionHash; - return instance; - }); + await contract.detectNetwork(); + + // Evaluate any arguments if they're promises + // (we need to do this in all cases to maintain consistency) + const newArgs = await Promise.all(args); + + // Check the last argument. If it's an object that tells us not to overwrite, then lets not. + if (newArgs.length > 0) { + shouldDeploy = canOverwrite(newArgs, isDeployed); + } + + if (shouldDeploy) { + let prefix; + (isDeployed) + ? prefix = "Deploying " + : prefix = "Replacing "; + + deployer.logger.log(prefix + contract.contract_name + "..."); + instance = await contract.new.apply(contract, newArgs); + } else { + instance = await contract.deployed(); + } + + (shouldDeploy) + ? deployer.logger.log(contract.contract_name + ": " + instance.address) + : deployer.logger.log("Didn't deploy " + contract.contract_name + "; using " + instance.address); + + + // Ensure the address and tx-hash are set on the contract. + contract.address = instance.address; + contract.transactionHash = instance.transactionHash; + return instance; }; }; + +module.exports = deploy; diff --git a/packages/truffle-deployer/src/actions/new.js b/packages/truffle-deployer/src/actions/new.js index 037c705859f..a43623bcc7b 100644 --- a/packages/truffle-deployer/src/actions/new.js +++ b/packages/truffle-deployer/src/actions/new.js @@ -1,9 +1,8 @@ module.exports = function(contract, args, deployer) { - return function() { + return async function() { deployer.logger.log("Creating new instance of " + contract.contract_name); // Evaluate any arguments if they're promises - return Promise.all(args).then(function(new_args) { - return contract.new.apply(contract, args) - }); + const newArgs = await Promise.all(args) + return contract.new.apply(contract, args); }; }; diff --git a/packages/truffle-deployer/src/linker.js b/packages/truffle-deployer/src/linker.js index 9b2788231ce..41a2c3b7dc1 100644 --- a/packages/truffle-deployer/src/linker.js +++ b/packages/truffle-deployer/src/linker.js @@ -1,6 +1,7 @@ module.exports = { link: function(library, destinations, logger) { - var self = this; + const self = this; + let hasAddress; logger = logger || console; @@ -12,14 +13,10 @@ module.exports = { throw new Error("Cannot link a library with no name."); } - var hasAddress = false; - // Abstractions; don't want to use .address directly because it will throw. - if (typeof library.isDeployed) { - hasAddress = library.isDeployed(); - } else { - hasAddress = library.address != null; - } + (typeof library.isDeployed) + ? hasAddress = library.isDeployed() + : hasAddress = library.address != null; if (!hasAddress) { throw new Error("Cannot link library: " + library.contract_name + " has no address. Has it been deployed?"); diff --git a/packages/truffle-deployer/test/deployer.js b/packages/truffle-deployer/test/deployer.js index 1dc5e897b99..de59bfbc98f 100644 --- a/packages/truffle-deployer/test/deployer.js +++ b/packages/truffle-deployer/test/deployer.js @@ -1,80 +1,143 @@ -var TestRPC = require("ganache-cli"); -var contract = require("truffle-contract"); -var Deployer = require("../index"); -var Web3 = require("web3"); -var assert = require("assert"); +const ganache = require("ganache-cli"); +const contract = require("truffle-contract"); +const Web3 = require("web3"); +const assert = require("assert"); -var exec = require("../src/actions/exec"); +const Deployer = require("../index"); +const utils = require('./utils'); describe("deployer", function() { - var provider = TestRPC.provider(); - var web3 = new Web3(provider); - - // This is the solidity code for the example below: - // - // pragma solidity ^0.4.2; - // - // contract Example { - // uint public value; - // function Example(uint val) { - // value = val; - // } - // } - var bytecode = "0x6060604052346000576040516020806100a083398101604052515b60008190555b505b6070806100306000396000f300606060405263ffffffff60e060020a6000350416633fa4f24581146022575b6000565b34600057602c603e565b60408051918252519081900360200190f35b600054815600a165627a7a72305820dfffdf45e86020a86e43daa92ec94d27d0aeb23dd72888379769a5a35656dc7d0029"; - var abi = [{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"val","type":"uint256"}],"payable":false,"type":"constructor"}]; - - var network_id; - var accounts; - - before("get network id", function() { - return web3 - .eth - .net - .getId() - .then(id => network_id = id); - }); + let owner + let options; + let networkId; + const provider = ganache.provider(); + const web3 = new Web3(provider); + + beforeEach(async function() { + networkId = await web3.eth.net.getId(); + const accounts = await web3.eth.getAccounts(); + + owner = accounts[0]; + options = { + contracts: null, + network: 'test', + network_id: networkId, + provider: provider + } - before("get accounts", function() { - return web3 - .eth - .getAccounts() - .then(accs => accounts = accs) + await utils.compile(); }); - it("deploys a contract correctly", function() { - var Example = contract({ - contract_name: "Example", - unlinked_binary: bytecode, - abi: abi + afterEach(() => utils.cleanUp()); + + describe('sync', function(){ + + it("deploy()", async function() { + const Example = utils.getContract('Example', provider, networkId, owner); + options.contracts = [ Example ]; + + const deployer = new Deployer(options); + + const migrate = function(){ + deployer.deploy(Example); + }; + + migrate(); + + await deployer.start(); + + assert(Example.address !== null); + assert(Example.transactionHash !== null); + + example = await Example.deployed(); + assert(await example.id() === 'Example' ); }); - Example.setProvider(provider); - var deployer = new Deployer({ - contracts: [Example], - network: "test", - network_id: network_id, - provider: provider + it('deploy().then', async function(){ + const Example = utils.getContract('Example', provider, networkId, owner); + const UsesExample = utils.getContract('UsesExample', provider, networkId, owner); + + options.contracts = [ Example, UsesExample ]; + + const deployer = new Deployer(options); + + const migrate = function(){ + deployer.deploy(Example).then(function() { + return deployer.deploy(UsesExample, Example.address); + }); + }; + + migrate(); + await deployer.start(); + + const example = await Example.deployed(); + const usesExample = await UsesExample.deployed(); + + assert(Example.address !== null); + + assert(await example.id() === 'Example' ); + assert(await usesExample.id() === 'UsesExample' ); + + assert(await usesExample.other() === Example.address); }); - var errored = false; + it('deployer.then', async function(){ + const Example = utils.getContract('Example', provider, networkId, owner); + const UsesExample = utils.getContract('UsesExample', provider, networkId, owner); - try { - var address = Example.address; - } catch(e) { - errored = true; - } + options.contracts = [ Example, UsesExample ]; + + const deployer = new Deployer(options); + + const migrate = function(){ + deployer.then(async function(){ + const example = await deployer.deploy(Example); + await deployer.deploy(UsesExample, example.address); + }) + }; + + migrate(); + await deployer.start(); - assert(errored, "Precondition: Example shouldn't have an address") + const example = await Example.deployed(); + const usesExample = await UsesExample.deployed(); - Example.defaults({ - gas: 3141592, - from: accounts[0] + assert(Example.address !== null); + + assert(await example.id() === 'Example' ); + assert(await usesExample.id() === 'UsesExample' ); + assert(await usesExample.other() === Example.address); }); - deployer.deploy(Example, 1); + it('deployer.link', async function(){ + const UsesLibrary = utils.getContract('UsesLibrary', provider, networkId, owner); + const IsLibrary = utils.getContract('IsLibrary', provider, networkId, owner); + options.contracts = [ UsesLibrary, IsLibrary ]; + + const deployer = new Deployer(options); + + const migrate = function(){ + deployer.deploy(IsLibrary); + deployer.link(IsLibrary, UsesLibrary); + deployer.deploy(UsesLibrary); + }; + + migrate(); + + await deployer.start(); + + assert(UsesLibrary.address !== null); + assert(IsLibrary.address !== null); + + const usesLibrary = await UsesLibrary.deployed(); + await usesLibrary.fireIsLibraryEvent(5); + await usesLibrary.fireUsesLibraryEvent(7); + + eventOptions = {fromBlock: 0, toBlock: 'latest'}; + const events = await usesLibrary.getPastEvents("allEvents", eventOptions); - return deployer.start().then(function() { - assert.notEqual(Example.address, null); + assert(events[0].args.eventID === '5'); + assert(events[1].args.eventID === '7'); }); }); }); diff --git a/packages/truffle-deployer/test/sources/Example.sol b/packages/truffle-deployer/test/sources/Example.sol new file mode 100644 index 00000000000..9ea1e534f32 --- /dev/null +++ b/packages/truffle-deployer/test/sources/Example.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.4; + + +contract Example { + string public id = 'Example'; + constructor() public {} +} diff --git a/packages/truffle-deployer/test/sources/ExampleError.sol b/packages/truffle-deployer/test/sources/ExampleError.sol new file mode 100644 index 00000000000..e512e6fe74a --- /dev/null +++ b/packages/truffle-deployer/test/sources/ExampleError.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.4.4; + + +contract ExampleError { + string public id = 'ExampleError'; + constructor() public { + require(false); + } +} diff --git a/packages/truffle-deployer/test/sources/IsLibrary.sol b/packages/truffle-deployer/test/sources/IsLibrary.sol new file mode 100644 index 00000000000..6b4489dd179 --- /dev/null +++ b/packages/truffle-deployer/test/sources/IsLibrary.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.4.4; + +library IsLibrary { + event IsLibraryEvent(uint eventID); + + function fireIsLibraryEvent(uint id) public { + emit IsLibraryEvent(id); + } +} \ No newline at end of file diff --git a/packages/truffle-deployer/test/sources/Migrations.sol b/packages/truffle-deployer/test/sources/Migrations.sol new file mode 100644 index 00000000000..027c2c32b85 --- /dev/null +++ b/packages/truffle-deployer/test/sources/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.17; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + modifier restricted() { + if (msg.sender == owner) _; + } + + constructor() public { + owner = msg.sender; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/packages/truffle-deployer/test/sources/UsesExample.sol b/packages/truffle-deployer/test/sources/UsesExample.sol new file mode 100644 index 00000000000..ba502c3d363 --- /dev/null +++ b/packages/truffle-deployer/test/sources/UsesExample.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.4; + + +contract UsesExample { + string public id = 'UsesExample'; + address public other; + constructor(address _other) public { + other = _other; + } +} diff --git a/packages/truffle-deployer/test/sources/UsesLibrary.sol b/packages/truffle-deployer/test/sources/UsesLibrary.sol new file mode 100644 index 00000000000..6e7ba6388c6 --- /dev/null +++ b/packages/truffle-deployer/test/sources/UsesLibrary.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.4.4; + +import "./IsLibrary.sol"; + +contract UsesLibrary { + + event UsesLibraryEvent(uint eventID); + + constructor() public {} + + function fireIsLibraryEvent(uint id) public { + IsLibrary.fireIsLibraryEvent(id); + } + + function fireUsesLibraryEvent(uint id) public { + emit UsesLibraryEvent(id); + } +} diff --git a/packages/truffle-deployer/test/utils.js b/packages/truffle-deployer/test/utils.js new file mode 100644 index 00000000000..f49ed7fd9e2 --- /dev/null +++ b/packages/truffle-deployer/test/utils.js @@ -0,0 +1,33 @@ +const TruffleContract = require('truffle-contract'); +const workflow = require('truffle-workflow-compile'); +const path = require('path'); +const fs = require('fs-extra'); + +const utils = { + buildDir: path.join(__dirname, './build'), + sourcesDir: path.join(__dirname, './sources'), + + compile: async function(){ + const config = { + contracts_build_directory: utils.buildDir, + contracts_directory: utils.sourcesDir + } + + return new Promise((accept, reject) => { + workflow.compile(config, err => err ? reject(err) : accept()); + }) + }, + + cleanUp: () => fs.removeSync(utils.buildDir), + + getContract: function(name, provider, networkId, account){ + const json = require(`./build/${name}`); + const contract = TruffleContract(json) + contract.setProvider(provider); + contract.setNetwork(networkId); + contract.defaults({ from: account }); + return contract; + }, +}; + +module.exports = utils; From 9c71d07264a5428a56760217a7aa6c996409a616 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 17 May 2018 19:20:09 -0700 Subject: [PATCH 07/74] Convert deployer to class --- packages/truffle-deployer/index.js | 127 ++++++++++++++--------------- 1 file changed, 61 insertions(+), 66 deletions(-) diff --git a/packages/truffle-deployer/index.js b/packages/truffle-deployer/index.js index 075b935f9df..623add8368a 100644 --- a/packages/truffle-deployer/index.js +++ b/packages/truffle-deployer/index.js @@ -5,80 +5,75 @@ var deployMany = require("./src/actions/deploymany"); var link = require("./src/actions/link"); var create = require("./src/actions/new"); -function Deployer(options) { - var self = this; - options = options || {}; - - expect.options(options, [ - "provider", - "network", - "network_id" - ]); - - this.chain = new DeferredChain(); - this.logger = options.logger || {log: function() {}}; - this.known_contracts = {}; - (options.contracts || []).forEach(function(contract) { - self.known_contracts[contract.contract_name] = contract; - }); - this.network = options.network; - this.network_id = options.network_id; - this.provider = options.provider; - this.basePath = options.basePath || process.cwd(); -}; - -// Note: In all code below we overwrite this.chain every time .then() is used -// in order to ensure proper error processing. - -Deployer.prototype.start = function() { - return this.chain.start(); -}; - -Deployer.prototype.link = function(library, destinations) { - return this.queueOrExec(link(library, destinations, this)); -}; - -Deployer.prototype.deploy = function() { - var args = Array.prototype.slice.call(arguments); - var contract = args.shift(); - - if (Array.isArray(contract)) { - return this.queueOrExec(deployMany(contract, this)); - } else { - return this.queueOrExec(deploy(contract, args, this)); +class Deployer { + constructor(options){ + options = options || {}; + expect.options(options, [ + "provider", + "network", + "network_id" + ]); + + this.chain = new DeferredChain(); + this.logger = options.logger || {log: function() {}}; + this.known_contracts = {}; + + (options.contracts || []) + .forEach(contract => this.known_contracts[contract.contract_name] = contract); + + this.network = options.network; + this.network_id = options.network_id; + this.provider = options.provider; + this.basePath = options.basePath || process.cwd(); } -}; -Deployer.prototype.new = function() { - var args = Array.prototype.slice.call(arguments); - var contract = args.shift(); + // Note: In all code below we overwrite this.chain every time .then() is used + // in order to ensure proper error processing. + start() { + return this.chain.start() + } + + link(library, destinations){ + return this.queueOrExec(link(library, destinations, this)) + } - return this.queueOrExec(create(contract, args, this)); -}; -Deployer.prototype.exec = function(file) { - throw new Error("deployer.exec() has been deprecated; please seen the truffle-require package for integration.") -}; + deploy() { + const args = Array.prototype.slice.call(arguments); + const contract = args.shift(); + + return (Array.isArray(contract)) + ? this.queueOrExec(deployMany(contract, this)) + : this.queueOrExec(deploy(contract, args, this)); + } -Deployer.prototype.then = function(fn) { - var self = this; + new() { + const args = Array.prototype.slice.call(arguments); + const contract = args.shift(); - return this.queueOrExec(function() { - self.logger.log("Running step..."); - return fn(this); - }); -}; + return this.queueOrExec(create(contract, args, this)); + } + + then(fn) { + var self = this; + + return this.queueOrExec(function() { + self.logger.log("Running step..."); + return fn(this); + }); + } -Deployer.prototype.queueOrExec = function(fn) { - var self = this; + queueOrExec(fn){ + var self = this; - if (this.chain.started == true) { - return new Promise(function(accept, reject) { - accept(); - }).then(fn); - } else { - return this.chain.then(fn); + if (this.chain.started == true) { + return new Promise(function(accept, reject) { + accept(); + }).then(fn); + } else { + return this.chain.then(fn); + } } -}; +} module.exports = Deployer; From 9b3d10b6cae64c5e48191c34035cd7d45c70b32a Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 17 May 2018 20:33:29 -0700 Subject: [PATCH 08/74] Convert DeferredChain to class --- .../truffle-deployer/src/deferredchain.js | 88 +++++++++---------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/packages/truffle-deployer/src/deferredchain.js b/packages/truffle-deployer/src/deferredchain.js index b808d43bb7a..fddb0962cfe 100644 --- a/packages/truffle-deployer/src/deferredchain.js +++ b/packages/truffle-deployer/src/deferredchain.js @@ -1,47 +1,45 @@ -function DeferredChain() { - var self = this; - this.chain = new Promise(function(accept, reject) { - self._accept = accept; - self._reject = reject; - }); - - this.await = new Promise(function() { - self._done = arguments[0]; - self._error = arguments[1]; - }); - this.started = false; -}; - -DeferredChain.prototype.then = function(fn) { - var self = this; - this.chain = this.chain.then(function() { - var args = Array.prototype.slice.call(arguments); - - return fn.apply(null, args); - }); - this.chain = this.chain.catch(function(e) { - self._error(e); - }); - - return this; -}; - -DeferredChain.prototype.catch = function(fn) { - var self = this; - this.chain = this.chain.catch(function() { - var args = Array.prototype.slice.call(arguments); - - return fn.apply(null, args); - }); - - return this; -}; - -DeferredChain.prototype.start = function() { - this.started = true; - this.chain = this.chain.then(this._done); - this._accept(); - return this.await; -}; +class DeferredChain { + constructor(){ + const self = this; + + this.chain = new Promise(function(accept, reject){ + self._accept = accept; + self._reject = reject; + }); + + this.await = new Promise(function(){ + self._done = arguments[0]; + self._error = arguments[1]; + }); + + this.started = false; + } + + then(fn){ + this.chain = this.chain.then(() => { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, args); + }); + + this.chain = this.chain.catch(e => this._error(e)); + return this; + } + + catch(fn){ + this.chain = this.chain.catch(() => { + var args = Array.prototype.slice.call(arguments); + return fn.apply(null, args); + }); + + return this; + } + + start(){ + this.started = true; + this.chain = this.chain.then(this._done); + this._accept(); + return this.await; + } +} module.exports = DeferredChain; From 995f540b7771ff57c02b1f9045ca48496a94dabc Mon Sep 17 00:00:00 2001 From: cgewecke Date: Fri, 18 May 2018 15:43:37 -0700 Subject: [PATCH 09/74] Add async/await test cases --- packages/truffle-deployer/index.js | 10 +- .../truffle-deployer/src/actions/deploy.js | 13 +- .../truffle-deployer/test/deployerAwait.js | 113 ++++++++++++++++++ .../test/{deployer.js => deployerClassic.js} | 2 +- 4 files changed, 129 insertions(+), 9 deletions(-) create mode 100644 packages/truffle-deployer/test/deployerAwait.js rename packages/truffle-deployer/test/{deployer.js => deployerClassic.js} (98%) diff --git a/packages/truffle-deployer/index.js b/packages/truffle-deployer/index.js index 623add8368a..c43d6849855 100644 --- a/packages/truffle-deployer/index.js +++ b/packages/truffle-deployer/index.js @@ -66,13 +66,9 @@ class Deployer { queueOrExec(fn){ var self = this; - if (this.chain.started == true) { - return new Promise(function(accept, reject) { - accept(); - }).then(fn); - } else { - return this.chain.then(fn); - } + return (this.chain.started == true) + ? new Promise(accept => accept()).then(fn) + : this.chain.then(fn); } } diff --git a/packages/truffle-deployer/src/actions/deploy.js b/packages/truffle-deployer/src/actions/deploy.js index e8933439503..5571e78c7e3 100644 --- a/packages/truffle-deployer/src/actions/deploy.js +++ b/packages/truffle-deployer/src/actions/deploy.js @@ -36,7 +36,18 @@ const deploy = function(contract, args, deployer) { : prefix = "Replacing "; deployer.logger.log(prefix + contract.contract_name + "..."); - instance = await contract.new.apply(contract, newArgs); + const promiEvent = contract.new.apply(contract, newArgs) + + promiEvent + .on('transactionHash', function(hash){ + this.removeListener('transactionHash'); + }) + .on('confirmation', function(num, receipt){ + this.removeListener('confirmation'); + }); + + instance = await promiEvent; + } else { instance = await contract.deployed(); } diff --git a/packages/truffle-deployer/test/deployerAwait.js b/packages/truffle-deployer/test/deployerAwait.js new file mode 100644 index 00000000000..92ee9377e62 --- /dev/null +++ b/packages/truffle-deployer/test/deployerAwait.js @@ -0,0 +1,113 @@ +const ganache = require("ganache-cli"); +const contract = require("truffle-contract"); +const Web3 = require("web3"); +const assert = require("assert"); + +const Deployer = require("../index"); +const utils = require('./utils'); + +describe("deployer (async / await)", function() { + let owner + let options; + let networkId; + const provider = ganache.provider(); + const web3 = new Web3(provider); + + beforeEach(async function() { + networkId = await web3.eth.net.getId(); + const accounts = await web3.eth.getAccounts(); + + owner = accounts[0]; + options = { + contracts: null, + network: 'test', + network_id: networkId, + provider: provider + } + + await utils.compile(); + }); + + afterEach(() => utils.cleanUp()); + + describe('sync', function(){ + + it("single deploy()", async function() { + const Example = utils.getContract('Example', provider, networkId, owner); + options.contracts = [ Example ]; + + const deployer = new Deployer(options); + + const migrate = async function(){ + await deployer.deploy(Example); + }; + + await deployer.start() + await deployer.then(migrate); + + assert(Example.address !== null); + assert(Example.transactionHash !== null); + + example = await Example.deployed(); + assert(await example.id() === 'Example' ); + }); + + it('deploy() with interdependent contracts', async function(){ + const Example = utils.getContract('Example', provider, networkId, owner); + const UsesExample = utils.getContract('UsesExample', provider, networkId, owner); + + options.contracts = [ Example, UsesExample ]; + + const deployer = new Deployer(options); + + const migrate = async function(){ + await deployer.deploy(Example) + await deployer.deploy(UsesExample, Example.address); + + }; + + await deployer.start(); + await deployer.then(migrate); + + const example = await Example.deployed(); + const usesExample = await UsesExample.deployed(); + + assert(Example.address !== null); + + assert(await example.id() === 'Example' ); + assert(await usesExample.id() === 'UsesExample' ); + + assert(await usesExample.other() === Example.address); + }); + + it('deployer.link', async function(){ + const UsesLibrary = utils.getContract('UsesLibrary', provider, networkId, owner); + const IsLibrary = utils.getContract('IsLibrary', provider, networkId, owner); + options.contracts = [ UsesLibrary, IsLibrary ]; + + const deployer = new Deployer(options); + + const migrate = async function(){ + await deployer.deploy(IsLibrary); + await deployer.link(IsLibrary, UsesLibrary); + await deployer.deploy(UsesLibrary); + }; + + await deployer.start(); + await deployer.then(migrate); + + assert(UsesLibrary.address !== null); + assert(IsLibrary.address !== null); + + const usesLibrary = await UsesLibrary.deployed(); + await usesLibrary.fireIsLibraryEvent(5); + await usesLibrary.fireUsesLibraryEvent(7); + + eventOptions = {fromBlock: 0, toBlock: 'latest'}; + const events = await usesLibrary.getPastEvents("allEvents", eventOptions); + + assert(events[0].args.eventID === '5'); + assert(events[1].args.eventID === '7'); + }); + }); +}); \ No newline at end of file diff --git a/packages/truffle-deployer/test/deployer.js b/packages/truffle-deployer/test/deployerClassic.js similarity index 98% rename from packages/truffle-deployer/test/deployer.js rename to packages/truffle-deployer/test/deployerClassic.js index de59bfbc98f..fd1116d06d9 100644 --- a/packages/truffle-deployer/test/deployer.js +++ b/packages/truffle-deployer/test/deployerClassic.js @@ -6,7 +6,7 @@ const assert = require("assert"); const Deployer = require("../index"); const utils = require('./utils'); -describe("deployer", function() { +describe("deployer (classic)", function() { let owner let options; let networkId; From f53e932fc48c0a2d71668b57263467acbe8556dc Mon Sep 17 00:00:00 2001 From: cgewecke Date: Fri, 18 May 2018 19:27:37 -0700 Subject: [PATCH 10/74] First draft: async-eventemitter --- packages/truffle-deployer/index.js | 20 +++-- packages/truffle-deployer/package.json | 1 + .../truffle-deployer/src/actions/deploy.js | 90 ++++++++++++++----- packages/truffle-deployer/src/actions/link.js | 2 +- packages/truffle-deployer/src/linker.js | 32 ++++--- packages/truffle-deployer/src/reporter.js | 77 ++++++++++++++++ 6 files changed, 182 insertions(+), 40 deletions(-) create mode 100644 packages/truffle-deployer/src/reporter.js diff --git a/packages/truffle-deployer/index.js b/packages/truffle-deployer/index.js index c43d6849855..fe27b1c4ae0 100644 --- a/packages/truffle-deployer/index.js +++ b/packages/truffle-deployer/index.js @@ -1,12 +1,14 @@ -var expect = require("truffle-expect"); -var DeferredChain = require("./src/deferredchain"); -var deploy = require("./src/actions/deploy"); -var deployMany = require("./src/actions/deploymany"); -var link = require("./src/actions/link"); -var create = require("./src/actions/new"); - -class Deployer { +const expect = require("truffle-expect"); +const EventEmitter = require('async-eventemitter'); +const DeferredChain = require("./src/deferredchain"); +const deploy = require("./src/actions/deploy"); +const deployMany = require("./src/actions/deploymany"); +const link = require("./src/actions/link"); +const create = require("./src/actions/new"); + +class Deployer extends EventEmitter { constructor(options){ + super(); options = options || {}; expect.options(options, [ "provider", @@ -58,7 +60,7 @@ class Deployer { var self = this; return this.queueOrExec(function() { - self.logger.log("Running step..."); + self.emit('step', {}); return fn(this); }); } diff --git a/packages/truffle-deployer/package.json b/packages/truffle-deployer/package.json index 48bceff668c..9e647426115 100644 --- a/packages/truffle-deployer/package.json +++ b/packages/truffle-deployer/package.json @@ -24,6 +24,7 @@ }, "homepage": "https://github.com/trufflesuite/truffle-deployer#readme", "dependencies": { + "async-eventemitter": "^0.2.4", "truffle-contract": "git+https://github.com/trufflesuite/truffle-contract.git#8f90e82bb3e22e2da6b493d8edd217fb49d8906c", "truffle-expect": "^0.0.4" }, diff --git a/packages/truffle-deployer/src/actions/deploy.js b/packages/truffle-deployer/src/actions/deploy.js index 5571e78c7e3..c3f9581e58d 100644 --- a/packages/truffle-deployer/src/actions/deploy.js +++ b/packages/truffle-deployer/src/actions/deploy.js @@ -1,4 +1,12 @@ +// --------------------------------------- Utils --------------------------------------------------- + +/** + * Helper to parse a deploy statement's overwrite option + * @param {Arry} args arguments passed to deploy + * @param {Boolean} isDeployed is contract deployed? + * @return {Boolean} true if overwrite is ok + */ const canOverwrite = function(args, isDeployed){ const lastArg = args[args.length - 1]; const isObject = typeof lastArg === "object"; @@ -11,51 +19,93 @@ const canOverwrite = function(args, isDeployed){ return !overwrite; } +/** + * Wrapper that transforms instance events for txHash, confirmation, etc + * into Deployer async-eventemitter events and manages confirmation delay. + * @param {PromiEvent} promiEvent return value of contract.new + * @param {Object} state to collect data (receipt) + */ +const handleContractEvents = function(promiEvent, state, deployer){ + promiEvent + .on('transactionHash', function(hash){ + deployer.emit('transactionHash', { + transactionHash: hash + }); + this.removeListener('transactionHash'); + }) + + .on('receipt', function(receipt){ + state.receipt = receipt; + }) + + .on('confirmation', function(num, receipt){ + deployer.emit('confirmation', { + num: num, + receipt: receipt + }); + // TO DO: add confirmation logic + this.removeListener('confirmation'); + }); +} + +// -------------------------------------- Deploy --------------------------------------------------- + +/** + * Deploys an instance. + * @param {TruffleContract} contract contract to deploy + * @param {Array} args (constructor args and options) + * @param {Deployer} deployer [description] + * @return {Promise} resolves an instance + */ const deploy = function(contract, args, deployer) { return async function() { let instance; + let state = {}; let shouldDeploy = true; const isDeployed = contract.isDeployed(); - // First detect the network to see if it's deployed. await contract.detectNetwork(); // Evaluate any arguments if they're promises // (we need to do this in all cases to maintain consistency) const newArgs = await Promise.all(args); - // Check the last argument. If it's an object that tells us not to overwrite, then lets not. + // Check the last argument. If it's an object that tells us not to overwrite, + // then lets not. if (newArgs.length > 0) { shouldDeploy = canOverwrite(newArgs, isDeployed); } if (shouldDeploy) { - let prefix; - (isDeployed) - ? prefix = "Deploying " - : prefix = "Replacing "; + deployer.emit('preDeploy', { + contract: contract, + deployed: isDeployed + }); - deployer.logger.log(prefix + contract.contract_name + "..."); - const promiEvent = contract.new.apply(contract, newArgs) + const promiEvent = contract.new.apply(contract, newArgs); + handleContractEvents(promiEvent, state, deployer); - promiEvent - .on('transactionHash', function(hash){ - this.removeListener('transactionHash'); - }) - .on('confirmation', function(num, receipt){ - this.removeListener('confirmation'); - }); + try { + instance = await promiEvent; + } catch(error){ + deployer.emit('deployFailed', error); + // Should this be conditional?? + throw new Error(); + } - instance = await promiEvent; + // TODO: Idle here, waiting for + // the min confirmations to click past } else { instance = await contract.deployed(); } - (shouldDeploy) - ? deployer.logger.log(contract.contract_name + ": " + instance.address) - : deployer.logger.log("Didn't deploy " + contract.contract_name + "; using " + instance.address); - + deployer.emit('postDeploy', { + contract: contract, + instance: instance, + deployed: shouldDeploy, + receipt: state.receipt + }); // Ensure the address and tx-hash are set on the contract. contract.address = instance.address; diff --git a/packages/truffle-deployer/src/actions/link.js b/packages/truffle-deployer/src/actions/link.js index ce8d10639c0..90912ef10a4 100644 --- a/packages/truffle-deployer/src/actions/link.js +++ b/packages/truffle-deployer/src/actions/link.js @@ -2,6 +2,6 @@ var Linker = require("../linker"); module.exports = function(library, destinations, deployer) { return function() { - Linker.link(library, destinations, deployer.logger); + Linker.link(library, destinations, deployer); }; }; diff --git a/packages/truffle-deployer/src/linker.js b/packages/truffle-deployer/src/linker.js index 41a2c3b7dc1..03b5507ce53 100644 --- a/packages/truffle-deployer/src/linker.js +++ b/packages/truffle-deployer/src/linker.js @@ -1,33 +1,45 @@ module.exports = { - link: function(library, destinations, logger) { - const self = this; - let hasAddress; - - logger = logger || console; + link: function(library, destinations, deployer) { if (!Array.isArray(destinations)) { destinations = [destinations]; } if (library.contract_name == null) { - throw new Error("Cannot link a library with no name."); + deployer.emit('error', { + type: 'noLibName' + }); + throw new Error(); } // Abstractions; don't want to use .address directly because it will throw. + let hasAddress; + (typeof library.isDeployed) ? hasAddress = library.isDeployed() : hasAddress = library.address != null; if (!hasAddress) { - throw new Error("Cannot link library: " + library.contract_name + " has no address. Has it been deployed?"); + deployer.emit('error', { + type: 'noLibAddress', + contract: library + }); + + throw new Error(); } destinations.forEach(function(destination) { // Don't link if result will have no effect. - if (destination.links[library.contract_name] == library.address) return; // already linked to same address - if (destination.unlinked_binary.indexOf(library.contract_name) < 0) return; // no linkage available + const alreadyLinked = (destination.links[library.contract_name] == library.address); + const noLinkage = (destination.unlinked_binary.indexOf(library.contract_name) < 0); + + if (alreadyLinked || noLinkage) return; + + deployer.emit('linking', { + library: library, + destination: destination + }); - logger.log("Linking " + library.contract_name + " to " + destination.contract_name); destination.link(library); }); }, diff --git a/packages/truffle-deployer/src/reporter.js b/packages/truffle-deployer/src/reporter.js new file mode 100644 index 00000000000..9448d985b88 --- /dev/null +++ b/packages/truffle-deployer/src/reporter.js @@ -0,0 +1,77 @@ +/** + * Example logger class that emulates the classic logger behavior + * and allows for optional async handling for tests + */ + +class Logger { + constructor(deployer, async){ + (async) + ? deployer.setAsyncHandler(this.asyncHandler) + : deployer.setAsyncHandler(null); + + this.listen(); + } + + listen(){ + deployer.on('preDeploy', this.preDeploy); + deployer.on('postDeploy', this.postDeploy); + deployer.on('linking', this.linking); + deployer.on('error', this.error); + deployer.on('transactionHash', this.hash); + deployer.on('confirmation', this.confirmation); + deployer.on('step', this.step); + } + + async asyncHandler(err, name, ...args){ + + } + + preDeploy(contract, deployed){ + let message; + (deployed) + ? message = this.messages('replacing', contract) + : message = this.messages('deploying', contract); + + deployer.logger.log(message); + }) + + postDeploy(contract, instance, deployed){ + let message; + (deployed) + ? message = this.messages('deployed', contract, instance) + : message = this.messages('notDeployed', contract, instance); + + deployer.logger.log(message); + }) + + linking(library, destination){ + let message = this.messages('linking', library, destination); + deployer.logger.log(message); + }) + + step(){ + let message = this.messages('step'); + deployer.logger.log(message); + } + + error(type, contract){ + let message = this.messages(type, contract); + deployer.logger.log(message); + }) + + transactionHash(){} + confirmation(){} + + messages(kind, ...args){ + const kinds = { + noLibName: `Link error: Cannot link a library with no name.`, + noLibAddress: `Link error: ${library.contract_name} has no address. Has it been deployed?` + deploying: `Deploying ${args[0].contract_name}...`, + replacing: `Replacing ${args[0].contract_name}...`, + deployed: `${args[0].contract_name}: ${args[1].address}`, + notDeployed: `Didn't deploy ${args[0].contract_name}; using ${args[1].address}`, + linking: `Linking ${args[0].contract_name} ${args[1].contract_name}` + } + return kinds[kind]; + } +} \ No newline at end of file From 2d15b92931fdc81ef419b9eb5ac464d9fe00b04c Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 23 May 2018 19:35:31 -0700 Subject: [PATCH 11/74] Second draft: reporters / working --- packages/truffle-deployer/index.js | 34 +-- packages/truffle-deployer/package.json | 2 +- .../truffle-deployer/src/actions/deploy.js | 117 --------- packages/truffle-deployer/src/deployment.js | 227 ++++++++++++++++++ packages/truffle-deployer/src/linker.js | 37 +-- packages/truffle-deployer/src/reporter.js | 77 ------ .../truffle-deployer/test/deployerAwait.js | 116 ++++----- .../truffle-deployer/test/deployerClassic.js | 160 ++++++------ .../test/reporters/asyncReporter.js | 37 +++ .../test/reporters/syncReporter.js | 78 ++++++ packages/truffle-deployer/test/utils.js | 12 + 11 files changed, 538 insertions(+), 359 deletions(-) delete mode 100644 packages/truffle-deployer/src/actions/deploy.js create mode 100644 packages/truffle-deployer/src/deployment.js delete mode 100644 packages/truffle-deployer/src/reporter.js create mode 100644 packages/truffle-deployer/test/reporters/asyncReporter.js create mode 100644 packages/truffle-deployer/test/reporters/syncReporter.js diff --git a/packages/truffle-deployer/index.js b/packages/truffle-deployer/index.js index fe27b1c4ae0..2b44e743448 100644 --- a/packages/truffle-deployer/index.js +++ b/packages/truffle-deployer/index.js @@ -1,14 +1,13 @@ const expect = require("truffle-expect"); -const EventEmitter = require('async-eventemitter'); +const Emittery = require('emittery'); const DeferredChain = require("./src/deferredchain"); -const deploy = require("./src/actions/deploy"); -const deployMany = require("./src/actions/deploymany"); +const Deployment = require("./src/deployment"); const link = require("./src/actions/link"); const create = require("./src/actions/new"); -class Deployer extends EventEmitter { +class Deployer extends Deployment { + constructor(options){ - super(); options = options || {}; expect.options(options, [ "provider", @@ -16,17 +15,20 @@ class Deployer extends EventEmitter { "network_id" ]); + const emitter = new Emittery(); + super(emitter); + + this.emitter = emitter; this.chain = new DeferredChain(); this.logger = options.logger || {log: function() {}}; - this.known_contracts = {}; - - (options.contracts || []) - .forEach(contract => this.known_contracts[contract.contract_name] = contract); - this.network = options.network; this.network_id = options.network_id; this.provider = options.provider; this.basePath = options.basePath || process.cwd(); + this.known_contracts = {}; + + (options.contracts || []) + .forEach(contract => this.known_contracts[contract.contract_name] = contract); } // Note: In all code below we overwrite this.chain every time .then() is used @@ -45,8 +47,8 @@ class Deployer extends EventEmitter { const contract = args.shift(); return (Array.isArray(contract)) - ? this.queueOrExec(deployMany(contract, this)) - : this.queueOrExec(deploy(contract, args, this)); + ? this.queueOrExec(this._deployMany(contract, this)) + : this.queueOrExec(this._deploy(contract, args, this)); } new() { @@ -59,8 +61,7 @@ class Deployer extends EventEmitter { then(fn) { var self = this; - return this.queueOrExec(function() { - self.emit('step', {}); + return this.queueOrExec(function(){ return fn(this); }); } @@ -72,6 +73,11 @@ class Deployer extends EventEmitter { ? new Promise(accept => accept()).then(fn) : this.chain.then(fn); } + + finish(){ + this.emitter.clearListeners(); + this._close(); + } } module.exports = Deployer; diff --git a/packages/truffle-deployer/package.json b/packages/truffle-deployer/package.json index 9e647426115..eb238731ff4 100644 --- a/packages/truffle-deployer/package.json +++ b/packages/truffle-deployer/package.json @@ -24,7 +24,7 @@ }, "homepage": "https://github.com/trufflesuite/truffle-deployer#readme", "dependencies": { - "async-eventemitter": "^0.2.4", + "emittery": "^0.3.0", "truffle-contract": "git+https://github.com/trufflesuite/truffle-contract.git#8f90e82bb3e22e2da6b493d8edd217fb49d8906c", "truffle-expect": "^0.0.4" }, diff --git a/packages/truffle-deployer/src/actions/deploy.js b/packages/truffle-deployer/src/actions/deploy.js deleted file mode 100644 index c3f9581e58d..00000000000 --- a/packages/truffle-deployer/src/actions/deploy.js +++ /dev/null @@ -1,117 +0,0 @@ - -// --------------------------------------- Utils --------------------------------------------------- - -/** - * Helper to parse a deploy statement's overwrite option - * @param {Arry} args arguments passed to deploy - * @param {Boolean} isDeployed is contract deployed? - * @return {Boolean} true if overwrite is ok - */ -const canOverwrite = function(args, isDeployed){ - const lastArg = args[args.length - 1]; - const isObject = typeof lastArg === "object"; - - const overwrite = isObject && - isDeployed && - lastArg.overwrite === false; - - isObject && delete lastArg.overwrite; - return !overwrite; -} - -/** - * Wrapper that transforms instance events for txHash, confirmation, etc - * into Deployer async-eventemitter events and manages confirmation delay. - * @param {PromiEvent} promiEvent return value of contract.new - * @param {Object} state to collect data (receipt) - */ -const handleContractEvents = function(promiEvent, state, deployer){ - promiEvent - .on('transactionHash', function(hash){ - deployer.emit('transactionHash', { - transactionHash: hash - }); - this.removeListener('transactionHash'); - }) - - .on('receipt', function(receipt){ - state.receipt = receipt; - }) - - .on('confirmation', function(num, receipt){ - deployer.emit('confirmation', { - num: num, - receipt: receipt - }); - // TO DO: add confirmation logic - this.removeListener('confirmation'); - }); -} - -// -------------------------------------- Deploy --------------------------------------------------- - -/** - * Deploys an instance. - * @param {TruffleContract} contract contract to deploy - * @param {Array} args (constructor args and options) - * @param {Deployer} deployer [description] - * @return {Promise} resolves an instance - */ -const deploy = function(contract, args, deployer) { - return async function() { - let instance; - let state = {}; - let shouldDeploy = true; - const isDeployed = contract.isDeployed(); - - await contract.detectNetwork(); - - // Evaluate any arguments if they're promises - // (we need to do this in all cases to maintain consistency) - const newArgs = await Promise.all(args); - - // Check the last argument. If it's an object that tells us not to overwrite, - // then lets not. - if (newArgs.length > 0) { - shouldDeploy = canOverwrite(newArgs, isDeployed); - } - - if (shouldDeploy) { - deployer.emit('preDeploy', { - contract: contract, - deployed: isDeployed - }); - - const promiEvent = contract.new.apply(contract, newArgs); - handleContractEvents(promiEvent, state, deployer); - - try { - instance = await promiEvent; - } catch(error){ - deployer.emit('deployFailed', error); - // Should this be conditional?? - throw new Error(); - } - - // TODO: Idle here, waiting for - // the min confirmations to click past - - } else { - instance = await contract.deployed(); - } - - deployer.emit('postDeploy', { - contract: contract, - instance: instance, - deployed: shouldDeploy, - receipt: state.receipt - }); - - // Ensure the address and tx-hash are set on the contract. - contract.address = instance.address; - contract.transactionHash = instance.transactionHash; - return instance; - }; -}; - -module.exports = deploy; diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js new file mode 100644 index 00000000000..4c656c1baaf --- /dev/null +++ b/packages/truffle-deployer/src/deployment.js @@ -0,0 +1,227 @@ + +/** + * @class Deployment + */ +class Deployment { + /** + * constructor + * @param {Object} emitter async `Emittery` emitter + * @param {Number} blocksToWait confirmations needed to resolve an instance + */ + constructor(emitter, blocksToWait){ + this.blocksToWait = blocksToWait || 0; + this.emitter = emitter; + this.promiEventEmitters = []; + this.confirmations = {}; + this.pollingInterval = 1000; + } + + // ------------------------------------ Utils --------------------------------------------------- + + /** + * Helper to parse a deploy statement's overwrite option + * @param {Arry} args arguments passed to deploy + * @param {Boolean} isDeployed is contract deployed? + * @return {Boolean} true if overwrite is ok + */ + _canOverwrite(args, isDeployed){ + const lastArg = args[args.length - 1]; + const isObject = typeof lastArg === "object"; + + const overwrite = isObject && + isDeployed && + lastArg.overwrite === false; + + isObject && delete lastArg.overwrite; + return !overwrite; + } + + /** + * Queries the confirmations mappping periodically to see if we have + * heard enough confirmations for a given tx to allow `deploy` to complete. + * Resolves when this is true. + * @param {String} hash contract creation tx hash + * @return {Promise} + */ + _waitForConfirmation(hash){ + let interval; + const self = this; + + return new Promise(accept => { + interval = setInterval(() => { + if (self.confirmations[hash] >= self.blocksToWait){ + clearInterval(interval); + resolve(); + } + }, self.pollingInterval ); + }) + } + + /** + * Handler for contract's `transactionHash` event. Rebroadcasts as a deployer event + * @param {Object} parent Deployment instance. Local `this` belongs to promievent + * @param {String} hash tranactionHash + */ + async _hashCb(parent, hash){ + const eventArgs = { + transactionHash: hash + } + + await parent.emitter.emit('transactionHash', eventArgs); + this.removeListener('transactionHash', parent._hashCb); + } + + /** + * Handler for contract's `receipt` event. Rebroadcasts as a deployer event + * @param {Object} parent Deployment instance. Local `this` belongs to promievent + * @param {Object} state store for the receipt value + * @param {Object} receipt + */ + async _receiptCb(parent, state, receipt){ + const eventArgs = { + receipt: receipt + } + + // We want this receipt available for the post-deploy event + // so gas reporting is at hand there. + state.receipt = receipt; + await parent.emitter.emit('receipt', eventArgs); + this.removeListener('receipt', parent._receiptCb); + } + + /** + * Handler for contract's `confirmation` event. Rebroadcasts as a deployer event + * and maintains a table of txHashes & their current confirmation number. This + * table gets polled if the user needs to wait a few blocks before getting + * an instance back. + * @param {Object} parent Deployment instance. Local `this` belongs to promievent + * @param {Number} num Confirmation number + * @param {Object} receipt transaction receipt + */ + async _confirmationCb(parent, num, receipt){ + const eventArgs = { + num: num, + receipt: receipt + }; + + parent.confirmations[receipt.transactionHash] = num; + await parent.emitter.emit('confirmation', eventArgs); + } + + // ------------------------------------ Methods -------------------------------------------------- + /** + * + * @param {Object} contract Contract abstraction + * @param {Array} args Constructor arguments + * @return {Promise} Resolves an instance + */ + _deploy(contract, args){ + const self = this; + + return async function() { + let instance; + let eventArgs; + let state = {}; + let shouldDeploy = true; + const isDeployed = contract.isDeployed(); + + await contract.detectNetwork(); + + // Arguments may be promises + const newArgs = await Promise.all(args); + + // Last arg can be an object that tells us not to overwrite. + if (newArgs.length > 0) { + shouldDeploy = self._canOverwrite(newArgs, isDeployed); + } + + // Case: deploy: + if (shouldDeploy) { + eventArgs = { + contract: contract, + deployed: isDeployed + } + + // Emit `preDeploy` & send transaction + await self.emitter.emit('preDeploy', eventArgs); + const promiEvent = contract.new.apply(contract, newArgs); + + // Track emitters for cleanup on exit + self.promiEventEmitters.push(promiEvent); + + // Subscribe to contract events / rebroadcast them to any reporters + promiEvent + .on('transactionHash', self._hashCb.bind(promiEvent, self)) + .on('receipt', self._receiptCb.bind(promiEvent, self, state)) + .on('confirmation', self._confirmationCb.bind(promiEvent, self)) + + // Get instance (or error) + try { + instance = await promiEvent; + } catch(error){ + await self.emitter.emit('deployFailed', { error: error}); + throw new Error(); + } + + // Wait for confirmations + if(self.blocksToWait !== 0){ + await self.waitForConfirmations(instance.transactionHash) + } + + // Case: already deployed + } else { + instance = await contract.deployed(); + } + + // Emit `postDeploy` + eventArgs = { + contract: contract, + instance: instance, + deployed: shouldDeploy, + receipt: state.receipt + } + + await self.emitter.emit('postDeploy', eventArgs); + + // Finish: Ensure the address and tx-hash are set on the contract. + contract.address = instance.address; + contract.transactionHash = instance.transactionHash; + return instance; + }; + } + + /** + * Deploys an array of contracts + * @param {Array} arr Array of contract abstractions to deploy + * @return {Promise} + */ + _deployMany(arr){ + return function() { + const deployments = arr.map(args => { + let contract; + + if (Array.isArray(args)) { + contract = args.shift(); + } else { + contract = args; + args = []; + } + + return deploy(contract, args, deployer)(); + }); + + return Promise.all(deployments); + }; + } + + /** + * Cleans up promiEvents' emitter listeners + */ + _close(){ + this.promiEventEmitters.forEach(item => { + item.removeAllListeners(); + }); + } +}; + +module.exports = Deployment; diff --git a/packages/truffle-deployer/src/linker.js b/packages/truffle-deployer/src/linker.js index 03b5507ce53..7afaf915e7b 100644 --- a/packages/truffle-deployer/src/linker.js +++ b/packages/truffle-deployer/src/linker.js @@ -1,18 +1,18 @@ module.exports = { link: function(library, destinations, deployer) { + let eventArgs; - if (!Array.isArray(destinations)) { - destinations = [destinations]; - } - + // Validate name if (library.contract_name == null) { - deployer.emit('error', { + eventArgs = { type: 'noLibName' - }); - throw new Error(); + } + + deployer.emitter.emit('error', eventArgs); + throw new Error(); // <-- Handle this } - // Abstractions; don't want to use .address directly because it will throw. + // Validate address: don't want to use .address directly because it will throw. let hasAddress; (typeof library.isDeployed) @@ -20,27 +20,34 @@ module.exports = { : hasAddress = library.address != null; if (!hasAddress) { - deployer.emit('error', { + eventArgs = { type: 'noLibAddress', contract: library - }); + } - throw new Error(); + deployer.emitter.emit('error', eventArgs); + throw new Error(); // <-- Handle this + } + + // Link all destinations + if (!Array.isArray(destinations)) { + destinations = [destinations]; } - destinations.forEach(function(destination) { + for (let destination of destinations) { // Don't link if result will have no effect. const alreadyLinked = (destination.links[library.contract_name] == library.address); const noLinkage = (destination.unlinked_binary.indexOf(library.contract_name) < 0); if (alreadyLinked || noLinkage) return; - deployer.emit('linking', { + eventArgs = { library: library, destination: destination - }); + } + deployer.emitter.emit('linking', eventArgs); destination.link(library); - }); + }; }, }; diff --git a/packages/truffle-deployer/src/reporter.js b/packages/truffle-deployer/src/reporter.js deleted file mode 100644 index 9448d985b88..00000000000 --- a/packages/truffle-deployer/src/reporter.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Example logger class that emulates the classic logger behavior - * and allows for optional async handling for tests - */ - -class Logger { - constructor(deployer, async){ - (async) - ? deployer.setAsyncHandler(this.asyncHandler) - : deployer.setAsyncHandler(null); - - this.listen(); - } - - listen(){ - deployer.on('preDeploy', this.preDeploy); - deployer.on('postDeploy', this.postDeploy); - deployer.on('linking', this.linking); - deployer.on('error', this.error); - deployer.on('transactionHash', this.hash); - deployer.on('confirmation', this.confirmation); - deployer.on('step', this.step); - } - - async asyncHandler(err, name, ...args){ - - } - - preDeploy(contract, deployed){ - let message; - (deployed) - ? message = this.messages('replacing', contract) - : message = this.messages('deploying', contract); - - deployer.logger.log(message); - }) - - postDeploy(contract, instance, deployed){ - let message; - (deployed) - ? message = this.messages('deployed', contract, instance) - : message = this.messages('notDeployed', contract, instance); - - deployer.logger.log(message); - }) - - linking(library, destination){ - let message = this.messages('linking', library, destination); - deployer.logger.log(message); - }) - - step(){ - let message = this.messages('step'); - deployer.logger.log(message); - } - - error(type, contract){ - let message = this.messages(type, contract); - deployer.logger.log(message); - }) - - transactionHash(){} - confirmation(){} - - messages(kind, ...args){ - const kinds = { - noLibName: `Link error: Cannot link a library with no name.`, - noLibAddress: `Link error: ${library.contract_name} has no address. Has it been deployed?` - deploying: `Deploying ${args[0].contract_name}...`, - replacing: `Replacing ${args[0].contract_name}...`, - deployed: `${args[0].contract_name}: ${args[1].address}`, - notDeployed: `Didn't deploy ${args[0].contract_name}; using ${args[1].address}`, - linking: `Linking ${args[0].contract_name} ${args[1].contract_name}` - } - return kinds[kind]; - } -} \ No newline at end of file diff --git a/packages/truffle-deployer/test/deployerAwait.js b/packages/truffle-deployer/test/deployerAwait.js index 92ee9377e62..bdb1f095cb0 100644 --- a/packages/truffle-deployer/test/deployerAwait.js +++ b/packages/truffle-deployer/test/deployerAwait.js @@ -5,6 +5,7 @@ const assert = require("assert"); const Deployer = require("../index"); const utils = require('./utils'); +const AsyncReporter = require('./reporters/asyncReporter') describe("deployer (async / await)", function() { let owner @@ -29,85 +30,86 @@ describe("deployer (async / await)", function() { }); afterEach(() => utils.cleanUp()); + afterEach(() => deployer.finish()); - describe('sync', function(){ + it("single deploy()", async function() { + const Example = utils.getContract('Example', provider, networkId, owner); + options.contracts = [ Example ]; - it("single deploy()", async function() { - const Example = utils.getContract('Example', provider, networkId, owner); - options.contracts = [ Example ]; + deployer = new Deployer(options); + reporter = new AsyncReporter(deployer, web3, utils.evm_mine); - const deployer = new Deployer(options); + const migrate = async function(){ + await deployer.deploy(Example); + }; - const migrate = async function(){ - await deployer.deploy(Example); - }; + await deployer.start() + await deployer.then(migrate); - await deployer.start() - await deployer.then(migrate); + assert(Example.address !== null); + assert(Example.transactionHash !== null); - assert(Example.address !== null); - assert(Example.transactionHash !== null); - - example = await Example.deployed(); - assert(await example.id() === 'Example' ); - }); - - it('deploy() with interdependent contracts', async function(){ - const Example = utils.getContract('Example', provider, networkId, owner); - const UsesExample = utils.getContract('UsesExample', provider, networkId, owner); + example = await Example.deployed(); + assert(await example.id() === 'Example' ); + }); - options.contracts = [ Example, UsesExample ]; + it('deploy() with interdependent contracts', async function(){ + const Example = utils.getContract('Example', provider, networkId, owner); + const UsesExample = utils.getContract('UsesExample', provider, networkId, owner); - const deployer = new Deployer(options); + options.contracts = [ Example, UsesExample ]; - const migrate = async function(){ - await deployer.deploy(Example) - await deployer.deploy(UsesExample, Example.address); + deployer = new Deployer(options); + reporter = new AsyncReporter(deployer, web3, utils.evm_mine) - }; + const migrate = async function(){ + await deployer.deploy(Example) + await deployer.deploy(UsesExample, Example.address); + }; - await deployer.start(); - await deployer.then(migrate); + await deployer.start(); + await deployer.then(migrate); - const example = await Example.deployed(); - const usesExample = await UsesExample.deployed(); + const example = await Example.deployed(); + const usesExample = await UsesExample.deployed(); - assert(Example.address !== null); + assert(Example.address !== null); - assert(await example.id() === 'Example' ); - assert(await usesExample.id() === 'UsesExample' ); + assert(await example.id() === 'Example' ); + assert(await usesExample.id() === 'UsesExample' ); - assert(await usesExample.other() === Example.address); - }); + assert(await usesExample.other() === Example.address); + }); - it('deployer.link', async function(){ - const UsesLibrary = utils.getContract('UsesLibrary', provider, networkId, owner); - const IsLibrary = utils.getContract('IsLibrary', provider, networkId, owner); - options.contracts = [ UsesLibrary, IsLibrary ]; + it('deployer.link', async function(){ + const UsesLibrary = utils.getContract('UsesLibrary', provider, networkId, owner); + const IsLibrary = utils.getContract('IsLibrary', provider, networkId, owner); + options.contracts = [ UsesLibrary, IsLibrary ]; - const deployer = new Deployer(options); + deployer = new Deployer(options); + reporter = new AsyncReporter(deployer, web3, utils.evm_mine) - const migrate = async function(){ - await deployer.deploy(IsLibrary); - await deployer.link(IsLibrary, UsesLibrary); - await deployer.deploy(UsesLibrary); - }; + const migrate = async function(){ + await deployer.deploy(IsLibrary); + await deployer.link(IsLibrary, UsesLibrary); + await deployer.deploy(UsesLibrary); + }; - await deployer.start(); - await deployer.then(migrate); + await deployer.start(); + await deployer.then(migrate); - assert(UsesLibrary.address !== null); - assert(IsLibrary.address !== null); + assert(UsesLibrary.address !== null); + assert(IsLibrary.address !== null); - const usesLibrary = await UsesLibrary.deployed(); - await usesLibrary.fireIsLibraryEvent(5); - await usesLibrary.fireUsesLibraryEvent(7); + const usesLibrary = await UsesLibrary.deployed(); + await usesLibrary.fireIsLibraryEvent(5); + await usesLibrary.fireUsesLibraryEvent(7); - eventOptions = {fromBlock: 0, toBlock: 'latest'}; - const events = await usesLibrary.getPastEvents("allEvents", eventOptions); + eventOptions = {fromBlock: 0, toBlock: 'latest'}; + const events = await usesLibrary.getPastEvents("allEvents", eventOptions); - assert(events[0].args.eventID === '5'); - assert(events[1].args.eventID === '7'); - }); + assert(events[0].args.eventID === '5'); + assert(events[1].args.eventID === '7'); }); + }); \ No newline at end of file diff --git a/packages/truffle-deployer/test/deployerClassic.js b/packages/truffle-deployer/test/deployerClassic.js index fd1116d06d9..f81d86f5ca4 100644 --- a/packages/truffle-deployer/test/deployerClassic.js +++ b/packages/truffle-deployer/test/deployerClassic.js @@ -4,6 +4,7 @@ const Web3 = require("web3"); const assert = require("assert"); const Deployer = require("../index"); +const SyncReporter = require("./reporters/syncReporter") const utils = require('./utils'); describe("deployer (classic)", function() { @@ -22,122 +23,125 @@ describe("deployer (classic)", function() { contracts: null, network: 'test', network_id: networkId, - provider: provider + provider: provider, + //logger: console } await utils.compile(); }); afterEach(() => utils.cleanUp()); + afterEach(() => deployer.finish()); - describe('sync', function(){ + it("deploy()", async function() { + const Example = utils.getContract('Example', provider, networkId, owner); + options.contracts = [ Example ]; - it("deploy()", async function() { - const Example = utils.getContract('Example', provider, networkId, owner); - options.contracts = [ Example ]; + deployer = new Deployer(options); + reporter = new SyncReporter(deployer); - const deployer = new Deployer(options); + const migrate = function(){ + deployer.deploy(Example); + }; - const migrate = function(){ - deployer.deploy(Example); - }; + migrate(); - migrate(); + await deployer.start(); - await deployer.start(); + assert(Example.address !== null); + assert(Example.transactionHash !== null); - assert(Example.address !== null); - assert(Example.transactionHash !== null); - - example = await Example.deployed(); - assert(await example.id() === 'Example' ); - }); + example = await Example.deployed(); + assert(await example.id() === 'Example' ); + }); - it('deploy().then', async function(){ - const Example = utils.getContract('Example', provider, networkId, owner); - const UsesExample = utils.getContract('UsesExample', provider, networkId, owner); + it('deploy().then', async function(){ + const Example = utils.getContract('Example', provider, networkId, owner); + const UsesExample = utils.getContract('UsesExample', provider, networkId, owner); - options.contracts = [ Example, UsesExample ]; + options.contracts = [ Example, UsesExample ]; - const deployer = new Deployer(options); + deployer = new Deployer(options); + reporter = new SyncReporter(deployer); - const migrate = function(){ - deployer.deploy(Example).then(function() { - return deployer.deploy(UsesExample, Example.address); - }); - }; + const migrate = function(){ + deployer.deploy(Example).then(function() { + return deployer.deploy(UsesExample, Example.address); + }); + }; - migrate(); - await deployer.start(); + migrate(); + await deployer.start(); - const example = await Example.deployed(); - const usesExample = await UsesExample.deployed(); + const example = await Example.deployed(); + const usesExample = await UsesExample.deployed(); - assert(Example.address !== null); + assert(Example.address !== null); - assert(await example.id() === 'Example' ); - assert(await usesExample.id() === 'UsesExample' ); + assert(await example.id() === 'Example' ); + assert(await usesExample.id() === 'UsesExample' ); - assert(await usesExample.other() === Example.address); - }); + assert(await usesExample.other() === Example.address); + }); - it('deployer.then', async function(){ - const Example = utils.getContract('Example', provider, networkId, owner); - const UsesExample = utils.getContract('UsesExample', provider, networkId, owner); + it('deployer.then', async function(){ + const Example = utils.getContract('Example', provider, networkId, owner); + const UsesExample = utils.getContract('UsesExample', provider, networkId, owner); - options.contracts = [ Example, UsesExample ]; + options.contracts = [ Example, UsesExample ]; - const deployer = new Deployer(options); + deployer = new Deployer(options); + reporter = new SyncReporter(deployer); - const migrate = function(){ - deployer.then(async function(){ - const example = await deployer.deploy(Example); - await deployer.deploy(UsesExample, example.address); - }) - }; + const migrate = function(){ + deployer.then(async function(){ + const example = await deployer.deploy(Example); + await deployer.deploy(UsesExample, example.address); + }) + }; - migrate(); - await deployer.start(); + migrate(); + await deployer.start(); - const example = await Example.deployed(); - const usesExample = await UsesExample.deployed(); + const example = await Example.deployed(); + const usesExample = await UsesExample.deployed(); - assert(Example.address !== null); + assert(Example.address !== null); - assert(await example.id() === 'Example' ); - assert(await usesExample.id() === 'UsesExample' ); - assert(await usesExample.other() === Example.address); - }); + assert(await example.id() === 'Example' ); + assert(await usesExample.id() === 'UsesExample' ); + assert(await usesExample.other() === Example.address); + }); - it('deployer.link', async function(){ - const UsesLibrary = utils.getContract('UsesLibrary', provider, networkId, owner); - const IsLibrary = utils.getContract('IsLibrary', provider, networkId, owner); - options.contracts = [ UsesLibrary, IsLibrary ]; + it('deployer.link', async function(){ + const UsesLibrary = utils.getContract('UsesLibrary', provider, networkId, owner); + const IsLibrary = utils.getContract('IsLibrary', provider, networkId, owner); + options.contracts = [ UsesLibrary, IsLibrary ]; - const deployer = new Deployer(options); + deployer = new Deployer(options); + reporter = new SyncReporter(deployer); - const migrate = function(){ - deployer.deploy(IsLibrary); - deployer.link(IsLibrary, UsesLibrary); - deployer.deploy(UsesLibrary); - }; + const migrate = function(){ + deployer.deploy(IsLibrary); + deployer.link(IsLibrary, UsesLibrary); + deployer.deploy(UsesLibrary); + }; - migrate(); + migrate(); - await deployer.start(); + await deployer.start(); - assert(UsesLibrary.address !== null); - assert(IsLibrary.address !== null); + assert(UsesLibrary.address !== null); + assert(IsLibrary.address !== null); - const usesLibrary = await UsesLibrary.deployed(); - await usesLibrary.fireIsLibraryEvent(5); - await usesLibrary.fireUsesLibraryEvent(7); + const usesLibrary = await UsesLibrary.deployed(); + await usesLibrary.fireIsLibraryEvent(5); + await usesLibrary.fireUsesLibraryEvent(7); - eventOptions = {fromBlock: 0, toBlock: 'latest'}; - const events = await usesLibrary.getPastEvents("allEvents", eventOptions); + eventOptions = {fromBlock: 0, toBlock: 'latest'}; + const events = await usesLibrary.getPastEvents("allEvents", eventOptions); - assert(events[0].args.eventID === '5'); - assert(events[1].args.eventID === '7'); - }); + assert(events[0].args.eventID === '5'); + assert(events[1].args.eventID === '7'); }); }); diff --git a/packages/truffle-deployer/test/reporters/asyncReporter.js b/packages/truffle-deployer/test/reporters/asyncReporter.js new file mode 100644 index 00000000000..bfac220dd47 --- /dev/null +++ b/packages/truffle-deployer/test/reporters/asyncReporter.js @@ -0,0 +1,37 @@ +/** + * Example reporter for testing async remote control of a deployer + * via the `preDeploy`, `postDeploy` and `link` events. + */ + +class AsyncReporter { + constructor(deployer, web3, mine){ + this.blocks = []; + this.deployer = deployer; + this.mine = mine; + this.web3 = web3; + this.listen(); + } + + async mineAndRecordBlock(name, args){ + if (name === 'postDeploy'){ + await this.mine(this.web3); + await this.mine(this.web3); + const blockNumber = await this.web3.eth.getBlockNumber(); + this.blocks.unshift(blockNumber); + } else if (name === 'confirmation'){ + // etc + } + } + + listen(){ + this.deployer.emitter.on('preDeploy', this.mineAndRecordBlock.bind(this, 'preDeploy')); + this.deployer.emitter.on('postDeploy', this.mineAndRecordBlock.bind(this, 'postDeploy')); + this.deployer.emitter.on('linking', this.mineAndRecordBlock.bind(this, 'linking')); + this.deployer.emitter.on('error', this.mineAndRecordBlock.bind(this, 'error')); + this.deployer.emitter.on('receipt', this.mineAndRecordBlock.bind(this, 'receipt')); + this.deployer.emitter.on('transactionHash', this.mineAndRecordBlock.bind(this, 'transactionHash')); + this.deployer.emitter.on('confirmation', this.mineAndRecordBlock.bind(this, 'confirmation')); + } +} + +module.exports = AsyncReporter; diff --git a/packages/truffle-deployer/test/reporters/syncReporter.js b/packages/truffle-deployer/test/reporters/syncReporter.js new file mode 100644 index 00000000000..93b2bde8c25 --- /dev/null +++ b/packages/truffle-deployer/test/reporters/syncReporter.js @@ -0,0 +1,78 @@ +/** + * Example reporter class that emulates the classic logger behavior + */ + +class SyncReporter { + constructor(deployer){ + this.deployer = deployer; + this.listen(); + } + + listen(){ + this.deployer.emitter.on('preDeploy', this.preDeploy.bind(this)); + this.deployer.emitter.on('postDeploy', this.postDeploy.bind(this)); + this.deployer.emitter.on('linking', this.linking.bind(this)); + this.deployer.emitter.on('error', this.error.bind(this)); + this.deployer.emitter.on('transactionHash', this.hash.bind(this)); + this.deployer.emitter.on('confirmation', this.confirmation.bind(this)); + } + + preDeploy(payload){ + let message; + (payload.deployed) + ? message = this.messages('replacing', payload.contract, payload.deployed) + : message = this.messages('deploying', payload.contract, payload.deployed); + + this.deployer.logger.log(message); + } + + postDeploy(payload){ + let message; + (payload.deployed) + ? message = this.messages('deployed', payload.contract, payload.instance) + : message = this.messages('notDeployed', payload.contract, payload.instance); + + this.deployer.logger.log(message); + } + + linking(payload){ + let message = this.messages('linking', payload.library, payload.destination); + this.deployer.logger.log(message); + } + + error(payload){ + let message = this.messages(payload.type, payload.contract); + this.deployer.logger.log(message); + } + + hash(payload){ + let message = this.messages('hash', payload.transactionHash); + this.deployer.logger.log(message); + } + + confirmation(payload){ + let message = this.messages('confirmation', payload.num, payload.receipt); + this.deployer.logger.log(message); + } + + messages(kind, ...args){ + args[0] = args[0] || {}; + args[1] = args[1] || {}; + + const kinds = { + noLibName: `Link error: Cannot link a library with no name.`, + noLibAddress: `Link error: ${args[0].contractName} has no address. Has it been deployed?`, + deploying: `Deploying ${args[0].contractName}...`, + replacing: `Replacing ${args[0].contractName}...`, + deployed: `${args[0].contractName}: ${args[1].address}`, + notDeployed: `Didn't deploy ${args[0].contractName}; using ${args[1].address}`, + linking: `Linking ${args[0].contractName} ${args[1].contract_name}`, + hash: `Transaction: ${args[0]}`, + confirmation: `Confirmation number: ${args[0]}`, + } + + return kinds[kind]; + } +} + +module.exports = SyncReporter; \ No newline at end of file diff --git a/packages/truffle-deployer/test/utils.js b/packages/truffle-deployer/test/utils.js index f49ed7fd9e2..85b5c789ed4 100644 --- a/packages/truffle-deployer/test/utils.js +++ b/packages/truffle-deployer/test/utils.js @@ -18,6 +18,18 @@ const utils = { }) }, + evm_mine: function(web3){ + return new Promise(function(accept, reject){ + web3.currentProvider.send({ + jsonrpc: "2.0", + method: "evm_mine", + id: new Date().getTime() + }, function(err, result){ + (err) ? reject(err) : accept(result); + }); + }); + }, + cleanUp: () => fs.removeSync(utils.buildDir), getContract: function(name, provider, networkId, account){ From e16cd4b2524508f8bc7214cc5831f12e87a7e747 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 6 Jun 2018 11:39:26 -0700 Subject: [PATCH 12/74] Third draft: errors --- packages/truffle-deployer/package.json | 5 +- packages/truffle-deployer/src/deployment.js | 136 +++++++-- packages/truffle-deployer/src/linker.js | 8 +- .../test/{deployerAwait.js => await.js} | 12 +- packages/truffle-deployer/test/deployer.js | 227 ++++++++++++++ .../truffle-deployer/test/deployerClassic.js | 147 --------- packages/truffle-deployer/test/errors.js | 264 ++++++++++++++++ .../truffle-deployer/test/helpers/reporter.js | 284 ++++++++++++++++++ .../test/{ => helpers}/utils.js | 21 +- .../test/reporters/asyncReporter.js | 37 --- .../test/reporters/syncReporter.js | 78 ----- .../test/sources/Abstract.sol | 5 + .../test/sources/ExampleAssert.sol | 8 + .../{ExampleError.sol => ExampleRevert.sol} | 3 +- .../test/sources/IsLibrary.sol | 5 +- .../truffle-deployer/test/sources/Loop.sol | 11 + 16 files changed, 938 insertions(+), 313 deletions(-) rename packages/truffle-deployer/test/{deployerAwait.js => await.js} (88%) create mode 100644 packages/truffle-deployer/test/deployer.js delete mode 100644 packages/truffle-deployer/test/deployerClassic.js create mode 100644 packages/truffle-deployer/test/errors.js create mode 100644 packages/truffle-deployer/test/helpers/reporter.js rename packages/truffle-deployer/test/{ => helpers}/utils.js (70%) delete mode 100644 packages/truffle-deployer/test/reporters/asyncReporter.js delete mode 100644 packages/truffle-deployer/test/reporters/syncReporter.js create mode 100644 packages/truffle-deployer/test/sources/Abstract.sol create mode 100644 packages/truffle-deployer/test/sources/ExampleAssert.sol rename packages/truffle-deployer/test/sources/{ExampleError.sol => ExampleRevert.sol} (55%) create mode 100644 packages/truffle-deployer/test/sources/Loop.sol diff --git a/packages/truffle-deployer/package.json b/packages/truffle-deployer/package.json index eb238731ff4..9d78707c21a 100644 --- a/packages/truffle-deployer/package.json +++ b/packages/truffle-deployer/package.json @@ -4,7 +4,7 @@ "description": "Light deployment module for easily deploying Ethereum contracts", "main": "index.js", "scripts": { - "test": "mocha --no-warnings" + "test": "mocha --no-warnings --timeout 10000" }, "repository": { "type": "git", @@ -30,7 +30,8 @@ }, "devDependencies": { "fs-extra": "^6.0.1", - "ganache-cli": "6.1.0-beta.4", + "ganache-cli": "6.1.0", + "memorystream": "^0.3.1", "mocha": "5.2.0", "truffle-workflow-compile": "^1.0.3", "web3": "1.0.0-beta.33" diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 4c656c1baaf..8eb3a34c312 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -1,15 +1,14 @@ - /** * @class Deployment */ class Deployment { /** * constructor - * @param {Object} emitter async `Emittery` emitter - * @param {Number} blocksToWait confirmations needed to resolve an instance + * @param {Object} emitter async `Emittery` emitter + * @param {Number} confirmationsRequired confirmations needed to resolve an instance */ - constructor(emitter, blocksToWait){ - this.blocksToWait = blocksToWait || 0; + constructor(emitter, confirmationsRequired){ + this.confirmationsRequired = confirmationsRequired || 0; this.emitter = emitter; this.promiEventEmitters = []; this.confirmations = {}; @@ -18,6 +17,15 @@ class Deployment { // ------------------------------------ Utils --------------------------------------------------- + /** + * Stub for future error code assignments on process.exit + * @param {String} type key map to code + * @return {Number} code to exit + */ + _errorCodes(type){ + return 1; + } + /** * Helper to parse a deploy statement's overwrite option * @param {Arry} args arguments passed to deploy @@ -37,36 +45,77 @@ class Deployment { } /** - * Queries the confirmations mappping periodically to see if we have + * Gets arbitrary values from constructor params, if they exist. + * @param {Array} args constructor params + * @return {Any|Undefined} gas value + */ + _extractFromArgs(args, key){ + let value; + + args.forEach(arg => { + const hasKey = !Array.isArray(arg) && + typeof arg === 'object' && + Object.keys(arg).includes(key); + + if(hasKey) value = arg[key]; + }); + return value; + } + + /** + * Queries the confirmations mapping periodically to see if we have * heard enough confirmations for a given tx to allow `deploy` to complete. * Resolves when this is true. * @param {String} hash contract creation tx hash * @return {Promise} */ - _waitForConfirmation(hash){ + async _waitForConfirmations(hash){ let interval; const self = this; return new Promise(accept => { interval = setInterval(() => { - if (self.confirmations[hash] >= self.blocksToWait){ + if (self.confirmations[hash] >= self.confirmationsRequired){ clearInterval(interval); - resolve(); + accept(); } - }, self.pollingInterval ); + }, self.pollingInterval); }) } + /** + * Sanity checks catch-all: + * Are we connected? + * Is contract deployable? + * @param {Object} contract TruffleContract + * @return {Promise} throws on error + */ + async _preFlightCheck(contract){ + // Check bytecode + if(contract.bytecode === '0x') { + await this.emitter.emit('error', { + type: 'noBytecode', + contract: contract, + }) + + throw new Error(this._errorCodes('noBytecode')); + } + + // Check network + await contract.detectNetwork(); + } + /** * Handler for contract's `transactionHash` event. Rebroadcasts as a deployer event * @param {Object} parent Deployment instance. Local `this` belongs to promievent * @param {String} hash tranactionHash */ - async _hashCb(parent, hash){ + async _hashCb(parent, state, hash){ const eventArgs = { + contractName: state.contractName, transactionHash: hash } - + state.transactionHash = hash; await parent.emitter.emit('transactionHash', eventArgs); this.removeListener('transactionHash', parent._hashCb); } @@ -79,6 +128,7 @@ class Deployment { */ async _receiptCb(parent, state, receipt){ const eventArgs = { + contractName: state.contractName, receipt: receipt } @@ -98,8 +148,9 @@ class Deployment { * @param {Number} num Confirmation number * @param {Object} receipt transaction receipt */ - async _confirmationCb(parent, num, receipt){ + async _confirmationCb(parent, state, num, receipt){ const eventArgs = { + contractName: state.contractName, num: num, receipt: receipt }; @@ -121,14 +172,16 @@ class Deployment { return async function() { let instance; let eventArgs; - let state = {}; let shouldDeploy = true; - const isDeployed = contract.isDeployed(); + let state = { + contractName: contract.contractName + }; - await contract.detectNetwork(); + await self._preFlightCheck(contract); - // Arguments may be promises + const isDeployed = contract.isDeployed(); const newArgs = await Promise.all(args); + const currentBlock = await contract.web3.eth.getBlock('latest'); // Last arg can be an object that tells us not to overwrite. if (newArgs.length > 0) { @@ -138,8 +191,20 @@ class Deployment { // Case: deploy: if (shouldDeploy) { eventArgs = { + state: state, contract: contract, - deployed: isDeployed + deployed: isDeployed, + blockLimit: currentBlock.gasLimit, + gas: self._extractFromArgs(newArgs, 'gas') || contract.defaults().gas, + gasPrice: self._extractFromArgs(newArgs, 'gasPrice') || contract.defaults().gasPrice, + from: self._extractFromArgs(newArgs, 'from') || contract.defaults().from, + } + + // Detect constructor revert by running estimateGas + try { + eventArgs.estimate = await contract.new.estimateGas.apply(contract, newArgs); + } catch(err){ + eventArgs.estimateError = err; } // Emit `preDeploy` & send transaction @@ -151,21 +216,22 @@ class Deployment { // Subscribe to contract events / rebroadcast them to any reporters promiEvent - .on('transactionHash', self._hashCb.bind(promiEvent, self)) + .on('transactionHash', self._hashCb.bind(promiEvent, self, state)) .on('receipt', self._receiptCb.bind(promiEvent, self, state)) - .on('confirmation', self._confirmationCb.bind(promiEvent, self)) + .on('confirmation', self._confirmationCb.bind(promiEvent, self, state)) // Get instance (or error) try { instance = await promiEvent; - } catch(error){ - await self.emitter.emit('deployFailed', { error: error}); - throw new Error(); + } catch(err){ + eventArgs.error = err.error || err; + await self.emitter.emit('deployFailed', eventArgs); + throw new Error(self._errorCodes('deployFailed')); } // Wait for confirmations - if(self.blocksToWait !== 0){ - await self.waitForConfirmations(instance.transactionHash) + if(self.confirmationsRequired !== 0){ + await self._waitForConfirmations(instance.transactionHash) } // Case: already deployed @@ -196,21 +262,31 @@ class Deployment { * @return {Promise} */ _deployMany(arr){ - return function() { + const self = this; + + return async function() { const deployments = arr.map(args => { + let params; let contract; if (Array.isArray(args)) { - contract = args.shift(); + contract = args[0]; + + (args.length > 1) + ? params = args.slice(1) + : params = []; + } else { contract = args; - args = []; + params = []; } - return deploy(contract, args, deployer)(); + return self._deploy(contract, params)(); }); - return Promise.all(deployments); + await self.emitter.emit('preDeployMany', arr); + await Promise.all(deployments); + await self.emitter.emit('postDeployMany', arr); }; } diff --git a/packages/truffle-deployer/src/linker.js b/packages/truffle-deployer/src/linker.js index 7afaf915e7b..f0c00326c75 100644 --- a/packages/truffle-deployer/src/linker.js +++ b/packages/truffle-deployer/src/linker.js @@ -22,7 +22,7 @@ module.exports = { if (!hasAddress) { eventArgs = { type: 'noLibAddress', - contract: library + contract: library, } deployer.emitter.emit('error', eventArgs); @@ -42,8 +42,10 @@ module.exports = { if (alreadyLinked || noLinkage) return; eventArgs = { - library: library, - destination: destination + libraryName: library.contractName, + libraryAddress: library.address, + contractName: destination.contractName, + contractAddress: destination.contractAddress, } deployer.emitter.emit('linking', eventArgs); diff --git a/packages/truffle-deployer/test/deployerAwait.js b/packages/truffle-deployer/test/await.js similarity index 88% rename from packages/truffle-deployer/test/deployerAwait.js rename to packages/truffle-deployer/test/await.js index bdb1f095cb0..443a26352bf 100644 --- a/packages/truffle-deployer/test/deployerAwait.js +++ b/packages/truffle-deployer/test/await.js @@ -4,10 +4,9 @@ const Web3 = require("web3"); const assert = require("assert"); const Deployer = require("../index"); -const utils = require('./utils'); -const AsyncReporter = require('./reporters/asyncReporter') +const utils = require('./helpers/utils'); -describe("deployer (async / await)", function() { +describe("Deployer (async / await)", function() { let owner let options; let networkId; @@ -37,7 +36,6 @@ describe("deployer (async / await)", function() { options.contracts = [ Example ]; deployer = new Deployer(options); - reporter = new AsyncReporter(deployer, web3, utils.evm_mine); const migrate = async function(){ await deployer.deploy(Example); @@ -60,7 +58,6 @@ describe("deployer (async / await)", function() { options.contracts = [ Example, UsesExample ]; deployer = new Deployer(options); - reporter = new AsyncReporter(deployer, web3, utils.evm_mine) const migrate = async function(){ await deployer.deploy(Example) @@ -77,7 +74,6 @@ describe("deployer (async / await)", function() { assert(await example.id() === 'Example' ); assert(await usesExample.id() === 'UsesExample' ); - assert(await usesExample.other() === Example.address); }); @@ -87,11 +83,10 @@ describe("deployer (async / await)", function() { options.contracts = [ UsesLibrary, IsLibrary ]; deployer = new Deployer(options); - reporter = new AsyncReporter(deployer, web3, utils.evm_mine) const migrate = async function(){ await deployer.deploy(IsLibrary); - await deployer.link(IsLibrary, UsesLibrary); + deployer.link(IsLibrary, UsesLibrary); await deployer.deploy(UsesLibrary); }; @@ -111,5 +106,4 @@ describe("deployer (async / await)", function() { assert(events[0].args.eventID === '5'); assert(events[1].args.eventID === '7'); }); - }); \ No newline at end of file diff --git a/packages/truffle-deployer/test/deployer.js b/packages/truffle-deployer/test/deployer.js new file mode 100644 index 00000000000..70a219db9c2 --- /dev/null +++ b/packages/truffle-deployer/test/deployer.js @@ -0,0 +1,227 @@ +const ganache = require("ganache-cli"); +const contract = require("truffle-contract"); +const Web3 = require("web3"); +const assert = require("assert"); + +const Deployer = require("../index"); +const Reporter = require("./helpers/reporter") +const utils = require('./helpers/utils'); + +describe("Deployer (sync)", function() { + let owner + let options; + let networkId; + let deployer; + let reporter; + let output = ''; + let Example; + let UsesExample; + let IsLibrary; + let UsesLibrary; + + const provider = ganache.provider({ + vmErrorsOnRPCResponse: false + }); + + const web3 = new Web3(provider); + + beforeEach(async function() { + networkId = await web3.eth.net.getId(); + const accounts = await web3.eth.getAccounts(); + + owner = accounts[0]; + await utils.compile(); + + Example = utils.getContract('Example', provider, networkId, owner); + ExampleRevert = utils.getContract('ExampleRevert', provider, networkId, owner); + UsesExample = utils.getContract('UsesExample', provider, networkId, owner); + UsesLibrary = utils.getContract('UsesLibrary', provider, networkId, owner); + IsLibrary = utils.getContract('IsLibrary', provider, networkId, owner); + Abstract = utils.getContract('Abstract', provider, networkId, owner); + + options = { + contracts: [ Example, ExampleRevert, UsesExample, IsLibrary, UsesLibrary, Abstract ], + network: 'test', + network_id: networkId, + provider: provider, + logger: { + log: (val) => { if (val) output += `${val}\n`}, + error: (val) => { if (val) output += `${val}\n`} + } + } + deployer = new Deployer(options); + reporter = new Reporter(deployer); + }); + + afterEach(() => { + output = ''; + utils.cleanUp(); + deployer.finish() + }); + + it("deploy()", async function() { + const migrate = function(){ + deployer.deploy(Example); + }; + + migrate(); + + await deployer.start(); + + assert(Example.address !== null); + assert(Example.transactionHash !== null); + + const example = await Example.deployed(); + const id = await example.id(); + + assert(id === 'Example' ); + + assert(output.includes('Example')); + assert(output.includes('Deploying')); + assert(output.includes('transaction hash')); + assert(output.includes('address')); + assert(output.includes('gas usage')); + }); + + it('deploy().then', async function(){ + function migrate(){ + deployer + .deploy(Example) + .then(() => deployer.deploy(UsesExample, Example.address)); + }; + + migrate(); + await deployer.start(); + + const example = await Example.deployed(); + const usesExample = await UsesExample.deployed(); + const exampleId = await example.id(); + const usesExampleId = await usesExample.id(); + const other = await usesExample.other(); + + assert(Example.address !== null); + assert(exampleId === 'Example' ); + assert(usesExampleId === 'UsesExample' ); + assert(other === Example.address); + + assert(output.includes('Replacing')) + assert(output.includes('Deploying')); + assert(output.includes('Example')); + assert(output.includes('UsesExample')); + }); + + it('deploy([contract, [contract, args], ...])', async function(){ + const deployArgs = [Example, [UsesExample, utils.zeroAddress]] + + function migrate(){ + deployer.deploy(deployArgs); + }; + + migrate(); + await deployer.start(); + + const example = await Example.deployed(); + const usesExample = await UsesExample.deployed(); + const exampleId = await example.id(); + const usesExampleId = await usesExample.id(); + const other = await usesExample.other(); + + assert(Example.address !== null); + assert(exampleId === 'Example' ); + assert(usesExampleId === 'UsesExample' ); + assert(other === utils.zeroAddress); + + assert(output.includes('Deploying Batch')) + assert(output.includes('Example')); + assert(output.includes('UsesExample')); + }); + + it('deployer.then', async function(){ + function migrate(){ + deployer.then(async function(){ + const example = await deployer.deploy(Example); + await deployer.deploy(UsesExample, example.address); + }) + }; + + migrate(); + await deployer.start(); + + const example = await Example.deployed(); + const usesExample = await UsesExample.deployed(); + const exampleId = await example.id(); + const usesExampleId = await usesExample.id(); + const other = await usesExample.other(); + + assert(Example.address !== null); + assert(exampleId === 'Example' ); + assert(usesExampleId === 'UsesExample' ); + assert(other === Example.address); + + assert(output.includes('Replacing')) + assert(output.includes('Example')); + assert(output.includes('UsesExample')); + }); + + it('deployer.link', async function(){ + const migrate = function(){ + deployer.deploy(IsLibrary); + deployer.link(IsLibrary, UsesLibrary); + deployer.deploy(UsesLibrary); + }; + + migrate(); + + await deployer.start(); + + assert(UsesLibrary.address !== null); + assert(IsLibrary.address !== null); + + const usesLibrary = await UsesLibrary.deployed(); + await usesLibrary.fireIsLibraryEvent(5); + await usesLibrary.fireUsesLibraryEvent(7); + + eventOptions = {fromBlock: 0, toBlock: 'latest'}; + const events = await usesLibrary.getPastEvents("allEvents", eventOptions); + + assert(events[0].args.eventID === '5'); + assert(events[1].args.eventID === '7'); + + assert(output.includes('Deploying')); + assert(output.includes('Linking')); + assert(output.includes('IsLibrary')); + assert(output.includes('UsesLibrary')); + }); + + it('waits for confirmations', async function(){ + this.timeout(5000); + const startBlock = await web3.eth.getBlockNumber(); + + utils.startAutoMine(web3, 500); + + const migrate = function(){ + deployer.deploy(IsLibrary); + deployer.deploy(Example); + }; + + migrate(); + + deployer.confirmationsRequired = 2; + await deployer.start(); + + utils.stopAutoMine(); + + const isLibrary = await IsLibrary.deployed(); + const example = await Example.deployed(); + + const libReceipt = await web3.eth.getTransactionReceipt(IsLibrary.transactionHash); + const exampleReceipt = await web3.eth.getTransactionReceipt(Example.transactionHash); + + // The first confirmation is the block that accepts the tx. Then we wait two more. + // Then Example is deployed in the consequent block. + assert(libReceipt.blockNumber === startBlock + 1); + assert(exampleReceipt.blockNumber === (libReceipt.blockNumber + 3)) + + deployer.confirmationsRequired = 0; + }); +}); diff --git a/packages/truffle-deployer/test/deployerClassic.js b/packages/truffle-deployer/test/deployerClassic.js deleted file mode 100644 index f81d86f5ca4..00000000000 --- a/packages/truffle-deployer/test/deployerClassic.js +++ /dev/null @@ -1,147 +0,0 @@ -const ganache = require("ganache-cli"); -const contract = require("truffle-contract"); -const Web3 = require("web3"); -const assert = require("assert"); - -const Deployer = require("../index"); -const SyncReporter = require("./reporters/syncReporter") -const utils = require('./utils'); - -describe("deployer (classic)", function() { - let owner - let options; - let networkId; - const provider = ganache.provider(); - const web3 = new Web3(provider); - - beforeEach(async function() { - networkId = await web3.eth.net.getId(); - const accounts = await web3.eth.getAccounts(); - - owner = accounts[0]; - options = { - contracts: null, - network: 'test', - network_id: networkId, - provider: provider, - //logger: console - } - - await utils.compile(); - }); - - afterEach(() => utils.cleanUp()); - afterEach(() => deployer.finish()); - - it("deploy()", async function() { - const Example = utils.getContract('Example', provider, networkId, owner); - options.contracts = [ Example ]; - - deployer = new Deployer(options); - reporter = new SyncReporter(deployer); - - const migrate = function(){ - deployer.deploy(Example); - }; - - migrate(); - - await deployer.start(); - - assert(Example.address !== null); - assert(Example.transactionHash !== null); - - example = await Example.deployed(); - assert(await example.id() === 'Example' ); - }); - - it('deploy().then', async function(){ - const Example = utils.getContract('Example', provider, networkId, owner); - const UsesExample = utils.getContract('UsesExample', provider, networkId, owner); - - options.contracts = [ Example, UsesExample ]; - - deployer = new Deployer(options); - reporter = new SyncReporter(deployer); - - const migrate = function(){ - deployer.deploy(Example).then(function() { - return deployer.deploy(UsesExample, Example.address); - }); - }; - - migrate(); - await deployer.start(); - - const example = await Example.deployed(); - const usesExample = await UsesExample.deployed(); - - assert(Example.address !== null); - - assert(await example.id() === 'Example' ); - assert(await usesExample.id() === 'UsesExample' ); - - assert(await usesExample.other() === Example.address); - }); - - it('deployer.then', async function(){ - const Example = utils.getContract('Example', provider, networkId, owner); - const UsesExample = utils.getContract('UsesExample', provider, networkId, owner); - - options.contracts = [ Example, UsesExample ]; - - deployer = new Deployer(options); - reporter = new SyncReporter(deployer); - - const migrate = function(){ - deployer.then(async function(){ - const example = await deployer.deploy(Example); - await deployer.deploy(UsesExample, example.address); - }) - }; - - migrate(); - await deployer.start(); - - const example = await Example.deployed(); - const usesExample = await UsesExample.deployed(); - - assert(Example.address !== null); - - assert(await example.id() === 'Example' ); - assert(await usesExample.id() === 'UsesExample' ); - assert(await usesExample.other() === Example.address); - }); - - it('deployer.link', async function(){ - const UsesLibrary = utils.getContract('UsesLibrary', provider, networkId, owner); - const IsLibrary = utils.getContract('IsLibrary', provider, networkId, owner); - options.contracts = [ UsesLibrary, IsLibrary ]; - - deployer = new Deployer(options); - reporter = new SyncReporter(deployer); - - const migrate = function(){ - deployer.deploy(IsLibrary); - deployer.link(IsLibrary, UsesLibrary); - deployer.deploy(UsesLibrary); - }; - - migrate(); - - await deployer.start(); - - assert(UsesLibrary.address !== null); - assert(IsLibrary.address !== null); - - const usesLibrary = await UsesLibrary.deployed(); - await usesLibrary.fireIsLibraryEvent(5); - await usesLibrary.fireUsesLibraryEvent(7); - - eventOptions = {fromBlock: 0, toBlock: 'latest'}; - const events = await usesLibrary.getPastEvents("allEvents", eventOptions); - - assert(events[0].args.eventID === '5'); - assert(events[1].args.eventID === '7'); - }); -}); diff --git a/packages/truffle-deployer/test/errors.js b/packages/truffle-deployer/test/errors.js new file mode 100644 index 00000000000..df60fa148d7 --- /dev/null +++ b/packages/truffle-deployer/test/errors.js @@ -0,0 +1,264 @@ +const ganache = require("ganache-cli"); +const contract = require("truffle-contract"); +const Web3 = require("web3"); +const assert = require("assert"); + +const Deployer = require("../index"); +const Reporter = require("./helpers/reporter") +const utils = require('./helpers/utils'); + +describe("Error cases", function() { + let owner; + let accounts; + let options; + let networkId; + let deployer; + let reporter; + let output = ''; + let Example; + let UsesExample; + let IsLibrary; + let UsesLibrary; + + const provider = ganache.provider({ + vmErrorsOnRPCResponse: false + }); + + const web3 = new Web3(provider); + + beforeEach(async function() { + networkId = await web3.eth.net.getId(); + accounts = await web3.eth.getAccounts(); + + owner = accounts[0]; + await utils.compile(); + + Example = utils.getContract('Example', provider, networkId, owner); + ExampleRevert = utils.getContract('ExampleRevert', provider, networkId, owner); + ExampleAssert = utils.getContract('ExampleAssert', provider, networkId, owner); + UsesExample = utils.getContract('UsesExample', provider, networkId, owner); + UsesLibrary = utils.getContract('UsesLibrary', provider, networkId, owner); + IsLibrary = utils.getContract('IsLibrary', provider, networkId, owner); + Abstract = utils.getContract('Abstract', provider, networkId, owner); + Loops = utils.getContract('Loops', provider, networkId, owner) + + options = { + contracts: [ + Example, + ExampleRevert, + ExampleAssert, + UsesExample, + IsLibrary, + UsesLibrary, + Abstract, + Loops + ], + network: 'test', + network_id: networkId, + provider: provider, + logger: { + log: (val) => { if (val) output += `${val}\n`}, + error: (val) => { if (val) output += `${val}\n`} + } + } + deployer = new Deployer(options); + reporter = new Reporter(deployer); + }); + + afterEach(() => { + output = ''; + utils.cleanUp(); + deployer.finish(); + }); + + it('library not deployed', async function(){ + const migrate = function(){ + deployer.link(IsLibrary, UsesLibrary); + }; + + migrate(); + + try { + await deployer.start(); + assert.fail(); + } catch( err ) { + assert(output.includes('Error')); + assert(output.includes('IsLibrary')); + } + }); + + it('contract has no bytecode', async function(){ + const migrate = function(){ + deployer.deploy(Abstract); + } + + migrate(); + + try { + await deployer.start(); + assert.fail(); + } catch(err){ + assert(output.includes('Abstract')); + assert(output.includes('Error')); + assert(output.includes('interface')); + } + }); + + it('OOG (no constructor args)', async function(){ + const migrate = function(){ + deployer.deploy(Example, {gas: 10}); + }; + + migrate(); + + try { + await deployer.start(); + assert.fail(); + } catch(err){ + assert(output.includes('Example')); + assert(output.includes('value you set')); + assert(output.includes('Block limit')); + assert(output.includes('Gas sent')); + assert(output.includes('10')); + } + }); + + it('OOG (w/ constructor args)', async function(){ + const migrate = function(){ + deployer.deploy(UsesExample, utils.zeroAddress, {gas: 10}); + }; + + migrate(); + + try { + await deployer.start(); + assert.fail(); + } catch(err){ + assert(output.includes('UsesExample')); + assert(output.includes('value you set')); + assert(output.includes('Block limit')); + assert(output.includes('Gas sent')); + assert(output.includes('10')); + } + }); + + it('OOG (w/ estimate, hits block limit)', async function(){ + this.timeout(20000); + + const migrate = function(){ + deployer.deploy(Loops); + }; + + migrate(); + + try { + await deployer.start(); + assert.fail(); + } catch(err){ + assert(output.includes('Loops')); + assert(output.includes('out of gas')); + assert(output.includes('constructor')); + } + }); + + it('OOG (w/ param, hits block limit)', async function(){ + this.timeout(20000); + + const migrate = function(){ + deployer.deploy(Loops, {gas: 100000}); + }; + + migrate(); + + try { + await deployer.start(); + assert.fail(); + } catch(err){ + assert(output.includes('Loops')); + assert(output.includes('out of gas')); + assert(output.includes('Gas sent')); + assert(output.includes('Block limit')); + } + }); + + it('revert', async function(){ + migrate = function(){ + deployer.deploy(ExampleRevert); + } + + migrate(); + + try { + await deployer.start(); + assert.fail(); + } catch(err){ + assert(output.includes('revert')); + } + }); + + it('assert', async function(){ + migrate = function(){ + deployer.deploy(ExampleAssert); + } + + migrate(); + + try { + await deployer.start(); + assert.fail(); + } catch(err){ + assert(output.includes('invalid opcode')); + } + }); + + it('exceeds block limit', async function(){ + const block = await web3.eth.getBlock('latest'); + const gas = block.gasLimit + 1000; + + migrate = function(){ + deployer.deploy(Example, {gas: gas}); + } + + migrate(); + + try { + await deployer.start(); + assert.fail(); + } catch(err){ + assert(output.includes('Example')); + assert(output.includes('Block limit')); + assert(output.includes('Gas sent')) + assert(output.includes('less gas')); + } + }) + + it('insufficient funds', async function(){ + const emptyAccount = accounts[7] + let balance = await web3.eth.getBalance(emptyAccount); + await web3.eth.sendTransaction({ + to: accounts[0], + from: emptyAccount, + value: balance, + gasPrice: 0 + }); + + balance = await web3.eth.getBalance(emptyAccount); + assert(parseInt(balance) === 0); + + migrate = function(){ + deployer.deploy(Example, {from: emptyAccount}); + } + + migrate(); + + try { + await deployer.start(); + assert.fail(); + } catch(err){ + assert(output.includes('Example')); + assert(output.includes('insufficient funds')); + assert(output.includes('Account')) + assert(output.includes('Balance')); + } + }) +}); diff --git a/packages/truffle-deployer/test/helpers/reporter.js b/packages/truffle-deployer/test/helpers/reporter.js new file mode 100644 index 00000000000..af0e6a454d8 --- /dev/null +++ b/packages/truffle-deployer/test/helpers/reporter.js @@ -0,0 +1,284 @@ +/** + * Example reporter class that emulates the classic logger behavior + */ + +class Reporter { + constructor(deployer){ + this.deployingMany = false; + this.logConfirmations = false; + this.deployer = deployer; + this.separator = '\n'; + this.listen(); + } + + listen(){ + this.deployer.emitter.on('preDeploy', this.preDeploy.bind(this)); + this.deployer.emitter.on('postDeploy', this.postDeploy.bind(this)); + this.deployer.emitter.on('preDeployMany', this.preDeployMany.bind(this)); + this.deployer.emitter.on('postDeployMany', this.postDeployMany.bind(this)); + this.deployer.emitter.on('deployFailed', this.deployFailed.bind(this)); + this.deployer.emitter.on('linking', this.linking.bind(this)); + this.deployer.emitter.on('error', this.error.bind(this)); + this.deployer.emitter.on('transactionHash', this.hash.bind(this)); + this.deployer.emitter.on('receipt', this.receipt.bind(this)); + this.deployer.emitter.on('confirmation', this.confirmation.bind(this)); + } + + async preDeploy(payload){ + let message; + (payload.deployed) + ? message = this.messages('replacing', payload.contract, payload.deployed) + : message = this.messages('deploying', payload.contract, payload.deployed); + + !this.deployingMany && this.deployer.logger.log(message); + } + + async postDeploy(payload){ + let message; + (payload.deployed) + ? message = this.messages('deployed', payload.contract.contractName, payload.instance.address) + : message = this.messages('notDeployed', payload.contract.contractName, payload.instance.address); + + this.deployer.logger.log(message); + } + + async preDeployMany(batch){ + let message = this.messages('many'); + + this.deployingMany = true; + this.deployer.logger.log(message); + + batch.forEach(item => { + Array.isArray(item) + ? message = this.messages('listMany', item[0].contractName) + : message = this.messages('listMany', item.contractName) + + this.deployer.logger.log(message); + }) + + this.deployer.logger.log(this.separator); + } + + async postDeployMany(){ + this.deployingMany = false; + } + + async deployFailed(payload){ + const message = await this.processDeploymentError(payload); + this.deployer.logger.error(message) + } + + linking(payload){ + let message = this.messages('linking', payload.libraryName, payload.libraryAddress, payload.contractName); + this.deployer.logger.log(message); + } + + async error(payload){ + let message = this.messages(payload.type, payload.contract); + this.deployer.logger.error(message); + } + + async hash(payload){ + let message = this.messages('hash', payload.transactionHash, payload.contractName); + this.deployer.logger.log(message); + } + + async receipt(payload){ + let message = this.messages('receipt', payload.receipt.gasUsed, payload.contractName); + this.deployer.logger.log(message); + } + + async confirmation(payload){ + let message = this.messages('confirmation', payload.num, payload.receipt, payload.contractName); + this.logConfirmations && this.deployer.logger.log(message); + } + + underline(msg){ + const ul = '-'.repeat(msg.length); + return `\n${msg}\n${ul}`; + } + + messages(kind, ...args){ + const prefix = '\nError:'; + + args[0] = args[0] || {}; + args[1] = args[1] || {}; + args[2] = args[2] || {}; + args[3] = args[3] || {}; + + const kinds = { + + // --------------------------------------- Errors -------------------------------------------- + + noLibName: `${prefix} Cannot link a library with no name.\n`, + noLibAddress: `${prefix} "${args[0].contractName}" has no address. Has it been deployed?\n`, + + noBytecode: `${prefix} "${args[0].contractName}" ` + + `is an abstract contract or an interface and cannot be deployed\n` + + ` * Hint: just import the contract into the '.sol' file that uses it.\n`, + + intWithGas: `${prefix} "${args[0].contractName}" ran out of gas ` + + `(using a value you set in your network config or deployment parameters.)\n` + + ` * Block limit: ${args[2]}\n` + + ` * Gas sent: ${args[1]}\n`, + + intNoGas: `${prefix} "${args[0].contractName}" ran out of gas ` + + `(using Truffle's estimate.)\n` + + ` * Block limit: ${args[1]}\n` + + ` * Gas sent: ${args[2]}\n` + + ` * Try:\n` + + ` + Setting a higher gas estimate multiplier for this contract\n` + + ` + Using the solc optimizer settings in 'truffle.js'\n` + + ` + Making your contract smaller\n` + + ` + Making your contract constructor more efficient\n` + + ` + Setting a higher network block limit if you are on a\n` + + ` private network or test client (like ganache).\n`, + + oogNoGas: `${prefix} "${args[0].contractName}" ran out of gas. Something in the constructor ` + + `(ex: infinite loop) caused gas estimation to fail. Try:\n` + + ` * Making your contract constructor more efficient\n` + + ` * Setting the gas manually in your config or a deployment parameter\n` + + ` * Using the solc optimizer settings in 'truffle.js'\n` + + ` * Setting a higher network block limit if you are on a\n` + + ` private network or test client (like ganache).\n`, + + rvtReason: `Revert with string error not implemented yet.`, + asrtReason: `Assert with string error not implemented yet.`, + + rvtNoReason: `${prefix} "${args[0].contractName}" hit a require or revert statement ` + + `somewhere in its constructor. Try:\n` + + ` * Verifying that your constructor params satisfy all require conditions.\n` + + ` * Adding reason strings to your require statements.\n`, + + asrtNoReason: `${prefix} "${args[0].contractName}" hit an invalid opcode while deploying. Try:\n` + + ` * Verifying that your constructor params satisfy all assert conditions.\n` + + ` * Verifying your constructor code doesn't access an array out of bounds.\n` + + ` * Adding reason strings to your assert statements.\n`, + + noMoney: `${prefix} "${args[0].contractName}" could not deploy due to insufficient funds\n` + + ` * Account: ${args[1]}\n` + + ` * Balance: ${args[2]} wei\n` + + ` * Message: ${args[3]}\n` + + ` * Try:\n` + + ` + Using an adequately funded account\n` + + ` + If you are using a local Geth node, verify that your node is synced.\n`, + + blockWithGas: `${prefix} "${args[0].contractName}" exceeded the block limit ` + + `(with a gas value you set).\n` + + ` * Block limit: ${args[2]}\n` + + ` * Gas sent: ${args[1]}\n` + + ` * Try:\n` + + ` + Sending less gas.\n` + + ` + Setting a higher network block limit if you are on a\n` + + ` private network or test client (like ganache).\n`, + + blockNoGas: `${prefix} "${args[0].contractName}" exceeded the block limit ` + + `(using Truffle's estimate).\n` + + ` * Block limit: ${args[1]}\n` + + ` * Report this error in the Truffle issues on Github. It should not happen.\n` + + ` * Try: setting gas manually in 'truffle.js' or as parameter to 'deployer.deploy'\n`, + + nonce: `${prefix} "${args[0].contractName}" received: ${args[1]}.\n` + + ` * This error is common when Infura is under heavy network load.\n` + + ` * Try: setting the 'confirmations' key in your network config\n` + + ` to wait for several block confirmations between each deployment.\n`, + + default: `${prefix} "${args[0].contractName}" -- ${args[1]}.\n`, + + // ------------------------------------ Successes -------------------------------------------- + + deploying: this.underline('Deploying'), + replacing: this.underline('Replacing'), + reusing: this.underline('Re-using'), + many: this.underline('Deploying Batch'), + linking: this.underline('Linking') + '\n' + + `${args[2]}`.padEnd(20) + ` > ${args[0]}`.padEnd(25) + `(${args[1]})`, + + listMany: `* ${args[0]}`, + deployed: `${args[0]}`.padEnd(20) + ' > address:'.padEnd(25) + args[1], + hash: `${args[1]}`.padEnd(20) + ' > transaction hash:'.padEnd(25) + args[0], + receipt: `${args[1]}`.padEnd(20) + ' > gas usage:'.padEnd(25) + args[0], + confirmation: `${args[2]}`.padEnd(20) + ' > confirmation number:'.padEnd(25) + args[0], + } + + return kinds[kind]; + } + + async processDeploymentError(payload){ + let message; + + const error = payload.estimateError || payload.error; + + const errors = { + INT: error.message.includes('base fee') || error.message.includes('intrinsic'), + OOG: error.message.includes('out of gas'), + RVT: error.message.includes('revert'), + ETH: error.message.includes('funds'), + BLK: error.message.includes('block gas limit'), + NCE: error.message.includes('nonce'), + INV: error.message.includes('invalid opcode'), + } + + let type = Object.keys(errors).find(key => errors[key]); + + switch (type) { + case 'INT': + (payload.gas) + ? message = this.messages('intWithGas', payload.contract, payload.gas, payload.blockLimit) + : message = this.messages('intNoGas', payload.contract, payload.blockLimit, payload.estimate); + + this.deployer.logger.error(message); + break; + + case 'OOG': + (payload.gas) + ? message = this.messages('intWithGas', payload.contract, payload.gas, payload.blockLimit) + : message = this.messages('oogNoGas', payload.contract, payload.blockLimit, payload.estimate); + + this.deployer.logger.error(message); + break; + + case 'RVT': + (payload.reason) + ? message = this.messages('rvtReason', payload.contract, payload.reason) + : message = this.messages('rvtNoReason', payload.contract); + + this.deployer.logger.error(message); + break; + + case 'INV': + (payload.reason) + ? message = this.messages('asrtReason', payload.contract, payload.reason) + : message = this.messages('asrtNoReason', payload.contract); + + this.deployer.logger.error(message); + break; + + case 'BLK': + (payload.gas) + ? message = this.messages('blockWithGas', payload.contract, payload.gas, payload.blockLimit) + : message = this.messages('blockNoGas', payload.contract, payload.blockLimit) + + this.deployer.logger.error(message); + break; + + case 'ETH': + let balance = await payload.contract.web3.eth.getBalance(payload.from); + balance = balance.toString(); + message = this.messages('noMoney', payload.contract, payload.from, balance, error.message); + this.deployer.logger.error(message); + break; + + case 'NCE': + message = this.messages('nonce', payload.contract, error.message); + this.deployer.logger.error(message); + break; + + default: + message = this.messages('default', payload.contract, error.message); + this.deployer.logger.error(message); + } + } +} + +module.exports = Reporter; \ No newline at end of file diff --git a/packages/truffle-deployer/test/utils.js b/packages/truffle-deployer/test/helpers/utils.js similarity index 70% rename from packages/truffle-deployer/test/utils.js rename to packages/truffle-deployer/test/helpers/utils.js index 85b5c789ed4..a0ca8724d81 100644 --- a/packages/truffle-deployer/test/utils.js +++ b/packages/truffle-deployer/test/helpers/utils.js @@ -4,8 +4,15 @@ const path = require('path'); const fs = require('fs-extra'); const utils = { - buildDir: path.join(__dirname, './build'), - sourcesDir: path.join(__dirname, './sources'), + + miningId: null, + + // Constants + zeroAddress: '0x0000000000000000000000000000000000000000', + + // Paths + buildDir: path.join(__dirname, '../build'), + sourcesDir: path.join(__dirname, '../sources'), compile: async function(){ const config = { @@ -30,10 +37,18 @@ const utils = { }); }, + startAutoMine: function(web3, interval){ + utils.miningId = setInterval(async() => { + await utils.evm_mine(web3); + }, interval); + }, + + stopAutoMine: () => clearInterval(utils.miningId), + cleanUp: () => fs.removeSync(utils.buildDir), getContract: function(name, provider, networkId, account){ - const json = require(`./build/${name}`); + const json = require(`../build/${name}`); const contract = TruffleContract(json) contract.setProvider(provider); contract.setNetwork(networkId); diff --git a/packages/truffle-deployer/test/reporters/asyncReporter.js b/packages/truffle-deployer/test/reporters/asyncReporter.js deleted file mode 100644 index bfac220dd47..00000000000 --- a/packages/truffle-deployer/test/reporters/asyncReporter.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Example reporter for testing async remote control of a deployer - * via the `preDeploy`, `postDeploy` and `link` events. - */ - -class AsyncReporter { - constructor(deployer, web3, mine){ - this.blocks = []; - this.deployer = deployer; - this.mine = mine; - this.web3 = web3; - this.listen(); - } - - async mineAndRecordBlock(name, args){ - if (name === 'postDeploy'){ - await this.mine(this.web3); - await this.mine(this.web3); - const blockNumber = await this.web3.eth.getBlockNumber(); - this.blocks.unshift(blockNumber); - } else if (name === 'confirmation'){ - // etc - } - } - - listen(){ - this.deployer.emitter.on('preDeploy', this.mineAndRecordBlock.bind(this, 'preDeploy')); - this.deployer.emitter.on('postDeploy', this.mineAndRecordBlock.bind(this, 'postDeploy')); - this.deployer.emitter.on('linking', this.mineAndRecordBlock.bind(this, 'linking')); - this.deployer.emitter.on('error', this.mineAndRecordBlock.bind(this, 'error')); - this.deployer.emitter.on('receipt', this.mineAndRecordBlock.bind(this, 'receipt')); - this.deployer.emitter.on('transactionHash', this.mineAndRecordBlock.bind(this, 'transactionHash')); - this.deployer.emitter.on('confirmation', this.mineAndRecordBlock.bind(this, 'confirmation')); - } -} - -module.exports = AsyncReporter; diff --git a/packages/truffle-deployer/test/reporters/syncReporter.js b/packages/truffle-deployer/test/reporters/syncReporter.js deleted file mode 100644 index 93b2bde8c25..00000000000 --- a/packages/truffle-deployer/test/reporters/syncReporter.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Example reporter class that emulates the classic logger behavior - */ - -class SyncReporter { - constructor(deployer){ - this.deployer = deployer; - this.listen(); - } - - listen(){ - this.deployer.emitter.on('preDeploy', this.preDeploy.bind(this)); - this.deployer.emitter.on('postDeploy', this.postDeploy.bind(this)); - this.deployer.emitter.on('linking', this.linking.bind(this)); - this.deployer.emitter.on('error', this.error.bind(this)); - this.deployer.emitter.on('transactionHash', this.hash.bind(this)); - this.deployer.emitter.on('confirmation', this.confirmation.bind(this)); - } - - preDeploy(payload){ - let message; - (payload.deployed) - ? message = this.messages('replacing', payload.contract, payload.deployed) - : message = this.messages('deploying', payload.contract, payload.deployed); - - this.deployer.logger.log(message); - } - - postDeploy(payload){ - let message; - (payload.deployed) - ? message = this.messages('deployed', payload.contract, payload.instance) - : message = this.messages('notDeployed', payload.contract, payload.instance); - - this.deployer.logger.log(message); - } - - linking(payload){ - let message = this.messages('linking', payload.library, payload.destination); - this.deployer.logger.log(message); - } - - error(payload){ - let message = this.messages(payload.type, payload.contract); - this.deployer.logger.log(message); - } - - hash(payload){ - let message = this.messages('hash', payload.transactionHash); - this.deployer.logger.log(message); - } - - confirmation(payload){ - let message = this.messages('confirmation', payload.num, payload.receipt); - this.deployer.logger.log(message); - } - - messages(kind, ...args){ - args[0] = args[0] || {}; - args[1] = args[1] || {}; - - const kinds = { - noLibName: `Link error: Cannot link a library with no name.`, - noLibAddress: `Link error: ${args[0].contractName} has no address. Has it been deployed?`, - deploying: `Deploying ${args[0].contractName}...`, - replacing: `Replacing ${args[0].contractName}...`, - deployed: `${args[0].contractName}: ${args[1].address}`, - notDeployed: `Didn't deploy ${args[0].contractName}; using ${args[1].address}`, - linking: `Linking ${args[0].contractName} ${args[1].contract_name}`, - hash: `Transaction: ${args[0]}`, - confirmation: `Confirmation number: ${args[0]}`, - } - - return kinds[kind]; - } -} - -module.exports = SyncReporter; \ No newline at end of file diff --git a/packages/truffle-deployer/test/sources/Abstract.sol b/packages/truffle-deployer/test/sources/Abstract.sol new file mode 100644 index 00000000000..1a4b29d7511 --- /dev/null +++ b/packages/truffle-deployer/test/sources/Abstract.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.4.4; + +contract Abstract { + function method() public; +} \ No newline at end of file diff --git a/packages/truffle-deployer/test/sources/ExampleAssert.sol b/packages/truffle-deployer/test/sources/ExampleAssert.sol new file mode 100644 index 00000000000..af4b360117b --- /dev/null +++ b/packages/truffle-deployer/test/sources/ExampleAssert.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.4.4; + + +contract ExampleAssert { + constructor() public { + assert(false); + } +} \ No newline at end of file diff --git a/packages/truffle-deployer/test/sources/ExampleError.sol b/packages/truffle-deployer/test/sources/ExampleRevert.sol similarity index 55% rename from packages/truffle-deployer/test/sources/ExampleError.sol rename to packages/truffle-deployer/test/sources/ExampleRevert.sol index e512e6fe74a..54e9d523dba 100644 --- a/packages/truffle-deployer/test/sources/ExampleError.sol +++ b/packages/truffle-deployer/test/sources/ExampleRevert.sol @@ -1,8 +1,7 @@ pragma solidity ^0.4.4; -contract ExampleError { - string public id = 'ExampleError'; +contract ExampleRevert { constructor() public { require(false); } diff --git a/packages/truffle-deployer/test/sources/IsLibrary.sol b/packages/truffle-deployer/test/sources/IsLibrary.sol index 6b4489dd179..b7ed3c78d8f 100644 --- a/packages/truffle-deployer/test/sources/IsLibrary.sol +++ b/packages/truffle-deployer/test/sources/IsLibrary.sol @@ -1,9 +1,10 @@ pragma solidity ^0.4.4; library IsLibrary { + string constant public id = 'IsLibrary'; event IsLibraryEvent(uint eventID); - function fireIsLibraryEvent(uint id) public { - emit IsLibraryEvent(id); + function fireIsLibraryEvent(uint _id) public { + emit IsLibraryEvent(_id); } } \ No newline at end of file diff --git a/packages/truffle-deployer/test/sources/Loop.sol b/packages/truffle-deployer/test/sources/Loop.sol new file mode 100644 index 00000000000..d995a3c77f8 --- /dev/null +++ b/packages/truffle-deployer/test/sources/Loop.sol @@ -0,0 +1,11 @@ +pragma solidity ^0.4.4; + + +contract Loops { + uint public id; + constructor() public { + for(uint i = 0; i < 10000; i++){ + id = i; + } + } +} \ No newline at end of file From 8348d6250c4fbb848cb7e69750a5140b67005418 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 6 Jun 2018 13:36:00 -0700 Subject: [PATCH 13/74] Revert changes to deferredchain / add test --- .../truffle-deployer/src/deferredchain.js | 92 ++++++++++--------- packages/truffle-deployer/test/deployer.js | 25 +++++ 2 files changed, 72 insertions(+), 45 deletions(-) diff --git a/packages/truffle-deployer/src/deferredchain.js b/packages/truffle-deployer/src/deferredchain.js index fddb0962cfe..a6a0af83f9b 100644 --- a/packages/truffle-deployer/src/deferredchain.js +++ b/packages/truffle-deployer/src/deferredchain.js @@ -1,45 +1,47 @@ -class DeferredChain { - constructor(){ - const self = this; - - this.chain = new Promise(function(accept, reject){ - self._accept = accept; - self._reject = reject; - }); - - this.await = new Promise(function(){ - self._done = arguments[0]; - self._error = arguments[1]; - }); - - this.started = false; - } - - then(fn){ - this.chain = this.chain.then(() => { - var args = Array.prototype.slice.call(arguments); - return fn.apply(null, args); - }); - - this.chain = this.chain.catch(e => this._error(e)); - return this; - } - - catch(fn){ - this.chain = this.chain.catch(() => { - var args = Array.prototype.slice.call(arguments); - return fn.apply(null, args); - }); - - return this; - } - - start(){ - this.started = true; - this.chain = this.chain.then(this._done); - this._accept(); - return this.await; - } -} - -module.exports = DeferredChain; +function DeferredChain() { + var self = this; + this.chain = new Promise(function(accept, reject) { + self._accept = accept; + self._reject = reject; + }); + + this.await = new Promise(function() { + self._done = arguments[0]; + self._error = arguments[1]; + }); + this.started = false; +}; + +DeferredChain.prototype.then = function(fn) { + var self = this; + this.chain = this.chain.then(function() { + var args = Array.prototype.slice.call(arguments); + + return fn.apply(null, args); + }); + this.chain = this.chain.catch(function(e) { + self._error(e); + }); + + return this; +}; + +DeferredChain.prototype.catch = function(fn) { + var self = this; + this.chain = this.chain.catch(function() { + var args = Array.prototype.slice.call(arguments); + + return fn.apply(null, args); + }); + + return this; +}; + +DeferredChain.prototype.start = function() { + this.started = true; + this.chain = this.chain.then(this._done); + this._accept(); + return this.await; +}; + +module.exports = DeferredChain; \ No newline at end of file diff --git a/packages/truffle-deployer/test/deployer.js b/packages/truffle-deployer/test/deployer.js index 70a219db9c2..d59f88df4ff 100644 --- a/packages/truffle-deployer/test/deployer.js +++ b/packages/truffle-deployer/test/deployer.js @@ -6,6 +6,7 @@ const assert = require("assert"); const Deployer = require("../index"); const Reporter = require("./helpers/reporter") const utils = require('./helpers/utils'); +const util = require('util'); describe("Deployer (sync)", function() { let owner @@ -193,6 +194,30 @@ describe("Deployer (sync)", function() { assert(output.includes('UsesLibrary')); }); + // There's a chain like this in the truffle-core solidity-tests + it('deployer.deploy().then()', async function(){ + const migrate = function(){ + deployer.deploy(Example).then(function() { + return Example.deployed(); + }).then(function(instance) { + return instance.id(); + }).then(function(id) { + return deployer.deploy(UsesExample, utils.zeroAddress) + .then(function() { + return UsesExample.deployed(); + }) + .then(function(usesExample) { + return usesExample.id(); + }) + }) + } + migrate(); + + await deployer.start(); + assert(output.includes('Example')); + assert(output.includes('UsesExample')); + }) + it('waits for confirmations', async function(){ this.timeout(5000); const startBlock = await web3.eth.getBlockNumber(); From c32b116c7398f915b8f63a777e3c6810bf0df2c9 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Mon, 11 Jun 2018 19:20:20 -0700 Subject: [PATCH 14/74] Migrations scenario tests (draft) --- packages/truffle/package.json | 3 +- .../truffle/test/scenarios/commandrunner.js | 10 +- .../migrations/2_deploy_contract.js.template | 9 -- .../test/scenarios/migrations/errors.js | 97 +++++++++++++++++++ .../test/scenarios/migrations/migrate.js | 84 ++++++++++++++++ .../test/scenarios/migrations/parameters.js | 69 ------------- .../migrations/async/contracts/Migrations.sol | 23 +++++ .../async/migrations/1_initial_migration.js | 5 + .../test/sources/migrations/async/truffle.js | 4 + .../migrations/error/contracts/Abstract.sol | 5 + .../migrations/error/contracts/Example.sol | 7 ++ .../error/contracts/ExampleAssert.sol | 8 ++ .../error/contracts/ExampleRevert.sol | 8 ++ .../migrations/error/contracts/Loops.sol | 11 +++ .../migrations/error/contracts/Migrations.sol | 23 +++++ .../error/contracts/UsesExample.sol | 10 ++ .../error/migrations/1_initial_migration.js | 5 + .../error/migrations/2_migrations_revert.js | 9 ++ .../error/migrations/3_migrations_ok.js | 5 + .../error/migrations/4_migrations_oog.js | 5 + .../test/sources/migrations/error/truffle.js | 12 +++ .../migrations/success/contracts/Example.sol | 7 ++ .../success/contracts/IsLibrary.sol | 10 ++ .../migrations/success/contracts/Loop.sol | 11 +++ .../success/contracts/Migrations.sol | 23 +++++ .../success/contracts/UsesExample.sol | 10 ++ .../success/contracts/UsesLibrary.sol | 18 ++++ .../success/migrations/1_initial_migration.js | 5 + .../success/migrations/2_migrations_sync.js | 8 ++ .../success/migrations/3_migrations_async.js | 10 ++ .../sources/migrations/success/truffle.js | 13 +++ 31 files changed, 447 insertions(+), 80 deletions(-) delete mode 100644 packages/truffle/test/scenarios/migrations/2_deploy_contract.js.template create mode 100644 packages/truffle/test/scenarios/migrations/errors.js create mode 100644 packages/truffle/test/scenarios/migrations/migrate.js delete mode 100644 packages/truffle/test/scenarios/migrations/parameters.js create mode 100644 packages/truffle/test/sources/migrations/async/contracts/Migrations.sol create mode 100644 packages/truffle/test/sources/migrations/async/migrations/1_initial_migration.js create mode 100644 packages/truffle/test/sources/migrations/async/truffle.js create mode 100644 packages/truffle/test/sources/migrations/error/contracts/Abstract.sol create mode 100644 packages/truffle/test/sources/migrations/error/contracts/Example.sol create mode 100644 packages/truffle/test/sources/migrations/error/contracts/ExampleAssert.sol create mode 100644 packages/truffle/test/sources/migrations/error/contracts/ExampleRevert.sol create mode 100644 packages/truffle/test/sources/migrations/error/contracts/Loops.sol create mode 100644 packages/truffle/test/sources/migrations/error/contracts/Migrations.sol create mode 100644 packages/truffle/test/sources/migrations/error/contracts/UsesExample.sol create mode 100644 packages/truffle/test/sources/migrations/error/migrations/1_initial_migration.js create mode 100644 packages/truffle/test/sources/migrations/error/migrations/2_migrations_revert.js create mode 100644 packages/truffle/test/sources/migrations/error/migrations/3_migrations_ok.js create mode 100644 packages/truffle/test/sources/migrations/error/migrations/4_migrations_oog.js create mode 100644 packages/truffle/test/sources/migrations/error/truffle.js create mode 100644 packages/truffle/test/sources/migrations/success/contracts/Example.sol create mode 100644 packages/truffle/test/sources/migrations/success/contracts/IsLibrary.sol create mode 100644 packages/truffle/test/sources/migrations/success/contracts/Loop.sol create mode 100644 packages/truffle/test/sources/migrations/success/contracts/Migrations.sol create mode 100644 packages/truffle/test/sources/migrations/success/contracts/UsesExample.sol create mode 100644 packages/truffle/test/sources/migrations/success/contracts/UsesLibrary.sol create mode 100644 packages/truffle/test/sources/migrations/success/migrations/1_initial_migration.js create mode 100644 packages/truffle/test/sources/migrations/success/migrations/2_migrations_sync.js create mode 100644 packages/truffle/test/sources/migrations/success/migrations/3_migrations_async.js create mode 100644 packages/truffle/test/sources/migrations/success/truffle.js diff --git a/packages/truffle/package.json b/packages/truffle/package.json index 028f905c8dc..a5c6bdefc4c 100644 --- a/packages/truffle/package.json +++ b/packages/truffle/package.json @@ -39,7 +39,8 @@ "build-cli": "webpack --config ./cli.webpack.config.js", "test": "npm run build-cli && mocha --colors", "publish:byoc": "node ./scripts/prereleaseVersion.js byoc-safe byoc", - "publish:next": "node ./scripts/prereleaseVersion.js next next" + "publish:next": "node ./scripts/prereleaseVersion.js next next", + "test:raw": "NO_BUILD=true mocha" }, "repository": { "type": "git", diff --git a/packages/truffle/test/scenarios/commandrunner.js b/packages/truffle/test/scenarios/commandrunner.js index 278b0d2f51a..9d8821ad5fd 100644 --- a/packages/truffle/test/scenarios/commandrunner.js +++ b/packages/truffle/test/scenarios/commandrunner.js @@ -4,7 +4,15 @@ var path = require("path"); module.exports = { run: function(command, config, callback) { - var execString = "node " + + var execString; + + (process.env.NO_BUILD) + ? execString = "node " + + path.join(__dirname, "../", "../", "node_modules", + "truffle-core", "cli.js") + + " " + command + + : execString = "node " + path.join(__dirname, "../", "../", "build", "cli.bundled.js") + " " + command; diff --git a/packages/truffle/test/scenarios/migrations/2_deploy_contract.js.template b/packages/truffle/test/scenarios/migrations/2_deploy_contract.js.template deleted file mode 100644 index 768e01a166d..00000000000 --- a/packages/truffle/test/scenarios/migrations/2_deploy_contract.js.template +++ /dev/null @@ -1,9 +0,0 @@ -var fs = require("fs"); -var path = require("path"); -module.exports = function(deployer, network, accounts) { - // Write the network and accounts output out to a file so we can test it. - fs.writeFileSync(path.join(__dirname, "output.json"), JSON.stringify({ - network: network, - accounts: accounts - }), "utf8"); -}; diff --git a/packages/truffle/test/scenarios/migrations/errors.js b/packages/truffle/test/scenarios/migrations/errors.js new file mode 100644 index 00000000000..9bf332f1563 --- /dev/null +++ b/packages/truffle/test/scenarios/migrations/errors.js @@ -0,0 +1,97 @@ +const MemoryLogger = require("../memorylogger"); +const CommandRunner = require("../commandrunner"); +const fs = require("fs"); +const path = require("path"); +const assert = require("assert"); +const Server = require("../server"); +const Reporter = require("../reporter"); +const sandbox = require("../sandbox"); +const Web3 = require('web3'); + +const log = console.log; + +const util = require('util'); + +function processErr(err, output){ + if (err){ + log(output); + throw new Error(err); + } +} + +describe("migration errors", function() { + let config; + let web3; + let networkId; + const project = path.join(__dirname, '../../sources/migrations/error'); + const logger = new MemoryLogger(); + + before(done => Server.start(done)); + after(done => Server.stop(done)); + + before(async function() { + this.timeout(10000); + config = await sandbox.create(project) + config.network = "development"; + config.logger = logger; + config.mocha = { + reporter: new Reporter(logger) + } + + const provider = new Web3.providers.HttpProvider('http://localhost:8545') + web3 = new Web3(provider); + networkId = await web3.eth.net.getId(); + }); + + it("should error and stop", function(done) { + this.timeout(20000); + + CommandRunner.run("migrate", config, err => { + const output = logger.contents(); + console.log(output) + assert(err); + + assert(output.includes('2_migrations_revert.js')); + assert(output.includes("Deploying 'Example'")) + assert(output.includes("Deploying 'ExampleRevert'")); + assert(output.includes("Error")); + assert(!output.includes("Deploying 'UsesExample'")) + assert(!output.includes('3_migrations_ok.js')); + + const location = path.join(config.contracts_build_directory, "UsesExample.json"); + const artifact = require(location); + const network = artifact.networks[networkId]; + assert(network === undefined); + done(); + }) + }); + + it("should run from the last successfully completely migration", function(done) { + this.timeout(20000); + + CommandRunner.run("migrate", config, err => { + const output = logger.contents(); + console.log(output) + assert(err); + + assert(!output.includes('1_initial_migration.js')) + assert(output.includes('2_migrations_revert.js')); + done(); + }) + }); + + it("should run out of gas correctly", function(done){ + this.timeout(20000); + + CommandRunner.run("migrate -f 4", config, err => { + const output = logger.contents(); + assert(err); + console.log(output) + assert(output.includes('4_migrations_oog.js')); + assert(output.includes("Deploying 'Loops'")); + assert(output.includes('Error')); + assert(output.includes('out of gas')); + done(); + }); + }) +}); \ No newline at end of file diff --git a/packages/truffle/test/scenarios/migrations/migrate.js b/packages/truffle/test/scenarios/migrations/migrate.js new file mode 100644 index 00000000000..531dc9dacd7 --- /dev/null +++ b/packages/truffle/test/scenarios/migrations/migrate.js @@ -0,0 +1,84 @@ +const MemoryLogger = require("../memorylogger"); +const CommandRunner = require("../commandrunner"); +const fs = require("fs"); +const path = require("path"); +const assert = require("assert"); +const Server = require("../server"); +const Reporter = require("../reporter"); +const sandbox = require("../sandbox"); +const Web3 = require('web3'); + +const log = console.log; + +const util = require('util'); + +function processErr(err, output){ + if (err){ + log(output); + throw new Error(err); + } +} + +describe("migrate (sync)", function() { + let config; + let web3; + let networkId; + const project = path.join(__dirname, '../../sources/migrations/success'); + const logger = new MemoryLogger(); + + before(done => Server.start(done)); + after(done => Server.stop(done)); + + before(async function() { + this.timeout(10000); + config = await sandbox.create(project) + config.network = "development"; + config.logger = logger; + config.mocha = { + reporter: new Reporter(logger) + } + + const provider = new Web3.providers.HttpProvider('http://localhost:8545') + web3 = new Web3(provider); + networkId = await web3.eth.net.getId(); + }); + + it("runs migrations (sync & async/await)", function(done) { + this.timeout(20000); + + CommandRunner.run("migrate", config, err => { + const output = logger.contents(); + processErr(err, output); + + assert(output.includes('2_migrations_sync.js')); + assert(output.includes("Deploying 'UsesExample'")) + assert(output.includes('3_migrations_async.js')); + assert(output.includes("Replacing 'UsesExample'")) + + const location = path.join(config.contracts_build_directory, "UsesExample.json"); + const artifact = require(location); + const network = artifact.networks[networkId]; + + assert(output.includes(network.transactionHash)); + assert(output.includes(network.address)); + + console.log(output) + done(); + }) + }); + + it('uses forces a migration -f option', function(done){ + this.timeout(20000); + + CommandRunner.run("migrate -f 3", config, err => { + const output = logger.contents(); + processErr(err, output); + assert(!output.includes('2_migrations_sync.js')); + assert(output.includes('3_migrations_async.js')); + assert(output.includes("Replacing 'IsLibrary'")) + assert(output.includes("Replacing 'UsesLibrary'")); + console.log(output) + done(); + }) + }); +}); \ No newline at end of file diff --git a/packages/truffle/test/scenarios/migrations/parameters.js b/packages/truffle/test/scenarios/migrations/parameters.js deleted file mode 100644 index 9f161f167e3..00000000000 --- a/packages/truffle/test/scenarios/migrations/parameters.js +++ /dev/null @@ -1,69 +0,0 @@ -var Box = require("truffle-box"); -var MemoryLogger = require("../memorylogger"); -var CommandRunner = require("../commandrunner"); -var contract = require("truffle-contract"); -var fs = require("fs-extra"); -var path = require("path"); -var assert = require("assert"); -var Reporter = require("../reporter"); -var Server = require("../server"); -var Web3 = require("web3"); - -describe("Migration Parameters", function() { - var config; - var logger = new MemoryLogger(); - var accounts; - - before("set up the server", function(done) { - Server.start(done); - }); - - after("stop server", function(done) { - Server.stop(done); - }); - - before("set up sandbox", function(done) { - this.timeout(10000); - Box.sandbox("default#web3-one", function(err, conf) { - if (err) return done(err); - config = conf; - config.logger = logger; - config.network = "development"; - config.mocha = { - reporter: new Reporter(logger) - } - done(); - }); - }); - - before("copy template migration", function() { - fs.copySync(path.join(__dirname, "2_deploy_contract.js.template"), path.join(config.migrations_directory, "2_deploy_contract.js")); - }); - - before("get accounts", function(done) { - var web3 = new Web3(config.provider); - web3.eth.getAccounts(function(err, accs) { - if (err) return done(err); - accounts = accs; - done(); - }); - }); - - it("will migrate and save the correct output data", function(done) { - this.timeout(50000); - - var expected_file = path.join(config.migrations_directory, "output.json"); - - CommandRunner.run("migrate", config, function(err) { - if (err) return done(err); - - var data = fs.readFileSync(expected_file, "utf8"); - data = JSON.parse(data); - - assert.equal(data.network, "development"); - assert.deepEqual(data.accounts, accounts); - - done(); - }); - }); -}); diff --git a/packages/truffle/test/sources/migrations/async/contracts/Migrations.sol b/packages/truffle/test/sources/migrations/async/contracts/Migrations.sol new file mode 100644 index 00000000000..c4efb65e216 --- /dev/null +++ b/packages/truffle/test/sources/migrations/async/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.23; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + constructor() public { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/packages/truffle/test/sources/migrations/async/migrations/1_initial_migration.js b/packages/truffle/test/sources/migrations/async/migrations/1_initial_migration.js new file mode 100644 index 00000000000..4d5f3f9b02e --- /dev/null +++ b/packages/truffle/test/sources/migrations/async/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +var Migrations = artifacts.require("./Migrations.sol"); + +module.exports = function(deployer) { + deployer.deploy(Migrations); +}; diff --git a/packages/truffle/test/sources/migrations/async/truffle.js b/packages/truffle/test/sources/migrations/async/truffle.js new file mode 100644 index 00000000000..a6330d6d564 --- /dev/null +++ b/packages/truffle/test/sources/migrations/async/truffle.js @@ -0,0 +1,4 @@ +module.exports = { + // See + // to customize your Truffle configuration! +}; diff --git a/packages/truffle/test/sources/migrations/error/contracts/Abstract.sol b/packages/truffle/test/sources/migrations/error/contracts/Abstract.sol new file mode 100644 index 00000000000..1a4b29d7511 --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/contracts/Abstract.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.4.4; + +contract Abstract { + function method() public; +} \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/error/contracts/Example.sol b/packages/truffle/test/sources/migrations/error/contracts/Example.sol new file mode 100644 index 00000000000..9ea1e534f32 --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/contracts/Example.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.4; + + +contract Example { + string public id = 'Example'; + constructor() public {} +} diff --git a/packages/truffle/test/sources/migrations/error/contracts/ExampleAssert.sol b/packages/truffle/test/sources/migrations/error/contracts/ExampleAssert.sol new file mode 100644 index 00000000000..af4b360117b --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/contracts/ExampleAssert.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.4.4; + + +contract ExampleAssert { + constructor() public { + assert(false); + } +} \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/error/contracts/ExampleRevert.sol b/packages/truffle/test/sources/migrations/error/contracts/ExampleRevert.sol new file mode 100644 index 00000000000..54e9d523dba --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/contracts/ExampleRevert.sol @@ -0,0 +1,8 @@ +pragma solidity ^0.4.4; + + +contract ExampleRevert { + constructor() public { + require(false); + } +} diff --git a/packages/truffle/test/sources/migrations/error/contracts/Loops.sol b/packages/truffle/test/sources/migrations/error/contracts/Loops.sol new file mode 100644 index 00000000000..d995a3c77f8 --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/contracts/Loops.sol @@ -0,0 +1,11 @@ +pragma solidity ^0.4.4; + + +contract Loops { + uint public id; + constructor() public { + for(uint i = 0; i < 10000; i++){ + id = i; + } + } +} \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/error/contracts/Migrations.sol b/packages/truffle/test/sources/migrations/error/contracts/Migrations.sol new file mode 100644 index 00000000000..c4efb65e216 --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.23; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + constructor() public { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/packages/truffle/test/sources/migrations/error/contracts/UsesExample.sol b/packages/truffle/test/sources/migrations/error/contracts/UsesExample.sol new file mode 100644 index 00000000000..ba502c3d363 --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/contracts/UsesExample.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.4; + + +contract UsesExample { + string public id = 'UsesExample'; + address public other; + constructor(address _other) public { + other = _other; + } +} diff --git a/packages/truffle/test/sources/migrations/error/migrations/1_initial_migration.js b/packages/truffle/test/sources/migrations/error/migrations/1_initial_migration.js new file mode 100644 index 00000000000..4d5f3f9b02e --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +var Migrations = artifacts.require("./Migrations.sol"); + +module.exports = function(deployer) { + deployer.deploy(Migrations); +}; diff --git a/packages/truffle/test/sources/migrations/error/migrations/2_migrations_revert.js b/packages/truffle/test/sources/migrations/error/migrations/2_migrations_revert.js new file mode 100644 index 00000000000..f48df498845 --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/migrations/2_migrations_revert.js @@ -0,0 +1,9 @@ +const Example = artifacts.require("Example"); +const ExampleRevert = artifacts.require("ExampleRevert"); +const UsesExample = artifacts.require("UsesExample"); + +module.exports = async function(deployer, network, accounts) { + await deployer.deploy(Example); + await deployer.deploy(ExampleRevert); + await deployer.deploy(UsesExample, Example.address); +}; \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/error/migrations/3_migrations_ok.js b/packages/truffle/test/sources/migrations/error/migrations/3_migrations_ok.js new file mode 100644 index 00000000000..0952576efe4 --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/migrations/3_migrations_ok.js @@ -0,0 +1,5 @@ +const UsesExample = artifacts.require("UsesExample"); + +module.exports = async function(deployer, network, accounts) { + await deployer.deploy(UsesExample, accounts[1]); +}; diff --git a/packages/truffle/test/sources/migrations/error/migrations/4_migrations_oog.js b/packages/truffle/test/sources/migrations/error/migrations/4_migrations_oog.js new file mode 100644 index 00000000000..8495831bbbe --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/migrations/4_migrations_oog.js @@ -0,0 +1,5 @@ +const Loops = artifacts.require("Loops"); + +module.exports = async function(deployer, network, accounts) { + await deployer.deploy(Loops); +}; \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/error/truffle.js b/packages/truffle/test/sources/migrations/error/truffle.js new file mode 100644 index 00000000000..66d168df689 --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/truffle.js @@ -0,0 +1,12 @@ +module.exports = { + networks: { + development: { + host: "127.0.0.1", + port: 8545, + network_id: '*', + gas: 4700000, + gasPrice: 20000000000, + }, + }, +}; + diff --git a/packages/truffle/test/sources/migrations/success/contracts/Example.sol b/packages/truffle/test/sources/migrations/success/contracts/Example.sol new file mode 100644 index 00000000000..9ea1e534f32 --- /dev/null +++ b/packages/truffle/test/sources/migrations/success/contracts/Example.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.4; + + +contract Example { + string public id = 'Example'; + constructor() public {} +} diff --git a/packages/truffle/test/sources/migrations/success/contracts/IsLibrary.sol b/packages/truffle/test/sources/migrations/success/contracts/IsLibrary.sol new file mode 100644 index 00000000000..b7ed3c78d8f --- /dev/null +++ b/packages/truffle/test/sources/migrations/success/contracts/IsLibrary.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.4; + +library IsLibrary { + string constant public id = 'IsLibrary'; + event IsLibraryEvent(uint eventID); + + function fireIsLibraryEvent(uint _id) public { + emit IsLibraryEvent(_id); + } +} \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/success/contracts/Loop.sol b/packages/truffle/test/sources/migrations/success/contracts/Loop.sol new file mode 100644 index 00000000000..d995a3c77f8 --- /dev/null +++ b/packages/truffle/test/sources/migrations/success/contracts/Loop.sol @@ -0,0 +1,11 @@ +pragma solidity ^0.4.4; + + +contract Loops { + uint public id; + constructor() public { + for(uint i = 0; i < 10000; i++){ + id = i; + } + } +} \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/success/contracts/Migrations.sol b/packages/truffle/test/sources/migrations/success/contracts/Migrations.sol new file mode 100644 index 00000000000..c4efb65e216 --- /dev/null +++ b/packages/truffle/test/sources/migrations/success/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.4.23; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + constructor() public { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/packages/truffle/test/sources/migrations/success/contracts/UsesExample.sol b/packages/truffle/test/sources/migrations/success/contracts/UsesExample.sol new file mode 100644 index 00000000000..ba502c3d363 --- /dev/null +++ b/packages/truffle/test/sources/migrations/success/contracts/UsesExample.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.4.4; + + +contract UsesExample { + string public id = 'UsesExample'; + address public other; + constructor(address _other) public { + other = _other; + } +} diff --git a/packages/truffle/test/sources/migrations/success/contracts/UsesLibrary.sol b/packages/truffle/test/sources/migrations/success/contracts/UsesLibrary.sol new file mode 100644 index 00000000000..6e7ba6388c6 --- /dev/null +++ b/packages/truffle/test/sources/migrations/success/contracts/UsesLibrary.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.4.4; + +import "./IsLibrary.sol"; + +contract UsesLibrary { + + event UsesLibraryEvent(uint eventID); + + constructor() public {} + + function fireIsLibraryEvent(uint id) public { + IsLibrary.fireIsLibraryEvent(id); + } + + function fireUsesLibraryEvent(uint id) public { + emit UsesLibraryEvent(id); + } +} diff --git a/packages/truffle/test/sources/migrations/success/migrations/1_initial_migration.js b/packages/truffle/test/sources/migrations/success/migrations/1_initial_migration.js new file mode 100644 index 00000000000..4d5f3f9b02e --- /dev/null +++ b/packages/truffle/test/sources/migrations/success/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +var Migrations = artifacts.require("./Migrations.sol"); + +module.exports = function(deployer) { + deployer.deploy(Migrations); +}; diff --git a/packages/truffle/test/sources/migrations/success/migrations/2_migrations_sync.js b/packages/truffle/test/sources/migrations/success/migrations/2_migrations_sync.js new file mode 100644 index 00000000000..bd81e5072a9 --- /dev/null +++ b/packages/truffle/test/sources/migrations/success/migrations/2_migrations_sync.js @@ -0,0 +1,8 @@ +const Example = artifacts.require("Example"); +const UsesExample = artifacts.require("UsesExample"); + +module.exports = function(deployer) { + deployer + .deploy(Example) + .then(() => deployer.deploy(UsesExample, Example.address)); +}; \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/success/migrations/3_migrations_async.js b/packages/truffle/test/sources/migrations/success/migrations/3_migrations_async.js new file mode 100644 index 00000000000..3b8cc4601a0 --- /dev/null +++ b/packages/truffle/test/sources/migrations/success/migrations/3_migrations_async.js @@ -0,0 +1,10 @@ +const IsLibrary = artifacts.require("IsLibrary"); +const UsesExample = artifacts.require("UsesExample"); +const UsesLibrary = artifacts.require("UsesLibrary"); + +module.exports = async function(deployer) { + await deployer.deploy(IsLibrary); + await deployer.link(IsLibrary, UsesLibrary); + await deployer.deploy(UsesExample, IsLibrary.address); + await deployer.deploy(UsesLibrary); +}; diff --git a/packages/truffle/test/sources/migrations/success/truffle.js b/packages/truffle/test/sources/migrations/success/truffle.js new file mode 100644 index 00000000000..445859efa0b --- /dev/null +++ b/packages/truffle/test/sources/migrations/success/truffle.js @@ -0,0 +1,13 @@ +module.exports = { + // See + // to customize your Truffle configuration! + networks: { + development: { + host: "127.0.0.1", + port: 8545, + network_id: '*', + gas: 4700000, + gasPrice: 20000000000, + }, + }, +}; From a409d362511a9cfa404a1fcde3f5c237e935bc76 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 13 Jun 2018 20:37:42 -0700 Subject: [PATCH 15/74] Update yarn.lock --- yarn.lock | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/yarn.lock b/yarn.lock index 808fa1d8c8f..457c76bd463 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3022,6 +3022,14 @@ elliptic@^6.0.0, elliptic@^6.2.3, elliptic@^6.4.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +emittery@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.3.0.tgz#e6dcedabae804b5478c760335ecbbaf159da645c" + +emittery@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.4.0.tgz#b5d194e28f89f494aaad7b308d143e52ac9d0b43" + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" @@ -4120,6 +4128,14 @@ fs-extra@^4.0.1: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@~0.6.1: version "0.6.4" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.6.4.tgz#f46f0c75b7841f8d200b3348cd4d691d5a099d15" From deaebcc638e43238f5c64058ee9fde12f96a7f1e Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sat, 16 Jun 2018 17:23:41 -0700 Subject: [PATCH 16/74] Add dry run report (draft) --- packages/truffle-core/lib/commands/migrate.js | 11 +- packages/truffle-core/lib/environment.js | 2 +- packages/truffle-migrate/index.js | 8 +- packages/truffle-migrate/reporter/reporter.js | 117 +++++++++++++----- .../truffle/test/scenarios/commandrunner.js | 2 +- .../test/scenarios/migrations/dryrun.js | 67 ++++++++++ .../test/scenarios/migrations/migrate.js | 2 +- 7 files changed, 167 insertions(+), 42 deletions(-) create mode 100644 packages/truffle/test/scenarios/migrations/dryrun.js diff --git a/packages/truffle-core/lib/commands/migrate.js b/packages/truffle-core/lib/commands/migrate.js index 3bb2e75c107..0669c0b5e63 100644 --- a/packages/truffle-core/lib/commands/migrate.js +++ b/packages/truffle-core/lib/commands/migrate.js @@ -69,6 +69,7 @@ var command = { } function runMigrations(callback) { + if (options.f) { Migrate.runFrom(options.f, config, done); } else { @@ -93,18 +94,10 @@ var command = { var dryRun = options.dryRun === true; - var networkMessage = "Using network '" + config.network + "'"; - - if (dryRun) { - networkMessage += " (dry run)"; - } - - config.logger.log(networkMessage + "." + OS.EOL); - if (dryRun) { setupDryRunEnvironmentThenRunMigrations(done); } else { - runMigrations(done); + runMigrations(true, done); } }); }); diff --git a/packages/truffle-core/lib/environment.js b/packages/truffle-core/lib/environment.js index fd51fe1cc30..9e58be145d1 100644 --- a/packages/truffle-core/lib/environment.js +++ b/packages/truffle-core/lib/environment.js @@ -88,7 +88,7 @@ var Environment = { var web3 = new Web3(config.provider); - web3.eth.getAccounts.then(accounts => { + web3.eth.getAccounts().then(accounts => { var upstreamNetwork = config.network; var upstreamConfig = config.networks[upstreamNetwork]; diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index e4098eff489..6551b526f07 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -13,14 +13,18 @@ const Require = require("truffle-require"); const ResolverIntercept = require("./resolverintercept"); const Reporter = require("./reporter/reporter"); +const util = require('util'); + class Migration { - constructor(file){ + constructor(file, options){ this.file = path.resolve(file); this.number = parseInt(path.basename(file)); this.emitter = new Emittery(); this.isFirst = false; this.isLast = false; + this.options = options || {}; + } async run(options, callback) { @@ -142,7 +146,7 @@ const Migrate = { let migrations = files .filter(file => isNaN(parseInt(path.basename(file))) == false) .filter(file => path.extname(file).match(options.allowed_extensions) != null) - .map(file => new Migration(file, options.network)); + .map(file => new Migration(file, options)); // Make sure to sort the prefixes as numbers and not strings. migrations = migrations.sort((a, b) => { diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index bc0bf1835f6..717114e8ca8 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -17,6 +17,10 @@ class Reporter { this.finalCostTotal = new web3Utils.BN(0); this.deployments = 0; this.separator = '\n'; + this.summary = []; + this.dryRun = migration.options.dryRun; + this.interactive = migration.options.interactive; + this.currentFileIndex = -1; this.listen(); } @@ -60,6 +64,14 @@ class Reporter { message = this.messages('firstMigrate', data); this.deployer.logger.log(message); } + + this.summary.push({ + file: data.file, + deployments: [], + }); + + this.currentFileIndex++; + message = this.messages('preMigrate', data); this.deployer.logger.log(message); } @@ -70,16 +82,27 @@ class Reporter { } async postMigrate(isLast){ - let message = this.messages('postMigrate'); + let data = {}; + data.cost = this.getTotals().cost; + this.summary[this.currentFileIndex].totalCost = data.cost; + + let message = this.messages('postMigrate', data); this.deployer.logger.log(message); if (isLast){ - message = this.messages('lastMigrate'); + data.totalDeployments = this.getTotals().deployments; + data.finalCost = this.getTotals().finalCost; + + this.summary.totalDeployments = data.totalDeployments; + this.summary.finalCost = data.finalCost; + + message = this.messages('lastMigrate', data); this.deployer.logger.log(message); } } async migrationError(data){ + this.summary[this.currentFileIndex].errored = true; const message = this.messages('migrateErr', data); this.deployer.logger.log(message); } @@ -104,6 +127,7 @@ class Reporter { const gas = new web3Utils.BN(data.receipt.gasUsed); const cost = gasPrice.mul(gas); + data.gasPrice = web3Utils.fromWei(gasPrice, 'gwei'); data.gas = data.receipt.gasUsed; data.from = tx.from; data.cost = web3Utils.fromWei(cost, 'ether'); @@ -114,6 +138,7 @@ class Reporter { this.currentAddress = this.from; this.deployments++; + this.summary[this.currentFileIndex].deployments.push(data); message = this.messages('deployed', data); } else { message = this.messages('notDeployed', data); @@ -174,7 +199,7 @@ class Reporter { underline(msg){ return (typeof msg === 'number') - ? `\n ${'-'.repeat(msg)}` + ? ` ${'-'.repeat(msg)}` : `\n ${msg}\n ${'-'.repeat(msg.length)}`; } @@ -184,7 +209,7 @@ class Reporter { } messages(kind, data){ - //console.log('data --> ' + util.format("%O", data)); + const self = this; const prefix = '\nError:'; const kinds = { @@ -300,44 +325,80 @@ class Reporter { many: () => this.underline(`Deploying Batch`), - linking: () => - this.underline(`Linking`) + - `\n * Contract: ${data.contractName} <--> Library: ${data.libraryName} `+ - `(at address: ${data.libraryAddress})`, + linking: () => { + let output = this.underline(`Linking`) + + `\n * Contract: ${data.contractName} <--> Library: ${data.libraryName} `; + + if(!self.dryRun) + output +=`(at address: ${data.libraryAddress})`; + + return output; + }, preMigrate: () => this.doubleline(`${data.file}`), - saving: () => - `\n * Saving migration`, + saving: () => { + return (!self.dryRun) + ? `\n * Saving migration` + : ''; + }, + + firstMigrate: () => { + let output; + (self.dryRun) + ? output = this.doubleline(`Migrations dry-run (simulation)`) + '\n' + : output = this.doubleline(`Starting migrations...`) + '\n'; - firstMigrate: () => - this.doubleline(`Starting migrations...`) + '\n' + - `> Network name: '${data.network}'\n` + - `> Network id: ${data.networkId}\n`, - postMigrate: () => - ` * Saving artifacts` + - this.underline(18) + '\n' + - ` > ${'Total cost:'.padEnd(15)} ${this.getTotals().cost.padStart(15)} ETH\n`, + output += + `> Network name: '${data.network}'\n` + + `> Network id: ${data.networkId}\n`; + + return output; + }, + + postMigrate: () => { + let output = ''; + + if (!self.dryRun) + output += ` * Saving artifacts\n`; + + output += this.underline(37) + '\n' + + ` > ${'Total cost:'.padEnd(15)} ${data.cost.padStart(15)} ETH\n`; + + return output; + }, lastMigrate: () => this.doubleline('Summary') + '\n' + - `> ${'TotalDeployments:'.padEnd(20)} ${this.getTotals().deployments}\n` + - `> ${'Final cost:'.padEnd(20)} ${this.getTotals().finalCost} ETH\n`, + `> ${'Total deployments:'.padEnd(20)} ${data.totalDeployments}\n` + + `> ${'Final cost:'.padEnd(20)} ${data.finalCost} ETH\n`, + + deployed: () => { + let output = ''; + + if(!self.dryRun) output += + ` > ${'contract address:'.padEnd(20)} ${data.receipt.contractAddress}\n`; + + output += + ` > ${'account:'.padEnd(20)} ${data.from}\n` + + ` > ${'balance:'.padEnd(20)} ${data.balance}\n` + + ` > ${'cost:'.padEnd(20)} ${data.cost} ETH\n` + + ` > ${'gas used:'.padEnd(20)} ${data.gas}\n` + + ` > ${'gas price:'.padEnd(20)} ${data.gasPrice} gwei\n`; - deployed: () => - ` > ${'contract address:'.padEnd(20)} ${data.receipt.contractAddress}\n` + - ` > ${'account:'.padEnd(20)} ${data.from}\n` + - ` > ${'cost:'.padEnd(20)} ${data.cost} ETH\n` + - ` > ${'balance:'.padEnd(20)} ${data.balance}\n` + - ` > ${'gas used:'.padEnd(20)} ${data.gas}\n`, + return output; + }, listMany: () => ` * ${data.contractName}`, - hash: () => - ` > ${'transaction hash:'.padEnd(20)} ` + data.transactionHash, + hash: () => { + return (!self.dryRun) + ? ` > ${'transaction hash:'.padEnd(20)} ` + data.transactionHash + : '' + }, receipt: () => ` > ${'gas usage:'.padEnd(20)} ` + data.gas, diff --git a/packages/truffle/test/scenarios/commandrunner.js b/packages/truffle/test/scenarios/commandrunner.js index 9d8821ad5fd..535e343c79d 100644 --- a/packages/truffle/test/scenarios/commandrunner.js +++ b/packages/truffle/test/scenarios/commandrunner.js @@ -8,7 +8,7 @@ module.exports = { (process.env.NO_BUILD) ? execString = "node " + - path.join(__dirname, "../", "../", "node_modules", + path.join(__dirname, "../", "../", "../", "truffle-core", "cli.js") + " " + command diff --git a/packages/truffle/test/scenarios/migrations/dryrun.js b/packages/truffle/test/scenarios/migrations/dryrun.js new file mode 100644 index 00000000000..3153da91dff --- /dev/null +++ b/packages/truffle/test/scenarios/migrations/dryrun.js @@ -0,0 +1,67 @@ +const MemoryLogger = require("../memorylogger"); +const CommandRunner = require("../commandrunner"); +const fs = require("fs"); +const path = require("path"); +const assert = require("assert"); +const Server = require("../server"); +const Reporter = require("../reporter"); +const sandbox = require("../sandbox"); +const Web3 = require('web3'); + +const log = console.log; + +const util = require('util'); + +function processErr(err, output){ + if (err){ + log(output); + throw new Error(err); + } +} + +describe("migrate (dry-run)", function() { + let config; + let web3; + let networkId; + const project = path.join(__dirname, '../../sources/migrations/success'); + const logger = new MemoryLogger(); + + before(done => Server.start(done)); + after(done => Server.stop(done)); + + before(async function() { + this.timeout(10000); + config = await sandbox.create(project) + config.network = "development"; + config.logger = logger; + config.mocha = { + reporter: new Reporter(logger) + } + + const provider = new Web3.providers.HttpProvider('http://localhost:8545') + web3 = new Web3(provider); + networkId = await web3.eth.net.getId(); + }); + + it('does a dry-run with the dry-run option', function(done){ + this.timeout(20000); + + CommandRunner.run("migrate --dry-run", config, err => { + const output = logger.contents(); + processErr(err, output); + + assert(output.includes('Migrations')); + assert(output.includes('development-fork')) + assert(output.includes('2_migrations_sync.js')); + assert(output.includes("Deploying 'UsesExample'")) + assert(output.includes('3_migrations_async.js')); + assert(output.includes("Replacing 'UsesExample'")) + assert(output.includes('Total deployments')); + + const location = path.join(config.contracts_build_directory, "UsesExample.json"); + + console.log(output) + done(); + }) + }); +}); \ No newline at end of file diff --git a/packages/truffle/test/scenarios/migrations/migrate.js b/packages/truffle/test/scenarios/migrations/migrate.js index 531dc9dacd7..5de7eca9818 100644 --- a/packages/truffle/test/scenarios/migrations/migrate.js +++ b/packages/truffle/test/scenarios/migrations/migrate.js @@ -67,7 +67,7 @@ describe("migrate (sync)", function() { }) }); - it('uses forces a migration -f option', function(done){ + it('forces a migration with the -f option', function(done){ this.timeout(20000); CommandRunner.run("migrate -f 3", config, err => { From 40dbbd3788ab5ccc8a8f2c094985d334619cb3b8 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sat, 16 Jun 2018 17:29:21 -0700 Subject: [PATCH 17/74] Copy gasPrice and gasLimit on fork --- packages/truffle-core/lib/environment.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/truffle-core/lib/environment.js b/packages/truffle-core/lib/environment.js index 9e58be145d1..a1271316d56 100644 --- a/packages/truffle-core/lib/environment.js +++ b/packages/truffle-core/lib/environment.js @@ -100,7 +100,9 @@ var Environment = { fork: config.provider, unlocked_accounts: accounts }), - from: config.from + from: config.from, + gas: upstreamConfig.gas, + gasPrice: upstreamConfig.gasPrice } config.network = forkedNetwork; From fd99765cc0c82c2697198ed0cff27c73abe353ac Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sat, 16 Jun 2018 20:15:03 -0700 Subject: [PATCH 18/74] Move reporter to outer scope --- packages/truffle-migrate/index.js | 11 +++++++--- packages/truffle-migrate/reporter/reporter.js | 21 ++++++++----------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 6551b526f07..96741abc53f 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -17,8 +17,9 @@ const util = require('util'); class Migration { - constructor(file, options){ + constructor(file, reporter, options){ this.file = path.resolve(file); + this.reporter = reporter; this.number = parseInt(path.basename(file)); this.emitter = new Emittery(); this.isFirst = false; @@ -54,7 +55,10 @@ class Migration { basePath: path.dirname(self.file) }); - const reporter = new Reporter(deployer, self); + this.reporter.migration = self; + this.reporter.deployer = deployer; + this.reporter.listen(); + const file = path.relative(options.migrations_directory, self.file) const preMigrationsData = { @@ -136,6 +140,7 @@ class Migration { const Migrate = { Migration: Migration, + reporter: new Reporter(), assemble: function(options, callback) { dir.files(options.migrations_directory, function(err, files) { @@ -146,7 +151,7 @@ const Migrate = { let migrations = files .filter(file => isNaN(parseInt(path.basename(file))) == false) .filter(file => path.extname(file).match(options.allowed_extensions) != null) - .map(file => new Migration(file, options)); + .map(file => new Migration(file, Migrate.reporter, options)); // Make sure to sort the prefixes as numbers and not strings. migrations = migrations.sort((a, b) => { diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index 717114e8ca8..24db1d92d32 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -7,21 +7,18 @@ const web3Utils = require('web3-utils'); const spinner = require('./indentedSpinner'); class Reporter { - constructor(deployer, migration){ + constructor(){ this.deployingMany = false; this.logConfirmations = false; - this.deployer = deployer; - this.migration = migration; + this.deployer = null; + this.migration = null; this.currentGasTotal = 0; this.currentCostTotal = new web3Utils.BN(0); this.finalCostTotal = new web3Utils.BN(0); this.deployments = 0; this.separator = '\n'; this.summary = []; - this.dryRun = migration.options.dryRun; - this.interactive = migration.options.interactive; this.currentFileIndex = -1; - this.listen(); } listen(){ @@ -329,7 +326,7 @@ class Reporter { let output = this.underline(`Linking`) + `\n * Contract: ${data.contractName} <--> Library: ${data.libraryName} `; - if(!self.dryRun) + if(!self.migration.dryRun) output +=`(at address: ${data.libraryAddress})`; return output; @@ -339,14 +336,14 @@ class Reporter { this.doubleline(`${data.file}`), saving: () => { - return (!self.dryRun) + return (!self.migration.dryRun) ? `\n * Saving migration` : ''; }, firstMigrate: () => { let output; - (self.dryRun) + (self.migration.dryRun) ? output = this.doubleline(`Migrations dry-run (simulation)`) + '\n' : output = this.doubleline(`Starting migrations...`) + '\n'; @@ -361,7 +358,7 @@ class Reporter { postMigrate: () => { let output = ''; - if (!self.dryRun) + if (!self.migration.dryRun) output += ` * Saving artifacts\n`; output += this.underline(37) + '\n' + @@ -378,7 +375,7 @@ class Reporter { deployed: () => { let output = ''; - if(!self.dryRun) output += + if(!self.migration.dryRun) output += ` > ${'contract address:'.padEnd(20)} ${data.receipt.contractAddress}\n`; output += @@ -395,7 +392,7 @@ class Reporter { ` * ${data.contractName}`, hash: () => { - return (!self.dryRun) + return (!self.migration.dryRun) ? ` > ${'transaction hash:'.padEnd(20)} ` + data.transactionHash : '' }, From 6125d4a72909ca6e69374a78054799675b627951 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sat, 16 Jun 2018 21:09:20 -0700 Subject: [PATCH 19/74] Upgrade ganche-core dev dep to 2.1.2 --- packages/truffle-contract/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/truffle-contract/package.json b/packages/truffle-contract/package.json index 247673b76d6..8045ca9a3e4 100644 --- a/packages/truffle-contract/package.json +++ b/packages/truffle-contract/package.json @@ -27,6 +27,7 @@ "homepage": "https://github.com/trufflesuite/truffle-contract#readme", "dependencies": { "ethjs-abi": "0.1.8", + "ganache-core": "2.1.2", "truffle-blockchain-utils": "^0.0.5", "truffle-contract-schema": "^2.0.1", "truffle-error": "^0.0.3", From 1513236b8287267eae5f174163fb4b68b642e720 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sun, 17 Jun 2018 17:47:35 -0700 Subject: [PATCH 20/74] Add reason string handling (truffle-contract) --- packages/truffle-contract/lib/execute.js | 81 ++++++++++++++----- packages/truffle-contract/lib/handlers.js | 1 + packages/truffle-contract/package.json | 2 +- packages/truffle-contract/test/deploy.js | 26 ++++++ packages/truffle-contract/test/errors.js | 39 ++++++++- packages/truffle-contract/test/methods.js | 14 ++++ .../truffle-contract/test/sources/Example.sol | 5 ++ 7 files changed, 147 insertions(+), 21 deletions(-) diff --git a/packages/truffle-contract/lib/execute.js b/packages/truffle-contract/lib/execute.js index 7644195b045..9fbc4286026 100644 --- a/packages/truffle-contract/lib/execute.js +++ b/packages/truffle-contract/lib/execute.js @@ -1,4 +1,6 @@ var Web3PromiEvent = require('web3-core-promievent'); +var abi = require('ethereumjs-abi'); + var EventEmitter = require('events'); var utils = require("./utils"); var StatusError = require("./statuserror"); @@ -20,23 +22,30 @@ var execute = { var web3 = this.web3; return new Promise(function(accept, reject){ - // Always prefer specified gas - this includes gas set by class_defaults - if (params.gas) return accept(params.gas); - if (!constructor.autoGas) return accept(); - web3.eth .estimateGas(params) .then(gas => { + + // Always prefer specified gas - this includes gas set by class_defaults + if (params.gas) return accept({gas: params.gas, error: null}); + if (!constructor.autoGas) return accept({gas: null, error: null}); + var bestEstimate = Math.floor(constructor.gasMultiplier * gas); // Don't go over blockLimit (bestEstimate >= blockLimit) - ? accept(blockLimit - 1) - : accept(bestEstimate); - - // We need to let txs that revert through. - // Often that's exactly what you are testing. - }).catch(err => accept()); + ? accept({gas: blockLimit - 1, error: null}) + : accept({gas: bestEstimate, error: null}); + + // If there's reason string in the revert and the client is ganache + // we can extract it here - this is a `.call` + }).catch(err => { + err.reason = execute.extractReason(err, web3); + + (params.gas) + ? accept({gas: params.gas, error: err}) + : accept({gas: null, error: err}) + }); }) }, @@ -65,6 +74,22 @@ var execute = { return utils.is_object(arg) && !utils.is_big_number(arg); }, + /** + * Processes .call/.estimateGas errors and extracts a reason string if + * + * @param {[type]} err [description] + * @return {[type]} [description] + */ + extractReason(err, web3){ + if (err && err.results){ + const hash = Object.keys(err.results)[0]; + + if (err.results[hash].return.includes('0x08c379a0')){ + return web3.eth.abi.decodeParameter('string', err.results[hash].return.slice(10)) + } + } + }, + /** * Parses function arguments to discover if the terminal argument specifies the `defaultBlock` * to execute a call at. @@ -131,6 +156,7 @@ var execute = { var args = Array.prototype.slice.call(arguments); var params = utils.getTxParams.call(constructor, args); var promiEvent = new Web3PromiEvent(); + var reason; var context = { contract: constructor, // Can't name this field `constructor` or `_constructor` @@ -145,10 +171,21 @@ var execute = { execute .getGasEstimate .call(constructor, params, network.blockLimit) - .then(gas => { - params.gas = gas + .then(result => { + (result.error) + ? context.reason = result.error.reason + : context.reason = null; + + params.gas = result.gas || undefined; deferred = web3.eth.sendTransaction(params); - deferred.catch(override.start.bind(constructor, context)); + + // vmErrorsOnResponse path. Client emulator will + // reject via the receipt handler + deferred.catch(err => { + err.reason = result.error.reason; + override.start.call(constructor, context, err) + }); + handlers.setup(deferred, context); }) .catch(promiEvent.reject) @@ -173,6 +210,7 @@ var execute = { var web3 = constructor.web3; var params = utils.getTxParams.call(constructor, args); var deferred; + var reason; var options = { data: constructor.binary, @@ -185,14 +223,18 @@ var execute = { execute .getGasEstimate .call(constructor, params, blockLimit) - .then(gas => { - params.gas = gas; + .then(result => { + + if (result.error) reason = result.error.reason; + + params.gas = result.gas || undefined; deferred = web3.eth.sendTransaction(params); handlers.setup(deferred, context); deferred.then(receipt => { if (parseInt(receipt.status) == 0){ var error = new StatusError(params, context.transactionHash, receipt); + error.reason = reason; return context.promiEvent.reject(error) } @@ -201,9 +243,12 @@ var execute = { context.promiEvent.resolve(new constructor(web3Instance)); - // Manage web3's 50 blocks' timeout error. - // Web3's own subscriptions go dead here. - }).catch(override.start.bind(constructor, context)) + // Manage web3's 50 blocks' timeout error. Web3's own subscriptions go dead here. + // Also propagate any reason strings captured during estimate gas. + }).catch(err => { + err.reason = reason; + override.start.call(constructor, context, err) + }) }).catch(context.promiEvent.reject); }, diff --git a/packages/truffle-contract/lib/handlers.js b/packages/truffle-contract/lib/handlers.js index c56106b419b..db33b1ef304 100644 --- a/packages/truffle-contract/lib/handlers.js +++ b/packages/truffle-contract/lib/handlers.js @@ -104,6 +104,7 @@ var handlers = { // .method(): resolve/reject receipt in handler if (parseInt(receipt.status) == 0 && !context.onlyEmitReceipt){ var error = new StatusError(context.params, receipt.transactionHash, receipt); + error.reason = context.reason; return context.promiEvent.reject(error) } diff --git a/packages/truffle-contract/package.json b/packages/truffle-contract/package.json index 8045ca9a3e4..ef02fecb998 100644 --- a/packages/truffle-contract/package.json +++ b/packages/truffle-contract/package.json @@ -45,7 +45,7 @@ "browserify": "^14.0.0", "chai": "4.1.2", "debug": "^3.1.0", - "ganache-core": "2.1.0", + "ganache-core": "2.1.2", "lodash": "4.17.10", "mocha": "5.2.0", "require-nocache": "^1.0.0", diff --git a/packages/truffle-contract/test/deploy.js b/packages/truffle-contract/test/deploy.js index fd592fdd907..a4f082c4652 100644 --- a/packages/truffle-contract/test/deploy.js +++ b/packages/truffle-contract/test/deploy.js @@ -121,6 +121,19 @@ describe("Deployments", function() { } }); + it("Handles absence of reason string gracefully", async function(){ + try { + await Example.new(2001) // 2001 fails a require gate + assert.fail() + } catch(e){ + const errorCorrect = e.message.includes('exceeds gas limit') || + e.message.includes('intrinsic gas too low'); + + assert(errorCorrect, 'Expected gas limit error'); + assert(e.receipt === undefined, 'Expected no receipt') + } + }); + // NB: constructor (?) message is unhelpful: // "Error: Invalid number of parameters for "undefined". Got 2 expected 1!"" it("should reject with web3 validation errors (constructor params)", async function(){ @@ -133,6 +146,19 @@ describe("Deployments", function() { }); }); + describe(".new(): revert with reasonstring (ganache only)", function(){ + it("should reject with reason string on revert", async function(){ + try { + await Example.new(2001); + assert.fail(); + } catch(error) { + assert(error.message.includes('exceeds gas limit')); + assert(error.receipt === undefined, 'Expected no receipt') + assert(error.reason === 'reasonstring'); + } + }); + }) + describe('pre-flight gas estimation', function(){ it('should automatically fund a deployment [ @geth ]', async function(){ diff --git a/packages/truffle-contract/test/errors.js b/packages/truffle-contract/test/errors.js index 022fed82474..e8ca25d240b 100644 --- a/packages/truffle-contract/test/errors.js +++ b/packages/truffle-contract/test/errors.js @@ -46,6 +46,17 @@ describe("Client appends errors (vmErrorsOnRPCResponse)", function() { await Example.new(13) // 13 fails a constructor require gate assert.fail() } catch(e){ + assert(!e.reason, 'Error should not include reason string'); + assert(e.message.includes('exceeds gas limit'), 'Error should be gas limit err'); + } + }); + + it("should error w/reason string if constructor reverts", async function(){ + try { + await Example.new(2001) // 2001 fails a constructor require gate w/ a reason + assert.fail() + } catch(e){ + assert(e.reason === 'reasonstring', 'Error should include reason string'); assert(e.message.includes('exceeds gas limit'), 'Error should be gas limit err'); } }); @@ -112,17 +123,41 @@ describe("Client appends errors (vmErrorsOnRPCResponse)", function() { await example.triggerRequireError(); assert.fail(); } catch(e){ - assert(e.message.includes('revert')); + assert(!e.reason, 'Should not include reasonstring'); + assert(e.message.includes('revert'), 'Should include revert message'); + }; + }); + + it("errors with reason string on revert", async function(){ + const example = await Example.new(1) + try { + await example.triggerRequireWithReasonError(); + assert.fail(); + } catch(e){ + assert(e.reason === 'reasonstring', 'Should include reasonstring'); + assert(e.message.includes('revert'), 'Should include revert message'); }; }); + it("errors with reason string on revert (gas specified)", async function(){ + const example = await Example.new(1) + try { + await example.triggerRequireWithReasonError({gas: 200000}); + assert.fail(); + } catch(e){ + assert(e.reason === 'reasonstring', 'Should include reason string'); + assert(e.message.includes('revert'), 'Should include revert'); + } + }); + it("errors with invalid opcode when gas specified", async function(){ const example = await Example.new(1) try { await example.triggerAssertError({gas: 200000}); assert.fail(); } catch(e){ - assert(e.message.includes('invalid opcode')); + assert(!e.reason, 'Should not include reason string'); + assert(e.message.includes('invalid opcode'), 'Should include invalid opcode'); } }); diff --git a/packages/truffle-contract/test/methods.js b/packages/truffle-contract/test/methods.js index 4f436d8e083..2c66f8c9eb8 100644 --- a/packages/truffle-contract/test/methods.js +++ b/packages/truffle-contract/test/methods.js @@ -310,6 +310,20 @@ describe("Methods", function() { }); }); + describe('revert with reason (ganache only)', function(){ + it("errors with receipt and revert message", async function(){ + const example = await Example.new(1) + try { + await example.triggerRequireWithReasonError(); + assert.fail(); + } catch(e){ + assert(e.reason === 'reasonstring'); + assert(e.message.includes('revert')); + assert(parseInt(e.receipt.status, 16) == 0) + }; + }); + }) + // This doesn't work on geth --dev because chainId is too high: 1337? Apparently // not configurable. Might work on a sub 100 id. describe.skip('web3 wallet', function(){ diff --git a/packages/truffle-contract/test/sources/Example.sol b/packages/truffle-contract/test/sources/Example.sol index 288aede6f92..20dab13af5f 100644 --- a/packages/truffle-contract/test/sources/Example.sol +++ b/packages/truffle-contract/test/sources/Example.sol @@ -12,6 +12,7 @@ contract Example { constructor(uint val) { // Constructor revert require(val != 13); + require(val != 2001, 'reasonstring'); // Expensive deployment if(val >= 50){ @@ -88,6 +89,10 @@ contract Example { assert(false); } + function triggerRequireWithReasonError(){ + require(false, 'reasonstring'); + } + function runsOutOfGas() { consumesGas(); } From 63ae9eb563ae227f386c5717bcb559ae442b6def Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sun, 17 Jun 2018 18:38:03 -0700 Subject: [PATCH 21/74] Fix extra require/yarn.lock --- packages/truffle-contract/lib/execute.js | 2 -- packages/truffle-contract/package.json | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/truffle-contract/lib/execute.js b/packages/truffle-contract/lib/execute.js index 9fbc4286026..d4dfb7fe48e 100644 --- a/packages/truffle-contract/lib/execute.js +++ b/packages/truffle-contract/lib/execute.js @@ -1,6 +1,4 @@ var Web3PromiEvent = require('web3-core-promievent'); -var abi = require('ethereumjs-abi'); - var EventEmitter = require('events'); var utils = require("./utils"); var StatusError = require("./statuserror"); diff --git a/packages/truffle-contract/package.json b/packages/truffle-contract/package.json index ef02fecb998..1c99df1c130 100644 --- a/packages/truffle-contract/package.json +++ b/packages/truffle-contract/package.json @@ -26,6 +26,7 @@ }, "homepage": "https://github.com/trufflesuite/truffle-contract#readme", "dependencies": { + "ethereumjs-util": "^5.2.0", "ethjs-abi": "0.1.8", "ganache-core": "2.1.2", "truffle-blockchain-utils": "^0.0.5", From ca16e6f7bfbc870afb8f9c51da6ddcd66097761e Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sun, 17 Jun 2018 19:02:41 -0700 Subject: [PATCH 22/74] Fix err.return check --- packages/truffle-contract/lib/execute.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/truffle-contract/lib/execute.js b/packages/truffle-contract/lib/execute.js index d4dfb7fe48e..4d0b36faccb 100644 --- a/packages/truffle-contract/lib/execute.js +++ b/packages/truffle-contract/lib/execute.js @@ -23,7 +23,6 @@ var execute = { web3.eth .estimateGas(params) .then(gas => { - // Always prefer specified gas - this includes gas set by class_defaults if (params.gas) return accept({gas: params.gas, error: null}); if (!constructor.autoGas) return accept({gas: null, error: null}); @@ -82,7 +81,7 @@ var execute = { if (err && err.results){ const hash = Object.keys(err.results)[0]; - if (err.results[hash].return.includes('0x08c379a0')){ + if (err.results[hash].return && err.results[hash].return.includes('0x08c379a0')){ return web3.eth.abi.decodeParameter('string', err.results[hash].return.slice(10)) } } From de626c8a292c64fee7c568711d8dbf30bfdda969 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Mon, 18 Jun 2018 16:01:52 -0700 Subject: [PATCH 23/74] Add geth specific error handling --- packages/truffle-contract/lib/execute.js | 2 +- packages/truffle-migrate/reporter/reporter.js | 14 +++++++++++++- .../truffle/test/scenarios/migrations/dryrun.js | 2 +- .../truffle/test/scenarios/migrations/errors.js | 9 +++++---- .../truffle/test/scenarios/migrations/migrate.js | 4 ++-- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/truffle-contract/lib/execute.js b/packages/truffle-contract/lib/execute.js index 4d0b36faccb..5e12a10c504 100644 --- a/packages/truffle-contract/lib/execute.js +++ b/packages/truffle-contract/lib/execute.js @@ -179,7 +179,7 @@ var execute = { // vmErrorsOnResponse path. Client emulator will // reject via the receipt handler deferred.catch(err => { - err.reason = result.error.reason; + err.reason = (result.error) ? result.error.reason : null; override.start.call(constructor, context, err) }); diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index 24db1d92d32..a3813f6ce4a 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -305,6 +305,13 @@ class Reporter { ` * Try: setting the 'confirmations' key in your network config\n` + ` to wait for several block confirmations between each deployment.\n`, + geth: () => + `${prefix} "${data.contract.contractName}" received a generic error from Geth that\n` + + `can be caused by hitting revert in a contract constructor or running out of gas.\n` + + ` * ${data.estimateError.message}.\n` + + ` * Try: + using the '--dry-run' option to reproduce this failure with clearer errors.\n` + + ` + verifying that your gas is adequate for this deployment.\n`, + default: () => `${prefix} "${data.contract.contractName}" -- ${data.error.message}.\n`, @@ -409,7 +416,6 @@ class Reporter { async processDeploymentError(data){ let message; - const error = data.estimateError || data.error; const errors = { @@ -420,6 +426,7 @@ class Reporter { BLK: error.message.includes('block gas limit'), NCE: error.message.includes('nonce'), INV: error.message.includes('invalid opcode'), + GTH: error.message.includes('always failing transaction') } let type = Object.keys(errors).find(key => errors[key]); @@ -477,6 +484,11 @@ class Reporter { this.deployer.logger.error(message); break; + case 'GTH': + message = this.messages('geth', data); + this.deployer.logger.error(message); + break; + default: message = this.messages('default', data); this.deployer.logger.error(message); diff --git a/packages/truffle/test/scenarios/migrations/dryrun.js b/packages/truffle/test/scenarios/migrations/dryrun.js index 3153da91dff..dc8cfd1a89f 100644 --- a/packages/truffle/test/scenarios/migrations/dryrun.js +++ b/packages/truffle/test/scenarios/migrations/dryrun.js @@ -44,7 +44,7 @@ describe("migrate (dry-run)", function() { }); it('does a dry-run with the dry-run option', function(done){ - this.timeout(20000); + this.timeout(70000); CommandRunner.run("migrate --dry-run", config, err => { const output = logger.contents(); diff --git a/packages/truffle/test/scenarios/migrations/errors.js b/packages/truffle/test/scenarios/migrations/errors.js index 9bf332f1563..bec98d0091d 100644 --- a/packages/truffle/test/scenarios/migrations/errors.js +++ b/packages/truffle/test/scenarios/migrations/errors.js @@ -44,7 +44,7 @@ describe("migration errors", function() { }); it("should error and stop", function(done) { - this.timeout(20000); + this.timeout(70000); CommandRunner.run("migrate", config, err => { const output = logger.contents(); @@ -55,6 +55,7 @@ describe("migration errors", function() { assert(output.includes("Deploying 'Example'")) assert(output.includes("Deploying 'ExampleRevert'")); assert(output.includes("Error")); + assert(output.includes('out of gas') || output.includes('gas required exceeds')); assert(!output.includes("Deploying 'UsesExample'")) assert(!output.includes('3_migrations_ok.js')); @@ -67,7 +68,7 @@ describe("migration errors", function() { }); it("should run from the last successfully completely migration", function(done) { - this.timeout(20000); + this.timeout(70000); CommandRunner.run("migrate", config, err => { const output = logger.contents(); @@ -81,7 +82,7 @@ describe("migration errors", function() { }); it("should run out of gas correctly", function(done){ - this.timeout(20000); + this.timeout(70000); CommandRunner.run("migrate -f 4", config, err => { const output = logger.contents(); @@ -90,7 +91,7 @@ describe("migration errors", function() { assert(output.includes('4_migrations_oog.js')); assert(output.includes("Deploying 'Loops'")); assert(output.includes('Error')); - assert(output.includes('out of gas')); + assert(output.includes('out of gas') || output.includes('gas required exceeds')); done(); }); }) diff --git a/packages/truffle/test/scenarios/migrations/migrate.js b/packages/truffle/test/scenarios/migrations/migrate.js index 5de7eca9818..7765a2715bc 100644 --- a/packages/truffle/test/scenarios/migrations/migrate.js +++ b/packages/truffle/test/scenarios/migrations/migrate.js @@ -44,7 +44,7 @@ describe("migrate (sync)", function() { }); it("runs migrations (sync & async/await)", function(done) { - this.timeout(20000); + this.timeout(70000); CommandRunner.run("migrate", config, err => { const output = logger.contents(); @@ -68,7 +68,7 @@ describe("migrate (sync)", function() { }); it('forces a migration with the -f option', function(done){ - this.timeout(20000); + this.timeout(70000); CommandRunner.run("migrate -f 3", config, err => { const output = logger.contents(); From 76ca3aefb2ba93200ceba4fa278e70c9507cbf17 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Mon, 18 Jun 2018 18:04:01 -0700 Subject: [PATCH 24/74] Add revert with reason test --- packages/truffle-contract/lib/execute.js | 1 - packages/truffle-migrate/reporter/reporter.js | 7 +++---- .../truffle/test/scenarios/migrations/errors.js | 16 +++++++++++++++- .../error/contracts/RevertWithReason.sol | 9 +++++++++ .../error/migrations/5_migrations_reason.js | 5 +++++ 5 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 packages/truffle/test/sources/migrations/error/contracts/RevertWithReason.sol create mode 100644 packages/truffle/test/sources/migrations/error/migrations/5_migrations_reason.js diff --git a/packages/truffle-contract/lib/execute.js b/packages/truffle-contract/lib/execute.js index 5e12a10c504..e14c4802c3a 100644 --- a/packages/truffle-contract/lib/execute.js +++ b/packages/truffle-contract/lib/execute.js @@ -221,7 +221,6 @@ var execute = { .getGasEstimate .call(constructor, params, blockLimit) .then(result => { - if (result.error) reason = result.error.reason; params.gas = result.gas || undefined; diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index a3813f6ce4a..7e479ff8ca9 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -256,10 +256,9 @@ class Reporter { ` private network or test client (like ganache).\n`, rvtReason: () => - `Revert with string error not implemented yet.`, - - asrtReason: () => - `Assert with string error not implemented yet.`, + `${prefix} "${data.contract.contractName}" hit a require or revert statement ` + + `with the following reason given:\n` + + ` * ${data.reason}\n`, rvtNoReason: () => `${prefix} "${data.contract.contractName}" hit a require or revert statement ` + diff --git a/packages/truffle/test/scenarios/migrations/errors.js b/packages/truffle/test/scenarios/migrations/errors.js index bec98d0091d..caafcc7d142 100644 --- a/packages/truffle/test/scenarios/migrations/errors.js +++ b/packages/truffle/test/scenarios/migrations/errors.js @@ -55,7 +55,7 @@ describe("migration errors", function() { assert(output.includes("Deploying 'Example'")) assert(output.includes("Deploying 'ExampleRevert'")); assert(output.includes("Error")); - assert(output.includes('out of gas') || output.includes('gas required exceeds')); + assert(output.includes('require or revert') || output.includes('gas required exceeds')); assert(!output.includes("Deploying 'UsesExample'")) assert(!output.includes('3_migrations_ok.js')); @@ -95,4 +95,18 @@ describe("migration errors", function() { done(); }); }) + + it("should expose the reason string if available [ @ganache ]", function(done){ + this.timeout(70000); + + CommandRunner.run("migrate -f 5", config, err => { + const output = logger.contents(); + assert(err); + console.log(output); + assert(output.includes('5_migrations_revert.js')); + assert(output.includes("Deploying 'RevertWithReason'")); + assert(output.includes('reasonstring')); + done(); + }); + }); }); \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/error/contracts/RevertWithReason.sol b/packages/truffle/test/sources/migrations/error/contracts/RevertWithReason.sol new file mode 100644 index 00000000000..016482ad8cc --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/contracts/RevertWithReason.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.4.4; + + +contract RevertWithReason { + string public id = 'RevertWithReason'; + constructor() public { + require(false, 'reasonstring'); + } +} \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/error/migrations/5_migrations_reason.js b/packages/truffle/test/sources/migrations/error/migrations/5_migrations_reason.js new file mode 100644 index 00000000000..03ef19204d6 --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/migrations/5_migrations_reason.js @@ -0,0 +1,5 @@ +const RevertWithReason = artifacts.require("RevertWithReason"); + +module.exports = async function(deployer, network, accounts) { + await deployer.deploy(RevertWithReason); +}; \ No newline at end of file From baad4faf7e2cd91f425c9246f8a3bee4498ecd9f Mon Sep 17 00:00:00 2001 From: cgewecke Date: Mon, 18 Jun 2018 20:07:27 -0700 Subject: [PATCH 25/74] Upgrade ganache-cli to 6.1.3 (everywhere) --- package.json | 3 ++- packages/truffle-artifactor/package.json | 2 +- packages/truffle-contract/lib/execute.js | 1 + packages/truffle-core/package.json | 2 +- packages/truffle-debugger/package.json | 2 +- packages/truffle-deployer/package.json | 2 +- packages/truffle-deployer/src/deployment.js | 3 +++ packages/truffle-migrate/reporter/reporter.js | 1 + packages/truffle-provider/package.json | 2 +- packages/truffle/package.json | 2 +- packages/truffle/test/scenarios/migrations/errors.js | 2 +- .../sources/migrations/error/contracts/RevertWithReason.sol | 2 +- 12 files changed, 15 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 072c8659c69..a144900abd6 100644 --- a/package.json +++ b/package.json @@ -14,5 +14,6 @@ "packages": [ "packages/*" ] - } + }, + "dependencies": {} } diff --git a/packages/truffle-artifactor/package.json b/packages/truffle-artifactor/package.json index c90c1954bca..a1a95b68147 100644 --- a/packages/truffle-artifactor/package.json +++ b/packages/truffle-artifactor/package.json @@ -15,7 +15,7 @@ "license": "MIT", "devDependencies": { "chai": "4.1.2", - "ganache-cli": "6.1.0-beta.4", + "ganache-cli": "6.1.3", "mocha": "5.2.0", "require-nocache": "^1.0.0", "solc": "0.4.24", diff --git a/packages/truffle-contract/lib/execute.js b/packages/truffle-contract/lib/execute.js index e14c4802c3a..fb48e2d98a2 100644 --- a/packages/truffle-contract/lib/execute.js +++ b/packages/truffle-contract/lib/execute.js @@ -5,6 +5,7 @@ var StatusError = require("./statuserror"); var handlers = require("./handlers"); var override = require("./override"); +var util = require('util'); var execute = { // ----------------------------------- Helpers -------------------------------------------------- diff --git a/packages/truffle-core/package.json b/packages/truffle-core/package.json index efa743520be..9e99f4ea823 100644 --- a/packages/truffle-core/package.json +++ b/packages/truffle-core/package.json @@ -16,7 +16,7 @@ "ethpm-registry": "0.0.10", "finalhandler": "^0.4.0", "fs-extra": "6.0.1", - "ganache-cli": "6.1.0-beta.4", + "ganache-cli": "6.1.3", "lodash": "4.17.10", "mkdirp": "^0.5.1", "mocha": "5.2.0", diff --git a/packages/truffle-debugger/package.json b/packages/truffle-debugger/package.json index 7b0cd05a44c..1aed111db9d 100644 --- a/packages/truffle-debugger/package.json +++ b/packages/truffle-debugger/package.json @@ -48,7 +48,7 @@ "esdoc-standard-plugin": "^1.0.0", "express": "^4.16.2", "fs-extra": "6.0.1", - "ganache-cli": "6.1.0-beta.4", + "ganache-cli": "6.1.3", "mocha": "5.2.0", "mocha-webpack": "^1.1.0", "node-interval-tree": "^1.3.3", diff --git a/packages/truffle-deployer/package.json b/packages/truffle-deployer/package.json index 9d78707c21a..efa88d73498 100644 --- a/packages/truffle-deployer/package.json +++ b/packages/truffle-deployer/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "fs-extra": "^6.0.1", - "ganache-cli": "6.1.0", + "ganache-cli": "6.1.3", "memorystream": "^0.3.1", "mocha": "5.2.0", "truffle-workflow-compile": "^1.0.3", diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 8eb3a34c312..2db9da77240 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -1,3 +1,5 @@ +const util = require('util') + /** * @class Deployment */ @@ -224,6 +226,7 @@ class Deployment { try { instance = await promiEvent; } catch(err){ + console.log('err @ deploy --> ' + util.format("%O", err)) eventArgs.error = err.error || err; await self.emitter.emit('deployFailed', eventArgs); throw new Error(self._errorCodes('deployFailed')); diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index 7e479ff8ca9..ae2f5d32015 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -415,6 +415,7 @@ class Reporter { async processDeploymentError(data){ let message; + console.log(util.format('%O', data)) const error = data.estimateError || data.error; const errors = { diff --git a/packages/truffle-provider/package.json b/packages/truffle-provider/package.json index 773f66d855c..fa0b2433fb0 100644 --- a/packages/truffle-provider/package.json +++ b/packages/truffle-provider/package.json @@ -27,7 +27,7 @@ "web3": "1.0.0-beta.33" }, "devDependencies": { - "ganache-cli": "6.1.0-beta.4", + "ganache-cli": "6.1.3", "mocha": "5.2.0" }, "publishConfig": { diff --git a/packages/truffle/package.json b/packages/truffle/package.json index a5c6bdefc4c..4796054273c 100644 --- a/packages/truffle/package.json +++ b/packages/truffle/package.json @@ -12,7 +12,7 @@ "clean-webpack-plugin": "^0.1.16", "copy-webpack-plugin": "^4.0.1", "fs-extra": "6.0.1", - "ganache-cli": "^6.1.0", + "ganache-cli": "6.1.3", "glob": "^7.1.2", "js-scrypt": "^0.2.0", "meta-npm": "^0.0.22", diff --git a/packages/truffle/test/scenarios/migrations/errors.js b/packages/truffle/test/scenarios/migrations/errors.js index caafcc7d142..186e78448aa 100644 --- a/packages/truffle/test/scenarios/migrations/errors.js +++ b/packages/truffle/test/scenarios/migrations/errors.js @@ -96,7 +96,7 @@ describe("migration errors", function() { }); }) - it("should expose the reason string if available [ @ganache ]", function(done){ + it.skip("should expose the reason string if available [ @ganache ]", function(done){ this.timeout(70000); CommandRunner.run("migrate -f 5", config, err => { diff --git a/packages/truffle/test/sources/migrations/error/contracts/RevertWithReason.sol b/packages/truffle/test/sources/migrations/error/contracts/RevertWithReason.sol index 016482ad8cc..afae938e0a7 100644 --- a/packages/truffle/test/sources/migrations/error/contracts/RevertWithReason.sol +++ b/packages/truffle/test/sources/migrations/error/contracts/RevertWithReason.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity ^0.4.23; contract RevertWithReason { From e6e0ea42f4228cc7266695ca719a96abed3ce83a Mon Sep 17 00:00:00 2001 From: cgewecke Date: Mon, 18 Jun 2018 20:51:35 -0700 Subject: [PATCH 26/74] Add low balance test --- packages/truffle-deployer/src/deployment.js | 1 - packages/truffle-migrate/reporter/reporter.js | 1 - .../test/scenarios/migrations/errors.js | 20 ++++++++++++++++++- .../error/migrations/6_migrations_funds.js | 15 ++++++++++++++ scripts/ci.sh | 4 ++++ 5 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 packages/truffle/test/sources/migrations/error/migrations/6_migrations_funds.js diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 2db9da77240..799e636b1ac 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -226,7 +226,6 @@ class Deployment { try { instance = await promiEvent; } catch(err){ - console.log('err @ deploy --> ' + util.format("%O", err)) eventArgs.error = err.error || err; await self.emitter.emit('deployFailed', eventArgs); throw new Error(self._errorCodes('deployFailed')); diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index ae2f5d32015..7e479ff8ca9 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -415,7 +415,6 @@ class Reporter { async processDeploymentError(data){ let message; - console.log(util.format('%O', data)) const error = data.estimateError || data.error; const errors = { diff --git a/packages/truffle/test/scenarios/migrations/errors.js b/packages/truffle/test/scenarios/migrations/errors.js index 186e78448aa..f338be7b6c9 100644 --- a/packages/truffle/test/scenarios/migrations/errors.js +++ b/packages/truffle/test/scenarios/migrations/errors.js @@ -109,4 +109,22 @@ describe("migration errors", function() { done(); }); }); -}); \ No newline at end of file + + it("should error on insufficient funds correctly", function(done){ + this.timeout(70000); + + CommandRunner.run("migrate -f 6", config, err => { + const output = logger.contents(); + assert(err); + console.log(output); + assert(output.includes('6_migrations_funds.js')); + assert(output.includes("Deploying 'Example'")); + assert(output.includes('insufficient funds')); + assert(output.includes('Account')); + assert(output.includes('Balance')); + done(); + }); + + }); +}); + diff --git a/packages/truffle/test/sources/migrations/error/migrations/6_migrations_funds.js b/packages/truffle/test/sources/migrations/error/migrations/6_migrations_funds.js new file mode 100644 index 00000000000..d2f4e31c1f9 --- /dev/null +++ b/packages/truffle/test/sources/migrations/error/migrations/6_migrations_funds.js @@ -0,0 +1,15 @@ +const Example = artifacts.require("Example"); + +module.exports = async function(deployer, network, accounts) { + const emptyAccount = accounts[7] + let balance = await web3.eth.getBalance(emptyAccount); + + await web3.eth.sendTransaction({ + to: accounts[0], + from: emptyAccount, + value: balance, + gasPrice: 0 + }); + + await deployer.deploy(Example, {from: emptyAccount}); +}; \ No newline at end of file diff --git a/scripts/ci.sh b/scripts/ci.sh index 61d05926bf0..36d78989f9b 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -1,4 +1,8 @@ #!/usr/bin/env bash + +# Exit script as soon as a command fails. +set -o errexit + run_geth() { docker run \ -v /$PWD/scripts:/scripts \ From ee056ccd44bb71c8307df9f36e4333ed07ed1c93 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Tue, 19 Jun 2018 17:56:18 -0700 Subject: [PATCH 27/74] Make reason string work --- packages/truffle-contract/lib/execute.js | 79 ++++++++++++------- packages/truffle-migrate/reporter/reporter.js | 4 +- .../test/scenarios/migrations/errors.js | 6 +- 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/packages/truffle-contract/lib/execute.js b/packages/truffle-contract/lib/execute.js index fb48e2d98a2..69c87f3d7f6 100644 --- a/packages/truffle-contract/lib/execute.js +++ b/packages/truffle-contract/lib/execute.js @@ -9,9 +9,9 @@ var util = require('util'); var execute = { // ----------------------------------- Helpers -------------------------------------------------- - /** * Retrieves gas estimate multiplied by the set gas multiplier for a `sendTransaction` call. + * We're using low level rpc calls here * @param {Object} params `sendTransaction` parameters * @param {Number} blockLimit most recent network block.blockLimit * @return {Number} gas estimate @@ -21,29 +21,44 @@ var execute = { var web3 = this.web3; return new Promise(function(accept, reject){ - web3.eth - .estimateGas(params) - .then(gas => { - // Always prefer specified gas - this includes gas set by class_defaults - if (params.gas) return accept({gas: params.gas, error: null}); - if (!constructor.autoGas) return accept({gas: null, error: null}); - - var bestEstimate = Math.floor(constructor.gasMultiplier * gas); - - // Don't go over blockLimit - (bestEstimate >= blockLimit) - ? accept({gas: blockLimit - 1, error: null}) - : accept({gas: bestEstimate, error: null}); - - // If there's reason string in the revert and the client is ganache - // we can extract it here - this is a `.call` - }).catch(err => { - err.reason = execute.extractReason(err, web3); + let reason; + + const packet = { + jsonrpc: "2.0", + method: "eth_call", + params: [params], + id: new Date().getTime(), + } + + // This rpc call extracts the reason string + web3.currentProvider.send(packet, (err, response) => { + if (response.error || response.result ) { + reason = execute.extractReason(response, web3) + } + + web3 + .eth + .estimateGas(params) + .then(gas => { + // Always prefer specified gas - this includes gas set by class_defaults + if (params.gas) return accept({gas: params.gas, error: null}); + if (!constructor.autoGas) return accept({gas: null, error: null}); + + var bestEstimate = Math.floor(constructor.gasMultiplier * gas); + + // Don't go over blockLimit + (bestEstimate >= blockLimit) + ? accept({gas: blockLimit - 1, error: null}) + : accept({gas: bestEstimate, error: null}); + }) + .catch(err => { + err.reason = reason; - (params.gas) - ? accept({gas: params.gas, error: err}) - : accept({gas: null, error: err}) - }); + (params.gas) + ? accept({gas: params.gas, error: err}) + : accept({gas: null, error: err}); + }) + }) }) }, @@ -78,14 +93,22 @@ var execute = { * @param {[type]} err [description] * @return {[type]} [description] */ - extractReason(err, web3){ - if (err && err.results){ - const hash = Object.keys(err.results)[0]; + extractReason(res, web3){ + const isObject = res && typeof res === 'object' && res.error && res.error.data; + const isString = res && typeof res === 'object' && typeof res.result === 'string'; + + if (isObject){ + const data = res.error.data; + const hash = Object.keys(data)[0]; - if (err.results[hash].return && err.results[hash].return.includes('0x08c379a0')){ - return web3.eth.abi.decodeParameter('string', err.results[hash].return.slice(10)) + if (data[hash].return && data[hash].return.includes('0x08c379a0')){ + return web3.eth.abi.decodeParameter('string', data[hash].return.slice(10)) } } + + if (isString && res.result.includes('0x08c379a0')){ + return web3.eth.abi.decodeParameter('string', res.result.slice(10)) + } }, /** diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index 7e479ff8ca9..f18f7572ed1 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -250,7 +250,7 @@ class Reporter { `${prefix} "${data.contract.contractName}" ran out of gas. Something in the constructor ` + `(ex: infinite loop) caused gas estimation to fail. Try:\n` + ` * Making your contract constructor more efficient\n` + - ` * Setting the gas manually in your config or a deployment parameter\n` + + ` * Setting the gas manually in your config or as a deployment parameter\n` + ` * Using the solc optimizer settings in 'truffle.js'\n` + ` * Setting a higher network block limit if you are on a\n` + ` private network or test client (like ganache).\n`, @@ -417,6 +417,8 @@ class Reporter { let message; const error = data.estimateError || data.error; + data.reason = (data.error) ? data.error.reason : null; + const errors = { INT: error.message.includes('base fee') || error.message.includes('intrinsic'), OOG: error.message.includes('out of gas'), diff --git a/packages/truffle/test/scenarios/migrations/errors.js b/packages/truffle/test/scenarios/migrations/errors.js index f338be7b6c9..04a0e7f6d51 100644 --- a/packages/truffle/test/scenarios/migrations/errors.js +++ b/packages/truffle/test/scenarios/migrations/errors.js @@ -96,14 +96,16 @@ describe("migration errors", function() { }); }) - it.skip("should expose the reason string if available [ @ganache ]", function(done){ + it("should expose the reason string if available [ @ganache ]", function(done){ + if (process.env.GETH) return; + this.timeout(70000); CommandRunner.run("migrate -f 5", config, err => { const output = logger.contents(); assert(err); console.log(output); - assert(output.includes('5_migrations_revert.js')); + assert(output.includes('5_migrations_reason.js')); assert(output.includes("Deploying 'RevertWithReason'")); assert(output.includes('reasonstring')); done(); From fe5bb716f98f697a2ad11be9cf09620f7390a53f Mon Sep 17 00:00:00 2001 From: cgewecke Date: Tue, 19 Jun 2018 19:40:08 -0700 Subject: [PATCH 28/74] Restructure Migration callbacks as class methods --- packages/truffle-migrate/index.js | 192 ++++++++++++++++++------------ 1 file changed, 114 insertions(+), 78 deletions(-) diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 96741abc53f..49194dd92b1 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -25,9 +25,113 @@ class Migration { this.isFirst = false; this.isLast = false; this.options = options || {}; + } + + // ------------------------------------- Private ------------------------------------------------- + /** + * Loads & validates migration, then runs it. + * @param {Object} options config and command-line + * @param {Object} context web3 + * @param {Object} deployer truffle module + * @param {Object} resolver truffle module + * @param {Function} callback + */ + async _load(options, context, deployer, resolver, callback){ + const self = this; + + // Load assets and run `execute` + try { + const accounts = await context.web3.eth.getAccounts(); + const requireOptions = { + file: self.file, + context: context, + resolver: resolver, + args: [deployer], + } + + Require.file(requireOptions, async (err, fn) => { + if (err) return callback(err); + + const unRunnable = !fn || !fn.length || fn.length == 0; + + if (unRunnable){ + const msg = `Migration ${self.file} invalid or does not take any parameters`; + return callback(new Error(msg)); + } + + // `migrateFn` might be sync or async. We negotiate that difference in + // `execute` through the deployer API. + const migrateFn = fn(deployer, options.network, accounts); + await self._deploy(options, deployer, resolver, migrateFn, callback); + }); + + } catch(err){ + callback(err) + } + } + + /** + * Initiates deployer sequence, then manages migrations info + * publication to chain / artifact saving. + * @param {Object} options config and command-line + * @param {Object} deployer truffle module + * @param {Object} resolver truffle module + * @param {[type]} migrateFn module.exports of a migrations.js + */ + async _deploy(options, deployer, resolver, migrateFn, callback) { + const self = this; + + try { + await deployer.start(); + + // Allow migrations method to be async and + // deploy to use await + if (migrateFn && migrateFn.then !== undefined){ + await deployer.then(() => migrateFn); + } + + // Migrate without saving + if (options.save === false) return; + + // Write migrations record to chain + const Migrations = resolver.require("./Migrations.sol"); + + if (Migrations && Migrations.isDeployed()) { + await self.emitter.emit('saveMigration', self.number); + const migrations = await Migrations.deployed(); + await migrations.setCompleted(self.number); + } + + // Save artifacts to local filesystem + await self.emitter.emit('postMigrate', self.isLast); + await options.artifactor.saveAll(resolver.contracts()); + deployer.finish(); + + // Cleanup + if (self.isLast){ + self.emitter.clearListeners(); + } + // Prevent errors thrown in the callback from triggering the below catch() + process.nextTick(callback); + } catch(e) { + const payload = { + type: 'migrateErr', + error: e + }; + await self.emitter.emit('error', payload); + deployer.finish(); + callback(e); + }; } + // ------------------------------------- Public ------------------------------------------------- + /** + * Instantiates a deployer, connects this migration and its deployer to the reporter + * and launches a migration file's deployment sequence + * @param {Object} options config and command-line + * @param {Function} callback + */ async run(options, callback) { const self = this; const logger = options.logger; @@ -40,104 +144,36 @@ class Migration { web3: web3 }; + // Instantiate a Deployer const deployer = new Deployer({ - logger: { - log: function(msg) { - logger.log(msg); - }, - error: function(msg){ - logger.error(msg); - } - }, + logger: logger, network: options.network, network_id: options.network_id, provider: options.provider, basePath: path.dirname(self.file) }); - this.reporter.migration = self; - this.reporter.deployer = deployer; - this.reporter.listen(); + // Connect reporter to this migration + self.reporter.migration = self; + self.reporter.deployer = deployer; + self.reporter.listen(); + // Get file path and emit pre-migration event const file = path.relative(options.migrations_directory, self.file) const preMigrationsData = { file: file, - isFirst: this.isFirst, + isFirst: self.isFirst, network: options.network, networkId: options.network_id, } await self.emitter.emit('preMigrate', preMigrationsData); - - var finish = async function(migrateFn) { - try { - await deployer.start(); - - if (migrateFn && migrateFn.then !== undefined){ - await deployer.then(() => migrateFn); - } - - if (options.save === false) return; - - const Migrations = resolver.require("./Migrations.sol"); - - if (Migrations && Migrations.isDeployed()) { - await self.emitter.emit('saveMigration', self.number); - const migrations = await Migrations.deployed(); - await migrations.setCompleted(self.number); - } - - await self.emitter.emit('postMigrate', self.isLast); - await options.artifactor.saveAll(resolver.contracts()); - deployer.finish(); - - if (self.isLast){ - self.emitter.clearListeners(); - } - // Prevent errors thrown in the callback from triggering the below catch() - process.nextTick(callback); - } catch(e) { - const payload = { - type: 'migrateErr', - error: e - }; - - await self.emitter.emit('error', payload); - deployer.finish(); - callback(e); - }; - }; - - try { - const accounts = await web3.eth.getAccounts(); - const requireOptions = { - file: self.file, - context: context, - resolver: resolver, - args: [deployer], - } - - Require.file(requireOptions, async (err, fn) => { - if (err) return callback(err); - - const unRunnable = !fn || !fn.length || fn.length == 0; - - if (unRunnable){ - const msg = `Migration ${self.file} invalid or does not take any parameters`; - return callback(new Error(msg)); - } - - const migrateFn = fn(deployer, options.network, accounts); - await finish(migrateFn); - }); - - } catch(err){ - callback(err) - } + await self._load(options, context, deployer, resolver, callback); } } + const Migrate = { Migration: Migration, reporter: new Reporter(), From d63edcbafeff0f5a1f0d9f6d6397eb08e36181e9 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 20 Jun 2018 09:04:51 -0700 Subject: [PATCH 29/74] Filter tests by geth/ganache tags in CI --- packages/truffle/package.json | 2 +- packages/truffle/scripts/geth-accounts.js | 33 ----------- packages/truffle/scripts/test.sh | 59 ++----------------- .../test/scenarios/migrations/errors.js | 2 - 4 files changed, 5 insertions(+), 91 deletions(-) delete mode 100644 packages/truffle/scripts/geth-accounts.js diff --git a/packages/truffle/package.json b/packages/truffle/package.json index 4796054273c..a1a9de64803 100644 --- a/packages/truffle/package.json +++ b/packages/truffle/package.json @@ -37,7 +37,7 @@ "prepare": "npm run build", "build": "npm run build-cli", "build-cli": "webpack --config ./cli.webpack.config.js", - "test": "npm run build-cli && mocha --colors", + "test": "./scripts/test.sh", "publish:byoc": "node ./scripts/prereleaseVersion.js byoc-safe byoc", "publish:next": "node ./scripts/prereleaseVersion.js next next", "test:raw": "NO_BUILD=true mocha" diff --git a/packages/truffle/scripts/geth-accounts.js b/packages/truffle/scripts/geth-accounts.js deleted file mode 100644 index 225400c1614..00000000000 --- a/packages/truffle/scripts/geth-accounts.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @author cpurta - * @github https://github.com/cpurta/geth-devnet - * This code comes from Christopher Purta's `geth-devnet` project. - * geth --dev seeds with a single account so we need to spin - * up more accounts and short-circuit account auto-locking to get multi-account - * tests passing. - */ - -function createAccounts() { - for (var i = 0; i < 10; i++) { - acc = personal.newAccount(""); - personal.unlockAccount(acc, ""); - eth.sendTransaction({from: eth.accounts[0], to: acc, value: web3.toWei(1000, "ether")}); - } -} - -function unlockAccounts() { - eth.accounts.forEach(function (account) { - console.log('Unlocking ' + account + '...'); - personal.unlockAccount(account, '', 86400); - }); -} - -function setupDevNode() { - // keep accounts unlocked - while (true) { - unlockAccounts() - } -} - -createAccounts(); -setupDevNode(); \ No newline at end of file diff --git a/packages/truffle/scripts/test.sh b/packages/truffle/scripts/test.sh index d3ef54ae45c..b256e9978c2 100755 --- a/packages/truffle/scripts/test.sh +++ b/packages/truffle/scripts/test.sh @@ -1,60 +1,9 @@ #!/usr/bin/env bash -run_geth() { - docker run \ - -v /$PWD/scripts:/scripts \ - -d \ - --net="host" \ - -p 8545:8545 \ - -p 30303:30303 \ - ethereum/client-go:latest \ - --rpc \ - --rpcaddr '0.0.0.0' \ - --rpcport 8545 \ - --rpccorsdomain '*' \ - --nodiscover \ - --dev \ - --dev.period 1 \ - --targetgaslimit '7000000' \ - js ./scripts/geth-accounts.js \ - > /dev/null & -} +set -o errexit -run_geth_test() { - docker run -it --rm --name ${TEST} --net="host" \ - -e TRAVIS_REPO_SLUG \ - -e TRAVIS_PULL_REQUEST \ - -e TRAVIS_PULL_REQUEST_SLUG \ - -e TRAVIS_PULL_REQUEST_BRANCH \ - -e TRAVIS_BRANCH \ - -e TEST \ - -e GETH \ - -e MAIN_REPO_CI \ - truffle/ci:latest run_tests -} - -run_ganache_test() { - docker run -it --rm --name ${TEST} \ - -e TRAVIS_REPO_SLUG \ - -e TRAVIS_PULL_REQUEST \ - -e TRAVIS_PULL_REQUEST_SLUG \ - -e TRAVIS_PULL_REQUEST_BRANCH \ - -e TRAVIS_BRANCH \ - -e TEST \ - -e GANACHE \ - -e MAIN_REPO_CI \ - truffle/ci:latest run_tests -} - - -if [ "$GETH" = true ]; then - run_geth - run_geth_test +if [ "$GETH" == true ]; then + npm run build-cli && mocha --timeout 50000 --grep @ganache --invert --colors else - run_ganache_test + npm run build-cli && mocha --no-warnings --timeout 7000 --grep @geth --invert --colors fi - - - - - diff --git a/packages/truffle/test/scenarios/migrations/errors.js b/packages/truffle/test/scenarios/migrations/errors.js index 04a0e7f6d51..ca43be45a15 100644 --- a/packages/truffle/test/scenarios/migrations/errors.js +++ b/packages/truffle/test/scenarios/migrations/errors.js @@ -97,8 +97,6 @@ describe("migration errors", function() { }) it("should expose the reason string if available [ @ganache ]", function(done){ - if (process.env.GETH) return; - this.timeout(70000); CommandRunner.run("migrate -f 5", config, err => { From d05d236356c7592bd35611beefb4e184bbbe366b Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 20 Jun 2018 20:43:35 -0700 Subject: [PATCH 30/74] Add block polling logic to deployer (conf & tx wait) --- packages/truffle-deployer/src/deployment.js | 97 ++++++++++++++++++- packages/truffle-deployer/test/deployer.js | 26 ++++- .../truffle-deployer/test/helpers/reporter.js | 7 ++ .../truffle-deployer/test/helpers/utils.js | 2 + 4 files changed, 127 insertions(+), 5 deletions(-) diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 799e636b1ac..7f8e197968c 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -15,6 +15,7 @@ class Deployment { this.promiEventEmitters = []; this.confirmations = {}; this.pollingInterval = 1000; + this.blockPoll; } // ------------------------------------ Utils --------------------------------------------------- @@ -65,6 +66,11 @@ class Deployment { } /** + * NB: This should work but there are outstanding issues at both + * geth (with websockets) & web3 (with confirmation handling over RPC) that + * prevent it from being reliable. We're using very simple block polling instead. + * (See also _confirmationCb ) + * * Queries the confirmations mapping periodically to see if we have * heard enough confirmations for a given tx to allow `deploy` to complete. * Resolves when this is true. @@ -85,6 +91,78 @@ class Deployment { }) } + /** + * Emits a `block` event on each new block heard. This polling is + * meant to be cancelled immediately on resolution of the + * contract instance or on error. (See stopBlockPolling) + */ + async _startBlockPolling(web3){ + const self = this; + let blocksWaited = 0; + let currentBlock = await web3.eth.getBlockNumber(); + + self.blockPoll = setInterval(async() => { + const newBlock = await web3.eth.getBlockNumber(); + + if (newBlock > currentBlock){ + blocksWaited = (newBlock - currentBlock) + blocksWaited; + currentBlock = newBlock; + + const eventArgs = { + blockNumber: newBlock, + blocksWaited: blocksWaited + }; + + await self.emitter.emit('block', eventArgs); + } + }, self.pollingInterval); + } + + /** + * Clears the interval timer initiated by `startBlockPolling + */ + _stopBlockPolling(){ + clearInterval(this.blockPoll); + } + + /** + * Waits `n` blocks after a tx is mined, firing a pseudo + * 'confirmation' event for each one. + * @param {Number} blocksToWait + * @param {Object} receipt + * @param {Object} web3 + * @return {Promise} Resolves after `blockToWait` blocks + */ + async _waitBlocks(blocksToWait, state, web3){ + const self = this; + let currentBlock = await web3.eth.getBlockNumber(); + + return new Promise(accept => { + let blocksHeard = 0; + + const poll = setInterval(async () => { + const newBlock = await web3.eth.getBlockNumber(); + + if(newBlock > currentBlock){ + blocksHeard = (newBlock - currentBlock) + blocksHeard; + currentBlock = newBlock; + + const eventArgs = { + contractName: state.contractName, + receipt: state.receipt, + num: blocksHeard, + }; + await self.emitter.emit('confirmation', eventArgs); + } + + if (blocksHeard >= blocksToWait){ + clearInterval(poll) + accept(); + } + }, self.pollingInterval); + }); + } + /** * Sanity checks catch-all: * Are we connected? @@ -142,6 +220,11 @@ class Deployment { } /** + * NB: This should work but there are outstanding issues at both + * geth (with websockets) & web3 (with confirmation handling over RPC) that + * prevent it from being reliable. We're using very simple block polling instead. + * (See also _waitForConfirmations ) + * * Handler for contract's `confirmation` event. Rebroadcasts as a deployer event * and maintains a table of txHashes & their current confirmation number. This * table gets polled if the user needs to wait a few blocks before getting @@ -220,20 +303,26 @@ class Deployment { promiEvent .on('transactionHash', self._hashCb.bind(promiEvent, self, state)) .on('receipt', self._receiptCb.bind(promiEvent, self, state)) - .on('confirmation', self._confirmationCb.bind(promiEvent, self, state)) + + await self._startBlockPolling(contract.web3); // Get instance (or error) try { + instance = await promiEvent; + self._stopBlockPolling(); + } catch(err){ + + self._stopBlockPolling(); eventArgs.error = err.error || err; await self.emitter.emit('deployFailed', eventArgs); throw new Error(self._errorCodes('deployFailed')); } - // Wait for confirmations + // Wait for `n` blocks if(self.confirmationsRequired !== 0){ - await self._waitForConfirmations(instance.transactionHash) + await self._waitBlocks(self.confirmationsRequired, state, contract.web3); } // Case: already deployed @@ -302,4 +391,4 @@ class Deployment { } }; -module.exports = Deployment; +module.exports = Deployment; \ No newline at end of file diff --git a/packages/truffle-deployer/test/deployer.js b/packages/truffle-deployer/test/deployer.js index d59f88df4ff..3132e6d5f8d 100644 --- a/packages/truffle-deployer/test/deployer.js +++ b/packages/truffle-deployer/test/deployer.js @@ -219,7 +219,7 @@ describe("Deployer (sync)", function() { }) it('waits for confirmations', async function(){ - this.timeout(5000); + this.timeout(10000); const startBlock = await web3.eth.getBlockNumber(); utils.startAutoMine(web3, 500); @@ -249,4 +249,28 @@ describe("Deployer (sync)", function() { deployer.confirmationsRequired = 0; }); + + it('emits block events while waiting for a tx to mine', async function(){ + this.timeout(10000); + const startBlock = await web3.eth.getBlockNumber(); + + utils.startAutoMine(web3, 2000); + + const migrate = function(){ + deployer.then(async function(){ + await deployer._startBlockPolling(web3); + await utils.waitMS(5000); + deployer._startBlockPolling(); + }); + }; + + migrate(); + await deployer.start(); + utils.stopAutoMine(); + + assert(output.includes(`Block number: ${startBlock + 1}`)); + assert(output.includes(`Wait: 1`)); + assert(output.includes(`Block number: ${startBlock + 2}`)); + assert(output.includes(`Wait: 2`)) + }); }); diff --git a/packages/truffle-deployer/test/helpers/reporter.js b/packages/truffle-deployer/test/helpers/reporter.js index af0e6a454d8..510074cd48f 100644 --- a/packages/truffle-deployer/test/helpers/reporter.js +++ b/packages/truffle-deployer/test/helpers/reporter.js @@ -22,6 +22,7 @@ class Reporter { this.deployer.emitter.on('transactionHash', this.hash.bind(this)); this.deployer.emitter.on('receipt', this.receipt.bind(this)); this.deployer.emitter.on('confirmation', this.confirmation.bind(this)); + this.deployer.emitter.on('block', this.block.bind(this)); } async preDeploy(payload){ @@ -93,6 +94,11 @@ class Reporter { this.logConfirmations && this.deployer.logger.log(message); } + async block(payload){ + let message = this.messages('block', payload.blockNumber, payload.blocksWaited); + this.deployer.logger.log(message); + } + underline(msg){ const ul = '-'.repeat(msg.length); return `\n${msg}\n${ul}`; @@ -199,6 +205,7 @@ class Reporter { hash: `${args[1]}`.padEnd(20) + ' > transaction hash:'.padEnd(25) + args[0], receipt: `${args[1]}`.padEnd(20) + ' > gas usage:'.padEnd(25) + args[0], confirmation: `${args[2]}`.padEnd(20) + ' > confirmation number:'.padEnd(25) + args[0], + block: `Block number: ${args[0]} Wait: ${args[1]}`, } return kinds[kind]; diff --git a/packages/truffle-deployer/test/helpers/utils.js b/packages/truffle-deployer/test/helpers/utils.js index a0ca8724d81..c6fb44b9fb2 100644 --- a/packages/truffle-deployer/test/helpers/utils.js +++ b/packages/truffle-deployer/test/helpers/utils.js @@ -45,6 +45,8 @@ const utils = { stopAutoMine: () => clearInterval(utils.miningId), + waitMS: async (ms) => new Promise(resolve => setTimeout(() => resolve(), ms)), + cleanUp: () => fs.removeSync(utils.buildDir), getContract: function(name, provider, networkId, account){ From e61eb36ec16ed399598254724b4e49dc52a6e3aa Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sat, 16 Jun 2018 11:12:54 -0700 Subject: [PATCH 31/74] Pretty print docker list correctly From 0363f9190d7f6eac742e69294b5bf14bda2fc754 Mon Sep 17 00:00:00 2001 From: Tim Coulter Date: Tue, 19 Jun 2018 14:53:44 -0700 Subject: [PATCH 32/74] Evaluate the `await` keyword in the console if supported by node version. --- packages/truffle-core/lib/console.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/truffle-core/lib/console.js b/packages/truffle-core/lib/console.js index 29d7f9325e8..86cc197f850 100644 --- a/packages/truffle-core/lib/console.js +++ b/packages/truffle-core/lib/console.js @@ -175,26 +175,29 @@ Console.prototype.interpret = function(cmd, context, filename, callback) { let includesAwait = /^\s*((?:(?:var|const|let)\s+)?[a-zA-Z_$][0-9a-zA-Z_$]*\s*=\s*)?(\(?\s*await[\s\S]*)/; var match = cmd.match(includesAwait); - var source = cmd; + var newSource = cmd; var assignment = null; // If our code includes an await, add special processing to ensure it's evaluated properly. if (match) { var assign = match[1]; var expression = match[2]; - + var RESULT = "__await_outside_result"; // Wrap the await inside an async function. // Strange indentation keeps column offset correct in stack traces - source = `(async function() { try { ${assign ? `global.${RESULT} =` : "return"} ( + newSource = `(async function() { try { ${assign ? `global.${RESULT} =` : "return"} ( ${expression.trim()} ); } catch(e) { global.ERROR = e; throw e; } }())`; assignment = assign ? `${assign.trim()} global.${RESULT}; void delete global.${RESULT};` : null; - } + } else { + // No await? Just process the source as normal. + newSource = cmd; + } var runScript = function(s) { const options = { displayErrors: true, breakOnSigint: true, filename: filename }; @@ -203,7 +206,7 @@ ${expression.trim()} try { const options = { displayErrors: true, lineOffset: -1 }; - var script = vm.createScript(source, options); + var script = vm.createScript(newSource, options); } catch (e) { // If syntax error, or similar, bail. return callback(e); @@ -214,7 +217,7 @@ ${expression.trim()} // this will ensure the console waits until that await is finished. Promise.resolve(runScript(script)).then(function(value) { // If there's an assignment to run, run that. - if (assignment) { + if (assignment) { return runScript(vm.createScript(assignment)); } else { return value; From 42ab1bb201b78e80958aaa357dda0322fb57c48b Mon Sep 17 00:00:00 2001 From: Tim Coulter Date: Tue, 19 Jun 2018 14:59:44 -0700 Subject: [PATCH 33/74] Remove unnecessary block; rename newSource to source. --- packages/truffle-core/lib/console.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/truffle-core/lib/console.js b/packages/truffle-core/lib/console.js index 86cc197f850..756f5121dce 100644 --- a/packages/truffle-core/lib/console.js +++ b/packages/truffle-core/lib/console.js @@ -175,7 +175,7 @@ Console.prototype.interpret = function(cmd, context, filename, callback) { let includesAwait = /^\s*((?:(?:var|const|let)\s+)?[a-zA-Z_$][0-9a-zA-Z_$]*\s*=\s*)?(\(?\s*await[\s\S]*)/; var match = cmd.match(includesAwait); - var newSource = cmd; + var source = cmd; var assignment = null; // If our code includes an await, add special processing to ensure it's evaluated properly. @@ -187,17 +187,14 @@ Console.prototype.interpret = function(cmd, context, filename, callback) { // Wrap the await inside an async function. // Strange indentation keeps column offset correct in stack traces - newSource = `(async function() { try { ${assign ? `global.${RESULT} =` : "return"} ( + source = `(async function() { try { ${assign ? `global.${RESULT} =` : "return"} ( ${expression.trim()} ); } catch(e) { global.ERROR = e; throw e; } }())`; assignment = assign ? `${assign.trim()} global.${RESULT}; void delete global.${RESULT};` : null; - } else { - // No await? Just process the source as normal. - newSource = cmd; - } + } var runScript = function(s) { const options = { displayErrors: true, breakOnSigint: true, filename: filename }; @@ -206,7 +203,7 @@ ${expression.trim()} try { const options = { displayErrors: true, lineOffset: -1 }; - var script = vm.createScript(newSource, options); + var script = vm.createScript(source, options); } catch (e) { // If syntax error, or similar, bail. return callback(e); From 48f73898b1e9e54fa89138bcad8e71e421e8ffdc Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 20 Jun 2018 08:02:37 -0700 Subject: [PATCH 34/74] Make send accept txParams / prefer user-defined send From 742652bd578e12fa468f59173a9cc8ba7da91063 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 21 Jun 2018 00:28:20 -0700 Subject: [PATCH 35/74] Fix merge breakage --- package.json | 2 +- packages/truffle-box/test/unbox.js | 13 +++---------- packages/truffle-contract/scripts/test.sh | 4 ++-- packages/truffle-core/test/compile.js | 2 +- packages/truffle-deployer/package.json | 2 +- scripts/ci.sh | 2 +- 6 files changed, 9 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index a144900abd6..26112c59fca 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "build:debugger": "cd packages/truffle-debugger && npm run build", "lerna:bootstrap": "lerna bootstrap", "bootstrap": "npm run lerna:bootstrap && npm run build:debugger", - "test": "lerna run test --stream -- --colors", + "test": "lerna run test --stream --concurrency=1 -- --colors", "ci": "./scripts/ci.sh" }, "devDependencies": { diff --git a/packages/truffle-box/test/unbox.js b/packages/truffle-box/test/unbox.js index 03c7a3dd5a5..45cf256d32c 100644 --- a/packages/truffle-box/test/unbox.js +++ b/packages/truffle-box/test/unbox.js @@ -9,13 +9,8 @@ var TRUFFLE_BOX_DEFAULT = "git@github.com:trufflesuite/truffle-init-default.git" describe("Unbox", function() { var destination = path.join(__dirname, ".truffle_test_tmp"); - before("mkdir", function(done) { - fs.ensureDir(destination, done); - }); - - after("remove tmp dir", function(done) { - fs.remove(destination, done); - }); + before("mkdir", async() => fs.ensureDir(destination)); + after("remove tmp dir", async() => fs.remove(destination)); it("unboxes truffle box from github", function() { this.timeout(5000); @@ -48,9 +43,7 @@ describe("Unbox", function() { // Assert our precondition assert(fs.existsSync(contracts_directory), "contracts directory should exist for this test to be meaningful"); - fs.remove(contracts_directory, function(err) { - if (err) return done(err); - + fs.remove(contracts_directory).then(() => { Box.unbox(TRUFFLE_BOX_DEFAULT, destination) .then(function(boxConfig) { assert( diff --git a/packages/truffle-contract/scripts/test.sh b/packages/truffle-contract/scripts/test.sh index 04ea6298e02..48bdbf8a970 100755 --- a/packages/truffle-contract/scripts/test.sh +++ b/packages/truffle-contract/scripts/test.sh @@ -3,7 +3,7 @@ set -o errexit if [ "$GETH" == true ]; then - mocha --timeout 50000 --grep @geth --colors + mocha --timeout 50000 --grep @geth --colors --exit else - mocha --no-warnings --timeout 7000 --colors + mocha --no-warnings --timeout 7000 --colors --exit fi diff --git a/packages/truffle-core/test/compile.js b/packages/truffle-core/test/compile.js index fe7b11a3f67..d223689e6c4 100644 --- a/packages/truffle-core/test/compile.js +++ b/packages/truffle-core/test/compile.js @@ -15,7 +15,7 @@ describe("compile", function() { var memStream; before("Create a sandbox", function(done) { - this.timeout(10000); + this.timeout(20000); Box.sandbox(function(err, result) { if (err) return done(err); diff --git a/packages/truffle-deployer/package.json b/packages/truffle-deployer/package.json index efa88d73498..394e57dfe5a 100644 --- a/packages/truffle-deployer/package.json +++ b/packages/truffle-deployer/package.json @@ -4,7 +4,7 @@ "description": "Light deployment module for easily deploying Ethereum contracts", "main": "index.js", "scripts": { - "test": "mocha --no-warnings --timeout 10000" + "test": "mocha --no-warnings --timeout 10000 --exit" }, "repository": { "type": "git", diff --git a/scripts/ci.sh b/scripts/ci.sh index 36d78989f9b..a85b4e1aa1f 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -38,6 +38,6 @@ elif [ "$GETH" = true ]; then else - lerna run --scope truffle-* test --stream + lerna run --scope truffle-* test --stream --concurrency=1 fi \ No newline at end of file From 1a7caeabddddc9be8dde8760824b8aac5e8951cd Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sun, 24 Jun 2018 15:11:21 -0700 Subject: [PATCH 36/74] Add confirmations key to config, production scenario sources --- package.json | 3 +- packages/truffle-config/index.js | 15 +++- packages/truffle-deployer/src/deployment.js | 16 ++-- .../test/scenarios/migrations/production.js | 82 +++++++++++++++++++ .../test/sources/migrations/async/truffle.js | 4 - .../production/contracts/Example.sol | 7 ++ .../contracts/Migrations.sol | 0 .../migrations/1_initial_migration.js | 0 .../migrations/2_migrations_conf.js | 6 ++ .../sources/migrations/production/truffle.js | 12 +++ scripts/geth.sh | 23 ++++++ 11 files changed, 154 insertions(+), 14 deletions(-) create mode 100644 packages/truffle/test/scenarios/migrations/production.js delete mode 100644 packages/truffle/test/sources/migrations/async/truffle.js create mode 100644 packages/truffle/test/sources/migrations/production/contracts/Example.sol rename packages/truffle/test/sources/migrations/{async => production}/contracts/Migrations.sol (100%) rename packages/truffle/test/sources/migrations/{async => production}/migrations/1_initial_migration.js (100%) create mode 100644 packages/truffle/test/sources/migrations/production/migrations/2_migrations_conf.js create mode 100644 packages/truffle/test/sources/migrations/production/truffle.js create mode 100755 scripts/geth.sh diff --git a/package.json b/package.json index 26112c59fca..b5314f289fa 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "lerna:bootstrap": "lerna bootstrap", "bootstrap": "npm run lerna:bootstrap && npm run build:debugger", "test": "lerna run test --stream --concurrency=1 -- --colors", - "ci": "./scripts/ci.sh" + "ci": "./scripts/ci.sh", + "geth": "./scripts/geth.sh" }, "devDependencies": { "lerna": "^2.11.0" diff --git a/packages/truffle-config/index.js b/packages/truffle-config/index.js index 85154b3dd0d..cf3ca55b877 100644 --- a/packages/truffle-config/index.js +++ b/packages/truffle-config/index.js @@ -16,7 +16,8 @@ function Config(truffle_directory, working_directory, network) { var default_tx_values = { gas: 6721975, gasPrice: 100000000000, // 100 Shannon, - from: null + from: null, + confirmations: 0 }; this._values = { @@ -167,6 +168,18 @@ function Config(truffle_directory, working_directory, network) { set: function(val) { throw new Error("Don't set config.provider directly. Instead, set config.networks and then set config.networks[].provider") } + }, + confirmations: { + get: function() { + try { + return self.network_config.confirmations; + } catch (e) { + return default_tx_values.confirmations; + } + }, + set: function(val) { + throw new Error("Don't set config.confirmations directly. Instead, set config.networks and then config.networks[].confirmations") + } } }; diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 7f8e197968c..955953f6125 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -7,13 +7,13 @@ class Deployment { /** * constructor * @param {Object} emitter async `Emittery` emitter - * @param {Number} confirmationsRequired confirmations needed to resolve an instance + * @param {Number} confirmations confirmations needed to resolve an instance */ - constructor(emitter, confirmationsRequired){ - this.confirmationsRequired = confirmationsRequired || 0; + constructor(emitter, confirmations){ + this.confirmations = confirmations || 0; this.emitter = emitter; this.promiEventEmitters = []; - this.confirmations = {}; + this.confirmationsMap = {}; this.pollingInterval = 1000; this.blockPoll; } @@ -83,7 +83,7 @@ class Deployment { return new Promise(accept => { interval = setInterval(() => { - if (self.confirmations[hash] >= self.confirmationsRequired){ + if (self.confirmationsMap[hash] >= self.confirmations){ clearInterval(interval); accept(); } @@ -240,7 +240,7 @@ class Deployment { receipt: receipt }; - parent.confirmations[receipt.transactionHash] = num; + parent.confirmationsMap[receipt.transactionHash] = num; await parent.emitter.emit('confirmation', eventArgs); } @@ -321,8 +321,8 @@ class Deployment { } // Wait for `n` blocks - if(self.confirmationsRequired !== 0){ - await self._waitBlocks(self.confirmationsRequired, state, contract.web3); + if(self.confirmations !== 0){ + await self._waitBlocks(self.confirmations, state, contract.web3); } // Case: already deployed diff --git a/packages/truffle/test/scenarios/migrations/production.js b/packages/truffle/test/scenarios/migrations/production.js new file mode 100644 index 00000000000..7341e1b859e --- /dev/null +++ b/packages/truffle/test/scenarios/migrations/production.js @@ -0,0 +1,82 @@ +const MemoryLogger = require("../memorylogger"); +const CommandRunner = require("../commandrunner"); +const fs = require("fs"); +const path = require("path"); +const assert = require("assert"); +const Server = require("../server"); +const Reporter = require("../reporter"); +const sandbox = require("../sandbox"); +const Web3 = require('web3'); + +const log = console.log; + +const util = require('util'); + +function processErr(err, output){ + if (err){ + log(output); + throw new Error(err); + } +} + +describe.only("production migrations [ @geth ]", function() { + let config; + let web3; + let networkId; + const project = path.join(__dirname, '../../sources/migrations/production'); + const logger = new MemoryLogger(); + + before(done => Server.start(done)); + after(done => Server.stop(done)); + + before(async function() { + this.timeout(10000); + config = await sandbox.create(project) + config.network = "ropsten"; + config.logger = logger; + config.mocha = { + reporter: new Reporter(logger) + } + + const provider = new Web3.providers.HttpProvider('http://localhost:8545') + web3 = new Web3(provider); + networkId = await web3.eth.net.getId(); + }); + + it("runs migrations (sync & async/await)", function(done) { + this.timeout(70000); + + CommandRunner.run("migrate", config, err => { + const output = logger.contents(); + processErr(err, output); + + assert(output.includes('2_migrations_conf.js')); + assert(output.includes("Deploying 'Example'")) + + const location = path.join(config.contracts_build_directory, "Example.json"); + const artifact = require(location); + const network = artifact.networks[networkId]; + + assert(output.includes(network.transactionHash)); + assert(output.includes(network.address)); + + console.log(output) + done(); + }) + }); + + /*it('forces a migration with the -f option', function(done){ + this.timeout(70000); + + CommandRunner.run("migrate -f 3", config, err => { + const output = logger.contents(); + processErr(err, output); + assert(!output.includes('2_migrations_sync.js')); + assert(output.includes('3_migrations_async.js')); + assert(output.includes("Replacing 'IsLibrary'")) + assert(output.includes("Replacing 'UsesLibrary'")); + console.log(output) + done(); + }) + });*/ +}); \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/async/truffle.js b/packages/truffle/test/sources/migrations/async/truffle.js deleted file mode 100644 index a6330d6d564..00000000000 --- a/packages/truffle/test/sources/migrations/async/truffle.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - // See - // to customize your Truffle configuration! -}; diff --git a/packages/truffle/test/sources/migrations/production/contracts/Example.sol b/packages/truffle/test/sources/migrations/production/contracts/Example.sol new file mode 100644 index 00000000000..9ea1e534f32 --- /dev/null +++ b/packages/truffle/test/sources/migrations/production/contracts/Example.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.4; + + +contract Example { + string public id = 'Example'; + constructor() public {} +} diff --git a/packages/truffle/test/sources/migrations/async/contracts/Migrations.sol b/packages/truffle/test/sources/migrations/production/contracts/Migrations.sol similarity index 100% rename from packages/truffle/test/sources/migrations/async/contracts/Migrations.sol rename to packages/truffle/test/sources/migrations/production/contracts/Migrations.sol diff --git a/packages/truffle/test/sources/migrations/async/migrations/1_initial_migration.js b/packages/truffle/test/sources/migrations/production/migrations/1_initial_migration.js similarity index 100% rename from packages/truffle/test/sources/migrations/async/migrations/1_initial_migration.js rename to packages/truffle/test/sources/migrations/production/migrations/1_initial_migration.js diff --git a/packages/truffle/test/sources/migrations/production/migrations/2_migrations_conf.js b/packages/truffle/test/sources/migrations/production/migrations/2_migrations_conf.js new file mode 100644 index 00000000000..7f58a6cf51d --- /dev/null +++ b/packages/truffle/test/sources/migrations/production/migrations/2_migrations_conf.js @@ -0,0 +1,6 @@ +const Example = artifacts.require("Example"); + +module.exports = async function(deployer) { + await deployer.deploy(Example); + await deployer.deploy(Example); +}; \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/production/truffle.js b/packages/truffle/test/sources/migrations/production/truffle.js new file mode 100644 index 00000000000..3db03d849f1 --- /dev/null +++ b/packages/truffle/test/sources/migrations/production/truffle.js @@ -0,0 +1,12 @@ +module.exports = { + networks: { + ropsten: { + host: "127.0.0.1", + port: 8545, + network_id: '*', + gas: 4700000, + gasPrice: 20000000000, + confirmations: 2, + }, + }, +}; diff --git a/scripts/geth.sh b/scripts/geth.sh new file mode 100755 index 00000000000..ca929c34b78 --- /dev/null +++ b/scripts/geth.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +docker pull ethereum/client-go:latest + +docker run \ + -v /$PWD/scripts:/scripts \ + -i \ + -p 8545:8545 \ + -p 8546:8546 \ + -p 30303:30303 \ + ethereum/client-go:latest \ + --rpc \ + --rpcaddr '0.0.0.0' \ + --rpcport 8545 \ + --rpccorsdomain '*' \ + --ws \ + --wsaddr '0.0.0.0' \ + --wsorigins '*' \ + --nodiscover \ + --dev \ + --dev.period 1 \ + --targetgaslimit '7000000' \ + js ./scripts/geth-accounts.js \ No newline at end of file From b2da71bff0f2f40a86bcbc84e743d6140481c159 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sun, 24 Jun 2018 16:34:53 -0700 Subject: [PATCH 37/74] Get confirmations display working --- packages/truffle-config/index.js | 2 +- packages/truffle-deployer/index.js | 2 +- packages/truffle-deployer/src/deployment.js | 11 +++++----- packages/truffle-deployer/test/deployer.js | 2 +- packages/truffle-migrate/index.js | 2 ++ packages/truffle-migrate/reporter/reporter.js | 8 +++++--- .../test/scenarios/migrations/production.js | 20 ++++--------------- 7 files changed, 20 insertions(+), 27 deletions(-) diff --git a/packages/truffle-config/index.js b/packages/truffle-config/index.js index cf3ca55b877..4c53d4e8306 100644 --- a/packages/truffle-config/index.js +++ b/packages/truffle-config/index.js @@ -17,7 +17,6 @@ function Config(truffle_directory, working_directory, network) { gas: 6721975, gasPrice: 100000000000, // 100 Shannon, from: null, - confirmations: 0 }; this._values = { @@ -29,6 +28,7 @@ function Config(truffle_directory, working_directory, network) { gas: null, gasPrice: null, from: null, + confirmations: 0, build: null, resolver: null, artifactor: null, diff --git a/packages/truffle-deployer/index.js b/packages/truffle-deployer/index.js index 2b44e743448..c6c3d5e0d32 100644 --- a/packages/truffle-deployer/index.js +++ b/packages/truffle-deployer/index.js @@ -16,7 +16,7 @@ class Deployer extends Deployment { ]); const emitter = new Emittery(); - super(emitter); + super(emitter, options.confirmations); this.emitter = emitter; this.chain = new DeferredChain(); diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 955953f6125..57d41a854df 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -151,7 +151,9 @@ class Deployment { contractName: state.contractName, receipt: state.receipt, num: blocksHeard, + block: currentBlock, }; + await self.emitter.emit('confirmation', eventArgs); } @@ -320,11 +322,6 @@ class Deployment { throw new Error(self._errorCodes('deployFailed')); } - // Wait for `n` blocks - if(self.confirmations !== 0){ - await self._waitBlocks(self.confirmations, state, contract.web3); - } - // Case: already deployed } else { instance = await contract.deployed(); @@ -340,6 +337,10 @@ class Deployment { await self.emitter.emit('postDeploy', eventArgs); + // Wait for `n` blocks + if(self.confirmations !== 0 && shouldDeploy){ + await self._waitBlocks(self.confirmations, state, contract.web3); + } // Finish: Ensure the address and tx-hash are set on the contract. contract.address = instance.address; contract.transactionHash = instance.transactionHash; diff --git a/packages/truffle-deployer/test/deployer.js b/packages/truffle-deployer/test/deployer.js index 3132e6d5f8d..2684f78aae6 100644 --- a/packages/truffle-deployer/test/deployer.js +++ b/packages/truffle-deployer/test/deployer.js @@ -231,7 +231,7 @@ describe("Deployer (sync)", function() { migrate(); - deployer.confirmationsRequired = 2; + deployer.confirmations = 2; await deployer.start(); utils.stopAutoMine(); diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 49194dd92b1..c4e4b9253bf 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -147,6 +147,7 @@ class Migration { // Instantiate a Deployer const deployer = new Deployer({ logger: logger, + confirmations: options.confirmations, network: options.network, network_id: options.network_id, provider: options.provider, @@ -156,6 +157,7 @@ class Migration { // Connect reporter to this migration self.reporter.migration = self; self.reporter.deployer = deployer; + self.reporter.confirmations = options.confirmations || 0; self.reporter.listen(); // Get file path and emit pre-migration event diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index f18f7572ed1..a5e2df6e26c 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -9,7 +9,6 @@ const spinner = require('./indentedSpinner'); class Reporter { constructor(){ this.deployingMany = false; - this.logConfirmations = false; this.deployer = null; this.migration = null; this.currentGasTotal = 0; @@ -191,7 +190,7 @@ class Reporter { async confirmation(data){ let message = this.messages('confirmation', data); - this.logConfirmations && this.deployer.logger.log(message); + this.deployer.logger.log(message); } underline(msg){ @@ -391,6 +390,9 @@ class Reporter { ` > ${'gas used:'.padEnd(20)} ${data.gas}\n` + ` > ${'gas price:'.padEnd(20)} ${data.gasPrice} gwei\n`; + if (self.confirmations !== 0) output += + this.underline(`Pausing for ${self.confirmations} confirmations...`); + return output; }, @@ -407,7 +409,7 @@ class Reporter { ` > ${'gas usage:'.padEnd(20)} ` + data.gas, confirmation: () => - ` > ${'confirmation number:'.padEnd(20)} ` + data.num, + ` > ${'confirmation number:'.padEnd(20)} ` + `${data.num} (block: ${data.block})`, } return kinds[kind](); diff --git a/packages/truffle/test/scenarios/migrations/production.js b/packages/truffle/test/scenarios/migrations/production.js index 7341e1b859e..e75f3707c42 100644 --- a/packages/truffle/test/scenarios/migrations/production.js +++ b/packages/truffle/test/scenarios/migrations/production.js @@ -46,7 +46,7 @@ describe.only("production migrations [ @geth ]", function() { it("runs migrations (sync & async/await)", function(done) { this.timeout(70000); - CommandRunner.run("migrate", config, err => { + CommandRunner.run("migrate --network ropsten", config, err => { const output = logger.contents(); processErr(err, output); @@ -59,24 +59,12 @@ describe.only("production migrations [ @geth ]", function() { assert(output.includes(network.transactionHash)); assert(output.includes(network.address)); + assert(output.includes('2 confirmations')); + assert(output.includes('confirmation number: 1')); + assert(output.includes('confirmation number: 2')); console.log(output) done(); }) }); - - /*it('forces a migration with the -f option', function(done){ - this.timeout(70000); - - CommandRunner.run("migrate -f 3", config, err => { - const output = logger.contents(); - processErr(err, output); - assert(!output.includes('2_migrations_sync.js')); - assert(output.includes('3_migrations_async.js')); - assert(output.includes("Replacing 'IsLibrary'")) - assert(output.includes("Replacing 'UsesLibrary'")); - console.log(output) - done(); - }) - });*/ }); \ No newline at end of file From c73f035daec30f6f417c8cf6590e3dd7ed781caa Mon Sep 17 00:00:00 2001 From: cgewecke Date: Mon, 25 Jun 2018 12:23:44 -0700 Subject: [PATCH 38/74] production-mode draft --- packages/truffle-core/lib/commands/migrate.js | 137 +++++++++++++----- packages/truffle-migrate/index.js | 2 + packages/truffle-migrate/reporter/reporter.js | 1 + .../test/scenarios/migrations/dryrun.js | 7 +- .../test/scenarios/migrations/production.js | 9 +- .../sources/migrations/production/truffle.js | 2 +- 6 files changed, 116 insertions(+), 42 deletions(-) diff --git a/packages/truffle-core/lib/commands/migrate.js b/packages/truffle-core/lib/commands/migrate.js index 0669c0b5e63..ff73083b036 100644 --- a/packages/truffle-core/lib/commands/migrate.js +++ b/packages/truffle-core/lib/commands/migrate.js @@ -27,48 +27,67 @@ var command = { var Contracts = require("truffle-workflow-compile"); var Resolver = require("truffle-resolver"); var Artifactor = require("truffle-artifactor"); - var Migrate = require("truffle-migrate"); + //var Migrate = require("truffle-migrate"); var Environment = require("../environment"); var temp = require("temp"); var copy = require("../copy"); - var config = Config.detect(options); - - function setupDryRunEnvironmentThenRunMigrations(callback) { - Environment.fork(config, function(err) { - if (err) return callback(err); - - // Copy artifacts to a temporary directory - temp.mkdir('migrate-dry-run-', function(err, temporaryDirectory) { - if (err) return callback(err); - - function cleanup() { - var args = arguments; - // Ensure directory cleanup. - temp.cleanup(function(err) { - // Ignore cleanup errors. - callback.apply(null, args); + // Source: ethereum.stackexchange.com/questions/17051 + var networkWhitelist = [ + 1, // Mainnet (ETH & ETC) + 2, // Morden (ETC) + 3, // Ropsten + 4, // Rinkeby + 8, // Ubiq + 42, // Kovan (Parity) + 77, // Sokol + 99, // Core + + 7762959, // Musiccoin + 61717561 // Aquachain + ] + + + function setupDryRunEnvironmentThenRunMigrations(config) { + return new Promise((resolve, reject) => { + Environment.fork(config, function(err) { + if (err) return reject(err); + + // Copy artifacts to a temporary directory + temp.mkdir('migrate-dry-run-', function(err, temporaryDirectory) { + if (err) return reject(err); + + function cleanup() { + var args = arguments; + + // Ensure directory cleanup. + temp.cleanup(function(err) { + (args.length && args[0] !== null) + ? reject(args[0]) + : resolve(); + }); + }; + + copy(config.contracts_build_directory, temporaryDirectory, function(err) { + if (err) return reject(err); + + config.contracts_build_directory = temporaryDirectory; + + // Note: Create a new artifactor and resolver with the updated config. + // This is because the contracts_build_directory changed. + // Ideally we could architect them to be reactive of the config changes. + config.artifactor = new Artifactor(temporaryDirectory); + config.resolver = new Resolver(config); + + runMigrations(config, cleanup); }); - }; - - copy(config.contracts_build_directory, temporaryDirectory, function(err) { - if (err) return callback(err); - - config.contracts_build_directory = temporaryDirectory; - - // Note: Create a new artifactor and resolver with the updated config. - // This is because the contracts_build_directory changed. - // Ideally we could architect them to be reactive of the config changes. - config.artifactor = new Artifactor(temporaryDirectory); - config.resolver = new Resolver(config); - - runMigrations(cleanup); }); }); }); } - function runMigrations(callback) { + function runMigrations(config, callback) { + const Migrate = require('truffle-migrate'); if (options.f) { Migrate.runFrom(options.f, config, done); @@ -77,7 +96,7 @@ var command = { if (err) return callback(err); if (needsMigrating) { - Migrate.run(config, done); + Migrate.run(config, callback); } else { config.logger.log("Network up to date.") callback(); @@ -86,18 +105,62 @@ var command = { } }; - Contracts.compile(config, function(err) { + function executePostDryRunMigration(buildDir){ + const environment = require('../environment'); + const NewConfig = require('truffle-config'); + const config = NewConfig.detect(options); + + config.contracts_build_directory = buildDir; + config.artifactor = new Artifactor(buildDir); + config.resolver = new Resolver(config); + + environment.detect(config, function(err) { + config.dryRun = false; + runMigrations(config, done); + }) + } + + const conf = Config.detect(options); + + Contracts.compile(conf, function(err) { if (err) return done(err); - Environment.detect(config, function(err) { + Environment.detect(conf, async function(err) { if (err) return done(err); var dryRun = options.dryRun === true; + var production = networkWhitelist.includes(conf.network_id) || conf.production; + // Dry run only if (dryRun) { - setupDryRunEnvironmentThenRunMigrations(done); + + try { + await setupDryRunEnvironmentThenRunMigrations(conf); + done(); + } catch(err){ + done(err) + }; + return; + + // Production: dry-run then real run + } + + else if (production){ + + const currentBuild = conf.contracts_build_directory; + conf.dryRun = true; + + try { + await setupDryRunEnvironmentThenRunMigrations(conf) + } catch(err){ + return done(err) + }; + + executePostDryRunMigration(currentBuild); + + // Development } else { - runMigrations(true, done); + runMigrations(conf, done); } }); }); diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index c4e4b9253bf..47ad28c14a0 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -24,6 +24,7 @@ class Migration { this.emitter = new Emittery(); this.isFirst = false; this.isLast = false; + this.dryRun = options.dryRun; this.options = options || {}; } @@ -274,6 +275,7 @@ const Migrate = { // Make migrations aware of their position in sequence const total = migrations.length; + if(total){ migrations[0].isFirst = true; migrations[total - 1].isLast = true; diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index a5e2df6e26c..72628110099 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -8,6 +8,7 @@ const spinner = require('./indentedSpinner'); class Reporter { constructor(){ + console.log('running reporter constructor') this.deployingMany = false; this.deployer = null; this.migration = null; diff --git a/packages/truffle/test/scenarios/migrations/dryrun.js b/packages/truffle/test/scenarios/migrations/dryrun.js index dc8cfd1a89f..7bf649ff4e1 100644 --- a/packages/truffle/test/scenarios/migrations/dryrun.js +++ b/packages/truffle/test/scenarios/migrations/dryrun.js @@ -43,13 +43,18 @@ describe("migrate (dry-run)", function() { networkId = await web3.eth.net.getId(); }); - it('does a dry-run with the dry-run option', function(done){ + it('uses the dry-run option', function(done){ this.timeout(70000); CommandRunner.run("migrate --dry-run", config, err => { const output = logger.contents(); processErr(err, output); + assert(output.includes('dry-run')); + + assert(!output.includes('transaction hash')); + assert(!output.includes('contract address')); + assert(output.includes('Migrations')); assert(output.includes('development-fork')) assert(output.includes('2_migrations_sync.js')); diff --git a/packages/truffle/test/scenarios/migrations/production.js b/packages/truffle/test/scenarios/migrations/production.js index e75f3707c42..f751af723cf 100644 --- a/packages/truffle/test/scenarios/migrations/production.js +++ b/packages/truffle/test/scenarios/migrations/production.js @@ -20,11 +20,13 @@ function processErr(err, output){ } describe.only("production migrations [ @geth ]", function() { + if (!process.env.GETH) return; + let config; let web3; let networkId; const project = path.join(__dirname, '../../sources/migrations/production'); - const logger = new MemoryLogger(); + const logger = console //new MemoryLogger(); before(done => Server.start(done)); after(done => Server.stop(done)); @@ -47,7 +49,8 @@ describe.only("production migrations [ @geth ]", function() { this.timeout(70000); CommandRunner.run("migrate --network ropsten", config, err => { - const output = logger.contents(); + + /*const output = logger.contents(); processErr(err, output); assert(output.includes('2_migrations_conf.js')); @@ -63,7 +66,7 @@ describe.only("production migrations [ @geth ]", function() { assert(output.includes('confirmation number: 1')); assert(output.includes('confirmation number: 2')); - console.log(output) + console.log(output)*/ done(); }) }); diff --git a/packages/truffle/test/sources/migrations/production/truffle.js b/packages/truffle/test/sources/migrations/production/truffle.js index 3db03d849f1..3578f445d9c 100644 --- a/packages/truffle/test/sources/migrations/production/truffle.js +++ b/packages/truffle/test/sources/migrations/production/truffle.js @@ -3,7 +3,7 @@ module.exports = { ropsten: { host: "127.0.0.1", port: 8545, - network_id: '*', + network_id: 3, gas: 4700000, gasPrice: 20000000000, confirmations: 2, From e7a5ba82395c84be65d41a151facca6e457eed3c Mon Sep 17 00:00:00 2001 From: cgewecke Date: Mon, 25 Jun 2018 13:51:16 -0700 Subject: [PATCH 39/74] Working production mode & test --- packages/truffle-compile/scripts/test.sh | 4 ++-- packages/truffle-config/index.js | 17 +++++++++++++++-- packages/truffle-core/lib/commands/migrate.js | 4 ++-- packages/truffle-migrate/index.js | 18 ++++++++++++------ packages/truffle-migrate/reporter/reporter.js | 1 - .../test/scenarios/migrations/production.js | 2 +- .../sources/migrations/production/truffle.js | 3 ++- 7 files changed, 34 insertions(+), 15 deletions(-) diff --git a/packages/truffle-compile/scripts/test.sh b/packages/truffle-compile/scripts/test.sh index af6a6c8e76f..b9a239365c7 100755 --- a/packages/truffle-compile/scripts/test.sh +++ b/packages/truffle-compile/scripts/test.sh @@ -3,7 +3,7 @@ set -o errexit if [ "$CI" = true ]; then - mocha --timeout 5000 + mocha --timeout 10000 else - mocha --invert --grep native --timeout 5000 + mocha --invert --grep native --timeout 10000 fi diff --git a/packages/truffle-config/index.js b/packages/truffle-config/index.js index 4c53d4e8306..15bf1aad6ca 100644 --- a/packages/truffle-config/index.js +++ b/packages/truffle-config/index.js @@ -29,6 +29,7 @@ function Config(truffle_directory, working_directory, network) { gasPrice: null, from: null, confirmations: 0, + production: false, build: null, resolver: null, artifactor: null, @@ -174,13 +175,25 @@ function Config(truffle_directory, working_directory, network) { try { return self.network_config.confirmations; } catch (e) { - return default_tx_values.confirmations; + return 0; } }, set: function(val) { throw new Error("Don't set config.confirmations directly. Instead, set config.networks and then config.networks[].confirmations") } - } + }, + production: { + get: function() { + try { + return self.network_config.production; + } catch (e) { + return false; + } + }, + set: function(val) { + throw new Error("Don't set config.production directly. Instead, set config.networks and then config.networks[].production") + } + }, }; Object.keys(props).forEach(function(prop) { diff --git a/packages/truffle-core/lib/commands/migrate.js b/packages/truffle-core/lib/commands/migrate.js index ff73083b036..e859812c72c 100644 --- a/packages/truffle-core/lib/commands/migrate.js +++ b/packages/truffle-core/lib/commands/migrate.js @@ -27,7 +27,7 @@ var command = { var Contracts = require("truffle-workflow-compile"); var Resolver = require("truffle-resolver"); var Artifactor = require("truffle-artifactor"); - //var Migrate = require("truffle-migrate"); + var Migrate = require("truffle-migrate"); var Environment = require("../environment"); var temp = require("temp"); var copy = require("../copy"); @@ -87,7 +87,7 @@ var command = { } function runMigrations(config, callback) { - const Migrate = require('truffle-migrate'); + Migrate.launchReporter(); if (options.f) { Migrate.runFrom(options.f, config, done); diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 47ad28c14a0..0b8247685ef 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -156,10 +156,12 @@ class Migration { }); // Connect reporter to this migration - self.reporter.migration = self; - self.reporter.deployer = deployer; - self.reporter.confirmations = options.confirmations || 0; - self.reporter.listen(); + if (self.reporter){ + self.reporter.migration = self; + self.reporter.deployer = deployer; + self.reporter.confirmations = options.confirmations || 0; + self.reporter.listen(); + } // Get file path and emit pre-migration event const file = path.relative(options.migrations_directory, self.file) @@ -179,7 +181,11 @@ class Migration { const Migrate = { Migration: Migration, - reporter: new Reporter(), + reporter: null, + + launchReporter: function(){ + Migrate.reporter = new Reporter(); + }, assemble: function(options, callback) { dir.files(options.migrations_directory, function(err, files) { @@ -275,7 +281,6 @@ const Migrate = { // Make migrations aware of their position in sequence const total = migrations.length; - if(total){ migrations[0].isFirst = true; migrations[total - 1].isLast = true; @@ -363,4 +368,5 @@ const Migrate = { } }; + module.exports = Migrate; diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index 72628110099..a5e2df6e26c 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -8,7 +8,6 @@ const spinner = require('./indentedSpinner'); class Reporter { constructor(){ - console.log('running reporter constructor') this.deployingMany = false; this.deployer = null; this.migration = null; diff --git a/packages/truffle/test/scenarios/migrations/production.js b/packages/truffle/test/scenarios/migrations/production.js index f751af723cf..643a9139a2d 100644 --- a/packages/truffle/test/scenarios/migrations/production.js +++ b/packages/truffle/test/scenarios/migrations/production.js @@ -19,7 +19,7 @@ function processErr(err, output){ } } -describe.only("production migrations [ @geth ]", function() { +describe("production migrations [ @geth ]", function() { if (!process.env.GETH) return; let config; diff --git a/packages/truffle/test/sources/migrations/production/truffle.js b/packages/truffle/test/sources/migrations/production/truffle.js index 3578f445d9c..a701130a01f 100644 --- a/packages/truffle/test/sources/migrations/production/truffle.js +++ b/packages/truffle/test/sources/migrations/production/truffle.js @@ -3,10 +3,11 @@ module.exports = { ropsten: { host: "127.0.0.1", port: 8545, - network_id: 3, + network_id: "*", gas: 4700000, gasPrice: 20000000000, confirmations: 2, + production: true }, }, }; From 332f2da8b7991f3a514059e6367e94a672a2c540 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Mon, 25 Jun 2018 17:56:54 -0700 Subject: [PATCH 40/74] Report value / include in cost --- packages/truffle-migrate/reporter/reporter.js | 9 ++++++--- packages/truffle/test/scenarios/migrations/migrate.js | 4 +++- .../migrations/success/contracts/PayableExample.sol | 7 +++++++ .../migrations/success/migrations/3_migrations_async.js | 4 ++++ 4 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 packages/truffle/test/sources/migrations/success/contracts/PayableExample.sol diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index a5e2df6e26c..3511bc42d33 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -121,11 +121,13 @@ class Reporter { const gasPrice = new web3Utils.BN(tx.gasPrice); const gas = new web3Utils.BN(data.receipt.gasUsed); - const cost = gasPrice.mul(gas); + const value = new web3Utils.BN(tx.value); + const cost = gasPrice.mul(gas).add(value); data.gasPrice = web3Utils.fromWei(gasPrice, 'gwei'); data.gas = data.receipt.gasUsed; data.from = tx.from; + data.value = web3Utils.fromWei(value, 'ether'); data.cost = web3Utils.fromWei(cost, 'ether'); data.balance = web3Utils.fromWei(balance, 'ether'); @@ -386,9 +388,10 @@ class Reporter { output += ` > ${'account:'.padEnd(20)} ${data.from}\n` + ` > ${'balance:'.padEnd(20)} ${data.balance}\n` + - ` > ${'cost:'.padEnd(20)} ${data.cost} ETH\n` + ` > ${'gas used:'.padEnd(20)} ${data.gas}\n` + - ` > ${'gas price:'.padEnd(20)} ${data.gasPrice} gwei\n`; + ` > ${'gas price:'.padEnd(20)} ${data.gasPrice} gwei\n` + + ` > ${'value sent:'.padEnd(20)} ${data.value} ETH\n` + + ` > ${'total cost:'.padEnd(20)} ${data.cost} ETH\n`; if (self.confirmations !== 0) output += this.underline(`Pausing for ${self.confirmations} confirmations...`); diff --git a/packages/truffle/test/scenarios/migrations/migrate.js b/packages/truffle/test/scenarios/migrations/migrate.js index 7765a2715bc..8f10d75e601 100644 --- a/packages/truffle/test/scenarios/migrations/migrate.js +++ b/packages/truffle/test/scenarios/migrations/migrate.js @@ -19,7 +19,7 @@ function processErr(err, output){ } } -describe("migrate (sync)", function() { +describe("migrate (success)", function() { let config; let web3; let networkId; @@ -54,6 +54,8 @@ describe("migrate (sync)", function() { assert(output.includes("Deploying 'UsesExample'")) assert(output.includes('3_migrations_async.js')); assert(output.includes("Replacing 'UsesExample'")) + assert(output.includes("PayableExample")); + assert(output.includes("1 ETH")); const location = path.join(config.contracts_build_directory, "UsesExample.json"); const artifact = require(location); diff --git a/packages/truffle/test/sources/migrations/success/contracts/PayableExample.sol b/packages/truffle/test/sources/migrations/success/contracts/PayableExample.sol new file mode 100644 index 00000000000..9037154c9df --- /dev/null +++ b/packages/truffle/test/sources/migrations/success/contracts/PayableExample.sol @@ -0,0 +1,7 @@ +pragma solidity ^0.4.4; + + +contract PayableExample { + string public id = 'PayableExample'; + constructor() public payable {} +} \ No newline at end of file diff --git a/packages/truffle/test/sources/migrations/success/migrations/3_migrations_async.js b/packages/truffle/test/sources/migrations/success/migrations/3_migrations_async.js index 3b8cc4601a0..5cb47571d88 100644 --- a/packages/truffle/test/sources/migrations/success/migrations/3_migrations_async.js +++ b/packages/truffle/test/sources/migrations/success/migrations/3_migrations_async.js @@ -1,10 +1,14 @@ +const web3 = require('web3'); + const IsLibrary = artifacts.require("IsLibrary"); const UsesExample = artifacts.require("UsesExample"); const UsesLibrary = artifacts.require("UsesLibrary"); +const PayableExample = artifacts.require("PayableExample"); module.exports = async function(deployer) { await deployer.deploy(IsLibrary); await deployer.link(IsLibrary, UsesLibrary); await deployer.deploy(UsesExample, IsLibrary.address); await deployer.deploy(UsesLibrary); + await deployer.deploy(PayableExample, {value: web3.utils.toWei('1', 'ether')}); }; From b0d0cce01905c6051b3ffc5adb5d6fce093b54a8 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Tue, 26 Jun 2018 16:19:57 -0700 Subject: [PATCH 41/74] Silence hd-wallet-provider errors on exit --- packages/truffle-migrate/index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 0b8247685ef..6fd5da2208b 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -111,6 +111,13 @@ class Migration { // Cleanup if (self.isLast){ self.emitter.clearListeners(); + + // Exiting w provider-engine appears to be hopeless. This hack on + // our fork just swallows errors from eth-block-tracking + // as we unwind the handlers downstream from here. + if (self.options.provider && self.options.provider.engine){ + self.options.provider.engine.silent = true; + } } // Prevent errors thrown in the callback from triggering the below catch() process.nextTick(callback); From 22053ec75e33cdb73f04eb336e4f00559b4ad6af Mon Sep 17 00:00:00 2001 From: cgewecke Date: Tue, 26 Jun 2018 16:22:18 -0700 Subject: [PATCH 42/74] Fix truffle-contract eth_call res handling --- packages/truffle-contract/lib/execute.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/truffle-contract/lib/execute.js b/packages/truffle-contract/lib/execute.js index 69c87f3d7f6..91b63defaf3 100644 --- a/packages/truffle-contract/lib/execute.js +++ b/packages/truffle-contract/lib/execute.js @@ -32,7 +32,7 @@ var execute = { // This rpc call extracts the reason string web3.currentProvider.send(packet, (err, response) => { - if (response.error || response.result ) { + if (response && (response.error || response.result)) { reason = execute.extractReason(response, web3) } From 0c4f8cd34132e5259576618cb5bdeeaaa05af00d Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 28 Jun 2018 22:44:33 -0700 Subject: [PATCH 43/74] Add "blocks waited" display --- packages/truffle-deployer/src/deployment.js | 12 +++++-- packages/truffle-migrate/index.js | 4 ++- packages/truffle-migrate/reporter/reporter.js | 36 ++++++++++++++++++- .../test/scenarios/migrations/dryrun.js | 4 +-- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 57d41a854df..7a358fc9a83 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -29,6 +29,10 @@ class Deployment { return 1; } + async _waitMS(ms){ + return new Promise(resolve => setTimeout(() => resolve(), ms)) + } + /** * Helper to parse a deploy statement's overwrite option * @param {Arry} args arguments passed to deploy @@ -98,6 +102,9 @@ class Deployment { */ async _startBlockPolling(web3){ const self = this; + const startTime = new Date().getTime(); + + let secondsWaited = 0; let blocksWaited = 0; let currentBlock = await web3.eth.getBlockNumber(); @@ -107,10 +114,12 @@ class Deployment { if (newBlock > currentBlock){ blocksWaited = (newBlock - currentBlock) + blocksWaited; currentBlock = newBlock; + secondsWaited = Math.floor((new Date().getTime() - startTime) / 1000); const eventArgs = { blockNumber: newBlock, - blocksWaited: blocksWaited + blocksWaited: blocksWaited, + secondsWaited: secondsWaited }; await self.emitter.emit('block', eventArgs); @@ -310,7 +319,6 @@ class Deployment { // Get instance (or error) try { - instance = await promiEvent; self._stopBlockPolling(); diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 6fd5da2208b..8d831daade8 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -103,9 +103,11 @@ class Migration { await migrations.setCompleted(self.number); } - // Save artifacts to local filesystem await self.emitter.emit('postMigrate', self.isLast); + + // Save artifacts to local filesystem await options.artifactor.saveAll(resolver.contracts()); + deployer.finish(); // Cleanup diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index 3511bc42d33..e592cd771ad 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -4,7 +4,8 @@ const util = require('util'); const web3Utils = require('web3-utils'); -const spinner = require('./indentedSpinner'); +const indentedSpinner = require('./indentedSpinner'); +const ora = require('ora'); class Reporter { constructor(){ @@ -18,6 +19,8 @@ class Reporter { this.separator = '\n'; this.summary = []; this.currentFileIndex = -1; + this.blockSpinner = null; + this.currentBlockWait = ''; } listen(){ @@ -36,6 +39,7 @@ class Reporter { this.deployer.emitter.on('transactionHash', this.hash.bind(this)); this.deployer.emitter.on('receipt', this.receipt.bind(this)); this.deployer.emitter.on('confirmation', this.confirmation.bind(this)); + this.deployer.emitter.on('block', this.block.bind(this)); } getTotals(){ @@ -167,6 +171,10 @@ class Reporter { } async deployFailed(data){ + if (this.blockSpinner){ + this.blockSpinner.stop(); + } + const message = await this.processDeploymentError(data); this.deployer.logger.error(message) } @@ -184,6 +192,17 @@ class Reporter { async hash(data){ let message = this.messages('hash', data); this.deployer.logger.log(message); + + this.currentBlockWait = `Blocks: 0`.padEnd(21) + + `Seconds: 0`; + + this.blockSpinner = new ora({ + text: this.currentBlockWait, + spinner: indentedSpinner, + color: 'red' + }); + + this.blockSpinner.start(); } async receipt(data){ @@ -195,6 +214,14 @@ class Reporter { this.deployer.logger.log(message); } + async block(data){ + this.currentBlockWait = `Blocks: ${data.blocksWaited}`.padEnd(21) + + `Seconds: ${data.secondsWaited}`; + if (this.blockSpinner){ + this.blockSpinner.text = this.currentBlockWait; + } + } + underline(msg){ return (typeof msg === 'number') ? ` ${'-'.repeat(msg)}` @@ -380,6 +407,13 @@ class Reporter { `> ${'Final cost:'.padEnd(20)} ${data.finalCost} ETH\n`, deployed: () => { + + if(this.blockSpinner){ + this.blockSpinner.stop(); + const stopText = ` > ${this.currentBlockWait}`; + this.deployer.logger.log(stopText); + } + let output = ''; if(!self.migration.dryRun) output += diff --git a/packages/truffle/test/scenarios/migrations/dryrun.js b/packages/truffle/test/scenarios/migrations/dryrun.js index 7bf649ff4e1..b5d67f9f525 100644 --- a/packages/truffle/test/scenarios/migrations/dryrun.js +++ b/packages/truffle/test/scenarios/migrations/dryrun.js @@ -49,7 +49,7 @@ describe("migrate (dry-run)", function() { CommandRunner.run("migrate --dry-run", config, err => { const output = logger.contents(); processErr(err, output); - + console.log(output) assert(output.includes('dry-run')); assert(!output.includes('transaction hash')); @@ -65,7 +65,7 @@ describe("migrate (dry-run)", function() { const location = path.join(config.contracts_build_directory, "UsesExample.json"); - console.log(output) + done(); }) }); From 4b422a92b15f334d2f457ed78d90f91682581192 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sat, 30 Jun 2018 13:23:12 -0700 Subject: [PATCH 44/74] Add interactive confirmation post dry-run (production-mode) --- packages/truffle-core/lib/commands/migrate.js | 39 +++++--- packages/truffle-migrate/index.js | 5 ++ packages/truffle-migrate/reporter/reporter.js | 89 +++++++++++++++---- .../test/scenarios/migrations/production.js | 6 +- 4 files changed, 105 insertions(+), 34 deletions(-) diff --git a/packages/truffle-core/lib/commands/migrate.js b/packages/truffle-core/lib/commands/migrate.js index e859812c72c..0d2c222d14a 100644 --- a/packages/truffle-core/lib/commands/migrate.js +++ b/packages/truffle-core/lib/commands/migrate.js @@ -19,7 +19,12 @@ var command = { f: { describe: "Specify a migration number to run from", type: "number" - } + }, + "interactive": { + describe: "Manually authorize deployments after seeing a preview", + type: "boolean", + default: false + }, }, run: function (options, done) { var OS = require("os"); @@ -105,19 +110,29 @@ var command = { } }; - function executePostDryRunMigration(buildDir){ - const environment = require('../environment'); - const NewConfig = require('truffle-config'); - const config = NewConfig.detect(options); + async function executePostDryRunMigration(buildDir){ + let accept = true; - config.contracts_build_directory = buildDir; - config.artifactor = new Artifactor(buildDir); - config.resolver = new Resolver(config); + if (options.interactive){ + accept = await Migrate.acceptDryRun(); + } + + if (accept){ + const environment = require('../environment'); + const NewConfig = require('truffle-config'); + const config = NewConfig.detect(options); + + config.contracts_build_directory = buildDir; + config.artifactor = new Artifactor(buildDir); + config.resolver = new Resolver(config); - environment.detect(config, function(err) { - config.dryRun = false; - runMigrations(config, done); - }) + environment.detect(config, function(err) { + config.dryRun = false; + runMigrations(config, done); + }) + } else { + done(); + } } const conf = Config.detect(options); diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 8d831daade8..5564de9532c 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -25,6 +25,7 @@ class Migration { this.isFirst = false; this.isLast = false; this.dryRun = options.dryRun; + this.interactive = options.interactive; this.options = options || {}; } @@ -196,6 +197,10 @@ const Migrate = { Migrate.reporter = new Reporter(); }, + acceptDryRun: async function(){ + return Migrate.reporter.acceptDryRun(); + }, + assemble: function(options, callback) { dir.files(options.migrations_directory, function(err, files) { if (err) return callback(err); diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index e592cd771ad..d5ba526be63 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -5,6 +5,7 @@ const util = require('util'); const web3Utils = require('web3-utils'); const indentedSpinner = require('./indentedSpinner'); +const readline = require('readline'); const ora = require('ora'); class Reporter { @@ -24,22 +25,22 @@ class Reporter { } listen(){ - this.migration.emitter.on('preMigrate', this.preMigrate.bind(this)); - this.migration.emitter.on('saveMigration', this.saveMigrate.bind(this)); - this.migration.emitter.on('postMigrate', this.postMigrate.bind(this)); - this.migration.emitter.on('error', this.error.bind(this)); - - this.deployer.emitter.on('preDeploy', this.preDeploy.bind(this)); - this.deployer.emitter.on('postDeploy', this.postDeploy.bind(this)); - this.deployer.emitter.on('preDeployMany', this.preDeployMany.bind(this)); - this.deployer.emitter.on('postDeployMany', this.postDeployMany.bind(this)); - this.deployer.emitter.on('deployFailed', this.deployFailed.bind(this)); - this.deployer.emitter.on('linking', this.linking.bind(this)); - this.deployer.emitter.on('error', this.error.bind(this)); - this.deployer.emitter.on('transactionHash', this.hash.bind(this)); - this.deployer.emitter.on('receipt', this.receipt.bind(this)); - this.deployer.emitter.on('confirmation', this.confirmation.bind(this)); - this.deployer.emitter.on('block', this.block.bind(this)); + this.migration.emitter.on('preMigrate', this.preMigrate.bind(this)); + this.migration.emitter.on('saveMigration', this.saveMigrate.bind(this)); + this.migration.emitter.on('postMigrate', this.postMigrate.bind(this)); + this.migration.emitter.on('error', this.error.bind(this)); + + this.deployer.emitter.on('preDeploy', this.preDeploy.bind(this)); + this.deployer.emitter.on('postDeploy', this.postDeploy.bind(this)); + this.deployer.emitter.on('preDeployMany', this.preDeployMany.bind(this)); + this.deployer.emitter.on('postDeployMany', this.postDeployMany.bind(this)); + this.deployer.emitter.on('deployFailed', this.deployFailed.bind(this)); + this.deployer.emitter.on('linking', this.linking.bind(this)); + this.deployer.emitter.on('error', this.error.bind(this)); + this.deployer.emitter.on('transactionHash', this.hash.bind(this)); + this.deployer.emitter.on('receipt', this.receipt.bind(this)); + this.deployer.emitter.on('confirmation', this.confirmation.bind(this)); + this.deployer.emitter.on('block', this.block.bind(this)); } getTotals(){ @@ -58,6 +59,35 @@ class Reporter { } } + askBoolean(type){ + const self = this; + const question = self.questions(type); + const exitLine = self.exitLines(type); + + // NB: We need direct access to a writeable stream here. + // This ignores `quiet` - but we only use that mode for `truffle test`. + const input = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + + const affirmations = ['y', 'yes', 'YES', 'Yes']; + + return new Promise(resolve => { + + input.question(question, (answer) => { + if (affirmations.includes(answer.trim())){ + input.close(); + return resolve(true); + }; + + input.close(); + self.migration.logger.log(exitLine); + resolve(false); + }) + }); + } + async preMigrate(data){ let message; if (data.isFirst){ @@ -101,6 +131,10 @@ class Reporter { } } + async acceptDryRun(){ + return this.askBoolean('acceptDryRun'); + } + async migrationError(data){ this.summary[this.currentFileIndex].errored = true; const message = this.messages('migrateErr', data); @@ -233,6 +267,24 @@ class Reporter { return `\n${msg}\n${ul}`; } + questions(kind){ + const prompt = " >> (y/n): " + const kinds = { + "acceptDryRun": `Dry-run successful. ` + + `Do you want to proceed with real deployment? ${prompt}` + } + + return kinds[kind]; + } + + exitLines(kind){ + const kinds = { + "acceptDryRun": "\nExiting without migrating...\n\n", + } + + return kinds[kind]; + } + messages(kind, data){ const self = this; @@ -381,7 +433,6 @@ class Reporter { ? output = this.doubleline(`Migrations dry-run (simulation)`) + '\n' : output = this.doubleline(`Starting migrations...`) + '\n'; - output += `> Network name: '${data.network}'\n` + `> Network id: ${data.networkId}\n`; @@ -459,8 +510,8 @@ class Reporter { data.reason = (data.error) ? data.error.reason : null; const errors = { + OOG: error.message.includes('out of gas') || (data.gas === data.blockLimit), INT: error.message.includes('base fee') || error.message.includes('intrinsic'), - OOG: error.message.includes('out of gas'), RVT: error.message.includes('revert'), ETH: error.message.includes('funds'), BLK: error.message.includes('block gas limit'), @@ -481,7 +532,7 @@ class Reporter { break; case 'OOG': - (data.gas) + (data.gas && !(data.gas === data.blockLimit)) ? message = this.messages('intWithGas', data) : message = this.messages('oogNoGas', data); diff --git a/packages/truffle/test/scenarios/migrations/production.js b/packages/truffle/test/scenarios/migrations/production.js index 643a9139a2d..ecfbcf4f654 100644 --- a/packages/truffle/test/scenarios/migrations/production.js +++ b/packages/truffle/test/scenarios/migrations/production.js @@ -26,7 +26,7 @@ describe("production migrations [ @geth ]", function() { let web3; let networkId; const project = path.join(__dirname, '../../sources/migrations/production'); - const logger = console //new MemoryLogger(); + const logger = new MemoryLogger(); before(done => Server.start(done)); after(done => Server.stop(done)); @@ -50,7 +50,7 @@ describe("production migrations [ @geth ]", function() { CommandRunner.run("migrate --network ropsten", config, err => { - /*const output = logger.contents(); + const output = logger.contents(); processErr(err, output); assert(output.includes('2_migrations_conf.js')); @@ -66,7 +66,7 @@ describe("production migrations [ @geth ]", function() { assert(output.includes('confirmation number: 1')); assert(output.includes('confirmation number: 2')); - console.log(output)*/ + console.log(output) done(); }) }); From dc699c9f53211384881d6f59d18c2f977c85192a Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sat, 30 Jun 2018 15:03:53 -0700 Subject: [PATCH 45/74] Fix reporter on overwrite=false & test --- packages/truffle-migrate/reporter/reporter.js | 5 +++-- packages/truffle/test/scenarios/migrations/dryrun.js | 1 - packages/truffle/test/scenarios/migrations/migrate.js | 1 + .../migrations/success/migrations/3_migrations_async.js | 5 +++++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index d5ba526be63..b2588a74a41 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -177,7 +177,7 @@ class Reporter { this.summary[this.currentFileIndex].deployments.push(data); message = this.messages('deployed', data); } else { - message = this.messages('notDeployed', data); + message = this.messages('reusing', data); } this.deployer.logger.log(message); @@ -403,7 +403,8 @@ class Reporter { this.underline(`Replacing '${data.contract.contractName}'`), reusing: () => - this.underline(`Re-using '${data.contract.contractName}'`), + this.underline(`Re-using deployed '${data.contract.contractName}'`) + '\n' + + ` > ${'contract address:'.padEnd(20)} ${data.contract.address}\n`, many: () => this.underline(`Deploying Batch`), diff --git a/packages/truffle/test/scenarios/migrations/dryrun.js b/packages/truffle/test/scenarios/migrations/dryrun.js index b5d67f9f525..f43b7bdc0c4 100644 --- a/packages/truffle/test/scenarios/migrations/dryrun.js +++ b/packages/truffle/test/scenarios/migrations/dryrun.js @@ -53,7 +53,6 @@ describe("migrate (dry-run)", function() { assert(output.includes('dry-run')); assert(!output.includes('transaction hash')); - assert(!output.includes('contract address')); assert(output.includes('Migrations')); assert(output.includes('development-fork')) diff --git a/packages/truffle/test/scenarios/migrations/migrate.js b/packages/truffle/test/scenarios/migrations/migrate.js index 8f10d75e601..3d2d306052b 100644 --- a/packages/truffle/test/scenarios/migrations/migrate.js +++ b/packages/truffle/test/scenarios/migrations/migrate.js @@ -53,6 +53,7 @@ describe("migrate (success)", function() { assert(output.includes('2_migrations_sync.js')); assert(output.includes("Deploying 'UsesExample'")) assert(output.includes('3_migrations_async.js')); + assert(output.includes("Re-using deployed 'Example'")) assert(output.includes("Replacing 'UsesExample'")) assert(output.includes("PayableExample")); assert(output.includes("1 ETH")); diff --git a/packages/truffle/test/sources/migrations/success/migrations/3_migrations_async.js b/packages/truffle/test/sources/migrations/success/migrations/3_migrations_async.js index 5cb47571d88..2fddaa936f2 100644 --- a/packages/truffle/test/sources/migrations/success/migrations/3_migrations_async.js +++ b/packages/truffle/test/sources/migrations/success/migrations/3_migrations_async.js @@ -1,14 +1,19 @@ const web3 = require('web3'); +const Example = artifacts.require("Example"); const IsLibrary = artifacts.require("IsLibrary"); const UsesExample = artifacts.require("UsesExample"); const UsesLibrary = artifacts.require("UsesLibrary"); const PayableExample = artifacts.require("PayableExample"); module.exports = async function(deployer) { + await deployer.deploy(Example); + await deployer.deploy(Example, {overwrite: false}); + await deployer.deploy(IsLibrary); await deployer.link(IsLibrary, UsesLibrary); await deployer.deploy(UsesExample, IsLibrary.address); await deployer.deploy(UsesLibrary); + await deployer.deploy(PayableExample, {value: web3.utils.toWei('1', 'ether')}); }; From 537bee429b2ff3b36ab4ad3a1b786fe2c521d155 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sat, 30 Jun 2018 17:49:58 -0700 Subject: [PATCH 46/74] Remove stray error log / clarify error exit --- packages/truffle-deployer/src/deployment.js | 11 ++++++----- packages/truffle-migrate/reporter/reporter.js | 5 ++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 7a358fc9a83..6d74fc5d80e 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -22,11 +22,11 @@ class Deployment { /** * Stub for future error code assignments on process.exit - * @param {String} type key map to code + * @param {String} name contract name * @return {Number} code to exit */ - _errorCodes(type){ - return 1; + _errors(name){ + return `Migrations failure` } async _waitMS(ms){ @@ -189,7 +189,7 @@ class Deployment { contract: contract, }) - throw new Error(this._errorCodes('noBytecode')); + throw new Error(this._errors(contract.contractName)); } // Check network @@ -327,7 +327,8 @@ class Deployment { self._stopBlockPolling(); eventArgs.error = err.error || err; await self.emitter.emit('deployFailed', eventArgs); - throw new Error(self._errorCodes('deployFailed')); + self._close(); + throw new Error(self._errors(contract.contractName)); } // Case: already deployed diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index b2588a74a41..a64bba3b589 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -209,8 +209,7 @@ class Reporter { this.blockSpinner.stop(); } - const message = await this.processDeploymentError(data); - this.deployer.logger.error(message) + await this.processDeploymentError(data); } linking(data){ @@ -288,7 +287,7 @@ class Reporter { messages(kind, data){ const self = this; - const prefix = '\nError:'; + const prefix = '\n*** Deployment Failed ***\n\n'; const kinds = { // --------------------------------------- Errors -------------------------------------------- From b208571e14ad6d17a58cf082c0c0d439061bc2f0 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sat, 30 Jun 2018 20:30:36 -0700 Subject: [PATCH 47/74] Restructure reporter, add comments --- packages/truffle-deployer/src/deployment.js | 2 +- packages/truffle-migrate/index.js | 4 +- packages/truffle-migrate/reporter/messages.js | 278 ++++++++ packages/truffle-migrate/reporter/reporter.js | 639 +++++++----------- 4 files changed, 535 insertions(+), 388 deletions(-) create mode 100644 packages/truffle-migrate/reporter/messages.js diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 6d74fc5d80e..18cafe6b40d 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -6,7 +6,7 @@ const util = require('util') class Deployment { /** * constructor - * @param {Object} emitter async `Emittery` emitter + * @param {Object} emitter async `Emittery` emitter * @param {Number} confirmations confirmations needed to resolve an instance */ constructor(emitter, confirmations){ diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 5564de9532c..1dffa55d8f8 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -167,8 +167,8 @@ class Migration { // Connect reporter to this migration if (self.reporter){ - self.reporter.migration = self; - self.reporter.deployer = deployer; + self.reporter.setMigration(self); + self.reporter.setDeployer(deployer); self.reporter.confirmations = options.confirmations || 0; self.reporter.listen(); } diff --git a/packages/truffle-migrate/reporter/messages.js b/packages/truffle-migrate/reporter/messages.js new file mode 100644 index 00000000000..f661270ed4d --- /dev/null +++ b/packages/truffle-migrate/reporter/messages.js @@ -0,0 +1,278 @@ +/** + * A module that formats output for the Migrations reporter. + * This is where all the strings go. + */ +class MigrationsMessages{ + + constructor(reporter){ + this.reporter = reporter; + } + + // ----------------------------------- Utilities ------------------------------------------------- + + underline(msg){ + return (typeof msg === 'number') + ? ` ${'-'.repeat(msg)}` + : `\n ${msg}\n ${'-'.repeat(msg.length)}`; + } + + doubleline(msg){ + const ul = '='.repeat(msg.length); + return `\n${msg}\n${ul}`; + } + + // ----------------------------------- Interactions ---------------------------------------------- + + questions(kind){ + const prompt = " >> (y/n): " + const kinds = { + "acceptDryRun": `Dry-run successful. ` + + `Do you want to proceed with real deployment? ${prompt}` + } + + return kinds[kind]; + } + + exitLines(kind){ + const kinds = { + "acceptDryRun": "\nExiting without migrating...\n\n", + } + + return kinds[kind]; + } + + // ---------------------------------- Errors ---------------------------------------------------- + + errors(kind, data){ + const reporter = this.reporter; + const prefix = '\n*** Deployment Failed ***\n\n'; + + const kinds = { + + migrateErr: () => + `Exiting: Review successful transactions manually by checking the transaction hashes ` + + `above on Etherscan.\n`, + + noLibName: () => + `${prefix} Cannot link a library with no name.\n`, + + noLibAddress: () => + `${prefix} "${data.contract.contractName}" has no address. Has it been deployed?\n`, + + noBytecode: () => + `${prefix} "${data.contract.contractName}" ` + + `is an abstract contract or an interface and cannot be deployed\n` + + ` * Hint: just import the contract into the '.sol' file that uses it.\n`, + + intWithGas: () => + `${prefix} "${data.contract.contractName}" ran out of gas ` + + `(using a value you set in your network config or deployment parameters.)\n` + + ` * Block limit: ${data.blockLimit}\n` + + ` * Gas sent: ${data.gas}\n`, + + intNoGas: () => + `${prefix} "${data.contract.contractName}" ran out of gas ` + + `(using Truffle's estimate.)\n` + + ` * Block limit: ${data.blockLimit}\n` + + ` * Gas sent: ${data.estimate}\n` + + ` * Try:\n` + + ` + Setting a higher gas estimate multiplier for this contract\n` + + ` + Using the solc optimizer settings in 'truffle.js'\n` + + ` + Making your contract smaller\n` + + ` + Making your contract constructor more efficient\n` + + ` + Setting a higher network block limit if you are on a\n` + + ` private network or test client (like ganache).\n`, + + oogNoGas: () => + `${prefix} "${data.contract.contractName}" ran out of gas. Something in the constructor ` + + `(ex: infinite loop) caused gas estimation to fail. Try:\n` + + ` * Making your contract constructor more efficient\n` + + ` * Setting the gas manually in your config or as a deployment parameter\n` + + ` * Using the solc optimizer settings in 'truffle.js'\n` + + ` * Setting a higher network block limit if you are on a\n` + + ` private network or test client (like ganache).\n`, + + rvtReason: () => + `${prefix} "${data.contract.contractName}" hit a require or revert statement ` + + `with the following reason given:\n` + + ` * ${data.reason}\n`, + + rvtNoReason: () => + `${prefix} "${data.contract.contractName}" hit a require or revert statement ` + + `somewhere in its constructor. Try:\n` + + ` * Verifying that your constructor params satisfy all require conditions.\n` + + ` * Adding reason strings to your require statements.\n`, + + asrtNoReason: () => + `${prefix} "${data.contract.contractName}" hit an invalid opcode while deploying. Try:\n` + + ` * Verifying that your constructor params satisfy all assert conditions.\n` + + ` * Verifying your constructor code doesn't access an array out of bounds.\n` + + ` * Adding reason strings to your assert statements.\n`, + + noMoney: () => + `${prefix} "${data.contract.contractName}" could not deploy due to insufficient funds\n` + + ` * Account: ${data.from}\n` + + ` * Balance: ${data.balance} wei\n` + + ` * Message: ${data.error.message}\n` + + ` * Try:\n` + + ` + Using an adequately funded account\n` + + ` + If you are using a local Geth node, verify that your node is synced.\n`, + + blockWithGas: () => + `${prefix} "${data.contract.contractName}" exceeded the block limit ` + + `(with a gas value you set).\n` + + ` * Block limit: ${data.blockLimit}\n` + + ` * Gas sent: ${data.gas}\n` + + ` * Try:\n` + + ` + Sending less gas.\n` + + ` + Setting a higher network block limit if you are on a\n` + + ` private network or test client (like ganache).\n`, + + blockNoGas: () => + `${prefix} "${data.contract.contractName}" exceeded the block limit ` + + `(using Truffle's estimate).\n` + + ` * Block limit: ${data.blockLimit}\n` + + ` * Report this error in the Truffle issues on Github. It should not happen.\n` + + ` * Try: setting gas manually in 'truffle.js' or as parameter to 'deployer.deploy'\n`, + + nonce: () => + `${prefix} "${data.contract.contractName}" received: ${data.error.message}.\n` + + ` * This error is common when Infura is under heavy network load.\n` + + ` * Try: setting the 'confirmations' key in your network config\n` + + ` to wait for several block confirmations between each deployment.\n`, + + geth: () => + `${prefix} "${data.contract.contractName}" received a generic error from Geth that\n` + + `can be caused by hitting revert in a contract constructor or running out of gas.\n` + + ` * ${data.estimateError.message}.\n` + + ` * Try: + using the '--dry-run' option to reproduce this failure with clearer errors.\n` + + ` + verifying that your gas is adequate for this deployment.\n`, + + default: () => + `${prefix} "${data.contract.contractName}" -- ${data.error.message}.\n`, + } + + return kinds[kind](); + } + + // ---------------------------------- Steps ---------------------------------------------------- + + steps(kind, data){ + const self = this; + const reporter = self.reporter; + + const kinds = { + + // Deployments + deploying: () => + self.underline(`Deploying '${data.contract.contractName}'`), + + replacing: () => + self.underline(`Replacing '${data.contract.contractName}'`), + + reusing: () => + self.underline(`Re-using deployed '${data.contract.contractName}'`) + '\n' + + ` > ${'contract address:'.padEnd(20)} ${data.contract.address}\n`, + + deployed: () => { + if(reporter.blockSpinner){ + reporter.blockSpinner.stop(); + const stopText = ` > ${reporter.currentBlockWait}`; + reporter.deployer.logger.log(stopText); + } + + let output = ''; + + if(!reporter.migration.dryRun) output += + ` > ${'contract address:'.padEnd(20)} ${data.receipt.contractAddress}\n`; + + output += + ` > ${'account:'.padEnd(20)} ${data.from}\n` + + ` > ${'balance:'.padEnd(20)} ${data.balance}\n` + + ` > ${'gas used:'.padEnd(20)} ${data.gas}\n` + + ` > ${'gas price:'.padEnd(20)} ${data.gasPrice} gwei\n` + + ` > ${'value sent:'.padEnd(20)} ${data.value} ETH\n` + + ` > ${'total cost:'.padEnd(20)} ${data.cost} ETH\n`; + + if (reporter.confirmations !== 0) output += + self.underline(`Pausing for ${reporter.confirmations} confirmations...`); + + return output; + }, + + + // Libraries + linking: () => { + let output = self.underline(`Linking`) + + `\n * Contract: ${data.contractName} <--> Library: ${data.libraryName} `; + + if(!reporter.migration.dryRun) + output +=`(at address: ${data.libraryAddress})`; + + return output; + }, + + // PromiEvents + hash: () => + ` > ${'transaction hash:'.padEnd(20)} ` + data.transactionHash, + + receipt: () => + ` > ${'gas usage:'.padEnd(20)} ` + data.gas, + + confirmation: () => + ` > ${'confirmation number:'.padEnd(20)} ` + `${data.num} (block: ${data.block})`, + + + // Migrations + preMigrate: () => + self.doubleline(`${data.file}`), + + saving: () => + `\n * Saving migration`, + + firstMigrate: () => { + let output; + (reporter.migration.dryRun) + ? output = self.doubleline(`Migrations dry-run (simulation)`) + '\n' + : output = self.doubleline(`Starting migrations...`) + '\n'; + + output += + `> Network name: '${data.network}'\n` + + `> Network id: ${data.networkId}\n`; + + return output; + }, + + postMigrate: () => { + let output = ''; + + if (!reporter.migration.dryRun) + output += ` * Saving artifacts\n`; + + output += self.underline(37) + '\n' + + ` > ${'Total cost:'.padEnd(15)} ${data.cost.padStart(15)} ETH\n`; + + return output; + }, + + lastMigrate: () => + self.doubleline('Summary') + '\n' + + `> ${'Total deployments:'.padEnd(20)} ${data.totalDeployments}\n` + + `> ${'Final cost:'.padEnd(20)} ${data.finalCost} ETH\n`, + + + // Batch + many: () => + self.underline(`Deploying Batch`), + + listMany: () => + ` * ${data.contractName}`, + + } + + return kinds[kind](); + } +} + +module.exports = MigrationsMessages; + diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index a64bba3b589..f05603a5160 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -1,14 +1,28 @@ -/** - * Example reporter class that emulates the classic logger behavior - */ - const util = require('util'); const web3Utils = require('web3-utils'); -const indentedSpinner = require('./indentedSpinner'); const readline = require('readline'); const ora = require('ora'); +const indentedSpinner = require('./indentedSpinner'); +const MigrationsMessages = require('./messages'); + +/** + * Reporter consumed by a migrations sequence which iteself consumes a series of Migration and + * Deployer instances that emit both async `Emittery` events and conventional EventEmitter + * events (from Web3PromiEvent). This reporter is designed to track the execution of + * several migrations files in sequence and is analagous to the Mocha reporter in that: + * + * test:: deployment + * suite:: deployer.start to deployer.finish + * test file:: migrations file + * + * Each time a new migrations file loads, the reporter needs the following properties + * updated to reflect the current emitter source: + * + `this.migration` + * + `this.deployer` + */ class Reporter { + constructor(){ this.deployingMany = false; this.deployer = null; @@ -22,14 +36,40 @@ class Reporter { this.currentFileIndex = -1; this.blockSpinner = null; this.currentBlockWait = ''; + + this.messages = new MigrationsMessages(this); + } + + // ------------------------------------ Utilities ----------------------------------------------- + + /** + * Sets a Migration instance to be the current migrations events emitter source + * @param {Migration} migration + */ + setMigration(migration){ + this.migration = migration; + } + + /** + * Sets a Deployer instance as the current deployer events emitter source + * @param {Deployer} deployer + */ + setDeployer(deployer){ + this.deployer = deployer } + /** + * Registers emitter handlers + */ listen(){ + + // Migration this.migration.emitter.on('preMigrate', this.preMigrate.bind(this)); this.migration.emitter.on('saveMigration', this.saveMigrate.bind(this)); this.migration.emitter.on('postMigrate', this.postMigrate.bind(this)); this.migration.emitter.on('error', this.error.bind(this)); + // Deployment this.deployer.emitter.on('preDeploy', this.preDeploy.bind(this)); this.deployer.emitter.on('postDeploy', this.postDeploy.bind(this)); this.deployer.emitter.on('preDeployMany', this.preDeployMany.bind(this)); @@ -38,11 +78,14 @@ class Reporter { this.deployer.emitter.on('linking', this.linking.bind(this)); this.deployer.emitter.on('error', this.error.bind(this)); this.deployer.emitter.on('transactionHash', this.hash.bind(this)); - this.deployer.emitter.on('receipt', this.receipt.bind(this)); this.deployer.emitter.on('confirmation', this.confirmation.bind(this)); this.deployer.emitter.on('block', this.block.bind(this)); } + /** + * Retrieves gas usage totals per migrations file / totals since the reporter + * started running. Calling this method resets the gas counters for migrations totals + */ getTotals(){ const gas = this.currentGasTotal; const cost = web3Utils.fromWei(this.currentCostTotal, "ether"); @@ -59,10 +102,15 @@ class Reporter { } } + /** + * Queries the user for a true/false response and resolves the result. + * @param {String} type identifier the reporter consumes to format query + * @return {Promise} + */ askBoolean(type){ const self = this; - const question = self.questions(type); - const exitLine = self.exitLines(type); + const question = this.messages.questions(type); + const exitLine = this.messages.exitLines(type); // NB: We need direct access to a writeable stream here. // This ignores `quiet` - but we only use that mode for `truffle test`. @@ -88,10 +136,118 @@ class Reporter { }); } + /** + * Error dispatcher. Parses the error returned from web3 and outputs a more verbose error after + * doing what it can to evaluate the failure context from data passed to it. + * @param {Object} data info collected during deployment attempt + */ + async processDeploymentError(data){ + let message; + const error = data.estimateError || data.error; + + data.reason = (data.error) ? data.error.reason : null; + + const errors = { + OOG: error.message.includes('out of gas') || (data.gas === data.blockLimit), + INT: error.message.includes('base fee') || error.message.includes('intrinsic'), + RVT: error.message.includes('revert'), + ETH: error.message.includes('funds'), + BLK: error.message.includes('block gas limit'), + NCE: error.message.includes('nonce'), + INV: error.message.includes('invalid opcode'), + GTH: error.message.includes('always failing transaction') + } + + let type = Object.keys(errors).find(key => errors[key]); + + switch (type) { + // `Intrinsic gas too low` + case 'INT': + (data.gas) + ? message = this.messages.errors('intWithGas', data) + : message = this.messages.errors('intNoGas', data); + + this.deployer.logger.error(message); + break; + + // `Out of gas` + case 'OOG': + (data.gas && !(data.gas === data.blockLimit)) + ? message = this.messages.errors('intWithGas', data) + : message = this.messages.errors('oogNoGas', data); + + this.deployer.logger.error(message); + break; + + // `Revert` + case 'RVT': + (data.reason) + ? message = this.messages.errors('rvtReason', data) + : message = this.messages.errors('rvtNoReason', data); + + this.deployer.logger.error(message); + break; + + // `Invalid opcode` + case 'INV': + (data.reason) + ? message = this.messages.errors('asrtReason', data) + : message = this.messages.errors('asrtNoReason', data); + + this.deployer.logger.error(message); + break; + + // `Exceeds block limit` + case 'BLK': + (data.gas) + ? message = this.messages.errors('blockWithGas', data) + : message = this.messages.errors('blockNoGas', data) + + this.deployer.logger.error(message); + break; + + // `Insufficient funds` + case 'ETH': + const balance = await data.contract.web3.eth.getBalance(data.from); + data.balance = balance.toString(); + message = this.messages.errors('noMoney', data); + this.deployer.logger.error(message); + break; + + // `Invalid nonce` + case 'NCE': + message = this.messages.errors('nonce', data); + this.deployer.logger.error(message); + break; + + // Generic geth error + case 'GTH': + message = this.messages.errors('geth', data); + this.deployer.logger.error(message); + break; + + default: + message = this.messages.errors('default', data); + this.deployer.logger.error(message); + } + } + + // ---------------------------- Interaction Handlers --------------------------------------------- + + async acceptDryRun(){ + return this.askBoolean('acceptDryRun'); + } + + // ------------------------- Migration File Handlers -------------------------------------------- + + /** + * Run when a migrations file is loaded, before deployments begin + * @param {Object} data + */ async preMigrate(data){ let message; if (data.isFirst){ - message = this.messages('firstMigrate', data); + message = this.messages.steps('firstMigrate', data); this.deployer.logger.log(message); } @@ -102,21 +258,32 @@ class Reporter { this.currentFileIndex++; - message = this.messages('preMigrate', data); + message = this.messages.steps('preMigrate', data); this.deployer.logger.log(message); } + /** + * Run when a migrations file deployment sequence has completed, + * before the migrations is saved to chain via Migrations.sol + * @param {Object} data + */ async saveMigrate(data){ - const message = this.messages('saving', data); + if (this.migration.dryRun) return; + + const message = this.messages.steps('saving', data); this.deployer.logger.log(message); } + /** + * Run after a migrations file has completed and the migration has been saved. + * @param {Boolean} isLast true if this the last file in the sequence. + */ async postMigrate(isLast){ let data = {}; data.cost = this.getTotals().cost; this.summary[this.currentFileIndex].totalCost = data.cost; - let message = this.messages('postMigrate', data); + let message = this.messages.steps('postMigrate', data); this.deployer.logger.log(message); if (isLast){ @@ -126,30 +293,45 @@ class Reporter { this.summary.totalDeployments = data.totalDeployments; this.summary.finalCost = data.finalCost; - message = this.messages('lastMigrate', data); + message = this.messages.steps('lastMigrate', data); this.deployer.logger.log(message); } } - async acceptDryRun(){ - return this.askBoolean('acceptDryRun'); - } - - async migrationError(data){ - this.summary[this.currentFileIndex].errored = true; - const message = this.messages('migrateErr', data); - this.deployer.logger.log(message); - } + // ---------------------------- Deployment Handlers -------------------------------------------- + /** + * Runs after pre-flight estimate has executed, before the sendTx is attempted + * @param {Object} data + */ async preDeploy(data){ let message; (data.deployed) - ? message = this.messages('replacing', data) - : message = this.messages('deploying', data); + ? message = this.messages.steps('replacing', data) + : message = this.messages.steps('deploying', data); !this.deployingMany && this.deployer.logger.log(message); } + /** + * Run at intervals after the sendTx has executed, before the deployment resolves + * @param {Object} data + */ + async block(data){ + this.currentBlockWait = `Blocks: ${data.blocksWaited}`.padEnd(21) + + `Seconds: ${data.secondsWaited}`; + if (this.blockSpinner){ + this.blockSpinner.text = this.currentBlockWait; + } + } + + /** + * Run after a deployment instance has resolved. This handler collects deployment cost + * data and stores it a `summary` map so that it can later be replayed in an interactive + * preview (e.g. dry-run --> real). Also passes this data to the messaging utility for + * output formatting. + * @param {Object} data + */ async postDeploy(data){ let message; if (data.deployed){ @@ -175,35 +357,20 @@ class Reporter { this.deployments++; this.summary[this.currentFileIndex].deployments.push(data); - message = this.messages('deployed', data); + message = this.messages.steps('deployed', data); } else { - message = this.messages('reusing', data); + message = this.messages.steps('reusing', data); } this.deployer.logger.log(message); } - async preDeployMany(batch){ - let message = this.messages('many'); - - this.deployingMany = true; - this.deployer.logger.log(message); - - batch.forEach(item => { - Array.isArray(item) - ? message = this.messages('listMany', item[0]) - : message = this.messages('listMany', item) - - this.deployer.logger.log(message); - }) - - this.deployer.logger.log(this.separator); - } - - async postDeployMany(){ - this.deployingMany = false; - } - + /** + * Runs on deployment error. Forwards err to the error parser/dispatcher after shutting down + * any `pending` UI. + * @param {O} data [description] + * @return {[type]} [description] + */ async deployFailed(data){ if (this.blockSpinner){ this.blockSpinner.stop(); @@ -212,18 +379,33 @@ class Reporter { await this.processDeploymentError(data); } + // ---------------------------- Library Event Handlers ------------------------------------------ linking(data){ - let message = this.messages('linking', data); + let message = this.messages.steps('linking', data); this.deployer.logger.log(message); } + + // ---------------------------- PromiEvent Handlers -------------------------------------------- + + /** + * For misc error reporting that requires no context specific UI mgmt + * @param {Object} data + */ async error(data){ - let message = this.messages(data.type, data); + let message = this.messages.errors(data.type, data); this.deployer.logger.error(message); } + /** + * Fired on Web3Promievent 'transactionHash' event. Begins running a UI + * a block / time counter. + * @param {Object} data + */ async hash(data){ - let message = this.messages('hash', data); + if (this.migration.dryRun) return; + + let message = this.messages.steps('hash', data); this.deployer.logger.log(message); this.currentBlockWait = `Blocks: 0`.padEnd(21) + @@ -238,353 +420,40 @@ class Reporter { this.blockSpinner.start(); } - async receipt(data){ - let message = this.messages('receipt', data); - } - + /** + * Fired on Web3Promievent 'confirmation' event. Begins running a UI + * a block / time counter. + * @param {Object} data + */ async confirmation(data){ - let message = this.messages('confirmation', data); + let message = this.messages.steps('confirmation', data); this.deployer.logger.log(message); } - async block(data){ - this.currentBlockWait = `Blocks: ${data.blocksWaited}`.padEnd(21) + - `Seconds: ${data.secondsWaited}`; - if (this.blockSpinner){ - this.blockSpinner.text = this.currentBlockWait; - } - } - - underline(msg){ - return (typeof msg === 'number') - ? ` ${'-'.repeat(msg)}` - : `\n ${msg}\n ${'-'.repeat(msg.length)}`; - } + // ---------------------------- Batch Handlers -------------------------------------------------- - doubleline(msg){ - const ul = '='.repeat(msg.length); - return `\n${msg}\n${ul}`; - } + async preDeployMany(batch){ + let message = this.messages.steps('many'); - questions(kind){ - const prompt = " >> (y/n): " - const kinds = { - "acceptDryRun": `Dry-run successful. ` + - `Do you want to proceed with real deployment? ${prompt}` - } + this.deployingMany = true; + this.deployer.logger.log(message); - return kinds[kind]; - } + batch.forEach(item => { + Array.isArray(item) + ? message = this.messages.steps('listMany', item[0]) + : message = this.messages.steps('listMany', item) - exitLines(kind){ - const kinds = { - "acceptDryRun": "\nExiting without migrating...\n\n", - } + this.deployer.logger.log(message); + }) - return kinds[kind]; + this.deployer.logger.log(this.separator); } - messages(kind, data){ - const self = this; - - const prefix = '\n*** Deployment Failed ***\n\n'; - const kinds = { - - // --------------------------------------- Errors -------------------------------------------- - migrateErr: () => - `Exiting: Review successful transactions manually by checking the transaction hashes ` + - `above on Etherscan.\n`, - - noLibName: () => - `${prefix} Cannot link a library with no name.\n`, - - noLibAddress: () => - `${prefix} "${data.contract.contractName}" has no address. Has it been deployed?\n`, - - noBytecode: () => - `${prefix} "${data.contract.contractName}" ` + - `is an abstract contract or an interface and cannot be deployed\n` + - ` * Hint: just import the contract into the '.sol' file that uses it.\n`, - - intWithGas: () => - `${prefix} "${data.contract.contractName}" ran out of gas ` + - `(using a value you set in your network config or deployment parameters.)\n` + - ` * Block limit: ${data.blockLimit}\n` + - ` * Gas sent: ${data.gas}\n`, - - intNoGas: () => - `${prefix} "${data.contract.contractName}" ran out of gas ` + - `(using Truffle's estimate.)\n` + - ` * Block limit: ${data.blockLimit}\n` + - ` * Gas sent: ${data.estimate}\n` + - ` * Try:\n` + - ` + Setting a higher gas estimate multiplier for this contract\n` + - ` + Using the solc optimizer settings in 'truffle.js'\n` + - ` + Making your contract smaller\n` + - ` + Making your contract constructor more efficient\n` + - ` + Setting a higher network block limit if you are on a\n` + - ` private network or test client (like ganache).\n`, - - oogNoGas: () => - `${prefix} "${data.contract.contractName}" ran out of gas. Something in the constructor ` + - `(ex: infinite loop) caused gas estimation to fail. Try:\n` + - ` * Making your contract constructor more efficient\n` + - ` * Setting the gas manually in your config or as a deployment parameter\n` + - ` * Using the solc optimizer settings in 'truffle.js'\n` + - ` * Setting a higher network block limit if you are on a\n` + - ` private network or test client (like ganache).\n`, - - rvtReason: () => - `${prefix} "${data.contract.contractName}" hit a require or revert statement ` + - `with the following reason given:\n` + - ` * ${data.reason}\n`, - - rvtNoReason: () => - `${prefix} "${data.contract.contractName}" hit a require or revert statement ` + - `somewhere in its constructor. Try:\n` + - ` * Verifying that your constructor params satisfy all require conditions.\n` + - ` * Adding reason strings to your require statements.\n`, - - asrtNoReason: () => - `${prefix} "${data.contract.contractName}" hit an invalid opcode while deploying. Try:\n` + - ` * Verifying that your constructor params satisfy all assert conditions.\n` + - ` * Verifying your constructor code doesn't access an array out of bounds.\n` + - ` * Adding reason strings to your assert statements.\n`, - - noMoney: () => - `${prefix} "${data.contract.contractName}" could not deploy due to insufficient funds\n` + - ` * Account: ${data.from}\n` + - ` * Balance: ${data.balance} wei\n` + - ` * Message: ${data.error.message}\n` + - ` * Try:\n` + - ` + Using an adequately funded account\n` + - ` + If you are using a local Geth node, verify that your node is synced.\n`, - - blockWithGas: () => - `${prefix} "${data.contract.contractName}" exceeded the block limit ` + - `(with a gas value you set).\n` + - ` * Block limit: ${data.blockLimit}\n` + - ` * Gas sent: ${data.gas}\n` + - ` * Try:\n` + - ` + Sending less gas.\n` + - ` + Setting a higher network block limit if you are on a\n` + - ` private network or test client (like ganache).\n`, - - blockNoGas: () => - `${prefix} "${data.contract.contractName}" exceeded the block limit ` + - `(using Truffle's estimate).\n` + - ` * Block limit: ${data.blockLimit}\n` + - ` * Report this error in the Truffle issues on Github. It should not happen.\n` + - ` * Try: setting gas manually in 'truffle.js' or as parameter to 'deployer.deploy'\n`, - - nonce: () => - `${prefix} "${data.contract.contractName}" received: ${data.error.message}.\n` + - ` * This error is common when Infura is under heavy network load.\n` + - ` * Try: setting the 'confirmations' key in your network config\n` + - ` to wait for several block confirmations between each deployment.\n`, - - geth: () => - `${prefix} "${data.contract.contractName}" received a generic error from Geth that\n` + - `can be caused by hitting revert in a contract constructor or running out of gas.\n` + - ` * ${data.estimateError.message}.\n` + - ` * Try: + using the '--dry-run' option to reproduce this failure with clearer errors.\n` + - ` + verifying that your gas is adequate for this deployment.\n`, - - default: () => - `${prefix} "${data.contract.contractName}" -- ${data.error.message}.\n`, - - // ------------------------------------ Successes -------------------------------------------- - - deploying: () => - this.underline(`Deploying '${data.contract.contractName}'`), - - replacing: () => - this.underline(`Replacing '${data.contract.contractName}'`), - - reusing: () => - this.underline(`Re-using deployed '${data.contract.contractName}'`) + '\n' + - ` > ${'contract address:'.padEnd(20)} ${data.contract.address}\n`, - - many: () => - this.underline(`Deploying Batch`), - - linking: () => { - let output = this.underline(`Linking`) + - `\n * Contract: ${data.contractName} <--> Library: ${data.libraryName} `; - - if(!self.migration.dryRun) - output +=`(at address: ${data.libraryAddress})`; - - return output; - }, - - preMigrate: () => - this.doubleline(`${data.file}`), - - saving: () => { - return (!self.migration.dryRun) - ? `\n * Saving migration` - : ''; - }, - - firstMigrate: () => { - let output; - (self.migration.dryRun) - ? output = this.doubleline(`Migrations dry-run (simulation)`) + '\n' - : output = this.doubleline(`Starting migrations...`) + '\n'; - - output += - `> Network name: '${data.network}'\n` + - `> Network id: ${data.networkId}\n`; - - return output; - }, - - postMigrate: () => { - let output = ''; - - if (!self.migration.dryRun) - output += ` * Saving artifacts\n`; - - output += this.underline(37) + '\n' + - ` > ${'Total cost:'.padEnd(15)} ${data.cost.padStart(15)} ETH\n`; - - return output; - }, - - lastMigrate: () => - this.doubleline('Summary') + '\n' + - `> ${'Total deployments:'.padEnd(20)} ${data.totalDeployments}\n` + - `> ${'Final cost:'.padEnd(20)} ${data.finalCost} ETH\n`, - - deployed: () => { - - if(this.blockSpinner){ - this.blockSpinner.stop(); - const stopText = ` > ${this.currentBlockWait}`; - this.deployer.logger.log(stopText); - } - - let output = ''; - - if(!self.migration.dryRun) output += - ` > ${'contract address:'.padEnd(20)} ${data.receipt.contractAddress}\n`; - - output += - ` > ${'account:'.padEnd(20)} ${data.from}\n` + - ` > ${'balance:'.padEnd(20)} ${data.balance}\n` + - ` > ${'gas used:'.padEnd(20)} ${data.gas}\n` + - ` > ${'gas price:'.padEnd(20)} ${data.gasPrice} gwei\n` + - ` > ${'value sent:'.padEnd(20)} ${data.value} ETH\n` + - ` > ${'total cost:'.padEnd(20)} ${data.cost} ETH\n`; - - if (self.confirmations !== 0) output += - this.underline(`Pausing for ${self.confirmations} confirmations...`); - - return output; - }, - - listMany: () => - ` * ${data.contractName}`, - - hash: () => { - return (!self.migration.dryRun) - ? ` > ${'transaction hash:'.padEnd(20)} ` + data.transactionHash - : '' - }, - - receipt: () => - ` > ${'gas usage:'.padEnd(20)} ` + data.gas, - - confirmation: () => - ` > ${'confirmation number:'.padEnd(20)} ` + `${data.num} (block: ${data.block})`, - } - - return kinds[kind](); + async postDeployMany(){ + this.deployingMany = false; } - async processDeploymentError(data){ - let message; - const error = data.estimateError || data.error; - - data.reason = (data.error) ? data.error.reason : null; - - const errors = { - OOG: error.message.includes('out of gas') || (data.gas === data.blockLimit), - INT: error.message.includes('base fee') || error.message.includes('intrinsic'), - RVT: error.message.includes('revert'), - ETH: error.message.includes('funds'), - BLK: error.message.includes('block gas limit'), - NCE: error.message.includes('nonce'), - INV: error.message.includes('invalid opcode'), - GTH: error.message.includes('always failing transaction') - } - - let type = Object.keys(errors).find(key => errors[key]); - - switch (type) { - case 'INT': - (data.gas) - ? message = this.messages('intWithGas', data) - : message = this.messages('intNoGas', data); - - this.deployer.logger.error(message); - break; - - case 'OOG': - (data.gas && !(data.gas === data.blockLimit)) - ? message = this.messages('intWithGas', data) - : message = this.messages('oogNoGas', data); - - this.deployer.logger.error(message); - break; - - case 'RVT': - (data.reason) - ? message = this.messages('rvtReason', data) - : message = this.messages('rvtNoReason', data); - - this.deployer.logger.error(message); - break; - - case 'INV': - (data.reason) - ? message = this.messages('asrtReason', data) - : message = this.messages('asrtNoReason', data); - - this.deployer.logger.error(message); - break; - - case 'BLK': - (data.gas) - ? message = this.messages('blockWithGas', data) - : message = this.messages('blockNoGas', data) - - this.deployer.logger.error(message); - break; - - case 'ETH': - const balance = await data.contract.web3.eth.getBalance(data.from); - data.balance = balance.toString(); - message = this.messages('noMoney', data); - this.deployer.logger.error(message); - break; - - case 'NCE': - message = this.messages('nonce', data); - this.deployer.logger.error(message); - break; - - case 'GTH': - message = this.messages('geth', data); - this.deployer.logger.error(message); - break; - - default: - message = this.messages('default', data); - this.deployer.logger.error(message); - } - } } -module.exports = Reporter; \ No newline at end of file +module.exports = Reporter; + From 314eea72201a31211c128e146547dba7ed16aa93 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sun, 1 Jul 2018 09:59:11 -0700 Subject: [PATCH 48/74] Separate migrate and migration classes --- packages/truffle-migrate/index.js | 190 +------------------------- packages/truffle-migrate/migration.js | 187 +++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 184 deletions(-) create mode 100644 packages/truffle-migrate/migration.js diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 1dffa55d8f8..82e64c38261 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -1,194 +1,16 @@ -const fs = require("fs"); const dir = require("node-dir"); const path = require("path"); - -const Emittery = require('emittery'); const async = require("async"); -const Web3 = require("web3"); - const expect = require("truffle-expect"); -const Deployer = require("truffle-deployer"); -const Require = require("truffle-require"); - -const ResolverIntercept = require("./resolverintercept"); -const Reporter = require("./reporter/reporter"); - const util = require('util'); -class Migration { - - constructor(file, reporter, options){ - this.file = path.resolve(file); - this.reporter = reporter; - this.number = parseInt(path.basename(file)); - this.emitter = new Emittery(); - this.isFirst = false; - this.isLast = false; - this.dryRun = options.dryRun; - this.interactive = options.interactive; - this.options = options || {}; - } - - // ------------------------------------- Private ------------------------------------------------- - /** - * Loads & validates migration, then runs it. - * @param {Object} options config and command-line - * @param {Object} context web3 - * @param {Object} deployer truffle module - * @param {Object} resolver truffle module - * @param {Function} callback - */ - async _load(options, context, deployer, resolver, callback){ - const self = this; - - // Load assets and run `execute` - try { - const accounts = await context.web3.eth.getAccounts(); - const requireOptions = { - file: self.file, - context: context, - resolver: resolver, - args: [deployer], - } - - Require.file(requireOptions, async (err, fn) => { - if (err) return callback(err); - - const unRunnable = !fn || !fn.length || fn.length == 0; - - if (unRunnable){ - const msg = `Migration ${self.file} invalid or does not take any parameters`; - return callback(new Error(msg)); - } - - // `migrateFn` might be sync or async. We negotiate that difference in - // `execute` through the deployer API. - const migrateFn = fn(deployer, options.network, accounts); - await self._deploy(options, deployer, resolver, migrateFn, callback); - }); - - } catch(err){ - callback(err) - } - } - - /** - * Initiates deployer sequence, then manages migrations info - * publication to chain / artifact saving. - * @param {Object} options config and command-line - * @param {Object} deployer truffle module - * @param {Object} resolver truffle module - * @param {[type]} migrateFn module.exports of a migrations.js - */ - async _deploy(options, deployer, resolver, migrateFn, callback) { - const self = this; - - try { - await deployer.start(); - - // Allow migrations method to be async and - // deploy to use await - if (migrateFn && migrateFn.then !== undefined){ - await deployer.then(() => migrateFn); - } - - // Migrate without saving - if (options.save === false) return; - - // Write migrations record to chain - const Migrations = resolver.require("./Migrations.sol"); - - if (Migrations && Migrations.isDeployed()) { - await self.emitter.emit('saveMigration', self.number); - const migrations = await Migrations.deployed(); - await migrations.setCompleted(self.number); - } - - await self.emitter.emit('postMigrate', self.isLast); - - // Save artifacts to local filesystem - await options.artifactor.saveAll(resolver.contracts()); - - deployer.finish(); - - // Cleanup - if (self.isLast){ - self.emitter.clearListeners(); - - // Exiting w provider-engine appears to be hopeless. This hack on - // our fork just swallows errors from eth-block-tracking - // as we unwind the handlers downstream from here. - if (self.options.provider && self.options.provider.engine){ - self.options.provider.engine.silent = true; - } - } - // Prevent errors thrown in the callback from triggering the below catch() - process.nextTick(callback); - } catch(e) { - const payload = { - type: 'migrateErr', - error: e - }; - - await self.emitter.emit('error', payload); - deployer.finish(); - callback(e); - }; - } - - // ------------------------------------- Public ------------------------------------------------- - /** - * Instantiates a deployer, connects this migration and its deployer to the reporter - * and launches a migration file's deployment sequence - * @param {Object} options config and command-line - * @param {Function} callback - */ - async run(options, callback) { - const self = this; - const logger = options.logger; - const resolver = new ResolverIntercept(options.resolver); - const web3 = new Web3(); - web3.setProvider(options.provider); - - // Initial context. - const context = { - web3: web3 - }; - - // Instantiate a Deployer - const deployer = new Deployer({ - logger: logger, - confirmations: options.confirmations, - network: options.network, - network_id: options.network_id, - provider: options.provider, - basePath: path.dirname(self.file) - }); - - // Connect reporter to this migration - if (self.reporter){ - self.reporter.setMigration(self); - self.reporter.setDeployer(deployer); - self.reporter.confirmations = options.confirmations || 0; - self.reporter.listen(); - } - - // Get file path and emit pre-migration event - const file = path.relative(options.migrations_directory, self.file) - - const preMigrationsData = { - file: file, - isFirst: self.isFirst, - network: options.network, - networkId: options.network_id, - } - - await self.emitter.emit('preMigrate', preMigrationsData); - await self._load(options, context, deployer, resolver, callback); - } -} - +const Reporter = require("./reporter/reporter"); +const Migration = require('./migration.js'); +/** + * This API is consumed by `truffle-core` at the `migrate` and `test` commands via + * the `.runMigrations` method. + */ const Migrate = { Migration: Migration, reporter: null, diff --git a/packages/truffle-migrate/migration.js b/packages/truffle-migrate/migration.js new file mode 100644 index 00000000000..d5207354fd4 --- /dev/null +++ b/packages/truffle-migrate/migration.js @@ -0,0 +1,187 @@ +const dir = require("node-dir"); +const path = require("path"); +const Deployer = require("truffle-deployer"); +const Require = require("truffle-require"); +const Emittery = require('emittery'); +const async = require("async"); +const Web3 = require("web3"); + +const ResolverIntercept = require("./resolverintercept"); + +const util = require('util'); + +class Migration { + + constructor(file, reporter, options){ + this.file = path.resolve(file); + this.reporter = reporter; + this.number = parseInt(path.basename(file)); + this.emitter = new Emittery(); + this.isFirst = false; + this.isLast = false; + this.dryRun = options.dryRun; + this.interactive = options.interactive; + this.options = options || {}; + } + + // ------------------------------------- Private ------------------------------------------------- + /** + * Loads & validates migration, then runs it. + * @param {Object} options config and command-line + * @param {Object} context web3 + * @param {Object} deployer truffle module + * @param {Object} resolver truffle module + * @param {Function} callback + */ + async _load(options, context, deployer, resolver, callback){ + const self = this; + + // Load assets and run `execute` + try { + const accounts = await context.web3.eth.getAccounts(); + const requireOptions = { + file: self.file, + context: context, + resolver: resolver, + args: [deployer], + } + + Require.file(requireOptions, async (err, fn) => { + if (err) return callback(err); + + const unRunnable = !fn || !fn.length || fn.length == 0; + + if (unRunnable){ + const msg = `Migration ${self.file} invalid or does not take any parameters`; + return callback(new Error(msg)); + } + + // `migrateFn` might be sync or async. We negotiate that difference in + // `execute` through the deployer API. + const migrateFn = fn(deployer, options.network, accounts); + await self._deploy(options, deployer, resolver, migrateFn, callback); + }); + + } catch(err){ + callback(err) + } + } + + /** + * Initiates deployer sequence, then manages migrations info + * publication to chain / artifact saving. + * @param {Object} options config and command-line + * @param {Object} deployer truffle module + * @param {Object} resolver truffle module + * @param {[type]} migrateFn module.exports of a migrations.js + */ + async _deploy(options, deployer, resolver, migrateFn, callback) { + const self = this; + + try { + await deployer.start(); + + // Allow migrations method to be async and + // deploy to use await + if (migrateFn && migrateFn.then !== undefined){ + await deployer.then(() => migrateFn); + } + + // Migrate without saving + if (options.save === false) return; + + // Write migrations record to chain + const Migrations = resolver.require("./Migrations.sol"); + + if (Migrations && Migrations.isDeployed()) { + await self.emitter.emit('saveMigration', self.number); + const migrations = await Migrations.deployed(); + await migrations.setCompleted(self.number); + } + + await self.emitter.emit('postMigrate', self.isLast); + + // Save artifacts to local filesystem + await options.artifactor.saveAll(resolver.contracts()); + + deployer.finish(); + + // Cleanup + if (self.isLast){ + self.emitter.clearListeners(); + + // Exiting w provider-engine appears to be hopeless. This hack on + // our fork just swallows errors from eth-block-tracking + // as we unwind the handlers downstream from here. + if (self.options.provider && self.options.provider.engine){ + self.options.provider.engine.silent = true; + } + } + // Prevent errors thrown in the callback from triggering the below catch() + process.nextTick(callback); + } catch(e) { + const payload = { + type: 'migrateErr', + error: e + }; + + await self.emitter.emit('error', payload); + deployer.finish(); + callback(e); + }; + } + + // ------------------------------------- Public ------------------------------------------------- + /** + * Instantiates a deployer, connects this migration and its deployer to the reporter + * and launches a migration file's deployment sequence + * @param {Object} options config and command-line + * @param {Function} callback + */ + async run(options, callback) { + const self = this; + const logger = options.logger; + const resolver = new ResolverIntercept(options.resolver); + const web3 = new Web3(); + web3.setProvider(options.provider); + + // Initial context. + const context = { + web3: web3 + }; + + // Instantiate a Deployer + const deployer = new Deployer({ + logger: logger, + confirmations: options.confirmations, + network: options.network, + network_id: options.network_id, + provider: options.provider, + basePath: path.dirname(self.file) + }); + + // Connect reporter to this migration + if (self.reporter){ + self.reporter.setMigration(self); + self.reporter.setDeployer(deployer); + self.reporter.confirmations = options.confirmations || 0; + self.reporter.listen(); + } + + // Get file path and emit pre-migration event + const file = path.relative(options.migrations_directory, self.file) + + const preMigrationsData = { + file: file, + isFirst: self.isFirst, + network: options.network, + networkId: options.network_id, + } + + await self.emitter.emit('preMigrate', preMigrationsData); + await self._load(options, context, deployer, resolver, callback); + } +} + +module.exports = Migration; + From 79e9d541c31a1acb72d588ad240a726f31b12999 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sun, 1 Jul 2018 13:53:09 -0700 Subject: [PATCH 49/74] Support timeoutBlocks by network w/ block-polling --- packages/truffle-config/index.js | 13 ++++ packages/truffle-contract/lib/override.js | 38 +++++++--- .../truffle-contract/lib/subscriptions.js | 5 ++ packages/truffle-contract/test/util.js | 4 + packages/truffle-deployer/index.js | 2 +- packages/truffle-deployer/src/deployment.js | 74 +++++++++---------- packages/truffle-migrate/migration.js | 1 + .../sources/migrations/production/truffle.js | 3 +- 8 files changed, 90 insertions(+), 50 deletions(-) diff --git a/packages/truffle-config/index.js b/packages/truffle-config/index.js index 15bf1aad6ca..dc7328160c4 100644 --- a/packages/truffle-config/index.js +++ b/packages/truffle-config/index.js @@ -29,6 +29,7 @@ function Config(truffle_directory, working_directory, network) { gasPrice: null, from: null, confirmations: 0, + timeoutBlocks: 0, production: false, build: null, resolver: null, @@ -194,6 +195,18 @@ function Config(truffle_directory, working_directory, network) { throw new Error("Don't set config.production directly. Instead, set config.networks and then config.networks[].production") } }, + timeoutBlocks: { + get: function(){ + try { + return self.network_config.timeoutBlocks; + } catch (e) { + return 0; + } + }, + set: function(val) { + throw new Error("Don't set config.timeoutBlocks directly. Instead, set config.networks and then config.networks[].timeoutBlocks") + } + } }; Object.keys(props).forEach(function(prop) { diff --git a/packages/truffle-contract/lib/override.js b/packages/truffle-contract/lib/override.js index e80d4ab22cf..626d8427514 100644 --- a/packages/truffle-contract/lib/override.js +++ b/packages/truffle-contract/lib/override.js @@ -2,8 +2,9 @@ var subscriptions = require("./subscriptions"); var override = { - timeoutMessage: '50 blocks', // Substring of timeout err fired by web3 - defaultMaxBlocks: 50, // Max # of blocks web3 will wait for a tx + timeoutMessage: 'not mined within', // Substring of timeout err fired by web3 + defaultMaxBlocks: 50, // Max # of blocks web3 will wait for a tx + pollingInterval: 1000, /** * Fired after web3 ceases to support subscriptions if user has specified @@ -14,7 +15,7 @@ var override = { * @param {Object} context execution state * @param {Object} err error */ - start: function(context, web3Error){ + start: async function(context, web3Error){ var constructor = this; var blockNumber = null; var currentBlock = override.defaultMaxBlocks; @@ -27,14 +28,12 @@ var override = { if (!timedOut || !shouldWait) return context.promiEvent.reject(web3Error); // This will run every block from now until contract.timeoutBlocks - var listener = function(err, data){ + var listener = function(pollID){ var self = this; currentBlock++; if (currentBlock > constructor.timeoutBlocks){ - subscriptions.unsubscribe(constructor, id); - self.removeListener('data', listener); - context.promiEvent.reject(err); + clearInterval(pollID) return; } @@ -42,8 +41,6 @@ var override = { .then(result => { if (!result) return; - //self.removeListener('data', listener); - (result.contractAddress) ? constructor .at(result.contractAddress) @@ -52,16 +49,35 @@ var override = { : constructor.promiEvent.resolve(result); - }).catch(err => { - //self.removeListener('data', listener); + }) + .catch(err => { + clearInterval(pollID) context.promiEvent.reject(err); }); }; + // Start polling + let currentPollingBlock = await constructor.web3.eth.getBlockNumber(); + + const pollID = setInterval(async() => { + const newBlock = await constructor.web3.eth.getBlockNumber(); + + if(newBlock > currentPollingBlock){ + currentPollingBlock = newBlock; + listener(pollID); + } + }, override.pollingInterval); + + + // Temporarily disabled new_head subscription. There are bugs at Web3 and at + // Geth that impair the reliablility of this sub atm. + + /* var id = new Date().getTime(); subscriptions.subscribe(constructor, 'newHeads', id) .then(result => constructor.web3.currentProvider.on('data', listener)) .catch(context.promiEvent.reject); + */ }, } diff --git a/packages/truffle-contract/lib/subscriptions.js b/packages/truffle-contract/lib/subscriptions.js index b48414b2400..6890793f73a 100644 --- a/packages/truffle-contract/lib/subscriptions.js +++ b/packages/truffle-contract/lib/subscriptions.js @@ -1,3 +1,8 @@ +/** + * This code is temporarily unused pending resolution of bugs at Web3 & Geth that make + * subscriptions for the block head unreliable. + */ + module.exports = { /** * @param {String} e.g. "newHeads" diff --git a/packages/truffle-contract/test/util.js b/packages/truffle-contract/test/util.js index 164d6f041f5..ed85360654c 100644 --- a/packages/truffle-contract/test/util.js +++ b/packages/truffle-contract/test/util.js @@ -115,6 +115,10 @@ var util = { if (transactionHash === util.realHash) return Promise.resolve(util.realReceipt) }, + + waitMS: async function(ms){ + return new Promise(resolve => setTimeout(resolve, ms)); + } } module.exports = util; diff --git a/packages/truffle-deployer/index.js b/packages/truffle-deployer/index.js index c6c3d5e0d32..65761599a4c 100644 --- a/packages/truffle-deployer/index.js +++ b/packages/truffle-deployer/index.js @@ -16,7 +16,7 @@ class Deployer extends Deployment { ]); const emitter = new Emittery(); - super(emitter, options.confirmations); + super(emitter, options); this.emitter = emitter; this.chain = new DeferredChain(); diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 18cafe6b40d..0c084a6ed9a 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -9,8 +9,9 @@ class Deployment { * @param {Object} emitter async `Emittery` emitter * @param {Number} confirmations confirmations needed to resolve an instance */ - constructor(emitter, confirmations){ - this.confirmations = confirmations || 0; + constructor(emitter, options){ + this.confirmations = options.confirmations || 0; + this.timeoutBlocks = options.timeoutBlocks || 0; this.emitter = emitter; this.promiEventEmitters = []; this.confirmationsMap = {}; @@ -29,10 +30,6 @@ class Deployment { return `Migrations failure` } - async _waitMS(ms){ - return new Promise(resolve => setTimeout(() => resolve(), ms)) - } - /** * Helper to parse a deploy statement's overwrite option * @param {Arry} args arguments passed to deploy @@ -69,32 +66,6 @@ class Deployment { return value; } - /** - * NB: This should work but there are outstanding issues at both - * geth (with websockets) & web3 (with confirmation handling over RPC) that - * prevent it from being reliable. We're using very simple block polling instead. - * (See also _confirmationCb ) - * - * Queries the confirmations mapping periodically to see if we have - * heard enough confirmations for a given tx to allow `deploy` to complete. - * Resolves when this is true. - * @param {String} hash contract creation tx hash - * @return {Promise} - */ - async _waitForConfirmations(hash){ - let interval; - const self = this; - - return new Promise(accept => { - interval = setInterval(() => { - if (self.confirmationsMap[hash] >= self.confirmations){ - clearInterval(interval); - accept(); - } - }, self.pollingInterval); - }) - } - /** * Emits a `block` event on each new block heard. This polling is * meant to be cancelled immediately on resolution of the @@ -230,12 +201,33 @@ class Deployment { this.removeListener('receipt', parent._receiptCb); } + // ----------------- Confirmations Handling (temporarily disabled) ------------------------------- /** - * NB: This should work but there are outstanding issues at both - * geth (with websockets) & web3 (with confirmation handling over RPC) that - * prevent it from being reliable. We're using very simple block polling instead. - * (See also _waitForConfirmations ) + * There are outstanding issues at both geth (with websockets) & web3 (with confirmation handling + * over RPC) that impair the confirmations handlers' reliability. In the interim we're using + * simple block polling instead. (See also _confirmationCb ) * + * Queries the confirmations mapping periodically to see if we have + * heard enough confirmations for a given tx to allow `deploy` to complete. + * Resolves when this is true. + * @param {String} hash contract creation tx hash + * @return {Promise} + */ + async _waitForConfirmations(hash){ + let interval; + const self = this; + + return new Promise(accept => { + interval = setInterval(() => { + if (self.confirmationsMap[hash] >= self.confirmations){ + clearInterval(interval); + accept(); + } + }, self.pollingInterval); + }) + } + + /** * Handler for contract's `confirmation` event. Rebroadcasts as a deployer event * and maintains a table of txHashes & their current confirmation number. This * table gets polled if the user needs to wait a few blocks before getting @@ -286,6 +278,13 @@ class Deployment { // Case: deploy: if (shouldDeploy) { + /* + Set timeout override. If this value is zero, + truffle-contract will defer to web3's defaults: + - 50 blocks (websockets) OR 50 * 15sec (http) + */ + contract.timeoutBlocks = self.timeoutBlocks; + eventArgs = { state: state, contract: contract, @@ -296,7 +295,8 @@ class Deployment { from: self._extractFromArgs(newArgs, 'from') || contract.defaults().from, } - // Detect constructor revert by running estimateGas + // Get an estimate for previews / detect constructor revert + // NB: web3 does not strip the revert msg here like it does for `deploy` try { eventArgs.estimate = await contract.new.estimateGas.apply(contract, newArgs); } catch(err){ diff --git a/packages/truffle-migrate/migration.js b/packages/truffle-migrate/migration.js index d5207354fd4..e4df73dccb3 100644 --- a/packages/truffle-migrate/migration.js +++ b/packages/truffle-migrate/migration.js @@ -154,6 +154,7 @@ class Migration { const deployer = new Deployer({ logger: logger, confirmations: options.confirmations, + timeoutBlocks: options.timeoutBlocks, network: options.network, network_id: options.network_id, provider: options.provider, diff --git a/packages/truffle/test/sources/migrations/production/truffle.js b/packages/truffle/test/sources/migrations/production/truffle.js index a701130a01f..c28c64f030a 100644 --- a/packages/truffle/test/sources/migrations/production/truffle.js +++ b/packages/truffle/test/sources/migrations/production/truffle.js @@ -7,7 +7,8 @@ module.exports = { gas: 4700000, gasPrice: 20000000000, confirmations: 2, - production: true + production: true, + timeoutBlocks: 70, }, }, }; From 44370b528194aa08393ff8d3d9f1418ba700a68f Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sun, 1 Jul 2018 17:28:23 -0700 Subject: [PATCH 50/74] Add missing new-lines --- packages/truffle-deployer/src/deferredchain.js | 3 ++- packages/truffle-deployer/test/await.js | 3 ++- packages/truffle-deployer/test/helpers/reporter.js | 3 ++- packages/truffle-deployer/test/sources/Abstract.sol | 3 ++- packages/truffle-deployer/test/sources/ExampleAssert.sol | 3 ++- packages/truffle-deployer/test/sources/IsLibrary.sol | 3 ++- packages/truffle-deployer/test/sources/Loop.sol | 3 ++- packages/truffle-migrate/reporter/indentedSpinner.js | 3 ++- packages/truffle/test/scenarios/migrations/dryrun.js | 3 ++- packages/truffle/test/scenarios/migrations/migrate.js | 3 ++- packages/truffle/test/scenarios/migrations/production.js | 2 +- .../test/sources/migrations/error/contracts/Abstract.sol | 3 ++- .../test/sources/migrations/error/contracts/ExampleAssert.sol | 2 +- .../truffle/test/sources/migrations/error/contracts/Loops.sol | 2 +- .../sources/migrations/error/contracts/RevertWithReason.sol | 2 +- .../sources/migrations/error/migrations/2_migrations_revert.js | 2 +- .../sources/migrations/error/migrations/4_migrations_oog.js | 2 +- .../sources/migrations/error/migrations/6_migrations_funds.js | 2 +- .../test/sources/migrations/success/contracts/IsLibrary.sol | 2 +- .../truffle/test/sources/migrations/success/contracts/Loop.sol | 2 +- .../sources/migrations/success/contracts/PayableExample.sol | 2 +- .../sources/migrations/success/migrations/2_migrations_sync.js | 2 +- scripts/ci.sh | 2 +- scripts/geth.sh | 2 +- 24 files changed, 35 insertions(+), 24 deletions(-) diff --git a/packages/truffle-deployer/src/deferredchain.js b/packages/truffle-deployer/src/deferredchain.js index a6a0af83f9b..09e182a5acd 100644 --- a/packages/truffle-deployer/src/deferredchain.js +++ b/packages/truffle-deployer/src/deferredchain.js @@ -44,4 +44,5 @@ DeferredChain.prototype.start = function() { return this.await; }; -module.exports = DeferredChain; \ No newline at end of file +module.exports = DeferredChain; + diff --git a/packages/truffle-deployer/test/await.js b/packages/truffle-deployer/test/await.js index 443a26352bf..9aa44cbd7b0 100644 --- a/packages/truffle-deployer/test/await.js +++ b/packages/truffle-deployer/test/await.js @@ -106,4 +106,5 @@ describe("Deployer (async / await)", function() { assert(events[0].args.eventID === '5'); assert(events[1].args.eventID === '7'); }); -}); \ No newline at end of file +}); + diff --git a/packages/truffle-deployer/test/helpers/reporter.js b/packages/truffle-deployer/test/helpers/reporter.js index 510074cd48f..ddfc65b570a 100644 --- a/packages/truffle-deployer/test/helpers/reporter.js +++ b/packages/truffle-deployer/test/helpers/reporter.js @@ -288,4 +288,5 @@ class Reporter { } } -module.exports = Reporter; \ No newline at end of file +module.exports = Reporter; + diff --git a/packages/truffle-deployer/test/sources/Abstract.sol b/packages/truffle-deployer/test/sources/Abstract.sol index 1a4b29d7511..bb134a1ebc9 100644 --- a/packages/truffle-deployer/test/sources/Abstract.sol +++ b/packages/truffle-deployer/test/sources/Abstract.sol @@ -2,4 +2,5 @@ pragma solidity ^0.4.4; contract Abstract { function method() public; -} \ No newline at end of file +} + diff --git a/packages/truffle-deployer/test/sources/ExampleAssert.sol b/packages/truffle-deployer/test/sources/ExampleAssert.sol index af4b360117b..c474e959ee2 100644 --- a/packages/truffle-deployer/test/sources/ExampleAssert.sol +++ b/packages/truffle-deployer/test/sources/ExampleAssert.sol @@ -5,4 +5,5 @@ contract ExampleAssert { constructor() public { assert(false); } -} \ No newline at end of file +} + diff --git a/packages/truffle-deployer/test/sources/IsLibrary.sol b/packages/truffle-deployer/test/sources/IsLibrary.sol index b7ed3c78d8f..c4f4998907d 100644 --- a/packages/truffle-deployer/test/sources/IsLibrary.sol +++ b/packages/truffle-deployer/test/sources/IsLibrary.sol @@ -7,4 +7,5 @@ library IsLibrary { function fireIsLibraryEvent(uint _id) public { emit IsLibraryEvent(_id); } -} \ No newline at end of file +} + diff --git a/packages/truffle-deployer/test/sources/Loop.sol b/packages/truffle-deployer/test/sources/Loop.sol index d995a3c77f8..f7fec8a08ea 100644 --- a/packages/truffle-deployer/test/sources/Loop.sol +++ b/packages/truffle-deployer/test/sources/Loop.sol @@ -8,4 +8,5 @@ contract Loops { id = i; } } -} \ No newline at end of file +} + diff --git a/packages/truffle-migrate/reporter/indentedSpinner.js b/packages/truffle-migrate/reporter/indentedSpinner.js index f00ee33224e..31132f78403 100644 --- a/packages/truffle-migrate/reporter/indentedSpinner.js +++ b/packages/truffle-migrate/reporter/indentedSpinner.js @@ -12,4 +12,5 @@ module.exports = { " ⠇", " ⠏" ] -} \ No newline at end of file +} + diff --git a/packages/truffle/test/scenarios/migrations/dryrun.js b/packages/truffle/test/scenarios/migrations/dryrun.js index f43b7bdc0c4..24377056421 100644 --- a/packages/truffle/test/scenarios/migrations/dryrun.js +++ b/packages/truffle/test/scenarios/migrations/dryrun.js @@ -68,4 +68,5 @@ describe("migrate (dry-run)", function() { done(); }) }); -}); \ No newline at end of file +}); + diff --git a/packages/truffle/test/scenarios/migrations/migrate.js b/packages/truffle/test/scenarios/migrations/migrate.js index 3d2d306052b..23f70a0aa51 100644 --- a/packages/truffle/test/scenarios/migrations/migrate.js +++ b/packages/truffle/test/scenarios/migrations/migrate.js @@ -84,4 +84,5 @@ describe("migrate (success)", function() { done(); }) }); -}); \ No newline at end of file +}); + diff --git a/packages/truffle/test/scenarios/migrations/production.js b/packages/truffle/test/scenarios/migrations/production.js index ecfbcf4f654..1937852c2a0 100644 --- a/packages/truffle/test/scenarios/migrations/production.js +++ b/packages/truffle/test/scenarios/migrations/production.js @@ -70,4 +70,4 @@ describe("production migrations [ @geth ]", function() { done(); }) }); -}); \ No newline at end of file +}); diff --git a/packages/truffle/test/sources/migrations/error/contracts/Abstract.sol b/packages/truffle/test/sources/migrations/error/contracts/Abstract.sol index 1a4b29d7511..bb134a1ebc9 100644 --- a/packages/truffle/test/sources/migrations/error/contracts/Abstract.sol +++ b/packages/truffle/test/sources/migrations/error/contracts/Abstract.sol @@ -2,4 +2,5 @@ pragma solidity ^0.4.4; contract Abstract { function method() public; -} \ No newline at end of file +} + diff --git a/packages/truffle/test/sources/migrations/error/contracts/ExampleAssert.sol b/packages/truffle/test/sources/migrations/error/contracts/ExampleAssert.sol index af4b360117b..295f7d1fa73 100644 --- a/packages/truffle/test/sources/migrations/error/contracts/ExampleAssert.sol +++ b/packages/truffle/test/sources/migrations/error/contracts/ExampleAssert.sol @@ -5,4 +5,4 @@ contract ExampleAssert { constructor() public { assert(false); } -} \ No newline at end of file +} diff --git a/packages/truffle/test/sources/migrations/error/contracts/Loops.sol b/packages/truffle/test/sources/migrations/error/contracts/Loops.sol index d995a3c77f8..cb375476137 100644 --- a/packages/truffle/test/sources/migrations/error/contracts/Loops.sol +++ b/packages/truffle/test/sources/migrations/error/contracts/Loops.sol @@ -8,4 +8,4 @@ contract Loops { id = i; } } -} \ No newline at end of file +} diff --git a/packages/truffle/test/sources/migrations/error/contracts/RevertWithReason.sol b/packages/truffle/test/sources/migrations/error/contracts/RevertWithReason.sol index afae938e0a7..759cd0496ce 100644 --- a/packages/truffle/test/sources/migrations/error/contracts/RevertWithReason.sol +++ b/packages/truffle/test/sources/migrations/error/contracts/RevertWithReason.sol @@ -6,4 +6,4 @@ contract RevertWithReason { constructor() public { require(false, 'reasonstring'); } -} \ No newline at end of file +} diff --git a/packages/truffle/test/sources/migrations/error/migrations/2_migrations_revert.js b/packages/truffle/test/sources/migrations/error/migrations/2_migrations_revert.js index f48df498845..b0c36209694 100644 --- a/packages/truffle/test/sources/migrations/error/migrations/2_migrations_revert.js +++ b/packages/truffle/test/sources/migrations/error/migrations/2_migrations_revert.js @@ -6,4 +6,4 @@ module.exports = async function(deployer, network, accounts) { await deployer.deploy(Example); await deployer.deploy(ExampleRevert); await deployer.deploy(UsesExample, Example.address); -}; \ No newline at end of file +}; diff --git a/packages/truffle/test/sources/migrations/error/migrations/4_migrations_oog.js b/packages/truffle/test/sources/migrations/error/migrations/4_migrations_oog.js index 8495831bbbe..72ed5b26098 100644 --- a/packages/truffle/test/sources/migrations/error/migrations/4_migrations_oog.js +++ b/packages/truffle/test/sources/migrations/error/migrations/4_migrations_oog.js @@ -2,4 +2,4 @@ const Loops = artifacts.require("Loops"); module.exports = async function(deployer, network, accounts) { await deployer.deploy(Loops); -}; \ No newline at end of file +}; diff --git a/packages/truffle/test/sources/migrations/error/migrations/6_migrations_funds.js b/packages/truffle/test/sources/migrations/error/migrations/6_migrations_funds.js index d2f4e31c1f9..bb8d185b8d1 100644 --- a/packages/truffle/test/sources/migrations/error/migrations/6_migrations_funds.js +++ b/packages/truffle/test/sources/migrations/error/migrations/6_migrations_funds.js @@ -12,4 +12,4 @@ module.exports = async function(deployer, network, accounts) { }); await deployer.deploy(Example, {from: emptyAccount}); -}; \ No newline at end of file +}; diff --git a/packages/truffle/test/sources/migrations/success/contracts/IsLibrary.sol b/packages/truffle/test/sources/migrations/success/contracts/IsLibrary.sol index b7ed3c78d8f..e43f7b821b8 100644 --- a/packages/truffle/test/sources/migrations/success/contracts/IsLibrary.sol +++ b/packages/truffle/test/sources/migrations/success/contracts/IsLibrary.sol @@ -7,4 +7,4 @@ library IsLibrary { function fireIsLibraryEvent(uint _id) public { emit IsLibraryEvent(_id); } -} \ No newline at end of file +} diff --git a/packages/truffle/test/sources/migrations/success/contracts/Loop.sol b/packages/truffle/test/sources/migrations/success/contracts/Loop.sol index d995a3c77f8..cb375476137 100644 --- a/packages/truffle/test/sources/migrations/success/contracts/Loop.sol +++ b/packages/truffle/test/sources/migrations/success/contracts/Loop.sol @@ -8,4 +8,4 @@ contract Loops { id = i; } } -} \ No newline at end of file +} diff --git a/packages/truffle/test/sources/migrations/success/contracts/PayableExample.sol b/packages/truffle/test/sources/migrations/success/contracts/PayableExample.sol index 9037154c9df..0d400ea41d6 100644 --- a/packages/truffle/test/sources/migrations/success/contracts/PayableExample.sol +++ b/packages/truffle/test/sources/migrations/success/contracts/PayableExample.sol @@ -4,4 +4,4 @@ pragma solidity ^0.4.4; contract PayableExample { string public id = 'PayableExample'; constructor() public payable {} -} \ No newline at end of file +} diff --git a/packages/truffle/test/sources/migrations/success/migrations/2_migrations_sync.js b/packages/truffle/test/sources/migrations/success/migrations/2_migrations_sync.js index bd81e5072a9..d513907afac 100644 --- a/packages/truffle/test/sources/migrations/success/migrations/2_migrations_sync.js +++ b/packages/truffle/test/sources/migrations/success/migrations/2_migrations_sync.js @@ -5,4 +5,4 @@ module.exports = function(deployer) { deployer .deploy(Example) .then(() => deployer.deploy(UsesExample, Example.address)); -}; \ No newline at end of file +}; diff --git a/scripts/ci.sh b/scripts/ci.sh index a85b4e1aa1f..2d307b5cb35 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -40,4 +40,4 @@ else lerna run --scope truffle-* test --stream --concurrency=1 -fi \ No newline at end of file +fi diff --git a/scripts/geth.sh b/scripts/geth.sh index ca929c34b78..3352a1b2666 100755 --- a/scripts/geth.sh +++ b/scripts/geth.sh @@ -20,4 +20,4 @@ docker run \ --dev \ --dev.period 1 \ --targetgaslimit '7000000' \ - js ./scripts/geth-accounts.js \ No newline at end of file + js ./scripts/geth-accounts.js From 2f70102f4272947c3d93e41e05d5069c0bbf1527 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Sun, 1 Jul 2018 20:24:40 -0700 Subject: [PATCH 51/74] Fix interactivity / block timer (for infura) --- packages/truffle-deployer/src/deployment.js | 24 +++++++++---------- packages/truffle-deployer/test/deployer.js | 6 ++--- packages/truffle-migrate/reporter/reporter.js | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 0c084a6ed9a..de5cd29b247 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -15,7 +15,7 @@ class Deployment { this.emitter = emitter; this.promiEventEmitters = []; this.confirmationsMap = {}; - this.pollingInterval = 1000; + this.pollingInterval = 2000; this.blockPoll; } @@ -82,19 +82,18 @@ class Deployment { self.blockPoll = setInterval(async() => { const newBlock = await web3.eth.getBlockNumber(); - if (newBlock > currentBlock){ - blocksWaited = (newBlock - currentBlock) + blocksWaited; - currentBlock = newBlock; - secondsWaited = Math.floor((new Date().getTime() - startTime) / 1000); + blocksWaited = (newBlock - currentBlock) + blocksWaited; + currentBlock = newBlock; + secondsWaited = Math.floor((new Date().getTime() - startTime) / 1000); - const eventArgs = { - blockNumber: newBlock, - blocksWaited: blocksWaited, - secondsWaited: secondsWaited - }; + const eventArgs = { + blockNumber: newBlock, + blocksWaited: blocksWaited, + secondsWaited: secondsWaited + }; + + await self.emitter.emit('block', eventArgs); - await self.emitter.emit('block', eventArgs); - } }, self.pollingInterval); } @@ -395,6 +394,7 @@ class Deployment { * Cleans up promiEvents' emitter listeners */ _close(){ + this.emitter.clearListeners(); this.promiEventEmitters.forEach(item => { item.removeAllListeners(); }); diff --git a/packages/truffle-deployer/test/deployer.js b/packages/truffle-deployer/test/deployer.js index 2684f78aae6..13c40f71655 100644 --- a/packages/truffle-deployer/test/deployer.js +++ b/packages/truffle-deployer/test/deployer.js @@ -222,7 +222,7 @@ describe("Deployer (sync)", function() { this.timeout(10000); const startBlock = await web3.eth.getBlockNumber(); - utils.startAutoMine(web3, 500); + utils.startAutoMine(web3, 1500); const migrate = function(){ deployer.deploy(IsLibrary); @@ -251,10 +251,10 @@ describe("Deployer (sync)", function() { }); it('emits block events while waiting for a tx to mine', async function(){ - this.timeout(10000); + this.timeout(15000); const startBlock = await web3.eth.getBlockNumber(); - utils.startAutoMine(web3, 2000); + utils.startAutoMine(web3, 1500); const migrate = function(){ deployer.then(async function(){ diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-migrate/reporter/reporter.js index f05603a5160..e38618e5936 100644 --- a/packages/truffle-migrate/reporter/reporter.js +++ b/packages/truffle-migrate/reporter/reporter.js @@ -130,7 +130,7 @@ class Reporter { }; input.close(); - self.migration.logger.log(exitLine); + self.deployer && self.deployer.logger.log(exitLine); resolve(false); }) }); From 4f69fd2c649d8bdf167437147638e4b4454808c3 Mon Sep 17 00:00:00 2001 From: "g. nicholas d'andrea" Date: Mon, 2 Jul 2018 13:28:59 -0400 Subject: [PATCH 52/74] Cleanup after rebase --- packages/truffle-contract/package.json | 1 - packages/truffle-deployer/package.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/truffle-contract/package.json b/packages/truffle-contract/package.json index 1c99df1c130..40b9a6f57a0 100644 --- a/packages/truffle-contract/package.json +++ b/packages/truffle-contract/package.json @@ -28,7 +28,6 @@ "dependencies": { "ethereumjs-util": "^5.2.0", "ethjs-abi": "0.1.8", - "ganache-core": "2.1.2", "truffle-blockchain-utils": "^0.0.5", "truffle-contract-schema": "^2.0.1", "truffle-error": "^0.0.3", diff --git a/packages/truffle-deployer/package.json b/packages/truffle-deployer/package.json index 394e57dfe5a..4392f494ae4 100644 --- a/packages/truffle-deployer/package.json +++ b/packages/truffle-deployer/package.json @@ -25,7 +25,7 @@ "homepage": "https://github.com/trufflesuite/truffle-deployer#readme", "dependencies": { "emittery": "^0.3.0", - "truffle-contract": "git+https://github.com/trufflesuite/truffle-contract.git#8f90e82bb3e22e2da6b493d8edd217fb49d8906c", + "truffle-contract": "^3.0.5", "truffle-expect": "^0.0.4" }, "devDependencies": { From fba366075450323d8379f78673f02fb2042d07eb Mon Sep 17 00:00:00 2001 From: cgewecke Date: Mon, 2 Jul 2018 12:11:29 -0700 Subject: [PATCH 53/74] Replace ora dependency --- packages/truffle-migrate/package.json | 1 + yarn.lock | 234 ++++++++++++++++++-------- 2 files changed, 166 insertions(+), 69 deletions(-) diff --git a/packages/truffle-migrate/package.json b/packages/truffle-migrate/package.json index 9a9950b7982..1f71bc136ed 100644 --- a/packages/truffle-migrate/package.json +++ b/packages/truffle-migrate/package.json @@ -24,6 +24,7 @@ "async": "2.6.1", "emittery": "^0.4.0", "node-dir": "0.1.17", + "ora": "^2.1.0", "truffle-deployer": "^2.0.7", "truffle-expect": "^0.0.4", "truffle-require": "^1.0.7", diff --git a/yarn.lock b/yarn.lock index 5875d5dd558..85dddb61c85 100644 --- a/yarn.lock +++ b/yarn.lock @@ -969,6 +969,12 @@ babylon@6.18.0, babylon@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + dependencies: + precond "0.2" + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -1107,7 +1113,7 @@ bn.js@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-1.3.0.tgz#0db4cbf96f8f23b742f5bcb9d1aa7a9994a05e83" -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.3, bn.js@^4.11.6, bn.js@^4.4.0, bn.js@^4.8.0: +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.3, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.4.0, bn.js@^4.8.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" @@ -1587,7 +1593,7 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.0: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -1747,6 +1753,10 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-spinners@^1.1.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" + cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" @@ -1919,7 +1929,7 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.4.10, concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@^1.6.1: +concat-stream@^1.4.10, concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.5.1, concat-stream@^1.6.0, concat-stream@^1.6.1: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" dependencies: @@ -2209,6 +2219,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +cross-fetch@^2.1.0, cross-fetch@^2.1.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.2.tgz#a47ff4f7fc712daba8f6a695a11c948440d45723" + dependencies: + node-fetch "2.1.2" + whatwg-fetch "2.0.4" + cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -2308,12 +2325,6 @@ dateformat@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" -debug@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" - dependencies: - ms "0.7.2" - debug@2.6.8: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" @@ -3097,6 +3108,46 @@ eth-block-tracker@^2.2.2: pify "^2.3.0" tape "^4.6.3" +eth-block-tracker@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-3.0.1.tgz#95cd5e763c7293e0b1b2790a2a39ac2ac188a5e1" + dependencies: + eth-query "^2.1.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.3" + ethjs-util "^0.1.3" + json-rpc-engine "^3.6.0" + pify "^2.3.0" + tape "^4.6.3" + +eth-json-rpc-infura@^3.1.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eth-json-rpc-infura/-/eth-json-rpc-infura-3.1.2.tgz#04c5d0cee98619e93ba8a9842492b771b316e83a" + dependencies: + cross-fetch "^2.1.1" + eth-json-rpc-middleware "^1.5.0" + json-rpc-engine "^3.4.0" + json-rpc-error "^2.0.0" + tape "^4.8.0" + +eth-json-rpc-middleware@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.6.0.tgz#5c9d4c28f745ccb01630f0300ba945f4bef9593f" + dependencies: + async "^2.5.0" + eth-query "^2.1.2" + eth-tx-summary "^3.1.2" + ethereumjs-block "^1.6.0" + ethereumjs-tx "^1.3.3" + ethereumjs-util "^5.1.2" + ethereumjs-vm "^2.1.0" + fetch-ponyfill "^4.0.0" + json-rpc-engine "^3.6.0" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + tape "^4.6.3" + eth-lib@0.1.27, eth-lib@^0.1.26: version "0.1.27" resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.27.tgz#f0b0fd144f865d2d6bf8257a40004f2e75ca1dd6" @@ -3117,7 +3168,7 @@ eth-lib@0.2.7: elliptic "^6.4.0" xhr-request-promise "^0.1.2" -eth-query@^2.1.0: +eth-query@^2.0.2, eth-query@^2.1.0, eth-query@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" dependencies: @@ -3131,6 +3182,24 @@ eth-sig-util@^1.4.2: ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git" ethereumjs-util "^5.1.1" +eth-tx-summary@^3.1.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/eth-tx-summary/-/eth-tx-summary-3.2.3.tgz#a52d7215616888e012fbc083b3eacd28f3e64764" + dependencies: + async "^2.1.2" + bn.js "^4.11.8" + clone "^2.0.0" + concat-stream "^1.5.1" + end-of-stream "^1.1.0" + eth-query "^2.0.2" + ethereumjs-block "^1.4.1" + ethereumjs-tx "^1.1.1" + ethereumjs-util "^5.0.1" + ethereumjs-vm "2.3.4" + through2 "^2.0.3" + treeify "^1.0.1" + web3-provider-engine "^13.3.2" + ethereum-common@0.0.16: version "0.0.16" resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.16.tgz#9a1e169ead34ab75e089f50ca512bfd0fbd12655" @@ -3158,7 +3227,7 @@ ethereumjs-account@^2.0.3, ethereumjs-account@~2.0.4: rlp "^2.0.0" safe-buffer "^5.1.1" -ethereumjs-block@^1.2.2, ethereumjs-block@~1.7.0: +ethereumjs-block@^1.2.2, ethereumjs-block@^1.4.1, ethereumjs-block@^1.6.0, ethereumjs-block@~1.7.0: version "1.7.1" resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz#78b88e6cc56de29a6b4884ee75379b6860333c3f" dependencies: @@ -3185,6 +3254,13 @@ ethereumjs-tx@^1.0.0, ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@ ethereum-common "^0.0.18" ethereumjs-util "^5.0.0" +ethereumjs-tx@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.6.tgz#d581c1703b7b250b2e54892031626534d53e0a79" + dependencies: + ethereum-common "^0.0.18" + ethereumjs-util "^5.0.0" + ethereumjs-util@^4.0.1, ethereumjs-util@^4.4.0: version "4.5.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-4.5.0.tgz#3e9428b317eebda3d7260d854fddda954b1f1bc6" @@ -3195,7 +3271,7 @@ ethereumjs-util@^4.0.1, ethereumjs-util@^4.4.0: rlp "^2.0.0" secp256k1 "^3.0.1" -ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5: +ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.3, ethereumjs-util@^5.1.5, ethereumjs-util@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz#3e0c0d1741471acf1036052d048623dee54ad642" dependencies: @@ -3207,9 +3283,9 @@ ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.3, ethereum safe-buffer "^5.1.1" secp256k1 "^3.0.1" -ethereumjs-vm@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.3.3.tgz#05719139e0c4a59e829022964a6048b17d2d84b0" +ethereumjs-vm@2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.3.4.tgz#f635d7cb047571a1840a6e9a74d29de4488f8ad6" dependencies: async "^2.1.2" async-eventemitter "^0.2.2" @@ -3223,7 +3299,7 @@ ethereumjs-vm@2.3.3: rustbn.js "~0.1.1" safe-buffer "^5.1.1" -ethereumjs-vm@^2.0.2: +ethereumjs-vm@2.3.5, ethereumjs-vm@^2.0.2, ethereumjs-vm@^2.1.0, ethereumjs-vm@^2.3.4: version "2.3.5" resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.3.5.tgz#e69306737b8a7ea80c633ceb9b7dd561897007de" dependencies: @@ -3805,14 +3881,6 @@ fs-extra@^4.0.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@~0.6.1: version "0.6.4" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.6.4.tgz#f46f0c75b7841f8d200b3348cd4d691d5a099d15" @@ -3901,9 +3969,9 @@ ganache-cli@6.1.4: dependencies: source-map-support "^0.5.3" -ganache-core@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.1.0.tgz#afe4ac8d5a2b5ed3622dd82d530acced78d5fb94" +ganache-core@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ganache-core/-/ganache-core-2.1.2.tgz#78810adc028a54f0dd4f3502c495917822fcda57" dependencies: abstract-leveldown "^3.0.0" async "^2.5.0" @@ -3915,8 +3983,8 @@ ganache-core@2.1.0: ethereumjs-account "~2.0.4" ethereumjs-block "~1.2.2" ethereumjs-tx "^1.3.0" - ethereumjs-util "^5.1.5" - ethereumjs-vm "2.3.3" + ethereumjs-util "^5.2.0" + ethereumjs-vm "2.3.5" ethereumjs-wallet "~0.6.0" fake-merkle-patricia-tree "~1.0.1" heap "~0.2.6" @@ -3926,16 +3994,15 @@ ganache-core@2.1.0: localstorage-down "^0.6.7" lodash "^4.17.5" merkle-patricia-tree "^2.2.0" - mocha "~3.3.0" pify "^3.0.0" prepend-file "^1.3.1" seedrandom "~2.4.2" shebang-loader "0.0.1" - solc "0.4.18" + solc "0.4.24" temp "^0.8.3" tmp "0.0.31" - web3 "^1.0.0-beta.30" - web3-provider-engine "^13.6.5" + web3 "^1.0.0-beta.34" + web3-provider-engine "^14.0.6" websocket "^1.0.24" yargs "^7.0.2" @@ -5319,7 +5386,7 @@ json-pointer@^0.6.0: dependencies: foreach "^2.0.4" -json-rpc-engine@^3.6.0: +json-rpc-engine@^3.4.0, json-rpc-engine@^3.6.0: version "3.7.3" resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.7.3.tgz#81dcabdb4f1ba5f79f99f04f560d20817908e4b5" dependencies: @@ -5898,7 +5965,7 @@ lodash@~2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.2.tgz#fadd834b9683073da179b3eae6d9c0d15053f73e" -log-symbols@^2.1.0: +log-symbols@^2.1.0, log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" dependencies: @@ -6407,22 +6474,6 @@ mocha@^4.1.0: mkdirp "0.5.1" supports-color "4.4.0" -mocha@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.3.0.tgz#d29b7428d3f52c82e2e65df1ecb7064e1aabbfb5" - dependencies: - browser-stdout "1.3.0" - commander "2.9.0" - debug "2.6.0" - diff "3.2.0" - escape-string-regexp "1.0.5" - glob "7.1.1" - growl "1.9.2" - json3 "3.3.2" - lodash.create "3.1.1" - mkdirp "0.5.1" - supports-color "3.1.2" - mock-fs@^4.1.0: version "4.5.0" resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.5.0.tgz#75245b966f7e3defe197b03454af9c5b355594b7" @@ -6474,10 +6525,6 @@ ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" -ms@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -6649,6 +6696,10 @@ node-emoji@^1.8.1: dependencies: lodash.toarray "^4.4.0" +node-fetch@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" + node-fetch@~1.7.1: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" @@ -6925,6 +6976,17 @@ optionator@^0.8.1, optionator@^0.8.2: type-check "~0.3.2" wordwrap "~1.0.0" +ora@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-2.1.0.tgz#6caf2830eb924941861ec53a173799e008b51e5b" + dependencies: + chalk "^2.3.1" + cli-cursor "^2.1.0" + cli-spinners "^1.1.0" + log-symbols "^2.2.0" + strip-ansi "^4.0.0" + wcwidth "^1.0.1" + original-require@1.0.1, original-require@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/original-require/-/original-require-1.0.1.tgz#0f130471584cd33511c5ec38c8d59213f9ac5e20" @@ -7255,6 +7317,10 @@ posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -8521,16 +8587,6 @@ socketcluster@^6.7.1: uid-number "0.0.5" uuid "3.1.0" -solc@0.4.18: - version "0.4.18" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.18.tgz#83ac6d871dd16a9710e67dbb76dad7f614100702" - dependencies: - fs-extra "^0.30.0" - memorystream "^0.3.1" - require-from-string "^1.1.0" - semver "^5.3.0" - yargs "^4.7.1" - solc@0.4.24, solc@^0.4.2: version "0.4.24" resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.24.tgz#354f14b269b38cbaa82a47d1ff151723502b954e" @@ -8985,7 +9041,7 @@ tapable@^0.2.7, tapable@~0.2.5: version "0.2.8" resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22" -tape@^4.4.0, tape@^4.6.3: +tape@^4.4.0, tape@^4.6.3, tape@^4.8.0: version "4.9.1" resolved "https://registry.yarnpkg.com/tape/-/tape-4.9.1.tgz#1173d7337e040c76fbf42ec86fcabedc9b3805c9" dependencies: @@ -9217,6 +9273,10 @@ tr46@~0.0.1: version "0.3.9" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" +treeify@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" + trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -9679,7 +9739,7 @@ watchpack@^1.3.1, watchpack@^1.4.0: graceful-fs "^4.1.2" neo-async "^2.5.0" -wcwidth@^1.0.0: +wcwidth@^1.0.0, wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" dependencies: @@ -9963,7 +10023,7 @@ web3-net@1.0.0-beta.34: web3-core-method "1.0.0-beta.34" web3-utils "1.0.0-beta.34" -web3-provider-engine@^13.6.5: +web3-provider-engine@^13.3.2: version "13.8.0" resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-13.8.0.tgz#4c7c1ad2af5f1fe10343b8a65495879a2f9c00df" dependencies: @@ -9987,6 +10047,32 @@ web3-provider-engine@^13.6.5: xhr "^2.2.0" xtend "^4.0.1" +web3-provider-engine@^14.0.6: + version "14.0.6" + resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-14.0.6.tgz#cbdd66fe20c0136a3a495cbe40d18b6c4160d5f0" + dependencies: + async "^2.5.0" + backoff "^2.5.0" + clone "^2.0.0" + cross-fetch "^2.1.0" + eth-block-tracker "^3.0.0" + eth-json-rpc-infura "^3.1.0" + eth-sig-util "^1.4.2" + ethereumjs-block "^1.2.2" + ethereumjs-tx "^1.2.0" + ethereumjs-util "^5.1.5" + ethereumjs-vm "^2.3.4" + json-rpc-error "^2.0.0" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.67.0" + semaphore "^1.0.3" + tape "^4.4.0" + ws "^5.1.1" + xhr "^2.2.0" + xtend "^4.0.1" + web3-providers-http@1.0.0-beta.33: version "1.0.0-beta.33" resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.0.0-beta.33.tgz#3b35ae00ee7df5b96b4934962ad4a86f2a5599c1" @@ -10116,7 +10202,7 @@ web3@^0.20.1: xhr2 "*" xmlhttprequest "*" -web3@^1.0.0-beta.30: +web3@^1.0.0-beta.34: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.34.tgz#347e561b784098cb5563315f490479a1d91f2ab1" dependencies: @@ -10243,6 +10329,10 @@ wget-improved@^1.4.0: minimist "1.2.0" tunnel "0.0.2" +whatwg-fetch@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + whatwg-url-compat@~0.6.5: version "0.6.5" resolved "https://registry.yarnpkg.com/whatwg-url-compat/-/whatwg-url-compat-0.6.5.tgz#00898111af689bb097541cd5a45ca6c8798445bf" @@ -10369,6 +10459,12 @@ ws@^3.0.0: safe-buffer "~5.1.0" ultron "~1.1.0" +ws@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.1.tgz#37827a0ba772d072a843c3615b0ad38bcdb354eb" + dependencies: + async-limiter "~1.0.0" + xhr-request-promise@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.2.tgz#343c44d1ee7726b8648069682d0f840c83b4261d" From fa9c802842a70763ecf2c3098b9f36363b75e3c2 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Mon, 2 Jul 2018 12:25:56 -0700 Subject: [PATCH 54/74] Lengthen polling interval (again) --- packages/truffle-deployer/src/deployment.js | 2 +- packages/truffle-deployer/test/deployer.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index de5cd29b247..32bcbbafc9e 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -15,7 +15,7 @@ class Deployment { this.emitter = emitter; this.promiEventEmitters = []; this.confirmationsMap = {}; - this.pollingInterval = 2000; + this.pollingInterval = 4000; this.blockPoll; } diff --git a/packages/truffle-deployer/test/deployer.js b/packages/truffle-deployer/test/deployer.js index 13c40f71655..f88225c39ce 100644 --- a/packages/truffle-deployer/test/deployer.js +++ b/packages/truffle-deployer/test/deployer.js @@ -219,7 +219,7 @@ describe("Deployer (sync)", function() { }) it('waits for confirmations', async function(){ - this.timeout(10000); + this.timeout(15000); const startBlock = await web3.eth.getBlockNumber(); utils.startAutoMine(web3, 1500); @@ -254,12 +254,12 @@ describe("Deployer (sync)", function() { this.timeout(15000); const startBlock = await web3.eth.getBlockNumber(); - utils.startAutoMine(web3, 1500); + utils.startAutoMine(web3, 4000); const migrate = function(){ deployer.then(async function(){ await deployer._startBlockPolling(web3); - await utils.waitMS(5000); + await utils.waitMS(9000); deployer._startBlockPolling(); }); }; @@ -267,7 +267,7 @@ describe("Deployer (sync)", function() { migrate(); await deployer.start(); utils.stopAutoMine(); - + console.log(output) assert(output.includes(`Block number: ${startBlock + 1}`)); assert(output.includes(`Wait: 1`)); assert(output.includes(`Block number: ${startBlock + 2}`)); From db2ca1045215f145c7de1450d0958dffbb5361d5 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Mon, 2 Jul 2018 20:33:26 -0700 Subject: [PATCH 55/74] Stop using handle.destroy on exit --- packages/truffle-core/cli.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/truffle-core/cli.js b/packages/truffle-core/cli.js index 6b2eed08ac8..dd7481a4d9b 100755 --- a/packages/truffle-core/cli.js +++ b/packages/truffle-core/cli.js @@ -47,7 +47,9 @@ command.run(process.argv.slice(2), options, function(err) { if (typeof handle.close === 'function'){ handle.close(); } else if (handle.readable && !handle._isStdio){ - handle.destroy(); + //This is causing problems over RPC + //with hd-wallet-provider V5 + //handle.destroy(); } }); }); From 9c98ee88ae405e97b4e4f6cdaad6791a5fd2a683 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Tue, 3 Jul 2018 15:58:41 -0700 Subject: [PATCH 56/74] Fix newlines / error msg spacing --- packages/truffle-deployer/src/deployment.js | 3 +- packages/truffle-migrate/reporter/messages.js | 30 +++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 32bcbbafc9e..4e112f52e7b 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -401,4 +401,5 @@ class Deployment { } }; -module.exports = Deployment; \ No newline at end of file +module.exports = Deployment; + diff --git a/packages/truffle-migrate/reporter/messages.js b/packages/truffle-migrate/reporter/messages.js index f661270ed4d..315a4184efb 100644 --- a/packages/truffle-migrate/reporter/messages.js +++ b/packages/truffle-migrate/reporter/messages.js @@ -54,24 +54,24 @@ class MigrationsMessages{ `above on Etherscan.\n`, noLibName: () => - `${prefix} Cannot link a library with no name.\n`, + `${prefix}Cannot link a library with no name.\n`, noLibAddress: () => - `${prefix} "${data.contract.contractName}" has no address. Has it been deployed?\n`, + `${prefix}"${data.contract.contractName}" has no address. Has it been deployed?\n`, noBytecode: () => - `${prefix} "${data.contract.contractName}" ` + + `${prefix}"${data.contract.contractName}" ` + `is an abstract contract or an interface and cannot be deployed\n` + ` * Hint: just import the contract into the '.sol' file that uses it.\n`, intWithGas: () => - `${prefix} "${data.contract.contractName}" ran out of gas ` + + `${prefix}"${data.contract.contractName}" ran out of gas ` + `(using a value you set in your network config or deployment parameters.)\n` + ` * Block limit: ${data.blockLimit}\n` + ` * Gas sent: ${data.gas}\n`, intNoGas: () => - `${prefix} "${data.contract.contractName}" ran out of gas ` + + `${prefix}"${data.contract.contractName}" ran out of gas ` + `(using Truffle's estimate.)\n` + ` * Block limit: ${data.blockLimit}\n` + ` * Gas sent: ${data.estimate}\n` + @@ -84,7 +84,7 @@ class MigrationsMessages{ ` private network or test client (like ganache).\n`, oogNoGas: () => - `${prefix} "${data.contract.contractName}" ran out of gas. Something in the constructor ` + + `${prefix}"${data.contract.contractName}" ran out of gas. Something in the constructor ` + `(ex: infinite loop) caused gas estimation to fail. Try:\n` + ` * Making your contract constructor more efficient\n` + ` * Setting the gas manually in your config or as a deployment parameter\n` + @@ -93,24 +93,24 @@ class MigrationsMessages{ ` private network or test client (like ganache).\n`, rvtReason: () => - `${prefix} "${data.contract.contractName}" hit a require or revert statement ` + + `${prefix}"${data.contract.contractName}" hit a require or revert statement ` + `with the following reason given:\n` + ` * ${data.reason}\n`, rvtNoReason: () => - `${prefix} "${data.contract.contractName}" hit a require or revert statement ` + + `${prefix}"${data.contract.contractName}" hit a require or revert statement ` + `somewhere in its constructor. Try:\n` + ` * Verifying that your constructor params satisfy all require conditions.\n` + ` * Adding reason strings to your require statements.\n`, asrtNoReason: () => - `${prefix} "${data.contract.contractName}" hit an invalid opcode while deploying. Try:\n` + + `${prefix}"${data.contract.contractName}" hit an invalid opcode while deploying. Try:\n` + ` * Verifying that your constructor params satisfy all assert conditions.\n` + ` * Verifying your constructor code doesn't access an array out of bounds.\n` + ` * Adding reason strings to your assert statements.\n`, noMoney: () => - `${prefix} "${data.contract.contractName}" could not deploy due to insufficient funds\n` + + `${prefix}"${data.contract.contractName}" could not deploy due to insufficient funds\n` + ` * Account: ${data.from}\n` + ` * Balance: ${data.balance} wei\n` + ` * Message: ${data.error.message}\n` + @@ -119,7 +119,7 @@ class MigrationsMessages{ ` + If you are using a local Geth node, verify that your node is synced.\n`, blockWithGas: () => - `${prefix} "${data.contract.contractName}" exceeded the block limit ` + + `${prefix}"${data.contract.contractName}" exceeded the block limit ` + `(with a gas value you set).\n` + ` * Block limit: ${data.blockLimit}\n` + ` * Gas sent: ${data.gas}\n` + @@ -129,27 +129,27 @@ class MigrationsMessages{ ` private network or test client (like ganache).\n`, blockNoGas: () => - `${prefix} "${data.contract.contractName}" exceeded the block limit ` + + `${prefix}"${data.contract.contractName}" exceeded the block limit ` + `(using Truffle's estimate).\n` + ` * Block limit: ${data.blockLimit}\n` + ` * Report this error in the Truffle issues on Github. It should not happen.\n` + ` * Try: setting gas manually in 'truffle.js' or as parameter to 'deployer.deploy'\n`, nonce: () => - `${prefix} "${data.contract.contractName}" received: ${data.error.message}.\n` + + `${prefix}"${data.contract.contractName}" received: ${data.error.message}.\n` + ` * This error is common when Infura is under heavy network load.\n` + ` * Try: setting the 'confirmations' key in your network config\n` + ` to wait for several block confirmations between each deployment.\n`, geth: () => - `${prefix} "${data.contract.contractName}" received a generic error from Geth that\n` + + `${prefix}"${data.contract.contractName}" received a generic error from Geth that\n` + `can be caused by hitting revert in a contract constructor or running out of gas.\n` + ` * ${data.estimateError.message}.\n` + ` * Try: + using the '--dry-run' option to reproduce this failure with clearer errors.\n` + ` + verifying that your gas is adequate for this deployment.\n`, default: () => - `${prefix} "${data.contract.contractName}" -- ${data.error.message}.\n`, + `${prefix}"${data.contract.contractName}" -- ${data.error.message}.\n`, } return kinds[kind](); From 663c96cb1262d2cd49dedb9d6fb2813c702b7b2d Mon Sep 17 00:00:00 2001 From: cgewecke Date: Tue, 3 Jul 2018 16:03:42 -0700 Subject: [PATCH 57/74] Deprecate batch deployments --- packages/truffle-deployer/index.js | 10 ++++-- packages/truffle-deployer/src/deployment.js | 34 --------------------- packages/truffle-deployer/test/deployer.js | 26 ---------------- 3 files changed, 7 insertions(+), 63 deletions(-) diff --git a/packages/truffle-deployer/index.js b/packages/truffle-deployer/index.js index 65761599a4c..db605f85fc3 100644 --- a/packages/truffle-deployer/index.js +++ b/packages/truffle-deployer/index.js @@ -46,9 +46,13 @@ class Deployer extends Deployment { const args = Array.prototype.slice.call(arguments); const contract = args.shift(); - return (Array.isArray(contract)) - ? this.queueOrExec(this._deployMany(contract, this)) - : this.queueOrExec(this._deploy(contract, args, this)); + if (Array.isArray(contract)){ + const msg = 'Support for batch deployments has been deprecated. ' + + 'Please deploy each contract individually.' + throw new Error(msg); + } + + this.queueOrExec(this._deploy(contract, args, this)); } new() { diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 4e112f52e7b..b4c87ecb0e4 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -356,40 +356,6 @@ class Deployment { }; } - /** - * Deploys an array of contracts - * @param {Array} arr Array of contract abstractions to deploy - * @return {Promise} - */ - _deployMany(arr){ - const self = this; - - return async function() { - const deployments = arr.map(args => { - let params; - let contract; - - if (Array.isArray(args)) { - contract = args[0]; - - (args.length > 1) - ? params = args.slice(1) - : params = []; - - } else { - contract = args; - params = []; - } - - return self._deploy(contract, params)(); - }); - - await self.emitter.emit('preDeployMany', arr); - await Promise.all(deployments); - await self.emitter.emit('postDeployMany', arr); - }; - } - /** * Cleans up promiEvents' emitter listeners */ diff --git a/packages/truffle-deployer/test/deployer.js b/packages/truffle-deployer/test/deployer.js index f88225c39ce..36f851daad3 100644 --- a/packages/truffle-deployer/test/deployer.js +++ b/packages/truffle-deployer/test/deployer.js @@ -111,32 +111,6 @@ describe("Deployer (sync)", function() { assert(output.includes('UsesExample')); }); - it('deploy([contract, [contract, args], ...])', async function(){ - const deployArgs = [Example, [UsesExample, utils.zeroAddress]] - - function migrate(){ - deployer.deploy(deployArgs); - }; - - migrate(); - await deployer.start(); - - const example = await Example.deployed(); - const usesExample = await UsesExample.deployed(); - const exampleId = await example.id(); - const usesExampleId = await usesExample.id(); - const other = await usesExample.other(); - - assert(Example.address !== null); - assert(exampleId === 'Example' ); - assert(usesExampleId === 'UsesExample' ); - assert(other === utils.zeroAddress); - - assert(output.includes('Deploying Batch')) - assert(output.includes('Example')); - assert(output.includes('UsesExample')); - }); - it('deployer.then', async function(){ function migrate(){ deployer.then(async function(){ From f325fd25b790ddd9d180abf1a64aa8ce01d4edb0 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Tue, 3 Jul 2018 16:33:09 -0700 Subject: [PATCH 58/74] Clarify public/private methods in deployment class --- packages/truffle-deployer/index.js | 5 +++-- packages/truffle-deployer/src/deployment.js | 17 +++++++++++++++-- packages/truffle-deployer/test/deployer.js | 2 +- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/truffle-deployer/index.js b/packages/truffle-deployer/index.js index db605f85fc3..f58d0636a0c 100644 --- a/packages/truffle-deployer/index.js +++ b/packages/truffle-deployer/index.js @@ -49,10 +49,11 @@ class Deployer extends Deployment { if (Array.isArray(contract)){ const msg = 'Support for batch deployments has been deprecated. ' + 'Please deploy each contract individually.' + throw new Error(msg); } - this.queueOrExec(this._deploy(contract, args, this)); + return this.queueOrExec(this.executeDeployment(contract, args, this)); } new() { @@ -80,7 +81,7 @@ class Deployer extends Deployment { finish(){ this.emitter.clearListeners(); - this._close(); + this.close(); } } diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index b4c87ecb0e4..22cf20f06f4 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -23,6 +23,7 @@ class Deployment { /** * Stub for future error code assignments on process.exit + * @private * @param {String} name contract name * @return {Number} code to exit */ @@ -32,6 +33,7 @@ class Deployment { /** * Helper to parse a deploy statement's overwrite option + * @private * @param {Arry} args arguments passed to deploy * @param {Boolean} isDeployed is contract deployed? * @return {Boolean} true if overwrite is ok @@ -50,6 +52,7 @@ class Deployment { /** * Gets arbitrary values from constructor params, if they exist. + * @private * @param {Array} args constructor params * @return {Any|Undefined} gas value */ @@ -70,6 +73,8 @@ class Deployment { * Emits a `block` event on each new block heard. This polling is * meant to be cancelled immediately on resolution of the * contract instance or on error. (See stopBlockPolling) + * @private + * @param {Object} web3 */ async _startBlockPolling(web3){ const self = this; @@ -99,6 +104,7 @@ class Deployment { /** * Clears the interval timer initiated by `startBlockPolling + * @private */ _stopBlockPolling(){ clearInterval(this.blockPoll); @@ -107,6 +113,7 @@ class Deployment { /** * Waits `n` blocks after a tx is mined, firing a pseudo * 'confirmation' event for each one. + * @private * @param {Number} blocksToWait * @param {Object} receipt * @param {Object} web3 @@ -148,6 +155,7 @@ class Deployment { * Sanity checks catch-all: * Are we connected? * Is contract deployable? + * @private * @param {Object} contract TruffleContract * @return {Promise} throws on error */ @@ -168,6 +176,7 @@ class Deployment { /** * Handler for contract's `transactionHash` event. Rebroadcasts as a deployer event + * @private * @param {Object} parent Deployment instance. Local `this` belongs to promievent * @param {String} hash tranactionHash */ @@ -183,6 +192,7 @@ class Deployment { /** * Handler for contract's `receipt` event. Rebroadcasts as a deployer event + * @private * @param {Object} parent Deployment instance. Local `this` belongs to promievent * @param {Object} state store for the receipt value * @param {Object} receipt @@ -209,6 +219,8 @@ class Deployment { * Queries the confirmations mapping periodically to see if we have * heard enough confirmations for a given tx to allow `deploy` to complete. * Resolves when this is true. + * + * @private * @param {String} hash contract creation tx hash * @return {Promise} */ @@ -231,6 +243,7 @@ class Deployment { * and maintains a table of txHashes & their current confirmation number. This * table gets polled if the user needs to wait a few blocks before getting * an instance back. + * @private * @param {Object} parent Deployment instance. Local `this` belongs to promievent * @param {Number} num Confirmation number * @param {Object} receipt transaction receipt @@ -253,7 +266,7 @@ class Deployment { * @param {Array} args Constructor arguments * @return {Promise} Resolves an instance */ - _deploy(contract, args){ + executeDeployment(contract, args){ const self = this; return async function() { @@ -359,7 +372,7 @@ class Deployment { /** * Cleans up promiEvents' emitter listeners */ - _close(){ + close(){ this.emitter.clearListeners(); this.promiEventEmitters.forEach(item => { item.removeAllListeners(); diff --git a/packages/truffle-deployer/test/deployer.js b/packages/truffle-deployer/test/deployer.js index 36f851daad3..9c7dfee914f 100644 --- a/packages/truffle-deployer/test/deployer.js +++ b/packages/truffle-deployer/test/deployer.js @@ -241,7 +241,7 @@ describe("Deployer (sync)", function() { migrate(); await deployer.start(); utils.stopAutoMine(); - console.log(output) + assert(output.includes(`Block number: ${startBlock + 1}`)); assert(output.includes(`Wait: 1`)); assert(output.includes(`Block number: ${startBlock + 2}`)); From b8a5f177031faa1f8fbf4f9bd42df72cc6aebf33 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Tue, 3 Jul 2018 16:40:43 -0700 Subject: [PATCH 59/74] Clarify extractReasonString conditional logic --- packages/truffle-contract/lib/execute.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/truffle-contract/lib/execute.js b/packages/truffle-contract/lib/execute.js index 91b63defaf3..504beaad45e 100644 --- a/packages/truffle-contract/lib/execute.js +++ b/packages/truffle-contract/lib/execute.js @@ -97,16 +97,15 @@ var execute = { const isObject = res && typeof res === 'object' && res.error && res.error.data; const isString = res && typeof res === 'object' && typeof res.result === 'string'; - if (isObject){ + if (isObject) { const data = res.error.data; const hash = Object.keys(data)[0]; if (data[hash].return && data[hash].return.includes('0x08c379a0')){ return web3.eth.abi.decodeParameter('string', data[hash].return.slice(10)) } - } - if (isString && res.result.includes('0x08c379a0')){ + } else if (isString && res.result.includes('0x08c379a0')){ return web3.eth.abi.decodeParameter('string', res.result.slice(10)) } }, From 73f0cfa1be8aa27b601ff22584c618d85856529c Mon Sep 17 00:00:00 2001 From: cgewecke Date: Tue, 3 Jul 2018 17:37:47 -0700 Subject: [PATCH 60/74] Stop using deployer array syntax for solidity test setup --- .../truffle-core/lib/testing/soliditytest.js | 21 +++++++++---------- packages/truffle-deployer/src/deployment.js | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/truffle-core/lib/testing/soliditytest.js b/packages/truffle-core/lib/testing/soliditytest.js index 0bca8fe8199..2b0e8c10f25 100644 --- a/packages/truffle-core/lib/testing/soliditytest.js +++ b/packages/truffle-core/lib/testing/soliditytest.js @@ -134,18 +134,17 @@ var SolidityTest = { var DeployedAddresses = runner.config.resolver.require("truffle/DeployedAddresses.sol"); var SafeSend = runner.config.resolver.require("SafeSend.sol"); - deployer.deploy([ - Assert, - DeployedAddresses - ]).then(function() { - dependency_paths.forEach(function(dependency_path) { - var dependency = runner.config.resolver.require(dependency_path); - - if (dependency.isDeployed()) { - deployer.link(dependency, abstraction); - } + deployer.deploy(Assert) + .then(() => deployer.deploy(DeployedAddresses)) + .then(() => { + return dependency_paths.forEach(function(dependency_path) { + var dependency = runner.config.resolver.require(dependency_path); + + if (dependency.isDeployed()) { + deployer.link(dependency, abstraction); + } + }); }); - }); var deployed; deployer.deploy(abstraction).then(function() { diff --git a/packages/truffle-deployer/src/deployment.js b/packages/truffle-deployer/src/deployment.js index 22cf20f06f4..32e0ae8c13d 100644 --- a/packages/truffle-deployer/src/deployment.js +++ b/packages/truffle-deployer/src/deployment.js @@ -339,7 +339,7 @@ class Deployment { self._stopBlockPolling(); eventArgs.error = err.error || err; await self.emitter.emit('deployFailed', eventArgs); - self._close(); + self.close(); throw new Error(self._errors(contract.contractName)); } From 2af8387c7f2b4afe4ecfa0ee23cbddac68b04bad Mon Sep 17 00:00:00 2001 From: cgewecke Date: Tue, 3 Jul 2018 17:49:33 -0700 Subject: [PATCH 61/74] Clean up migrate command --- packages/truffle-core/lib/commands/migrate.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/truffle-core/lib/commands/migrate.js b/packages/truffle-core/lib/commands/migrate.js index 0d2c222d14a..781c5744771 100644 --- a/packages/truffle-core/lib/commands/migrate.js +++ b/packages/truffle-core/lib/commands/migrate.js @@ -155,12 +155,9 @@ var command = { } catch(err){ done(err) }; - return; // Production: dry-run then real run - } - - else if (production){ + } else if (production) { const currentBuild = conf.contracts_build_directory; conf.dryRun = true; From 54fd631c0048ef45d8024897985a1d7d7e88dc38 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Tue, 3 Jul 2018 20:12:02 -0700 Subject: [PATCH 62/74] Fix production tests for Geth --- packages/truffle/test/scenarios/migrations/production.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/truffle/test/scenarios/migrations/production.js b/packages/truffle/test/scenarios/migrations/production.js index 1937852c2a0..8c03cb1bcb1 100644 --- a/packages/truffle/test/scenarios/migrations/production.js +++ b/packages/truffle/test/scenarios/migrations/production.js @@ -63,8 +63,13 @@ describe("production migrations [ @geth ]", function() { assert(output.includes(network.transactionHash)); assert(output.includes(network.address)); assert(output.includes('2 confirmations')); - assert(output.includes('confirmation number: 1')); - assert(output.includes('confirmation number: 2')); + + // Geth automines too quickly for the 4 sec resolution we set + // to trigger the output. + if (!process.env.GETH){ + assert(output.includes('confirmation number: 1')); + assert(output.includes('confirmation number: 2')); + } console.log(output) done(); From d2c03e077b79189fbf15bb945ad1738148cc7f91 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Thu, 5 Jul 2018 13:34:35 -0700 Subject: [PATCH 63/74] Make sure fork tracks parent block gasLimit --- packages/truffle-core/lib/environment.js | 13 +++++++++---- packages/truffle-migrate/migration.js | 2 ++ packages/truffle-migrate/reporter/messages.js | 5 +++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/truffle-core/lib/environment.js b/packages/truffle-core/lib/environment.js index a1271316d56..a02be01d73c 100644 --- a/packages/truffle-core/lib/environment.js +++ b/packages/truffle-core/lib/environment.js @@ -81,14 +81,16 @@ var Environment = { }, // Ensure you call Environment.detect() first. - fork: function(config, callback) { + fork: async function(config, callback) { expect.options(config, [ "from" ]); var web3 = new Web3(config.provider); - web3.eth.getAccounts().then(accounts => { + try { + var accounts = await web3.eth.getAccounts() + var block = await web3.eth.getBlock('latest'); var upstreamNetwork = config.network; var upstreamConfig = config.networks[upstreamNetwork]; @@ -98,7 +100,8 @@ var Environment = { network_id: config.network_id, provider: TestRPC.provider({ fork: config.provider, - unlocked_accounts: accounts + unlocked_accounts: accounts, + gasLimit: block.gasLimit }), from: config.from, gas: upstreamConfig.gas, @@ -108,7 +111,9 @@ var Environment = { callback(); - }).catch(callback); + } catch(err){ + callback(err) + }; }, develop: function(config, testrpcOptions, callback) { diff --git a/packages/truffle-migrate/migration.js b/packages/truffle-migrate/migration.js index e4df73dccb3..0a9b9ef8337 100644 --- a/packages/truffle-migrate/migration.js +++ b/packages/truffle-migrate/migration.js @@ -171,12 +171,14 @@ class Migration { // Get file path and emit pre-migration event const file = path.relative(options.migrations_directory, self.file) + const block = await web3.eth.getBlock('latest'); const preMigrationsData = { file: file, isFirst: self.isFirst, network: options.network, networkId: options.network_id, + blockLimit: block.gasLimit } await self.emitter.emit('preMigrate', preMigrationsData); diff --git a/packages/truffle-migrate/reporter/messages.js b/packages/truffle-migrate/reporter/messages.js index 315a4184efb..c0934a9f8c1 100644 --- a/packages/truffle-migrate/reporter/messages.js +++ b/packages/truffle-migrate/reporter/messages.js @@ -237,8 +237,9 @@ class MigrationsMessages{ : output = self.doubleline(`Starting migrations...`) + '\n'; output += - `> Network name: '${data.network}'\n` + - `> Network id: ${data.networkId}\n`; + `> Network name: '${data.network}'\n` + + `> Network id: ${data.networkId}\n` + + `> Block gas limit: ${data.blockLimit}\n`; return output; }, From 509a5433d7649a124f08c93fa66cd853546011fd Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 11 Jul 2018 19:32:28 -0700 Subject: [PATCH 64/74] Add reason string to error messages --- packages/truffle-contract/lib/execute.js | 10 ++++++++-- packages/truffle-contract/lib/handlers.js | 10 ++++++++-- packages/truffle-contract/lib/override.js | 3 +++ packages/truffle-contract/lib/statuserror.js | 8 ++++++-- packages/truffle-contract/test/deploy.js | 6 +++++- packages/truffle-contract/test/errors.js | 14 ++++++++++---- packages/truffle-contract/test/methods.js | 1 + 7 files changed, 41 insertions(+), 11 deletions(-) diff --git a/packages/truffle-contract/lib/execute.js b/packages/truffle-contract/lib/execute.js index 504beaad45e..7a7935f8633 100644 --- a/packages/truffle-contract/lib/execute.js +++ b/packages/truffle-contract/lib/execute.js @@ -252,8 +252,14 @@ var execute = { deferred.then(receipt => { if (parseInt(receipt.status) == 0){ - var error = new StatusError(params, context.transactionHash, receipt); - error.reason = reason; + + var error = new StatusError( + params, + context.transactionHash, + receipt, + reason + ); + return context.promiEvent.reject(error) } diff --git a/packages/truffle-contract/lib/handlers.js b/packages/truffle-contract/lib/handlers.js index db33b1ef304..c3cb5a7f0a3 100644 --- a/packages/truffle-contract/lib/handlers.js +++ b/packages/truffle-contract/lib/handlers.js @@ -103,8 +103,14 @@ var handlers = { // .method(): resolve/reject receipt in handler if (parseInt(receipt.status) == 0 && !context.onlyEmitReceipt){ - var error = new StatusError(context.params, receipt.transactionHash, receipt); - error.reason = context.reason; + + var error = new StatusError( + context.params, + receipt.transactionHash, + receipt, + context.reason + ); + return context.promiEvent.reject(error) } diff --git a/packages/truffle-contract/lib/override.js b/packages/truffle-contract/lib/override.js index 626d8427514..d95af01787c 100644 --- a/packages/truffle-contract/lib/override.js +++ b/packages/truffle-contract/lib/override.js @@ -24,6 +24,9 @@ var override = { var timedOut = web3Error.message && web3Error.message.includes(override.timeoutMessage); var shouldWait = maxBlocks > currentBlock; + // Append error message + if (web3Error.reason) web3Error.message += ` -- Reason given: ${web3Error.reason}.`; + // Reject if we shouldn't be waiting. if (!timedOut || !shouldWait) return context.promiEvent.reject(web3Error); diff --git a/packages/truffle-contract/lib/statuserror.js b/packages/truffle-contract/lib/statuserror.js index b75ff84e12f..113d334aee7 100644 --- a/packages/truffle-contract/lib/statuserror.js +++ b/packages/truffle-contract/lib/statuserror.js @@ -6,9 +6,12 @@ inherits(StatusError, TruffleError); var defaultGas = 90000; -function StatusError(args, tx, receipt) { +function StatusError(args, tx, receipt, reason) { var message; var gasLimit = parseInt(args.gas) || defaultGas; + var reasonString = ''; + + if(reason) reasonString = `Reason given: ${reason}.`; if(receipt.gasUsed === gasLimit){ @@ -20,7 +23,7 @@ function StatusError(args, tx, receipt) { } else { - message = "Transaction: " + tx + " exited with an error (status 0).\n" + + message = `Transaction: ${tx} exited with an error (status 0). ${reasonString}\n` + " Please check that the transaction:\n" + " - satisfies all conditions set by Solidity `require` statements.\n" + " - does not trigger a Solidity `revert` statement.\n"; @@ -29,6 +32,7 @@ function StatusError(args, tx, receipt) { StatusError.super_.call(this, message); this.tx = tx; this.receipt = receipt; + this.reason = reason; } module.exports = StatusError; \ No newline at end of file diff --git a/packages/truffle-contract/test/deploy.js b/packages/truffle-contract/test/deploy.js index a4f082c4652..8e8c650eb0f 100644 --- a/packages/truffle-contract/test/deploy.js +++ b/packages/truffle-contract/test/deploy.js @@ -121,7 +121,10 @@ describe("Deployments", function() { } }); + // This example contains a reason string when run with ganache but no + // reason strings when run vs geth. it("Handles absence of reason string gracefully", async function(){ + try { await Example.new(2001) // 2001 fails a require gate assert.fail() @@ -130,7 +133,7 @@ describe("Deployments", function() { e.message.includes('intrinsic gas too low'); assert(errorCorrect, 'Expected gas limit error'); - assert(e.receipt === undefined, 'Expected no receipt') + assert(e.receipt === undefined, 'Expected no receipt'); } }); @@ -153,6 +156,7 @@ describe("Deployments", function() { assert.fail(); } catch(error) { assert(error.message.includes('exceeds gas limit')); + assert(error.message.includes('reasonstring')); assert(error.receipt === undefined, 'Expected no receipt') assert(error.reason === 'reasonstring'); } diff --git a/packages/truffle-contract/test/errors.js b/packages/truffle-contract/test/errors.js index e8ca25d240b..9b227219c32 100644 --- a/packages/truffle-contract/test/errors.js +++ b/packages/truffle-contract/test/errors.js @@ -46,7 +46,8 @@ describe("Client appends errors (vmErrorsOnRPCResponse)", function() { await Example.new(13) // 13 fails a constructor require gate assert.fail() } catch(e){ - assert(!e.reason, 'Error should not include reason string'); + assert(!e.reason, 'Error should not include reason property'); + assert(!e.message.includes('Reason'), 'Should not include reason message'); assert(e.message.includes('exceeds gas limit'), 'Error should be gas limit err'); } }); @@ -56,7 +57,8 @@ describe("Client appends errors (vmErrorsOnRPCResponse)", function() { await Example.new(2001) // 2001 fails a constructor require gate w/ a reason assert.fail() } catch(e){ - assert(e.reason === 'reasonstring', 'Error should include reason string'); + assert(e.reason === 'reasonstring', 'Error should include reason property'); + assert(e.message.includes('reasonstring'), 'Error message should include reason'); assert(e.message.includes('exceeds gas limit'), 'Error should be gas limit err'); } }); @@ -124,6 +126,7 @@ describe("Client appends errors (vmErrorsOnRPCResponse)", function() { assert.fail(); } catch(e){ assert(!e.reason, 'Should not include reasonstring'); + assert(!e.message.includes('Reason'), 'Error message should not include reason'); assert(e.message.includes('revert'), 'Should include revert message'); }; }); @@ -134,7 +137,8 @@ describe("Client appends errors (vmErrorsOnRPCResponse)", function() { await example.triggerRequireWithReasonError(); assert.fail(); } catch(e){ - assert(e.reason === 'reasonstring', 'Should include reasonstring'); + assert(e.reason === 'reasonstring', 'Should include reason property'); + assert(e.message.includes('reasonstring'), 'Should reason in message'); assert(e.message.includes('revert'), 'Should include revert message'); }; }); @@ -145,7 +149,8 @@ describe("Client appends errors (vmErrorsOnRPCResponse)", function() { await example.triggerRequireWithReasonError({gas: 200000}); assert.fail(); } catch(e){ - assert(e.reason === 'reasonstring', 'Should include reason string'); + assert(e.reason === 'reasonstring', 'Should include reason property'); + assert(e.message.includes('reasonstring'), 'Should reason in message'); assert(e.message.includes('revert'), 'Should include revert'); } }); @@ -157,6 +162,7 @@ describe("Client appends errors (vmErrorsOnRPCResponse)", function() { assert.fail(); } catch(e){ assert(!e.reason, 'Should not include reason string'); + assert(!e.message.includes('Reason'), 'Should not include reason message'); assert(e.message.includes('invalid opcode'), 'Should include invalid opcode'); } }); diff --git a/packages/truffle-contract/test/methods.js b/packages/truffle-contract/test/methods.js index 2c66f8c9eb8..0d44da987ab 100644 --- a/packages/truffle-contract/test/methods.js +++ b/packages/truffle-contract/test/methods.js @@ -318,6 +318,7 @@ describe("Methods", function() { assert.fail(); } catch(e){ assert(e.reason === 'reasonstring'); + assert(e.message.includes('reasonstring')); assert(e.message.includes('revert')); assert(parseInt(e.receipt.status, 16) == 0) }; From d0ce12e7c82113900276cd0f4baad23857eb6de1 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 11 Jul 2018 19:53:17 -0700 Subject: [PATCH 65/74] Clean up reason string logic --- packages/truffle-contract/lib/execute.js | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/truffle-contract/lib/execute.js b/packages/truffle-contract/lib/execute.js index 7a7935f8633..77ad6ba2f37 100644 --- a/packages/truffle-contract/lib/execute.js +++ b/packages/truffle-contract/lib/execute.js @@ -21,8 +21,6 @@ var execute = { var web3 = this.web3; return new Promise(function(accept, reject){ - let reason; - const packet = { jsonrpc: "2.0", method: "eth_call", @@ -32,12 +30,9 @@ var execute = { // This rpc call extracts the reason string web3.currentProvider.send(packet, (err, response) => { - if (response && (response.error || response.result)) { - reason = execute.extractReason(response, web3) - } + const reason = execute.extractReason(response, web3); - web3 - .eth + web3.eth .estimateGas(params) .then(gas => { // Always prefer specified gas - this includes gas set by class_defaults @@ -89,11 +84,15 @@ var execute = { /** * Processes .call/.estimateGas errors and extracts a reason string if - * - * @param {[type]} err [description] - * @return {[type]} [description] + * @param {Object} res response from `eth_call` to extract reason + * @param {Web3} web3 a helpful friend + * @return {String|Undefined} decoded reason string */ extractReason(res, web3){ + if (!res || (!res.error && !res.result)) return; + + const errorStringHash = '0x08c379a0'; + const isObject = res && typeof res === 'object' && res.error && res.error.data; const isString = res && typeof res === 'object' && typeof res.result === 'string'; @@ -101,11 +100,11 @@ var execute = { const data = res.error.data; const hash = Object.keys(data)[0]; - if (data[hash].return && data[hash].return.includes('0x08c379a0')){ + if (data[hash].return && data[hash].return.includes(errorStringHash)){ return web3.eth.abi.decodeParameter('string', data[hash].return.slice(10)) } - } else if (isString && res.result.includes('0x08c379a0')){ + } else if (isString && res.result.includes(errorStringHash)){ return web3.eth.abi.decodeParameter('string', res.result.slice(10)) } }, @@ -176,7 +175,6 @@ var execute = { var args = Array.prototype.slice.call(arguments); var params = utils.getTxParams.call(constructor, args); var promiEvent = new Web3PromiEvent(); - var reason; var context = { contract: constructor, // Can't name this field `constructor` or `_constructor` From 0e1f8dd902e22dce01c542f965029cdc2fc8c884 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 11 Jul 2018 19:56:48 -0700 Subject: [PATCH 66/74] Use canonical migrations name --- packages/truffle-migrate/migration.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/truffle-migrate/migration.js b/packages/truffle-migrate/migration.js index 0a9b9ef8337..17f69c352a3 100644 --- a/packages/truffle-migrate/migration.js +++ b/packages/truffle-migrate/migration.js @@ -91,7 +91,7 @@ class Migration { if (options.save === false) return; // Write migrations record to chain - const Migrations = resolver.require("./Migrations.sol"); + const Migrations = resolver.require("Migrations"); if (Migrations && Migrations.isDeployed()) { await self.emitter.emit('saveMigration', self.number); From 5d39172d4163aa8d9dea109bd8fea277b151d855 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 11 Jul 2018 20:10:52 -0700 Subject: [PATCH 67/74] Add note about --exit flag --- packages/truffle-contract/scripts/test.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/truffle-contract/scripts/test.sh b/packages/truffle-contract/scripts/test.sh index 48bdbf8a970..8ffb5ead707 100755 --- a/packages/truffle-contract/scripts/test.sh +++ b/packages/truffle-contract/scripts/test.sh @@ -2,6 +2,9 @@ set -o errexit +# Since mocha v4, the test runner doesn't automatically process.exit +# when it's done running. Because we're running geth as a separate client in CI, +# the provider connections stay open and everything hangs. Hence the `--exit` flag. if [ "$GETH" == true ]; then mocha --timeout 50000 --grep @geth --colors --exit else From d9ea3f35e3cecce8ad5b3cb1f9c161d545d050ef Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 11 Jul 2018 20:13:47 -0700 Subject: [PATCH 68/74] Cleaup if copy fails --- packages/truffle-core/lib/commands/migrate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/truffle-core/lib/commands/migrate.js b/packages/truffle-core/lib/commands/migrate.js index 781c5744771..91b8bb9811d 100644 --- a/packages/truffle-core/lib/commands/migrate.js +++ b/packages/truffle-core/lib/commands/migrate.js @@ -74,7 +74,7 @@ var command = { }; copy(config.contracts_build_directory, temporaryDirectory, function(err) { - if (err) return reject(err); + if (err) return cleanup(err); config.contracts_build_directory = temporaryDirectory; From c754c896f2d63b6d8ae2ac71ca0d2456f3d3b6df Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 11 Jul 2018 20:34:12 -0700 Subject: [PATCH 69/74] Add test for long reason string --- packages/truffle-contract/test/deploy.js | 14 +++++++++++++- packages/truffle-contract/test/sources/Example.sol | 1 + packages/truffle-deployer/index.js | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/truffle-contract/test/deploy.js b/packages/truffle-contract/test/deploy.js index 8e8c650eb0f..8a9717bfe8e 100644 --- a/packages/truffle-contract/test/deploy.js +++ b/packages/truffle-contract/test/deploy.js @@ -152,7 +152,7 @@ describe("Deployments", function() { describe(".new(): revert with reasonstring (ganache only)", function(){ it("should reject with reason string on revert", async function(){ try { - await Example.new(2001); + await Example.new(2001); // Triggers error with a normal reason string assert.fail(); } catch(error) { assert(error.message.includes('exceeds gas limit')); @@ -161,6 +161,18 @@ describe("Deployments", function() { assert(error.reason === 'reasonstring'); } }); + + it("should reject with long reason string on revert", async function(){ + try { + await Example.new(20001); // Triggers error with a long reason string + assert.fail(); + } catch(error) { + assert(error.message.includes('exceeds gas limit')); + assert(error.message.includes('solidity storage is a fun lesson in endianness')); + assert(error.receipt === undefined, 'Expected no receipt') + assert(error.reason === 'solidity storage is a fun lesson in endianness'); + } + }); }) describe('pre-flight gas estimation', function(){ diff --git a/packages/truffle-contract/test/sources/Example.sol b/packages/truffle-contract/test/sources/Example.sol index 20dab13af5f..1817ed5d184 100644 --- a/packages/truffle-contract/test/sources/Example.sol +++ b/packages/truffle-contract/test/sources/Example.sol @@ -13,6 +13,7 @@ contract Example { // Constructor revert require(val != 13); require(val != 2001, 'reasonstring'); + require(val != 20001, 'solidity storage is a fun lesson in endianness'); // Expensive deployment if(val >= 50){ diff --git a/packages/truffle-deployer/index.js b/packages/truffle-deployer/index.js index f58d0636a0c..42cb7d4a563 100644 --- a/packages/truffle-deployer/index.js +++ b/packages/truffle-deployer/index.js @@ -47,7 +47,7 @@ class Deployer extends Deployment { const contract = args.shift(); if (Array.isArray(contract)){ - const msg = 'Support for batch deployments has been deprecated. ' + + const msg = 'Support for batch deployments is no longer supported. ' + 'Please deploy each contract individually.' throw new Error(msg); From e4d3da9781d5a7c0f0340bf35a9a7db3502ad841 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 11 Jul 2018 21:07:26 -0700 Subject: [PATCH 70/74] Remove unused logic for 50 blocks override --- packages/truffle-contract/lib/override.js | 13 ------- .../truffle-contract/lib/subscriptions.js | 37 ------------------- packages/truffle-migrate/migration.js | 2 +- 3 files changed, 1 insertion(+), 51 deletions(-) delete mode 100644 packages/truffle-contract/lib/subscriptions.js diff --git a/packages/truffle-contract/lib/override.js b/packages/truffle-contract/lib/override.js index d95af01787c..dfe68ca5a36 100644 --- a/packages/truffle-contract/lib/override.js +++ b/packages/truffle-contract/lib/override.js @@ -1,5 +1,3 @@ -var subscriptions = require("./subscriptions"); - var override = { timeoutMessage: 'not mined within', // Substring of timeout err fired by web3 @@ -70,17 +68,6 @@ var override = { listener(pollID); } }, override.pollingInterval); - - - // Temporarily disabled new_head subscription. There are bugs at Web3 and at - // Geth that impair the reliablility of this sub atm. - - /* - var id = new Date().getTime(); - subscriptions.subscribe(constructor, 'newHeads', id) - .then(result => constructor.web3.currentProvider.on('data', listener)) - .catch(context.promiEvent.reject); - */ }, } diff --git a/packages/truffle-contract/lib/subscriptions.js b/packages/truffle-contract/lib/subscriptions.js deleted file mode 100644 index 6890793f73a..00000000000 --- a/packages/truffle-contract/lib/subscriptions.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * This code is temporarily unused pending resolution of bugs at Web3 & Geth that make - * subscriptions for the block head unreliable. - */ - -module.exports = { - /** - * @param {String} e.g. "newHeads" - * @return {Promise} subscription enabled on resolution - */ - subscribe: function(constructor, topic, id){ - return new Promise((accept, reject) => { - constructor.web3.currentProvider.send({ - jsonrpc: "2.0", - method: "eth_subscribe", - params: [topic], - id: id - }, - (err, result) => (err) ? reject(err) : accept(result)); - }) - }, - - /** - * @param {Number} id of subscription to cancel - * @return {Promise} subscription cancelled on resolution - */ - unsubscribe: function(constructor, id){ - return new Promise((accept, reject) => { - constructor.web3.currentProvider.send({ - jsonrpc: "2.0", - method: "eth_unsubscribe", - params: [id], - }, - (err, result) => (err) ? reject(err) : accept(result)); - }) - } -} \ No newline at end of file diff --git a/packages/truffle-migrate/migration.js b/packages/truffle-migrate/migration.js index 17f69c352a3..0a9b9ef8337 100644 --- a/packages/truffle-migrate/migration.js +++ b/packages/truffle-migrate/migration.js @@ -91,7 +91,7 @@ class Migration { if (options.save === false) return; // Write migrations record to chain - const Migrations = resolver.require("Migrations"); + const Migrations = resolver.require("./Migrations.sol"); if (Migrations && Migrations.isDeployed()) { await self.emitter.emit('saveMigration', self.number); From 05719d4d2ddb86adf10fa7fdeca02729a4b4b575 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 11 Jul 2018 22:27:17 -0700 Subject: [PATCH 71/74] Move migrations reporter to truffle-reporters --- packages/truffle-deployer/package.json | 1 + packages/truffle-deployer/test/deployer.js | 2 +- packages/truffle-deployer/test/errors.js | 2 +- packages/truffle-migrate/index.js | 2 +- packages/truffle-migrate/package.json | 2 +- packages/truffle-reporters/index.js | 5 +++++ packages/truffle-reporters/package.json | 16 ++++++++++++++++ .../reporters/migrations-V5}/indentedSpinner.js | 0 .../reporters/migrations-V5}/messages.js | 0 .../reporters/migrations-V5}/reporter.js | 0 yarn.lock | 2 +- 11 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 packages/truffle-reporters/index.js create mode 100644 packages/truffle-reporters/package.json rename packages/{truffle-migrate/reporter => truffle-reporters/reporters/migrations-V5}/indentedSpinner.js (100%) rename packages/{truffle-migrate/reporter => truffle-reporters/reporters/migrations-V5}/messages.js (100%) rename packages/{truffle-migrate/reporter => truffle-reporters/reporters/migrations-V5}/reporter.js (100%) diff --git a/packages/truffle-deployer/package.json b/packages/truffle-deployer/package.json index ba6ccaf2177..e8046dae8df 100644 --- a/packages/truffle-deployer/package.json +++ b/packages/truffle-deployer/package.json @@ -33,6 +33,7 @@ "ganache-cli": "6.1.4", "memorystream": "^0.3.1", "mocha": "5.2.0", + "truffle-reporters": "^1.0.0", "truffle-workflow-compile": "^1.0.3", "web3": "1.0.0-beta.33" }, diff --git a/packages/truffle-deployer/test/deployer.js b/packages/truffle-deployer/test/deployer.js index 9c7dfee914f..26ec7cb0361 100644 --- a/packages/truffle-deployer/test/deployer.js +++ b/packages/truffle-deployer/test/deployer.js @@ -2,9 +2,9 @@ const ganache = require("ganache-cli"); const contract = require("truffle-contract"); const Web3 = require("web3"); const assert = require("assert"); +const Reporter = require("truffle-reporters").migrationsV5; const Deployer = require("../index"); -const Reporter = require("./helpers/reporter") const utils = require('./helpers/utils'); const util = require('util'); diff --git a/packages/truffle-deployer/test/errors.js b/packages/truffle-deployer/test/errors.js index df60fa148d7..6d320eb2ad9 100644 --- a/packages/truffle-deployer/test/errors.js +++ b/packages/truffle-deployer/test/errors.js @@ -2,9 +2,9 @@ const ganache = require("ganache-cli"); const contract = require("truffle-contract"); const Web3 = require("web3"); const assert = require("assert"); +const Reporter = require("truffle-reporters").migrationsV5; const Deployer = require("../index"); -const Reporter = require("./helpers/reporter") const utils = require('./helpers/utils'); describe("Error cases", function() { diff --git a/packages/truffle-migrate/index.js b/packages/truffle-migrate/index.js index 82e64c38261..5088f72d82e 100644 --- a/packages/truffle-migrate/index.js +++ b/packages/truffle-migrate/index.js @@ -4,7 +4,7 @@ const async = require("async"); const expect = require("truffle-expect"); const util = require('util'); -const Reporter = require("./reporter/reporter"); +const Reporter = require("truffle-reporters").migrationsV5; const Migration = require('./migration.js'); /** diff --git a/packages/truffle-migrate/package.json b/packages/truffle-migrate/package.json index 1f71bc136ed..43d345446eb 100644 --- a/packages/truffle-migrate/package.json +++ b/packages/truffle-migrate/package.json @@ -24,9 +24,9 @@ "async": "2.6.1", "emittery": "^0.4.0", "node-dir": "0.1.17", - "ora": "^2.1.0", "truffle-deployer": "^2.0.7", "truffle-expect": "^0.0.4", + "truffle-reporters": "^1.0.0", "truffle-require": "^1.0.7", "web3": "1.0.0-beta.33" }, diff --git a/packages/truffle-reporters/index.js b/packages/truffle-reporters/index.js new file mode 100644 index 00000000000..49496833df3 --- /dev/null +++ b/packages/truffle-reporters/index.js @@ -0,0 +1,5 @@ +const migrationsV5 = require('./reporters/migrations-V5/reporter'); + +module.exports = { + migrationsV5: migrationsV5, +} diff --git a/packages/truffle-reporters/package.json b/packages/truffle-reporters/package.json new file mode 100644 index 00000000000..6e139ddf195 --- /dev/null +++ b/packages/truffle-reporters/package.json @@ -0,0 +1,16 @@ +{ + "name": "truffle-reporters", + "version": "1.0.0", + "description": "Reporters for Truffle modules", + "main": "index.js", + "scripts": {}, + "author": "", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "ora": "^2.1.0", + "web3-utils": "^1.0.0-beta.34" + } +} diff --git a/packages/truffle-migrate/reporter/indentedSpinner.js b/packages/truffle-reporters/reporters/migrations-V5/indentedSpinner.js similarity index 100% rename from packages/truffle-migrate/reporter/indentedSpinner.js rename to packages/truffle-reporters/reporters/migrations-V5/indentedSpinner.js diff --git a/packages/truffle-migrate/reporter/messages.js b/packages/truffle-reporters/reporters/migrations-V5/messages.js similarity index 100% rename from packages/truffle-migrate/reporter/messages.js rename to packages/truffle-reporters/reporters/migrations-V5/messages.js diff --git a/packages/truffle-migrate/reporter/reporter.js b/packages/truffle-reporters/reporters/migrations-V5/reporter.js similarity index 100% rename from packages/truffle-migrate/reporter/reporter.js rename to packages/truffle-reporters/reporters/migrations-V5/reporter.js diff --git a/yarn.lock b/yarn.lock index 85dddb61c85..1cc814fd032 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10149,7 +10149,7 @@ web3-utils@1.0.0-beta.33: underscore "1.8.3" utf8 "2.1.1" -web3-utils@1.0.0-beta.34: +web3-utils@1.0.0-beta.34, web3-utils@^1.0.0-beta.34: version "1.0.0-beta.34" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.34.tgz#9411fc39aaef39ca4e06169f762297d9ff020970" dependencies: From 9218386b8660b21fab357ad22ef90d9fd407effd Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 11 Jul 2018 23:07:05 -0700 Subject: [PATCH 72/74] Fix deployer tests --- packages/truffle-deployer/test/deployer.js | 21 ++++++++++++------- packages/truffle-deployer/test/errors.js | 16 +++++++++++--- .../reporters/migrations-V5/reporter.js | 5 ++++- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/packages/truffle-deployer/test/deployer.js b/packages/truffle-deployer/test/deployer.js index 26ec7cb0361..c5d818c8cf9 100644 --- a/packages/truffle-deployer/test/deployer.js +++ b/packages/truffle-deployer/test/deployer.js @@ -3,6 +3,7 @@ const contract = require("truffle-contract"); const Web3 = require("web3"); const assert = require("assert"); const Reporter = require("truffle-reporters").migrationsV5; +const EventEmitter = require('events'); const Deployer = require("../index"); const utils = require('./helpers/utils'); @@ -24,6 +25,10 @@ describe("Deployer (sync)", function() { vmErrorsOnRPCResponse: false }); + const mockMigration = { + emitter: new EventEmitter() + } + const web3 = new Web3(provider); beforeEach(async function() { @@ -51,7 +56,10 @@ describe("Deployer (sync)", function() { } } deployer = new Deployer(options); - reporter = new Reporter(deployer); + reporter = new Reporter(); + reporter.setDeployer(deployer); + reporter.setMigration(mockMigration); + reporter.listen(); }); afterEach(() => { @@ -76,12 +84,11 @@ describe("Deployer (sync)", function() { const id = await example.id(); assert(id === 'Example' ); - assert(output.includes('Example')); assert(output.includes('Deploying')); assert(output.includes('transaction hash')); assert(output.includes('address')); - assert(output.includes('gas usage')); + assert(output.includes('gas used')); }); it('deploy().then', async function(){ @@ -105,7 +112,7 @@ describe("Deployer (sync)", function() { assert(usesExampleId === 'UsesExample' ); assert(other === Example.address); - assert(output.includes('Replacing')) + assert(output.includes('Replacing')); assert(output.includes('Deploying')); assert(output.includes('Example')); assert(output.includes('UsesExample')); @@ -242,9 +249,7 @@ describe("Deployer (sync)", function() { await deployer.start(); utils.stopAutoMine(); - assert(output.includes(`Block number: ${startBlock + 1}`)); - assert(output.includes(`Wait: 1`)); - assert(output.includes(`Block number: ${startBlock + 2}`)); - assert(output.includes(`Wait: 2`)) + // We used to test output here but the ora spinner doesn't use the logger + // Keeping this test just to run the logic, make sure it's not crashing. }); }); diff --git a/packages/truffle-deployer/test/errors.js b/packages/truffle-deployer/test/errors.js index 6d320eb2ad9..46d7f44975a 100644 --- a/packages/truffle-deployer/test/errors.js +++ b/packages/truffle-deployer/test/errors.js @@ -3,6 +3,7 @@ const contract = require("truffle-contract"); const Web3 = require("web3"); const assert = require("assert"); const Reporter = require("truffle-reporters").migrationsV5; +const EventEmitter = require('events'); const Deployer = require("../index"); const utils = require('./helpers/utils'); @@ -24,6 +25,10 @@ describe("Error cases", function() { vmErrorsOnRPCResponse: false }); + const mockMigration = { + emitter: new EventEmitter() + } + const web3 = new Web3(provider); beforeEach(async function() { @@ -62,7 +67,10 @@ describe("Error cases", function() { } } deployer = new Deployer(options); - reporter = new Reporter(deployer); + reporter = new Reporter(); + reporter.setDeployer(deployer); + reporter.setMigration(mockMigration); + reporter.listen(); }); afterEach(() => { @@ -82,8 +90,9 @@ describe("Error cases", function() { await deployer.start(); assert.fail(); } catch( err ) { - assert(output.includes('Error')); + assert(output.includes('Deployment Failed')); assert(output.includes('IsLibrary')); + assert(output.includes('has no address')); } }); @@ -98,9 +107,10 @@ describe("Error cases", function() { await deployer.start(); assert.fail(); } catch(err){ + assert(output.includes('Deployment Failed')); assert(output.includes('Abstract')); - assert(output.includes('Error')); assert(output.includes('interface')); + assert(output.includes('cannot be deployed')); } }); diff --git a/packages/truffle-reporters/reporters/migrations-V5/reporter.js b/packages/truffle-reporters/reporters/migrations-V5/reporter.js index e38618e5936..4ae5188bd22 100644 --- a/packages/truffle-reporters/reporters/migrations-V5/reporter.js +++ b/packages/truffle-reporters/reporters/migrations-V5/reporter.js @@ -356,7 +356,10 @@ class Reporter { this.currentAddress = this.from; this.deployments++; - this.summary[this.currentFileIndex].deployments.push(data); + if (this.summary[this.currentFileIndex]){ + this.summary[this.currentFileIndex].deployments.push(data); + } + message = this.messages.steps('deployed', data); } else { message = this.messages.steps('reusing', data); From fb288f014512670b8b6e34b86f0963ab0cc94ed1 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 11 Jul 2018 23:07:36 -0700 Subject: [PATCH 73/74] Remove test reporter --- .../truffle-deployer/test/helpers/reporter.js | 292 ------------------ 1 file changed, 292 deletions(-) delete mode 100644 packages/truffle-deployer/test/helpers/reporter.js diff --git a/packages/truffle-deployer/test/helpers/reporter.js b/packages/truffle-deployer/test/helpers/reporter.js deleted file mode 100644 index ddfc65b570a..00000000000 --- a/packages/truffle-deployer/test/helpers/reporter.js +++ /dev/null @@ -1,292 +0,0 @@ -/** - * Example reporter class that emulates the classic logger behavior - */ - -class Reporter { - constructor(deployer){ - this.deployingMany = false; - this.logConfirmations = false; - this.deployer = deployer; - this.separator = '\n'; - this.listen(); - } - - listen(){ - this.deployer.emitter.on('preDeploy', this.preDeploy.bind(this)); - this.deployer.emitter.on('postDeploy', this.postDeploy.bind(this)); - this.deployer.emitter.on('preDeployMany', this.preDeployMany.bind(this)); - this.deployer.emitter.on('postDeployMany', this.postDeployMany.bind(this)); - this.deployer.emitter.on('deployFailed', this.deployFailed.bind(this)); - this.deployer.emitter.on('linking', this.linking.bind(this)); - this.deployer.emitter.on('error', this.error.bind(this)); - this.deployer.emitter.on('transactionHash', this.hash.bind(this)); - this.deployer.emitter.on('receipt', this.receipt.bind(this)); - this.deployer.emitter.on('confirmation', this.confirmation.bind(this)); - this.deployer.emitter.on('block', this.block.bind(this)); - } - - async preDeploy(payload){ - let message; - (payload.deployed) - ? message = this.messages('replacing', payload.contract, payload.deployed) - : message = this.messages('deploying', payload.contract, payload.deployed); - - !this.deployingMany && this.deployer.logger.log(message); - } - - async postDeploy(payload){ - let message; - (payload.deployed) - ? message = this.messages('deployed', payload.contract.contractName, payload.instance.address) - : message = this.messages('notDeployed', payload.contract.contractName, payload.instance.address); - - this.deployer.logger.log(message); - } - - async preDeployMany(batch){ - let message = this.messages('many'); - - this.deployingMany = true; - this.deployer.logger.log(message); - - batch.forEach(item => { - Array.isArray(item) - ? message = this.messages('listMany', item[0].contractName) - : message = this.messages('listMany', item.contractName) - - this.deployer.logger.log(message); - }) - - this.deployer.logger.log(this.separator); - } - - async postDeployMany(){ - this.deployingMany = false; - } - - async deployFailed(payload){ - const message = await this.processDeploymentError(payload); - this.deployer.logger.error(message) - } - - linking(payload){ - let message = this.messages('linking', payload.libraryName, payload.libraryAddress, payload.contractName); - this.deployer.logger.log(message); - } - - async error(payload){ - let message = this.messages(payload.type, payload.contract); - this.deployer.logger.error(message); - } - - async hash(payload){ - let message = this.messages('hash', payload.transactionHash, payload.contractName); - this.deployer.logger.log(message); - } - - async receipt(payload){ - let message = this.messages('receipt', payload.receipt.gasUsed, payload.contractName); - this.deployer.logger.log(message); - } - - async confirmation(payload){ - let message = this.messages('confirmation', payload.num, payload.receipt, payload.contractName); - this.logConfirmations && this.deployer.logger.log(message); - } - - async block(payload){ - let message = this.messages('block', payload.blockNumber, payload.blocksWaited); - this.deployer.logger.log(message); - } - - underline(msg){ - const ul = '-'.repeat(msg.length); - return `\n${msg}\n${ul}`; - } - - messages(kind, ...args){ - const prefix = '\nError:'; - - args[0] = args[0] || {}; - args[1] = args[1] || {}; - args[2] = args[2] || {}; - args[3] = args[3] || {}; - - const kinds = { - - // --------------------------------------- Errors -------------------------------------------- - - noLibName: `${prefix} Cannot link a library with no name.\n`, - noLibAddress: `${prefix} "${args[0].contractName}" has no address. Has it been deployed?\n`, - - noBytecode: `${prefix} "${args[0].contractName}" ` + - `is an abstract contract or an interface and cannot be deployed\n` + - ` * Hint: just import the contract into the '.sol' file that uses it.\n`, - - intWithGas: `${prefix} "${args[0].contractName}" ran out of gas ` + - `(using a value you set in your network config or deployment parameters.)\n` + - ` * Block limit: ${args[2]}\n` + - ` * Gas sent: ${args[1]}\n`, - - intNoGas: `${prefix} "${args[0].contractName}" ran out of gas ` + - `(using Truffle's estimate.)\n` + - ` * Block limit: ${args[1]}\n` + - ` * Gas sent: ${args[2]}\n` + - ` * Try:\n` + - ` + Setting a higher gas estimate multiplier for this contract\n` + - ` + Using the solc optimizer settings in 'truffle.js'\n` + - ` + Making your contract smaller\n` + - ` + Making your contract constructor more efficient\n` + - ` + Setting a higher network block limit if you are on a\n` + - ` private network or test client (like ganache).\n`, - - oogNoGas: `${prefix} "${args[0].contractName}" ran out of gas. Something in the constructor ` + - `(ex: infinite loop) caused gas estimation to fail. Try:\n` + - ` * Making your contract constructor more efficient\n` + - ` * Setting the gas manually in your config or a deployment parameter\n` + - ` * Using the solc optimizer settings in 'truffle.js'\n` + - ` * Setting a higher network block limit if you are on a\n` + - ` private network or test client (like ganache).\n`, - - rvtReason: `Revert with string error not implemented yet.`, - asrtReason: `Assert with string error not implemented yet.`, - - rvtNoReason: `${prefix} "${args[0].contractName}" hit a require or revert statement ` + - `somewhere in its constructor. Try:\n` + - ` * Verifying that your constructor params satisfy all require conditions.\n` + - ` * Adding reason strings to your require statements.\n`, - - asrtNoReason: `${prefix} "${args[0].contractName}" hit an invalid opcode while deploying. Try:\n` + - ` * Verifying that your constructor params satisfy all assert conditions.\n` + - ` * Verifying your constructor code doesn't access an array out of bounds.\n` + - ` * Adding reason strings to your assert statements.\n`, - - noMoney: `${prefix} "${args[0].contractName}" could not deploy due to insufficient funds\n` + - ` * Account: ${args[1]}\n` + - ` * Balance: ${args[2]} wei\n` + - ` * Message: ${args[3]}\n` + - ` * Try:\n` + - ` + Using an adequately funded account\n` + - ` + If you are using a local Geth node, verify that your node is synced.\n`, - - blockWithGas: `${prefix} "${args[0].contractName}" exceeded the block limit ` + - `(with a gas value you set).\n` + - ` * Block limit: ${args[2]}\n` + - ` * Gas sent: ${args[1]}\n` + - ` * Try:\n` + - ` + Sending less gas.\n` + - ` + Setting a higher network block limit if you are on a\n` + - ` private network or test client (like ganache).\n`, - - blockNoGas: `${prefix} "${args[0].contractName}" exceeded the block limit ` + - `(using Truffle's estimate).\n` + - ` * Block limit: ${args[1]}\n` + - ` * Report this error in the Truffle issues on Github. It should not happen.\n` + - ` * Try: setting gas manually in 'truffle.js' or as parameter to 'deployer.deploy'\n`, - - nonce: `${prefix} "${args[0].contractName}" received: ${args[1]}.\n` + - ` * This error is common when Infura is under heavy network load.\n` + - ` * Try: setting the 'confirmations' key in your network config\n` + - ` to wait for several block confirmations between each deployment.\n`, - - default: `${prefix} "${args[0].contractName}" -- ${args[1]}.\n`, - - // ------------------------------------ Successes -------------------------------------------- - - deploying: this.underline('Deploying'), - replacing: this.underline('Replacing'), - reusing: this.underline('Re-using'), - many: this.underline('Deploying Batch'), - linking: this.underline('Linking') + '\n' + - `${args[2]}`.padEnd(20) + ` > ${args[0]}`.padEnd(25) + `(${args[1]})`, - - listMany: `* ${args[0]}`, - deployed: `${args[0]}`.padEnd(20) + ' > address:'.padEnd(25) + args[1], - hash: `${args[1]}`.padEnd(20) + ' > transaction hash:'.padEnd(25) + args[0], - receipt: `${args[1]}`.padEnd(20) + ' > gas usage:'.padEnd(25) + args[0], - confirmation: `${args[2]}`.padEnd(20) + ' > confirmation number:'.padEnd(25) + args[0], - block: `Block number: ${args[0]} Wait: ${args[1]}`, - } - - return kinds[kind]; - } - - async processDeploymentError(payload){ - let message; - - const error = payload.estimateError || payload.error; - - const errors = { - INT: error.message.includes('base fee') || error.message.includes('intrinsic'), - OOG: error.message.includes('out of gas'), - RVT: error.message.includes('revert'), - ETH: error.message.includes('funds'), - BLK: error.message.includes('block gas limit'), - NCE: error.message.includes('nonce'), - INV: error.message.includes('invalid opcode'), - } - - let type = Object.keys(errors).find(key => errors[key]); - - switch (type) { - case 'INT': - (payload.gas) - ? message = this.messages('intWithGas', payload.contract, payload.gas, payload.blockLimit) - : message = this.messages('intNoGas', payload.contract, payload.blockLimit, payload.estimate); - - this.deployer.logger.error(message); - break; - - case 'OOG': - (payload.gas) - ? message = this.messages('intWithGas', payload.contract, payload.gas, payload.blockLimit) - : message = this.messages('oogNoGas', payload.contract, payload.blockLimit, payload.estimate); - - this.deployer.logger.error(message); - break; - - case 'RVT': - (payload.reason) - ? message = this.messages('rvtReason', payload.contract, payload.reason) - : message = this.messages('rvtNoReason', payload.contract); - - this.deployer.logger.error(message); - break; - - case 'INV': - (payload.reason) - ? message = this.messages('asrtReason', payload.contract, payload.reason) - : message = this.messages('asrtNoReason', payload.contract); - - this.deployer.logger.error(message); - break; - - case 'BLK': - (payload.gas) - ? message = this.messages('blockWithGas', payload.contract, payload.gas, payload.blockLimit) - : message = this.messages('blockNoGas', payload.contract, payload.blockLimit) - - this.deployer.logger.error(message); - break; - - case 'ETH': - let balance = await payload.contract.web3.eth.getBalance(payload.from); - balance = balance.toString(); - message = this.messages('noMoney', payload.contract, payload.from, balance, error.message); - this.deployer.logger.error(message); - break; - - case 'NCE': - message = this.messages('nonce', payload.contract, error.message); - this.deployer.logger.error(message); - break; - - default: - message = this.messages('default', payload.contract, error.message); - this.deployer.logger.error(message); - } - } -} - -module.exports = Reporter; - From 393f99203fa6abe8427333a7dfdff91913d0f137 Mon Sep 17 00:00:00 2001 From: cgewecke Date: Wed, 11 Jul 2018 23:18:55 -0700 Subject: [PATCH 74/74] Remove unused batch deployment logic in reporter --- .../reporters/migrations-V5/reporter.js | 30 +------------------ 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/packages/truffle-reporters/reporters/migrations-V5/reporter.js b/packages/truffle-reporters/reporters/migrations-V5/reporter.js index 4ae5188bd22..9c593ff17de 100644 --- a/packages/truffle-reporters/reporters/migrations-V5/reporter.js +++ b/packages/truffle-reporters/reporters/migrations-V5/reporter.js @@ -24,7 +24,6 @@ const MigrationsMessages = require('./messages'); class Reporter { constructor(){ - this.deployingMany = false; this.deployer = null; this.migration = null; this.currentGasTotal = 0; @@ -72,8 +71,6 @@ class Reporter { // Deployment this.deployer.emitter.on('preDeploy', this.preDeploy.bind(this)); this.deployer.emitter.on('postDeploy', this.postDeploy.bind(this)); - this.deployer.emitter.on('preDeployMany', this.preDeployMany.bind(this)); - this.deployer.emitter.on('postDeployMany', this.postDeployMany.bind(this)); this.deployer.emitter.on('deployFailed', this.deployFailed.bind(this)); this.deployer.emitter.on('linking', this.linking.bind(this)); this.deployer.emitter.on('error', this.error.bind(this)); @@ -424,38 +421,13 @@ class Reporter { } /** - * Fired on Web3Promievent 'confirmation' event. Begins running a UI - * a block / time counter. + * Fired on Web3Promievent 'confirmation' event. * @param {Object} data */ async confirmation(data){ let message = this.messages.steps('confirmation', data); this.deployer.logger.log(message); } - - // ---------------------------- Batch Handlers -------------------------------------------------- - - async preDeployMany(batch){ - let message = this.messages.steps('many'); - - this.deployingMany = true; - this.deployer.logger.log(message); - - batch.forEach(item => { - Array.isArray(item) - ? message = this.messages.steps('listMany', item[0]) - : message = this.messages.steps('listMany', item) - - this.deployer.logger.log(message); - }) - - this.deployer.logger.log(this.separator); - } - - async postDeployMany(){ - this.deployingMany = false; - } - } module.exports = Reporter;