Merge pull request #508 from soad003/staticanalysis
Static Analysis: Assembly, Checks Effetct Interaction, Constant Functions without modifiers.pull/1/head
commit
a6f3391610
@ -0,0 +1,152 @@ |
|||||||
|
var common = require('./staticAnalysisCommon') |
||||||
|
var AstWalker = require('ethereum-remix').util.AstWalker |
||||||
|
|
||||||
|
function abstractAstView () { |
||||||
|
this.contracts = [] |
||||||
|
this.currentContractIndex = null |
||||||
|
this.currentFunctionIndex = null |
||||||
|
this.currentModifierIndex = null |
||||||
|
this.isFunctionNotModifier = false |
||||||
|
/* |
||||||
|
file1: contract c{} |
||||||
|
file2: import "file1" as x; contract c{} |
||||||
|
therefore we have two contracts with the same name c. At the moment this is not handled because alias name "x" is not |
||||||
|
available in the current AST implementation thus can not be resolved. |
||||||
|
Additionally the fullQuallified function names e.g. [contractName].[functionName](param1Type, param2Type, ... ) must be prefixed to |
||||||
|
fully support this and when inheritance is resolved it must include alias resolving e.g x.c = file1.c |
||||||
|
*/ |
||||||
|
this.multipleContractsWithSameName = false |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Builds a higher level AST view. I creates a list with each contract as an object in it. |
||||||
|
* Example contractsOut: |
||||||
|
* |
||||||
|
* { |
||||||
|
* "node": {}, // actual AST Node of the contract
|
||||||
|
* "functions": [ |
||||||
|
* { |
||||||
|
* "node": {}, // actual AST Node of the function
|
||||||
|
* "relevantNodes": [], // AST nodes in the function that are relevant for the anlysis of this function
|
||||||
|
* "modifierInvocations": [], // Modifier invocation AST nodes that are applied on this function
|
||||||
|
* "localVariables": [], // Local variable declaration nodes
|
||||||
|
* "parameters": [] // Parameter types of the function in order of definition
|
||||||
|
* } |
||||||
|
* ], |
||||||
|
* "modifiers": [], // Modifiers definded by the contract, format similar to functions
|
||||||
|
* "inheritsFrom": [], // Names of contract this one inherits from in order of definition
|
||||||
|
* "stateVariables": [] // AST nodes of all State variables
|
||||||
|
* } |
||||||
|
* |
||||||
|
* @relevantNodeFilter {ASTNode -> bool} function that selects relevant ast nodes for analysis on function level. |
||||||
|
* @contractsOut {list} return list for high level AST view |
||||||
|
* @return {ASTNode -> void} returns a function that can be used as visit function for static analysis modules, to build up a higher level AST view for further analysis. |
||||||
|
*/ |
||||||
|
abstractAstView.prototype.build_visit = function (relevantNodeFilter) { |
||||||
|
var that = this |
||||||
|
return function (node) { |
||||||
|
if (common.isContractDefinition(node)) { |
||||||
|
setCurrentContract(that, { |
||||||
|
node: node, |
||||||
|
functions: [], |
||||||
|
modifiers: [], |
||||||
|
inheritsFrom: [], |
||||||
|
stateVariables: common.getStateVariableDeclarationsFormContractNode(node) |
||||||
|
}) |
||||||
|
} else if (common.isInheritanceSpecifier(node)) { |
||||||
|
var currentContract = getCurrentContract(that) |
||||||
|
var inheritsFromName = common.getInheritsFromName(node) |
||||||
|
currentContract.inheritsFrom.push(inheritsFromName) |
||||||
|
} else if (common.isFunctionDefinition(node)) { |
||||||
|
setCurrentFunction(that, { |
||||||
|
node: node, |
||||||
|
relevantNodes: [], |
||||||
|
modifierInvocations: [], |
||||||
|
localVariables: getLocalVariables(node), |
||||||
|
parameters: getLocalParameters(node) |
||||||
|
}) |
||||||
|
} else if (common.isModifierDefinition(node)) { |
||||||
|
setCurrentModifier(that, { |
||||||
|
node: node, |
||||||
|
relevantNodes: [], |
||||||
|
localVariables: getLocalVariables(node), |
||||||
|
parameters: getLocalParameters(node) |
||||||
|
}) |
||||||
|
} else if (common.isModifierInvocation(node)) { |
||||||
|
if (!that.isFunctionNotModifier) throw new Error('abstractAstView.js: Found modifier invocation outside of function scope.') |
||||||
|
getCurrentFunction(that).modifierInvocations.push(node) |
||||||
|
} else if (relevantNodeFilter(node)) { |
||||||
|
((that.isFunctionNotModifier) ? getCurrentFunction(that) : getCurrentModifier(that)).relevantNodes.push(node) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
abstractAstView.prototype.build_report = function (wrap) { |
||||||
|
var that = this |
||||||
|
return function (compilationResult) { |
||||||
|
resolveStateVariablesInHierarchy(that.contracts) |
||||||
|
return wrap(that.contracts, that.multipleContractsWithSameName) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function resolveStateVariablesInHierarchy (contracts) { |
||||||
|
contracts.map((c) => { |
||||||
|
resolveStateVariablesInHierarchyForContract(c, contracts) |
||||||
|
}) |
||||||
|
} |
||||||
|
function resolveStateVariablesInHierarchyForContract (currentContract, contracts) { |
||||||
|
currentContract.inheritsFrom.map((inheritsFromName) => { |
||||||
|
// add variables from inherited contracts
|
||||||
|
var inheritsFrom = contracts.find((contract) => common.getContractName(contract.node) === inheritsFromName) |
||||||
|
if (inheritsFrom) { |
||||||
|
currentContract.stateVariables = currentContract.stateVariables.concat(inheritsFrom.stateVariables) |
||||||
|
} else { |
||||||
|
console.log('abstractAstView.js: could not find contract defintion inherited from ' + inheritsFromName) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
function setCurrentContract (that, contract) { |
||||||
|
var name = common.getContractName(contract.node) |
||||||
|
if (that.contracts.map((c) => common.getContractName(c.node)).filter((n) => n === name).length > 0) { |
||||||
|
console.log('abstractAstView.js: two or more contracts with the same name dectected, import aliases not supported at the moment') |
||||||
|
that.multipleContractsWithSameName = true |
||||||
|
} |
||||||
|
that.currentContractIndex = (that.contracts.push(contract) - 1) |
||||||
|
} |
||||||
|
|
||||||
|
function setCurrentFunction (that, func) { |
||||||
|
that.isFunctionNotModifier = true |
||||||
|
that.currentFunctionIndex = (getCurrentContract(that).functions.push(func) - 1) |
||||||
|
} |
||||||
|
|
||||||
|
function setCurrentModifier (that, modi) { |
||||||
|
that.isFunctionNotModifier = false |
||||||
|
that.currentModifierIndex = (getCurrentContract(that).modifiers.push(modi) - 1) |
||||||
|
} |
||||||
|
|
||||||
|
function getCurrentContract (that) { |
||||||
|
return that.contracts[that.currentContractIndex] |
||||||
|
} |
||||||
|
|
||||||
|
function getCurrentFunction (that) { |
||||||
|
return getCurrentContract(that).functions[that.currentFunctionIndex] |
||||||
|
} |
||||||
|
|
||||||
|
function getCurrentModifier (that) { |
||||||
|
return getCurrentContract(that).modifiers[that.currentModifierIndex] |
||||||
|
} |
||||||
|
|
||||||
|
function getLocalParameters (funcNode) { |
||||||
|
return getLocalVariables(common.getFunctionOrModifierDefinitionParameterPart(funcNode)).map(common.getType) |
||||||
|
} |
||||||
|
|
||||||
|
function getLocalVariables (funcNode) { |
||||||
|
var locals = [] |
||||||
|
new AstWalker().walk(funcNode, {'*': function (node) { |
||||||
|
if (common.isVariableDeclaration(node)) locals.push(node) |
||||||
|
return true |
||||||
|
}}) |
||||||
|
return locals |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = abstractAstView |
@ -1,4 +1,5 @@ |
|||||||
module.exports = { |
module.exports = { |
||||||
SECURITY: {displayName: 'Security', id: 'SEC'}, |
SECURITY: {displayName: 'Security', id: 'SEC'}, |
||||||
GAS: {displayName: 'Gas & Economy', id: 'GAS'} |
GAS: {displayName: 'Gas & Economy', id: 'GAS'}, |
||||||
|
MISC: {displayName: 'Miscellaneous', id: 'MISC'} |
||||||
} |
} |
||||||
|
@ -0,0 +1,88 @@ |
|||||||
|
var name = 'Checks-Effects-Interaction pattern' |
||||||
|
var desc = 'Avoid potential reentrancy bugs' |
||||||
|
var categories = require('./categories') |
||||||
|
var common = require('./staticAnalysisCommon') |
||||||
|
var fcallGraph = require('./functionCallGraph') |
||||||
|
var AbstractAst = require('./abstractAstView') |
||||||
|
|
||||||
|
function checksEffectsInteraction () { |
||||||
|
this.abstractAst = new AbstractAst() |
||||||
|
this.visit = this.abstractAst.build_visit( |
||||||
|
(node) => common.isInteraction(node) || common.isEffect(node) || common.isLocalCallGraphRelevantNode(node) |
||||||
|
) |
||||||
|
|
||||||
|
this.report = this.abstractAst.build_report(report) |
||||||
|
} |
||||||
|
|
||||||
|
checksEffectsInteraction.prototype.visit = function () { throw new Error('checksEffectsInteraction.js no visit function set upon construction') } |
||||||
|
|
||||||
|
checksEffectsInteraction.prototype.report = function () { throw new Error('checksEffectsInteraction.js no report function set upon construction') } |
||||||
|
|
||||||
|
function report (contracts, multipleContractsWithSameName) { |
||||||
|
var warnings = [] |
||||||
|
var hasModifiers = contracts.some((item) => item.modifiers.length > 0) |
||||||
|
|
||||||
|
var callGraph = fcallGraph.buildGlobalFuncCallGraph(contracts) |
||||||
|
|
||||||
|
contracts.forEach((contract) => { |
||||||
|
contract.functions.forEach((func) => { |
||||||
|
func.changesState = checkIfChangesState(common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters), |
||||||
|
getContext(callGraph, contract, func)) |
||||||
|
}) |
||||||
|
|
||||||
|
contract.functions.forEach((func) => { |
||||||
|
if (isPotentialVulnerableFunction(func, getContext(callGraph, contract, func))) { |
||||||
|
var funcName = common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) |
||||||
|
var comments = (hasModifiers) ? '<br/><i>Note:</i> Modifiers are currently not considered by this static analysis.' : '' |
||||||
|
comments += (multipleContractsWithSameName) ? '<br/><i>Note:</i> Import aliases are currently not supported by this static analysis.' : '' |
||||||
|
warnings.push({ |
||||||
|
warning: `Potential Violation of Checks-Effects-Interaction pattern in <i>${funcName}</i>: Could potentially lead to re-entrancy vulnerability. ${comments}`, |
||||||
|
location: func.src, |
||||||
|
more: 'http://solidity.readthedocs.io/en/develop/security-considerations.html#re-entrancy' |
||||||
|
}) |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
return warnings |
||||||
|
} |
||||||
|
|
||||||
|
function getContext (callGraph, currentContract, func) { |
||||||
|
return { callGraph: callGraph, currentContract: currentContract, stateVariables: getStateVariables(currentContract, func) } |
||||||
|
} |
||||||
|
|
||||||
|
function getStateVariables (contract, func) { |
||||||
|
return contract.stateVariables.concat(func.localVariables.filter(common.isStorageVariableDeclaration)) |
||||||
|
} |
||||||
|
|
||||||
|
function isPotentialVulnerableFunction (func, context) { |
||||||
|
var isPotentialVulnerable = false |
||||||
|
var interaction = false |
||||||
|
func.relevantNodes.forEach((node) => { |
||||||
|
if (common.isInteraction(node)) { |
||||||
|
interaction = true |
||||||
|
} else if (interaction && (common.isWriteOnStateVariable(node, context.stateVariables) || isLocalCallWithStateChange(node, context))) { |
||||||
|
isPotentialVulnerable = true |
||||||
|
} |
||||||
|
}) |
||||||
|
return isPotentialVulnerable |
||||||
|
} |
||||||
|
|
||||||
|
function isLocalCallWithStateChange (node, context) { |
||||||
|
if (common.isLocalCallGraphRelevantNode(node)) { |
||||||
|
var func = fcallGraph.resolveCallGraphSymbol(context.callGraph, common.getFullQualifiedFunctionCallIdent(context.currentContract.node, node)) |
||||||
|
return !func || (func && func.node.changesState) |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
function checkIfChangesState (startFuncName, context) { |
||||||
|
return fcallGraph.analyseCallGraph(context.callGraph, startFuncName, context, (node, context) => common.isWriteOnStateVariable(node, context.stateVariables)) |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
name: name, |
||||||
|
description: desc, |
||||||
|
category: categories.SECURITY, |
||||||
|
Module: checksEffectsInteraction |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
var name = 'Constant functions' |
||||||
|
var desc = 'Check for potentially constant functions' |
||||||
|
var categories = require('./categories') |
||||||
|
var common = require('./staticAnalysisCommon') |
||||||
|
var fcallGraph = require('./functionCallGraph') |
||||||
|
var AbstractAst = require('./abstractAstView') |
||||||
|
|
||||||
|
function constantFunctions () { |
||||||
|
this.abstractAst = new AbstractAst() |
||||||
|
|
||||||
|
this.visit = this.abstractAst.build_visit( |
||||||
|
(node) => common.isLowLevelCall(node) || common.isTransfer(node) || common.isExternalDirectCall(node) || common.isEffect(node) || common.isLocalCallGraphRelevantNode(node) || common.isInlineAssembly(node) || common.isNewExpression(node) |
||||||
|
) |
||||||
|
|
||||||
|
this.report = this.abstractAst.build_report(report) |
||||||
|
} |
||||||
|
|
||||||
|
constantFunctions.prototype.visit = function () { throw new Error('constantFunctions.js no visit function set upon construction') } |
||||||
|
|
||||||
|
constantFunctions.prototype.report = function () { throw new Error('constantFunctions.js no report function set upon construction') } |
||||||
|
|
||||||
|
function report (contracts, multipleContractsWithSameName) { |
||||||
|
var warnings = [] |
||||||
|
var hasModifiers = contracts.some((item) => item.modifiers.length > 0) |
||||||
|
|
||||||
|
var callGraph = fcallGraph.buildGlobalFuncCallGraph(contracts) |
||||||
|
|
||||||
|
contracts.forEach((contract) => { |
||||||
|
contract.functions.forEach((func) => { |
||||||
|
func.potentiallyshouldBeConst = checkIfShouldBeConstant(common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters), |
||||||
|
getContext(callGraph, contract, func)) |
||||||
|
}) |
||||||
|
|
||||||
|
contract.functions.filter((func) => common.hasFunctionBody(func.node)).forEach((func) => { |
||||||
|
if (common.isConstantFunction(func.node) !== func.potentiallyshouldBeConst) { |
||||||
|
var funcName = common.getFullQuallyfiedFuncDefinitionIdent(contract.node, func.node, func.parameters) |
||||||
|
var comments = (hasModifiers) ? '<br/><i>Note:</i> Modifiers are currently not considered by this static analysis.' : '' |
||||||
|
comments += (multipleContractsWithSameName) ? '<br/><i>Note:</i> Import aliases are currently not supported by this static analysis.' : '' |
||||||
|
if (func.potentiallyshouldBeConst) { |
||||||
|
warnings.push({ |
||||||
|
warning: `<i>${funcName}</i>: Potentially should be constant but is not. ${comments}`, |
||||||
|
location: func.src, |
||||||
|
more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions' |
||||||
|
}) |
||||||
|
} else { |
||||||
|
warnings.push({ |
||||||
|
warning: `<i>${funcName}</i>: Is constant but potentially should not be. ${comments}`, |
||||||
|
location: func.src, |
||||||
|
more: 'http://solidity.readthedocs.io/en/develop/contracts.html#constant-functions' |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
return warnings |
||||||
|
} |
||||||
|
|
||||||
|
function getContext (callGraph, currentContract, func) { |
||||||
|
return { callGraph: callGraph, currentContract: currentContract, stateVariables: getStateVariables(currentContract, func) } |
||||||
|
} |
||||||
|
|
||||||
|
function getStateVariables (contract, func) { |
||||||
|
return contract.stateVariables.concat(func.localVariables.filter(common.isStorageVariableDeclaration)) |
||||||
|
} |
||||||
|
|
||||||
|
function checkIfShouldBeConstant (startFuncName, context) { |
||||||
|
return !fcallGraph.analyseCallGraph(context.callGraph, startFuncName, context, isConstBreaker) |
||||||
|
} |
||||||
|
|
||||||
|
function isConstBreaker (node, context) { |
||||||
|
return common.isWriteOnStateVariable(node, context.stateVariables) || |
||||||
|
common.isLowLevelCall(node) || |
||||||
|
common.isTransfer(node) || |
||||||
|
isCallOnNonConstExternalInterfaceFunction(node, context) || |
||||||
|
common.isCallToNonConstLocalFunction(node) || |
||||||
|
common.isInlineAssembly(node) || |
||||||
|
common.isNewExpression(node) |
||||||
|
} |
||||||
|
|
||||||
|
function isCallOnNonConstExternalInterfaceFunction (node, context) { |
||||||
|
if (common.isExternalDirectCall(node)) { |
||||||
|
var func = fcallGraph.resolveCallGraphSymbol(context.callGraph, common.getFullQualifiedFunctionCallIdent(context.currentContract, node)) |
||||||
|
return !func || (func && !common.isConstantFunction(func.node.node)) |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
name: name, |
||||||
|
description: desc, |
||||||
|
category: categories.MISC, |
||||||
|
Module: constantFunctions |
||||||
|
} |
@ -0,0 +1,114 @@ |
|||||||
|
'use strict' |
||||||
|
|
||||||
|
var common = require('./staticAnalysisCommon') |
||||||
|
|
||||||
|
function buildLocalFuncCallGraphInternal (functions, nodeFilter, extractNodeIdent, extractFuncDefIdent) { |
||||||
|
var callGraph = {} |
||||||
|
functions.forEach((func) => { |
||||||
|
var calls = func.relevantNodes |
||||||
|
.filter(nodeFilter) |
||||||
|
.map(extractNodeIdent) |
||||||
|
.filter((name) => name !== extractFuncDefIdent(func)) // filter self recursive call
|
||||||
|
|
||||||
|
callGraph[extractFuncDefIdent(func)] = { node: func, calls: calls } |
||||||
|
}) |
||||||
|
|
||||||
|
return callGraph |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Builds a function call graph for the current contracts. |
||||||
|
* Example Contract call graph: |
||||||
|
* |
||||||
|
* { |
||||||
|
* "KingOfTheEtherThrone": { |
||||||
|
* "contracts": {...}, // Contract node as defined in abstractAstView.js
|
||||||
|
* "functions": { |
||||||
|
* "KingOfTheEtherThrone.claimThrone(string memory)": { // function in KingOfEtherThrone
|
||||||
|
* "node": {...}, // function node as defined in abstractAstView.js
|
||||||
|
* "calls": { // list of full qualified function names which are called form this function
|
||||||
|
* } |
||||||
|
* } |
||||||
|
* } |
||||||
|
* }, |
||||||
|
* "foo": { |
||||||
|
* "contract": {...}, // Contract node as definded in abstractAstView.js
|
||||||
|
* "functions": {} // map from full qualified function name to func node
|
||||||
|
* } |
||||||
|
* } |
||||||
|
* |
||||||
|
* @contracts {list contracts} Expects as input the contract structure defined in abstractAstView.js |
||||||
|
* @return {map (string -> Contract Call Graph)} returns map from contract name to contract call graph |
||||||
|
*/ |
||||||
|
function buildGlobalFuncCallGraph (contracts) { |
||||||
|
var callGraph = {} |
||||||
|
contracts.forEach((contract) => { |
||||||
|
var filterNodes = (node) => { return common.isLocalCallGraphRelevantNode(node) || common.isExternalDirectCall(node) } |
||||||
|
var getNodeIdent = (node) => { return common.getFullQualifiedFunctionCallIdent(contract.node, node) } |
||||||
|
var getFunDefIdent = (funcDef) => { return common.getFullQuallyfiedFuncDefinitionIdent(contract.node, funcDef.node, funcDef.parameters) } |
||||||
|
|
||||||
|
callGraph[common.getContractName(contract.node)] = { contract: contract, functions: buildLocalFuncCallGraphInternal(contract.functions, filterNodes, getNodeIdent, getFunDefIdent) } |
||||||
|
}) |
||||||
|
|
||||||
|
return callGraph |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Walks through the call graph from a defined starting function, true if nodeCheck holds for every relevant node in the callgraph |
||||||
|
* @callGraph {callGraph} As returned by buildGlobalFuncCallGraph |
||||||
|
* @funcName {string} full qualified name of the starting function |
||||||
|
* @context {Object} provides additional context information that can be used by the nodeCheck function |
||||||
|
* @nodeCheck {(ASTNode, context) -> bool} applied on every relevant node in the call graph |
||||||
|
* @return {bool} returns map from contract name to contract call graph |
||||||
|
*/ |
||||||
|
function analyseCallGraph (callGraph, funcName, context, nodeCheck) { |
||||||
|
return analyseCallGraphInternal(callGraph, funcName, context, (a, b) => a || b, nodeCheck, {}) |
||||||
|
} |
||||||
|
|
||||||
|
function analyseCallGraphInternal (callGraph, funcName, context, combinator, nodeCheck, visited) { |
||||||
|
var current = resolveCallGraphSymbol(callGraph, funcName) |
||||||
|
|
||||||
|
if (current === undefined || visited[funcName] === true) return true |
||||||
|
visited[funcName] = true |
||||||
|
|
||||||
|
return combinator(current.node.relevantNodes.reduce((acc, val) => combinator(acc, nodeCheck(val, context)), false), |
||||||
|
current.calls.reduce((acc, val) => combinator(acc, analyseCallGraphInternal(callGraph, val, context, combinator, nodeCheck, visited)), false)) |
||||||
|
} |
||||||
|
|
||||||
|
function resolveCallGraphSymbol (callGraph, funcName) { |
||||||
|
return resolveCallGraphSymbolInternal(callGraph, funcName, false) |
||||||
|
} |
||||||
|
|
||||||
|
function resolveCallGraphSymbolInternal (callGraph, funcName, silent) { |
||||||
|
var current |
||||||
|
if (funcName.includes('.')) { |
||||||
|
var parts = funcName.split('.') |
||||||
|
var contractPart = parts[0] |
||||||
|
var functionPart = parts[1] |
||||||
|
var currentContract = callGraph[contractPart] |
||||||
|
if (!(currentContract === undefined)) { |
||||||
|
current = currentContract.functions[funcName] |
||||||
|
// resolve inheritance hierarchy
|
||||||
|
if (current === undefined) { |
||||||
|
// resolve inheritance lookup in linearized fashion
|
||||||
|
var inheritsFromNames = currentContract.contract.inheritsFrom.reverse() |
||||||
|
for (var i = 0; i < inheritsFromNames.length; i++) { |
||||||
|
var res = resolveCallGraphSymbolInternal(callGraph, inheritsFromNames[i] + '.' + functionPart, true) |
||||||
|
if (!(res === undefined)) return res |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (!silent) console.log(`static analysis functionCallGraph.js: Contract ${contractPart} not found in function call graph.`) |
||||||
|
} |
||||||
|
} else { |
||||||
|
throw new Error('functionCallGraph.js: function does not have full qualified name.') |
||||||
|
} |
||||||
|
if (current === undefined && !silent) console.log(`static analysis functionCallGraph.js: ${funcName} not found in function call graph.`) |
||||||
|
return current |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
analyseCallGraph: analyseCallGraph, |
||||||
|
buildGlobalFuncCallGraph: buildGlobalFuncCallGraph, |
||||||
|
resolveCallGraphSymbol: resolveCallGraphSymbol |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
var name = 'Inline Assembly' |
||||||
|
var desc = 'Use of Inline Assembly' |
||||||
|
var categories = require('./categories') |
||||||
|
var common = require('./staticAnalysisCommon') |
||||||
|
|
||||||
|
function inlineAssembly () { |
||||||
|
this.inlineAssNodes = [] |
||||||
|
} |
||||||
|
|
||||||
|
inlineAssembly.prototype.visit = function (node) { |
||||||
|
if (common.isInlineAssembly(node)) this.inlineAssNodes.push(node) |
||||||
|
} |
||||||
|
|
||||||
|
inlineAssembly.prototype.report = function (compilationResults) { |
||||||
|
return this.inlineAssNodes.map((node) => { |
||||||
|
return { |
||||||
|
warning: `CAUTION: The Contract uses inline assembly, this is only advised in rare cases. Additionally static analysis modules do not parse inline Assembly, this can lead to wrong analysis results.`, |
||||||
|
location: node.src, |
||||||
|
more: 'http://solidity.readthedocs.io/en/develop/assembly.html#solidity-assembly' |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
name: name, |
||||||
|
description: desc, |
||||||
|
category: categories.SECURITY, |
||||||
|
Module: inlineAssembly |
||||||
|
} |
@ -1,5 +1,12 @@ |
|||||||
module.exports = [ |
module.exports = [ |
||||||
require('./txOrigin'), |
require('./txOrigin'), |
||||||
require('./gasCosts'), |
require('./gasCosts'), |
||||||
require('./thisLocal') |
require('./thisLocal'), |
||||||
|
require('./checksEffectsInteraction'), |
||||||
|
require('./constantFunctions') |
||||||
|
// require('./similarVariableNames.js'),
|
||||||
|
// require('./inlineAssembly'),
|
||||||
|
// require('./blockTimestamp'),
|
||||||
|
// require('./lowLevelCalls'),
|
||||||
|
// require('./blockBlockhash')
|
||||||
] |
] |
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,207 @@ |
|||||||
|
var test = require('tape') |
||||||
|
|
||||||
|
var StatRunner = require('../../src/app/staticanalysis/staticAnalysisRunner') |
||||||
|
// const util = require('util')
|
||||||
|
|
||||||
|
var solc = require('solc/wrapper') |
||||||
|
var compiler = solc(require('../../soljson')) |
||||||
|
|
||||||
|
var fs = require('fs') |
||||||
|
var path = require('path') |
||||||
|
|
||||||
|
var testFiles = [ |
||||||
|
'KingOfTheEtherThrone.sol', |
||||||
|
'assembly.sol', |
||||||
|
'ballot.sol', |
||||||
|
'ballot_reentrant.sol', |
||||||
|
'ballot_withoutWarnings.sol', |
||||||
|
'cross_contract.sol', |
||||||
|
'inheritance.sol', |
||||||
|
'modifier1.sol', |
||||||
|
'modifier2.sol', |
||||||
|
'notReentrant.sol', |
||||||
|
'structReentrant.sol', |
||||||
|
'thisLocal.sol', |
||||||
|
'globals.sol', |
||||||
|
'library.sol' |
||||||
|
] |
||||||
|
|
||||||
|
var testFileAsts = {} |
||||||
|
|
||||||
|
testFiles.forEach((fileName) => { |
||||||
|
var contents = fs.readFileSync(path.join(__dirname, 'test-contracts', fileName), 'utf8') |
||||||
|
testFileAsts[fileName] = compiler.compile(contents, 0) |
||||||
|
}) |
||||||
|
|
||||||
|
test('Integration test thisLocal.js', function (t) { |
||||||
|
t.plan(testFiles.length) |
||||||
|
|
||||||
|
var module = require('../../src/app/staticanalysis/modules/thisLocal') |
||||||
|
|
||||||
|
var lengthCheck = { |
||||||
|
'KingOfTheEtherThrone.sol': 0, |
||||||
|
'assembly.sol': 0, |
||||||
|
'ballot.sol': 0, |
||||||
|
'ballot_reentrant.sol': 1, |
||||||
|
'ballot_withoutWarnings.sol': 0, |
||||||
|
'cross_contract.sol': 0, |
||||||
|
'inheritance.sol': 0, |
||||||
|
'modifier1.sol': 0, |
||||||
|
'modifier2.sol': 0, |
||||||
|
'notReentrant.sol': 0, |
||||||
|
'structReentrant.sol': 0, |
||||||
|
'thisLocal.sol': 1, |
||||||
|
'globals.sol': 0, |
||||||
|
'library.sol': 0 |
||||||
|
} |
||||||
|
|
||||||
|
runModuleOnFiles(module, t, (file, report) => { |
||||||
|
t.equal(report.length, lengthCheck[file], `${file} has right amount of this local warnings`) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
test('Integration test checksEffectsInteraction.js', function (t) { |
||||||
|
t.plan(testFiles.length) |
||||||
|
|
||||||
|
var module = require('../../src/app/staticanalysis/modules/checksEffectsInteraction') |
||||||
|
|
||||||
|
var lengthCheck = { |
||||||
|
'KingOfTheEtherThrone.sol': 1, |
||||||
|
'assembly.sol': 1, |
||||||
|
'ballot.sol': 0, |
||||||
|
'ballot_reentrant.sol': 1, |
||||||
|
'ballot_withoutWarnings.sol': 0, |
||||||
|
'cross_contract.sol': 0, |
||||||
|
'inheritance.sol': 1, |
||||||
|
'modifier1.sol': 0, |
||||||
|
'modifier2.sol': 0, |
||||||
|
'notReentrant.sol': 0, |
||||||
|
'structReentrant.sol': 1, |
||||||
|
'thisLocal.sol': 0, |
||||||
|
'globals.sol': 1, |
||||||
|
'library.sol': 1 |
||||||
|
} |
||||||
|
|
||||||
|
runModuleOnFiles(module, t, (file, report) => { |
||||||
|
t.equal(report.length, lengthCheck[file], `${file} has right amount of checks-effects-interaction warnings`) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
test('Integration test constantFunctions.js', function (t) { |
||||||
|
t.plan(testFiles.length) |
||||||
|
|
||||||
|
var module = require('../../src/app/staticanalysis/modules/constantFunctions') |
||||||
|
|
||||||
|
var lengthCheck = { |
||||||
|
'KingOfTheEtherThrone.sol': 0, |
||||||
|
'assembly.sol': 0, |
||||||
|
'ballot.sol': 0, |
||||||
|
'ballot_reentrant.sol': 0, |
||||||
|
'ballot_withoutWarnings.sol': 0, |
||||||
|
'cross_contract.sol': 1, |
||||||
|
'inheritance.sol': 0, |
||||||
|
'modifier1.sol': 1, |
||||||
|
'modifier2.sol': 0, |
||||||
|
'notReentrant.sol': 0, |
||||||
|
'structReentrant.sol': 0, |
||||||
|
'thisLocal.sol': 1, |
||||||
|
'globals.sol': 0, |
||||||
|
'library.sol': 1 |
||||||
|
} |
||||||
|
|
||||||
|
runModuleOnFiles(module, t, (file, report) => { |
||||||
|
t.equal(report.length, lengthCheck[file], `${file} has right amount of constant warnings`) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
test('Integration test inlineAssembly.js', function (t) { |
||||||
|
t.plan(testFiles.length) |
||||||
|
|
||||||
|
var module = require('../../src/app/staticanalysis/modules/inlineAssembly') |
||||||
|
|
||||||
|
var lengthCheck = { |
||||||
|
'KingOfTheEtherThrone.sol': 0, |
||||||
|
'assembly.sol': 2, |
||||||
|
'ballot.sol': 0, |
||||||
|
'ballot_reentrant.sol': 0, |
||||||
|
'ballot_withoutWarnings.sol': 0, |
||||||
|
'cross_contract.sol': 0, |
||||||
|
'inheritance.sol': 0, |
||||||
|
'modifier1.sol': 0, |
||||||
|
'modifier2.sol': 0, |
||||||
|
'notReentrant.sol': 0, |
||||||
|
'structReentrant.sol': 0, |
||||||
|
'thisLocal.sol': 0, |
||||||
|
'globals.sol': 0, |
||||||
|
'library.sol': 0 |
||||||
|
} |
||||||
|
|
||||||
|
runModuleOnFiles(module, t, (file, report) => { |
||||||
|
t.equal(report.length, lengthCheck[file], `${file} has right amount of inline assembly warnings`) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
test('Integration test txOrigin.js', function (t) { |
||||||
|
t.plan(testFiles.length) |
||||||
|
|
||||||
|
var module = require('../../src/app/staticanalysis/modules/txOrigin') |
||||||
|
|
||||||
|
var lengthCheck = { |
||||||
|
'KingOfTheEtherThrone.sol': 0, |
||||||
|
'assembly.sol': 1, |
||||||
|
'ballot.sol': 0, |
||||||
|
'ballot_reentrant.sol': 0, |
||||||
|
'ballot_withoutWarnings.sol': 0, |
||||||
|
'cross_contract.sol': 0, |
||||||
|
'inheritance.sol': 0, |
||||||
|
'modifier1.sol': 0, |
||||||
|
'modifier2.sol': 0, |
||||||
|
'notReentrant.sol': 0, |
||||||
|
'structReentrant.sol': 0, |
||||||
|
'thisLocal.sol': 0, |
||||||
|
'globals.sol': 1, |
||||||
|
'library.sol': 0 |
||||||
|
} |
||||||
|
|
||||||
|
runModuleOnFiles(module, t, (file, report) => { |
||||||
|
t.equal(report.length, lengthCheck[file], `${file} has right amount of tx.origin warnings`) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
test('Integration test gasCosts.js', function (t) { |
||||||
|
t.plan(testFiles.length) |
||||||
|
|
||||||
|
var module = require('../../src/app/staticanalysis/modules/gasCosts') |
||||||
|
|
||||||
|
var lengthCheck = { |
||||||
|
'KingOfTheEtherThrone.sol': 2, |
||||||
|
'assembly.sol': 2, |
||||||
|
'ballot.sol': 3, |
||||||
|
'ballot_reentrant.sol': 2, |
||||||
|
'ballot_withoutWarnings.sol': 0, |
||||||
|
'cross_contract.sol': 1, |
||||||
|
'inheritance.sol': 1, |
||||||
|
'modifier1.sol': 0, |
||||||
|
'modifier2.sol': 1, |
||||||
|
'notReentrant.sol': 1, |
||||||
|
'structReentrant.sol': 1, |
||||||
|
'thisLocal.sol': 2, |
||||||
|
'globals.sol': 1, |
||||||
|
'library.sol': 1 |
||||||
|
} |
||||||
|
|
||||||
|
runModuleOnFiles(module, t, (file, report) => { |
||||||
|
t.equal(report.length, lengthCheck[file], `${file} has right amount of gasCost warnings`) |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
// #################### Helpers
|
||||||
|
function runModuleOnFiles (module, t, cb) { |
||||||
|
var statRunner = new StatRunner() |
||||||
|
|
||||||
|
testFiles.forEach((fileName) => { |
||||||
|
statRunner.runWithModuleList(testFileAsts[fileName], [{ name: module.name, mod: new module.Module() }], (reports) => { |
||||||
|
cb(fileName, reports[0].report) |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
// return value send |
||||||
|
contract KingOfTheEtherThrone{ |
||||||
|
struct Monarch { |
||||||
|
// address of the king . |
||||||
|
address ethAddr ; |
||||||
|
string name ; |
||||||
|
// how much he pays to previous king |
||||||
|
uint claimPrice ; |
||||||
|
uint coronationTimestamp; |
||||||
|
} |
||||||
|
Monarch public currentMonarch ; |
||||||
|
|
||||||
|
function claimThrone ( string name ) { |
||||||
|
address wizardAddress; |
||||||
|
uint compensation = 100; |
||||||
|
uint valuePaid = 10; |
||||||
|
|
||||||
|
if ( currentMonarch.ethAddr != wizardAddress ) |
||||||
|
if (currentMonarch.ethAddr.send( compensation )) throw; |
||||||
|
|
||||||
|
currentMonarch = Monarch(msg.sender,name,valuePaid,block.timestamp); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
pragma solidity ^0.4.9; |
||||||
|
contract test { |
||||||
|
|
||||||
|
address owner; |
||||||
|
|
||||||
|
function at(address _addr) returns (bytes o_code) { |
||||||
|
assembly { |
||||||
|
// retrieve the size of the code, this needs assembly |
||||||
|
let size := extcodesize(_addr) |
||||||
|
// allocate output byte array - this could also be done without assembly |
||||||
|
// by using o_code = new bytes(size) |
||||||
|
o_code := mload(0x40) |
||||||
|
// new "memory end" including padding |
||||||
|
mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) |
||||||
|
// store length in memory |
||||||
|
mstore(o_code, size) |
||||||
|
// actually retrieve the code, this needs assembly |
||||||
|
extcodecopy(_addr, add(o_code, 0x20), 0, size) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function bla() { |
||||||
|
if(tx.origin == owner) |
||||||
|
msg.sender.send(19); |
||||||
|
assembly { |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,145 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
/// @title Voting with delegation. |
||||||
|
contract Ballot { |
||||||
|
// This declares a new complex type which will |
||||||
|
// be used for variables later. |
||||||
|
// It will represent a single voter. |
||||||
|
struct Voter { |
||||||
|
uint weight; // weight is accumulated by delegation |
||||||
|
bool voted; // if true, that person already voted |
||||||
|
address delegate; // person delegated to |
||||||
|
uint vote; // index of the voted proposal |
||||||
|
} |
||||||
|
|
||||||
|
// This is a type for a single proposal. |
||||||
|
struct Proposal |
||||||
|
{ |
||||||
|
bytes32 name; // short name (up to 32 bytes) |
||||||
|
uint voteCount; // number of accumulated votes |
||||||
|
} |
||||||
|
|
||||||
|
address public chairperson; |
||||||
|
|
||||||
|
// This declares a state variable that |
||||||
|
// stores a `Voter` struct for each possible address. |
||||||
|
mapping(address => Voter) public voters; |
||||||
|
|
||||||
|
// A dynamically-sized array of `Proposal` structs. |
||||||
|
Proposal[] public proposals; |
||||||
|
|
||||||
|
/// Create a new ballot to choose one of `proposalNames`. |
||||||
|
function Ballot(bytes32[] proposalNames) { |
||||||
|
chairperson = msg.sender; |
||||||
|
voters[chairperson].weight = 1; |
||||||
|
|
||||||
|
// For each of the provided proposal names, |
||||||
|
// create a new proposal object and add it |
||||||
|
// to the end of the array. |
||||||
|
for (uint i = 0; i < proposalNames.length; i++) { |
||||||
|
// `Proposal({...})` creates a temporary |
||||||
|
// Proposal object and `proposals.push(...)` |
||||||
|
// appends it to the end of `proposals`. |
||||||
|
proposals.push(Proposal({ |
||||||
|
name: proposalNames[i], |
||||||
|
voteCount: 0 |
||||||
|
})); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Give `voter` the right to vote on this ballot. |
||||||
|
// May only be called by `chairperson`. |
||||||
|
function giveRightToVote(address voter) { |
||||||
|
if (msg.sender != chairperson || voters[voter].voted) { |
||||||
|
// `throw` terminates and reverts all changes to |
||||||
|
// the state and to Ether balances. It is often |
||||||
|
// a good idea to use this if functions are |
||||||
|
// called incorrectly. But watch out, this |
||||||
|
// will also consume all provided gas. |
||||||
|
throw; |
||||||
|
} |
||||||
|
voters[voter].weight = 1; |
||||||
|
} |
||||||
|
|
||||||
|
/// Delegate your vote to the voter `to`. |
||||||
|
function delegate(address to) { |
||||||
|
// assigns reference |
||||||
|
Voter sender = voters[msg.sender]; |
||||||
|
if (sender.voted) |
||||||
|
throw; |
||||||
|
|
||||||
|
// Forward the delegation as long as |
||||||
|
// `to` also delegated. |
||||||
|
// In general, such loops are very dangerous, |
||||||
|
// because if they run too long, they might |
||||||
|
// need more gas than is available in a block. |
||||||
|
// In this case, the delegation will not be executed, |
||||||
|
// but in other situations, such loops might |
||||||
|
// cause a contract to get "stuck" completely. |
||||||
|
while ( |
||||||
|
voters[to].delegate != address(0) && |
||||||
|
voters[to].delegate != msg.sender |
||||||
|
) { |
||||||
|
to = voters[to].delegate; |
||||||
|
} |
||||||
|
|
||||||
|
// We found a loop in the delegation, not allowed. |
||||||
|
if (to == msg.sender) { |
||||||
|
throw; |
||||||
|
} |
||||||
|
|
||||||
|
// Since `sender` is a reference, this |
||||||
|
// modifies `voters[msg.sender].voted` |
||||||
|
sender.voted = true; |
||||||
|
sender.delegate = to; |
||||||
|
Voter delegate = voters[to]; |
||||||
|
if (delegate.voted) { |
||||||
|
// If the delegate already voted, |
||||||
|
// directly add to the number of votes |
||||||
|
proposals[delegate.vote].voteCount += sender.weight; |
||||||
|
} else { |
||||||
|
// If the delegate did not vote yet, |
||||||
|
// add to her weight. |
||||||
|
delegate.weight += sender.weight; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Give your vote (including votes delegated to you) |
||||||
|
/// to proposal `proposals[proposal].name`. |
||||||
|
function vote(uint proposal) { |
||||||
|
Voter sender = voters[msg.sender]; |
||||||
|
if (sender.voted) |
||||||
|
throw; |
||||||
|
sender.voted = true; |
||||||
|
sender.vote = proposal; |
||||||
|
|
||||||
|
// If `proposal` is out of the range of the array, |
||||||
|
// this will throw automatically and revert all |
||||||
|
// changes. |
||||||
|
proposals[proposal].voteCount += sender.weight; |
||||||
|
} |
||||||
|
|
||||||
|
/// @dev Computes the winning proposal taking all |
||||||
|
/// previous votes into account. |
||||||
|
function winningProposal() constant |
||||||
|
returns (uint winningProposal) |
||||||
|
{ |
||||||
|
uint winningVoteCount = 0; |
||||||
|
for (uint p = 0; p < proposals.length; p++) { |
||||||
|
if (proposals[p].voteCount > winningVoteCount) { |
||||||
|
winningVoteCount = proposals[p].voteCount; |
||||||
|
winningProposal = p; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Calls winningProposal() function to get the index |
||||||
|
// of the winner contained in the proposals array and then |
||||||
|
// returns the name of the winner |
||||||
|
function winnerName() constant |
||||||
|
returns (bytes32 winnerName) |
||||||
|
{ |
||||||
|
winnerName = proposals[winningProposal()].name; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,101 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
contract InfoFeed { |
||||||
|
function info() payable returns (uint ret); |
||||||
|
function call1(uint a) payable returns (bool); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
contract Ballot { |
||||||
|
|
||||||
|
InfoFeed feed; |
||||||
|
|
||||||
|
struct Voter { |
||||||
|
uint weight; |
||||||
|
bool voted; |
||||||
|
uint8 vote; |
||||||
|
address delegate; |
||||||
|
} |
||||||
|
struct Proposal { |
||||||
|
uint voteCount; |
||||||
|
} |
||||||
|
|
||||||
|
address chairperson; |
||||||
|
mapping(address => Voter) voters; |
||||||
|
Proposal[] proposals; |
||||||
|
|
||||||
|
function send1(address a) { |
||||||
|
giveRightToVote(a,a); |
||||||
|
} |
||||||
|
|
||||||
|
/// Create a new ballot with $(_numProposals) different proposals. |
||||||
|
function Ballot(uint8 _numProposals) { |
||||||
|
address d; |
||||||
|
if(!d.delegatecall.gas(800)('function_name', 'arg1', 'arg2')) throw; |
||||||
|
if(!d.callcode.gas(800)('function_name', 'arg1', 'arg2')) throw; |
||||||
|
if(!d.call.value(10).gas(800)('function_name', 'arg1', 'arg2')) throw; |
||||||
|
if(!d.call.value(10).gas(800)('function_name', 'arg1', 'arg2')) throw; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!msg.sender.send(1 wei)) throw; |
||||||
|
if(!d.call('function_name', 'arg1', 'arg2')) throw; |
||||||
|
|
||||||
|
|
||||||
|
uint a = now; |
||||||
|
uint c = block.timestamp; |
||||||
|
if(block.timestamp < 100){} |
||||||
|
chairperson = msg.sender; |
||||||
|
voters[chairperson].weight = 1; |
||||||
|
proposals.length = _numProposals; |
||||||
|
if(!d.send(1 wei)) throw; |
||||||
|
feed.info.value(10).gas(800)(); |
||||||
|
|
||||||
|
feed.call1(1); |
||||||
|
|
||||||
|
this.send1(d); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/// Give $(voter) the right to vote on this ballot. |
||||||
|
/// May only be called by $(chairperson). |
||||||
|
function giveRightToVote(address voter, address b) payable returns (bool){ |
||||||
|
if (msg.sender != chairperson || voters[voter].voted) return; |
||||||
|
voters[voter].weight = 1; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/// Delegate your vote to the voter $(to). |
||||||
|
function delegate(address to) { |
||||||
|
Voter sender = voters[msg.sender]; // assigns reference |
||||||
|
if (sender.voted) return; |
||||||
|
while (voters[to].delegate != address(0) && voters[to].delegate != msg.sender) |
||||||
|
to = voters[to].delegate; |
||||||
|
if (to == msg.sender) return; |
||||||
|
sender.voted = true; |
||||||
|
sender.delegate = to; |
||||||
|
Voter delegate = voters[to]; |
||||||
|
if (delegate.voted) |
||||||
|
proposals[delegate.vote].voteCount += sender.weight; |
||||||
|
else |
||||||
|
delegate.weight += sender.weight; |
||||||
|
} |
||||||
|
|
||||||
|
/// Give a single vote to proposal $(proposal). |
||||||
|
function vote(uint8 proposal) { |
||||||
|
Voter sender = voters[msg.sender]; |
||||||
|
if (sender.voted || proposal >= proposals.length) return; |
||||||
|
sender.voted = true; |
||||||
|
sender.vote = proposal; |
||||||
|
proposals[proposal].voteCount += sender.weight; |
||||||
|
} |
||||||
|
|
||||||
|
function winningProposal() constant returns (uint8 winningProposal) { |
||||||
|
uint256 winningVoteCount = 0; |
||||||
|
for (uint8 proposal = 0; proposal < proposals.length; proposal++) |
||||||
|
if (proposals[proposal].voteCount > winningVoteCount) { |
||||||
|
winningVoteCount = proposals[proposal].voteCount; |
||||||
|
winningProposal = proposal; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
/// @title Voting with delegation. |
||||||
|
contract Ballot { |
||||||
|
|
||||||
|
struct Proposal |
||||||
|
{ |
||||||
|
bytes32 name; // short name (up to 32 bytes) |
||||||
|
uint voteCount; // number of accumulated votes |
||||||
|
} |
||||||
|
|
||||||
|
// A dynamically-sized array of `Proposal` structs. |
||||||
|
Proposal[] public proposals; |
||||||
|
|
||||||
|
/// @dev Computes the winning proposal taking all |
||||||
|
/// previous votes into account. |
||||||
|
function winningProposal() constant |
||||||
|
returns (uint winningProposal) |
||||||
|
{ |
||||||
|
winningProposal = 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Calls winningProposal() function to get the index |
||||||
|
// of the winner contained in the proposals array and then |
||||||
|
// returns the name of the winner |
||||||
|
function winnerName() constant |
||||||
|
returns (bytes32 winnerName) |
||||||
|
{ |
||||||
|
winnerName = proposals[winningProposal()].name; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
contract a { |
||||||
|
|
||||||
|
uint x; |
||||||
|
|
||||||
|
function foo() { |
||||||
|
x++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
contract b { |
||||||
|
a x; |
||||||
|
function bar() constant { |
||||||
|
address a; |
||||||
|
a.send(100 wei); |
||||||
|
x.foo(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
pragma solidity ^0.4.9; |
||||||
|
contract bla { |
||||||
|
uint brr; |
||||||
|
function duper() { |
||||||
|
brr++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
contract a is bla { |
||||||
|
|
||||||
|
function blub() { |
||||||
|
brr++; |
||||||
|
} |
||||||
|
|
||||||
|
function r () { |
||||||
|
address a; |
||||||
|
bytes32 hash; |
||||||
|
uint8 v; |
||||||
|
bytes32 r; |
||||||
|
bytes32 s; |
||||||
|
|
||||||
|
block.blockhash(1); |
||||||
|
block.coinbase; |
||||||
|
block.difficulty; |
||||||
|
block.gaslimit; |
||||||
|
block.number; |
||||||
|
block.timestamp; |
||||||
|
msg.data; |
||||||
|
msg.gas; |
||||||
|
msg.sender; |
||||||
|
msg.value; |
||||||
|
now; |
||||||
|
tx.gasprice; |
||||||
|
tx.origin; |
||||||
|
//assert(now == block.timestamp); |
||||||
|
//require(now == block.timestamp); |
||||||
|
keccak256(a); |
||||||
|
sha3(a); |
||||||
|
sha256(a); |
||||||
|
ripemd160(a); |
||||||
|
ecrecover(hash, v, r, s); |
||||||
|
addmod(1, 2, 2); |
||||||
|
mulmod(4,4,12); |
||||||
|
|
||||||
|
a.balance; |
||||||
|
blub(); |
||||||
|
a.send(a.balance); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
super.duper(); |
||||||
|
//a.transfer(a.balance); |
||||||
|
selfdestruct(a); |
||||||
|
//revert(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
pragma solidity ^0.4.9; |
||||||
|
|
||||||
|
contract r { |
||||||
|
function s() constant {} |
||||||
|
} |
||||||
|
|
||||||
|
contract a is r { |
||||||
|
uint x = 1; |
||||||
|
|
||||||
|
function getX() constant returns (uint) { |
||||||
|
return x; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
contract b is a { |
||||||
|
uint y = 2; |
||||||
|
uint x = 3; |
||||||
|
|
||||||
|
|
||||||
|
function getY(uint z, bool r) returns (uint) { |
||||||
|
return y++; |
||||||
|
} |
||||||
|
|
||||||
|
function getY(string storage n) internal constant returns (uint) { return 10; } |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
contract c is b { |
||||||
|
string x; |
||||||
|
|
||||||
|
function d() returns (uint a, uint b) { |
||||||
|
//d(); |
||||||
|
//sha3("bla"); |
||||||
|
msg.sender.call.gas(200000).value(this.balance)(bytes4(sha3("pay()"))); |
||||||
|
//x++; |
||||||
|
getY(x); |
||||||
|
a = getX() + getY(1, false); |
||||||
|
b = getX() + getY(x); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
library Set { |
||||||
|
// We define a new struct datatype that will be used to |
||||||
|
// hold its data in the calling contract. |
||||||
|
struct Data { mapping(uint => bool) flags; } |
||||||
|
|
||||||
|
// Note that the first parameter is of type "storage |
||||||
|
// reference" and thus only its storage address and not |
||||||
|
// its contents is passed as part of the call. This is a |
||||||
|
// special feature of library functions. It is idiomatic |
||||||
|
// to call the first parameter 'self', if the function can |
||||||
|
// be seen as a method of that object. |
||||||
|
function insert(Data storage self, uint value) |
||||||
|
returns (bool) |
||||||
|
{ |
||||||
|
if (self.flags[value]) |
||||||
|
return false; // already there |
||||||
|
self.flags[value] = true; |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
function remove(Data storage self, uint value) |
||||||
|
returns (bool) |
||||||
|
{ |
||||||
|
if (!self.flags[value]) |
||||||
|
return false; // not there |
||||||
|
self.flags[value] = false; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
function contains(Data storage self, uint value) |
||||||
|
returns (bool) |
||||||
|
{ |
||||||
|
return self.flags[value]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
contract C { |
||||||
|
Set.Data knownValues; |
||||||
|
|
||||||
|
function register(uint value) { |
||||||
|
// The library functions can be called without a |
||||||
|
// specific instance of the library, since the |
||||||
|
// "instance" will be the current contract. |
||||||
|
address a; |
||||||
|
a.send(10 wei); |
||||||
|
if (!Set.insert(knownValues, value)) |
||||||
|
throw; |
||||||
|
} |
||||||
|
// In this contract, we can also directly access knownValues.flags, if we want. |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
contract test { |
||||||
|
|
||||||
|
address owner; |
||||||
|
|
||||||
|
modifier onlyOwner { |
||||||
|
var a = 0; |
||||||
|
if (msg.sender != owner) |
||||||
|
throw; |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function b(address a) onlyOwner returns (bool) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
contract owned { |
||||||
|
|
||||||
|
uint r=0; |
||||||
|
|
||||||
|
modifier ntimes(uint n) { |
||||||
|
for(uint i=0;i<n-1;i++){ |
||||||
|
_; |
||||||
|
} |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
modifier plus100 { |
||||||
|
var bla=1; |
||||||
|
r+=100; |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function a() ntimes(10) plus100 payable returns (uint) { |
||||||
|
{ |
||||||
|
uint bla=5; |
||||||
|
} |
||||||
|
r += bla; |
||||||
|
return r; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,13 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
contract Fund { |
||||||
|
/// Mapping of ether shares of the contract. |
||||||
|
mapping(address => uint) shares; |
||||||
|
/// Withdraw your share. |
||||||
|
function withdraw() { |
||||||
|
var share = shares[msg.sender]; |
||||||
|
shares[msg.sender] = 0; |
||||||
|
if (!msg.sender.send(share)) |
||||||
|
throw; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
contract InfoFeed { |
||||||
|
uint c; |
||||||
|
function info() constant returns (uint ret) {return c;} |
||||||
|
function call1(uint a) constant returns (bool) {return true;} |
||||||
|
} |
||||||
|
|
||||||
|
// THIS CONTRACT CONTAINS A BUG - DO NOT USE |
||||||
|
contract Fund { |
||||||
|
/// Mapping of ether shares of the contract. |
||||||
|
//mapping(address => uint) shares; |
||||||
|
/// Withdraw your share. |
||||||
|
|
||||||
|
uint c = 0; |
||||||
|
function withdraw() constant { |
||||||
|
InfoFeed f; |
||||||
|
|
||||||
|
|
||||||
|
//shares[msg.sender] /= 1; |
||||||
|
|
||||||
|
f.info(); |
||||||
|
|
||||||
|
//if (msg.sender.send(shares[msg.sender])) throw; |
||||||
|
// shares[msg.sender] = 0; |
||||||
|
|
||||||
|
|
||||||
|
b(true, false); |
||||||
|
//shares[msg.sender]++; |
||||||
|
//c++; |
||||||
|
|
||||||
|
} |
||||||
|
mapping(address => uint) shares; |
||||||
|
|
||||||
|
function b(bool a, bool b) returns (bool) { |
||||||
|
mapping(address => uint) c = shares; |
||||||
|
c[msg.sender] = 0; |
||||||
|
//f(); |
||||||
|
//withdraw(); |
||||||
|
//shares[msg.sender]++; |
||||||
|
//c++; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
function f() { |
||||||
|
c++; |
||||||
|
withdraw(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
pragma solidity ^0.4.9; |
||||||
|
|
||||||
|
contract Ballot { |
||||||
|
|
||||||
|
struct Voter { |
||||||
|
uint weight; |
||||||
|
bool voted; |
||||||
|
uint8 vote; |
||||||
|
address delegate; |
||||||
|
baz foo; |
||||||
|
} |
||||||
|
|
||||||
|
struct baz{ |
||||||
|
uint bar; |
||||||
|
} |
||||||
|
|
||||||
|
mapping(address => Voter) voters; |
||||||
|
|
||||||
|
/// Create a new ballot with $(_numProposals) different proposals. |
||||||
|
function bla(address a) { |
||||||
|
Voter x = voters[a]; |
||||||
|
|
||||||
|
if (!a.send(10)) |
||||||
|
throw; |
||||||
|
|
||||||
|
//voters[a] = Voter(10,true,1,a); |
||||||
|
//x.foo.bar *= 100; |
||||||
|
bli(x); |
||||||
|
} |
||||||
|
|
||||||
|
//function bla(){} |
||||||
|
|
||||||
|
function bli(Voter storage x) private { |
||||||
|
x.foo.bar++; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
pragma solidity ^0.4.0; |
||||||
|
|
||||||
|
contract test { |
||||||
|
|
||||||
|
function (){ |
||||||
|
address x; |
||||||
|
this.b(x); |
||||||
|
x.call('something'); |
||||||
|
x.send(1 wei); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function b(address a) returns (bool) { |
||||||
|
|
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue