Commit 3d7c17ec authored by aniket-engg's avatar aniket-engg

gas costs analysis using AST

parent 5594cdea
import { default as category } from './categories' import { default as category } from './categories'
import { default as algorithm } from './algorithmCategories' import { default as algorithm } from './algorithmCategories'
import AbstractAst from './abstractAstView' import { getFunctionDefinitionName, helpers, getType } from './staticAnalysisCommon'
import { ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, CompiledContractObj, CompiledContract, VisitFunction, AnalyzerModule} from './../../types' import { ModuleAlgorithm, ModuleCategory, ReportObj, CompilationResult, CompiledContractObj, CompiledContract, VisitFunction, AnalyzerModule, FunctionDefinitionAstNode} from './../../types'
interface VisitedContract { interface VisitedContract {
name: string name: string
...@@ -15,57 +15,71 @@ export default class gasCosts implements AnalyzerModule { ...@@ -15,57 +15,71 @@ export default class gasCosts implements AnalyzerModule {
category: ModuleCategory = category.GAS category: ModuleCategory = category.GAS
algorithm: ModuleAlgorithm = algorithm.EXACT algorithm: ModuleAlgorithm = algorithm.EXACT
abstractAst: AbstractAst = new AbstractAst() warningNodes: FunctionDefinitionAstNode[] = []
visit: VisitFunction = this.abstractAst.build_visit((node: any) => false) visit (node: FunctionDefinitionAstNode): void {
if (node.nodeType === 'FunctionDefinition' && node.kind !== 'constructor') this.warningNodes.push(node)
}
report (compilationResults: CompilationResult): ReportObj[] { report (compilationResults: CompilationResult): ReportObj[] {
const report: ReportObj[] = [] const report: ReportObj[] = []
this.visitContracts(compilationResults.contracts, (contract: VisitedContract) => {
if ( const methodsWithSignature = this.warningNodes.map(node => {
!contract.object.evm.gasEstimates || return {
!contract.object.evm.gasEstimates.external name: node.name,
) { src: node.src,
return signature: helpers.buildAbiSignature(getFunctionDefinitionName(node), node.parameters.parameters.map(node => node.typeDescriptions.typeString.split(' ')[0]))
} }
const fallback: string = contract.object.evm.gasEstimates.external[''] })
if (fallback !== undefined) { for (const method of methodsWithSignature) {
if (fallback === null || parseInt(fallback) >= 2100 || fallback === 'infinite') { for (const contractName in compilationResults.contracts['test.sol']) {
const contract = compilationResults.contracts['test.sol'][contractName]
const methodGas: any = this.checkMethodGas(contract, method.signature)
if(methodGas.isInfinite) {
if(methodGas.isFallback) {
report.push({ report.push({
warning: `Fallback function of contract ${contract.name} requires too much gas (${fallback}). warning: `Fallback function of contract ${contractName} requires too much gas (${methodGas.msg}).
If the fallback function requires more than 2300 gas, the contract cannot receive Ether.` If the fallback function requires more than 2300 gas, the contract cannot receive Ether.`
}) })
} } else {
}
for (var functionName in contract.object.evm.gasEstimates.external) {
if (functionName === '') {
continue
}
const gas: string = contract.object.evm.gasEstimates.external[functionName]
const gasString: string = gas === null ? 'unknown or not constant' : 'high: ' + gas
if (gas === null || parseInt(gas) >= 3000000 || gas === 'infinite') {
report.push({ report.push({
warning: `Gas requirement of function ${contract.name}.${functionName} ${gasString}. warning: `Gas requirement of function ${contractName}.${method.name} ${methodGas.msg}.
If the gas requirement of a function is higher than the block gas limit, it cannot be executed. If the gas requirement of a function is higher than the block gas limit, it cannot be executed.
Please avoid loops in your functions or actions that modify large areas of storage Please avoid loops in your functions or actions that modify large areas of storage
(this includes clearing or copying arrays in storage)` (this includes clearing or copying arrays in storage)`
}) })
} }
} }
}) }
}
return report return report
} }
/** private checkMethodGas(contract: any, methodSignature: string) {
* call the given @arg cb (function) for all the contracts. Uses last compilation result if(methodSignature === '()') {
* stop visiting when cb return true const fallback: string = contract.evm.gasEstimates.external['']
* @param {Function} cb - callback if (fallback !== undefined && (fallback === null || parseInt(fallback) >= 2100 || fallback === 'infinite')) {
*/ return {
// @TODO has been copied from remix-ide repo ! should fix that soon ! isInfinite: true,
private visitContracts (contracts: CompiledContractObj | undefined, cb: ((contract: VisitedContract) => void | undefined)): void { isFallback: true,
for (let file in contracts) { msg: fallback
for (let name in contracts[file]) { }
if (cb({ name: name, object: contracts[file][name], file: file })) return } else {
return {
isInfinite: false
}
}
} else {
const gas: string = contract.evm.gasEstimates.external[methodSignature]
const gasString: string = gas === null ? 'unknown or not constant' : 'high: ' + gas
if (gas === null || parseInt(gas) >= 3000000 || gas === 'infinite') {
return {
isInfinite: true,
msg: gasString
}
} else {
return {
isInfinite: false
}
} }
} }
} }
......
...@@ -51,7 +51,7 @@ export interface CompilationResult { ...@@ -51,7 +51,7 @@ export interface CompilationResult {
[contractName: string]: CompilationSource [contractName: string]: CompilationSource
} }
/** This contains the contract-level outputs. It can be limited/filtered by the outputSelection settings */ /** This contains the contract-level outputs. It can be limited/filtered by the outputSelection settings */
contracts?: CompiledContractObj /** If the language used has no contract names, this field should equal to an empty string. */ contracts: CompiledContractObj /** If the language used has no contract names, this field should equal to an empty string. */
} }
export interface CompiledContractObj { export interface CompiledContractObj {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment