UNPKG

cc-zos

Version:

Command-line interface for the ZeppelinOS smart contract platform

349 lines (277 loc) 13.2 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash); var _ccZosLib = require('cc-zos-lib'); var _StatusChecker = require('../status/StatusChecker'); var _StatusChecker2 = _interopRequireDefault(_StatusChecker); var _Verifier = require('../Verifier'); var _Verifier2 = _interopRequireDefault(_Verifier); var _async = require('../../utils/async'); var _ValidationLogger = require('../../interface/ValidationLogger'); var _ValidationLogger2 = _interopRequireDefault(_ValidationLogger); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const log = new _ccZosLib.Logger('NetworkController'); class NetworkBaseController { constructor(localController, network, txParams, networkFile = undefined) { this.localController = localController; this.txParams = txParams; this.network = network; this.networkFile = networkFile || localController.packageFile.networkFile(network); } get packageFile() { return this.localController.packageFile; } get packageVersion() { return this.packageFile.version; } get currentVersion() { return this.networkFile.version; } get packageAddress() { return this.networkFile.packageAddress; } getDeployer() { throw Error("Unimplemented function getDeployer()"); } get isLib() { return this.packageFile.isLib; } get isLightweight() { return false; } async fetchOrDeploy(requestedVersion) { this.project = await this.getDeployer(requestedVersion).fetchOrDeploy(); return this.project; } async compareCurrentStatus() { if (this.isLightweight) throw Error('Command status-pull is not supported for unpublished projects'); const statusComparator = _StatusChecker2.default.compare(this.networkFile, this.txParams); await statusComparator.call(); } async pullRemoteStatus() { if (this.isLightweight) throw Error('Command status-fix is not supported for unpublished projects'); const statusFetcher = _StatusChecker2.default.fetch(this.networkFile, this.txParams); await statusFetcher.call(); } async createProxy() { throw Error('Unimplemented function createProxy()'); } async push(reupload = false, force = false) { const changedLibraries = this._solidityLibsForPush(!reupload); const contracts = this._contractsListForPush(!reupload, changedLibraries); const buildArtifacts = (0, _ccZosLib.getBuildArtifacts)(); // ValidateContracts also extends each contract class with validation errors and storage info if (!this.validateContracts(contracts, buildArtifacts) && !force) { throw Error('One or more contracts have validation errors. Please review the items listed above and fix them, or run this command again with the --force option.'); } this._checkVersion(); await this.fetchOrDeploy(this.packageVersion); await this.handleLibsLink(); this.checkNotFrozen(); await this.uploadSolidityLibs(changedLibraries); await Promise.all([this.uploadContracts(contracts), this.unsetContracts()]); await this._unsetSolidityLibs(); } async handleLibsLink() { return; } checkNotFrozen() { if (this.networkFile.frozen) { throw Error('Cannot modify contracts in a frozen version. Run zos bump to create a new version first.'); } } async newVersion(versionName) { return this.project.newVersion(versionName); } _checkVersion() { if (this._newVersionRequired()) { log.info(`Current version ${this.currentVersion}`); log.info(`Creating new version ${this.packageVersion}`); this.networkFile.frozen = false; this.networkFile.contracts = {}; } } _newVersionRequired() { return this.packageVersion !== this.currentVersion && !this.isLightweight; } _contractsListForPush(onlyChanged = false, changedLibraries = []) { const newVersion = this._newVersionRequired(); return (0, _lodash2.default)(this.packageFile.contracts).toPairs().map(([contractAlias, contractName]) => [contractAlias, _ccZosLib.Contracts.getFromLocal(contractName)]).filter(([contractAlias, contractClass]) => newVersion || !onlyChanged || this.hasContractChanged(contractAlias, contractClass) || this._hasChangedLibraries(contractClass, changedLibraries)).value(); } _solidityLibsForPush(onlyChanged = false) { const { contractNames, contractAliases } = this.packageFile; const libNames = this._getAllSolidityLibNames(contractNames); const clashes = _lodash2.default.intersection(libNames, contractAliases); if (!_lodash2.default.isEmpty(clashes)) { throw new Error(`Cannot upload libraries with the same name as a contract alias: ${clashes.join(', ')}`); } return libNames.map(libName => _ccZosLib.Contracts.getFromLocal(libName)).filter(libClass => { const hasSolidityLib = this.networkFile.hasSolidityLib(libClass.contractName); const hasChanged = this._hasSolidityLibChanged(libClass); return !hasSolidityLib || !onlyChanged || hasChanged; }); } async uploadSolidityLibs(libs) { await (0, _async.allPromisesOrError)(libs.map(lib => this._uploadSolidityLib(lib))); } async _uploadSolidityLib(libClass) { const libName = libClass.contractName; log.info(`Uploading ${libName} library...`); const libInstance = await this.project.setImplementation(libClass, libName); this.networkFile.addSolidityLib(libName, libInstance); } async uploadContracts(contracts) { await (0, _async.allPromisesOrError)(contracts.map(([contractAlias, contractClass]) => this.uploadContract(contractAlias, contractClass))); } async uploadContract(contractAlias, contractClass) { try { const currentContractLibs = (0, _ccZosLib.getSolidityLibNames)(contractClass.bytecode); const libraries = this.networkFile.getSolidityLibs(currentContractLibs); log.info(`Uploading ${contractClass.contractName} contract as ${contractAlias}`); await contractClass.link(libraries); const contractInstance = await this.project.setImplementation(contractClass, contractAlias); this.networkFile.addContract(contractAlias, contractInstance, { warnings: contractClass.warnings, types: contractClass.storageInfo.types, storage: contractClass.storageInfo.storage }); } catch (error) { throw Error(`${contractAlias} deployment failed with error: ${error.message}`); } } async _unsetSolidityLibs() { const { contractNames } = this.packageFile; const libNames = this._getAllSolidityLibNames(contractNames); await (0, _async.allPromisesOrError)(this.networkFile.solidityLibsMissing(libNames).map(libName => this._unsetSolidityLib(libName))); } async _unsetSolidityLib(libName) { try { log.info(`Removing ${libName} library`); await this.project.unsetImplementation(libName); this.networkFile.unsetSolidityLib(libName); } catch (error) { throw Error(`Removal of ${libName} failed with error: ${error.message}`); } } _hasChangedLibraries(contractClass, changedLibraries) { const libNames = (0, _ccZosLib.getSolidityLibNames)(contractClass.bytecode); return !_lodash2.default.isEmpty(_lodash2.default.intersection(changedLibraries.map(c => c.contractName), libNames)); } _getAllSolidityLibNames(contractNames) { const libNames = contractNames.map(contractName => { const contractClass = _ccZosLib.Contracts.getFromLocal(contractName); return (0, _ccZosLib.getSolidityLibNames)(contractClass.bytecode); }); return _lodash2.default.uniq(_lodash2.default.flatten(libNames)); } async unsetContracts() { await (0, _async.allPromisesOrError)(this.networkFile.contractAliasesMissingFromPackage().map(contractAlias => this.unsetContract(contractAlias))); } async unsetContract(contractAlias) { try { log.info(`Removing ${contractAlias} contract`); await this.project.unsetImplementation(contractAlias); this.networkFile.unsetContract(contractAlias); } catch (error) { throw Error(`Removal of ${contractAlias} failed with error: ${error.message}`); } } validateContracts(contracts, buildArtifacts) { return _lodash2.default.every(contracts.map(([contractAlias, contractClass]) => this.validateContract(contractAlias, contractClass, buildArtifacts))); } validateContract(contractAlias, contractClass, buildArtifacts) { log.info(`Validating contract ${contractClass.contractName}`); const existingContractInfo = this.networkFile.contract(contractAlias) || {}; const warnings = (0, _ccZosLib.validate)(contractClass, existingContractInfo, buildArtifacts); const newWarnings = (0, _ccZosLib.newValidationErrors)(warnings, existingContractInfo.warnings); const validationLogger = new _ValidationLogger2.default(contractClass, existingContractInfo); validationLogger.log(newWarnings, buildArtifacts); contractClass.warnings = warnings; contractClass.storageInfo = (0, _ccZosLib.getStorageLayout)(contractClass, buildArtifacts); return (0, _ccZosLib.validationPasses)(newWarnings); } checkContractDeployed(packageName, contractAlias, throwIfFail = false) { if (!packageName) packageName = this.packageFile.name; const err = this._errorForContractDeployed(packageName, contractAlias); if (err) this._handleErrorMessage(err, throwIfFail); } _errorForContractDeployed(packageName, contractAlias) { return this._errorForLocalContractDeployed(contractAlias); } checkLocalContractsDeployed(throwIfFail = false) { const err = this._errorForLocalContractsDeployed(); if (err) this._handleErrorMessage(err, throwIfFail); } _errorForLocalContractsDeployed() { const [contractsDeployed, contractsMissing] = _lodash2.default.partition(this.packageFile.contractAliases, alias => this.isContractDeployed(alias)); const contractsChanged = _lodash2.default.filter(contractsDeployed, alias => this.hasContractChanged(alias)); if (!_lodash2.default.isEmpty(contractsMissing)) { return `Contracts ${contractsMissing.join(', ')} are not deployed.`; } else if (!_lodash2.default.isEmpty(contractsChanged)) { return `Contracts ${contractsChanged.join(', ')} have changed since the last deploy.`; } } checkLocalContractDeployed(contractAlias, throwIfFail = false) { // if (!packageName) packageName = this.packageFile.name const err = this._errorForLocalContractDeployed(contractAlias); if (err) this._handleErrorMessage(err, throwIfFail); } _errorForLocalContractDeployed(contractAlias) { if (!this.isContractDefined(contractAlias)) { return `Contract ${contractAlias} not found in this project`; } else if (!this.isContractDeployed(contractAlias)) { return `Contract ${contractAlias} is not deployed to ${this.network}.`; } else if (this.hasContractChanged(contractAlias)) { return `Contract ${contractAlias} has changed locally since the last deploy, consider running 'zos push'.`; } } _handleErrorMessage(msg, throwIfFail = false) { if (throwIfFail) { throw Error(msg); } else { log.info(msg); } } _hasSolidityLibChanged(libClass) { return !this.networkFile.hasSameBytecode(libClass.contractName, libClass); } hasContractChanged(contractAlias, contractClass = undefined) { if (!this.isLocalContract(contractAlias)) return false; if (!this.isContractDeployed(contractAlias)) return true; if (!contractClass) { const contractName = this.packageFile.contract(contractAlias); contractClass = _ccZosLib.Contracts.getFromLocal(contractName); } return !this.networkFile.hasSameBytecode(contractAlias, contractClass); } isLocalContract(contractAlias) { return this.packageFile.hasContract(contractAlias); } isContractDefined(contractAlias) { return this.packageFile.hasContract(contractAlias); } isContractDeployed(contractAlias) { return !this.isLocalContract(contractAlias) || this.networkFile.hasContract(contractAlias); } async verifyAndPublishContract(contractAlias, optimizer, optimizerRuns, remote) { const contractName = this.packageFile.contract(contractAlias); const { compilerVersion, sourcePath } = this.localController.getContractSourcePath(contractAlias); const contractSource = await (0, _ccZosLib.flattenSourceCode)([sourcePath]); const contractAddress = this.networkFile.contracts[contractAlias].address; log.info(`Verifying and publishing ${contractAlias} on ${remote}`); await _Verifier2.default.verifyAndPublish(remote, { contractName, compilerVersion, optimizer, optimizerRuns, contractSource, contractAddress }); } writeNetworkPackageIfNeeded() { this.networkFile.write(); } async freeze() { if (!this.packageAddress) throw Error('Cannot freeze an unpublished project'); await this.fetchOrDeploy(this.currentVersion); await this.project.freeze(); this.networkFile.frozen = true; } } exports.default = NetworkBaseController;