2017-03-29 17:50:05 +00:00
let async = require ( 'async' ) ;
2018-01-05 20:10:47 +00:00
//require("../utils/debug_util.js")(__filename, async);
2018-05-18 20:51:03 +00:00
let utils = require ( '../utils/utils.js' ) ;
2017-02-19 18:17:28 +00:00
2017-03-29 17:50:05 +00:00
let RunCode = require ( '../core/runCode.js' ) ;
2017-02-19 18:17:28 +00:00
2017-03-29 17:50:05 +00:00
let DeployTracker = require ( './deploy_tracker.js' ) ;
2017-08-03 23:29:09 +00:00
let CodeGenerator = require ( './code_generator.js' ) ;
2016-09-25 01:10:47 +00:00
2017-03-30 11:12:39 +00:00
class Deploy {
constructor ( options ) {
2018-05-18 22:31:47 +00:00
this . blockchain = options . blockchain ;
this . web3 = this . blockchain . web3 ;
2017-03-30 11:12:39 +00:00
this . contractsManager = options . contractsManager ;
this . logger = options . logger ;
2018-02-27 20:40:05 +00:00
this . events = options . events ;
2017-03-30 11:12:39 +00:00
this . env = options . env ;
2018-01-05 20:10:47 +00:00
this . chainConfig = options . chainConfig ;
2018-01-17 23:04:19 +00:00
this . plugins = options . plugins ;
2018-01-13 16:38:10 +00:00
this . gasLimit = options . gasLimit ;
2018-05-08 15:30:46 +00:00
this . events . setCommandHandler ( "contracts:contract" , ( contractName , cb ) => {
cb ( this . contractsManager . getContract ( contractName ) ) ;
} ) ;
2018-01-05 20:10:47 +00:00
}
2017-03-30 11:12:39 +00:00
2018-01-05 20:10:47 +00:00
initTracker ( cb ) {
2017-03-30 11:12:39 +00:00
this . deployTracker = new DeployTracker ( {
2018-01-05 20:10:47 +00:00
logger : this . logger , chainConfig : this . chainConfig , web3 : this . web3 , env : this . env
} , cb ) ;
2016-10-21 03:31:42 +00:00
}
2018-03-05 01:07:39 +00:00
determineArguments ( suppliedArgs , contract ) {
2017-03-30 11:12:39 +00:00
let realArgs = [ ] , l , arg , contractName , referedContract ;
2018-03-05 01:07:39 +00:00
let args = suppliedArgs ;
if ( ! Array . isArray ( args ) ) {
args = [ ] ;
let abi = contract . abiDefinition . find ( ( abi ) => abi . type === 'constructor' ) ;
for ( let input of abi . inputs ) {
let inputValue = suppliedArgs [ input . name ] ;
if ( ! inputValue ) {
2018-05-08 21:49:46 +00:00
this . logger . error ( _ _ ( "{{inputName}} has not been defined for {{className}} constructor" , { inputName : input . name , className : contract . className } ) ) ;
2018-03-05 01:07:39 +00:00
}
args . push ( inputValue || "" ) ;
}
}
for ( l = 0 ; l < args . length ; l ++ ) {
arg = args [ l ] ;
2017-03-30 11:12:39 +00:00
if ( arg [ 0 ] === "$" ) {
contractName = arg . substr ( 1 ) ;
referedContract = this . contractsManager . getContract ( contractName ) ;
realArgs . push ( referedContract . deployedAddress ) ;
2018-03-04 23:46:12 +00:00
} else if ( Array . isArray ( arg ) ) {
let subRealArgs = [ ] ;
for ( let sub _arg of arg ) {
if ( sub _arg [ 0 ] === "$" ) {
contractName = sub _arg . substr ( 1 ) ;
referedContract = this . contractsManager . getContract ( contractName ) ;
subRealArgs . push ( referedContract . deployedAddress ) ;
} else {
subRealArgs . push ( sub _arg ) ;
}
}
realArgs . push ( subRealArgs ) ;
2017-03-30 11:12:39 +00:00
} else {
realArgs . push ( arg ) ;
}
}
2016-09-28 01:13:54 +00:00
2017-03-30 11:12:39 +00:00
return realArgs ;
2016-09-28 01:13:54 +00:00
}
2017-03-30 11:12:39 +00:00
checkAndDeployContract ( contract , params , callback ) {
let self = this ;
let realArgs ;
contract . error = false ;
if ( contract . deploy === false ) {
2018-02-27 20:40:05 +00:00
self . events . emit ( 'contractsState' , self . contractsManager . contractsState ( ) ) ;
2017-03-30 11:12:39 +00:00
return callback ( ) ;
}
2016-09-25 01:10:47 +00:00
2018-03-05 01:07:39 +00:00
realArgs = self . determineArguments ( params || contract . args , contract ) ;
2016-09-27 04:55:35 +00:00
2017-12-28 13:27:20 +00:00
if ( contract . address !== undefined ) {
2018-03-05 00:18:39 +00:00
try {
2018-05-18 20:51:03 +00:00
utils . toChecksumAddress ( contract . address ) ;
2018-03-05 00:18:39 +00:00
} catch ( e ) {
2018-05-08 21:49:46 +00:00
self . logger . error ( _ _ ( "error deploying %s" , contract . className ) ) ;
2018-03-05 00:18:39 +00:00
self . logger . error ( e . message ) ;
contract . error = e . message ;
self . events . emit ( 'contractsState' , self . contractsManager . contractsState ( ) ) ;
return callback ( e . message ) ;
}
2017-03-30 11:12:39 +00:00
contract . deployedAddress = contract . address ;
2018-05-08 21:49:46 +00:00
self . logger . info ( contract . className . bold . cyan + _ _ ( " already deployed at " ) . green + contract . address . bold . cyan ) ;
2018-01-05 20:10:47 +00:00
if ( this . deployTracker ) {
self . deployTracker . trackContract ( contract . className , contract . realRuntimeBytecode , realArgs , contract . address ) ;
self . deployTracker . save ( ) ;
}
2018-02-27 20:40:05 +00:00
self . events . emit ( 'contractsState' , self . contractsManager . contractsState ( ) ) ;
2017-03-30 11:12:39 +00:00
return callback ( ) ;
}
2016-10-02 21:57:33 +00:00
2018-01-05 20:10:47 +00:00
if ( ! this . deployTracker ) {
return self . contractToDeploy ( contract , params , callback ) ;
}
2017-12-28 13:27:20 +00:00
let trackedContract = self . deployTracker . getContract ( contract . className , contract . realRuntimeBytecode , realArgs ) ;
2018-01-05 20:10:47 +00:00
if ( ! trackedContract ) {
return self . contractToDeploy ( contract , params , callback ) ;
}
this . web3 . eth . getCode ( trackedContract . address , function ( _getCodeErr , codeInChain ) {
if ( codeInChain !== "0x" ) {
self . contractAlreadyDeployed ( contract , trackedContract , callback ) ;
} else {
self . contractToDeploy ( contract , params , callback ) ;
}
} ) ;
}
2017-02-18 21:53:49 +00:00
2018-01-05 20:10:47 +00:00
contractAlreadyDeployed ( contract , trackedContract , callback ) {
const self = this ;
2018-05-08 21:49:46 +00:00
self . logger . info ( contract . className . bold . cyan + _ _ ( " already deployed at " ) . green + trackedContract . address . bold . cyan ) ;
2018-01-05 20:10:47 +00:00
contract . deployedAddress = trackedContract . address ;
2018-02-27 20:40:05 +00:00
self . events . emit ( 'contractsState' , self . contractsManager . contractsState ( ) ) ;
2018-01-05 20:10:47 +00:00
// always run contractCode so other functionality like 'afterDeploy' can also work
let codeGenerator = new CodeGenerator ( { contractsManager : self . contractsManager } ) ;
2018-01-13 16:38:10 +00:00
let contractCode = codeGenerator . generateContractCode ( contract , self . gasLimit ) ;
2018-05-18 19:55:34 +00:00
RunCode . doEval ( contractCode , { web3 : self . web3 } ) ;
2018-01-05 20:10:47 +00:00
return callback ( ) ;
}
contractToDeploy ( contract , params , callback ) {
const self = this ;
2018-03-05 01:07:39 +00:00
let realArgs = self . determineArguments ( params || contract . args , contract ) ;
2018-01-05 20:10:47 +00:00
this . deployContract ( contract , realArgs , function ( err , address ) {
if ( err ) {
return callback ( new Error ( err ) ) ;
}
self . deployTracker . trackContract ( contract . className , contract . realRuntimeBytecode , realArgs , address ) ;
self . deployTracker . save ( ) ;
2018-02-27 20:40:05 +00:00
self . events . emit ( 'contractsState' , self . contractsManager . contractsState ( ) ) ;
2017-12-27 16:40:21 +00:00
// always run contractCode so other functionality like 'afterDeploy' can also work
let codeGenerator = new CodeGenerator ( { contractsManager : self . contractsManager } ) ;
2018-01-13 16:38:10 +00:00
let contractCode = codeGenerator . generateContractCode ( contract , self . gasLimit ) ;
2018-05-18 19:55:34 +00:00
RunCode . doEval ( contractCode , { web3 : self . web3 } ) ;
2017-12-27 16:40:21 +00:00
2018-01-05 20:10:47 +00:00
if ( contract . onDeploy !== undefined ) {
2018-05-08 21:49:46 +00:00
self . logger . info ( _ _ ( 'executing onDeploy commands' ) ) ;
2016-08-14 12:04:34 +00:00
2018-01-13 16:38:10 +00:00
let contractCode = codeGenerator . generateContractCode ( contract , self . gasLimit ) ;
2018-05-18 19:55:34 +00:00
RunCode . doEval ( contractCode , { web3 : self . web3 } ) ;
2017-12-27 16:40:21 +00:00
2018-01-05 20:10:47 +00:00
let withErrors = false ;
let regex = /\$\w+/g ;
let onDeployCode = contract . onDeploy . map ( ( cmd ) => {
let realCmd = cmd . replace ( regex , ( match ) => {
let referedContractName = match . slice ( 1 ) ;
let referedContract = self . contractsManager . getContract ( referedContractName ) ;
if ( ! referedContract ) {
2018-05-08 21:49:46 +00:00
self . logger . error ( _ _ ( 'error executing onDeploy for ' ) + contract . className . cyan ) ;
self . logger . error ( referedContractName + _ _ ( ' does not exist' ) ) ;
self . logger . error ( _ _ ( "error running onDeploy: " ) + cmd ) ;
2018-01-05 20:10:47 +00:00
withErrors = true ;
return ;
}
if ( referedContract && referedContract . deploy === false ) {
2018-05-08 21:49:46 +00:00
self . logger . error ( _ _ ( 'error executing onDeploy for ' ) + contract . className . cyan ) ;
self . logger . error ( referedContractName + _ _ ( " exists but has been set to not deploy" ) ) ;
self . logger . error ( _ _ ( "error running onDeploy: " ) + cmd ) ;
2018-01-05 20:10:47 +00:00
withErrors = true ;
return ;
}
if ( referedContract && ! referedContract . deployedAddress ) {
2018-05-08 21:49:46 +00:00
self . logger . error ( _ _ ( 'error executing onDeploy for ' ) + contract . className . cyan ) ;
self . logger . error ( _ _ ( "couldn't find a valid address for %s has it been deployed?" , referedContractName ) ) ;
self . logger . error ( _ _ ( "error running onDeploy: " ) + cmd ) ;
2018-01-05 20:10:47 +00:00
withErrors = true ;
return ;
}
return referedContract . deployedAddress ;
2017-12-20 19:09:35 +00:00
} ) ;
2018-01-05 20:10:47 +00:00
return realCmd ;
} ) ;
2017-12-20 19:09:35 +00:00
2018-01-05 20:10:47 +00:00
if ( withErrors ) {
contract . error = "onDeployCmdError" ;
return callback ( new Error ( "error running onDeploy" ) ) ;
}
2017-12-20 19:09:35 +00:00
2018-01-05 20:10:47 +00:00
// TODO: convert to for to avoid repeated callback
for ( let cmd of onDeployCode ) {
2018-05-08 21:49:46 +00:00
self . logger . info ( _ _ ( "executing: " ) + cmd ) ;
2018-01-05 20:10:47 +00:00
try {
2018-05-18 19:55:34 +00:00
RunCode . doEval ( cmd , { web3 : self . web3 } ) ;
2018-01-05 20:10:47 +00:00
} catch ( e ) {
if ( e . message . indexOf ( "invalid opcode" ) >= 0 ) {
2018-05-08 21:49:46 +00:00
self . logger . error ( _ _ ( 'the transaction was rejected; this usually happens due to a throw or a require, it can also happen due to an invalid operation' ) ) ;
2017-12-28 13:27:20 +00:00
}
2018-01-05 20:10:47 +00:00
return callback ( new Error ( e ) ) ;
2017-12-28 13:27:20 +00:00
}
2017-03-30 11:12:39 +00:00
}
2018-01-05 20:10:47 +00:00
}
2015-06-28 02:20:07 +00:00
2018-01-05 20:10:47 +00:00
callback ( ) ;
} ) ;
2017-03-30 11:12:39 +00:00
}
2016-08-14 12:04:34 +00:00
2017-03-30 11:12:39 +00:00
deployContract ( contract , params , callback ) {
let self = this ;
2018-01-18 19:41:33 +00:00
let accounts = [ ] ;
2017-03-30 11:12:39 +00:00
let contractParams = ( params || contract . args ) . slice ( ) ;
2018-01-18 19:41:33 +00:00
let contractCode = contract . code ;
2018-05-18 22:31:47 +00:00
let deploymentAccount = self . blockchain . defaultAccount ( ) ;
2018-01-18 19:41:33 +00:00
let deployObject ;
async . waterfall ( [
function getAccounts ( next ) {
2018-05-18 22:31:47 +00:00
self . blockchain . getAccounts ( function ( err , _accounts ) {
2018-01-18 19:41:33 +00:00
if ( err ) {
2018-01-18 19:46:53 +00:00
return next ( new Error ( err ) ) ;
2018-01-18 19:41:33 +00:00
}
accounts = _accounts ;
2018-01-20 00:56:05 +00:00
// applying deployer account configuration, if any
if ( typeof contract . fromIndex == 'number' ) {
deploymentAccount = accounts [ contract . fromIndex ] ;
2018-01-20 01:38:28 +00:00
if ( deploymentAccount === undefined ) {
2018-05-08 21:49:46 +00:00
return next ( _ _ ( "error deploying" ) + " " + contract . className + ": " + _ _ ( "no account found at index" ) + " " + contract . fromIndex + _ _ ( " check the config" ) ) ;
2018-01-20 01:38:28 +00:00
}
2018-01-20 00:56:05 +00:00
}
if ( typeof contract . from == 'string' && typeof contract . fromIndex != 'undefined' ) {
2018-05-08 21:49:46 +00:00
self . logger . warn ( _ _ ( 'Both "from" and "fromIndex" are defined for contract' ) + ' "' + contract . className + '". ' + _ _ ( 'Using "from" as deployer account.' ) ) ;
2018-01-20 00:56:05 +00:00
}
if ( typeof contract . from == 'string' ) {
deploymentAccount = contract . from ;
}
2018-01-18 19:41:33 +00:00
deploymentAccount = deploymentAccount || accounts [ 0 ] ;
next ( ) ;
} ) ;
} ,
function doLinking ( next ) {
// Applying linked contracts
let contractsList = self . contractsManager . listContracts ( ) ;
for ( let contractObj of contractsList ) {
let filename = contractObj . filename ;
let deployedAddress = contractObj . deployedAddress ;
if ( deployedAddress ) {
deployedAddress = deployedAddress . substr ( 2 ) ;
}
let linkReference = '__' + filename + ":" + contractObj . className ;
if ( contractCode . indexOf ( linkReference ) < 0 ) {
continue ;
}
if ( linkReference . length > 40 ) {
2018-05-08 21:49:46 +00:00
return next ( new Error ( _ _ ( "{{linkReference}} is too long, try reducing the path of the contract ({{filename}}) and/or its name {{contractName}}" , { linkReference : linkReference , filename : filename , contractName : contractObj . className } ) ) ) ;
2018-01-18 19:41:33 +00:00
}
let toReplace = linkReference + "_" . repeat ( 40 - linkReference . length ) ;
if ( deployedAddress === undefined ) {
let libraryName = contractObj . className ;
2018-05-08 21:49:46 +00:00
return next ( new Error ( _ _ ( "{{contractName}} needs {{libraryName}} but an address was not found, did you deploy it or configured an address?" , { contractName : contract . className , libraryName : libraryName } ) ) ) ;
2018-01-18 19:41:33 +00:00
}
contractCode = contractCode . replace ( new RegExp ( toReplace , "g" ) , deployedAddress ) ;
2017-12-15 21:18:19 +00:00
}
2018-01-18 19:41:33 +00:00
// saving code changes back to contract object
contract . code = contractCode ;
next ( ) ;
} ,
function applyBeforeDeploy ( next ) {
let beforeDeployPlugins = self . plugins . getPluginsFor ( 'beforeDeploy' ) ;
//self.logger.info("applying beforeDeploy plugins...", beforeDeployPlugins.length);
async . eachSeries ( beforeDeployPlugins , ( plugin , eachPluginCb ) => {
2018-05-08 21:49:46 +00:00
self . logger . info ( _ _ ( "running beforeDeploy plugin %s ." , plugin . name ) ) ;
2018-01-18 19:41:33 +00:00
// calling each beforeDeploy handler declared by the plugin
async . eachSeries ( plugin . beforeDeploy , ( beforeDeployFn , eachCb ) => {
2018-04-25 19:01:01 +00:00
function beforeDeployCb ( resObj ) {
contract . code = resObj . contractCode ;
eachCb ( ) ;
}
2018-01-18 19:41:33 +00:00
beforeDeployFn ( {
embarkDeploy : self ,
pluginConfig : plugin . pluginConfig ,
deploymentAccount : deploymentAccount ,
contract : contract ,
2018-04-25 19:01:01 +00:00
callback : beforeDeployCb
} , beforeDeployCb ) ;
2018-01-17 23:34:38 +00:00
} , ( ) => {
2018-01-18 19:41:33 +00:00
//self.logger.info('All beforeDeploy handlers of the plugin has processed.');
eachPluginCb ( ) ;
2018-01-17 23:04:19 +00:00
} ) ;
2018-01-18 19:41:33 +00:00
} , ( ) => {
//self.logger.info('All beforeDeploy plugins has been processed.');
contractCode = contract . code ;
next ( ) ;
} ) ;
} ,
function createDeployObject ( next ) {
let contractObject = new self . web3 . eth . Contract ( contract . abiDefinition ) ;
try {
2018-04-13 19:48:19 +00:00
const dataCode = contractCode . startsWith ( '0x' ) ? contractCode : "0x" + contractCode ;
deployObject = contractObject . deploy ( { arguments : contractParams , data : dataCode } ) ;
2018-01-18 19:41:33 +00:00
} catch ( e ) {
2018-03-02 22:48:30 +00:00
if ( e . message . indexOf ( 'Invalid number of parameters for "undefined"' ) >= 0 ) {
2018-05-08 21:49:46 +00:00
return next ( new Error ( _ _ ( "attempted to deploy %s without specifying parameters" , contract . className ) ) ) ;
2018-01-18 19:41:33 +00:00
} else {
return next ( new Error ( e ) ) ;
2018-01-17 23:04:19 +00:00
}
2018-01-05 20:10:47 +00:00
}
2018-01-18 19:41:33 +00:00
next ( ) ;
} ,
2018-01-19 18:57:35 +00:00
function estimateCorrectGas ( next ) {
if ( contract . gas === 'auto' ) {
return deployObject . estimateGas ( ) . then ( ( gasValue ) => {
contract . gas = gasValue ;
next ( ) ;
} ) . catch ( next ) ;
}
next ( ) ;
} ,
2018-01-18 19:41:33 +00:00
function deployTheContract ( next ) {
2018-05-08 21:49:46 +00:00
self . logger . info ( _ _ ( "deploying" ) + " " + contract . className . bold . cyan + " " + _ _ ( "with" ) . green + " " + contract . gas + " " + _ _ ( "gas" ) . green ) ;
2018-01-18 19:41:33 +00:00
deployObject . send ( {
from : deploymentAccount ,
gas : contract . gas ,
gasPrice : contract . gasPrice
} ) . on ( 'receipt' , function ( receipt ) {
if ( receipt . contractAddress !== undefined ) {
2018-05-08 21:49:46 +00:00
self . logger . info ( contract . className . bold . cyan + " " + _ _ ( "deployed at" ) . green + " " + receipt . contractAddress . bold . cyan ) ;
2018-01-18 19:41:33 +00:00
contract . deployedAddress = receipt . contractAddress ;
contract . transactionHash = receipt . transactionHash ;
2018-02-27 20:40:05 +00:00
self . events . emit ( 'contractsState' , self . contractsManager . contractsState ( ) ) ;
2018-01-18 19:41:33 +00:00
return next ( null , receipt . contractAddress ) ;
}
2018-02-27 20:40:05 +00:00
self . events . emit ( 'contractsState' , self . contractsManager . contractsState ( ) ) ;
2018-01-18 19:41:33 +00:00
} ) . on ( 'error' , function ( error ) {
2018-02-27 20:40:05 +00:00
self . events . emit ( 'contractsState' , self . contractsManager . contractsState ( ) ) ;
2018-05-08 21:49:46 +00:00
return next ( new Error ( _ _ ( "error deploying" ) + " =" + contract . className + "= " + _ _ ( "due to error" ) + ": " + error . message ) ) ;
2018-01-18 19:41:33 +00:00
} ) ;
}
] , callback ) ;
2017-03-30 11:12:39 +00:00
}
deployAll ( done ) {
let self = this ;
2018-05-08 21:49:46 +00:00
this . logger . info ( _ _ ( "deploying contracts" ) ) ;
2018-04-26 18:15:43 +00:00
let contracts = this . contractsManager . listContracts ( ) ;
2017-03-30 11:12:39 +00:00
2018-04-26 18:15:43 +00:00
async . eachOfSeries ( contracts ,
2017-03-30 11:12:39 +00:00
function ( contract , key , callback ) {
self . logger . trace ( arguments ) ;
self . checkAndDeployContract ( contract , null , callback ) ;
} ,
2017-10-14 10:12:54 +00:00
function ( err , _results ) {
2017-03-30 11:12:39 +00:00
if ( err ) {
2018-05-08 21:49:46 +00:00
self . logger . error ( _ _ ( "error deploying contracts" ) ) ;
2017-03-30 11:12:39 +00:00
self . logger . error ( err . message ) ;
self . logger . debug ( err . stack ) ;
}
2018-04-26 18:15:43 +00:00
if ( contracts . length === 0 ) {
2018-05-08 21:49:46 +00:00
self . logger . info ( _ _ ( "no contracts found" ) ) ;
2018-04-26 18:15:43 +00:00
return done ( ) ;
}
2018-05-08 21:49:46 +00:00
self . logger . info ( _ _ ( "finished deploying contracts" ) ) ;
2017-03-30 11:12:39 +00:00
self . logger . trace ( arguments ) ;
2017-12-20 19:09:35 +00:00
done ( err ) ;
2017-03-30 11:12:39 +00:00
}
) ;
2015-08-04 12:18:04 +00:00
2017-03-30 11:38:14 +00:00
}
2017-03-30 11:12:39 +00:00
}
2016-08-14 12:04:34 +00:00
module . exports = Deploy ;