Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

New Migrations #1028

Merged
merged 76 commits into from
Jul 12, 2018
Merged
Show file tree
Hide file tree
Changes from 64 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
fdff074
Migrations to ES6
cgewecke Jun 8, 2018
61af79f
Add migrations emitter and reporter draft
cgewecke Jun 8, 2018
f1f96bb
Add reporter details, formatting
cgewecke Jun 9, 2018
10242e4
Working async/await and basic reporter format
cgewecke Jun 12, 2018
3e62aac
Remove unused code
cgewecke May 16, 2018
d6cab54
Add test fixtures, deployer tests
cgewecke May 18, 2018
9c71d07
Convert deployer to class
cgewecke May 18, 2018
9b3d10b
Convert DeferredChain to class
cgewecke May 18, 2018
995f540
Add async/await test cases
cgewecke May 18, 2018
f53e932
First draft: async-eventemitter
cgewecke May 19, 2018
2d15b92
Second draft: reporters / working
cgewecke May 24, 2018
e16cd4b
Third draft: errors
cgewecke Jun 6, 2018
8348d62
Revert changes to deferredchain / add test
cgewecke Jun 6, 2018
c32b116
Migrations scenario tests (draft)
cgewecke Jun 12, 2018
a409d36
Update yarn.lock
cgewecke Jun 14, 2018
deaebcc
Add dry run report (draft)
cgewecke Jun 17, 2018
40dbbd3
Copy gasPrice and gasLimit on fork
cgewecke Jun 17, 2018
fd99765
Move reporter to outer scope
cgewecke Jun 17, 2018
6125d4a
Upgrade ganche-core dev dep to 2.1.2
cgewecke Jun 17, 2018
1513236
Add reason string handling (truffle-contract)
cgewecke Jun 18, 2018
63ae9eb
Fix extra require/yarn.lock
cgewecke Jun 18, 2018
ca16e6f
Fix err.return check
cgewecke Jun 18, 2018
de626c8
Add geth specific error handling
cgewecke Jun 18, 2018
76ca3ae
Add revert with reason test
cgewecke Jun 19, 2018
baad4fa
Upgrade ganache-cli to 6.1.3 (everywhere)
cgewecke Jun 19, 2018
e6e0ea4
Add low balance test
cgewecke Jun 19, 2018
ee056cc
Make reason string work
cgewecke Jun 20, 2018
fe5bb71
Restructure Migration callbacks as class methods
cgewecke Jun 20, 2018
d63edcb
Filter tests by geth/ganache tags in CI
cgewecke Jun 20, 2018
d05d236
Add block polling logic to deployer (conf & tx wait)
cgewecke Jun 21, 2018
e61eb36
Pretty print docker list correctly
cgewecke Jun 16, 2018
0363f91
Evaluate the `await` keyword in the console if supported by node vers…
tcoulter Jun 19, 2018
42ab1bb
Remove unnecessary block; rename newSource to source.
tcoulter Jun 19, 2018
48f7389
Make send accept txParams / prefer user-defined send
cgewecke Jun 20, 2018
742652b
Fix merge breakage
cgewecke Jun 21, 2018
1a7caea
Add confirmations key to config, production scenario sources
cgewecke Jun 24, 2018
b2da71b
Get confirmations display working
cgewecke Jun 24, 2018
c73f035
production-mode draft
cgewecke Jun 25, 2018
e7a5ba8
Working production mode & test
cgewecke Jun 25, 2018
332f2da
Report value / include in cost
cgewecke Jun 26, 2018
b0d0cce
Silence hd-wallet-provider errors on exit
cgewecke Jun 26, 2018
22053ec
Fix truffle-contract eth_call res handling
cgewecke Jun 26, 2018
0c4f8cd
Add "blocks waited" display
cgewecke Jun 29, 2018
4b422a9
Add interactive confirmation post dry-run (production-mode)
cgewecke Jun 30, 2018
dc699c9
Fix reporter on overwrite=false & test
cgewecke Jun 30, 2018
537bee4
Remove stray error log / clarify error exit
cgewecke Jul 1, 2018
b208571
Restructure reporter, add comments
cgewecke Jul 1, 2018
314eea7
Separate migrate and migration classes
cgewecke Jul 1, 2018
79e9d54
Support timeoutBlocks by network w/ block-polling
cgewecke Jul 1, 2018
44370b5
Add missing new-lines
cgewecke Jul 2, 2018
2f70102
Fix interactivity / block timer (for infura)
cgewecke Jul 2, 2018
4f69fd2
Cleanup after rebase
gnidan Jul 2, 2018
9fd7389
Merge branch 'next' of github.com:trufflesuite/truffle into async-dep…
gnidan Jul 2, 2018
fba3660
Replace ora dependency
cgewecke Jul 2, 2018
fa9c802
Lengthen polling interval (again)
cgewecke Jul 2, 2018
db2ca10
Stop using handle.destroy on exit
cgewecke Jul 3, 2018
9c98ee8
Fix newlines / error msg spacing
cgewecke Jul 3, 2018
663c96c
Deprecate batch deployments
cgewecke Jul 3, 2018
f325fd2
Clarify public/private methods in deployment class
cgewecke Jul 3, 2018
b8a5f17
Clarify extractReasonString conditional logic
cgewecke Jul 3, 2018
73f0cfa
Stop using deployer array syntax for solidity test setup
cgewecke Jul 4, 2018
2af8387
Clean up migrate command
cgewecke Jul 4, 2018
54fd631
Fix production tests for Geth
cgewecke Jul 4, 2018
d2c03e0
Make sure fork tracks parent block gasLimit
cgewecke Jul 5, 2018
509a543
Add reason string to error messages
cgewecke Jul 12, 2018
d0ce12e
Clean up reason string logic
cgewecke Jul 12, 2018
0e1f8dd
Use canonical migrations name
cgewecke Jul 12, 2018
5d39172
Add note about --exit flag
cgewecke Jul 12, 2018
d9ea3f3
Cleaup if copy fails
cgewecke Jul 12, 2018
c754c89
Add test for long reason string
cgewecke Jul 12, 2018
e4d3da9
Remove unused logic for 50 blocks override
cgewecke Jul 12, 2018
05719d4
Move migrations reporter to truffle-reporters
cgewecke Jul 12, 2018
9218386
Fix deployer tests
cgewecke Jul 12, 2018
fb288f0
Remove test reporter
cgewecke Jul 12, 2018
393f992
Remove unused batch deployment logic in reporter
cgewecke Jul 12, 2018
59067fa
Merge pull request #1099 from trufflesuite/add-truffle-reporters
cgewecke Jul 12, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
"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",
"ci": "./scripts/ci.sh"
"test": "lerna run test --stream --concurrency=1 -- --colors",
"ci": "./scripts/ci.sh",
"geth": "./scripts/geth.sh"
},
"devDependencies": {
"lerna": "^2.11.0"
Expand All @@ -14,5 +15,6 @@
"packages": [
"packages/*"
]
}
},
"dependencies": {}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

13 changes: 3 additions & 10 deletions packages/truffle-box/test/unbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions packages/truffle-compile/scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
41 changes: 40 additions & 1 deletion packages/truffle-config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function Config(truffle_directory, working_directory, network) {
var default_tx_values = {
gas: 6721975,
gasPrice: 100000000000, // 100 Shannon,
from: null
from: null,
};

this._values = {
Expand All @@ -28,6 +28,9 @@ function Config(truffle_directory, working_directory, network) {
gas: null,
gasPrice: null,
from: null,
confirmations: 0,
timeoutBlocks: 0,
production: false,
build: null,
resolver: null,
artifactor: null,
Expand Down Expand Up @@ -167,6 +170,42 @@ 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[<network name>].provider")
}
},
confirmations: {
get: function() {
try {
return self.network_config.confirmations;
} catch (e) {
return 0;
}
},
set: function(val) {
throw new Error("Don't set config.confirmations directly. Instead, set config.networks and then config.networks[<network name>].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[<network name>].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[<network name>].timeoutBlocks")
}
}
};

Expand Down
116 changes: 90 additions & 26 deletions packages/truffle-contract/lib/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ var StatusError = require("./statuserror");
var handlers = require("./handlers");
var override = require("./override");

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
Expand All @@ -20,23 +21,44 @@ 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 => {
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());
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 && (response.error || response.result)) {
reason = execute.extractReason(response, web3)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the if just be moved into extractReason here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah so provider.send() still only uses a callback

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


web3
.eth
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of prefer the way you had it with web3.eth. I'd also be into web3.eth.estimateGas(params) all on one line, but I can see why separating it looks nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

.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});
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all really pretty

})
})
},

Expand Down Expand Up @@ -65,6 +87,29 @@ 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(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 (data[hash].return && data[hash].return.includes('0x08c379a0')){
return web3.eth.abi.decodeParameter('string', data[hash].return.slice(10))
}

} else if (isString && res.result.includes('0x08c379a0')){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's that value? I think let's make this a const at the top of the function so it gets a name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm. yes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess derived from this axic comment in the EIP ? 🙂

Now we could instead just assume that there is a generic error with a signature of Error(string), which hashes to 08c379a0 followed by an ABI encoded string, therefore revert("oh noes") would result in the data 08c379a0 00...20 00..07 6f68206e6f6573..00

return web3.eth.abi.decodeParameter('string', res.result.slice(10))
}
},

/**
* Parses function arguments to discover if the terminal argument specifies the `defaultBlock`
* to execute a call at.
Expand Down Expand Up @@ -131,6 +176,7 @@ var execute = {
var args = Array.prototype.slice.call(arguments);
var params = utils.getTxParams.call(constructor, args);
var promiEvent = new Web3PromiEvent();
var reason;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't appear to be used


var context = {
contract: constructor, // Can't name this field `constructor` or `_constructor`
Expand All @@ -145,10 +191,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;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this getting attached at the receipt handler? Check

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is - we also have tests for reason string handling across a matrix of the following:

  • deploy and send
  • both states of vmErrorsOnResponse
  • non-existence of reason / geth
  • ganache as provider and server (the former at truffle-contract / the latter at scenario)

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) ? result.error.reason : null;
override.start.call(constructor, context, err)
});

handlers.setup(deferred, context);
})
.catch(promiEvent.reject)
Expand All @@ -173,6 +230,7 @@ var execute = {
var web3 = constructor.web3;
var params = utils.getTxParams.call(constructor, args);
var deferred;
var reason;

var options = {
data: constructor.binary,
Expand All @@ -185,14 +243,17 @@ 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)
}

Expand All @@ -201,9 +262,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);
},

Expand Down
1 change: 1 addition & 0 deletions packages/truffle-contract/lib/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little bit worried about attaching reason to these error objects like this. It feels like an anti-pattern, but maybe I'm just afraid of statefulness. Let's keep an eye on this in the beta, in case it causes unforeseen complications.

Pardon me if I haven't seen it yet, but it might be worth making a special note somewhere in a comment, so we can encourage future work to be mindful of pre- vs. post-reason-attach errors. I know you've written a good matrix of tests around this, so it's probably safe enough, esp. since it's not likely to go without undefined checks anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved this into the StatusError file where it (possibly) looks less arbitrary. Truffle-contract has actually attached quite a bit of stuff to the error traditionally - like the txHash for example (which we continue to do for backwards compatibility).

return context.promiEvent.reject(error)
}

Expand Down
38 changes: 27 additions & 11 deletions packages/truffle-contract/lib/override.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -27,23 +28,19 @@ 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;
}

constructor.web3.eth.getTransactionReceipt(context.transactionHash)
.then(result => {
if (!result) return;

//self.removeListener('data', listener);

(result.contractAddress)
? constructor
.at(result.contractAddress)
Expand All @@ -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);
*/
},
}

Expand Down
Loading