Use source tree hash instead of compile flag

This commit is contained in:
Amir Bandeali
2018-02-19 19:15:57 -08:00
parent 6685cb3fba
commit c1bbcaba73
3 changed files with 43 additions and 44 deletions

View File

@@ -1,5 +1,5 @@
lib
generated
.nyc_output
/packages/contracts/build/contracts
/packages/contracts/src/artifacts
package.json

View File

@@ -81,12 +81,12 @@ export class Compiler {
*/
private static _getContractSpecificSourceData(source: string): ContractSpecificSourceData {
const dependencies: string[] = [];
const sourceHash = `0x${ethUtil.sha3(source).toString('hex')}`;
const sourceHash = ethUtil.sha3(source);
const solc_version = Compiler._parseSolidityVersion(source);
const contractSpecificSourceData: ContractSpecificSourceData = {
dependencies,
solc_version,
keccak256: sourceHash,
sourceHash,
};
const lines = source.split('\n');
_.forEach(lines, line => {
@@ -101,18 +101,6 @@ export class Compiler {
});
return contractSpecificSourceData;
}
/**
* Finds dependencies, keccak256 hashes, and compile flag for each contract.
* @param sources Mapping of contract file name to source code.
* @return Dependencies, keccak256 hash, and compile flag for each contract.
*/
private static _getContractSourceData(sources: ContractSources): ContractSourceData {
const contractSourceData: ContractSourceData = {};
_.forIn(sources, (source, fileName) => {
contractSourceData[fileName] = Compiler._getContractSpecificSourceData(source);
});
return contractSourceData;
}
/**
* Searches Solidity source code for compiler version.
* @param source Source code of contract.
@@ -162,11 +150,15 @@ export class Compiler {
public async compileAllAsync(): Promise<void> {
await this._createArtifactsDirIfDoesNotExistAsync();
this._contractSourcesIfExists = await Compiler._getContractSourcesAsync(this._contractsDir);
this._contractSourceData = Compiler._getContractSourceData(this._contractSourcesIfExists);
_.forIn(this._contractSourcesIfExists, (source, fileName) => {
this._contractSourceData[fileName] = Compiler._getContractSpecificSourceData(source);
});
const fileNames = this._specifiedContracts.has(ALL_CONTRACTS_IDENTIFIER)
? _.keys(this._contractSourcesIfExists)
: Array.from(this._specifiedContracts.values());
await Promise.all(_.map(fileNames, async fileName => this._setCompileActionAsync(fileName)));
_.forEach(fileNames, fileName => {
this._setSourceTreeHash(fileName);
});
await Promise.all(_.map(fileNames, async fileName => this._compileContractAsync(fileName)));
this._solcErrors.forEach(errMsg => {
utils.consoleLog(errMsg);
@@ -181,7 +173,15 @@ export class Compiler {
throw new Error('Contract sources not yet initialized');
}
const contractSpecificSourceData = this._contractSourceData[fileName];
if (!contractSpecificSourceData.shouldCompile) {
const currentArtifact = (await this._getContractArtifactOrReturnAsync(fileName)) as ContractArtifact;
const sourceHash = `0x${contractSpecificSourceData.sourceHash.toString('hex')}`;
const sourceTreeHash = `0x${contractSpecificSourceData.sourceTreeHash.toString('hex')}`;
const shouldCompile =
_.isUndefined(currentArtifact) ||
currentArtifact.networks[this._networkId].optimizer_enabled !== this._optimizerEnabled ||
currentArtifact.networks[this._networkId].source_tree_hash !== sourceTreeHash;
if (!shouldCompile) {
return;
}
@@ -218,7 +218,8 @@ export class Compiler {
const updated_at = Date.now();
const contractNetworkData: ContractNetworkData = {
solc_version: contractSpecificSourceData.solc_version,
keccak256: contractSpecificSourceData.keccak256,
keccak256: sourceHash,
source_tree_hash: sourceTreeHash,
optimizer_enabled: this._optimizerEnabled,
abi,
unlinked_binary,
@@ -226,7 +227,6 @@ export class Compiler {
};
let newArtifact: ContractArtifact;
const currentArtifact = (await this._getContractArtifactOrReturnAsync(fileName)) as ContractArtifact;
if (!_.isUndefined(currentArtifact)) {
newArtifact = {
...currentArtifact,
@@ -250,35 +250,33 @@ export class Compiler {
utils.consoleLog(`${fileName} artifact saved!`);
}
/**
* Recursively sets the compile action for a specific contract and dependencies.
* @param fileName Name of contracts file.
* Sets the source tree hash for a file and its dependencies.
* @param fileName Name of contract file.
*/
private async _setCompileActionAsync(fileName: string): Promise<void> {
private _setSourceTreeHash(fileName: string) {
const contractSpecificSourceData = this._contractSourceData[fileName];
if (_.isUndefined(contractSpecificSourceData)) {
throw new Error(`Contract data for ${fileName} not yet set`);
}
if (_.isUndefined(contractSpecificSourceData.shouldCompile)) {
const contractArtifact = (await this._getContractArtifactOrReturnAsync(fileName)) as ContractArtifact;
if (_.isUndefined(contractArtifact)) {
contractSpecificSourceData.shouldCompile = true;
if (_.isUndefined(contractSpecificSourceData.sourceTreeHash)) {
const dependencies = contractSpecificSourceData.dependencies;
if (dependencies.length === 0) {
contractSpecificSourceData.sourceTreeHash = contractSpecificSourceData.sourceHash;
} else {
const contractNetworkData = contractArtifact.networks[this._networkId];
contractSpecificSourceData.shouldCompile =
contractNetworkData.keccak256 !== contractSpecificSourceData.keccak256 ||
this._optimizerEnabled !== contractNetworkData.optimizer_enabled ||
contractNetworkData.solc_version !== contractSpecificSourceData.solc_version;
_.forEach(dependencies, dependency => {
this._setSourceTreeHash(dependency);
});
const dependencySourceTreeHashes = _.map(
dependencies,
dependency => this._contractSourceData[dependency].sourceTreeHash,
);
const sourceTreeHashesBuffer = Buffer.concat([
contractSpecificSourceData.sourceHash,
...dependencySourceTreeHashes,
]);
contractSpecificSourceData.sourceTreeHash = ethUtil.sha3(sourceTreeHashesBuffer);
}
}
await Promise.all(
_.map(contractSpecificSourceData.dependencies, async dependency => this._setCompileActionAsync(dependency)),
);
_.forEach(contractSpecificSourceData.dependencies, dependency => {
contractSpecificSourceData.shouldCompile =
contractSpecificSourceData.shouldCompile ||
(this._contractSourceData[dependency].shouldCompile &&
(this._specifiedContracts.has('*') || this._specifiedContracts.has(dependency)));
});
}
/**
* Callback to resolve dependencies with `solc.compile`.
@@ -308,7 +306,7 @@ export class Compiler {
}
/**
* Gets contract data on network or returns if an artifact does not exist.
* @param fileName Name of contracts file.
* @param fileName Name of contract file.
* @return Contract data on network or undefined.
*/
private async _getContractArtifactOrReturnAsync(fileName: string): Promise<ContractArtifact | void> {

View File

@@ -22,6 +22,7 @@ export interface ContractNetworkData {
solc_version: string;
optimizer_enabled: number;
keccak256: string;
source_tree_hash: string;
abi: Web3.ContractAbi;
unlinked_binary: string;
address?: string;
@@ -71,8 +72,8 @@ export interface ContractSourceData {
export interface ContractSpecificSourceData {
dependencies: string[];
solc_version: string;
keccak256: string;
shouldCompile?: boolean;
sourceHash: Buffer;
sourceTreeHash?: Buffer;
}
export interface ImportContents {