Address feedback

This commit is contained in:
Amir Bandeali 2018-02-20 13:42:35 -08:00
parent c1bbcaba73
commit 67f2864501
6 changed files with 55 additions and 50 deletions

View File

@ -1,8 +1,8 @@
# CHANGELOG # CHANGELOG
## v0.2.0 - _??_ ## v0.2.0 - _TBD, 2018_
* Check dependencies when determining if contracts should be recompiled. * Check dependencies when determining if contracts should be recompiled (#408).
## v0.1.0 - _February 16, 2018_ ## v0.1.0 - _February 16, 2018_

View File

@ -16,6 +16,7 @@ const DEFAULT_NETWORK_ID = 50;
const DEFAULT_JSONRPC_PORT = 8545; const DEFAULT_JSONRPC_PORT = 8545;
const DEFAULT_GAS_PRICE = (10 ** 9 * 2).toString(); const DEFAULT_GAS_PRICE = (10 ** 9 * 2).toString();
const DEFAULT_CONTRACTS_LIST = '*'; const DEFAULT_CONTRACTS_LIST = '*';
/** /**
* Compiles all contracts with options passed in through CLI. * Compiles all contracts with options passed in through CLI.
* @param argv Instance of process.argv provided by yargs. * @param argv Instance of process.argv provided by yargs.

View File

@ -20,17 +20,17 @@ import {
import { utils } from './utils/utils'; import { utils } from './utils/utils';
const ALL_CONTRACTS_IDENTIFIER = '*'; const ALL_CONTRACTS_IDENTIFIER = '*';
const SOLIDITY_VERSION_REGEX = /(?:solidity\s\^?)([0-9]{1,2}[.][0-9]{1,2}[.][0-9]{1,2})/; const SOLIDITY_VERSION_REGEX = /(?:solidity\s\^?)(\d+\.\d+\.\d+)/;
const SOLIDITY_FILE_EXTENSION_REGEX = /(.*\.sol)/; const SOLIDITY_FILE_EXTENSION_REGEX = /(.*\.sol)/;
const IMPORT_REGEX = /(import\s)/; const IMPORT_REGEX = /(import\s)/;
const DEPENDENCY_PATH_REGEX = /"([^"]+)"/; const DEPENDENCY_PATH_REGEX = /"([^"]+)"/; // Source: https://github.com/BlockChainCompany/soljitsu/blob/master/lib/shared.js
export class Compiler { export class Compiler {
private _contractsDir: string; private _contractsDir: string;
private _networkId: number; private _networkId: number;
private _optimizerEnabled: number; private _optimizerEnabled: number;
private _artifactsDir: string; private _artifactsDir: string;
private _contractSourcesIfExists?: ContractSources; private _contractSources?: ContractSources;
private _solcErrors: Set<string> = new Set(); private _solcErrors: Set<string> = new Set();
private _specifiedContracts: Set<string> = new Set(); private _specifiedContracts: Set<string> = new Set();
private _contractSourceData: ContractSourceData = {}; private _contractSourceData: ContractSourceData = {};
@ -82,10 +82,10 @@ export class Compiler {
private static _getContractSpecificSourceData(source: string): ContractSpecificSourceData { private static _getContractSpecificSourceData(source: string): ContractSpecificSourceData {
const dependencies: string[] = []; const dependencies: string[] = [];
const sourceHash = ethUtil.sha3(source); const sourceHash = ethUtil.sha3(source);
const solc_version = Compiler._parseSolidityVersion(source); const solcVersion = Compiler._parseSolidityVersion(source);
const contractSpecificSourceData: ContractSpecificSourceData = { const contractSpecificSourceData: ContractSpecificSourceData = {
dependencies, dependencies,
solc_version, solcVersion,
sourceHash, sourceHash,
}; };
const lines = source.split('\n'); const lines = source.split('\n');
@ -149,12 +149,12 @@ export class Compiler {
*/ */
public async compileAllAsync(): Promise<void> { public async compileAllAsync(): Promise<void> {
await this._createArtifactsDirIfDoesNotExistAsync(); await this._createArtifactsDirIfDoesNotExistAsync();
this._contractSourcesIfExists = await Compiler._getContractSourcesAsync(this._contractsDir); this._contractSources = await Compiler._getContractSourcesAsync(this._contractsDir);
_.forIn(this._contractSourcesIfExists, (source, fileName) => { _.forIn(this._contractSources, (source, fileName) => {
this._contractSourceData[fileName] = Compiler._getContractSpecificSourceData(source); this._contractSourceData[fileName] = Compiler._getContractSpecificSourceData(source);
}); });
const fileNames = this._specifiedContracts.has(ALL_CONTRACTS_IDENTIFIER) const fileNames = this._specifiedContracts.has(ALL_CONTRACTS_IDENTIFIER)
? _.keys(this._contractSourcesIfExists) ? _.keys(this._contractSources)
: Array.from(this._specifiedContracts.values()); : Array.from(this._specifiedContracts.values());
_.forEach(fileNames, fileName => { _.forEach(fileNames, fileName => {
this._setSourceTreeHash(fileName); this._setSourceTreeHash(fileName);
@ -169,29 +169,29 @@ export class Compiler {
* @param fileName Name of contract with '.sol' extension. * @param fileName Name of contract with '.sol' extension.
*/ */
private async _compileContractAsync(fileName: string): Promise<void> { private async _compileContractAsync(fileName: string): Promise<void> {
if (_.isUndefined(this._contractSourcesIfExists)) { if (_.isUndefined(this._contractSources)) {
throw new Error('Contract sources not yet initialized'); throw new Error('Contract sources not yet initialized');
} }
const contractSpecificSourceData = this._contractSourceData[fileName]; const contractSpecificSourceData = this._contractSourceData[fileName];
const currentArtifact = (await this._getContractArtifactOrReturnAsync(fileName)) as ContractArtifact; const currentArtifactIfExists = (await this._getContractArtifactIfExistsAsync(fileName)) as ContractArtifact;
const sourceHash = `0x${contractSpecificSourceData.sourceHash.toString('hex')}`; const sourceHash = `0x${contractSpecificSourceData.sourceHash.toString('hex')}`;
const sourceTreeHash = `0x${contractSpecificSourceData.sourceTreeHash.toString('hex')}`; const sourceTreeHash = `0x${contractSpecificSourceData.sourceTreeHashIfExists.toString('hex')}`;
const shouldCompile = const shouldCompile =
_.isUndefined(currentArtifact) || _.isUndefined(currentArtifactIfExists) ||
currentArtifact.networks[this._networkId].optimizer_enabled !== this._optimizerEnabled || currentArtifactIfExists.networks[this._networkId].optimizer_enabled !== this._optimizerEnabled ||
currentArtifact.networks[this._networkId].source_tree_hash !== sourceTreeHash; currentArtifactIfExists.networks[this._networkId].source_tree_hash !== sourceTreeHash;
if (!shouldCompile) { if (!shouldCompile) {
return; return;
} }
const fullSolcVersion = binPaths[contractSpecificSourceData.solc_version]; const fullSolcVersion = binPaths[contractSpecificSourceData.solcVersion];
const solcBinPath = `./solc/solc_bin/${fullSolcVersion}`; const solcBinPath = `./solc/solc_bin/${fullSolcVersion}`;
const solcBin = require(solcBinPath); const solcBin = require(solcBinPath);
const solcInstance = solc.setupMethods(solcBin); const solcInstance = solc.setupMethods(solcBin);
utils.consoleLog(`Compiling ${fileName}...`); utils.consoleLog(`Compiling ${fileName}...`);
const source = this._contractSourcesIfExists[fileName]; const source = this._contractSources[fileName];
const input = { const input = {
[fileName]: source, [fileName]: source,
}; };
@ -217,7 +217,7 @@ export class Compiler {
const unlinked_binary = `0x${compiled.contracts[contractIdentifier].bytecode}`; const unlinked_binary = `0x${compiled.contracts[contractIdentifier].bytecode}`;
const updated_at = Date.now(); const updated_at = Date.now();
const contractNetworkData: ContractNetworkData = { const contractNetworkData: ContractNetworkData = {
solc_version: contractSpecificSourceData.solc_version, solc_version: contractSpecificSourceData.solcVersion,
keccak256: sourceHash, keccak256: sourceHash,
source_tree_hash: sourceTreeHash, source_tree_hash: sourceTreeHash,
optimizer_enabled: this._optimizerEnabled, optimizer_enabled: this._optimizerEnabled,
@ -227,11 +227,11 @@ export class Compiler {
}; };
let newArtifact: ContractArtifact; let newArtifact: ContractArtifact;
if (!_.isUndefined(currentArtifact)) { if (!_.isUndefined(currentArtifactIfExists)) {
newArtifact = { newArtifact = {
...currentArtifact, ...currentArtifactIfExists,
networks: { networks: {
...currentArtifact.networks, ...currentArtifactIfExists.networks,
[this._networkId]: contractNetworkData, [this._networkId]: contractNetworkData,
}, },
}; };
@ -253,28 +253,28 @@ export class Compiler {
* Sets the source tree hash for a file and its dependencies. * Sets the source tree hash for a file and its dependencies.
* @param fileName Name of contract file. * @param fileName Name of contract file.
*/ */
private _setSourceTreeHash(fileName: string) { private _setSourceTreeHash(fileName: string): void {
const contractSpecificSourceData = this._contractSourceData[fileName]; const contractSpecificSourceData = this._contractSourceData[fileName];
if (_.isUndefined(contractSpecificSourceData)) { if (_.isUndefined(contractSpecificSourceData)) {
throw new Error(`Contract data for ${fileName} not yet set`); throw new Error(`Contract data for ${fileName} not yet set`);
} }
if (_.isUndefined(contractSpecificSourceData.sourceTreeHash)) { if (_.isUndefined(contractSpecificSourceData.sourceTreeHashIfExists)) {
const dependencies = contractSpecificSourceData.dependencies; const dependencies = contractSpecificSourceData.dependencies;
if (dependencies.length === 0) { if (dependencies.length === 0) {
contractSpecificSourceData.sourceTreeHash = contractSpecificSourceData.sourceHash; contractSpecificSourceData.sourceTreeHashIfExists = contractSpecificSourceData.sourceHash;
} else { } else {
_.forEach(dependencies, dependency => { _.forEach(dependencies, dependency => {
this._setSourceTreeHash(dependency); this._setSourceTreeHash(dependency);
}); });
const dependencySourceTreeHashes = _.map( const dependencySourceTreeHashes = _.map(
dependencies, dependencies,
dependency => this._contractSourceData[dependency].sourceTreeHash, dependency => this._contractSourceData[dependency].sourceTreeHashIfExists,
); );
const sourceTreeHashesBuffer = Buffer.concat([ const sourceTreeHashesBuffer = Buffer.concat([
contractSpecificSourceData.sourceHash, contractSpecificSourceData.sourceHash,
...dependencySourceTreeHashes, ...dependencySourceTreeHashes,
]); ]);
contractSpecificSourceData.sourceTreeHash = ethUtil.sha3(sourceTreeHashesBuffer); contractSpecificSourceData.sourceTreeHashIfExists = ethUtil.sha3(sourceTreeHashesBuffer);
} }
} }
} }
@ -285,11 +285,11 @@ export class Compiler {
* @return Import contents object containing source code of dependency. * @return Import contents object containing source code of dependency.
*/ */
private _findImportsIfSourcesExist(importPath: string): ImportContents { private _findImportsIfSourcesExist(importPath: string): ImportContents {
if (_.isUndefined(this._contractSourcesIfExists)) {
throw new Error('Contract sources not yet initialized');
}
const fileName = path.basename(importPath); const fileName = path.basename(importPath);
const source = this._contractSourcesIfExists[fileName]; const source = this._contractSources[fileName];
if (_.isUndefined(source)) {
throw new Error(`Contract source not found for ${fileName}`);
}
const importContents: ImportContents = { const importContents: ImportContents = {
contents: source, contents: source,
}; };
@ -309,7 +309,7 @@ export class Compiler {
* @param fileName Name of contract file. * @param fileName Name of contract file.
* @return Contract data on network or undefined. * @return Contract data on network or undefined.
*/ */
private async _getContractArtifactOrReturnAsync(fileName: string): Promise<ContractArtifact | void> { private async _getContractArtifactIfExistsAsync(fileName: string): Promise<ContractArtifact | void> {
let contractArtifact; let contractArtifact;
const contractName = path.basename(fileName, constants.SOLIDITY_FILE_EXTENSION); const contractName = path.basename(fileName, constants.SOLIDITY_FILE_EXTENSION);
const currentArtifactPath = `${this._artifactsDir}/${contractName}.json`; const currentArtifactPath = `${this._artifactsDir}/${contractName}.json`;
@ -322,7 +322,7 @@ export class Compiler {
return contractArtifact; return contractArtifact;
} catch (err) { } catch (err) {
utils.consoleLog(`Artifact for ${fileName} does not exist`); utils.consoleLog(`Artifact for ${fileName} does not exist`);
return contractArtifact; return undefined;
} }
} }
} }

View File

@ -35,9 +35,11 @@ export class Deployer {
* @return Deployed contract instance. * @return Deployed contract instance.
*/ */
public async deployAsync(contractName: string, args: any[] = []): Promise<Web3.ContractInstance> { public async deployAsync(contractName: string, args: any[] = []): Promise<Web3.ContractInstance> {
const contractArtifact: ContractArtifact = this._loadContractArtifactIfExists(contractName); const contractArtifactIfExists: ContractArtifact = this._loadContractArtifactIfExists(contractName);
const contractData: ContractNetworkData = this._getContractDataFromArtifactIfExists(contractArtifact); const contractNetworkDataIfExists: ContractNetworkData = this._getContractNetworkDataFromArtifactIfExists(
const data = contractData.unlinked_binary; contractArtifactIfExists,
);
const data = contractNetworkDataIfExists.unlinked_binary;
const from = await this._getFromAddressAsync(); const from = await this._getFromAddressAsync();
const gas = await this._getAllowableGasEstimateAsync(data); const gas = await this._getAllowableGasEstimateAsync(data);
const txData = { const txData = {
@ -46,7 +48,7 @@ export class Deployer {
data, data,
gas, gas,
}; };
const abi = contractData.abi; const abi = contractNetworkDataIfExists.abi;
const web3ContractInstance = await this._deployFromAbiAsync(abi, args, txData); const web3ContractInstance = await this._deployFromAbiAsync(abi, args, txData);
utils.consoleLog(`${contractName}.sol successfully deployed at ${web3ContractInstance.address}`); utils.consoleLog(`${contractName}.sol successfully deployed at ${web3ContractInstance.address}`);
const contractInstance = new Contract(web3ContractInstance, this._defaults); const contractInstance = new Contract(web3ContractInstance, this._defaults);
@ -100,19 +102,21 @@ export class Deployer {
contractAddress: string, contractAddress: string,
args: any[], args: any[],
): Promise<void> { ): Promise<void> {
const contractArtifact: ContractArtifact = this._loadContractArtifactIfExists(contractName); const contractArtifactIfExists: ContractArtifact = this._loadContractArtifactIfExists(contractName);
const contractData: ContractNetworkData = this._getContractDataFromArtifactIfExists(contractArtifact); const contractNetworkDataIfExists: ContractNetworkData = this._getContractNetworkDataFromArtifactIfExists(
const abi = contractData.abi; contractArtifactIfExists,
);
const abi = contractNetworkDataIfExists.abi;
const encodedConstructorArgs = encoder.encodeConstructorArgsFromAbi(args, abi); const encodedConstructorArgs = encoder.encodeConstructorArgsFromAbi(args, abi);
const newContractData = { const newContractData = {
...contractData, ...contractNetworkDataIfExists,
address: contractAddress, address: contractAddress,
constructor_args: encodedConstructorArgs, constructor_args: encodedConstructorArgs,
}; };
const newArtifact = { const newArtifact = {
...contractArtifact, ...contractArtifactIfExists,
networks: { networks: {
...contractArtifact.networks, ...contractArtifactIfExists.networks,
[this._networkId]: newContractData, [this._networkId]: newContractData,
}, },
}; };
@ -139,12 +143,12 @@ export class Deployer {
* @param contractArtifact The contract artifact. * @param contractArtifact The contract artifact.
* @return Network specific contract data. * @return Network specific contract data.
*/ */
private _getContractDataFromArtifactIfExists(contractArtifact: ContractArtifact): ContractNetworkData { private _getContractNetworkDataFromArtifactIfExists(contractArtifact: ContractArtifact): ContractNetworkData {
const contractData = contractArtifact.networks[this._networkId]; const contractNetworkDataIfExists = contractArtifact.networks[this._networkId];
if (_.isUndefined(contractData)) { if (_.isUndefined(contractNetworkDataIfExists)) {
throw new Error(`Data not found in artifact for contract: ${contractArtifact.contract_name}`); throw new Error(`Data not found in artifact for contract: ${contractArtifact.contract_name}`);
} }
return contractData; return contractNetworkDataIfExists;
} }
/** /**
* Gets the address to use for sending a transaction. * Gets the address to use for sending a transaction.

View File

@ -71,9 +71,9 @@ export interface ContractSourceData {
export interface ContractSpecificSourceData { export interface ContractSpecificSourceData {
dependencies: string[]; dependencies: string[];
solc_version: string; solcVersion: string;
sourceHash: Buffer; sourceHash: Buffer;
sourceTreeHash?: Buffer; sourceTreeHashIfExists?: Buffer;
} }
export interface ImportContents { export interface ImportContents {