From 3c99aab27aae162dbb8266b74a24eb152e80d307 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Tue, 14 Apr 2020 17:34:40 -0700 Subject: [PATCH 01/49] Adding regression for case where harness can call otherwise unreachable code. Also guarding code that only allows non-contracts to initiate transactions with a flag that is on by default --- Sources/SolToBoogie/HarnessGenerator.cs | 22 ++++++++++++++-------- Sources/SolToBoogie/ParseUtils.cs | 5 +++++ Sources/SolToBoogie/TranslatorFlags.cs | 4 ++++ Test/regressions/UnreachableAssertion.sol | 20 ++++++++++++++++++++ 4 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 Test/regressions/UnreachableAssertion.sol diff --git a/Sources/SolToBoogie/HarnessGenerator.cs b/Sources/SolToBoogie/HarnessGenerator.cs index a2710454..e0824881 100644 --- a/Sources/SolToBoogie/HarnessGenerator.cs +++ b/Sources/SolToBoogie/HarnessGenerator.cs @@ -233,16 +233,22 @@ private BoogieStmtList GenerateHavocBlock(ContractDefinition contract, List x.Equals("/allowTxnsFromContract"))) + { + translatorFlags.NoTxnsFromContract = false; + } + if (args.Any(x => x.Equals("/instrumentSums"))) { translatorFlags.InstrumentSums = true; diff --git a/Sources/SolToBoogie/TranslatorFlags.cs b/Sources/SolToBoogie/TranslatorFlags.cs index 1726aada..946ba14e 100644 --- a/Sources/SolToBoogie/TranslatorFlags.cs +++ b/Sources/SolToBoogie/TranslatorFlags.cs @@ -32,7 +32,11 @@ public TranslatorFlags() CreateMainHarness = false; NoCustomTypes = false; OmitAssumeFalseForDynDispatch = false; + NoTxnsFromContract = true; } + + public bool NoTxnsFromContract { get; set; } + public bool NoCustomTypes { get; set; } public bool CreateMainHarness { get; set; } diff --git a/Test/regressions/UnreachableAssertion.sol b/Test/regressions/UnreachableAssertion.sol new file mode 100644 index 00000000..edcdf138 --- /dev/null +++ b/Test/regressions/UnreachableAssertion.sol @@ -0,0 +1,20 @@ +pragma solidity ^0.5.0; + + +contract UnreachableAssertion { + address private owner; + + constructor() public { + owner = address(this); + require(owner != address(0)); + } + + modifier onlyOwner() { + require(msg.sender == owner); + _; + } + + function neverSuccessful() onlyOwner view public { + assert(false); + } +} From 7fa1551b950e376ae0e5bfbd28ee9f394b0b8851 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Wed, 22 Apr 2020 15:46:32 -0700 Subject: [PATCH 02/49] Adding in simple syntactic alias check --- Sources/SolToBoogie/BoogieTranslator.cs | 8 +- Sources/SolToBoogie/ParseUtils.cs | 5 + Sources/SolToBoogie/SolToBoogie.csproj | 1 + Sources/SolToBoogie/TranslatorContext.cs | 8 +- Sources/SolToBoogie/TranslatorFlags.cs | 3 + Sources/SolidityAnalysis/AliasAnalysis.cs | 265 ++++++++++++++++++ .../SolidityAnalysis/SolidityAnalysis.csproj | 21 ++ Sources/SolidityAnalysis/SolidityAnalyzer.cs | 16 ++ Sources/VeriSol.sln | 14 + 9 files changed, 337 insertions(+), 4 deletions(-) create mode 100644 Sources/SolidityAnalysis/AliasAnalysis.cs create mode 100644 Sources/SolidityAnalysis/SolidityAnalysis.csproj create mode 100644 Sources/SolidityAnalysis/SolidityAnalyzer.cs diff --git a/Sources/SolToBoogie/BoogieTranslator.cs b/Sources/SolToBoogie/BoogieTranslator.cs index d15bfba0..ab3cf6d5 100644 --- a/Sources/SolToBoogie/BoogieTranslator.cs +++ b/Sources/SolToBoogie/BoogieTranslator.cs @@ -1,5 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. + +using SolidityAnalysis; + namespace SolToBoogie { using System; @@ -19,11 +22,10 @@ public BoogieAST Translate(AST solidityAST, HashSet> ignor bool generateInlineAttributesInBpl = _translatorFlags.GenerateInlineAttributes; SourceUnitList sourceUnits = solidityAST.GetSourceUnits(); - - TranslatorContext context = new TranslatorContext(ignoredMethods, generateInlineAttributesInBpl, _translatorFlags, entryPointContract); + + TranslatorContext context = new TranslatorContext(solidityAST, ignoredMethods, generateInlineAttributesInBpl, _translatorFlags, entryPointContract); context.IdToNodeMap = solidityAST.GetIdToNodeMap(); context.SourceDirectory = solidityAST.SourceDirectory; - // collect the absolute source path and line number for each AST node SourceInfoCollector sourceInfoCollector = new SourceInfoCollector(context); sourceUnits.Accept(sourceInfoCollector); diff --git a/Sources/SolToBoogie/ParseUtils.cs b/Sources/SolToBoogie/ParseUtils.cs index c65c403e..e45ea877 100644 --- a/Sources/SolToBoogie/ParseUtils.cs +++ b/Sources/SolToBoogie/ParseUtils.cs @@ -162,6 +162,11 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, translatorFlags.InstrumentSums = true; } + if (args.Any(x => x.Equals("/alias"))) + { + translatorFlags.RunAliasAnalysis = true; + } + translatorFlags.PerformContractInferce = args.Any(x => x.StartsWith("/contractInfer")); // don't perform verification for some of these omitFlags diff --git a/Sources/SolToBoogie/SolToBoogie.csproj b/Sources/SolToBoogie/SolToBoogie.csproj index ce0e4a13..b42c4b98 100644 --- a/Sources/SolToBoogie/SolToBoogie.csproj +++ b/Sources/SolToBoogie/SolToBoogie.csproj @@ -14,6 +14,7 @@ + diff --git a/Sources/SolToBoogie/TranslatorContext.cs b/Sources/SolToBoogie/TranslatorContext.cs index 558fbdfa..63c89f82 100644 --- a/Sources/SolToBoogie/TranslatorContext.cs +++ b/Sources/SolToBoogie/TranslatorContext.cs @@ -1,5 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. + +using SolidityAnalysis; + namespace SolToBoogie { using System; @@ -93,6 +96,8 @@ public class TranslatorContext public Dictionary> ModifierToPreludeLocalVars { get; private set; } public String EntryPointContract { get; private set; } + + public SolidityAnalyzer Analysis { get; private set; } // Options flags public TranslatorFlags TranslateFlags { get; private set; } @@ -112,7 +117,7 @@ public class TranslatorContext // maps Contract C --> (source, dest), where source is a library type private readonly Dictionary> usingMap; - public TranslatorContext(HashSet> ignoreMethods, bool _genInlineAttrInBpl, TranslatorFlags _translateFlags = null, String entryPointContract = "") + public TranslatorContext(AST solidityAST, HashSet> ignoreMethods, bool _genInlineAttrInBpl, TranslatorFlags _translateFlags = null, String entryPointContract = "") { Program = new BoogieProgram(); ContractDefinitions = new HashSet(); @@ -144,6 +149,7 @@ public TranslatorContext(HashSet> ignoreMethods, bool _gen genInlineAttrInBpl = _genInlineAttrInBpl; TranslateFlags = _translateFlags; EntryPointContract = entryPointContract; + Analysis = new SolidityAnalyzer(solidityAST, ignoreMethods, entryPointContract); } public bool HasASTNodeId(int id) diff --git a/Sources/SolToBoogie/TranslatorFlags.cs b/Sources/SolToBoogie/TranslatorFlags.cs index 946ba14e..79bb2b69 100644 --- a/Sources/SolToBoogie/TranslatorFlags.cs +++ b/Sources/SolToBoogie/TranslatorFlags.cs @@ -33,8 +33,11 @@ public TranslatorFlags() NoCustomTypes = false; OmitAssumeFalseForDynDispatch = false; NoTxnsFromContract = true; + RunAliasAnalysis = false; } + public bool RunAliasAnalysis { get; set; } + public bool NoTxnsFromContract { get; set; } public bool NoCustomTypes { get; set; } diff --git a/Sources/SolidityAnalysis/AliasAnalysis.cs b/Sources/SolidityAnalysis/AliasAnalysis.cs new file mode 100644 index 00000000..2047a189 --- /dev/null +++ b/Sources/SolidityAnalysis/AliasAnalysis.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using SolidityAST; + +namespace SolidityAnalysis +{ + + public class AliasAnalysis : BasicASTVisitor + { + private HashSet results; + private Dictionary nameToStruct; + private AST solidityAST; + private HashSet> ignoredMethods; + private String entryPoint; + + class DeclarationFinder : BasicASTVisitor + { + private IndexAccess start; + private int numAccesses; + private VariableDeclaration result; + private AST solidityAST; + + public DeclarationFinder(IndexAccess access, AST solidityAst) + { + this.solidityAST = solidityAst; + this.start = access; + this.numAccesses = 1; + start.BaseExpression.Accept(this); + } + public override bool Visit(IndexAccess access) + { + numAccesses++; + access.BaseExpression.Accept(this); + return false; + } + + public override bool Visit(Identifier ident) + { + ASTNode node = solidityAST.GetASTNodeByID(ident.ReferencedDeclaration); + if (node is VariableDeclaration decl) + { + result = decl; + return false; + } + + throw new Exception("AliasAnalysis Exception: Expected identifier declaration to be of VariableDeclaration type, not " + node.GetType()); + } + + public VariableDeclaration getDecl() + { + return result; + } + + public int getNumAccesses() + { + return numAccesses; + } + } + + public AliasAnalysis(AST solidityAST, HashSet> ignoredMethods, String entryPointContract = "") + { + this.solidityAST = solidityAST; + this.ignoredMethods = ignoredMethods; + this.entryPoint = entryPointContract; + this.nameToStruct = new Dictionary(); + this.results = null; + } + + public HashSet getResults() + { + if (results == null) + { + runAnalysis(); + } + + return results; + } + + public void runAnalysis() + { + results = new HashSet(); + solidityAST.GetSourceUnits().Accept(this); + } + + public override bool Visit(StructDefinition def) + { + nameToStruct.Add(def.Name, def); + return true; + } + + public override bool Visit(VariableDeclaration decl) + { + TypeName type = decl.TypeName; + if (decl.Name == "" || type is ElementaryTypeName || decl.StateVariable == false) + { + return false; + } + + if (type is Mapping || type is ArrayTypeName) + { + results.Add(decl); + } + + /* Can we add in user-defined types? + else if (type is UserDefinedTypeName userDefined) + { + results.Add(decl); + }*/ + + return true; + } + + public override bool Visit(Identifier ident) + { + if (!ident.IsLValue) + { + return true; + } + + ASTNode node = solidityAST.GetASTNodeByID(ident.ReferencedDeclaration); + assert(node is VariableDeclaration, + "Expected identifier declaration to be of VariableDeclaration type, not " + node.GetType()); + + VariableDeclaration decl = (VariableDeclaration) node; + results.Remove(decl); + return false; + } + + public TypeName getBaseType(TypeName ty) + { + while (ty is Mapping || ty is ArrayTypeName) + { + if (ty is Mapping mapping) + { + ty = mapping.ValueType; + } + else if (ty is ArrayTypeName arr) + { + ty = arr.BaseType; + } + } + + return ty; + } + + public int getIndexDimSize(TypeName ty) + { + int dimSize = 0; + while (ty is Mapping || ty is ArrayTypeName) + { + dimSize++; + if (ty is Mapping mapping) + { + ty = mapping.ValueType; + } + else if (ty is ArrayTypeName arr) + { + ty = arr.BaseType; + } + } + + return dimSize; + } + + public override bool Visit(IndexAccess access) + { + DeclarationFinder declFinder = new DeclarationFinder(access, solidityAST); + VariableDeclaration decl = declFinder.getDecl(); + + if (results.Contains(decl)) + { + int numAccesses = declFinder.getNumAccesses(); + int numIndexDims = getIndexDimSize(decl.TypeName); + + if (numIndexDims != numAccesses) + { + results.Remove(decl); + } + } + + return false; + } + + /* + * Guide analysis to statements that can cause aliasing. i.e. rhs expressions, function arguments, etc + */ + + public override bool Visit(Assignment node) + { + if (node.Operator == "=") + { + node.RightHandSide.Accept(this); + } + + return false; + } + + public override bool Visit(FunctionDefinition node) + { + return node.Implemented; + } + + /* + * Nodes that cannot cause aliasing + */ + + public override bool Visit(ArrayTypeName node) + { + return false; + } + + public override bool Visit(Break node) + { + return false; + } + + public override bool Visit(BinaryOperation node) + { + return false; + } + + public override bool Visit(EmitStatement node) + { + return false; + } + + public override bool Visit(UnaryOperation node) + { + return false; + } + + public override bool Visit(EventDefinition node) + { + return false; + } + + public override bool Visit(UsingForDirective node) + { + return false; + } + + public override bool Visit(PragmaDirective node) + { + return false; + } + + public override bool Visit(ImportDirective node) + { + return false; + } + + public override bool Visit(Continue node) + { + return false; + } + + public void assert(bool cond, String msg) + { + if (!cond) + { + throw new Exception("AliasAnalysis Exception: " + msg); + } + } + } +} \ No newline at end of file diff --git a/Sources/SolidityAnalysis/SolidityAnalysis.csproj b/Sources/SolidityAnalysis/SolidityAnalysis.csproj new file mode 100644 index 00000000..7cac2a7e --- /dev/null +++ b/Sources/SolidityAnalysis/SolidityAnalysis.csproj @@ -0,0 +1,21 @@ + + + + Library + netcoreapp2.2 + true + + + + + + + + + + + + + + + diff --git a/Sources/SolidityAnalysis/SolidityAnalyzer.cs b/Sources/SolidityAnalysis/SolidityAnalyzer.cs new file mode 100644 index 00000000..c3ef1fe1 --- /dev/null +++ b/Sources/SolidityAnalysis/SolidityAnalyzer.cs @@ -0,0 +1,16 @@ +namespace SolidityAnalysis +{ + using System; + using System.Collections.Generic; + using SolidityAST; + + public class SolidityAnalyzer + { + public AliasAnalysis Alias { get; } + + public SolidityAnalyzer(AST solidityAST, HashSet> ignoredMethods, String entryPointContract = "") + { + Alias = new AliasAnalysis(solidityAST, ignoredMethods, entryPointContract); + } + } +} \ No newline at end of file diff --git a/Sources/VeriSol.sln b/Sources/VeriSol.sln index c5272132..f251cb1e 100644 --- a/Sources/VeriSol.sln +++ b/Sources/VeriSol.sln @@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VeriSol", "VeriSol\VeriSol. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExternalToolsManager", "ExternalToolsManager\ExternalToolsManager.csproj", "{BF09262B-4902-4B54-8888-A9BDB65AF0D0}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SolidityAnalysis", "SolidityAnalysis\SolidityAnalysis.csproj", "{9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -111,6 +113,18 @@ Global {BF09262B-4902-4B54-8888-A9BDB65AF0D0}.Release|x64.Build.0 = Release|Any CPU {BF09262B-4902-4B54-8888-A9BDB65AF0D0}.Release|x86.ActiveCfg = Release|Any CPU {BF09262B-4902-4B54-8888-A9BDB65AF0D0}.Release|x86.Build.0 = Release|Any CPU + {9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}.Debug|x64.ActiveCfg = Debug|Any CPU + {9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}.Debug|x64.Build.0 = Debug|Any CPU + {9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}.Debug|x86.ActiveCfg = Debug|Any CPU + {9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}.Debug|x86.Build.0 = Debug|Any CPU + {9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}.Release|Any CPU.Build.0 = Release|Any CPU + {9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}.Release|x64.ActiveCfg = Release|Any CPU + {9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}.Release|x64.Build.0 = Release|Any CPU + {9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}.Release|x86.ActiveCfg = Release|Any CPU + {9177A69B-7FDE-48DC-9A2C-22AAB5ED3768}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 047e3cc35c6866839da43795737eea04d765704a Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Wed, 22 Apr 2020 16:55:46 -0700 Subject: [PATCH 03/49] Adding tip for translating from solidity to boogie in global declarations --- Sources/SolToBoogie/ProcedureTranslator.cs | 73 ++++++++++++++++++++-- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 00ac9b07..87b15038 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -130,6 +130,46 @@ private void TranslateStructDefinition(StructDefinition structDefn) } } + private String GetAccessPattern(VariableDeclaration varDecl, String boogieName) + { + String solAccess = String.Format("this.{0}", varDecl.Name); + String boogieAccess = String.Format("{0}[this]", boogieName); + TypeName solType = varDecl.TypeName; + int dim = 0; + + while (solType is Mapping || solType is ArrayTypeName) + { + String dimName = String.Format("i{0}", dim); + dim++; + BoogieType keyType = null; + BoogieType valType = null; + + if (solType is Mapping map) + { + keyType = TransUtils.GetBoogieTypeFromSolidityTypeName(map.KeyType); + valType = TransUtils.GetBoogieTypeFromSolidityTypeName(map.ValueType); + solType = map.ValueType; + } + else if (solType is ArrayTypeName arr) + { + keyType = BoogieType.Int; + valType = TransUtils.GetBoogieTypeFromSolidityTypeName(arr.BaseType); + solType = arr.BaseType; + } + + String memMap = MapArrayHelper.GetMemoryMapName(keyType, valType); + solAccess = String.Format("{0}[{1}]", solAccess, dimName); + boogieAccess = String.Format("{0}[{1}][{2}]", memMap, boogieAccess, dimName); + } + + return String.Format("\"{0}={1}\"", solAccess, boogieAccess); + } + + private String GetSumAccessPattern(VariableDeclaration varDecl, String boogieName) + { + return String.Format("\"sum(this.{0})={1}[{2}[this]]\"", varDecl.Name, getSumName(), boogieName); + } + private void TranslateStateVarDeclaration(VariableDeclaration varDecl) { VeriSolAssert(varDecl.StateVariable, $"{varDecl} is not a state variable"); @@ -143,19 +183,37 @@ private void TranslateStateVarDeclaration(VariableDeclaration varDecl) { Console.WriteLine($"Warning: signed integer arithmetic is not handled with /useModularArithmetic option"); } - + if (varDecl.TypeName is Mapping) { - context.Program.AddDeclaration(new BoogieGlobalVariable(new BoogieTypedIdent(name, mapType))); + BoogieGlobalVariable global = new BoogieGlobalVariable(new BoogieTypedIdent(name, mapType)); + global.Attributes = new List(); + global.Attributes.Add(new BoogieAttribute("access", GetAccessPattern(varDecl, name))); + if (context.TranslateFlags.InstrumentSums) + { + global.Attributes.Add(new BoogieAttribute("sum", GetSumAccessPattern(varDecl, name))); + } + context.Program.AddDeclaration(global); } else if (varDecl.TypeName is ArrayTypeName) { //array variables can be assigned - context.Program.AddDeclaration(new BoogieGlobalVariable(new BoogieTypedIdent(name, mapType))); + BoogieGlobalVariable global = new BoogieGlobalVariable(new BoogieTypedIdent(name, mapType)); + global.Attributes = new List(); + global.Attributes.Add(new BoogieAttribute("access", GetAccessPattern(varDecl, name))); + if (context.TranslateFlags.InstrumentSums) + { + global.Attributes.Add(new BoogieAttribute("sum", GetSumAccessPattern(varDecl, name))); + } + + context.Program.AddDeclaration(global); } else // other type of state variables { - context.Program.AddDeclaration(new BoogieGlobalVariable(new BoogieTypedIdent(name, mapType))); + BoogieGlobalVariable global = new BoogieGlobalVariable(new BoogieTypedIdent(name, mapType)); + global.Attributes = new List(); + global.Attributes.Add(new BoogieAttribute("access", GetAccessPattern(varDecl, name))); + context.Program.AddDeclaration(global); } } @@ -665,9 +723,14 @@ private BoogieFuncCallExpr GetCallExprForZeroInit(BoogieType key, BoogieType val return new BoogieFuncCallExpr("zero" + keyStr + valStr + "Arr", new List()); } + private String getSumName() + { + return "sum"; + } + private BoogieExpr getSumArray() { - return new BoogieIdentifierExpr("sum"); + return new BoogieIdentifierExpr(getSumName()); } private BoogieExpr getSumAccess(BoogieExpr key) From 76f27f702f61777d69f9e4f51bed3479839f6f61 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Thu, 23 Apr 2020 19:36:00 -0700 Subject: [PATCH 04/49] Adding map splitting for the alias analysis. Also fixed a bug in the analysis --- Sources/SolToBoogie/BoogieTranslator.cs | 7 +- .../SolToBoogie/GhostVarAndAxiomGenerator.cs | 46 ++++++------- Sources/SolToBoogie/MapArrayHelper.cs | 58 +++++++++++++--- Sources/SolToBoogie/ModifierCollector.cs | 2 - Sources/SolToBoogie/ProcedureTranslator.cs | 41 +++++++----- Sources/SolidityAnalysis/AliasAnalysis.cs | 54 ++------------- Sources/SolidityAnalysis/DeclarationFinder.cs | 67 +++++++++++++++++++ .../SolidityAnalysis/SolidityAnalysis.csproj | 7 +- 8 files changed, 175 insertions(+), 107 deletions(-) create mode 100644 Sources/SolidityAnalysis/DeclarationFinder.cs diff --git a/Sources/SolToBoogie/BoogieTranslator.cs b/Sources/SolToBoogie/BoogieTranslator.cs index ab3cf6d5..c92bdbe5 100644 --- a/Sources/SolToBoogie/BoogieTranslator.cs +++ b/Sources/SolToBoogie/BoogieTranslator.cs @@ -67,8 +67,11 @@ public BoogieAST Translate(AST solidityAST, HashSet> ignor FunctionEventResolver functionEventResolver = new FunctionEventResolver(context); functionEventResolver.Resolve(); + // Generate map helper + MapArrayHelper mapHelper = new MapArrayHelper(context, solidityAST); + // add types, gobal ghost variables, and axioms - GhostVarAndAxiomGenerator generator = new GhostVarAndAxiomGenerator(context); + GhostVarAndAxiomGenerator generator = new GhostVarAndAxiomGenerator(context, mapHelper); generator.Generate(); // collect modifiers information @@ -80,7 +83,7 @@ public BoogieAST Translate(AST solidityAST, HashSet> ignor sourceUnits.Accept(usingCollector); // translate procedures - ProcedureTranslator procTranslator = new ProcedureTranslator(context, generateInlineAttributesInBpl); + ProcedureTranslator procTranslator = new ProcedureTranslator(context, mapHelper, generateInlineAttributesInBpl); sourceUnits.Accept(procTranslator); // generate fallbacks diff --git a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs index 00866ebf..2315cec7 100644 --- a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs +++ b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs @@ -13,11 +13,14 @@ public class GhostVarAndAxiomGenerator // require the ContractDefintions member is populated private TranslatorContext context; + private MapArrayHelper mapHelper; + private BoogieCtorType contractType = new BoogieCtorType("ContractName"); - public GhostVarAndAxiomGenerator(TranslatorContext context) + public GhostVarAndAxiomGenerator(TranslatorContext context, MapArrayHelper mapHelper) { this.context = context; + this.mapHelper = mapHelper; } public void Generate() @@ -652,7 +655,7 @@ private BoogieAxiom GenerateAbiEncodePackedAxiomTwoArgsOneRef() private void GenerateMemoryVariables() { - HashSet> generatedTypes = new HashSet>(); + HashSet generatedTypes = new HashSet(); // mappings foreach (ContractDefinition contract in context.ContractToMappingsMap.Keys) { @@ -660,7 +663,7 @@ private void GenerateMemoryVariables() { Debug.Assert(varDecl.TypeName is Mapping); Mapping mapping = varDecl.TypeName as Mapping; - GenerateMemoryVariablesForMapping(mapping, generatedTypes); + GenerateMemoryVariablesForMapping(varDecl, mapping, generatedTypes); } } // arrays @@ -670,24 +673,24 @@ private void GenerateMemoryVariables() { Debug.Assert(varDecl.TypeName is ArrayTypeName); ArrayTypeName array = varDecl.TypeName as ArrayTypeName; - GenerateMemoryVariablesForArray(array, generatedTypes); + GenerateMemoryVariablesForArray(varDecl, array, generatedTypes); } } } - private void GenerateMemoryVariablesForMapping(Mapping mapping, HashSet> generatedTypes) + private void GenerateMemoryVariablesForMapping(VariableDeclaration decl, Mapping mapping, HashSet generatedMaps) { BoogieType boogieKeyType = TransUtils.GetBoogieTypeFromSolidityTypeName(mapping.KeyType); BoogieType boogieValType = null; if (mapping.ValueType is Mapping submapping) { boogieValType = BoogieType.Ref; - GenerateMemoryVariablesForMapping(submapping, generatedTypes); + GenerateMemoryVariablesForMapping(decl, submapping, generatedMaps); } else if (mapping.ValueType is ArrayTypeName array) { boogieValType = BoogieType.Ref; - GenerateMemoryVariablesForArray(array, generatedTypes); + GenerateMemoryVariablesForArray(decl, array, generatedMaps); } else { @@ -695,26 +698,23 @@ private void GenerateMemoryVariablesForMapping(Mapping mapping, HashSet pair = new KeyValuePair(boogieKeyType, boogieValType); - if (!generatedTypes.Contains(pair)) - { - generatedTypes.Add(pair); - GenerateSingleMemoryVariable(boogieKeyType, boogieValType); - } + + GenerateSingleMemoryVariable(decl, boogieKeyType, boogieValType, generatedMaps); } - private void GenerateMemoryVariablesForArray(ArrayTypeName array, HashSet> generatedTypes) + private void GenerateMemoryVariablesForArray(VariableDeclaration decl, ArrayTypeName array, HashSet generatedMaps) { BoogieType boogieKeyType = BoogieType.Int; BoogieType boogieValType = null; if (array.BaseType is ArrayTypeName subarray) { boogieValType = BoogieType.Ref; - GenerateMemoryVariablesForArray(subarray, generatedTypes); + GenerateMemoryVariablesForArray(decl, subarray, generatedMaps); } else if (array.BaseType is Mapping mapping) { boogieValType = BoogieType.Ref; - GenerateMemoryVariablesForMapping(mapping, generatedTypes); + GenerateMemoryVariablesForMapping(decl, mapping, generatedMaps); } else { @@ -722,20 +722,20 @@ private void GenerateMemoryVariablesForArray(ArrayTypeName array, HashSet pair = new KeyValuePair(boogieKeyType, boogieValType); - if (!generatedTypes.Contains(pair)) - { - generatedTypes.Add(pair); - GenerateSingleMemoryVariable(boogieKeyType, boogieValType); - } + GenerateSingleMemoryVariable(decl, boogieKeyType, boogieValType, generatedMaps); } - private void GenerateSingleMemoryVariable(BoogieType keyType, BoogieType valType) + private void GenerateSingleMemoryVariable(VariableDeclaration decl, BoogieType keyType, BoogieType valType, HashSet generatedMaps) { BoogieMapType map = new BoogieMapType(keyType, valType); map = new BoogieMapType(BoogieType.Ref, map); - string name = MapArrayHelper.GetMemoryMapName(keyType, valType); - context.Program.AddDeclaration(new BoogieGlobalVariable(new BoogieTypedIdent(name, map))); + string name = mapHelper.GetMemoryMapName(decl, keyType, valType); + if (!generatedMaps.Contains(name)) + { + generatedMaps.Add(name); + context.Program.AddDeclaration(new BoogieGlobalVariable(new BoogieTypedIdent(name, map))); + } } } } diff --git a/Sources/SolToBoogie/MapArrayHelper.cs b/Sources/SolToBoogie/MapArrayHelper.cs index bd7fa1d3..74d58fec 100644 --- a/Sources/SolToBoogie/MapArrayHelper.cs +++ b/Sources/SolToBoogie/MapArrayHelper.cs @@ -1,5 +1,9 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. + +using solidityAnalysis; +using SolidityAST; + namespace SolToBoogie { using System; @@ -14,14 +18,46 @@ public class MapArrayHelper // mapping (uint => uint[]) does not have storage/memory in Typestring // private static Regex arrayRegex = new Regex(@"(.+)\[\w*\]$"); - public static string GetMemoryMapName(BoogieType keyType, BoogieType valType) + private TranslatorContext context { get; } + + private AST solidityAst { get; } + + public MapArrayHelper(TranslatorContext ctxt, AST solidityAst) + { + this.context = ctxt; + this.solidityAst = solidityAst; + } + + public Boolean notAliased(VariableDeclaration decl) + { + return context.TranslateFlags.RunAliasAnalysis && context.Analysis.Alias.getResults().Contains(decl); + } + + public static string GetCanonicalMemName(BoogieType keyType, BoogieType valType) { return "M_" + keyType.ToString() + "_" + valType.ToString(); } - public static BoogieExpr GetMemoryMapSelectExpr(BoogieType mapKeyType, BoogieType mapValType, BoogieExpr baseExpr, BoogieExpr indexExpr) + public VariableDeclaration getDecl(Expression access) + { + DeclarationFinder declFinder = new DeclarationFinder(access, solidityAst); + return declFinder.getDecl(); + } + + public string GetMemoryMapName(VariableDeclaration decl, BoogieType keyType, BoogieType valType) { - string mapName = GetMemoryMapName(mapKeyType, mapValType); + if (notAliased(decl)) + { + return TransUtils.GetCanonicalVariableName(decl, context) + "_" + keyType.ToString() + "_" + + valType.ToString(); + } + + return GetCanonicalMemName(keyType, valType); + } + + public BoogieExpr GetMemoryMapSelectExpr(VariableDeclaration decl, BoogieType mapKeyType, BoogieType mapValType, BoogieExpr baseExpr, BoogieExpr indexExpr) + { + string mapName = GetMemoryMapName(decl, mapKeyType, mapValType); BoogieIdentifierExpr mapIdent = new BoogieIdentifierExpr(mapName); BoogieMapSelect mapSelectExpr = new BoogieMapSelect(mapIdent, baseExpr); mapSelectExpr = new BoogieMapSelect(mapSelectExpr, indexExpr); @@ -54,7 +90,7 @@ public static BoogieType InferExprTypeFromTypeString(string typeString) else if (typeString.StartsWith("int") && !typeString.Contains("[")) { return BoogieType.Int; - } + } else if (typeString.StartsWith("byte") && !typeString.Contains("[")) { return BoogieType.Int; @@ -97,9 +133,9 @@ public static BoogieType InferKeyTypeFromTypeString(string typeString) { return BoogieType.Ref; } - else if (typeString.StartsWith("bytes") && !typeString.Equals("bytes")) - { - return BoogieType.Int; + else if (typeString.StartsWith("bytes") && !typeString.Equals("bytes")) + { + return BoogieType.Int; } else { @@ -123,9 +159,9 @@ public static BoogieType InferValueTypeFromTypeString(string typeString) { return BoogieType.Ref; } - else if (typeString.StartsWith("bytes") && !typeString.Equals("bytes")) - { - return BoogieType.Int; + else if (typeString.StartsWith("bytes") && !typeString.Equals("bytes")) + { + return BoogieType.Int; } else { diff --git a/Sources/SolToBoogie/ModifierCollector.cs b/Sources/SolToBoogie/ModifierCollector.cs index 30e96611..1b4961b6 100644 --- a/Sources/SolToBoogie/ModifierCollector.cs +++ b/Sources/SolToBoogie/ModifierCollector.cs @@ -15,12 +15,10 @@ namespace SolToBoogie public class ModifierCollector : BasicASTVisitor { private TranslatorContext context; - private ProcedureTranslator localTranslator; public ModifierCollector(TranslatorContext context) { this.context = context; - this.localTranslator = new ProcedureTranslator(context); } public override bool Visit(ModifierDefinition modifier) diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 87b15038..b44454bf 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -44,6 +44,8 @@ public class ProcedureTranslator : BasicASTVisitor // to collect contract invariants private Dictionary> contractInvariants = null; + private MapArrayHelper mapHelper; + private static void emitGasCheck(BoogieStmtList newBody) { BoogieStmtList thenBody = new BoogieStmtList(); @@ -74,9 +76,10 @@ private void preTranslationAction(ASTNode node) } } - public ProcedureTranslator(TranslatorContext context, bool _genInlineAttrsInBpl = true) + public ProcedureTranslator(TranslatorContext context, MapArrayHelper mapHelper, bool _genInlineAttrsInBpl = true) { this.context = context; + this.mapHelper = mapHelper; boogieToLocalVarsMap = new Dictionary>(); genInlineAttrsInBpl = _genInlineAttrsInBpl; contractInvariants = new Dictionary>(); @@ -132,14 +135,14 @@ private void TranslateStructDefinition(StructDefinition structDefn) private String GetAccessPattern(VariableDeclaration varDecl, String boogieName) { - String solAccess = String.Format("this.{0}", varDecl.Name); - String boogieAccess = String.Format("{0}[this]", boogieName); + String solAccess = $"this.{varDecl.Name}"; + String boogieAccess = $"{boogieName}[this]"; TypeName solType = varDecl.TypeName; int dim = 0; while (solType is Mapping || solType is ArrayTypeName) { - String dimName = String.Format("i{0}", dim); + String dimName = $"i{dim}"; dim++; BoogieType keyType = null; BoogieType valType = null; @@ -157,17 +160,17 @@ private String GetAccessPattern(VariableDeclaration varDecl, String boogieName) solType = arr.BaseType; } - String memMap = MapArrayHelper.GetMemoryMapName(keyType, valType); - solAccess = String.Format("{0}[{1}]", solAccess, dimName); - boogieAccess = String.Format("{0}[{1}][{2}]", memMap, boogieAccess, dimName); + String memMap = mapHelper.GetMemoryMapName(varDecl, keyType, valType); + solAccess = $"{solAccess}[{dimName}]"; + boogieAccess = $"{memMap}[{boogieAccess}][{dimName}]"; } - return String.Format("\"{0}={1}\"", solAccess, boogieAccess); + return $"\"{solAccess}={boogieAccess}\""; } private String GetSumAccessPattern(VariableDeclaration varDecl, String boogieName) { - return String.Format("\"sum(this.{0})={1}[{2}[this]]\"", varDecl.Name, getSumName(), boogieName); + return $"\"sum(this.{varDecl.Name})={getSumName()}[{boogieName}[this]]\""; } private void TranslateStateVarDeclaration(VariableDeclaration varDecl) @@ -929,7 +932,7 @@ private void InitializeNestedArrayMappingStateVar(VariableDeclaration varDecl, M currentStmtList.AddStatement(new BoogieCommentCmd($"Initialize length of 1-level nested array in {varDecl.Name}")); // Issue with inferring Array[] expressions in GetBoogieTypesFromMapping (TODO: use GetBoogieTypesFromMapping after fix) var mapKeyType = MapArrayHelper.InferExprTypeFromTypeString(mapping.KeyType.TypeDescriptions.ToString()); - string mapName = MapArrayHelper.GetMemoryMapName(mapKeyType, BoogieType.Ref); + string mapName = mapHelper.GetMemoryMapName(varDecl, mapKeyType, BoogieType.Ref); string varName = TransUtils.GetCanonicalStateVariableName(varDecl, context); var varExpr = new BoogieIdentifierExpr(varName); //lhs is Mem_t_ref[x[this]] @@ -1028,7 +1031,7 @@ private void GetBoogieTypesFromMapping(VariableDeclaration varDecl, Mapping mapp mapping.ValueType.ToString(); // needed as a mapping(int => contract A) only has "A" as the valueType.ToSTring() var mapValueType = MapArrayHelper.InferExprTypeFromTypeString(mapValueTypeString); - string mapName = MapArrayHelper.GetMemoryMapName(mapKeyType, mapValueType); + string mapName = mapHelper.GetMemoryMapName(varDecl, mapKeyType, mapValueType); string varName = TransUtils.GetCanonicalStateVariableName(varDecl, context); var varExpr = new BoogieIdentifierExpr(varName); @@ -1937,7 +1940,8 @@ private List ExtractSpecifications(string specStringCall, BoogieStmt private BoogieExpr TranslateModifiesStmt(BoogieExpr boogieExpr1, BoogieExpr boogieExpr2) { //has to be M_ref_int[mapp[this]] instead of mapp[this] - var mapName = MapArrayHelper.GetMemoryMapName(BoogieType.Ref, BoogieType.Int); + //TODO: this is not the proper way of finding the correct map. Come back and fix. + var mapName = MapArrayHelper.GetCanonicalMemName(BoogieType.Ref, BoogieType.Int); var mappingExpr = new BoogieMapSelect(new BoogieIdentifierExpr(mapName), boogieExpr1); //boogieExpr2 is a tuple, we need to flatten it into an array @@ -2574,7 +2578,9 @@ private BoogieExpr TranslateVeriSolCodeContractFuncCall(FunctionCall node) if (verisolFunc.Equals("_SumMapping_VeriSol")) { //has to be M_ref_int[mapp[this]] instead of mapp[this] - var mapName = MapArrayHelper.GetMemoryMapName(BoogieType.Ref, BoogieType.Int); + VariableDeclaration decl = mapHelper.getDecl(node.Arguments[0]); + VeriSolAssert(decl != null, "Could not find declaration of " + node.Arguments[0]); + var mapName = mapHelper.GetMemoryMapName(decl, BoogieType.Ref, BoogieType.Int); boogieExprs[0] = new BoogieMapSelect(new BoogieIdentifierExpr(mapName), boogieExprs[0]); } return new BoogieFuncCallExpr(verisolFunc, boogieExprs); @@ -3015,7 +3021,9 @@ private void TranslateDynamicArrayPush(FunctionCall node) // M[this][a][tmp] := e; BoogieType mapKeyType = BoogieType.Int; BoogieType mapValType = MapArrayHelper.InferExprTypeFromTypeString(node.Arguments[0].TypeDescriptions.TypeString); - BoogieExpr mapSelect = MapArrayHelper.GetMemoryMapSelectExpr(mapKeyType, mapValType, receiver, tmp); + VariableDeclaration decl = mapHelper.getDecl(memberAccess.Expression); + VeriSolAssert(decl != null, "Could not find declaration of " + node.Arguments[0]); + BoogieExpr mapSelect = mapHelper.GetMemoryMapSelectExpr(decl, mapKeyType, mapValType, receiver, tmp); BoogieAssignCmd writeCmd = new BoogieAssignCmd(mapSelect, element); currentStmtList.AddStatement(writeCmd); @@ -3749,7 +3757,8 @@ public override bool Visit(IndexAccess node) //} BoogieExpr indexAccessExpr = new BoogieMapSelect(baseExpr, indexExpr); - currentExpr = MapArrayHelper.GetMemoryMapSelectExpr(baseKeyType, baseValType, baseExpr, indexExpr); + VariableDeclaration decl = mapHelper.getDecl(node); + currentExpr = mapHelper.GetMemoryMapSelectExpr(decl, baseKeyType, baseValType, baseExpr, indexExpr); if (context.TranslateFlags.LazyNestedAlloc) { @@ -3776,7 +3785,7 @@ public override bool Visit(IndexAccess node) BoogieType nestedValType = MapArrayHelper.InferValueTypeFromTypeString(valTypeString); BoogieType nestedKeyType = MapArrayHelper.InferKeyTypeFromTypeString(valTypeString); - var mapName = new BoogieIdentifierExpr(MapArrayHelper.GetMemoryMapName(nestedKeyType, nestedValType)); + var mapName = new BoogieIdentifierExpr(mapHelper.GetMemoryMapName(decl, nestedKeyType, nestedValType)); var derefCurrExpr = new BoogieMapSelect(mapName, currentExpr); var qVar1 = QVarGenerator.NewQVar(0, 0); diff --git a/Sources/SolidityAnalysis/AliasAnalysis.cs b/Sources/SolidityAnalysis/AliasAnalysis.cs index 2047a189..624611ae 100644 --- a/Sources/SolidityAnalysis/AliasAnalysis.cs +++ b/Sources/SolidityAnalysis/AliasAnalysis.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using solidityAnalysis; using SolidityAST; namespace SolidityAnalysis @@ -13,50 +15,6 @@ public class AliasAnalysis : BasicASTVisitor private HashSet> ignoredMethods; private String entryPoint; - class DeclarationFinder : BasicASTVisitor - { - private IndexAccess start; - private int numAccesses; - private VariableDeclaration result; - private AST solidityAST; - - public DeclarationFinder(IndexAccess access, AST solidityAst) - { - this.solidityAST = solidityAst; - this.start = access; - this.numAccesses = 1; - start.BaseExpression.Accept(this); - } - public override bool Visit(IndexAccess access) - { - numAccesses++; - access.BaseExpression.Accept(this); - return false; - } - - public override bool Visit(Identifier ident) - { - ASTNode node = solidityAST.GetASTNodeByID(ident.ReferencedDeclaration); - if (node is VariableDeclaration decl) - { - result = decl; - return false; - } - - throw new Exception("AliasAnalysis Exception: Expected identifier declaration to be of VariableDeclaration type, not " + node.GetType()); - } - - public VariableDeclaration getDecl() - { - return result; - } - - public int getNumAccesses() - { - return numAccesses; - } - } - public AliasAnalysis(AST solidityAST, HashSet> ignoredMethods, String entryPointContract = "") { this.solidityAST = solidityAST; @@ -112,14 +70,16 @@ public override bool Visit(VariableDeclaration decl) public override bool Visit(Identifier ident) { - if (!ident.IsLValue) + if (!solidityAST.GetIdToNodeMap().ContainsKey(ident.ReferencedDeclaration)) { return true; } ASTNode node = solidityAST.GetASTNodeByID(ident.ReferencedDeclaration); - assert(node is VariableDeclaration, - "Expected identifier declaration to be of VariableDeclaration type, not " + node.GetType()); + if (!(node is VariableDeclaration)) + { + return true; + } VariableDeclaration decl = (VariableDeclaration) node; results.Remove(decl); diff --git a/Sources/SolidityAnalysis/DeclarationFinder.cs b/Sources/SolidityAnalysis/DeclarationFinder.cs new file mode 100644 index 00000000..4a174fb6 --- /dev/null +++ b/Sources/SolidityAnalysis/DeclarationFinder.cs @@ -0,0 +1,67 @@ +using System; +using SolidityAST; + +namespace solidityAnalysis +{ + public class DeclarationFinder : BasicASTVisitor + { + private Expression start; + private int numAccesses; + private VariableDeclaration result; + private AST solidityAST; + + public DeclarationFinder(Expression access, AST solidityAst) + { + this.solidityAST = solidityAst; + this.start = access; + this.numAccesses = 0; + start.Accept(this); + } + public override bool Visit(IndexAccess access) + { + numAccesses++; + access.BaseExpression.Accept(this); + return false; + } + + public override bool Visit(MemberAccess access) + { + if (access.ReferencedDeclaration.HasValue) + { + ASTNode node = solidityAST.GetASTNodeByID(access.ReferencedDeclaration.Value); + if (node is VariableDeclaration decl) + { + result = decl; + return false; + } + + throw new Exception("Analysis Exception: Expected member access declaration to be of VariableDeclaration type, not " + node.GetType()); + } + + access.Expression.Accept(this); + return false; + } + + public override bool Visit(Identifier ident) + { + ASTNode node = solidityAST.GetASTNodeByID(ident.ReferencedDeclaration); + if (node is VariableDeclaration decl) + { + result = decl; + return false; + } + + throw new Exception("Analysis Exception: Expected identifier declaration to be of VariableDeclaration type, not " + node.GetType()); + } + + public VariableDeclaration getDecl() + { + return result; + } + + public int getNumAccesses() + { + return numAccesses; + } + } +} diff --git a/Sources/SolidityAnalysis/SolidityAnalysis.csproj b/Sources/SolidityAnalysis/SolidityAnalysis.csproj index 7cac2a7e..dd2f9204 100644 --- a/Sources/SolidityAnalysis/SolidityAnalysis.csproj +++ b/Sources/SolidityAnalysis/SolidityAnalysis.csproj @@ -1,5 +1,4 @@ - Library netcoreapp2.2 @@ -7,15 +6,11 @@ - - - - - + \ No newline at end of file From cacf02ef2d3fd47ac0b1deabdb4f38af421870f9 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Fri, 24 Apr 2020 12:36:30 -0700 Subject: [PATCH 05/49] Adding sum splitting --- .../SolToBoogie/GhostVarAndAxiomGenerator.cs | 35 +++++++++++----- Sources/SolToBoogie/MapArrayHelper.cs | 25 ++++++++++- Sources/SolToBoogie/ProcedureTranslator.cs | 41 +++++++------------ Sources/SolidityAnalysis/AliasAnalysis.cs | 19 +++++++-- 4 files changed, 77 insertions(+), 43 deletions(-) diff --git a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs index 2315cec7..50d740d0 100644 --- a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs +++ b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs @@ -311,13 +311,6 @@ private void GenerateGlobalVariables() context.Program.AddDeclaration(gas); } - if (context.TranslateFlags.InstrumentSums) - { - BoogieTypedIdent sumId = new BoogieTypedIdent("sum", new BoogieMapType(BoogieType.Ref, BoogieType.Int)); - BoogieGlobalVariable sum = new BoogieGlobalVariable(sumId); - context.Program.AddDeclaration(sum); - } - // Solidity-specific vars BoogieTypedIdent nowVar = new BoogieTypedIdent("now", BoogieType.Int); context.Program.AddDeclaration(new BoogieGlobalVariable(nowVar)); @@ -655,7 +648,7 @@ private BoogieAxiom GenerateAbiEncodePackedAxiomTwoArgsOneRef() private void GenerateMemoryVariables() { - HashSet generatedTypes = new HashSet(); + HashSet generatedMaps = new HashSet(); // mappings foreach (ContractDefinition contract in context.ContractToMappingsMap.Keys) { @@ -663,7 +656,18 @@ private void GenerateMemoryVariables() { Debug.Assert(varDecl.TypeName is Mapping); Mapping mapping = varDecl.TypeName as Mapping; - GenerateMemoryVariablesForMapping(varDecl, mapping, generatedTypes); + GenerateMemoryVariablesForMapping(varDecl, mapping, generatedMaps); + + if (context.TranslateFlags.InstrumentSums) + { + String sumName = mapHelper.GetSumName(varDecl); + if (!generatedMaps.Contains(sumName)) + { + generatedMaps.Add(sumName); + BoogieType sumType = new BoogieMapType(BoogieType.Ref, BoogieType.Int); + context.Program.AddDeclaration(new BoogieGlobalVariable(new BoogieTypedIdent(sumName, sumType))); + } + } } } // arrays @@ -673,7 +677,18 @@ private void GenerateMemoryVariables() { Debug.Assert(varDecl.TypeName is ArrayTypeName); ArrayTypeName array = varDecl.TypeName as ArrayTypeName; - GenerateMemoryVariablesForArray(varDecl, array, generatedTypes); + GenerateMemoryVariablesForArray(varDecl, array, generatedMaps); + + if (context.TranslateFlags.InstrumentSums) + { + String sumName = mapHelper.GetSumName(varDecl); + if (!generatedMaps.Contains(sumName)) + { + generatedMaps.Add(sumName); + BoogieType sumType = new BoogieMapType(BoogieType.Ref, BoogieType.Int); + context.Program.AddDeclaration(new BoogieGlobalVariable(new BoogieTypedIdent(sumName, sumType))); + } + } } } } diff --git a/Sources/SolToBoogie/MapArrayHelper.cs b/Sources/SolToBoogie/MapArrayHelper.cs index 74d58fec..5e1714c3 100644 --- a/Sources/SolToBoogie/MapArrayHelper.cs +++ b/Sources/SolToBoogie/MapArrayHelper.cs @@ -48,8 +48,7 @@ public string GetMemoryMapName(VariableDeclaration decl, BoogieType keyType, Boo { if (notAliased(decl)) { - return TransUtils.GetCanonicalVariableName(decl, context) + "_" + keyType.ToString() + "_" + - valType.ToString(); + return GetCanonicalMemName(keyType, valType) + "_" + context.Analysis.Alias.getGroupName(decl); } return GetCanonicalMemName(keyType, valType); @@ -63,6 +62,28 @@ public BoogieExpr GetMemoryMapSelectExpr(VariableDeclaration decl, BoogieType ma mapSelectExpr = new BoogieMapSelect(mapSelectExpr, indexExpr); return mapSelectExpr; } + + public static String GetCanonicalSumName() + { + return "sum"; + } + + public String GetSumName(VariableDeclaration decl) + { + if (notAliased(decl)) + { + return GetCanonicalSumName() + "_" + context.Analysis.Alias.getGroupName(decl); + } + + return GetCanonicalSumName(); + } + + public BoogieExpr GetSumExpr(VariableDeclaration decl, BoogieExpr varExpr) + { + BoogieIdentifierExpr sumIdent = new BoogieIdentifierExpr(GetSumName(decl)); + BoogieMapSelect sumSelect = new BoogieMapSelect(sumIdent, varExpr); + return sumSelect; + } public static BoogieType InferExprTypeFromTypeString(string typeString) { diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index b44454bf..b1a0b0ac 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -170,7 +170,7 @@ private String GetAccessPattern(VariableDeclaration varDecl, String boogieName) private String GetSumAccessPattern(VariableDeclaration varDecl, String boogieName) { - return $"\"sum(this.{varDecl.Name})={getSumName()}[{boogieName}[this]]\""; + return $"\"sum(this.{varDecl.Name})={mapHelper.GetSumName(varDecl)}[{boogieName}[this]]\""; } private void TranslateStateVarDeclaration(VariableDeclaration varDecl) @@ -726,26 +726,9 @@ private BoogieFuncCallExpr GetCallExprForZeroInit(BoogieType key, BoogieType val return new BoogieFuncCallExpr("zero" + keyStr + valStr + "Arr", new List()); } - private String getSumName() + public BoogieAssignCmd adjustSum(VariableDeclaration decl, BoogieBinaryOperation.Opcode op, BoogieExpr sumInd, BoogieExpr amt) { - return "sum"; - } - - private BoogieExpr getSumArray() - { - return new BoogieIdentifierExpr(getSumName()); - } - - private BoogieExpr getSumAccess(BoogieExpr key) - { - return new BoogieMapSelect(getSumArray(), key); - } - - public BoogieAssignCmd adjustSum(BoogieBinaryOperation.Opcode op, BoogieExpr sumInd, BoogieExpr amt) - { - /*BoogieExpr arrExpr = accessExpr.BaseExpr; - BoogieExpr arrExpr1 = TranslateExpr(baseExpression);*/ - BoogieExpr sumAccess = getSumAccess(sumInd); + BoogieExpr sumAccess = mapHelper.GetSumExpr(decl, sumInd); BoogieExpr sumSub = new BoogieBinaryOperation(op, sumAccess, amt); return new BoogieAssignCmd(sumAccess, sumSub); } @@ -856,7 +839,7 @@ private void GenerateInitializationForMappingStateVar(VariableDeclaration varDec if (context.TranslateFlags.InstrumentSums) { - currentStmtList.AddStatement(new BoogieAssignCmd(getSumAccess(lhsMap), new BoogieLiteralExpr(BigInteger.Zero))); + currentStmtList.AddStatement(new BoogieAssignCmd(mapHelper.GetSumExpr(varDecl, lhsMap), new BoogieLiteralExpr(BigInteger.Zero))); } } else @@ -1514,7 +1497,8 @@ public void updateAssignedSums(BoogieBinaryOperation.Opcode op, List var isInt = expr.TypeDescriptions.IsInt() || expr.TypeDescriptions.IsUint(); if (isInt && expr is IndexAccess access && boogieExprs[i] is BoogieMapSelect sel && sel.BaseExpr is BoogieMapSelect arrIdent) { - currentStmtList.AddStatement(adjustSum(op, arrIdent.Arguments[0], boogieExprs[i])); + VariableDeclaration decl = mapHelper.getDecl(access); + currentStmtList.AddStatement(adjustSum(decl, op, arrIdent.Arguments[0], boogieExprs[i])); } } } @@ -2056,7 +2040,8 @@ public override bool Visit(ExpressionStatement node) if (context.TranslateFlags.InstrumentSums && isInt && unaryOperation.SubExpression is IndexAccess access && lhs is BoogieMapSelect sel && sel.BaseExpr is BoogieMapSelect arrIdent) { - currentStmtList.AddStatement(adjustSum(oper, arrIdent.Arguments[0], new BoogieLiteralExpr(BigInteger.One))); + VariableDeclaration decl = mapHelper.getDecl(access); + currentStmtList.AddStatement(adjustSum(decl, oper, arrIdent.Arguments[0], new BoogieLiteralExpr(BigInteger.One))); } //print the value @@ -2087,7 +2072,8 @@ public override bool Visit(ExpressionStatement node) var isInt = typeDescription.IsInt() || typeDescription.IsUint(); if (context.TranslateFlags.InstrumentSums && isInt) { - BoogieExpr sumExpr = getSumAccess(element); + VariableDeclaration decl = mapHelper.getDecl(unaryOperation.SubExpression); + BoogieExpr sumExpr = mapHelper.GetSumExpr(decl, element); var sumAssign = new BoogieAssignCmd(sumExpr, new BoogieLiteralExpr(BigInteger.Zero)); currentStmtList.AddStatement(sumAssign); } @@ -2104,7 +2090,8 @@ public override bool Visit(ExpressionStatement node) if (context.TranslateFlags.InstrumentSums && isInt && unaryOperation.SubExpression is IndexAccess access && lhs is BoogieMapSelect sel && sel.BaseExpr is BoogieMapSelect arrIdent) { - currentStmtList.AddStatement(adjustSum(BoogieBinaryOperation.Opcode.SUB, arrIdent.Arguments[0], lhs)); + VariableDeclaration decl = mapHelper.getDecl(access); + currentStmtList.AddStatement(adjustSum(decl, BoogieBinaryOperation.Opcode.SUB, arrIdent.Arguments[0], lhs)); } BoogieExpr rhs = null; @@ -3035,7 +3022,7 @@ private void TranslateDynamicArrayPush(FunctionCall node) var isInt = mapValType.Equals(BoogieType.Int); if (context.TranslateFlags.InstrumentSums && isInt) { - BoogieExpr sumAccess = getSumAccess(receiver); + BoogieExpr sumAccess = mapHelper.GetSumExpr(decl, receiver); BoogieAssignCmd sumAssign = new BoogieAssignCmd(sumAccess, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, sumAccess, element)); currentStmtList.AddStatement(sumAssign); @@ -3826,7 +3813,7 @@ public override bool Visit(IndexAccess node) if (context.TranslateFlags.InstrumentSums) { - allocAndInit.AddStatement(new BoogieAssignCmd(getSumAccess(currentExpr), new BoogieLiteralExpr(BigInteger.Zero))); + allocAndInit.AddStatement(new BoogieAssignCmd(mapHelper.GetSumExpr(decl, currentExpr), new BoogieLiteralExpr(BigInteger.Zero))); } } else if (nestedValType == BoogieType.Ref) diff --git a/Sources/SolidityAnalysis/AliasAnalysis.cs b/Sources/SolidityAnalysis/AliasAnalysis.cs index 624611ae..fbfa4fcf 100644 --- a/Sources/SolidityAnalysis/AliasAnalysis.cs +++ b/Sources/SolidityAnalysis/AliasAnalysis.cs @@ -9,7 +9,7 @@ namespace SolidityAnalysis public class AliasAnalysis : BasicASTVisitor { - private HashSet results; + private List results; private Dictionary nameToStruct; private AST solidityAST; private HashSet> ignoredMethods; @@ -24,7 +24,7 @@ public AliasAnalysis(AST solidityAST, HashSet> ignoredMeth this.results = null; } - public HashSet getResults() + public List getResults() { if (results == null) { @@ -36,10 +36,20 @@ public HashSet getResults() public void runAnalysis() { - results = new HashSet(); + results = new List(); solidityAST.GetSourceUnits().Accept(this); } + public String getGroupName(VariableDeclaration varDec) + { + if (getResults().Contains(varDec)) + { + return varDec.Name + results.IndexOf(varDec); + } + + return ""; + } + public override bool Visit(StructDefinition def) { nameToStruct.Add(def.Name, def); @@ -58,7 +68,7 @@ public override bool Visit(VariableDeclaration decl) { results.Add(decl); } - + /* Can we add in user-defined types? else if (type is UserDefinedTypeName userDefined) { @@ -149,6 +159,7 @@ public override bool Visit(Assignment node) { if (node.Operator == "=") { + node.LeftHandSide.Accept(this); node.RightHandSide.Accept(this); } From 485260e40f87125d53de321abe92728da8d4e6ae Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Mon, 27 Apr 2020 12:03:52 -0700 Subject: [PATCH 06/49] A transaction is rejected if a value is provided to a non-payable function. Adding an assumption that msgvalue is 0 for all non-payable functions. --- Sources/SolToBoogie/TransUtils.cs | 234 +++++++++++++++--------------- 1 file changed, 121 insertions(+), 113 deletions(-) diff --git a/Sources/SolToBoogie/TransUtils.cs b/Sources/SolToBoogie/TransUtils.cs index 7a3acac3..946eab43 100644 --- a/Sources/SolToBoogie/TransUtils.cs +++ b/Sources/SolToBoogie/TransUtils.cs @@ -7,13 +7,13 @@ namespace SolToBoogie using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Numerics; - using System.Text; - using System.Text.RegularExpressions; + using System.Text; + using System.Text.RegularExpressions; using BoogieAST; using SolidityAST; - public static class Extensions - { + public static class Extensions + { public static bool IsInt(this TypeDescription typeDescription) { return !typeDescription.IsDynamicArray() && !typeDescription.IsStaticArray() @@ -262,21 +262,21 @@ public static bool IsDynamicArray(this TypeDescription typeDescription) { var match = Regex.Match(typeDescription.TypeString, @".*\[\]").Success; return match; - } - - // TODO: Provide a better way to check for array type + } + + // TODO: Provide a better way to check for array type public static bool IsStaticArray(this TypeDescription typeDescription) { var match = Regex.Match(typeDescription.TypeString, @".*\[\d+\]").Success; - return match; + return match; } - + public static bool IsArray(this TypeDescription typeDescription) { return IsStaticArray(typeDescription) || IsDynamicArray(typeDescription); } - } - + } + public static class TransUtils { public static BoogieType GetBoogieTypeFromSolidityTypeName(TypeName type) @@ -562,9 +562,9 @@ public static string InferFunctionSignature(TranslatorContext context, FunctionC } builder.Length -= 2; } - builder.Append(")"); - return builder.ToString(); - } + builder.Append(")"); + return builder.ToString(); + } } private static string InferTypeFromTypeString(string typestr) @@ -607,104 +607,104 @@ private static string InferTypeFromTypeString(string typestr) return typeString; } - public static string InferTypeFromExpression(Expression expression) - { - Debug.Assert(expression.TypeDescriptions != null, $"Null type description for {expression}"); - string typeString = expression.TypeDescriptions.TypeString; - if (typeString.Equals("bool")) - { - return "bool"; - } - else if (typeString.StartsWith("uint")) - { - return "uint"; - } - else if (typeString.StartsWith("int")) - { - return "int"; - } - else if (typeString.Equals("address") || typeString.Equals("address payable")) - { - return "address"; - } - else - { - throw new SystemException($"Unknown type string {typeString} for expression {expression}"); - } - } - - public static string GetFuncNameFromFuncCall(FunctionCall node) - { - if (node.Expression is FunctionCall funcCall) - { - if (funcCall.Expression is MemberAccess memberAccess) - { - return GetFuncNameFromFuncCallExpr(memberAccess.Expression); - } - else - { - return GetFuncNameFromFuncCallExpr(funcCall.Expression); - } - } - else - { - return GetFuncNameFromFuncCallExpr(node.Expression); - } - } - - public static string GetFuncNameFromFuncCallExpr(Expression expr) - { - string functionName = null; - if (expr is Identifier ident) - { - functionName = ident.Name; - } + public static string InferTypeFromExpression(Expression expression) + { + Debug.Assert(expression.TypeDescriptions != null, $"Null type description for {expression}"); + string typeString = expression.TypeDescriptions.TypeString; + if (typeString.Equals("bool")) + { + return "bool"; + } + else if (typeString.StartsWith("uint")) + { + return "uint"; + } + else if (typeString.StartsWith("int")) + { + return "int"; + } + else if (typeString.Equals("address") || typeString.Equals("address payable")) + { + return "address"; + } + else + { + throw new SystemException($"Unknown type string {typeString} for expression {expression}"); + } + } + + public static string GetFuncNameFromFuncCall(FunctionCall node) + { + if (node.Expression is FunctionCall funcCall) + { + if (funcCall.Expression is MemberAccess memberAccess) + { + return GetFuncNameFromFuncCallExpr(memberAccess.Expression); + } + else + { + return GetFuncNameFromFuncCallExpr(funcCall.Expression); + } + } + else + { + return GetFuncNameFromFuncCallExpr(node.Expression); + } + } + + public static string GetFuncNameFromFuncCallExpr(Expression expr) + { + string functionName = null; + if (expr is Identifier ident) + { + functionName = ident.Name; + } else if (expr is ElementaryTypeNameExpression elemExpr) { functionName = elemExpr.TypeName; - } - else if (expr is MemberAccess memberAccess) - { - functionName = memberAccess.MemberName; - } - else if (expr is FunctionCall funcCall) - { - functionName = GetFuncNameFromFuncCall(funcCall); - } - else - { - throw new SystemException($"Unknown form of function call expr: {expr}"); - } - Debug.Assert(functionName != null); - return functionName; - } - - public static Tuple GenerateSourceInfoAnnotation(ASTNode node, TranslatorContext context) - { - return (Tuple.Create(context.GetAbsoluteSourcePathOfASTNode(node), context.GetLineNumberOfASTNode(node))); - } - - public static List GetDefaultInParams() - { - return new List() - { - // add a parameter for this object - new BoogieFormalParam(new BoogieTypedIdent("this", BoogieType.Ref)), - // add a parameter for msg.sender - new BoogieFormalParam(new BoogieTypedIdent("msgsender_MSG", BoogieType.Ref)), - // add a parameter for msg.value - new BoogieFormalParam(new BoogieTypedIdent("msgvalue_MSG", BoogieType.Int)), - }; - } - - public static List GetDefaultArguments() - { - return new List() - { - new BoogieIdentifierExpr("this"), - new BoogieIdentifierExpr("msgsender_MSG"), - new BoogieIdentifierExpr("msgvalue_MSG"), - }; + } + else if (expr is MemberAccess memberAccess) + { + functionName = memberAccess.MemberName; + } + else if (expr is FunctionCall funcCall) + { + functionName = GetFuncNameFromFuncCall(funcCall); + } + else + { + throw new SystemException($"Unknown form of function call expr: {expr}"); + } + Debug.Assert(functionName != null); + return functionName; + } + + public static Tuple GenerateSourceInfoAnnotation(ASTNode node, TranslatorContext context) + { + return (Tuple.Create(context.GetAbsoluteSourcePathOfASTNode(node), context.GetLineNumberOfASTNode(node))); + } + + public static List GetDefaultInParams() + { + return new List() + { + // add a parameter for this object + new BoogieFormalParam(new BoogieTypedIdent("this", BoogieType.Ref)), + // add a parameter for msg.sender + new BoogieFormalParam(new BoogieTypedIdent("msgsender_MSG", BoogieType.Ref)), + // add a parameter for msg.value + new BoogieFormalParam(new BoogieTypedIdent("msgvalue_MSG", BoogieType.Int)), + }; + } + + public static List GetDefaultArguments() + { + return new List() + { + new BoogieIdentifierExpr("this"), + new BoogieIdentifierExpr("msgsender_MSG"), + new BoogieIdentifierExpr("msgvalue_MSG"), + }; } public static List CollectLocalVars(List contracts, TranslatorContext context) @@ -769,7 +769,7 @@ public static List CollectLocalVars(List con } return localVars; } - + /// /// generate a non-deterministic choice block to call every public visible functions except constructors /// @@ -853,6 +853,14 @@ public static BoogieIfCmd GenerateChoiceBlock(List contracts var gasVar = new BoogieIdentifierExpr("gas"); thenBody.AddStatement(new BoogieAssignCmd(gasVar, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, gasVar, new BoogieLiteralExpr(TranslatorContext.TX_GAS_COST)))); } + + if (!funcDef.StateMutability.Equals(EnumStateMutability.PAYABLE)) + { + BoogieExpr assumeExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, + new BoogieIdentifierExpr("msgvalue_MSG"), new BoogieLiteralExpr(BigInteger.Zero)); + thenBody.AddStatement(new BoogieAssumeCmd(assumeExpr)); + } + BoogieCallCmd callCmd = new BoogieCallCmd(callee, inputs, outputs); thenBody.AddStatement(callCmd); @@ -877,6 +885,6 @@ public static void havocGas(List list) new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GT, gasVar, new BoogieLiteralExpr(TranslatorContext.MIN_GAS_LIMIT)), new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.LE, gasVar, new BoogieLiteralExpr(TranslatorContext.MAX_GAS_LIMIT))))); } - - } -} + + } +} From 5365665aa99320f71819be83e62d7097400a1e9a Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Mon, 27 Apr 2020 15:34:59 -0700 Subject: [PATCH 07/49] Essentially adding a deployer mode. It will call all of the public functions of contract variables in addition to the contract's public functions --- Sources/SolToBoogie/HarnessGenerator.cs | 59 +++++++++++++++++++++++-- Sources/SolToBoogie/ParseUtils.cs | 5 +++ Sources/SolToBoogie/TransUtils.cs | 15 ++++--- Sources/SolToBoogie/TranslatorFlags.cs | 3 ++ 4 files changed, 73 insertions(+), 9 deletions(-) diff --git a/Sources/SolToBoogie/HarnessGenerator.cs b/Sources/SolToBoogie/HarnessGenerator.cs index e0824881..baf961b9 100644 --- a/Sources/SolToBoogie/HarnessGenerator.cs +++ b/Sources/SolToBoogie/HarnessGenerator.cs @@ -1,5 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. + +using System; + namespace SolToBoogie { using System.Collections.Generic; @@ -98,7 +101,8 @@ private void GenerateBoogieHarnessForContract(ContractDefinition contract, Dicti BoogieProcedure harness = new BoogieProcedure(harnessName, inParams, outParams); context.Program.AddDeclaration(harness); - List localVars = TransUtils.CollectLocalVars(new List() { contract }, context); + //List localVars = TransUtils.CollectLocalVars(new List() { contract }, context); + List localVars = CollectLocalVars(contract); localVars.Add(new BoogieLocalVariable(new BoogieTypedIdent("tmpNow", BoogieType.Int))); BoogieStmtList harnessBody = new BoogieStmtList(); harnessBody.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, new BoogieIdentifierExpr("now"), new BoogieLiteralExpr(0)))); @@ -188,7 +192,8 @@ private BoogieWhileCmd GenerateWhileLoop(ContractDefinition contract, Dictionary BoogieStmtList body = GenerateHavocBlock(contract, localVars); // generate the choice block - body.AddStatement(TransUtils.GenerateChoiceBlock(new List() { contract }, context)); + //body.AddStatement(TransUtils.GenerateChoiceBlock(new List() { contract }, context)); + body.AddStatement(GenerateChoices(contract)); // generate candidate invariants for Houdini List candidateInvs = new List(); @@ -269,14 +274,60 @@ private void GenerateCorralChoiceProcForContract(ContractDefinition contract) BoogieProcedure harness = new BoogieProcedure(procName, inParams, outParams); context.Program.AddDeclaration(harness); - List localVars = RemoveThisFromVariables(TransUtils.CollectLocalVars(new List() { contract }, context)); + List localVars = RemoveThisFromVariables(CollectLocalVars(contract)); localVars.Add(new BoogieLocalVariable(new BoogieTypedIdent("tmpNow", BoogieType.Int))); BoogieStmtList procBody = GenerateHavocBlock(contract, localVars); - procBody.AddStatement(TransUtils.GenerateChoiceBlock(new List() { contract }, context)); + //procBody.AddStatement(TransUtils.GenerateChoiceBlock(new List() { contract }, context)); + procBody.AddStatement(GenerateChoices(contract)); BoogieImplementation procImpl = new BoogieImplementation(procName, inParams, outParams, localVars, procBody); context.Program.AddDeclaration(procImpl); } + private List CollectLocalVars(ContractDefinition contract) + { + List contracts = new List() {contract}; + + if (context.TranslateFlags.TxnsOnFields) + { + HashSet contractFields = context.GetStateVarsByContract(contract); + foreach (VariableDeclaration contractField in contractFields) + { + if (contractField.TypeDescriptions.IsContract() && contractField.TypeName is UserDefinedTypeName) + { + String fieldContractName = contractField.TypeName.ToString(); + ContractDefinition fieldDef = context.GetContractByName(fieldContractName); + contracts.Add(fieldDef); + } + } + } + + return TransUtils.CollectLocalVars(contracts, context); + } + + private BoogieIfCmd GenerateChoices(ContractDefinition contract) + { + BoogieExpr thisVal = new BoogieIdentifierExpr("this"); + Tuple curChoices = TransUtils.GeneratePartialChoiceBlock(new List() {contract}, context, thisVal, 0); + if (context.TranslateFlags.TxnsOnFields) + { + HashSet contractFields = context.GetStateVarsByContract(contract); + + foreach (VariableDeclaration contractField in contractFields) + { + if (contractField.TypeDescriptions.IsContract() && contractField.TypeName is UserDefinedTypeName) + { + BoogieExpr fieldInstance = new BoogieMapSelect(new BoogieIdentifierExpr(TransUtils.GetCanonicalVariableName(contractField, context)), thisVal); + String fieldContractName = contractField.TypeName.ToString(); + ContractDefinition fieldDef = context.GetContractByName(fieldContractName); + curChoices = TransUtils.GeneratePartialChoiceBlock(new List() {fieldDef}, + context, fieldInstance, curChoices.Item2, curChoices.Item1); + } + } + } + + return curChoices.Item1; + } + private void GenerateCorralHarnessForContract(ContractDefinition contract) { string harnessName = "CorralEntry_" + contract.Name; diff --git a/Sources/SolToBoogie/ParseUtils.cs b/Sources/SolToBoogie/ParseUtils.cs index e45ea877..84993cda 100644 --- a/Sources/SolToBoogie/ParseUtils.cs +++ b/Sources/SolToBoogie/ParseUtils.cs @@ -167,6 +167,11 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, translatorFlags.RunAliasAnalysis = true; } + if (args.Any(x => x.Equals("/txnsOnFields"))) + { + translatorFlags.TxnsOnFields = true; + } + translatorFlags.PerformContractInferce = args.Any(x => x.StartsWith("/contractInfer")); // don't perform verification for some of these omitFlags diff --git a/Sources/SolToBoogie/TransUtils.cs b/Sources/SolToBoogie/TransUtils.cs index 946eab43..eb4901b0 100644 --- a/Sources/SolToBoogie/TransUtils.cs +++ b/Sources/SolToBoogie/TransUtils.cs @@ -769,7 +769,7 @@ public static List CollectLocalVars(List con } return localVars; } - + /// /// generate a non-deterministic choice block to call every public visible functions except constructors /// @@ -780,8 +780,13 @@ public static List CollectLocalVars(List con /// public static BoogieIfCmd GenerateChoiceBlock(List contracts, TranslatorContext context, Tuple callBackTarget = null) { - BoogieIfCmd ifCmd = null; - int j = 0; + return GeneratePartialChoiceBlock(contracts, context, new BoogieIdentifierExpr("this"), 0, null, callBackTarget).Item1; + } + + public static Tuple GeneratePartialChoiceBlock(List contracts, TranslatorContext context, BoogieExpr thisExpr, int startingPoint, BoogieIfCmd alternatives = null, Tuple callBackTarget = null) + { + BoogieIfCmd ifCmd = alternatives; + int j = startingPoint; foreach (var contract in contracts) { if (contract.ContractKind != EnumContractKind.CONTRACT) continue; @@ -811,7 +816,7 @@ public static BoogieIfCmd GenerateChoiceBlock(List contracts List inputs = new List() { // let us just call back into the calling contract - callBackTarget != null ? new BoogieIdentifierExpr(callBackTarget.Item1) : new BoogieIdentifierExpr("this"), + callBackTarget != null ? new BoogieIdentifierExpr(callBackTarget.Item1) : thisExpr, callBackTarget != null ? new BoogieIdentifierExpr(callBackTarget.Item2) : new BoogieIdentifierExpr("msgsender_MSG"), new BoogieIdentifierExpr("msgvalue_MSG"), }; @@ -868,7 +873,7 @@ public static BoogieIfCmd GenerateChoiceBlock(List contracts ifCmd = new BoogieIfCmd(guard, thenBody, elseBody); } } - return ifCmd; + return new Tuple(ifCmd, j); } public static void havocGas(BoogieStmtList list) { diff --git a/Sources/SolToBoogie/TranslatorFlags.cs b/Sources/SolToBoogie/TranslatorFlags.cs index 79bb2b69..ba77dcfd 100644 --- a/Sources/SolToBoogie/TranslatorFlags.cs +++ b/Sources/SolToBoogie/TranslatorFlags.cs @@ -34,8 +34,11 @@ public TranslatorFlags() OmitAssumeFalseForDynDispatch = false; NoTxnsFromContract = true; RunAliasAnalysis = false; + TxnsOnFields = false; } + public bool TxnsOnFields { get; set; } + public bool RunAliasAnalysis { get; set; } public bool NoTxnsFromContract { get; set; } From d5bc39552318c2b0a41935df181fd6ad149957e3 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Mon, 4 May 2020 10:34:06 -0700 Subject: [PATCH 08/49] Adding a fix to a small bug in lazyAlloc scheme --- Sources/SolToBoogie/ProcedureTranslator.cs | 39 +++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index b1a0b0ac..67189198 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -969,7 +969,44 @@ private void InitializeNestedArrayMappingStateVar(VariableDeclaration varDecl, M { if (context.TranslateFlags.QuantFreeAllocs) { - currentStmtList.AddStatement(new BoogieAssignCmd(lhs0, GetCallExprForZeroInit(mapKeyType, BoogieType.Ref))); + //currentStmtList.AddStatement(new BoogieAssignCmd(lhs0, GetCallExprForZeroInit(mapKeyType, BoogieType.Ref))); + BoogieExpr index = new BoogieMapSelect(varExpr, new BoogieIdentifierExpr("this")); + + TypeName curType = varDecl.TypeName; + + while (curType is Mapping || curType is ArrayTypeName) + { + BoogieType keyType = null; + BoogieType valType = null; + if (curType is Mapping map) + { + keyType = TransUtils.GetBoogieTypeFromSolidityTypeName(map.KeyType); + valType = TransUtils.GetBoogieTypeFromSolidityTypeName(map.ValueType); + curType = map.ValueType; + } + else if (curType is ArrayTypeName arr) + { + keyType = BoogieType.Int; + valType = TransUtils.GetBoogieTypeFromSolidityTypeName(arr.BaseType); + curType = arr.BaseType; + } + + string memName = mapHelper.GetMemoryMapName(varDecl, keyType, valType); + BoogieMapSelect lhs = new BoogieMapSelect(new BoogieIdentifierExpr(memName), index); + currentStmtList.AddStatement((new BoogieAssignCmd(lhs, GetCallExprForZeroInit(keyType, valType)))); + if (valType.Equals(BoogieType.Int)) + { + index = new BoogieLiteralExpr(BigInteger.Zero); + } + else if (valType.Equals(BoogieType.Ref)) + { + index = new BoogieIdentifierExpr("null"); + } + else + { + index = new BoogieLiteralExpr(false); + } + } } else { From 069bc5f829674f26d43f48766a2834500f1a169a Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Wed, 13 May 2020 18:00:32 -0700 Subject: [PATCH 09/49] Adding a smtDefined to ConstantToRef so that it acts as the identity function. Adding a flag to model non-linear arithmetic as an uninterpreted function. Adding preliminary support for super. Function with super now seem to behave correctly --- .../SolToBoogie/GhostVarAndAxiomGenerator.cs | 29 +++++++ Sources/SolToBoogie/ParseUtils.cs | 5 ++ Sources/SolToBoogie/ProcedureTranslator.cs | 78 +++++++++++++++++-- Sources/SolToBoogie/TranslatorFlags.cs | 3 + 4 files changed, 110 insertions(+), 5 deletions(-) diff --git a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs index 50d740d0..6c19be46 100644 --- a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs +++ b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs @@ -54,6 +54,12 @@ private void GenerateFunctions() context.Program.AddDeclaration(GenerateZeroRefRefArrayFunction()); context.Program.AddDeclaration(GenerateZeroIntRefArrayFunction()); } + + if (context.TranslateFlags.NoNonlinearArith) + { + context.Program.AddDeclaration(generateNonlinearMulFunction()); + context.Program.AddDeclaration(generateNonlinearDivFunction()); + } } private BoogieFunction GenerateZeroRefIntArrayFunction() @@ -187,6 +193,7 @@ private BoogieFunction GenerateConstToRefFunction() //function for Int to Ref var inVar = new BoogieFormalParam(new BoogieTypedIdent("x", BoogieType.Int)); var outVar = new BoogieFormalParam(new BoogieTypedIdent("ret", BoogieType.Ref)); + BoogieAttribute attr = new BoogieAttribute(":smtdefined", "x"); return new BoogieFunction( "ConstantToRef", new List() { inVar }, @@ -232,6 +239,28 @@ private BoogieFunction GenerateVeriSolSumFunction() null); } + private BoogieFunction generateNonlinearMulFunction() + { + string fnName = "nonlinearMul"; + var inVar1 = new BoogieFormalParam(new BoogieTypedIdent("x", BoogieType.Int)); + var inVar2 = new BoogieFormalParam(new BoogieTypedIdent("y", BoogieType.Int)); + var outVar = new BoogieFormalParam(new BoogieTypedIdent("ret", BoogieType.Int)); + + return new BoogieFunction(fnName, new List() {inVar1, inVar2}, + new List() {outVar}); + } + + private BoogieFunction generateNonlinearDivFunction() + { + string fnName = "nonlinearDiv"; + var inVar1 = new BoogieFormalParam(new BoogieTypedIdent("x", BoogieType.Int)); + var inVar2 = new BoogieFormalParam(new BoogieTypedIdent("y", BoogieType.Int)); + var outVar = new BoogieFormalParam(new BoogieTypedIdent("ret", BoogieType.Int)); + + return new BoogieFunction(fnName, new List() {inVar1, inVar2}, + new List() {outVar}); + } + private void GenerateTypes() { diff --git a/Sources/SolToBoogie/ParseUtils.cs b/Sources/SolToBoogie/ParseUtils.cs index 84993cda..9bdf3af3 100644 --- a/Sources/SolToBoogie/ParseUtils.cs +++ b/Sources/SolToBoogie/ParseUtils.cs @@ -172,6 +172,11 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, translatorFlags.TxnsOnFields = true; } + if (args.Any(x => x.Equals("/noNonlinearArith"))) + { + translatorFlags.NoNonlinearArith = true; + } + translatorFlags.PerformContractInferce = args.Any(x => x.StartsWith("/contractInfer")); // don't perform verification for some of these omitFlags diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 67189198..2f98a1c0 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -2259,6 +2259,10 @@ public override bool Visit(Identifier node) { currentExpr = new BoogieIdentifierExpr("this"); } + else if (node.Name.Equals("super")) + { + currentExpr = new BoogieIdentifierExpr("this"); + } else if (node.Name.Equals("now")) { currentExpr = new BoogieIdentifierExpr("now"); @@ -3085,6 +3089,11 @@ private bool IsExternalFunctionCall(FunctionCall node) { return true; } + + if (identifier.Name == "super") + { + return true; + } var contract = context.GetASTNodeById(identifier.ReferencedDeclaration) as ContractDefinition; if (contract == null) { @@ -3188,11 +3197,13 @@ private void TranslateExternalFunctionCall(FunctionCall node, List outParams, List arguments, bool condition, BoogieExpr receiver) + private void TranslateDynamicDispatchCall(FunctionCall node, List outParams, List arguments, bool condition, bool isSuper, BoogieExpr receiver) { ContractDefinition contractDefn; VariableDeclaration varDecl; // Solidity internally generates foo() getter for any public state // variable foo in a contract. + if (IsGetterForPublicVariable(node, out varDecl, out contractDefn)) { + VeriSolAssert(!isSuper, "Super is not supported for public getters right now"); List subtypes = new List(context.GetSubTypesOfContract(contractDefn)); Debug.Assert(subtypes.Count > 0); @@ -3369,7 +3382,37 @@ private void TranslateDynamicDispatchCall(FunctionCall node, List() { leftExpr, rightExpr}); + } + } + else if (context.TranslateFlags.NoNonlinearArith && node.Operator == "/") + { + if ((node.LeftExpression is Literal leftLiteral && leftLiteral.Kind.Equals("number")) || + (node.RightExpression is Literal rightLiteral && rightLiteral.Kind.Equals("number"))) + { + binaryExpr = new BoogieBinaryOperation(op, leftExpr, rightExpr); + } + else + { + binaryExpr = new BoogieFuncCallExpr("nonlinearDiv", new List() { leftExpr, rightExpr}); + } + } else { binaryExpr = new BoogieBinaryOperation(op, leftExpr, rightExpr); diff --git a/Sources/SolToBoogie/TranslatorFlags.cs b/Sources/SolToBoogie/TranslatorFlags.cs index ba77dcfd..8ba544f7 100644 --- a/Sources/SolToBoogie/TranslatorFlags.cs +++ b/Sources/SolToBoogie/TranslatorFlags.cs @@ -35,8 +35,11 @@ public TranslatorFlags() NoTxnsFromContract = true; RunAliasAnalysis = false; TxnsOnFields = false; + NoNonlinearArith = false; } + public bool NoNonlinearArith { get; set; } + public bool TxnsOnFields { get; set; } public bool RunAliasAnalysis { get; set; } From 74cb74ca3daa03daabba5053a9192e131b5d5d5a Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Wed, 13 May 2020 19:53:08 -0700 Subject: [PATCH 10/49] Forgot to add attribute to ConstantToRef --- Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs index 6c19be46..94990289 100644 --- a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs +++ b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs @@ -198,7 +198,7 @@ private BoogieFunction GenerateConstToRefFunction() "ConstantToRef", new List() { inVar }, new List() { outVar }, - null); + new List() { attr }); } private BoogieFunction GenerateRefToInt() From 8458f7a5a0493b35d26cc963446ead907b792acc Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Wed, 13 May 2020 19:57:46 -0700 Subject: [PATCH 11/49] Adding fix to ConstToRef --- Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs index 94990289..5f720f30 100644 --- a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs +++ b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs @@ -193,7 +193,7 @@ private BoogieFunction GenerateConstToRefFunction() //function for Int to Ref var inVar = new BoogieFormalParam(new BoogieTypedIdent("x", BoogieType.Int)); var outVar = new BoogieFormalParam(new BoogieTypedIdent("ret", BoogieType.Ref)); - BoogieAttribute attr = new BoogieAttribute(":smtdefined", "x"); + BoogieAttribute attr = new BoogieAttribute("smtdefined", "\"x\""); return new BoogieFunction( "ConstantToRef", new List() { inVar }, From 0c7946f43e138b3e421f5f1b1cf633366737ad12 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Thu, 14 May 2020 09:47:45 -0700 Subject: [PATCH 12/49] Adding power function to non-linear modeling --- Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs | 12 ++++++++++++ Sources/SolToBoogie/ProcedureTranslator.cs | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs index 5f720f30..93fb38bf 100644 --- a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs +++ b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs @@ -59,6 +59,7 @@ private void GenerateFunctions() { context.Program.AddDeclaration(generateNonlinearMulFunction()); context.Program.AddDeclaration(generateNonlinearDivFunction()); + context.Program.AddDeclaration(generateNonlinearPowFunction()); } } @@ -260,6 +261,17 @@ private BoogieFunction generateNonlinearDivFunction() return new BoogieFunction(fnName, new List() {inVar1, inVar2}, new List() {outVar}); } + + private BoogieFunction generateNonlinearPowFunction() + { + string fnName = "nonlinearPow"; + var inVar1 = new BoogieFormalParam(new BoogieTypedIdent("x", BoogieType.Int)); + var inVar2 = new BoogieFormalParam(new BoogieTypedIdent("y", BoogieType.Int)); + var outVar = new BoogieFormalParam(new BoogieTypedIdent("ret", BoogieType.Int)); + + return new BoogieFunction(fnName, new List() {inVar1, inVar2}, + new List() {outVar}); + } private void GenerateTypes() diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 2f98a1c0..343e1ae1 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -3741,6 +3741,10 @@ public override bool Visit(BinaryOperation node) binaryExpr = new BoogieLiteralExpr((BigInteger)Math.Pow((double)valueLeft, (double)valueRight)); } + else if (context.TranslateFlags.NoNonlinearArith) + { + binaryExpr = new BoogieFuncCallExpr("nonlinearPow", new List() { leftExpr, rightExpr}); + } else { Console.WriteLine($"VeriSol translation error: power operation for non-constants or with constant subexpressions is not supported; hint: use temps for subexpressions"); From a1f90de30836751e6600be1ff6f158388254e897 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Thu, 14 May 2020 14:09:35 -0700 Subject: [PATCH 13/49] Fixing a bug with super arguments --- Sources/SolToBoogie/ProcedureTranslator.cs | 23 ++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 343e1ae1..040337b2 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -3183,13 +3183,24 @@ private void TranslateExternalFunctionCall(FunctionCall node, List arguments = new List() + List arguments = null; + + if (isSuper) { - receiver, - new BoogieIdentifierExpr("this"), - msgValueExpr, - }; + arguments = TransUtils.GetDefaultArguments(); + } + else + { + arguments = new List() { + receiver, + new BoogieIdentifierExpr("this"), + msgValueExpr, + }; + } + foreach (Expression arg in node.Arguments) { @@ -3197,7 +3208,7 @@ private void TranslateExternalFunctionCall(FunctionCall node, List Date: Thu, 21 May 2020 20:28:31 -0700 Subject: [PATCH 14/49] Updating gas instrumentation for fallback. It was possible to enter an infinite loop where you had run out of gas but it was never checked. We not guard against that. Also adding an explicit revert call for the callback model. --- Sources/SolToBoogie/FallbackGenerator.cs | 21 ++++++++++++++++++++- Sources/SolToBoogie/TransUtils.cs | 22 +++++++++++++++------- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/Sources/SolToBoogie/FallbackGenerator.cs b/Sources/SolToBoogie/FallbackGenerator.cs index e4edce2b..0bc31291 100644 --- a/Sources/SolToBoogie/FallbackGenerator.cs +++ b/Sources/SolToBoogie/FallbackGenerator.cs @@ -137,7 +137,26 @@ private BoogieStmtList CreateBodyOfUnknownFallback(List fbLocalV // if (*) fn1(from, *, ...) // we only redirect the calling contract, but the msg.sender need not be to, as it can call into anohter contract that calls // into from - procBody.AddStatement(TransUtils.GenerateChoiceBlock(context.ContractDefinitions.ToList(), context, Tuple.Create(inParams[0].Name, inParams[1].Name))); + + Tuple choices = TransUtils.GeneratePartialChoiceBlock(context.ContractDefinitions.ToList(), context, + new BoogieIdentifierExpr("this"), 0, null, Tuple.Create(inParams[0].Name, inParams[1].Name)); + BoogieIfCmd ifCmd = null; + + if (context.TranslateFlags.ModelReverts) + { + BoogieExpr guard = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, + new BoogieIdentifierExpr("choice"), + new BoogieLiteralExpr(choices.Item2 + 1)); + + BoogieAssignCmd assign = new BoogieAssignCmd(new BoogieIdentifierExpr("revert"), new BoogieLiteralExpr(true)); + ifCmd = new BoogieIfCmd(guard, BoogieStmtList.MakeSingletonStmtList(assign), BoogieStmtList.MakeSingletonStmtList(choices.Item1)); + } + else + { + ifCmd = choices.Item1; + } + + procBody.AddStatement(ifCmd); } return procBody; } diff --git a/Sources/SolToBoogie/TransUtils.cs b/Sources/SolToBoogie/TransUtils.cs index eb4901b0..90190a20 100644 --- a/Sources/SolToBoogie/TransUtils.cs +++ b/Sources/SolToBoogie/TransUtils.cs @@ -853,21 +853,29 @@ public static Tuple GeneratePartialChoiceBlock(List Date: Fri, 22 May 2020 15:49:48 -0700 Subject: [PATCH 15/49] Adding ability to support modular arithmetic with modulus. Only supporting uints right now. --- Sources/BoogieAST/BoogieAST.cs | 6 ++-- Sources/SolToBoogie/ParseUtils.cs | 7 +++++ Sources/SolToBoogie/ProcedureTranslator.cs | 34 +++++++++++++++++++--- Sources/SolToBoogie/TranslatorFlags.cs | 2 ++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Sources/BoogieAST/BoogieAST.cs b/Sources/BoogieAST/BoogieAST.cs index f5f1af9f..f3f64687 100644 --- a/Sources/BoogieAST/BoogieAST.cs +++ b/Sources/BoogieAST/BoogieAST.cs @@ -1212,6 +1212,8 @@ public enum Opcode UNKNOWN, } + public static bool USE_ARITH_OPS { get; set; } + public Opcode Op { get; set; } public BoogieExpr Lhs { get; set; } @@ -1245,9 +1247,9 @@ public static string OpcodeToString(Opcode op) case Opcode.MUL: return "*"; case Opcode.DIV: - return "div"; + return USE_ARITH_OPS ? "/" : "div"; case Opcode.MOD: - return "mod"; + return USE_ARITH_OPS ? "%" : "mod"; case Opcode.EQ: return "=="; case Opcode.NEQ: diff --git a/Sources/SolToBoogie/ParseUtils.cs b/Sources/SolToBoogie/ParseUtils.cs index 9bdf3af3..85e7ba7a 100644 --- a/Sources/SolToBoogie/ParseUtils.cs +++ b/Sources/SolToBoogie/ParseUtils.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Linq; using System.Text; +using BoogieAST; namespace SolToBoogie { @@ -177,6 +178,12 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, translatorFlags.NoNonlinearArith = true; } + if (args.Any(x => x.Equals("/useNumericOperators"))) + { + translatorFlags.UseNumericOperators = true; + BoogieBinaryOperation.USE_ARITH_OPS = true; + } + translatorFlags.PerformContractInferce = args.Any(x => x.StartsWith("/contractInfer")); // don't perform verification for some of these omitFlags diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 040337b2..f871b274 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -1509,7 +1509,7 @@ public override bool Visit(VariableDeclarationStatement node) private BoogieExpr AddModuloOp(Expression srcExpr, BoogieExpr expr, TypeDescription type) { - if (context.TranslateFlags.UseModularArithmetic) + if (context.TranslateFlags.UseModularArithmetic && !context.TranslateFlags.UseNumericOperators) { if (type != null) { @@ -1522,6 +1522,15 @@ private BoogieExpr AddModuloOp(Expression srcExpr, BoogieExpr expr, TypeDescript } } } + else if (context.TranslateFlags.UseModularArithmetic && context.TranslateFlags.UseNumericOperators) + { + if (type.IsUintWSize(srcExpr, out uint uintSize)) + { + BigInteger maxUIntValue = (BigInteger)Math.Pow(2, uintSize); + return new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.MOD, expr, new BoogieLiteralExpr(maxUIntValue)); + } + } + return expr; } @@ -1806,7 +1815,7 @@ private void AddAssumeForUints(Expression expr, BoogieExpr boogieExpr, TypeDescr { var ge0 = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, boogieExpr, new BoogieLiteralExpr(BigInteger.Zero)); - if (context.TranslateFlags.UseModularArithmetic) + if (context.TranslateFlags.UseModularArithmetic && !context.TranslateFlags.UseNumericOperators) { var isUint = typeDesc.IsUintWSize(expr, out uint sz); if (isUint) @@ -3594,7 +3603,7 @@ private void TranslateTypeCast(FunctionCall node, BoogieExpr lhs, out bool isEle } // We do not handle downcasts between unsigned integers, when /useModularArithmetic option is enabled: - if (context.TranslateFlags.UseModularArithmetic) + if (context.TranslateFlags.UseModularArithmetic && !context.TranslateFlags.UseNumericOperators) { bool argTypeIsUint = node.Arguments[0].TypeDescriptions.IsUintWSize(node.Arguments[0], out uint argSz); if (argTypeIsUint && elemType.ToString().StartsWith("uint")) @@ -3606,6 +3615,14 @@ private void TranslateTypeCast(FunctionCall node, BoogieExpr lhs, out bool isEle } } } + else if (context.TranslateFlags.UseModularArithmetic && context.TranslateFlags.UseNumericOperators) + { + if (node.Arguments[0].TypeDescriptions.IsUintWSize(node.Arguments[0], out uint uintSize)) + { + BigInteger maxUIntValue = (BigInteger)Math.Pow(2, uintSize); + rhsExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.MOD, rhsExpr, new BoogieLiteralExpr(maxUIntValue)); + } + } // lets update currentExpr with rhsExpr. The caller may update it with the temporary currentExpr = rhsExpr; @@ -3793,7 +3810,7 @@ public override bool Visit(BinaryOperation node) } currentExpr = binaryExpr; - if (context.TranslateFlags.UseModularArithmetic) + if (context.TranslateFlags.UseModularArithmetic && !context.TranslateFlags.UseNumericOperators) { //if (node.Operator == "+" || node.Operator == "-" || node.Operator == "*" || node.Operator == "/" || node.Operator == "**") if (node.Operator == "+" || node.Operator == "-" || node.Operator == "*" || node.Operator == "/") @@ -3818,6 +3835,15 @@ public override bool Visit(BinaryOperation node) } } } + else if (context.TranslateFlags.UseModularArithmetic && context.TranslateFlags.UseNumericOperators) + { + if (node.TypeDescriptions.IsUintWSize(node, out uint uintSize)) + { + BigInteger maxUIntValue = (BigInteger)Math.Pow(2, uintSize); + currentExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.MOD, currentExpr, new BoogieLiteralExpr(maxUIntValue)); + } + } + return false; } diff --git a/Sources/SolToBoogie/TranslatorFlags.cs b/Sources/SolToBoogie/TranslatorFlags.cs index 8ba544f7..a94adff3 100644 --- a/Sources/SolToBoogie/TranslatorFlags.cs +++ b/Sources/SolToBoogie/TranslatorFlags.cs @@ -36,8 +36,10 @@ public TranslatorFlags() RunAliasAnalysis = false; TxnsOnFields = false; NoNonlinearArith = false; + UseNumericOperators = false; } + public bool UseNumericOperators { get; set; } public bool NoNonlinearArith { get; set; } public bool TxnsOnFields { get; set; } From ff01efc4b027b8b26db92f4c5b4177297f9a0ea6 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Mon, 25 May 2020 11:08:28 -0700 Subject: [PATCH 16/49] Adding the adversarial model --- Sources/BoogieAST/BoogieAST.cs | 8 +++++ Sources/SolToBoogie/FallbackGenerator.cs | 41 +++++++++++++++++++++--- Sources/SolToBoogie/ParseUtils.cs | 2 +- Sources/SolToBoogie/TransUtils.cs | 8 ++++- Sources/SolToBoogie/TranslatorFlags.cs | 2 ++ 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/Sources/BoogieAST/BoogieAST.cs b/Sources/BoogieAST/BoogieAST.cs index f3f64687..0a239034 100644 --- a/Sources/BoogieAST/BoogieAST.cs +++ b/Sources/BoogieAST/BoogieAST.cs @@ -925,6 +925,14 @@ public override string ToString() public abstract class BoogieStructuredCmd : BoogieCmd { } + + public class BoogieWildcardExpr : BoogieExpr + { + public override string ToString() + { + return "*"; + } + } public class BoogieIfCmd : BoogieStructuredCmd { diff --git a/Sources/SolToBoogie/FallbackGenerator.cs b/Sources/SolToBoogie/FallbackGenerator.cs index 0bc31291..bbc4e854 100644 --- a/Sources/SolToBoogie/FallbackGenerator.cs +++ b/Sources/SolToBoogie/FallbackGenerator.cs @@ -58,7 +58,7 @@ public void Generate() BoogieStmtList fbBody = new BoogieStmtList(); var fbLocalVars = new List(); - if (context.TranslateFlags.ModelStubsAsSkips() || context.TranslateFlags.ModelStubsAsCallbacks()) + if (context.TranslateFlags.ModelStubsAsSkips() || context.TranslateFlags.ModelStubsAsCallbacks() || context.TranslateFlags.ModelStubsAsMultipleCallbacks()) { fbBody.AppendStmtList(CreateBodyOfUnknownFallback(fbLocalVars, inParams)); } @@ -112,10 +112,24 @@ private BoogieStmtList CreateBodyOfSend(List inParams, List locals) + { + BoogieStmtList stmtList = new BoogieStmtList(); + foreach (BoogieVariable localVar in locals) + { + string varName = localVar.TypedIdent.Name; + if (!varName.Equals("this")) + { + stmtList.AddStatement(new BoogieHavocCmd(new BoogieIdentifierExpr(varName))); + } + } + + return stmtList; + } private BoogieStmtList CreateBodyOfUnknownFallback(List fbLocalVars, List inParams) { - Debug.Assert(context.TranslateFlags.ModelStubsAsSkips() || context.TranslateFlags.ModelStubsAsCallbacks(), + Debug.Assert(context.TranslateFlags.ModelStubsAsSkips() || context.TranslateFlags.ModelStubsAsCallbacks() || context.TranslateFlags.ModelStubsAsMultipleCallbacks(), "CreateBodyOfUnknownFallback called in unexpected context"); var procBody = new BoogieStmtList(); @@ -131,13 +145,27 @@ private BoogieStmtList CreateBodyOfUnknownFallback(List fbLocalV procBody.AddStatement(new BoogieAssignCmd(balnThis, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, balnThis, msgVal))); procBody.AddStatement(new BoogieCommentCmd("---- Logic for payable function END ")); - if (context.TranslateFlags.ModelStubsAsCallbacks()) + BoogieStmtList body = procBody; + if (context.TranslateFlags.ModelStubsAsMultipleCallbacks()) + { + BoogieStmtList loopBody = new BoogieStmtList(); + procBody.AddStatement(new BoogieWhileCmd(new BoogieWildcardExpr(), loopBody, new List())); + body = loopBody; + } + + if (context.TranslateFlags.ModelStubsAsCallbacks() || context.TranslateFlags.ModelStubsAsMultipleCallbacks()) { - fbLocalVars.AddRange(TransUtils.CollectLocalVars(context.ContractDefinitions.ToList(), context)); + List localVars = TransUtils.CollectLocalVars(context.ContractDefinitions.ToList(), context); + fbLocalVars.AddRange(localVars); // if (*) fn1(from, *, ...) // we only redirect the calling contract, but the msg.sender need not be to, as it can call into anohter contract that calls // into from + if (context.TranslateFlags.ModelStubsAsMultipleCallbacks()) + { + body.AppendStmtList(HavocLocals(localVars)); + } + Tuple choices = TransUtils.GeneratePartialChoiceBlock(context.ContractDefinitions.ToList(), context, new BoogieIdentifierExpr("this"), 0, null, Tuple.Create(inParams[0].Name, inParams[1].Name)); BoogieIfCmd ifCmd = null; @@ -156,8 +184,11 @@ private BoogieStmtList CreateBodyOfUnknownFallback(List fbLocalV ifCmd = choices.Item1; } - procBody.AddStatement(ifCmd); + body.AddStatement(ifCmd); } + + + return procBody; } diff --git a/Sources/SolToBoogie/ParseUtils.cs b/Sources/SolToBoogie/ParseUtils.cs index 85e7ba7a..b97f5621 100644 --- a/Sources/SolToBoogie/ParseUtils.cs +++ b/Sources/SolToBoogie/ParseUtils.cs @@ -115,7 +115,7 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, { Debug.Assert(stubModels.Count() == 1, "Multiple instances of /stubModel:"); var model = stubModels.First().Substring("/stubModel:".Length); - Debug.Assert(model.Equals("skip") || model.Equals("havoc") || model.Equals("callback"), + Debug.Assert(model.Equals("skip") || model.Equals("havoc") || model.Equals("callback") || model.Equals("multipleCallbacks"), $"The argument to /stubModel: can be either {{skip, havoc, callback}}, found {model}"); translatorFlags.ModelOfStubs = model; } diff --git a/Sources/SolToBoogie/TransUtils.cs b/Sources/SolToBoogie/TransUtils.cs index 90190a20..d64aad95 100644 --- a/Sources/SolToBoogie/TransUtils.cs +++ b/Sources/SolToBoogie/TransUtils.cs @@ -853,7 +853,13 @@ public static Tuple GeneratePartialChoiceBlock(List Date: Mon, 25 May 2020 15:33:00 -0700 Subject: [PATCH 17/49] Adding non-linear mod --- Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs | 12 ++++++++++++ Sources/SolToBoogie/ProcedureTranslator.cs | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs index 93fb38bf..fb436113 100644 --- a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs +++ b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs @@ -60,6 +60,7 @@ private void GenerateFunctions() context.Program.AddDeclaration(generateNonlinearMulFunction()); context.Program.AddDeclaration(generateNonlinearDivFunction()); context.Program.AddDeclaration(generateNonlinearPowFunction()); + context.Program.AddDeclaration(generateNonlinearModFunction()); } } @@ -272,6 +273,17 @@ private BoogieFunction generateNonlinearPowFunction() return new BoogieFunction(fnName, new List() {inVar1, inVar2}, new List() {outVar}); } + + private BoogieFunction generateNonlinearModFunction() + { + string fnName = "nonlinearMod"; + var inVar1 = new BoogieFormalParam(new BoogieTypedIdent("x", BoogieType.Int)); + var inVar2 = new BoogieFormalParam(new BoogieTypedIdent("y", BoogieType.Int)); + var outVar = new BoogieFormalParam(new BoogieTypedIdent("ret", BoogieType.Int)); + + return new BoogieFunction(fnName, new List() {inVar1, inVar2}, + new List() {outVar}); + } private void GenerateTypes() diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index f871b274..70a0b9e2 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -3804,6 +3804,18 @@ public override bool Visit(BinaryOperation node) binaryExpr = new BoogieFuncCallExpr("nonlinearDiv", new List() { leftExpr, rightExpr}); } } + else if (context.TranslateFlags.NoNonlinearArith && node.Operator == "%") + { + if ((node.LeftExpression is Literal leftLiteral && leftLiteral.Kind.Equals("number")) || + (node.RightExpression is Literal rightLiteral && rightLiteral.Kind.Equals("number"))) + { + binaryExpr = new BoogieBinaryOperation(op, leftExpr, rightExpr); + } + else + { + binaryExpr = new BoogieFuncCallExpr("nonlinearMod", new List() { leftExpr, rightExpr}); + } + } else { binaryExpr = new BoogieBinaryOperation(op, leftExpr, rightExpr); From 658ae07c7439d336abde8067af6ae0b4403634e5 Mon Sep 17 00:00:00 2001 From: kostas Date: Mon, 25 May 2020 17:40:14 -0500 Subject: [PATCH 18/49] Treat block.number as now (temporarily). --- Sources/SolToBoogie/ProcedureTranslator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index f871b274..3530d966 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -2361,7 +2361,7 @@ public override bool Visit(MemberAccess node) } else if (identifier.Name.Equals("block")) { - if (node.MemberName.Equals("timestamp")) + if (node.MemberName.Equals("timestamp") || node.MemberName.Equals("number")) currentExpr = new BoogieIdentifierExpr("now"); else //we will havoc the value From 4e284bf85f8b34ea451df6e318260f57f1521981 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Tue, 26 May 2020 19:23:15 -0700 Subject: [PATCH 19/49] Adding constraints to variables so they are within the range of their type if modularArithmetic flag is on. Fixed bug with casting down in Modular arithmetic and found bug in VeriSol where it will translate a += foo(b) incorrectly. Added an assertion that will fill if the translation will be incorrect --- Sources/SolToBoogie/ProcedureTranslator.cs | 7 +++-- Sources/SolToBoogie/TransUtils.cs | 31 +++++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 70a0b9e2..ccebda90 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -1639,7 +1639,10 @@ public override bool Visit(Assignment node) if (!isTupleAssignment) { if (usedTmpVar || !(lhs[0] is BoogieIdentifierExpr)) //bad bug: was && before!! - currentStmtList.AddStatement(new BoogieAssignCmd(lhs[0], tmpVars[0])); + { + VeriSolAssert(node.Operator.Equals("="), "I don't know why this is only doing assignment"); + currentStmtList.AddStatement(new BoogieAssignCmd(lhs[0], tmpVars[0])); + } } else { for (int i = 0; i < lhs.Count; ++i) @@ -3617,7 +3620,7 @@ private void TranslateTypeCast(FunctionCall node, BoogieExpr lhs, out bool isEle } else if (context.TranslateFlags.UseModularArithmetic && context.TranslateFlags.UseNumericOperators) { - if (node.Arguments[0].TypeDescriptions.IsUintWSize(node.Arguments[0], out uint uintSize)) + if (node.TypeDescriptions.IsUintWSize(node, out uint uintSize)) { BigInteger maxUIntValue = (BigInteger)Math.Pow(2, uintSize); rhsExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.MOD, rhsExpr, new BoogieLiteralExpr(maxUIntValue)); diff --git a/Sources/SolToBoogie/TransUtils.cs b/Sources/SolToBoogie/TransUtils.cs index d64aad95..a12650ff 100644 --- a/Sources/SolToBoogie/TransUtils.cs +++ b/Sources/SolToBoogie/TransUtils.cs @@ -828,7 +828,9 @@ public static Tuple GeneratePartialChoiceBlock(List GeneratePartialChoiceBlock(List(), new List() { new BoogieIdentifierExpr(name) })); } + thenBody.AppendStmtList(constrainVarValues(context, param, boogieVar)); } List outputs = new List(); @@ -889,6 +892,32 @@ public static Tuple GeneratePartialChoiceBlock(List(ifCmd, j); } + + public static BoogieStmtList constrainVarValues(TranslatorContext context, VariableDeclaration var, BoogieIdentifierExpr boogieVar) + { + BoogieStmtList stmts = new BoogieStmtList(); + + if (context.TranslateFlags.UseModularArithmetic) + { + if (var.TypeDescriptions.IsUintWSize(null, out uint uintSize)) + { + BigInteger maxUIntValue = (BigInteger)Math.Pow(2, uintSize); + BoogieBinaryOperation lower = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, boogieVar, new BoogieLiteralExpr(BigInteger.Zero)); + BoogieBinaryOperation upper = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.LT, boogieVar, new BoogieLiteralExpr(maxUIntValue)); + stmts.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.AND, lower, upper))); + } + else if (var.TypeDescriptions.IsIntWSize(out uint intSize)) + { + BigInteger maxIntValue = (BigInteger)Math.Pow(2, intSize); + BoogieBinaryOperation lower = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, boogieVar, new BoogieLiteralExpr(-maxIntValue)); + BoogieBinaryOperation upper = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.LT, boogieVar, new BoogieLiteralExpr(maxIntValue)); + stmts.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.AND, lower, upper))); + } + } + + return stmts; + } + public static void havocGas(BoogieStmtList list) { List cmdList = new List(); From 8e614282a9dc76ea0f253d6ed3b99a62fbc50a62 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Wed, 27 May 2020 11:53:20 -0700 Subject: [PATCH 20/49] Fixing callback and multipleCallbacks fallback function. For callback, we should check the type of the contract we are making the call to, otherwise we we have a lot of repetition and can make illegal calls. For multipleCallback I'm changing the while loop from while(*) to while(nondet && gas >= 21000) --- Sources/SolToBoogie/FallbackGenerator.cs | 52 +++++++++++++++++++++--- Sources/SolToBoogie/ParseUtils.cs | 2 +- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/Sources/SolToBoogie/FallbackGenerator.cs b/Sources/SolToBoogie/FallbackGenerator.cs index bbc4e854..b5077fd4 100644 --- a/Sources/SolToBoogie/FallbackGenerator.cs +++ b/Sources/SolToBoogie/FallbackGenerator.cs @@ -146,10 +146,34 @@ private BoogieStmtList CreateBodyOfUnknownFallback(List fbLocalV procBody.AddStatement(new BoogieCommentCmd("---- Logic for payable function END ")); BoogieStmtList body = procBody; + if (context.TranslateFlags.ModelStubsAsCallbacks() || + context.TranslateFlags.ModelStubsAsMultipleCallbacks()) + { + if (context.TranslateFlags.ModelReverts) + { + BoogieBinaryOperation revertGuard = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, new BoogieIdentifierExpr("choice"), new BoogieLiteralExpr(BigInteger.Zero)); + BoogieStmtList thenBody = new BoogieStmtList(); + thenBody.AddStatement(new BoogieAssignCmd(new BoogieIdentifierExpr("revert"), new BoogieLiteralExpr(true))); + thenBody.AddStatement(new BoogieReturnCmd()); + BoogieIfCmd earlyExitCmd = new BoogieIfCmd(revertGuard, thenBody, null); + body.AddStatement(earlyExitCmd); + } + if (context.TranslateFlags.InstrumentGas) + { + BoogieBinaryOperation gasGuard = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.LT, new BoogieIdentifierExpr("gas"), new BoogieLiteralExpr(TranslatorContext.TX_GAS_COST)); + BoogieIfCmd earlyExitCmd = new BoogieIfCmd(gasGuard, BoogieStmtList.MakeSingletonStmtList(new BoogieReturnCmd()), null); + body.AddStatement(earlyExitCmd); + } + + } + if (context.TranslateFlags.ModelStubsAsMultipleCallbacks()) { + fbLocalVars.Add(new BoogieLocalVariable(new BoogieTypedIdent("iterate", BoogieType.Bool))); + BoogieBinaryOperation gasGuard = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, new BoogieIdentifierExpr("gas"), new BoogieLiteralExpr(TranslatorContext.TX_GAS_COST)); + BoogieBinaryOperation guard = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.AND, new BoogieIdentifierExpr("iterate"), gasGuard); BoogieStmtList loopBody = new BoogieStmtList(); - procBody.AddStatement(new BoogieWhileCmd(new BoogieWildcardExpr(), loopBody, new List())); + procBody.AddStatement(new BoogieWhileCmd(guard, loopBody, new List())); body = loopBody; } @@ -164,11 +188,27 @@ private BoogieStmtList CreateBodyOfUnknownFallback(List fbLocalV if (context.TranslateFlags.ModelStubsAsMultipleCallbacks()) { body.AppendStmtList(HavocLocals(localVars)); + body.AddStatement(new BoogieHavocCmd(new BoogieIdentifierExpr("iterate"))); } - Tuple choices = TransUtils.GeneratePartialChoiceBlock(context.ContractDefinitions.ToList(), context, - new BoogieIdentifierExpr("this"), 0, null, Tuple.Create(inParams[0].Name, inParams[1].Name)); - BoogieIfCmd ifCmd = null; + + BoogieIfCmd typeIf = null; + foreach (ContractDefinition curDef in context.ContractDefinitions.ToList()) + { + BoogieIfCmd reentrantCalls = TransUtils.GenerateChoiceBlock(new List() {curDef}, + context, Tuple.Create(inParams[0].Name, inParams[1].Name)); + + if (reentrantCalls == null) + { + continue; + } + + BoogieExpr dtype = new BoogieMapSelect(new BoogieIdentifierExpr("DType"), new BoogieIdentifierExpr(inParams[0].Name)); + BoogieBinaryOperation guard = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, dtype, new BoogieIdentifierExpr(curDef.Name)); + typeIf = new BoogieIfCmd(guard, BoogieStmtList.MakeSingletonStmtList(reentrantCalls), typeIf == null ? null : BoogieStmtList.MakeSingletonStmtList(typeIf)); + } + + /*BoogieIfCmd ifCmd = null; if (context.TranslateFlags.ModelReverts) { @@ -182,9 +222,9 @@ private BoogieStmtList CreateBodyOfUnknownFallback(List fbLocalV else { ifCmd = choices.Item1; - } + }*/ - body.AddStatement(ifCmd); + body.AddStatement(typeIf); } diff --git a/Sources/SolToBoogie/ParseUtils.cs b/Sources/SolToBoogie/ParseUtils.cs index b97f5621..f52d9982 100644 --- a/Sources/SolToBoogie/ParseUtils.cs +++ b/Sources/SolToBoogie/ParseUtils.cs @@ -116,7 +116,7 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, Debug.Assert(stubModels.Count() == 1, "Multiple instances of /stubModel:"); var model = stubModels.First().Substring("/stubModel:".Length); Debug.Assert(model.Equals("skip") || model.Equals("havoc") || model.Equals("callback") || model.Equals("multipleCallbacks"), - $"The argument to /stubModel: can be either {{skip, havoc, callback}}, found {model}"); + $"The argument to /stubModel: can be either {{skip, havoc, callback, multipleCallbacks}}, found {model}"); translatorFlags.ModelOfStubs = model; } if (args.Any(x => x.StartsWith("/inlineDepth:"))) From e845ce97c3d0944db95cd04bd014f6167c16c0ac Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Mon, 31 Aug 2020 15:59:38 -0700 Subject: [PATCH 21/49] Adding a lazy allocation scheme that doesn't modify the memory structures on an access. It passes all regressions as long as ERC20 recursionDepth=4. I also think it may need a bit more work on nested structure assignment, but we should be able to cross that bridge later. --- .../SolToBoogie/GhostVarAndAxiomGenerator.cs | 44 ++- Sources/SolToBoogie/MapArrayHelper.cs | 21 ++ Sources/SolToBoogie/ParseUtils.cs | 7 + Sources/SolToBoogie/ProcedureTranslator.cs | 252 +++++++++++++++++- Sources/SolToBoogie/TranslatorFlags.cs | 7 +- Sources/SolidityAST/SolidityAST.cs | 2 +- 6 files changed, 315 insertions(+), 18 deletions(-) diff --git a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs index fb436113..73b638f4 100644 --- a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs +++ b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs @@ -709,7 +709,7 @@ private void GenerateMemoryVariables() { Debug.Assert(varDecl.TypeName is Mapping); Mapping mapping = varDecl.TypeName as Mapping; - GenerateMemoryVariablesForMapping(varDecl, mapping, generatedMaps); + GenerateMemoryVariablesForMapping(varDecl, mapping, generatedMaps, 0); if (context.TranslateFlags.InstrumentSums) { @@ -730,7 +730,7 @@ private void GenerateMemoryVariables() { Debug.Assert(varDecl.TypeName is ArrayTypeName); ArrayTypeName array = varDecl.TypeName as ArrayTypeName; - GenerateMemoryVariablesForArray(varDecl, array, generatedMaps); + GenerateMemoryVariablesForArray(varDecl, array, generatedMaps, 0); if (context.TranslateFlags.InstrumentSums) { @@ -746,19 +746,32 @@ private void GenerateMemoryVariables() } } - private void GenerateMemoryVariablesForMapping(VariableDeclaration decl, Mapping mapping, HashSet generatedMaps) + private void GenerateMemoryVariablesForMapping(VariableDeclaration decl, Mapping mapping, HashSet generatedMaps, int lvl) { BoogieType boogieKeyType = TransUtils.GetBoogieTypeFromSolidityTypeName(mapping.KeyType); BoogieType boogieValType = null; if (mapping.ValueType is Mapping submapping) { boogieValType = BoogieType.Ref; - GenerateMemoryVariablesForMapping(decl, submapping, generatedMaps); + GenerateMemoryVariablesForMapping(decl, submapping, generatedMaps, lvl + 1); + + // The last level gets initialized all at once + if (context.TranslateFlags.LazyAllocNoMod) + { + BoogieMapType mapType = new BoogieMapType(BoogieType.Ref, new BoogieMapType(boogieKeyType, BoogieType.Bool)); + context.Program.AddDeclaration(new BoogieGlobalVariable(new BoogieTypedIdent(mapHelper.GetNestedAllocName(decl, lvl), mapType))); + } } else if (mapping.ValueType is ArrayTypeName array) { boogieValType = BoogieType.Ref; - GenerateMemoryVariablesForArray(decl, array, generatedMaps); + GenerateMemoryVariablesForArray(decl, array, generatedMaps, lvl + 1); + + if (context.TranslateFlags.LazyAllocNoMod) + { + BoogieMapType mapType = new BoogieMapType(BoogieType.Ref, new BoogieMapType(boogieKeyType, BoogieType.Bool)); + context.Program.AddDeclaration(new BoogieGlobalVariable(new BoogieTypedIdent(mapHelper.GetNestedAllocName(decl, lvl), mapType))); + } } else { @@ -770,24 +783,39 @@ private void GenerateMemoryVariablesForMapping(VariableDeclaration decl, Mapping GenerateSingleMemoryVariable(decl, boogieKeyType, boogieValType, generatedMaps); } - private void GenerateMemoryVariablesForArray(VariableDeclaration decl, ArrayTypeName array, HashSet generatedMaps) + private void GenerateMemoryVariablesForArray(VariableDeclaration decl, ArrayTypeName array, HashSet generatedMaps, int lvl) { BoogieType boogieKeyType = BoogieType.Int; BoogieType boogieValType = null; if (array.BaseType is ArrayTypeName subarray) { boogieValType = BoogieType.Ref; - GenerateMemoryVariablesForArray(decl, subarray, generatedMaps); + GenerateMemoryVariablesForArray(decl, subarray, generatedMaps, lvl + 1); + + // The last level gets initialized all at once + if (context.TranslateFlags.LazyAllocNoMod) + { + BoogieMapType mapType = new BoogieMapType(BoogieType.Ref, new BoogieMapType(boogieKeyType, BoogieType.Bool)); + context.Program.AddDeclaration(new BoogieGlobalVariable(new BoogieTypedIdent(mapHelper.GetNestedAllocName(decl, lvl), mapType))); + } } else if (array.BaseType is Mapping mapping) { boogieValType = BoogieType.Ref; - GenerateMemoryVariablesForMapping(decl, mapping, generatedMaps); + GenerateMemoryVariablesForMapping(decl, mapping, generatedMaps, lvl + 1); + + if (context.TranslateFlags.LazyAllocNoMod) + { + BoogieMapType mapType = new BoogieMapType(BoogieType.Ref, new BoogieMapType(boogieKeyType, BoogieType.Bool)); + context.Program.AddDeclaration(new BoogieGlobalVariable(new BoogieTypedIdent(mapHelper.GetNestedAllocName(decl, lvl), mapType))); + } } else { boogieValType = TransUtils.GetBoogieTypeFromSolidityTypeName(array.BaseType); } + + KeyValuePair pair = new KeyValuePair(boogieKeyType, boogieValType); GenerateSingleMemoryVariable(decl, boogieKeyType, boogieValType, generatedMaps); diff --git a/Sources/SolToBoogie/MapArrayHelper.cs b/Sources/SolToBoogie/MapArrayHelper.cs index 5e1714c3..ac0bb2de 100644 --- a/Sources/SolToBoogie/MapArrayHelper.cs +++ b/Sources/SolToBoogie/MapArrayHelper.cs @@ -206,6 +206,22 @@ public static string GetValueTypeString(string typeString) return null; } + + public static string GetIndexTypeString(string typeString) + { + if (mappingRegex.IsMatch(typeString)) + { + Match match = mappingRegex.Match(typeString); + return match.Groups[1].Value; + } + + if (arrayRegex.IsMatch(typeString)) + { + return "uint256"; + } + + return null; + } public static bool IsMappingTypeString(string typeString) { @@ -216,5 +232,10 @@ public static bool IsArrayTypeString(string typeString) { return arrayRegex.IsMatch(typeString); } + + public string GetNestedAllocName(VariableDeclaration decl, int lvl) + { + return "alloc_" + decl.Name + "_lvl" + lvl; + } } } diff --git a/Sources/SolToBoogie/ParseUtils.cs b/Sources/SolToBoogie/ParseUtils.cs index f52d9982..7c9ad55e 100644 --- a/Sources/SolToBoogie/ParseUtils.cs +++ b/Sources/SolToBoogie/ParseUtils.cs @@ -139,6 +139,13 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, translatorFlags.LazyNestedAlloc = true; } + if (args.Any(x => x.Equals("/LazyAllocNoMod"))) + { + translatorFlags.LazyNestedAlloc = true; + translatorFlags.LazyAllocNoMod = true; + translatorFlags.QuantFreeAllocs = true; + } + if (args.Any(x => x.Equals("/OmitAssumeFalseForDynDispatch"))) { translatorFlags.OmitAssumeFalseForDynDispatch = true; diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 86041600..fc13bd51 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -967,12 +967,81 @@ private void InitializeNestedArrayMappingStateVar(VariableDeclaration varDecl, M } else { - if (context.TranslateFlags.QuantFreeAllocs) + BoogieExpr index = new BoogieMapSelect(varExpr, new BoogieIdentifierExpr("this")); + TypeName curType = varDecl.TypeName; + + if (context.TranslateFlags.LazyAllocNoMod) { - //currentStmtList.AddStatement(new BoogieAssignCmd(lhs0, GetCallExprForZeroInit(mapKeyType, BoogieType.Ref))); - BoogieExpr index = new BoogieMapSelect(varExpr, new BoogieIdentifierExpr("this")); + BoogieType keyType = null; + BoogieType valType = null; + if (curType is Mapping map) + { + keyType = TransUtils.GetBoogieTypeFromSolidityTypeName(map.KeyType); + valType = TransUtils.GetBoogieTypeFromSolidityTypeName(map.ValueType); + curType = map.ValueType; + } + else if (curType is ArrayTypeName arr) + { + keyType = BoogieType.Int; + valType = TransUtils.GetBoogieTypeFromSolidityTypeName(arr.BaseType); + curType = arr.BaseType; + } - TypeName curType = varDecl.TypeName; + VeriSolAssert(curType is Mapping || curType is ArrayTypeName, "Expected a nested structure"); + /*if (!(curType is Mapping || curType is ArrayTypeName)) + { + string memName = mapHelper.GetMemoryMapName(varDecl, keyType, valType); + BoogieMapSelect lhs = new BoogieMapSelect(new BoogieIdentifierExpr(memName), index); + if (context.TranslateFlags.QuantFreeAllocs) + { + currentStmtList.AddStatement( + (new BoogieAssignCmd(lhs, GetCallExprForZeroInit(keyType, valType)))); + } + else + { + + } + + }*/ + + int lvl = 0; + while (curType is Mapping || curType is ArrayTypeName) + { + string allocName = mapHelper.GetNestedAllocName(varDecl, lvl); + BoogieMapSelect lhs = new BoogieMapSelect(new BoogieIdentifierExpr(allocName), index); + if (context.TranslateFlags.QuantFreeAllocs) + { + currentStmtList.AddStatement((new BoogieAssignCmd(lhs, + GetCallExprForZeroInit(keyType, BoogieType.Bool)))); + } + else + { + var negAllocExpr = new BoogieUnaryOperation(BoogieUnaryOperation.Opcode.NOT, new BoogieMapSelect(lhs, qVar1)); + var negAllocQExpr = new BoogieQuantifiedExpr(true, new List() {qVar1}, + new List() {mapKeyType}, negAllocExpr); + //assume forall i !Alloc[M_t_ref[x[this]][i]] + currentStmtList.AddStatement(new BoogieAssumeCmd(negAllocQExpr)); + } + + if (curType is Mapping m) + { + keyType = TransUtils.GetBoogieTypeFromSolidityTypeName(m.KeyType); + valType = TransUtils.GetBoogieTypeFromSolidityTypeName(m.ValueType); + curType = m.ValueType; + } + else if (curType is ArrayTypeName arr) + { + keyType = BoogieType.Int; + valType = TransUtils.GetBoogieTypeFromSolidityTypeName(arr.BaseType); + curType = arr.BaseType; + } + + lvl++; + } + } + else if (context.TranslateFlags.QuantFreeAllocs) + { + //currentStmtList.AddStatement(new BoogieAssignCmd(lhs0, GetCallExprForZeroInit(mapKeyType, BoogieType.Ref))); while (curType is Mapping || curType is ArrayTypeName) { @@ -990,10 +1059,11 @@ private void InitializeNestedArrayMappingStateVar(VariableDeclaration varDecl, M valType = TransUtils.GetBoogieTypeFromSolidityTypeName(arr.BaseType); curType = arr.BaseType; } - + string memName = mapHelper.GetMemoryMapName(varDecl, keyType, valType); BoogieMapSelect lhs = new BoogieMapSelect(new BoogieIdentifierExpr(memName), index); - currentStmtList.AddStatement((new BoogieAssignCmd(lhs, GetCallExprForZeroInit(keyType, valType)))); + currentStmtList.AddStatement((new BoogieAssignCmd(lhs, + GetCallExprForZeroInit(keyType, valType)))); if (valType.Equals(BoogieType.Int)) { index = new BoogieLiteralExpr(BigInteger.Zero); @@ -3876,6 +3946,101 @@ public override bool Visit(Conditional node) return false; } + public int GetMaxLvl(VariableDeclaration decl) + { + int lvl = -1; + TypeName curType = decl.TypeName; + while (curType is Mapping || curType is ArrayTypeName) + { + if (curType is Mapping mapping) + { + curType = mapping.ValueType; + } + else if (curType is ArrayTypeName arr) + { + curType = arr.BaseType; + } + + lvl++; + } + + return lvl; + } + + public int GetIndexLvl(VariableDeclaration decl, IndexAccess node) + { + int maxLvl = GetMaxLvl(decl); + string typeStr = node.TypeDescriptions.TypeString; + + int lvl = 0; + while (MapArrayHelper.IsMappingTypeString(typeStr) || MapArrayHelper.IsArrayTypeString(typeStr)) + { + typeStr = MapArrayHelper.GetValueTypeString(typeStr); + lvl++; + } + + return maxLvl - lvl; + } + + /*public int GetIndexLvl(VariableDeclaration decl, IndexAccess node) + { + Expression val = decl.Value; + string typeStr = node.TypeDescriptions.TypeString; + string valTypeStr = MapArrayHelper.GetValueTypeString(typeStr); + string indTypeStr = MapArrayHelper.GetIndexTypeString(typeStr); + + int lvl = 0; + TypeName curType = decl.TypeName; + while (curType is Mapping || curType is ArrayTypeName) + { + if (curType is Mapping mapping) + { + curType = mapping.ValueType; + } + else if (curType is ArrayTypeName arr) + { + curType = arr.BaseType; + } + + if (curType is Mapping map && MapArrayHelper.IsMappingTypeString(typeStr) && valTypeStr.Equals(map.ValueType.ToString()) && indTypeStr.Equals(map.KeyType.ToString())) + { + return lvl; + } + else if (curType is ArrayTypeName arr && MapArrayHelper.IsArrayTypeString(typeStr) && valTypeStr.Equals(arr.BaseType.ToString())) + { + return lvl; + } + else if (curType.ToString().Equals(typeStr)) + { + return lvl; + } + + lvl++; + } + + VeriSolAssert(false, "Could not determine access index"); + return -1; + }*/ + + public BoogieExpr GetDefaultVal(BoogieType boogieType) + { + if (boogieType.Equals(BoogieType.Int)) + { + return new BoogieLiteralExpr(BigInteger.Zero); + } + else if (boogieType.Equals(BoogieType.Bool)) + { + return new BoogieLiteralExpr(false); + } + else if (boogieType.Equals(BoogieType.Ref)) + { + return new BoogieIdentifierExpr("null"); + } + + VeriSolAssert(false, "Unknown solidity type"); + return null; + } + public override bool Visit(IndexAccess node) { preTranslationAction(node); @@ -3908,7 +4073,80 @@ public override bool Visit(IndexAccess node) VariableDeclaration decl = mapHelper.getDecl(node); currentExpr = mapHelper.GetMemoryMapSelectExpr(decl, baseKeyType, baseValType, baseExpr, indexExpr); - if (context.TranslateFlags.LazyNestedAlloc) + if (context.TranslateFlags.LazyAllocNoMod) + { + var valTypeString = MapArrayHelper.GetValueTypeString(baseExpression.TypeDescriptions.TypeString); + var valIsMapping = MapArrayHelper.IsMappingTypeString(valTypeString); + var valIsArray = MapArrayHelper.IsArrayTypeString(valTypeString); + + if (valIsArray || valIsMapping) + { + BoogieStmtList allocAndInit = new BoogieStmtList(); + var tmpVarIdentExpr = MkNewLocalVariableWithType(baseValType); + allocAndInit.AddStatement(new BoogieCallCmd("FreshRefGenerator", + new List(), + new List() {tmpVarIdentExpr} + )); + + if (valIsArray) + { + allocAndInit.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, new BoogieMapSelect(new BoogieIdentifierExpr("Length"), currentExpr), new BoogieLiteralExpr(0)))); + } + + //allocAndInit.AddStatement(new BoogieAssignCmd(currentExpr, tmpVarIdentExpr)); + int lvl = GetIndexLvl(decl, node); + + string nestedVal = MapArrayHelper.GetValueTypeString(valTypeString); + bool isNestedStructure = MapArrayHelper.IsMappingTypeString(nestedVal) || + MapArrayHelper.IsArrayTypeString(nestedVal); + string allocName = mapHelper.GetNestedAllocName(decl, lvl); + + //var mapName = new BoogieIdentifierExpr(mapHelper.GetMemoryMapName(decl, nestedKeyType, nestedValType)); + //var derefCurrExpr = new BoogieMapSelect(mapName, currentExpr); + + var allocMap = new BoogieIdentifierExpr(allocName); + var allocExpr = new BoogieMapSelect(new BoogieMapSelect(allocMap, baseExpr), indexExpr); + allocAndInit.AddStatement(new BoogieAssignCmd(allocExpr, new BoogieLiteralExpr(true))); + + if (!isNestedStructure) + { + BoogieType nestedValType = MapArrayHelper.InferValueTypeFromTypeString(valTypeString); + BoogieType nestedKeyType = MapArrayHelper.InferKeyTypeFromTypeString(valTypeString); + + var mapName = new BoogieIdentifierExpr(mapHelper.GetMemoryMapName(decl, nestedKeyType, nestedValType)); + var derefCurrExpr = new BoogieMapSelect(mapName, currentExpr); + + //allocate struct + if (context.TranslateFlags.QuantFreeAllocs) + { + BoogieFuncCallExpr zeroInit = GetCallExprForZeroInit(nestedKeyType, nestedValType); + allocAndInit.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, derefCurrExpr, zeroInit))); + } + else + { + var qVar = QVarGenerator.NewQVar(0, 0); + BoogieExpr defaultVal = GetDefaultVal(nestedValType); + var bodyExpr = new BoogieBinaryOperation( + BoogieBinaryOperation.Opcode.EQ, + new BoogieMapSelect(derefCurrExpr, qVar), + defaultVal); + var qExpr = new BoogieQuantifiedExpr(true, new List() {qVar}, + new List() {nestedKeyType}, bodyExpr); + allocAndInit.AddStatement(new BoogieAssumeCmd(qExpr)); + } + } + + allocAndInit.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, currentExpr, tmpVarIdentExpr))); + + if (context.TranslateFlags.InstrumentSums) + { + allocAndInit.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, mapHelper.GetSumExpr(decl, currentExpr), new BoogieLiteralExpr(BigInteger.Zero)))); + } + + currentStmtList.AddStatement(new BoogieIfCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, allocExpr, new BoogieLiteralExpr(false)), allocAndInit, null)); + } + } + else if (context.TranslateFlags.LazyNestedAlloc) { var valTypeString = MapArrayHelper.GetValueTypeString(baseExpression.TypeDescriptions.TypeString); var valIsMapping = MapArrayHelper.IsMappingTypeString(valTypeString); diff --git a/Sources/SolToBoogie/TranslatorFlags.cs b/Sources/SolToBoogie/TranslatorFlags.cs index 88930b5d..2df6516d 100644 --- a/Sources/SolToBoogie/TranslatorFlags.cs +++ b/Sources/SolToBoogie/TranslatorFlags.cs @@ -25,8 +25,6 @@ public TranslatorFlags() PerformContractInferce = false; DoModSetAnalysis = false; RemoveScopeInVarName = false; - QuantFreeAllocs = false; - LazyNestedAlloc = false; InstrumentSums = false; NoBoogieHarness = false; CreateMainHarness = false; @@ -37,6 +35,9 @@ public TranslatorFlags() TxnsOnFields = false; NoNonlinearArith = false; UseNumericOperators = false; + LazyAllocNoMod = false; + QuantFreeAllocs = false; + LazyNestedAlloc = false; } public bool UseNumericOperators { get; set; } @@ -100,6 +101,8 @@ public TranslatorFlags() /// Allocate nested maps lazily, i.e., eliminate HavocAllocMany /// public bool LazyNestedAlloc { get; set; } + + public bool LazyAllocNoMod { get; set; } /// /// Remove assume false for the default branch of dyn dispatch diff --git a/Sources/SolidityAST/SolidityAST.cs b/Sources/SolidityAST/SolidityAST.cs index 4666e9bf..ae892ee0 100644 --- a/Sources/SolidityAST/SolidityAST.cs +++ b/Sources/SolidityAST/SolidityAST.cs @@ -881,7 +881,7 @@ public override T Accept(IASTGenericVisitor visitor) public override string ToString() { StringBuilder builder = new StringBuilder(); - builder.Append("mapping (").Append(KeyType).Append(" => ").Append(ValueType).Append(")"); + builder.Append("mapping(").Append(KeyType).Append(" => ").Append(ValueType).Append(")"); return builder.ToString(); } } From 28b4ec353a8793675f8b109443f7600e46ef680b Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Mon, 7 Sep 2020 16:29:45 -0700 Subject: [PATCH 22/49] Adding a mode to model arrays/mappings as multi-dimension boogie maps rather than a split map if not aliased. All regressions pass in this mode, but there is still some work that needs to be done for the qantFreeAlloc mode. Right now only zero functions are generated but for statically sized arrays, other length initializers might be necessary. Will add this in later. --- .../SolToBoogie/GhostVarAndAxiomGenerator.cs | 29 +- Sources/SolToBoogie/MapArrayHelper.cs | 220 +++++++++++- Sources/SolToBoogie/ParseUtils.cs | 6 + Sources/SolToBoogie/ProcedureTranslator.cs | 323 ++++++++++++++++-- Sources/SolToBoogie/TransUtils.cs | 60 ++++ Sources/SolToBoogie/TranslatorFlags.cs | 3 + 6 files changed, 600 insertions(+), 41 deletions(-) diff --git a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs index 73b638f4..364baeae 100644 --- a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs +++ b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs @@ -47,12 +47,29 @@ private void GenerateFunctions() if (context.TranslateFlags.QuantFreeAllocs) { - context.Program.AddDeclaration(GenerateZeroRefIntArrayFunction()); - context.Program.AddDeclaration(GenerateZeroIntIntArrayFunction()); - context.Program.AddDeclaration(GenerateZeroRefBoolArrayFunction()); - context.Program.AddDeclaration(GenerateZeroIntBoolArrayFunction()); - context.Program.AddDeclaration(GenerateZeroRefRefArrayFunction()); - context.Program.AddDeclaration(GenerateZeroIntRefArrayFunction()); + if (context.TranslateFlags.UseMultiDim) + { + foreach(VariableDeclaration decl in context.Analysis.Alias.getResults()) + { + TypeName type = decl.TypeName; + if (type is Mapping || type is ArrayTypeName) + { + context.Program.AddDeclaration(MapArrayHelper.GenerateMultiDimZeroFunction(decl)); + } + } + } + else + { + context.Program.AddDeclaration(GenerateZeroRefIntArrayFunction()); + context.Program.AddDeclaration(GenerateZeroIntIntArrayFunction()); + context.Program.AddDeclaration(GenerateZeroRefBoolArrayFunction()); + context.Program.AddDeclaration(GenerateZeroIntBoolArrayFunction()); + context.Program.AddDeclaration(GenerateZeroRefRefArrayFunction()); + context.Program.AddDeclaration(GenerateZeroIntRefArrayFunction()); + } + + + } if (context.TranslateFlags.NoNonlinearArith) diff --git a/Sources/SolToBoogie/MapArrayHelper.cs b/Sources/SolToBoogie/MapArrayHelper.cs index ac0bb2de..e9ffb88d 100644 --- a/Sources/SolToBoogie/MapArrayHelper.cs +++ b/Sources/SolToBoogie/MapArrayHelper.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System.Collections.Generic; using solidityAnalysis; using SolidityAST; @@ -14,7 +15,7 @@ public class MapArrayHelper { private static Regex mappingRegex = new Regex(@"mapping\((\w+)\s*\w*\s*=>\s*(.+)\)$"); //mapping(string => uint) appears as mapping(string memory => uint) - private static Regex arrayRegex = new Regex(@"(.+)\[\w*\] (storage ref|storage pointer|memory|calldata)$"); + private static Regex arrayRegex = new Regex(@"(.+)\[\w*\]( storage ref| storage pointer| memory| calldata)?$"); // mapping (uint => uint[]) does not have storage/memory in Typestring // private static Regex arrayRegex = new Regex(@"(.+)\[\w*\]$"); @@ -46,7 +47,7 @@ public VariableDeclaration getDecl(Expression access) public string GetMemoryMapName(VariableDeclaration decl, BoogieType keyType, BoogieType valType) { - if (notAliased(decl)) + if (notAliased(decl) && !context.TranslateFlags.UseMultiDim) { return GetCanonicalMemName(keyType, valType) + "_" + context.Analysis.Alias.getGroupName(decl); } @@ -237,5 +238,220 @@ public string GetNestedAllocName(VariableDeclaration decl, int lvl) { return "alloc_" + decl.Name + "_lvl" + lvl; } + + public static BoogieFuncCallExpr GetCallExprForZeroInit(BoogieType key, BoogieType value) + { + var keyStr = char.ToUpper(key.ToString()[0]) + key.ToString().Substring(1); + var valStr = char.ToUpper(value.ToString()[0]) + value.ToString().Substring(1); + return new BoogieFuncCallExpr("zero" + keyStr + valStr + "Arr", new List()); + } + + public static string GetSmtType(BoogieType type) + { + if (type.Equals(BoogieType.Bool)) + { + return "Bool"; + } + else if (type.Equals(BoogieType.Int)) + { + return "Int"; + } + else if (type.Equals(BoogieType.Ref)) + { + return "Int"; + } + + throw new Exception($"Unknown BoogieType {type}"); + } + + public static BoogieFunction GenerateMultiDimZeroFunction(VariableDeclaration decl) + { + TypeName curType = decl.TypeName; + List boogieType = new List(); + + while (curType is Mapping || curType is ArrayTypeName) + { + if (curType is Mapping map) + { + boogieType.Insert(0, TransUtils.GetBoogieTypeFromSolidityTypeName(map.KeyType)); + curType = map.ValueType; + } + else if(curType is ArrayTypeName arr) + { + boogieType.Insert(0, BoogieType.Int); + curType = arr.BaseType; + } + } + + BoogieType valType = TransUtils.GetBoogieTypeFromSolidityTypeName(curType); + BoogieExpr boogieInit = TransUtils.GetDefaultVal(valType); + + string smtType = GetSmtType(valType); + string smtInit = boogieInit.ToString().Equals("null") ? "0" : boogieInit.ToString(); + BoogieType mapType = valType; + string fnName = $"{valType.ToString()}Arr"; + + foreach (BoogieType type in boogieType) + { + smtType = $"(Array {GetSmtType(type)} {smtType})"; + mapType = new BoogieMapType(type, mapType); + smtInit = $"((as const {smtType}) {smtInit})"; + fnName = $"{type.ToString()}{fnName}"; + } + + var outVar = new BoogieFormalParam(new BoogieTypedIdent("ret", mapType)); + var smtDefinedAttr = new BoogieAttribute("smtdefined", $"\"{smtInit}\""); + return new BoogieFunction($"zero{fnName}", new List(), new List() {outVar}, new List() {smtDefinedAttr}); + } + + public static BoogieFuncCallExpr GetCallExprForZeroInit(TypeName curType) + { + string fnName = "zero"; + + while (curType is Mapping || curType is ArrayTypeName) + { + if (curType is Mapping map) + { + fnName = $"{fnName}{TransUtils.GetBoogieTypeFromSolidityTypeName(map.KeyType).ToString()}"; + curType = map.ValueType; + } + else if (curType is ArrayTypeName arr) + { + fnName = $"{fnName}{BoogieType.Int.ToString()}"; + curType = arr.BaseType; + } + } + + fnName = $"{fnName}{TransUtils.GetBoogieTypeFromSolidityTypeName(curType).ToString()}Arr"; + return new BoogieFuncCallExpr(fnName, new List()); + } + public static BoogieFuncCallExpr GetCallExprForZeroInit(VariableDeclaration decl) + { + return GetCallExprForZeroInit(decl.TypeName); + } + + public static BoogieType GetMultiDimBoogieType(TypeName varType) + { + if (!(varType is Mapping || varType is ArrayTypeName)) + { + return TransUtils.GetBoogieTypeFromSolidityTypeName(varType); + } + + BoogieType resultType = null; + if (varType is Mapping map) + { + BoogieType valType = GetMultiDimBoogieType(map.ValueType); + BoogieType keyType = GetMultiDimBoogieType(map.KeyType); + resultType = new BoogieMapType(keyType, valType); + } + else if (varType is ArrayTypeName arr) + { + BoogieType baseType = GetMultiDimBoogieType(arr.BaseType); + resultType = new BoogieMapType(BoogieType.Int, baseType); + } + + return resultType; + } + + public static TypeName GetMappedType(VariableDeclaration varDecl) + { + TypeName curType = varDecl.TypeName; + + while (curType is Mapping || curType is ArrayTypeName) + { + if (curType is Mapping map) + { + curType = map.ValueType; + } + else if (curType is ArrayTypeName arr) + { + curType = arr.BaseType; + } + } + + return curType; + } + + public static string GetMultiDimLengthName(VariableDeclaration varDecl, int lvl) + { + return $"Length_{varDecl.Name}_lvl{lvl}"; + } + + public List GetMultiDimArrayLens(VariableDeclaration decl) + { + List lenVars = new List(); + TypeName curType = decl.TypeName; + + List indTypes = new List() {BoogieType.Ref}; + + int lvl = 0; + while (curType is Mapping || curType is ArrayTypeName) + { + if (curType is Mapping map) + { + curType = map.ValueType; + indTypes.Add(TransUtils.GetBoogieTypeFromSolidityTypeName(map.ValueType)); + } + else if (curType is ArrayTypeName arr) + { + BoogieType mapType = BoogieType.Int; + for (int i = indTypes.Count - 1; i >= 0; i--) + { + mapType = new BoogieMapType(indTypes[i], mapType); + } + + BoogieGlobalVariable lenVar = new BoogieGlobalVariable(new BoogieTypedIdent(GetMultiDimLengthName(decl, lvl), mapType)); + lenVars.Add(lenVar); + + curType = arr.BaseType; + indTypes.Add(BoogieType.Int); + } + + lvl++; + } + + return lenVars; + } + + public BoogieExpr GetLength(VariableDeclaration varDecl, BoogieExpr receiver) + { + if (context.TranslateFlags.UseMultiDim && context.Analysis.Alias.getResults().Contains(varDecl)) + { + BoogieExpr curExpr = receiver; + int lvl = -1; + List keys = new List(); + while (curExpr is BoogieMapSelect sel) + { + curExpr = sel.BaseExpr; + keys.Insert(0, sel.Arguments[0]); + lvl++; + } + + string lenName = GetMultiDimLengthName(varDecl, lvl); + BoogieExpr lenExpr = new BoogieIdentifierExpr(lenName); + + foreach (BoogieExpr key in keys) + { + lenExpr = new BoogieMapSelect(lenExpr, key); + } + + return lenExpr; + } + + return new BoogieMapSelect(new BoogieIdentifierExpr("Length"), receiver); + } + + public BoogieExpr GetLength(Expression solExpr, BoogieExpr receiver) + { + VariableDeclaration decl = getDecl(solExpr); + + //this can happen in the case of newExpression + if (decl == null) + { + return new BoogieMapSelect(new BoogieIdentifierExpr("Length"), receiver); + } + + return GetLength(decl, receiver); + } } } diff --git a/Sources/SolToBoogie/ParseUtils.cs b/Sources/SolToBoogie/ParseUtils.cs index 7c9ad55e..83418f86 100644 --- a/Sources/SolToBoogie/ParseUtils.cs +++ b/Sources/SolToBoogie/ParseUtils.cs @@ -174,6 +174,12 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, { translatorFlags.RunAliasAnalysis = true; } + + if (args.Any(x => x.Equals("/useMultiDim"))) + { + translatorFlags.RunAliasAnalysis = true; + translatorFlags.UseMultiDim = true; + } if (args.Any(x => x.Equals("/txnsOnFields"))) { diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index fc13bd51..fb6d7756 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -135,7 +135,103 @@ private void TranslateStructDefinition(StructDefinition structDefn) private String GetAccessPattern(VariableDeclaration varDecl, String boogieName) { - String solAccess = $"this.{varDecl.Name}"; + Identifier varIdent = new Identifier(); + varIdent.Name = varDecl.Name; + varIdent.OverloadedDeclarations = new List(); + varIdent.ReferencedDeclaration = varDecl.Id; + varIdent.TypeDescriptions = varDecl.TypeDescriptions; + + Expression varExpr = varIdent; + + TypeName curType = varDecl.TypeName; + List localIds = new List(); + + int i = 1; + while (curType is Mapping || curType is ArrayTypeName) + { + ElementaryTypeName indType = null; + if (curType is Mapping map) + { + indType = map.KeyType; + curType = map.ValueType; + } + else if (curType is ArrayTypeName arr) + { + TypeDescription intDescription = new TypeDescription(); + intDescription.TypeString = "uint"; + ElementaryTypeName intTypeName = new ElementaryTypeName(); + intTypeName.TypeDescriptions = intDescription; + indType = intTypeName; + curType = arr.BaseType; + } + + VariableDeclaration local = new VariableDeclaration(); + local.Constant = false; + local.Indexed = false; + local.Name = $"i{i}"; + local.Value = null; + local.Visibility = EnumVisibility.DEFAULT; + local.StateVariable = false; + local.StorageLocation = EnumLocation.DEFAULT; + local.TypeName = indType; + local.TypeDescriptions = indType.TypeDescriptions; + int id = -1 - i; + localIds.Add(id); + + //remove later + context.IdToNodeMap.Add(id, local); + + IndexAccess access = new IndexAccess(); + access.BaseExpression = varExpr; + Identifier localIdent = new Identifier(); + localIdent.Name = $"i{i}"; + localIdent.OverloadedDeclarations = new List(); + localIdent.ReferencedDeclaration = id; + localIdent.TypeDescriptions = local.TypeDescriptions; + access.IndexExpression = localIdent; + access.TypeDescriptions = TransUtils.TypeNameToTypeDescription(curType); + //access.TypeDescriptions.TypeString = curType.ToString(); + + varExpr = access; + i++; + } + + BoogieStmtList oldList = currentStmtList; + BoogieExpr oldExpr = currentExpr; + currentBoogieProc = ""; + currentExpr = null; + currentStmtList = new BoogieStmtList(); + this.boogieToLocalVarsMap.Add(currentBoogieProc, new List()); + + if (varExpr is Identifier ident) + { + Visit(ident); + } + else if (varExpr is IndexAccess access) + { + Visit(access); + } + + BoogieExpr translatedExpr = currentExpr; + currentExpr = oldExpr; + currentStmtList = oldList; + this.boogieToLocalVarsMap.Remove(currentBoogieProc); + currentBoogieProc = null; + string accessPattern = $"this.{varExpr}={translatedExpr}"; + + for (int j = 1; j < i; j++) + { + accessPattern = Regex.Replace(accessPattern, $"i{j}" + @"_[^\]]+", $"i{j}"); + } + + foreach(int id in localIds) + { + context.IdToNodeMap.Remove(id); + } + + return $"\"{accessPattern}\""; + + /*String solAccess = $"this.{varDecl.Name}"; String boogieAccess = $"{boogieName}[this]"; TypeName solType = varDecl.TypeName; int dim = 0; @@ -165,11 +261,16 @@ private String GetAccessPattern(VariableDeclaration varDecl, String boogieName) boogieAccess = $"{memMap}[{boogieAccess}][{dimName}]"; } - return $"\"{solAccess}={boogieAccess}\""; + return $"\"{solAccess}={boogieAccess}\"";*/ } private String GetSumAccessPattern(VariableDeclaration varDecl, String boogieName) { + if (context.TranslateFlags.UseMultiDim && context.Analysis.Alias.getResults().Contains(varDecl)) + { + return $"\"sum(this.{varDecl.Name})={mapHelper.GetSumName(varDecl)}[this]\""; + } + return $"\"sum(this.{varDecl.Name})={mapHelper.GetSumName(varDecl)}[{boogieName}[this]]\""; } @@ -178,7 +279,20 @@ private void TranslateStateVarDeclaration(VariableDeclaration varDecl) VeriSolAssert(varDecl.StateVariable, $"{varDecl} is not a state variable"); string name = TransUtils.GetCanonicalStateVariableName(varDecl, context); - BoogieType type = TransUtils.GetBoogieTypeFromSolidityTypeName(varDecl.TypeName); + + BoogieType type = null; + if (context.TranslateFlags.UseMultiDim && context.Analysis.Alias.getResults().Contains(varDecl)) + { + List lenVars = mapHelper.GetMultiDimArrayLens(varDecl); + lenVars.ForEach(context.Program.AddDeclaration); + + type = MapArrayHelper.GetMultiDimBoogieType(varDecl.TypeName); + } + else + { + type = TransUtils.GetBoogieTypeFromSolidityTypeName(varDecl.TypeName); + } + BoogieMapType mapType = new BoogieMapType(BoogieType.Ref, type); // Issue a warning for intXX variables in case /useModularArithemtic option is used: @@ -679,9 +793,122 @@ private BoogieStmtList GenerateInitializationStmts(ContractDefinition contract) return currentStmtList; } + + public BoogieStmtList GetMultiDimInitialization(VariableDeclaration decl, BoogieMapSelect contractVar, bool quantFree) + { + BoogieStmtList init = new BoogieStmtList(); + if (quantFree) + { + TypeName type = decl.TypeName; + + int curLvl = 0; + while (type is Mapping || type is ArrayTypeName) + { + if (type is Mapping map) + { + type = map.ValueType; + } + else if (type is ArrayTypeName arr) + { + ArrayTypeName lenType = new ArrayTypeName(); + ElementaryTypeName intType = new ElementaryTypeName(); + intType.TypeDescriptions = new TypeDescription(); + intType.TypeDescriptions.TypeString = "uint"; + lenType.BaseType = intType; + + if (arr.Length != null) + { + throw new Exception("Must add support for static arrays"); + } + + BoogieFuncCallExpr lenZero = MapArrayHelper.GetCallExprForZeroInit(lenType); + + string lenName = MapArrayHelper.GetMultiDimLengthName(decl, curLvl); + BoogieMapSelect lenAccess = new BoogieMapSelect(new BoogieIdentifierExpr(lenName), contractVar.Arguments); + init.AddStatement(new BoogieAssignCmd(lenAccess, lenZero)); + + type = arr.BaseType; + } + + curLvl++; + } + + init.AddStatement(new BoogieAssignCmd(contractVar, MapArrayHelper.GetCallExprForZeroInit(decl))); + return init; + } + + List qvars = new List(); + List qVarTypes = new List(); + BoogieExpr accessExpr = contractVar; + + int lvl = 0; + TypeName curType = decl.TypeName; + while (curType is Mapping || curType is ArrayTypeName) + { + var qVar = QVarGenerator.NewQVar(0, lvl); + qvars.Add(qVar); + + if (curType is Mapping map) + { + qVarTypes.Add(TransUtils.GetBoogieTypeFromSolidityTypeName(map.KeyType)); + curType = map.ValueType; + } + else if (curType is ArrayTypeName arr) + { + qVarTypes.Add(BoogieType.Int); + string lenName = MapArrayHelper.GetMultiDimLengthName(decl, lvl); + BoogieMapSelect lenAccess = new BoogieMapSelect(new BoogieIdentifierExpr(lenName), contractVar.Arguments[0]); + + List lenQVars = new List(); + List lenQVarTypes = new List(); + for (int i = 0; i < qvars.Count - 1; i++) + { + lenAccess = new BoogieMapSelect(lenAccess, qvars[i]); + lenQVars.Add(qvars[i]); + lenQVarTypes.Add(qVarTypes[i]); + } + /*foreach (BoogieIdentifierExpr qv in qvars) + { + lenAccess = new BoogieMapSelect(lenAccess, qv); + }*/ + + var lengthExpr = arr.Length == null ? TransUtils.GetDefaultVal(BoogieType.Int) : TranslateExpr(arr.Length); + BoogieExpr lenExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, lenAccess, lengthExpr); + if (lenQVars.Count != 0) + { + lenExpr = new BoogieQuantifiedExpr(true, lenQVars, lenQVarTypes, lenExpr); + } + + init.AddStatement(new BoogieAssumeCmd(lenExpr)); + + curType = arr.BaseType; + } + + accessExpr = new BoogieMapSelect(accessExpr, qVar); + lvl++; + } + + BoogieQuantifiedExpr qExpr = new BoogieQuantifiedExpr(true, qvars, qVarTypes, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, accessExpr, TransUtils.GetDefaultVal(curType))); + init.AddStatement(new BoogieAssumeCmd(qExpr)); + return init; + } private void GenerateInitializationForArrayStateVar(VariableDeclaration varDecl, ArrayTypeName array) { + if (context.TranslateFlags.UseMultiDim && context.Analysis.Alias.getResults().Contains(varDecl)) + { + string name = TransUtils.GetCanonicalStateVariableName(varDecl, context); + BoogieMapSelect contractInstance = new BoogieMapSelect(new BoogieIdentifierExpr(name), new BoogieIdentifierExpr("this")); + currentStmtList.AppendStmtList(GetMultiDimInitialization(varDecl, contractInstance, context.TranslateFlags.QuantFreeAllocs)); + + TypeName mappedType = MapArrayHelper.GetMappedType(varDecl); + if (context.TranslateFlags.InstrumentSums && mappedType is ElementaryTypeName elem && (elem.TypeDescriptions.IsInt() || elem.TypeDescriptions.IsUint())) + { + currentStmtList.AddStatement(new BoogieAssignCmd(mapHelper.GetSumExpr(varDecl, new BoogieIdentifierExpr("this")), new BoogieLiteralExpr(BigInteger.Zero))); + } + + return; + } // Issue a warning for intXX type in case /useModularArithemtic option is used: if (context.TranslateFlags.UseModularArithmetic && array.BaseType.ToString().StartsWith("int")) { @@ -691,7 +918,8 @@ private void GenerateInitializationForArrayStateVar(VariableDeclaration varDecl, BoogieMapSelect lhsMap = CreateDistinctArrayMappingAddress(currentStmtList, varDecl); // lets also initialize the array Lengths (only for Arrays declared in this class) - var lengthMapSelect = new BoogieMapSelect(new BoogieIdentifierExpr("Length"), lhsMap); + //var lengthMapSelect = new BoogieMapSelect(new BoogieIdentifierExpr("Length"), lhsMap); + var lengthMapSelect = mapHelper.GetLength(varDecl, lhsMap); var lengthExpr = array.Length == null ? new BoogieLiteralExpr(BigInteger.Zero) : TranslateExpr(array.Length); // var lengthEqualsZero = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, lengthMapSelect, new BoogieLiteralExpr(0)); var lengthConstraint = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, lengthMapSelect, lengthExpr); @@ -719,13 +947,6 @@ private void GenerateInitializationForStructStateVar(VariableDeclaration varDecl currentStmtList.AddStatement(new BoogieAssignCmd(lhsMap, tmpVarIdentExpr)); } - private BoogieFuncCallExpr GetCallExprForZeroInit(BoogieType key, BoogieType value) - { - var keyStr = char.ToUpper(key.ToString()[0]) + key.ToString().Substring(1); - var valStr = char.ToUpper(value.ToString()[0]) + value.ToString().Substring(1); - return new BoogieFuncCallExpr("zero" + keyStr + valStr + "Arr", new List()); - } - public BoogieAssignCmd adjustSum(VariableDeclaration decl, BoogieBinaryOperation.Opcode op, BoogieExpr sumInd, BoogieExpr amt) { BoogieExpr sumAccess = mapHelper.GetSumExpr(decl, sumInd); @@ -735,6 +956,20 @@ public BoogieAssignCmd adjustSum(VariableDeclaration decl, BoogieBinaryOperation private void GenerateInitializationForMappingStateVar(VariableDeclaration varDecl, Mapping mapping) { + if (context.TranslateFlags.UseMultiDim && context.Analysis.Alias.getResults().Contains(varDecl)) + { + string name = TransUtils.GetCanonicalStateVariableName(varDecl, context); + BoogieMapSelect contractInstance = new BoogieMapSelect(new BoogieIdentifierExpr(name), new BoogieIdentifierExpr("this")); + currentStmtList.AppendStmtList(GetMultiDimInitialization(varDecl, contractInstance, context.TranslateFlags.QuantFreeAllocs)); + + TypeName mappedType = MapArrayHelper.GetMappedType(varDecl); + if (context.TranslateFlags.InstrumentSums && mappedType is ElementaryTypeName elem && (elem.TypeDescriptions.IsInt() || elem.TypeDescriptions.IsUint())) + { + currentStmtList.AddStatement(new BoogieAssignCmd(mapHelper.GetSumExpr(varDecl, new BoogieIdentifierExpr("this")), new BoogieLiteralExpr(BigInteger.Zero))); + } + return; + } + BoogieMapSelect lhsMap = CreateDistinctArrayMappingAddress(currentStmtList, varDecl); //nested arrays (only 1 level for now) @@ -780,9 +1015,9 @@ private void GenerateInitializationForMappingStateVar(VariableDeclaration varDec { if (mapping.ValueType.ToString().StartsWith("bytes")) currentStmtList.AddStatement(new BoogieAssignCmd(lhs, - GetCallExprForZeroInit(mapKeyType, BoogieType.Int))); + MapArrayHelper.GetCallExprForZeroInit(mapKeyType, BoogieType.Int))); else - currentStmtList.AddStatement(new BoogieAssignCmd(lhs, GetCallExprForZeroInit(mapKeyType, BoogieType.Ref))); + currentStmtList.AddStatement(new BoogieAssignCmd(lhs, MapArrayHelper.GetCallExprForZeroInit(mapKeyType, BoogieType.Ref))); } } else if (mapping.ValueType.ToString().Equals("bool")) @@ -803,7 +1038,7 @@ private void GenerateInitializationForMappingStateVar(VariableDeclaration varDec } else { - currentStmtList.AddStatement(new BoogieAssignCmd(lhs, GetCallExprForZeroInit(mapKeyType, BoogieType.Bool))); + currentStmtList.AddStatement(new BoogieAssignCmd(lhs, MapArrayHelper.GetCallExprForZeroInit(mapKeyType, BoogieType.Bool))); } } // TODO: Cleanup, StartsWith("uint") can include uint[12] as well. @@ -834,7 +1069,7 @@ private void GenerateInitializationForMappingStateVar(VariableDeclaration varDec } else { - currentStmtList.AddStatement(new BoogieAssignCmd(lhs, GetCallExprForZeroInit(mapKeyType, BoogieType.Int))); + currentStmtList.AddStatement(new BoogieAssignCmd(lhs, MapArrayHelper.GetCallExprForZeroInit(mapKeyType, BoogieType.Int))); } if (context.TranslateFlags.InstrumentSums) @@ -929,10 +1164,14 @@ private void InitializeNestedArrayMappingStateVar(VariableDeclaration varDecl, M if (!context.TranslateFlags.LazyNestedAlloc) { //Length[Mem_t_ref[x[this]][i]] == 0 - var bodyExpr = new BoogieBinaryOperation( + /*var bodyExpr = new BoogieBinaryOperation( BoogieBinaryOperation.Opcode.EQ, new BoogieMapSelect(new BoogieIdentifierExpr("Length"), lhs1), - new BoogieLiteralExpr(0)); + new BoogieLiteralExpr(0));*/ + var bodyExpr = new BoogieBinaryOperation( + BoogieBinaryOperation.Opcode.EQ, + mapHelper.GetLength(varDecl, lhs1), + new BoogieLiteralExpr(0)); var qExpr = new BoogieQuantifiedExpr(true, new List() {qVar1}, new List() {mapKeyType}, bodyExpr); currentStmtList.AddStatement(new BoogieAssumeCmd(qExpr)); @@ -1012,7 +1251,7 @@ private void InitializeNestedArrayMappingStateVar(VariableDeclaration varDecl, M if (context.TranslateFlags.QuantFreeAllocs) { currentStmtList.AddStatement((new BoogieAssignCmd(lhs, - GetCallExprForZeroInit(keyType, BoogieType.Bool)))); + MapArrayHelper.GetCallExprForZeroInit(keyType, BoogieType.Bool)))); } else { @@ -1063,7 +1302,7 @@ private void InitializeNestedArrayMappingStateVar(VariableDeclaration varDecl, M string memName = mapHelper.GetMemoryMapName(varDecl, keyType, valType); BoogieMapSelect lhs = new BoogieMapSelect(new BoogieIdentifierExpr(memName), index); currentStmtList.AddStatement((new BoogieAssignCmd(lhs, - GetCallExprForZeroInit(keyType, valType)))); + MapArrayHelper.GetCallExprForZeroInit(keyType, valType)))); if (valType.Equals(BoogieType.Int)) { index = new BoogieLiteralExpr(BigInteger.Zero); @@ -2183,7 +2422,8 @@ public override bool Visit(ExpressionStatement node) if (typeDescription.IsDynamicArray()) { BoogieExpr element = TranslateExpr(unaryOperation.SubExpression); - BoogieExpr lengthMapSelect = new BoogieMapSelect(new BoogieIdentifierExpr("Length"), element); + //BoogieExpr lengthMapSelect = new BoogieMapSelect(new BoogieIdentifierExpr("Length"), element); + BoogieExpr lengthMapSelect = mapHelper.GetLength(unaryOperation.SubExpression, element); BoogieExpr rhs = new BoogieLiteralExpr(BigInteger.Zero); var assignCmd = new BoogieAssignCmd(lengthMapSelect, rhs); currentStmtList.AddStatement(assignCmd); @@ -2501,7 +2741,8 @@ private BoogieExpr TranslateArrayLength(MemberAccess node) VeriSolAssert(node.MemberName.Equals("length")); BoogieExpr indexExpr = TranslateExpr(node.Expression); - var mapSelect = new BoogieMapSelect(new BoogieIdentifierExpr("Length"), indexExpr); + //var mapSelect = new BoogieMapSelect(new BoogieIdentifierExpr("Length"), indexExpr); + var mapSelect = mapHelper.GetLength(node.Expression, indexExpr); return mapSelect; } @@ -2687,11 +2928,16 @@ private BoogieExpr TranslateVeriSolCodeContractFuncCall(FunctionCall node) // HACK for Sum if (verisolFunc.Equals("_SumMapping_VeriSol")) { - //has to be M_ref_int[mapp[this]] instead of mapp[this] + VariableDeclaration decl = mapHelper.getDecl(node.Arguments[0]); VeriSolAssert(decl != null, "Could not find declaration of " + node.Arguments[0]); - var mapName = mapHelper.GetMemoryMapName(decl, BoogieType.Ref, BoogieType.Int); - boogieExprs[0] = new BoogieMapSelect(new BoogieIdentifierExpr(mapName), boogieExprs[0]); + if (!(context.TranslateFlags.UseMultiDim && context.Analysis.Alias.getResults().Contains(decl))) + { + //has to be M_ref_int[mapp[this]] instead of mapp[this] + var mapName = mapHelper.GetMemoryMapName(decl, BoogieType.Ref, BoogieType.Int); + boogieExprs[0] = new BoogieMapSelect(new BoogieIdentifierExpr(mapName), boogieExprs[0]); + } + } return new BoogieFuncCallExpr(verisolFunc, boogieExprs); } @@ -2992,7 +3238,8 @@ private void TranslateNewStatement(FunctionCall node, BoogieExpr lhs) // length[tmp] = 5 currentStmtList.AddStatement( new BoogieAssignCmd( - new BoogieMapSelect(new BoogieIdentifierExpr("Length"), tmpVarIdentExpr), + //new BoogieMapSelect(new BoogieIdentifierExpr("Length"), tmpVarIdentExpr), + mapHelper.GetLength(newExpr, tmpVarIdentExpr), TranslateExpr(node.Arguments[0]) ) ); @@ -3119,7 +3366,8 @@ private void TranslateDynamicArrayPush(FunctionCall node) BoogieExpr receiver = TranslateExpr(memberAccess.Expression); BoogieExpr element = TranslateExpr(node.Arguments[0]); - BoogieExpr lengthMapSelect = new BoogieMapSelect(new BoogieIdentifierExpr("Length"), receiver); + //BoogieExpr lengthMapSelect = new BoogieMapSelect(new BoogieIdentifierExpr("Length"), receiver); + BoogieExpr lengthMapSelect = mapHelper.GetLength(memberAccess.Expression, receiver); // suppose the form is a.push(e) // tmp := Length[this][a]; BoogieTypedIdent tmpIdent = context.MakeFreshTypedIdent(BoogieType.Int); @@ -4069,8 +4317,15 @@ public override bool Visit(IndexAccess node) // VeriSolAssert(false, $"Unknown base in index access: {node.BaseExpression}"); //} - BoogieExpr indexAccessExpr = new BoogieMapSelect(baseExpr, indexExpr); + VariableDeclaration decl = mapHelper.getDecl(node); + + if (context.TranslateFlags.UseMultiDim && context.Analysis.Alias.getResults().Contains(decl)) + { + currentExpr = new BoogieMapSelect(baseExpr, indexExpr); + return false; + } + currentExpr = mapHelper.GetMemoryMapSelectExpr(decl, baseKeyType, baseValType, baseExpr, indexExpr); if (context.TranslateFlags.LazyAllocNoMod) @@ -4090,7 +4345,8 @@ public override bool Visit(IndexAccess node) if (valIsArray) { - allocAndInit.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, new BoogieMapSelect(new BoogieIdentifierExpr("Length"), currentExpr), new BoogieLiteralExpr(0)))); + //allocAndInit.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, new BoogieMapSelect(new BoogieIdentifierExpr("Length"), currentExpr), new BoogieLiteralExpr(0)))); + allocAndInit.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, mapHelper.GetLength(decl, currentExpr), new BoogieLiteralExpr(0)))); } //allocAndInit.AddStatement(new BoogieAssignCmd(currentExpr, tmpVarIdentExpr)); @@ -4119,7 +4375,7 @@ public override bool Visit(IndexAccess node) //allocate struct if (context.TranslateFlags.QuantFreeAllocs) { - BoogieFuncCallExpr zeroInit = GetCallExprForZeroInit(nestedKeyType, nestedValType); + BoogieFuncCallExpr zeroInit = MapArrayHelper.GetCallExprForZeroInit(nestedKeyType, nestedValType); allocAndInit.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, derefCurrExpr, zeroInit))); } else @@ -4165,7 +4421,8 @@ public override bool Visit(IndexAccess node) if (valIsArray) { - allocAndInit.AddStatement(new BoogieAssignCmd(new BoogieMapSelect(new BoogieIdentifierExpr("Length"), currentExpr), new BoogieLiteralExpr(0))); + //allocAndInit.AddStatement(new BoogieAssignCmd(new BoogieMapSelect(new BoogieIdentifierExpr("Length"), currentExpr), new BoogieLiteralExpr(0))); + allocAndInit.AddStatement(new BoogieAssignCmd(mapHelper.GetLength(decl, currentExpr), new BoogieLiteralExpr(0))); } BoogieType nestedValType = MapArrayHelper.InferValueTypeFromTypeString(valTypeString); @@ -4180,7 +4437,7 @@ public override bool Visit(IndexAccess node) { if (context.TranslateFlags.QuantFreeAllocs) { - allocAndInit.AddStatement(new BoogieAssignCmd(derefCurrExpr, GetCallExprForZeroInit(nestedKeyType, BoogieType.Bool))); + allocAndInit.AddStatement(new BoogieAssignCmd(derefCurrExpr, MapArrayHelper.GetCallExprForZeroInit(nestedKeyType, BoogieType.Bool))); } else { @@ -4197,7 +4454,7 @@ public override bool Visit(IndexAccess node) { if (context.TranslateFlags.QuantFreeAllocs) { - allocAndInit.AddStatement(new BoogieAssignCmd(derefCurrExpr, GetCallExprForZeroInit(nestedKeyType, BoogieType.Int))); + allocAndInit.AddStatement(new BoogieAssignCmd(derefCurrExpr, MapArrayHelper.GetCallExprForZeroInit(nestedKeyType, BoogieType.Int))); } else { @@ -4219,7 +4476,7 @@ public override bool Visit(IndexAccess node) { if (context.TranslateFlags.QuantFreeAllocs) { - allocAndInit.AddStatement(new BoogieAssignCmd(derefCurrExpr, GetCallExprForZeroInit(nestedKeyType, BoogieType.Ref))); + allocAndInit.AddStatement(new BoogieAssignCmd(derefCurrExpr, MapArrayHelper.GetCallExprForZeroInit(nestedKeyType, BoogieType.Ref))); } else { diff --git a/Sources/SolToBoogie/TransUtils.cs b/Sources/SolToBoogie/TransUtils.cs index a12650ff..b4a015bd 100644 --- a/Sources/SolToBoogie/TransUtils.cs +++ b/Sources/SolToBoogie/TransUtils.cs @@ -1,5 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. + +using System.Security.Cryptography; + namespace SolToBoogie { using System; @@ -934,5 +937,62 @@ public static void havocGas(List list) new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.LE, gasVar, new BoogieLiteralExpr(TranslatorContext.MAX_GAS_LIMIT))))); } + public static BoogieExpr GetDefaultVal(TypeName type) + { + return GetDefaultVal(TransUtils.GetBoogieTypeFromSolidityTypeName(type)); + } + public static BoogieExpr GetDefaultVal(BoogieType boogieType) + { + if (boogieType.Equals(BoogieType.Int)) + { + return new BoogieLiteralExpr(BigInteger.Zero); + } + else if (boogieType.Equals(BoogieType.Bool)) + { + return new BoogieLiteralExpr(false); + } + else if (boogieType.Equals(BoogieType.Ref)) + { + return new BoogieIdentifierExpr("null"); + } + + throw new Exception($"Unknown BoogieType {boogieType}"); + } + + public static TypeDescription TypeNameToTypeDescription(TypeName typeName) + { + if (typeName is ArrayTypeName arr) + { + TypeDescription desc = TypeNameToTypeDescription(arr.BaseType); + desc.TypeIndentifier = $"{desc.TypeIndentifier}[]"; + desc.TypeString = $"{desc.TypeString}[]"; + return desc; + } + else if (typeName is Mapping map) + { + TypeDescription keyDesc = TypeNameToTypeDescription(map.KeyType); + TypeDescription valDesc = TypeNameToTypeDescription(map.ValueType); + TypeDescription desc = new TypeDescription(); + desc.TypeIndentifier = $"mapping({keyDesc.TypeIndentifier} => {valDesc.TypeIndentifier})"; + desc.TypeString = $"mapping({keyDesc.TypeString} => {valDesc.TypeString})"; + return desc; + } + else if (typeName is ElementaryTypeName elem) + { + TypeDescription desc = new TypeDescription(); + desc.TypeIndentifier = elem.TypeDescriptions.TypeIndentifier; + desc.TypeString = elem.TypeDescriptions.TypeString; + return desc; + } + else if (typeName is UserDefinedTypeName user) + { + TypeDescription desc = new TypeDescription(); + desc.TypeIndentifier = user.TypeDescriptions.TypeIndentifier; + desc.TypeString = user.TypeDescriptions.TypeString; + return desc; + } + + throw new Exception("Unknown type name"); + } } } diff --git a/Sources/SolToBoogie/TranslatorFlags.cs b/Sources/SolToBoogie/TranslatorFlags.cs index 2df6516d..a1b7149d 100644 --- a/Sources/SolToBoogie/TranslatorFlags.cs +++ b/Sources/SolToBoogie/TranslatorFlags.cs @@ -32,6 +32,7 @@ public TranslatorFlags() OmitAssumeFalseForDynDispatch = false; NoTxnsFromContract = true; RunAliasAnalysis = false; + UseMultiDim = false; TxnsOnFields = false; NoNonlinearArith = false; UseNumericOperators = false; @@ -126,5 +127,7 @@ public TranslatorFlags() // remove the scoping from variable name (risky and not exposed) public bool RemoveScopeInVarName { get; set; } + + public bool UseMultiDim { get; set; } } } From 3488ce51f013818b5e31463a08841830feeb2e7f Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Tue, 15 Sep 2020 13:53:24 -0700 Subject: [PATCH 23/49] Adding a Pre/Post mode for SmartPulse --- Sources/SolToBoogie/HarnessGenerator.cs | 30 ++++++++++++++++++++----- Sources/SolToBoogie/ParseUtils.cs | 5 +++++ Sources/SolToBoogie/TranslatorFlags.cs | 3 +++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Sources/SolToBoogie/HarnessGenerator.cs b/Sources/SolToBoogie/HarnessGenerator.cs index baf961b9..9b4c5024 100644 --- a/Sources/SolToBoogie/HarnessGenerator.cs +++ b/Sources/SolToBoogie/HarnessGenerator.cs @@ -361,7 +361,11 @@ private void GenerateCorralHarnessForContract(ContractDefinition contract) harnessBody.AddStatement(new BoogieCallCmd("FreshRefGenerator", new List(), new List() {new BoogieIdentifierExpr("this")})); harnessBody.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, new BoogieIdentifierExpr("now"), new BoogieLiteralExpr(0)))); harnessBody.AddStatement(GenerateDynamicTypeAssumes(contract)); - GenerateConstructorCall(contract).ForEach(x => harnessBody.AddStatement(x)); + if (!context.TranslateFlags.PrePostHarness) + { + GenerateConstructorCall(contract).ForEach(x => harnessBody.AddStatement(x)); + } + if (context.TranslateFlags.ModelReverts) { BoogieExpr assumePred = new BoogieUnaryOperation(BoogieUnaryOperation.Opcode.NOT, new BoogieIdentifierExpr("revert")); @@ -372,20 +376,36 @@ private void GenerateCorralHarnessForContract(ContractDefinition contract) harnessBody.AddStatement(new BoogieAssumeCmd(assumePred)); } - harnessBody.AddStatement(GenerateCorralWhileLoop(contract)); + + if (context.TranslateFlags.PrePostHarness) + { + harnessBody.AddStatement(GenerateCorralCall(contract)); + BoogieStmtList body = new BoogieStmtList(); + harnessBody.AddStatement(new BoogieWhileCmd(new BoogieLiteralExpr(true), body)); + } + else + { + harnessBody.AddStatement(GenerateCorralWhileLoop(contract)); + } + BoogieImplementation harnessImpl = new BoogieImplementation(harnessName, inParams, outParams, localVars, harnessBody); context.Program.AddDeclaration(harnessImpl); } - private BoogieWhileCmd GenerateCorralWhileLoop(ContractDefinition contract) + private BoogieCallCmd GenerateCorralCall(ContractDefinition contract) { - BoogieStmtList body = new BoogieStmtList(); string callee = "CorralChoice_" + contract.Name; List inputs = new List() { new BoogieIdentifierExpr("this"), }; - body.AddStatement(new BoogieCallCmd(callee, inputs, null)); + + return new BoogieCallCmd(callee, inputs, null); + } + private BoogieWhileCmd GenerateCorralWhileLoop(ContractDefinition contract) + { + BoogieStmtList body = new BoogieStmtList(); + body.AddStatement(GenerateCorralCall(contract)); List candidateInvs = new List(); // add the contract invariant if present diff --git a/Sources/SolToBoogie/ParseUtils.cs b/Sources/SolToBoogie/ParseUtils.cs index 83418f86..8e66dd67 100644 --- a/Sources/SolToBoogie/ParseUtils.cs +++ b/Sources/SolToBoogie/ParseUtils.cs @@ -197,6 +197,11 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, BoogieBinaryOperation.USE_ARITH_OPS = true; } + if (args.Any(x => x.Equals("/prePostHarness"))) + { + translatorFlags.PrePostHarness = true; + } + translatorFlags.PerformContractInferce = args.Any(x => x.StartsWith("/contractInfer")); // don't perform verification for some of these omitFlags diff --git a/Sources/SolToBoogie/TranslatorFlags.cs b/Sources/SolToBoogie/TranslatorFlags.cs index a1b7149d..a9ab4f39 100644 --- a/Sources/SolToBoogie/TranslatorFlags.cs +++ b/Sources/SolToBoogie/TranslatorFlags.cs @@ -39,6 +39,7 @@ public TranslatorFlags() LazyAllocNoMod = false; QuantFreeAllocs = false; LazyNestedAlloc = false; + PrePostHarness = false; } public bool UseNumericOperators { get; set; } @@ -128,6 +129,8 @@ public TranslatorFlags() // remove the scoping from variable name (risky and not exposed) public bool RemoveScopeInVarName { get; set; } + public bool PrePostHarness { get; set; } + public bool UseMultiDim { get; set; } } } From 35b78478bdef462ce696adf4f5baf5017d5490cc Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Tue, 15 Sep 2020 13:58:40 -0700 Subject: [PATCH 24/49] Adding the script I use to reset the installation --- resetInstall.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 resetInstall.sh diff --git a/resetInstall.sh b/resetInstall.sh new file mode 100644 index 00000000..343776da --- /dev/null +++ b/resetInstall.sh @@ -0,0 +1,5 @@ +dotnet tool uninstall --global VeriSol +dotnet tool uninstall --global SolToBoogieTest +dotnet build Sources/VeriSol.sln +dotnet tool install VeriSol --version 0.1.1-alpha --global --add-source ./nupkg/ +dotnet tool install --global SolToBoogieTest --version 0.1.1-alpha --add-source ./nupkg/ From f318df301a89713aaa22ed8e0729d472b4ad6fb9 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Fri, 18 Sep 2020 09:07:43 -0700 Subject: [PATCH 25/49] Adding an argument to generate getters for public variables --- Sources/BoogieAST/BoogieAST.cs | 14 ++- Sources/SolToBoogie/FunctionEventCollector.cs | 117 +++++++++++++++++- Sources/SolToBoogie/ParseUtils.cs | 5 + Sources/SolToBoogie/ProcedureTranslator.cs | 2 +- Sources/SolToBoogie/TranslatorFlags.cs | 3 + 5 files changed, 137 insertions(+), 4 deletions(-) diff --git a/Sources/BoogieAST/BoogieAST.cs b/Sources/BoogieAST/BoogieAST.cs index 0a239034..504094ae 100644 --- a/Sources/BoogieAST/BoogieAST.cs +++ b/Sources/BoogieAST/BoogieAST.cs @@ -317,19 +317,29 @@ public class BoogieImplementation : BoogieDeclWithFormals public BoogieStmtList StructuredStmts { get; set; } - public BoogieImplementation(string name, List inParams, List outParams, List localVars, BoogieStmtList stmts) + public BoogieImplementation(string name, List inParams, List outParams, List localVars, BoogieStmtList stmts, List attributes = null) { this.Name = name; this.InParams = inParams; this.OutParams = outParams; this.LocalVars = localVars; this.StructuredStmts = stmts; + this.Attributes = attributes; } public override string ToString() { StringBuilder builder = new StringBuilder(); - builder.Append("implementation ").Append(Name).Append("("); + builder.Append("implementation "); + if (Attributes != null) + { + foreach (BoogieAttribute attribute in Attributes) + { + builder.Append(attribute).Append(" "); + } + } + + builder.Append(Name).Append("("); if (InParams.Count > 0) { foreach (BoogieVariable inParam in InParams) diff --git a/Sources/SolToBoogie/FunctionEventCollector.cs b/Sources/SolToBoogie/FunctionEventCollector.cs index 7978bc0a..471bf568 100644 --- a/Sources/SolToBoogie/FunctionEventCollector.cs +++ b/Sources/SolToBoogie/FunctionEventCollector.cs @@ -1,5 +1,9 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. + +using System.Collections.Generic; +using System.Linq; + namespace SolToBoogie { using System.Diagnostics; @@ -16,6 +20,7 @@ public class FunctionEventCollector : BasicASTVisitor // current contract that the visitor is visiting private ContractDefinition currentContract = null; + private List nodesToAdd = null; public FunctionEventCollector(TranslatorContext context) { @@ -25,12 +30,15 @@ public FunctionEventCollector(TranslatorContext context) public override bool Visit(ContractDefinition node) { currentContract = node; + nodesToAdd = new List(); return true; } public override void EndVisit(ContractDefinition node) { + currentContract.Nodes.AddRange(nodesToAdd); currentContract = null; + nodesToAdd = null; } public override bool Visit(EventDefinition node) @@ -46,5 +54,112 @@ public override bool Visit(FunctionDefinition node) context.AddFunctionToContract(currentContract, node); return false; } + + public FunctionDefinition GenerateGetter(VariableDeclaration varDecl) + { + FunctionDefinition fnDef = new FunctionDefinition(); + Block body = new Block(); + body.Scope = 0; + body.Statements = new List(); + ParameterList fnParams = new ParameterList(); + fnParams.Parameters = new List(); + ParameterList rets = new ParameterList(); + rets.Parameters = new List(); + + fnDef.Visibility = EnumVisibility.PUBLIC; + fnDef.Implemented = true; + fnDef.Name = varDecl.Name; + fnDef.StateMutability = EnumStateMutability.VIEW; + fnDef.Body = body; + fnDef.Parameters = fnParams; + fnDef.ReturnParameters = rets; + fnDef.Modifiers = new List(); + + TypeName curType = varDecl.TypeName; + + Identifier ident = new Identifier(); + ident.Name = varDecl.Name; + ident.ReferencedDeclaration = varDecl.Id; + ident.OverloadedDeclarations = new List(); + ident.TypeDescriptions = varDecl.TypeDescriptions; + + int id = context.IdToNodeMap.Keys.Max() + 1; + + Expression curExpr = ident; + List localIds = new List(); + + while (curType is Mapping || curType is ArrayTypeName) + { + VariableDeclaration paramDecl = new VariableDeclaration(); + paramDecl.Name = "arg" + id; + paramDecl.Visibility = EnumVisibility.DEFAULT; + paramDecl.StorageLocation = EnumLocation.DEFAULT; + paramDecl.Id = id; + context.IdToNodeMap.Add(id, paramDecl); + + if (curType is Mapping map) + { + paramDecl.TypeName = map.KeyType; + paramDecl.TypeDescriptions = map.KeyType.TypeDescriptions; + fnParams.Parameters.Add(paramDecl); + curType = map.ValueType; + } + else if (curType is ArrayTypeName arr) + { + TypeDescription intDescription = new TypeDescription(); + intDescription.TypeString = "uint"; + ElementaryTypeName intTypeName = new ElementaryTypeName(); + intTypeName.TypeDescriptions = intDescription; + paramDecl.TypeName = intTypeName; + paramDecl.TypeDescriptions = intDescription; + curType = arr.BaseType; + } + + Identifier paramIdent = new Identifier(); + paramIdent.Name = paramDecl.Name; + paramIdent.OverloadedDeclarations = new List(); + paramIdent.TypeDescriptions = paramDecl.TypeDescriptions; + paramIdent.ReferencedDeclaration = paramDecl.Id; + + IndexAccess access = new IndexAccess(); + access.BaseExpression = curExpr; + access.IndexExpression = paramIdent; + access.TypeDescriptions = TransUtils.TypeNameToTypeDescription(curType); + + curExpr = access; + id++; + } + + VariableDeclaration retVar = new VariableDeclaration(); + retVar = new VariableDeclaration(); + retVar.Name = null; + retVar.TypeDescriptions = TransUtils.TypeNameToTypeDescription(curType); + retVar.TypeName = curType; + rets.Parameters.Add(retVar); + + Return ret = new Return(); + ret.Expression = curExpr; + + context.ASTNodeToSourcePathMap[ret] = context.ASTNodeToSourcePathMap[varDecl]; + context.ASTNodeToSourceLineNumberMap[ret] = context.ASTNodeToSourceLineNumberMap[varDecl]; + context.ASTNodeToSourcePathMap[body] = context.ASTNodeToSourcePathMap[varDecl]; + context.ASTNodeToSourceLineNumberMap[body] = context.ASTNodeToSourceLineNumberMap[varDecl]; + + body.Statements.Add(ret); + + return fnDef; + } + + public override bool Visit(VariableDeclaration varDecl) + { + if (context.TranslateFlags.GenerateGetters && varDecl.Visibility.Equals(EnumVisibility.PUBLIC)) + { + FunctionDefinition getter = GenerateGetter(varDecl); + nodesToAdd.Add(getter); + context.AddFunctionToContract(currentContract, getter); + } + + return false; + } } } diff --git a/Sources/SolToBoogie/ParseUtils.cs b/Sources/SolToBoogie/ParseUtils.cs index 8e66dd67..53aba30f 100644 --- a/Sources/SolToBoogie/ParseUtils.cs +++ b/Sources/SolToBoogie/ParseUtils.cs @@ -202,6 +202,11 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, translatorFlags.PrePostHarness = true; } + if (args.Any(x => x.Equals("/generateGetters"))) + { + translatorFlags.GenerateGetters = true; + } + translatorFlags.PerformContractInferce = args.Any(x => x.StartsWith("/contractInfer")); // don't perform verification for some of these omitFlags diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index fb6d7756..e9905682 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -273,7 +273,7 @@ private String GetSumAccessPattern(VariableDeclaration varDecl, String boogieNam return $"\"sum(this.{varDecl.Name})={mapHelper.GetSumName(varDecl)}[{boogieName}[this]]\""; } - + private void TranslateStateVarDeclaration(VariableDeclaration varDecl) { VeriSolAssert(varDecl.StateVariable, $"{varDecl} is not a state variable"); diff --git a/Sources/SolToBoogie/TranslatorFlags.cs b/Sources/SolToBoogie/TranslatorFlags.cs index a9ab4f39..05b71bc9 100644 --- a/Sources/SolToBoogie/TranslatorFlags.cs +++ b/Sources/SolToBoogie/TranslatorFlags.cs @@ -40,6 +40,7 @@ public TranslatorFlags() QuantFreeAllocs = false; LazyNestedAlloc = false; PrePostHarness = false; + GenerateGetters = false; } public bool UseNumericOperators { get; set; } @@ -132,5 +133,7 @@ public TranslatorFlags() public bool PrePostHarness { get; set; } public bool UseMultiDim { get; set; } + + public bool GenerateGetters { get; set; } } } From 4048ca7beea65c58121593517b4c3f299062a7e2 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Thu, 24 Sep 2020 16:42:43 -0700 Subject: [PATCH 26/49] Adding mode to generate ERC20 specifications --- Sources/SolToBoogie/BoogieTranslator.cs | 6 + Sources/SolToBoogie/ERC20SpecGenerator.cs | 441 ++++++++++++++++++++++ Sources/SolToBoogie/ParseUtils.cs | 5 + Sources/SolToBoogie/TranslatorFlags.cs | 3 + 4 files changed, 455 insertions(+) create mode 100644 Sources/SolToBoogie/ERC20SpecGenerator.cs diff --git a/Sources/SolToBoogie/BoogieTranslator.cs b/Sources/SolToBoogie/BoogieTranslator.cs index c92bdbe5..d848cecd 100644 --- a/Sources/SolToBoogie/BoogieTranslator.cs +++ b/Sources/SolToBoogie/BoogieTranslator.cs @@ -109,6 +109,12 @@ public BoogieAST Translate(AST solidityAST, HashSet> ignor modSetAnalysis.PerformModSetAnalysis(); } + if (context.TranslateFlags.GenerateERC20Spec) + { + ERC20SpecGenerator specGen = new ERC20SpecGenerator(context, solidityAST, entryPointContract); + specGen.GenerateSpec(); + } + return new BoogieAST(context.Program); } } diff --git a/Sources/SolToBoogie/ERC20SpecGenerator.cs b/Sources/SolToBoogie/ERC20SpecGenerator.cs new file mode 100644 index 00000000..7007cf81 --- /dev/null +++ b/Sources/SolToBoogie/ERC20SpecGenerator.cs @@ -0,0 +1,441 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.ConstrainedExecution; +using System.Runtime.Serialization; +using SolidityAST; + +namespace SolToBoogie +{ + public class ERC20SpecGenerator + { + private TranslatorContext context; + private AST solidityAST; + private List ERC20Vars = new List() {"totalSupply", "balanceOf", "allowance"}; + private List ERC20fns = new List() {"totalSupply", "balanceOf", "allowance", "approve", "transfer", "transferFrom"}; + private ContractDefinition entryContract; + private Dictionary varDecls; + private Dictionary fnContracts; + private List otherVars; + + public ERC20SpecGenerator(TranslatorContext context, AST solidityAST, String entryPoint) + { + this.context = context; + this.solidityAST = solidityAST; + varDecls = new Dictionary(); + fnContracts = new Dictionary(); + otherVars = new List(); + + foreach (ContractDefinition def in context.ContractDefinitions) + { + if (def.Name.Equals(entryPoint)) + { + entryContract = def; + } + } + + foreach (int id in entryContract.LinearizedBaseContracts) + { + ContractDefinition contract = context.GetASTNodeById(id) as ContractDefinition; + + if (context.ContractToStateVarsMap.ContainsKey(contract)) + { + otherVars.AddRange(context.ContractToStateVarsMap[contract]); + } + + if (!context.ContractToFunctionsMap.ContainsKey(contract)) + { + continue; + } + + HashSet fnDefs = context.ContractToFunctionsMap[contract]; + + foreach (FunctionDefinition fnDef in fnDefs) + { + if (ERC20fns.Contains(fnDef.Name) && !fnContracts.ContainsKey(fnDef.Name)) + { + fnContracts[fnDef.Name] = contract; + } + + if (ERC20Vars.Contains(fnDef.Name) && !varDecls.ContainsKey(fnDef.Name)) + { + ReturnDeclarationFinder declFinder = new ReturnDeclarationFinder(context); + VariableDeclaration decl = declFinder.findDecl(contract, fnDef); + + if (decl != null) + { + varDecls[fnDef.Name] = decl; + } + } + } + } + + foreach (VariableDeclaration decl in varDecls.Values) + { + otherVars.Remove(decl); + } + } + + + public void GenerateSpec() + { + StreamWriter writer = new StreamWriter("__ERC20__.config"); + + string totSupply = varDecls.ContainsKey("totalSupply") ? varDecls["totalSupply"].Name : ""; + string bal = varDecls.ContainsKey("balanceOf") ? varDecls["balanceOf"].Name : ""; + string allowances = varDecls.ContainsKey("allowance") ? varDecls["allowance"].Name : ""; + string totContract = fnContracts.ContainsKey("totalSupply") ? fnContracts["totalSupply"].Name : ""; + string balContract = fnContracts.ContainsKey("balanceOf") ? fnContracts["balanceOf"].Name : ""; + string allowanceContract = fnContracts.ContainsKey("allowance") ? fnContracts["allowance"].Name : ""; + string approveContract = fnContracts.ContainsKey("approve") ? fnContracts["approve"].Name : ""; + string transferContract = fnContracts.ContainsKey("transfer") ? fnContracts["transfer"].Name : ""; + string transferFromContract = + fnContracts.ContainsKey("transferFrom") ? fnContracts["transferFrom"].Name : ""; + string extraVars = String.Join(" ", otherVars.Select(v => v.Name)); + + writer.WriteLine($"FILE_NAME={context.ASTNodeToSourcePathMap[entryContract]}"); + writer.WriteLine($"CONTRACT_NAME={entryContract.Name}"); + writer.WriteLine($"TOTAL={totSupply}"); + writer.WriteLine($"BALANCES={bal}"); + writer.WriteLine($"ALLOWANCES={allowances}"); + writer.WriteLine($"TOT_SUP_CONTRACT={totContract}"); + writer.WriteLine($"BAL_OF_CONTRACT={balContract}"); + writer.WriteLine($"ALLOWANCE_CONTRACT={allowanceContract}"); + writer.WriteLine($"APPROVE_CONTRACT={approveContract}"); + writer.WriteLine($"TRANSFER_CONTRACT={transferContract}"); + writer.WriteLine($"TRANSFER_FROM_CONTRACT={transferFromContract}"); + writer.WriteLine($"EXTRA_VARS=({extraVars})"); + writer.Close(); + } + + + private class ReturnDeclarationFinder : BasicASTVisitor + { + private VariableDeclaration retDecl; + private String findVar; + private TranslatorContext context; + private ContractDefinition curContract; + + public ReturnDeclarationFinder(TranslatorContext context) + { + this.context = context; + retDecl = null; + } + + public VariableDeclaration findDecl(ContractDefinition curContract, FunctionDefinition def) + { + if (def.ReturnParameters.Parameters.Count != 1) + { + return null; + } + + if (!String.IsNullOrEmpty(def.ReturnParameters.Parameters[0].Name)) + { + findVar = def.ReturnParameters.Parameters[0].Name; + } + + this.curContract = curContract; + + def.Body.Accept(this); + return retDecl; + } + + public override bool Visit(ArrayTypeName node) + { + return false; + } + + public override bool Visit(Assignment node) + { + if (node.LeftHandSide is Identifier ident) + { + if (ident.Name.Equals(findVar)) + { + node.RightHandSide.Accept(this); + } + } + return false; + } + + public override bool Visit(SourceUnit node) + { + return false; + } + + public override bool Visit(BinaryOperation node) + { + return false; + } + + public override bool Visit(Block node) + { + for (int i = node.Statements.Count - 1; i >= 0; i--) + { + node.Statements[i].Accept(this); + } + return false; + } + + public override bool Visit(Break node) + { + return false; + } + + public override bool Visit(Conditional node) + { + return false; + } + + public override bool Visit(Continue node) + { + return false; + } + + public override bool Visit(ContractDefinition node) + { + return false; + } + + public override bool Visit(PragmaDirective node) + { + return false; + } + + public override bool Visit(DoWhileStatement node) + { + return false; + } + + public override bool Visit(ElementaryTypeName node) + { + return false; + } + + public override bool Visit(ElementaryTypeNameExpression node) + { + return false; + } + + public override bool Visit(EmitStatement node) + { + return false; + } + + public override bool Visit(EnumDefinition node) + { + return false; + } + + public override bool Visit(UsingForDirective node) + { + return false; + } + + public override bool Visit(ImportDirective node) + { + return false; + } + + public override bool Visit(InheritanceSpecifier node) + { + return false; + } + + public override bool Visit(EnumValue node) + { + return false; + } + + public override bool Visit(EventDefinition node) + { + return false; + } + + public override bool Visit(ExpressionStatement node) + { + return false; + } + + public override bool Visit(ForStatement node) + { + return false; + } + + public FunctionDefinition findFn(string fnName, bool usesSuper) + { + List searchContracts = new List(curContract.LinearizedBaseContracts); + + if (!usesSuper) + { + searchContracts.Insert(0, curContract.Id); + } + + foreach (int id in searchContracts) + { + ContractDefinition def = context.IdToNodeMap[id] as ContractDefinition; + HashSet < FunctionDefinition > fnDefs = context.ContractToFunctionsMap[def]; + Dictionary nameToFn = fnDefs.ToDictionary(v => v.Name, v => v); + if (nameToFn.ContainsKey(fnName)) + { + return nameToFn[fnName]; + } + } + + return null; + } + + public override bool Visit(FunctionCall node) + { + if (node.Expression is Identifier call) + { + FunctionDefinition def = findFn(call.Name, false); + if (def != null && def.ReturnParameters.Parameters.Count != 0) + { + def.Body.Accept(this); + } + } + else if (node.Expression is MemberAccess access) + { + if (access.Expression is Identifier ident && ident.Name.Equals("super")) + { + FunctionDefinition def = findFn(access.MemberName, true); + if (def != null && def.ReturnParameters.Parameters.Count != 0) + { + def.Body.Accept(this); + } + } + } + + return false; + } + + public override bool Visit(FunctionDefinition node) + { + return false; + } + + public override bool Visit(Identifier node) + { + int id = node.ReferencedDeclaration; + VariableDeclaration varDecl = context.IdToNodeMap[id] as VariableDeclaration; + + if (varDecl.StateVariable) + { + retDecl = varDecl; + return false; + } + + findVar = node.Name; + return false; + } + + public override bool Visit(ParameterList node) + { + return false; + } + + public override bool Visit(ModifierDefinition node) + { + return false; + } + + public override bool Visit(IfStatement node) + { + return false; + } + + public override bool Visit(ModifierInvocation node) + { + return false; + } + + public override bool Visit(StructDefinition node) + { + return false; + } + + public override bool Visit(IndexAccess node) + { + node.BaseExpression.Accept(this); + return false; + } + + public override bool Visit(VariableDeclaration node) + { + return false; + } + + public override bool Visit(UserDefinedTypeName node) + { + return false; + } + + public override bool Visit(InlineAssembly node) + { + return false; + } + + public override bool Visit(Literal node) + { + return false; + } + + public override bool Visit(Mapping node) + { + return false; + } + + public override bool Visit(MemberAccess node) + { + return false; + } + + public override bool Visit(NewExpression node) + { + return false; + } + + public override bool Visit(PlaceholderStatement node) + { + return false; + } + + public override bool Visit(WhileStatement node) + { + return false; + } + + public override bool Visit(Return node) + { + node.Expression.Accept(this); + return false; + } + + public override bool Visit(SourceUnitList node) + { + return false; + } + + public override bool Visit(Throw node) + { + return false; + } + + public override bool Visit(UnaryOperation node) + { + return false; + } + + public override bool Visit(TupleExpression node) + { + return false; + } + + public override bool Visit(VariableDeclarationStatement node) + { + return false; + } + } + } +} \ No newline at end of file diff --git a/Sources/SolToBoogie/ParseUtils.cs b/Sources/SolToBoogie/ParseUtils.cs index 53aba30f..dc6e233c 100644 --- a/Sources/SolToBoogie/ParseUtils.cs +++ b/Sources/SolToBoogie/ParseUtils.cs @@ -207,6 +207,11 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, translatorFlags.GenerateGetters = true; } + if (args.Any(x => x.Equals("/generateERC20Spec"))) + { + translatorFlags.GenerateERC20Spec = true; + } + translatorFlags.PerformContractInferce = args.Any(x => x.StartsWith("/contractInfer")); // don't perform verification for some of these omitFlags diff --git a/Sources/SolToBoogie/TranslatorFlags.cs b/Sources/SolToBoogie/TranslatorFlags.cs index 05b71bc9..5c346d53 100644 --- a/Sources/SolToBoogie/TranslatorFlags.cs +++ b/Sources/SolToBoogie/TranslatorFlags.cs @@ -41,6 +41,7 @@ public TranslatorFlags() LazyNestedAlloc = false; PrePostHarness = false; GenerateGetters = false; + GenerateERC20Spec = false; } public bool UseNumericOperators { get; set; } @@ -135,5 +136,7 @@ public TranslatorFlags() public bool UseMultiDim { get; set; } public bool GenerateGetters { get; set; } + + public bool GenerateERC20Spec { get; set; } } } From d51dd65f77ba33cdb6cfa472f22646313532d54f Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Fri, 25 Sep 2020 09:50:00 -0700 Subject: [PATCH 27/49] Making minor modifications to the spec generation --- Sources/SolToBoogie/ERC20SpecGenerator.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Sources/SolToBoogie/ERC20SpecGenerator.cs b/Sources/SolToBoogie/ERC20SpecGenerator.cs index 7007cf81..34170256 100644 --- a/Sources/SolToBoogie/ERC20SpecGenerator.cs +++ b/Sources/SolToBoogie/ERC20SpecGenerator.cs @@ -80,11 +80,24 @@ public ERC20SpecGenerator(TranslatorContext context, AST solidityAST, String ent public void GenerateSpec() { - StreamWriter writer = new StreamWriter("__ERC20__.config"); + String filename = context.ASTNodeToSourcePathMap[entryContract]; + StreamWriter writer = new StreamWriter($"{filename.Substring(0, filename.Length - 4)}.config"); string totSupply = varDecls.ContainsKey("totalSupply") ? varDecls["totalSupply"].Name : ""; + if (String.IsNullOrEmpty(totSupply)) + { + Console.WriteLine("Warning: Could not find totalSupply variable"); + } string bal = varDecls.ContainsKey("balanceOf") ? varDecls["balanceOf"].Name : ""; + if (String.IsNullOrEmpty(bal)) + { + Console.WriteLine("Warning: Could not find balance variable"); + } string allowances = varDecls.ContainsKey("allowance") ? varDecls["allowance"].Name : ""; + if (String.IsNullOrEmpty(allowances)) + { + Console.WriteLine("Warning: Could not find allowances variable"); + } string totContract = fnContracts.ContainsKey("totalSupply") ? fnContracts["totalSupply"].Name : ""; string balContract = fnContracts.ContainsKey("balanceOf") ? fnContracts["balanceOf"].Name : ""; string allowanceContract = fnContracts.ContainsKey("allowance") ? fnContracts["allowance"].Name : ""; @@ -94,7 +107,7 @@ public void GenerateSpec() fnContracts.ContainsKey("transferFrom") ? fnContracts["transferFrom"].Name : ""; string extraVars = String.Join(" ", otherVars.Select(v => v.Name)); - writer.WriteLine($"FILE_NAME={context.ASTNodeToSourcePathMap[entryContract]}"); + writer.WriteLine($"FILE_NAME={filename}"); writer.WriteLine($"CONTRACT_NAME={entryContract.Name}"); writer.WriteLine($"TOTAL={totSupply}"); writer.WriteLine($"BALANCES={bal}"); From 9f5cce7502751302ef99a72167479d2441b35615 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Sun, 27 Sep 2020 21:21:08 -0700 Subject: [PATCH 28/49] Fixing bugs --- Sources/SolToBoogie/ERC20SpecGenerator.cs | 5 +++ Sources/SolToBoogie/FunctionEventCollector.cs | 2 +- .../SolToBoogie/GhostVarAndAxiomGenerator.cs | 10 +++++- Sources/SolToBoogie/MapArrayHelper.cs | 16 ++++++++- Sources/SolToBoogie/ProcedureTranslator.cs | 33 +++++++++++++++++-- Sources/SolidityAST/SolidityCompiler.cs | 12 +++---- Sources/SolidityAnalysis/AliasAnalysis.cs | 11 +++++-- 7 files changed, 75 insertions(+), 14 deletions(-) diff --git a/Sources/SolToBoogie/ERC20SpecGenerator.cs b/Sources/SolToBoogie/ERC20SpecGenerator.cs index 34170256..1df864f8 100644 --- a/Sources/SolToBoogie/ERC20SpecGenerator.cs +++ b/Sources/SolToBoogie/ERC20SpecGenerator.cs @@ -138,6 +138,11 @@ public ReturnDeclarationFinder(TranslatorContext context) public VariableDeclaration findDecl(ContractDefinition curContract, FunctionDefinition def) { + if (def.Body == null) + { + return null; + } + if (def.ReturnParameters.Parameters.Count != 1) { return null; diff --git a/Sources/SolToBoogie/FunctionEventCollector.cs b/Sources/SolToBoogie/FunctionEventCollector.cs index 471bf568..cb5d5308 100644 --- a/Sources/SolToBoogie/FunctionEventCollector.cs +++ b/Sources/SolToBoogie/FunctionEventCollector.cs @@ -96,12 +96,12 @@ public FunctionDefinition GenerateGetter(VariableDeclaration varDecl) paramDecl.StorageLocation = EnumLocation.DEFAULT; paramDecl.Id = id; context.IdToNodeMap.Add(id, paramDecl); + fnParams.Parameters.Add(paramDecl); if (curType is Mapping map) { paramDecl.TypeName = map.KeyType; paramDecl.TypeDescriptions = map.KeyType.TypeDescriptions; - fnParams.Parameters.Add(paramDecl); curType = map.ValueType; } else if (curType is ArrayTypeName arr) diff --git a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs index 364baeae..ffe1c8dd 100644 --- a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs +++ b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs @@ -45,6 +45,7 @@ private void GenerateFunctions() context.Program.AddDeclaration(GenerateAbiEncodedFunctionOneArgRef()); context.Program.AddDeclaration(GenerateAbiEncodedFunctionTwoArgsOneRef()); + HashSet addedDecls = new HashSet(); if (context.TranslateFlags.QuantFreeAllocs) { if (context.TranslateFlags.UseMultiDim) @@ -54,7 +55,14 @@ private void GenerateFunctions() TypeName type = decl.TypeName; if (type is Mapping || type is ArrayTypeName) { - context.Program.AddDeclaration(MapArrayHelper.GenerateMultiDimZeroFunction(decl)); + BoogieFunction initFn = MapArrayHelper.GenerateMultiDimZeroFunction(decl); + + if (!addedDecls.Contains(initFn.Name)) + { + context.Program.AddDeclaration(initFn); + addedDecls.Add(initFn.Name); + } + } } } diff --git a/Sources/SolToBoogie/MapArrayHelper.cs b/Sources/SolToBoogie/MapArrayHelper.cs index e9ffb88d..a91079ce 100644 --- a/Sources/SolToBoogie/MapArrayHelper.cs +++ b/Sources/SolToBoogie/MapArrayHelper.cs @@ -303,6 +303,20 @@ public static BoogieFunction GenerateMultiDimZeroFunction(VariableDeclaration de var smtDefinedAttr = new BoogieAttribute("smtdefined", $"\"{smtInit}\""); return new BoogieFunction($"zero{fnName}", new List(), new List() {outVar}, new List() {smtDefinedAttr}); } + + public static BoogieFuncCallExpr GetCallExprForZeroInit(BoogieType curType) + { + string fnName = "zero"; + + while (curType is BoogieMapType map) + { + fnName = $"{fnName}{String.Join("", map.Arguments)}"; + curType = map.Result; + } + + fnName = $"{fnName}{curType}Arr"; + return new BoogieFuncCallExpr(fnName, new List()); + } public static BoogieFuncCallExpr GetCallExprForZeroInit(TypeName curType) { @@ -390,7 +404,7 @@ public List GetMultiDimArrayLens(VariableDeclaration decl) if (curType is Mapping map) { curType = map.ValueType; - indTypes.Add(TransUtils.GetBoogieTypeFromSolidityTypeName(map.ValueType)); + indTypes.Add(TransUtils.GetBoogieTypeFromSolidityTypeName(map.KeyType)); } else if (curType is ArrayTypeName arr) { diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index e9905682..60a5e403 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -802,15 +802,17 @@ public BoogieStmtList GetMultiDimInitialization(VariableDeclaration decl, Boogie TypeName type = decl.TypeName; int curLvl = 0; + List indTypes = new List() {}; while (type is Mapping || type is ArrayTypeName) { if (type is Mapping map) { type = map.ValueType; + indTypes.Add(TransUtils.GetBoogieTypeFromSolidityTypeName(map.KeyType)); } else if (type is ArrayTypeName arr) { - ArrayTypeName lenType = new ArrayTypeName(); + /*ArrayTypeName lenType = new ArrayTypeName(); ElementaryTypeName intType = new ElementaryTypeName(); intType.TypeDescriptions = new TypeDescription(); intType.TypeDescriptions.TypeString = "uint"; @@ -825,9 +827,36 @@ public BoogieStmtList GetMultiDimInitialization(VariableDeclaration decl, Boogie string lenName = MapArrayHelper.GetMultiDimLengthName(decl, curLvl); BoogieMapSelect lenAccess = new BoogieMapSelect(new BoogieIdentifierExpr(lenName), contractVar.Arguments); + init.AddStatement(new BoogieAssignCmd(lenAccess, lenZero));*/ + + if (arr.Length != null) + { + throw new Exception("Must add support for static arrays"); + } + + BoogieType lenType = BoogieType.Int; + for (int i = indTypes.Count - 1; i >= 0; i--) + { + lenType = new BoogieMapType(indTypes[i], lenType); + } + + String lenName = MapArrayHelper.GetMultiDimLengthName(decl, curLvl); + BoogieMapSelect lenAccess = new BoogieMapSelect(new BoogieIdentifierExpr(lenName), contractVar.Arguments); + + BoogieExpr lenZero = null; + if (lenType is BoogieMapType) + { + lenZero = MapArrayHelper.GetCallExprForZeroInit(lenType); + } + else + { + lenZero = new BoogieLiteralExpr(BigInteger.Zero); + } + init.AddStatement(new BoogieAssignCmd(lenAccess, lenZero)); type = arr.BaseType; + indTypes.Add(BoogieType.Int); } curLvl++; @@ -2555,7 +2584,7 @@ private BoogieExpr TranslateNumberToExpr(Literal node) } else { - num = BigInteger.Parse(node.Value); + num = BigInteger.Parse(node.Value, NumberStyles.AllowExponent); } //if (node.TypeDescriptions.IsAddress()) diff --git a/Sources/SolidityAST/SolidityCompiler.cs b/Sources/SolidityAST/SolidityCompiler.cs index 6721b3f1..2e3972f4 100644 --- a/Sources/SolidityAST/SolidityCompiler.cs +++ b/Sources/SolidityAST/SolidityCompiler.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. namespace SolidityAST { @@ -41,10 +41,10 @@ public CompilerOutput Compile(string solcPath, string derivedFilePath) return compilerOutput; } - /// - /// - /// - /// Path to the top-level solidty file + /// + /// + /// + /// Path to the top-level solidty file /// private string RunSolc(string solcPath, string derivedFilePath) { @@ -62,7 +62,7 @@ private string RunSolc(string solcPath, string derivedFilePath) p.Start(); string configString = "{ \"language\": \"Solidity\", \"sources\": { %SOLPLACEHOLDER%: { \"urls\": [ %URLPLACEHOLDER% ]}}," - + "\"settings\": {\"evmVersion\": \"byzantium\", \"outputSelection\": {\"*\": {\"\": [ \"ast\" ]}}}}"; + + "\"settings\": {\"evmVersion\": \"constantinople\", \"outputSelection\": {\"*\": {\"\": [ \"ast\" ]}}}}"; configString = configString.Replace("%SOLPLACEHOLDER%", "\"" + derivedFileName + "\"" /*, StringComparison.CurrentCulture*/); configString = configString.Replace("%URLPLACEHOLDER%", "\"" + derivedFilePath + "\""/*, StringComparison.CurrentCulture*/); diff --git a/Sources/SolidityAnalysis/AliasAnalysis.cs b/Sources/SolidityAnalysis/AliasAnalysis.cs index fbfa4fcf..80b47f8f 100644 --- a/Sources/SolidityAnalysis/AliasAnalysis.cs +++ b/Sources/SolidityAnalysis/AliasAnalysis.cs @@ -10,7 +10,7 @@ namespace SolidityAnalysis public class AliasAnalysis : BasicASTVisitor { private List results; - private Dictionary nameToStruct; + //private Dictionary nameToStruct; private AST solidityAST; private HashSet> ignoredMethods; private String entryPoint; @@ -20,7 +20,7 @@ public AliasAnalysis(AST solidityAST, HashSet> ignoredMeth this.solidityAST = solidityAST; this.ignoredMethods = ignoredMethods; this.entryPoint = entryPointContract; - this.nameToStruct = new Dictionary(); + //this.nameToStruct = new Dictionary(); this.results = null; } @@ -52,7 +52,7 @@ public String getGroupName(VariableDeclaration varDec) public override bool Visit(StructDefinition def) { - nameToStruct.Add(def.Name, def); + //nameToStruct.Add(def.Name, def); return true; } @@ -134,6 +134,11 @@ public int getIndexDimSize(TypeName ty) public override bool Visit(IndexAccess access) { + if (access.BaseExpression.ToString().Equals("msg.data")) + { + return false; + } + DeclarationFinder declFinder = new DeclarationFinder(access, solidityAST); VariableDeclaration decl = declFinder.getDecl(); From 1e1f3724883fd19d97cf31e7b5c319209ff66871 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Fri, 2 Oct 2020 14:41:25 -0700 Subject: [PATCH 29/49] Fixing bugs. Adding new method to model inline assembly that havoc's all written variables --- Sources/SolToBoogie/BoogieTranslator.cs | 7 + Sources/SolToBoogie/ERC20SpecGenerator.cs | 8 +- Sources/SolToBoogie/FallbackGenerator.cs | 16 +- Sources/SolToBoogie/FunctionCallHelper.cs | 218 +++++++++++ .../FunctionDependencyCollector.cs | 153 ++++++++ Sources/SolToBoogie/ParseUtils.cs | 17 + Sources/SolToBoogie/ProcedureTranslator.cs | 343 ++++++------------ Sources/SolToBoogie/TransUtils.cs | 6 + Sources/SolToBoogie/TranslatorContext.cs | 2 +- Sources/SolToBoogie/TranslatorFlags.cs | 14 + Sources/SolidityAST/SolidityAST.cs | 11 +- Sources/SolidityAST/SolidityCompiler.cs | 5 +- 12 files changed, 565 insertions(+), 235 deletions(-) create mode 100644 Sources/SolToBoogie/FunctionCallHelper.cs create mode 100644 Sources/SolToBoogie/FunctionDependencyCollector.cs diff --git a/Sources/SolToBoogie/BoogieTranslator.cs b/Sources/SolToBoogie/BoogieTranslator.cs index d848cecd..79f7b285 100644 --- a/Sources/SolToBoogie/BoogieTranslator.cs +++ b/Sources/SolToBoogie/BoogieTranslator.cs @@ -82,6 +82,13 @@ public BoogieAST Translate(AST solidityAST, HashSet> ignor UsingCollector usingCollector = new UsingCollector(context); sourceUnits.Accept(usingCollector); + if (context.TranslateFlags.PerformFunctionSlice) + { + FunctionDependencyCollector depCollector = new FunctionDependencyCollector(context, entryPointContract, context.TranslateFlags.SliceFunctionNames); + context.TranslateFlags.SliceFunctions = depCollector.GetFunctionDeps(); + context.TranslateFlags.SliceModifiers = depCollector.getModifierDeps(); + } + // translate procedures ProcedureTranslator procTranslator = new ProcedureTranslator(context, mapHelper, generateInlineAttributesInBpl); sourceUnits.Accept(procTranslator); diff --git a/Sources/SolToBoogie/ERC20SpecGenerator.cs b/Sources/SolToBoogie/ERC20SpecGenerator.cs index 1df864f8..7b5200f4 100644 --- a/Sources/SolToBoogie/ERC20SpecGenerator.cs +++ b/Sources/SolToBoogie/ERC20SpecGenerator.cs @@ -83,17 +83,17 @@ public void GenerateSpec() String filename = context.ASTNodeToSourcePathMap[entryContract]; StreamWriter writer = new StreamWriter($"{filename.Substring(0, filename.Length - 4)}.config"); - string totSupply = varDecls.ContainsKey("totalSupply") ? varDecls["totalSupply"].Name : ""; + string totSupply = varDecls.ContainsKey("totalSupply") ? $"this.{varDecls["totalSupply"].Name}" : ""; if (String.IsNullOrEmpty(totSupply)) { Console.WriteLine("Warning: Could not find totalSupply variable"); } - string bal = varDecls.ContainsKey("balanceOf") ? varDecls["balanceOf"].Name : ""; + string bal = varDecls.ContainsKey("balanceOf") ? $"this.{varDecls["balanceOf"].Name}" : ""; if (String.IsNullOrEmpty(bal)) { Console.WriteLine("Warning: Could not find balance variable"); } - string allowances = varDecls.ContainsKey("allowance") ? varDecls["allowance"].Name : ""; + string allowances = varDecls.ContainsKey("allowance") ? $"this.{varDecls["allowance"].Name}" : ""; if (String.IsNullOrEmpty(allowances)) { Console.WriteLine("Warning: Could not find allowances variable"); @@ -105,7 +105,7 @@ public void GenerateSpec() string transferContract = fnContracts.ContainsKey("transfer") ? fnContracts["transfer"].Name : ""; string transferFromContract = fnContracts.ContainsKey("transferFrom") ? fnContracts["transferFrom"].Name : ""; - string extraVars = String.Join(" ", otherVars.Select(v => v.Name)); + string extraVars = String.Join(" ", otherVars.Select(v => $"this.{v.Name}")); writer.WriteLine($"FILE_NAME={filename}"); writer.WriteLine($"CONTRACT_NAME={entryContract.Name}"); diff --git a/Sources/SolToBoogie/FallbackGenerator.cs b/Sources/SolToBoogie/FallbackGenerator.cs index b5077fd4..112f3da2 100644 --- a/Sources/SolToBoogie/FallbackGenerator.cs +++ b/Sources/SolToBoogie/FallbackGenerator.cs @@ -283,12 +283,18 @@ private BoogieStmtList GenerateBodyOfFallbackDispatch(List inPar if (function != null) { + if (context.TranslateFlags.PerformFunctionSlice && + !context.TranslateFlags.SliceFunctions.Contains(function)) + { + continue; + } + List arguments = new List() - { - new BoogieIdentifierExpr(inParams[1].Name), - new BoogieIdentifierExpr(inParams[0].Name), - new BoogieIdentifierExpr(inParams[2].Name) - }; + { + new BoogieIdentifierExpr(inParams[1].Name), + new BoogieIdentifierExpr(inParams[0].Name), + new BoogieIdentifierExpr(inParams[2].Name) + }; string callee = TransUtils.GetCanonicalFunctionName(function, context); // to.fallback(from, amount), thus need to switch param0 and param1 diff --git a/Sources/SolToBoogie/FunctionCallHelper.cs b/Sources/SolToBoogie/FunctionCallHelper.cs new file mode 100644 index 00000000..c325da8c --- /dev/null +++ b/Sources/SolToBoogie/FunctionCallHelper.cs @@ -0,0 +1,218 @@ +using System; +using SolidityAST; + +namespace SolToBoogie +{ + public class FunctionCallHelper + { + public static bool IsDynamicDispatching(FunctionCall node) + { + return node.Expression is Identifier; + } + + public static bool IsTypeCast(FunctionCall node) + { + return node.Kind.Equals("typeConversion"); + } + + public static bool IsStaticDispatching(TranslatorContext context, FunctionCall node) + { + if (node.Expression is MemberAccess memberAccess) + { + if (memberAccess.Expression is Identifier ident) + { + if (context.GetASTNodeById(ident.ReferencedDeclaration) is ContractDefinition) + { + return true; + } + } + } + return false; + } + + public static ContractDefinition GetStaticDispatchingContract(TranslatorContext context, FunctionCall node) + { + VeriSolAssert(node.Expression is MemberAccess); + MemberAccess memberAccess = node.Expression as MemberAccess; + + Identifier contractId = memberAccess.Expression as Identifier; + VeriSolAssert(contractId != null, $"Unknown contract name: {memberAccess.Expression}"); + + ContractDefinition contract = context.GetASTNodeById(contractId.ReferencedDeclaration) as ContractDefinition; + VeriSolAssert(contract != null); + return contract; + } + + public static bool IsUsingBasedLibraryCall(MemberAccess memberAccess) + { + // since we only permit "using A for B" for non-contract types + // this is sufficient, but not necessary in general since non + // contracts (including libraries) do not have support methods + return !memberAccess.Expression.TypeDescriptions.IsContract(); + } + + public static ContractDefinition IsLibraryFunctionCall(TranslatorContext context, FunctionCall node) + { + if (node.Expression is MemberAccess memberAccess) + { + if (memberAccess.Expression is Identifier identifier) + { + var contract = context.GetASTNodeById(identifier.ReferencedDeclaration) as ContractDefinition; + // a Library is treated as an external function call + // we need to do it here as the Lib.Foo, Lib is not an expression but name of a contract + if (contract.ContractKind == EnumContractKind.LIBRARY) + { + return contract; + } + } + } + return null; + } + + public static bool IsExternalFunctionCall(TranslatorContext context, FunctionCall node) + { + if (node.Expression is MemberAccess memberAccess) + { + if (memberAccess.Expression is Identifier identifier) + { + if (identifier.Name == "this") + { + return true; + } + + if (identifier.Name == "super") + { + return true; + } + var contract = context.GetASTNodeById(identifier.ReferencedDeclaration) as ContractDefinition; + if (contract == null) + { + return true; + } + } + else if (memberAccess.Expression is MemberAccess structSelect) + { + //a.b.c.foo(...) + //TODO: do we want to check that the contract the struct variable is declared + // is not in the "context"? Why this isn't done for IndexAccess? + return true; + } + else if (memberAccess.Expression.ToString().Equals("msg.sender")) + { + // calls can be of the form "msg.sender.call()" or "msg.sender.send()" or "msg.sender.transfer()" + return true; + } + else if (memberAccess.Expression is FunctionCall) + { + // TODO: example? + return true; + } else if (memberAccess.Expression is IndexAccess) + { + //a[i].foo(..) + return true; + } + } + return false; + } + + public static bool IsImplicitFunc(FunctionCall node) + { + return + IsKeccakFunc(node) || + IsAbiEncodePackedFunc(node) || + IsTypeCast(node) || + IsStructConstructor(node) || + IsContractConstructor(node); + } + + public static bool IsContractConstructor(FunctionCall node) + { + return node.Expression is NewExpression; + } + + public static bool IsStructConstructor(FunctionCall node) + { + return node.Kind.Equals("structConstructorCall"); + } + + public static bool IsKeccakFunc(FunctionCall node) + { + if (node.Expression is Identifier ident) + { + return ident.Name.Equals("keccak256"); + } + return false; + } + + public static bool IsBuiltInTransferFunc(string functionName, FunctionCall node) + { + if (!functionName.Equals("transfer")) return false; + if (node.Expression is MemberAccess member) + { + if (member.Expression.TypeDescriptions.IsAddress()) + return true; + } + return false; + } + + public static bool IsAbiEncodePackedFunc(FunctionCall node) + { + if (node.Expression is MemberAccess member) + { + if (member.Expression is Identifier ident) + { + if (ident.Name.Equals("abi")) + { + if (member.MemberName.Equals("encodePacked")) + return true; + } + } + } + return false; + } + + public static bool IsAssert(FunctionCall node) + { + var functionName = TransUtils.GetFuncNameFromFuncCall(node); + return functionName.Equals("assert"); + } + + public static bool IsRequire(FunctionCall node) + { + var functionName = TransUtils.GetFuncNameFromFuncCall(node); + return functionName.Equals("require"); + } + + public static bool IsRevert(FunctionCall node) + { + var functionName = TransUtils.GetFuncNameFromFuncCall(node); + return functionName.Equals("revert"); + } + + public static bool IsLowLevelCall(FunctionCall node) + { + var functionName = TransUtils.GetFuncNameFromFuncCall(node); + return functionName.Equals("call"); + } + + public static bool isDelegateCall(FunctionCall node) + { + var functionName = TransUtils.GetFuncNameFromFuncCall(node); + return functionName.Equals("delegatecall"); + } + + public static bool isSend(FunctionCall node) + { + var functionName = TransUtils.GetFuncNameFromFuncCall(node); + return functionName.Equals("send"); + } + + private static void VeriSolAssert(bool cond, string message = "") + { + if (!cond) + { + throw new Exception ($"{message}...."); + } + } + } +} \ No newline at end of file diff --git a/Sources/SolToBoogie/FunctionDependencyCollector.cs b/Sources/SolToBoogie/FunctionDependencyCollector.cs new file mode 100644 index 00000000..8ad6855a --- /dev/null +++ b/Sources/SolToBoogie/FunctionDependencyCollector.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using SolidityAST; + +namespace SolToBoogie +{ + public class FunctionDependencyCollector : BasicASTVisitor + { + private TranslatorContext context; + private HashSet fnDeps; + private HashSet modDeps; + private Dictionary> nameToFn; + private HashSet seenFns; + private FunctionDefinition curFn; + private LinkedList worklist; + + + + public FunctionDependencyCollector(TranslatorContext context, String baseContractName, HashSet fnNames) + { + this.context = context; + nameToFn = new Dictionary>(); + fnDeps = new HashSet(); + modDeps = new HashSet(); + seenFns = new HashSet(); + worklist = new LinkedList(); + ContractDefinition baseContract = null; + + foreach (ContractDefinition def in context.ContractDefinitions) + { + if (def.Name.Equals(baseContractName)) + { + baseContract = def; + } + + if (context.ContractToFunctionsMap.ContainsKey(def)) + { + foreach (FunctionDefinition fnDef in context.ContractToFunctionsMap[def]) + { + if (nameToFn.ContainsKey(fnDef.Name)) + { + nameToFn[fnDef.Name].Add(fnDef); + } + else + { + nameToFn[fnDef.Name] = new List() {fnDef}; + } + } + } + } + + foreach (FunctionDefinition fnDef in context.GetVisibleFunctionsByContract(baseContract)) + { + if (fnNames.Contains(fnDef.Name)) + { + worklist.AddLast(fnDef); + seenFns.Add(fnDef); + } + } + + while (worklist.Count != 0) + { + curFn = worklist.First.Value; + worklist.RemoveFirst(); + fnDeps.Add(curFn); + + if (curFn.Modifiers != null) + { + foreach (ModifierInvocation mod in curFn.Modifiers) + { + int id = mod.ModifierName.ReferencedDeclaration; + ASTNode node = context.GetASTNodeById(id); + + if (node is ModifierDefinition modDef) + { + if (!modDeps.Contains(modDef)) + { + modDeps.Add(modDef); + modDef.Body.Accept(this); + } + } + else + { + throw new Exception("Modifier id does not reference a modifier declaration"); + } + } + } + + if (curFn.Body != null) + { + curFn.Body.Accept(this); + } + } + } + + public bool IsDependent(FunctionDefinition dep) + { + return fnDeps.Contains(dep); + } + + public HashSet GetFunctionDeps() + { + return fnDeps; + } + + public HashSet getModifierDeps() + { + return modDeps; + } + + private bool isBuiltinFn(FunctionCall call) + { + return FunctionCallHelper.IsLowLevelCall(call) || FunctionCallHelper.isSend(call) || + FunctionCallHelper.IsAssert(call) || FunctionCallHelper.isDelegateCall(call) || + FunctionCallHelper.IsRequire(call) || FunctionCallHelper.IsRevert(call) || + FunctionCallHelper.IsKeccakFunc(call) || FunctionCallHelper.IsAbiEncodePackedFunc(call) || + FunctionCallHelper.IsTypeCast(call) || FunctionCallHelper.IsBuiltInTransferFunc(TransUtils.GetFuncNameFromFuncCall(call), call); + } + + public override bool Visit(FunctionCall node) + { + if (FunctionCallHelper.IsLowLevelCall(node) || FunctionCallHelper.isDelegateCall(node)) + { + worklist.Clear(); + foreach (FunctionDefinition def in context.FunctionToContractMap.Keys) + { + fnDeps.Add(def); + } + } + else if (isBuiltinFn(node)) + { + return true; + } + else + { + string fnName = TransUtils.GetFuncNameFromFuncCall(node); + if (nameToFn.ContainsKey(fnName)) + { + foreach (FunctionDefinition fnDef in nameToFn[fnName]) + { + if (!seenFns.Contains(fnDef)) + { + worklist.AddLast(fnDef); + seenFns.Add(fnDef); + } + } + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/Sources/SolToBoogie/ParseUtils.cs b/Sources/SolToBoogie/ParseUtils.cs index dc6e233c..68fdae96 100644 --- a/Sources/SolToBoogie/ParseUtils.cs +++ b/Sources/SolToBoogie/ParseUtils.cs @@ -45,6 +45,18 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, var contract = str.Substring(str.IndexOf("@") + 1); ignoredMethods.Add(Tuple.Create(method, contract)); } + + foreach (var arg in args.Where(x => x.StartsWith("/SliceFunctions:"))) + { + var str = arg.Substring("/SliceFunctions:".Length); + String[] fns = str.Split(","); + translatorFlags.PerformFunctionSlice = true; + foreach (String fn in fns) + { + translatorFlags.SliceFunctionNames.Add(fn); + } + } + if (args.Any(x => x.StartsWith("/ignoreMethod:"))) { Console.WriteLine($"Ignored method/contract pairs ==> \n\t {string.Join(",", ignoredMethods.Select(x => x.Item1 + "@" + x.Item2))}"); @@ -212,6 +224,11 @@ public static void ParseCommandLineArgs(string[] args, out string solidityFile, translatorFlags.GenerateERC20Spec = true; } + if (args.Any(x => x.Equals("/modelAssemblyAsHavoc"))) + { + translatorFlags.AssemblyAsHavoc = true; + } + translatorFlags.PerformContractInferce = args.Any(x => x.StartsWith("/contractInfer")); // don't perform verification for some of these omitFlags diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 60a5e403..4bbf63c2 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -1,5 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. + +using Newtonsoft.Json.Linq; + namespace SolToBoogie { using System; @@ -451,6 +454,18 @@ private void PrintArguments(FunctionDefinition node, List inPara } public override bool Visit(FunctionDefinition node) { + if (context.TranslateFlags.PerformFunctionSlice) + { + /*if (!node.IsConstructor && !node.IsFallback && !context.TranslateFlags.SliceFunctions.Contains(node)) + { + return false; + } */ + if (!context.TranslateFlags.SliceFunctions.Contains(node)) + { + return false; + } + } + preTranslationAction(node); // VeriSolAssert(node.IsConstructor || node.Modifiers.Count <= 1, "Multiple Modifiers are not supported yet"); VeriSolAssert(currentContract != null); @@ -680,6 +695,14 @@ private bool IsVeriSolContractInvariantFunction(FunctionDefinition node, BoogieS public override bool Visit(ModifierDefinition node) { + if (context.TranslateFlags.PerformFunctionSlice) + { + if (!context.TranslateFlags.SliceModifiers.Contains(node)) + { + return false; + } + } + preTranslationAction(node); currentBoogieProc = node.Name + "_pre"; boogieToLocalVarsMap[currentBoogieProc] = new List(); @@ -1401,6 +1424,11 @@ private void GetBoogieTypesFromMapping(VariableDeclaration varDecl, Mapping mapp // TODO: refactor this code with the code to generate constructor code when definition is present private void GenerateDefaultConstructor(ContractDefinition contract) { + if (context.TranslateFlags.PerformFunctionSlice && context.TranslateFlags.PrePostHarness) + { + return; + } + // generate the internal one without base constructors string procName = contract.Name + "_" + contract.Name + "_NoBaseCtor"; currentBoogieProc = procName; @@ -1887,6 +1915,43 @@ public void updateAssignedSums(BoogieBinaryOperation.Opcode op, List } } + public BoogieStmtList performAssignment(Assignment node, BoogieExpr lhs, BoogieExpr rhs) + { + BoogieStmtList stmtList = new BoogieStmtList(); + + switch (node.Operator) + { + case "=": + stmtList.AddStatement(new BoogieAssignCmd(lhs, rhs)); + break; + case "+=": + BoogieExpr addExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, lhs, rhs); + addExpr = AddModuloOp(node.LeftHandSide, addExpr, node.LeftHandSide.TypeDescriptions); + stmtList.AddStatement(new BoogieAssignCmd(lhs, addExpr)); + break; + case "-=": + BoogieExpr subExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, lhs, rhs); + subExpr = AddModuloOp(node.LeftHandSide, subExpr, node.LeftHandSide.TypeDescriptions); + stmtList.AddStatement(new BoogieAssignCmd(lhs, subExpr)); + break; + case "*=": + BoogieExpr mulExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.MUL, lhs, rhs); + mulExpr = AddModuloOp(node.LeftHandSide, mulExpr, node.LeftHandSide.TypeDescriptions); + stmtList.AddStatement(new BoogieAssignCmd(lhs, mulExpr)); + break; + case "/=": + BoogieExpr divExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.DIV, lhs, rhs); + divExpr = AddModuloOp(node.LeftHandSide, divExpr, node.LeftHandSide.TypeDescriptions); + stmtList.AddStatement(new BoogieAssignCmd(lhs, divExpr)); + break; + default: + VeriSolAssert(false, $"Unknown assignment operator: {node.Operator}"); + break; + } + + return stmtList; + } + public override bool Visit(Assignment node) { preTranslationAction(node); @@ -1935,29 +2000,29 @@ public override bool Visit(Assignment node) // a Boolean to decide is we needed to use tmpVar bool usedTmpVar = true; - if (IsContractConstructor(funcCall)) + if (FunctionCallHelper.IsContractConstructor(funcCall)) { VeriSolAssert(!isTupleAssignment, "Not expecting a tuple for Constructors"); TranslateNewStatement(funcCall, tmpVars[0]); } - else if (IsStructConstructor(funcCall)) + else if (FunctionCallHelper.IsStructConstructor(funcCall)) { VeriSolAssert(!isTupleAssignment, "Not expecting a tuple for Constructors"); TranslateStructConstructor(funcCall, tmpVars[0]); } - else if (IsKeccakFunc(funcCall)) + else if (FunctionCallHelper.IsKeccakFunc(funcCall)) { VeriSolAssert(!isTupleAssignment, "Not expecting a tuple for Keccak256"); TranslateKeccakFuncCall(funcCall, lhs[0]); //this is not a procedure call in Boogie usedTmpVar = false; } - else if (IsAbiEncodePackedFunc(funcCall)) + else if (FunctionCallHelper.IsAbiEncodePackedFunc(funcCall)) { TranslateAbiEncodedFuncCall(funcCall, tmpVars[0]); //this is not a procedure call in Boogie VeriSolAssert(!isTupleAssignment, "Not expecting a tuple for abi.encodePacked"); usedTmpVar = false; } - else if (IsTypeCast(funcCall)) + else if (FunctionCallHelper.IsTypeCast(funcCall)) { // assume the type cast is used as: obj = C(var); VeriSolAssert(!isTupleAssignment, "Not expecting a tuple for type cast"); @@ -1978,8 +2043,7 @@ public override bool Visit(Assignment node) { if (usedTmpVar || !(lhs[0] is BoogieIdentifierExpr)) //bad bug: was && before!! { - VeriSolAssert(node.Operator.Equals("="), "I don't know why this is only doing assignment"); - currentStmtList.AddStatement(new BoogieAssignCmd(lhs[0], tmpVars[0])); + currentStmtList.AppendStmtList(performAssignment(node, lhs[0], tmpVars[0])); } } else { @@ -2005,43 +2069,14 @@ public override bool Visit(Assignment node) VeriSolAssert(false, "Not implemented...currently only support assignment of tuples as returns of a function call"); BoogieExpr rhs = TranslateExpr(node.RightHandSide); - BoogieStmtList stmtList = new BoogieStmtList(); - BoogieExpr assignedExpr = new BoogieExpr(); - switch (node.Operator) - { - case "=": - stmtList.AddStatement(new BoogieAssignCmd(lhs[0], rhs)); - break; - case "+=": - assignedExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, lhs[0], rhs); - assignedExpr = AddModuloOp(node.LeftHandSide, assignedExpr, node.LeftHandSide.TypeDescriptions); - stmtList.AddStatement(new BoogieAssignCmd(lhs[0], assignedExpr)); - break; - case "-=": - assignedExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, lhs[0], rhs); - assignedExpr = AddModuloOp(node.LeftHandSide, assignedExpr, node.LeftHandSide.TypeDescriptions); - stmtList.AddStatement(new BoogieAssignCmd(lhs[0], assignedExpr)); - break; - case "*=": - assignedExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.MUL, lhs[0], rhs); - assignedExpr = AddModuloOp(node.LeftHandSide, assignedExpr, node.LeftHandSide.TypeDescriptions); - stmtList.AddStatement(new BoogieAssignCmd(lhs[0], assignedExpr)); - break; - case "/=": - assignedExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.DIV, lhs[0], rhs); - assignedExpr = AddModuloOp(node.LeftHandSide, assignedExpr, node.LeftHandSide.TypeDescriptions); - stmtList.AddStatement(new BoogieAssignCmd(lhs[0], assignedExpr)); - break; - default: - VeriSolAssert(false, $"Unknown assignment operator: {node.Operator}"); - break; - } - + if (context.TranslateFlags.InstrumentSums) { updateAssignedSums(BoogieBinaryOperation.Opcode.SUB, lhsExprs, lhs); } - currentStmtList.AppendStmtList(stmtList); + + currentStmtList.AppendStmtList(performAssignment(node, lhs[0], rhs)); + if (context.TranslateFlags.InstrumentSums) { updateAssignedSums(BoogieBinaryOperation.Opcode.ADD, lhsExprs, lhs); @@ -2072,7 +2107,7 @@ private void TranslateInBuiltFunction(FunctionCall funcCall, BoogieExpr lhs) private void TranslateFunctionCalls(FunctionCall funcCall, List outParams) { - if (IsExternalFunctionCall(funcCall)) + if (FunctionCallHelper.IsExternalFunctionCall(context, funcCall)) { TranslateExternalFunctionCall(funcCall, outParams); } @@ -2085,8 +2120,12 @@ private void TranslateFunctionCalls(FunctionCall funcCall, List /// /// - private bool IsImplicitFunc(FunctionCall node) - { - return - IsKeccakFunc(node) || - IsAbiEncodePackedFunc(node) || - IsTypeCast(node) || - IsStructConstructor(node) || - IsContractConstructor(node); - } - - private bool IsContractConstructor(FunctionCall node) - { - return node.Expression is NewExpression; - } - - private bool IsStructConstructor(FunctionCall node) - { - return node.Kind.Equals("structConstructorCall"); - } - - private bool IsKeccakFunc(FunctionCall node) - { - if (node.Expression is Identifier ident) - { - return ident.Name.Equals("keccak256"); - } - return false; - } - - private bool IsAbiEncodePackedFunc(FunctionCall node) - { - if (node.Expression is MemberAccess member) - { - if (member.Expression is Identifier ident) - { - if (ident.Name.Equals("abi")) - { - if (member.MemberName.Equals("encodePacked")) - return true; - } - } - } - return false; - } private void TranslateKeccakFuncCall(FunctionCall funcCall, BoogieExpr lhs) { @@ -3438,70 +3422,6 @@ private void AddUnsignedTypeAssumeCmd(BoogieIdentifierExpr v) currentStmtList.AddStatement(assumeCmd); } - private bool IsExternalFunctionCall(FunctionCall node) - { - if (node.Expression is MemberAccess memberAccess) - { - if (memberAccess.Expression is Identifier identifier) - { - if (identifier.Name == "this") - { - return true; - } - - if (identifier.Name == "super") - { - return true; - } - var contract = context.GetASTNodeById(identifier.ReferencedDeclaration) as ContractDefinition; - if (contract == null) - { - return true; - } - } - else if (memberAccess.Expression is MemberAccess structSelect) - { - //a.b.c.foo(...) - //TODO: do we want to check that the contract the struct variable is declared - // is not in the "context"? Why this isn't done for IndexAccess? - return true; - } - else if (memberAccess.Expression.ToString().Equals("msg.sender")) - { - // calls can be of the form "msg.sender.call()" or "msg.sender.send()" or "msg.sender.transfer()" - return true; - } - else if (memberAccess.Expression is FunctionCall) - { - // TODO: example? - return true; - } else if (memberAccess.Expression is IndexAccess) - { - //a[i].foo(..) - return true; - } - } - return false; - } - - private ContractDefinition IsLibraryFunctionCall(FunctionCall node) - { - if (node.Expression is MemberAccess memberAccess) - { - if (memberAccess.Expression is Identifier identifier) - { - var contract = context.GetASTNodeById(identifier.ReferencedDeclaration) as ContractDefinition; - // a Library is treated as an external function call - // we need to do it here as the Lib.Foo, Lib is not an expression but name of a contract - if (contract.ContractKind == EnumContractKind.LIBRARY) - { - return contract; - } - } - } - return null; - } - private void TranslateExternalFunctionCall(FunctionCall node, List outParams = null) { VeriSolAssert(node.Expression is MemberAccess, $"Expecting a member access expression here {node.Expression.ToString()}"); @@ -3517,13 +3437,13 @@ private void TranslateExternalFunctionCall(FunctionCall node, List outParams = null) { List arguments = TransUtils.GetDefaultArguments(); // a Library is treated as an external function call // we need to do it here as the Lib.Foo, Lib is not an expression but name of a contract - if (IsLibraryFunctionCall(node) != null) + if (FunctionCallHelper.IsLibraryFunctionCall(context, node) != null) { arguments[1] = arguments[0]; //msg.sender is also this } @@ -3685,13 +3597,13 @@ private void TranslateInternalFunctionCall(FunctionCall node, List /// @@ -4534,6 +4408,31 @@ public override bool Visit(IndexAccess node) public override bool Visit(InlineAssembly node) { preTranslationAction(node); + + if (context.TranslateFlags.AssemblyAsHavoc) + { + this.currentStmtList.AddStatement(new BoogieCommentCmd("---- modeling inline assembly ----")); + string ops = node.Operations; + foreach(Dictionary refs in node.ExternalReferences) + { + foreach (ExternalReference extRef in refs.Values) + { + VariableDeclaration varDecl = context.GetASTNodeById(extRef.declaration) as VariableDeclaration; + Regex assignChk = new Regex($"{varDecl.Name}([^a-zA-Z_0-9].*:=|:=)"); + Regex storeCheck = new Regex(@"store.?\s*\(\s*" + varDecl.Name + @"_slot\s*,"); + if (assignChk.IsMatch(ops) || storeCheck.IsMatch(ops)) + { + string boogieVar = TransUtils.GetCanonicalVariableName(varDecl, context); + BoogieHavocCmd varHavoc = new BoogieHavocCmd(new BoogieIdentifierExpr(boogieVar)); + this.currentStmtList.AddStatement(varHavoc); + } + + } + } + + return false; + } + Console.WriteLine($"Warning: Inline assembly in function {currentFunction.Name}; replacing function result with non-det value"); throw new NotImplementedException("inline assembly"); } diff --git a/Sources/SolToBoogie/TransUtils.cs b/Sources/SolToBoogie/TransUtils.cs index b4a015bd..ac310a62 100644 --- a/Sources/SolToBoogie/TransUtils.cs +++ b/Sources/SolToBoogie/TransUtils.cs @@ -800,6 +800,12 @@ public static Tuple GeneratePartialChoiceBlock(List (source, dest), where source is a library type - private readonly Dictionary> usingMap; + private readonly Dictionary> usingMap; public TranslatorContext(AST solidityAST, HashSet> ignoreMethods, bool _genInlineAttrInBpl, TranslatorFlags _translateFlags = null, String entryPointContract = "") { diff --git a/Sources/SolToBoogie/TranslatorFlags.cs b/Sources/SolToBoogie/TranslatorFlags.cs index 5c346d53..6965ef50 100644 --- a/Sources/SolToBoogie/TranslatorFlags.cs +++ b/Sources/SolToBoogie/TranslatorFlags.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using SolidityAST; namespace SolToBoogie { @@ -42,8 +43,21 @@ public TranslatorFlags() PrePostHarness = false; GenerateGetters = false; GenerateERC20Spec = false; + SliceFunctionNames = new HashSet(); + SliceFunctions = new HashSet(); + SliceModifiers = new HashSet(); + PerformFunctionSlice = false; + AssemblyAsHavoc = false; } + + public bool PerformFunctionSlice { get; set; } + + public bool AssemblyAsHavoc { get; set; } + + public HashSet SliceModifiers { get; set; } + public HashSet SliceFunctions { get; set; } + public HashSet SliceFunctionNames { get; set; } public bool UseNumericOperators { get; set; } public bool NoNonlinearArith { get; set; } diff --git a/Sources/SolidityAST/SolidityAST.cs b/Sources/SolidityAST/SolidityAST.cs index ae892ee0..371726ad 100644 --- a/Sources/SolidityAST/SolidityAST.cs +++ b/Sources/SolidityAST/SolidityAST.cs @@ -1300,9 +1300,18 @@ public override string ToString() } } + public class ExternalReference + { + public int declaration { get; set; } + public bool isOffset { get; set; } + public bool isSlot { get; set; } + public String src { get; set; } + public int valueSize { get; set; } + } public class InlineAssembly : Statement { - public List ExternalReferences { get; set; } + //public List ExternalReferences { get; set; } + public List> ExternalReferences { get; set; } public string Operations { get; set; } diff --git a/Sources/SolidityAST/SolidityCompiler.cs b/Sources/SolidityAST/SolidityCompiler.cs index 2e3972f4..7edbeefb 100644 --- a/Sources/SolidityAST/SolidityCompiler.cs +++ b/Sources/SolidityAST/SolidityCompiler.cs @@ -20,7 +20,7 @@ public CompilerOutput Compile(string solcPath, string derivedFilePath) } derivedFilePath = derivedFilePath.Replace("\\", "/" /*, StringComparison.CurrentCulture*/); - + Console.WriteLine(solcPath); string jsonString = RunSolc(solcPath, derivedFilePath); List errors = new List(); @@ -33,6 +33,7 @@ public CompilerOutput Compile(string solcPath, string derivedFilePath) }, }; + CompilerOutput compilerOutput = JsonConvert.DeserializeObject(jsonString); if (errors.Count != 0) { @@ -62,7 +63,7 @@ private string RunSolc(string solcPath, string derivedFilePath) p.Start(); string configString = "{ \"language\": \"Solidity\", \"sources\": { %SOLPLACEHOLDER%: { \"urls\": [ %URLPLACEHOLDER% ]}}," - + "\"settings\": {\"evmVersion\": \"constantinople\", \"outputSelection\": {\"*\": {\"\": [ \"ast\" ]}}}}"; + + "\"settings\": {\"evmVersion\": \"istanbul\", \"outputSelection\": {\"*\": {\"\": [ \"ast\" ]}}}}"; configString = configString.Replace("%SOLPLACEHOLDER%", "\"" + derivedFileName + "\"" /*, StringComparison.CurrentCulture*/); configString = configString.Replace("%URLPLACEHOLDER%", "\"" + derivedFilePath + "\""/*, StringComparison.CurrentCulture*/); From da0914205a9649d907f0850bf8393144b7fa9a30 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Wed, 7 Oct 2020 14:45:11 -0700 Subject: [PATCH 30/49] Changing VeriSol so it will not fail if multiple functions named same thing --- .../SolToBoogie/GhostVarAndAxiomGenerator.cs | 10 +- Sources/SolToBoogie/MapArrayHelper.cs | 93 +++++++++++++++++-- Sources/SolToBoogie/ProcedureTranslator.cs | 73 +++++++++++---- Sources/SolToBoogie/TransUtils.cs | 31 ++++++- 4 files changed, 177 insertions(+), 30 deletions(-) diff --git a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs index ffe1c8dd..60c72478 100644 --- a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs +++ b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs @@ -16,6 +16,8 @@ public class GhostVarAndAxiomGenerator private MapArrayHelper mapHelper; private BoogieCtorType contractType = new BoogieCtorType("ContractName"); + + private HashSet addedDecls = new HashSet(); public GhostVarAndAxiomGenerator(TranslatorContext context, MapArrayHelper mapHelper) { @@ -45,7 +47,6 @@ private void GenerateFunctions() context.Program.AddDeclaration(GenerateAbiEncodedFunctionOneArgRef()); context.Program.AddDeclaration(GenerateAbiEncodedFunctionTwoArgsOneRef()); - HashSet addedDecls = new HashSet(); if (context.TranslateFlags.QuantFreeAllocs) { if (context.TranslateFlags.UseMultiDim) @@ -854,6 +855,13 @@ private void GenerateSingleMemoryVariable(VariableDeclaration decl, BoogieType k string name = mapHelper.GetMemoryMapName(decl, keyType, valType); if (!generatedMaps.Contains(name)) { + BoogieFunction initFn = MapArrayHelper.GenerateMultiDimZeroFunction(keyType, valType); + if (!addedDecls.Contains(initFn.Name)) + { + addedDecls.Add(initFn.Name); + context.Program.AddDeclaration(initFn); + } + generatedMaps.Add(name); context.Program.AddDeclaration(new BoogieGlobalVariable(new BoogieTypedIdent(name, map))); } diff --git a/Sources/SolToBoogie/MapArrayHelper.cs b/Sources/SolToBoogie/MapArrayHelper.cs index a91079ce..88297c03 100644 --- a/Sources/SolToBoogie/MapArrayHelper.cs +++ b/Sources/SolToBoogie/MapArrayHelper.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System.Collections.Generic; +using System.Runtime.CompilerServices; using solidityAnalysis; using SolidityAST; @@ -18,7 +19,7 @@ public class MapArrayHelper private static Regex arrayRegex = new Regex(@"(.+)\[\w*\]( storage ref| storage pointer| memory| calldata)?$"); // mapping (uint => uint[]) does not have storage/memory in Typestring // private static Regex arrayRegex = new Regex(@"(.+)\[\w*\]$"); - + private TranslatorContext context { get; } private AST solidityAst { get; } @@ -236,14 +237,14 @@ public static bool IsArrayTypeString(string typeString) public string GetNestedAllocName(VariableDeclaration decl, int lvl) { - return "alloc_" + decl.Name + "_lvl" + lvl; + return "alloc_" + TransUtils.GetCanonicalVariableName(decl, context) + "_lvl" + lvl; } public static BoogieFuncCallExpr GetCallExprForZeroInit(BoogieType key, BoogieType value) { - var keyStr = char.ToUpper(key.ToString()[0]) + key.ToString().Substring(1); - var valStr = char.ToUpper(value.ToString()[0]) + value.ToString().Substring(1); - return new BoogieFuncCallExpr("zero" + keyStr + valStr + "Arr", new List()); + /*var keyStr = char.ToUpper(key.ToString()[0]) + key.ToString().Substring(1); + var valStr = char.ToUpper(value.ToString()[0]) + value.ToString().Substring(1);*/ + return new BoogieFuncCallExpr("zero" + key.ToString() + value.ToString() + "Arr", new List()); } public static string GetSmtType(BoogieType type) @@ -264,6 +265,21 @@ public static string GetSmtType(BoogieType type) throw new Exception($"Unknown BoogieType {type}"); } + public static BoogieFunction GenerateMultiDimZeroFunction(BoogieType keyType, BoogieType valType) + { + BoogieExpr boogieInit = TransUtils.GetDefaultVal(valType); + string smtType = GetSmtType(valType); + BoogieType mapType = new BoogieMapType(keyType, valType); + string fnName = $"zero{keyType.ToString()}{valType.ToString()}Arr"; + + string smtInit = boogieInit.ToString().Equals("null") ? "0" : boogieInit.ToString(); + smtInit = $"((as const (Array {GetSmtType(keyType)} {smtType})) {smtInit})"; + + var outVar = new BoogieFormalParam(new BoogieTypedIdent("ret", mapType)); + var smtDefinedAttr = new BoogieAttribute("smtdefined", $"\"{smtInit}\""); + return new BoogieFunction(fnName, new List(), new List() {outVar}, new List() {smtDefinedAttr}); + } + public static BoogieFunction GenerateMultiDimZeroFunction(VariableDeclaration decl) { TypeName curType = decl.TypeName; @@ -304,6 +320,57 @@ public static BoogieFunction GenerateMultiDimZeroFunction(VariableDeclaration de return new BoogieFunction($"zero{fnName}", new List(), new List() {outVar}, new List() {smtDefinedAttr}); } + public static BoogieFunction GenerateMultiDimInitFunction(BoogieMapType type) + { + BoogieType curType = type; + List boogieType = new List(); + + while (curType is BoogieMapType map) + { + if (map.Arguments.Count != 1) + { + throw new Exception("Boogie map must have one argument"); + } + boogieType.Insert(0, map.Arguments[0]); + curType = map.Result; + } + + BoogieVariable arg = new BoogieFormalParam(new BoogieTypedIdent("n", curType)); + string smtInit = "n"; + string smtType = GetSmtType(curType); + string fnName = $"{curType.ToString()}Arr"; + + foreach (BoogieType dimType in boogieType) + { + smtType = $"(Array {GetSmtType(dimType)} {smtType})"; + smtInit = $"((as const {smtType}) {smtInit})"; + fnName = $"{dimType.ToString()}{fnName}"; + } + + var outVar = new BoogieFormalParam(new BoogieTypedIdent("ret", type)); + var smtDefinedAttr = new BoogieAttribute("smtdefined", $"\"{smtInit}\""); + return new BoogieFunction($"init{fnName}", new List() {arg}, new List() {outVar}, new List() {smtDefinedAttr}); + } + + public static BoogieExpr GetCallExprForInit(BoogieType curType, BoogieExpr initExpr) + { + if (!(curType is BoogieMapType)) + { + return initExpr; + } + + string fnName = "init"; + + while (curType is BoogieMapType map) + { + fnName = $"{fnName}{String.Join("", map.Arguments)}"; + curType = map.Result; + } + + fnName = $"{fnName}{curType}Arr"; + return new BoogieFuncCallExpr(fnName, new List() {initExpr}); + } + public static BoogieFuncCallExpr GetCallExprForZeroInit(BoogieType curType) { string fnName = "zero"; @@ -386,14 +453,14 @@ public static TypeName GetMappedType(VariableDeclaration varDecl) return curType; } - public static string GetMultiDimLengthName(VariableDeclaration varDecl, int lvl) + public string GetMultiDimLengthName(VariableDeclaration varDecl, int lvl) { - return $"Length_{varDecl.Name}_lvl{lvl}"; + return $"Length_{TransUtils.GetCanonicalVariableName(varDecl, context)}_lvl{lvl}"; } - public List GetMultiDimArrayLens(VariableDeclaration decl) + public List GetMultiDimArrayLens(VariableDeclaration decl) { - List lenVars = new List(); + List lenVars = new List(); TypeName curType = decl.TypeName; List indTypes = new List() {BoogieType.Ref}; @@ -409,10 +476,18 @@ public List GetMultiDimArrayLens(VariableDeclaration decl) else if (curType is ArrayTypeName arr) { BoogieType mapType = BoogieType.Int; + BoogieType initType = BoogieType.Int; for (int i = indTypes.Count - 1; i >= 0; i--) { + initType = mapType; mapType = new BoogieMapType(indTypes[i], mapType); } + + if (arr.Length != null && initType is BoogieMapType lenMap) + { + BoogieFunction initFn = GenerateMultiDimInitFunction(lenMap); + lenVars.Add(initFn); + } BoogieGlobalVariable lenVar = new BoogieGlobalVariable(new BoogieTypedIdent(GetMultiDimLengthName(decl, lvl), mapType)); lenVars.Add(lenVar); diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 4bbf63c2..f5ee2c7f 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -286,7 +286,7 @@ private void TranslateStateVarDeclaration(VariableDeclaration varDecl) BoogieType type = null; if (context.TranslateFlags.UseMultiDim && context.Analysis.Alias.getResults().Contains(varDecl)) { - List lenVars = mapHelper.GetMultiDimArrayLens(varDecl); + List lenVars = mapHelper.GetMultiDimArrayLens(varDecl); lenVars.ForEach(context.Program.AddDeclaration); type = MapArrayHelper.GetMultiDimBoogieType(varDecl.TypeName); @@ -473,11 +473,16 @@ public override bool Visit(FunctionDefinition node) currentFunction = node; // procedure name - string procName = node.Name + "_" + currentContract.Name; + //string procName = node.Name + "_" + currentContract.Name; + string procName = null; if (node.IsConstructor) { - procName += "_NoBaseCtor"; + procName = $"{TransUtils.GetCanonicalConstructorName(currentContract)}_NoBaseCtor"; + } + else + { + procName = TransUtils.GetCanonicalFunctionName(node, context); } currentBoogieProc = procName; @@ -851,30 +856,37 @@ public BoogieStmtList GetMultiDimInitialization(VariableDeclaration decl, Boogie string lenName = MapArrayHelper.GetMultiDimLengthName(decl, curLvl); BoogieMapSelect lenAccess = new BoogieMapSelect(new BoogieIdentifierExpr(lenName), contractVar.Arguments); init.AddStatement(new BoogieAssignCmd(lenAccess, lenZero));*/ - - if (arr.Length != null) + + BoogieExpr initVal = null; + if (arr.Length == null) { - throw new Exception("Must add support for static arrays"); + initVal = new BoogieLiteralExpr(BigInteger.Zero); + } + else + { + initVal = TranslateExpr(arr.Length); } BoogieType lenType = BoogieType.Int; + BoogieType initType = BoogieType.Int; for (int i = indTypes.Count - 1; i >= 0; i--) { + initType = lenType; lenType = new BoogieMapType(indTypes[i], lenType); } - String lenName = MapArrayHelper.GetMultiDimLengthName(decl, curLvl); + String lenName = mapHelper.GetMultiDimLengthName(decl, curLvl); BoogieMapSelect lenAccess = new BoogieMapSelect(new BoogieIdentifierExpr(lenName), contractVar.Arguments); - BoogieExpr lenZero = null; - if (lenType is BoogieMapType) + BoogieExpr lenZero = MapArrayHelper.GetCallExprForInit(initType, initVal); + /*if (lenType is BoogieMapType) { - lenZero = MapArrayHelper.GetCallExprForZeroInit(lenType); + lenZero = } else { lenZero = new BoogieLiteralExpr(BigInteger.Zero); - } + }*/ init.AddStatement(new BoogieAssignCmd(lenAccess, lenZero)); @@ -908,7 +920,7 @@ public BoogieStmtList GetMultiDimInitialization(VariableDeclaration decl, Boogie else if (curType is ArrayTypeName arr) { qVarTypes.Add(BoogieType.Int); - string lenName = MapArrayHelper.GetMultiDimLengthName(decl, lvl); + string lenName = mapHelper.GetMultiDimLengthName(decl, lvl); BoogieMapSelect lenAccess = new BoogieMapSelect(new BoogieIdentifierExpr(lenName), contractVar.Arguments[0]); List lenQVars = new List(); @@ -1430,7 +1442,7 @@ private void GenerateDefaultConstructor(ContractDefinition contract) } // generate the internal one without base constructors - string procName = contract.Name + "_" + contract.Name + "_NoBaseCtor"; + string procName = TransUtils.GetCanonicalConstructorName(contract) + "_NoBaseCtor"; currentBoogieProc = procName; if (!boogieToLocalVarsMap.ContainsKey(currentBoogieProc)) { @@ -3574,6 +3586,34 @@ private void TranslateUsingLibraryCall(FunctionCall node, List arguments, + List outParams) + { + ContractDefinition contract = FunctionCallHelper.GetStaticDispatchingContract(context, node); + string signature = TransUtils.InferFunctionSignature(context, node); + VeriSolAssert(context.HasFuncSignature(signature), $"Cannot find a function with signature: {signature}"); + var dynamicTypeToFuncMap = context.GetAllFuncDefinitions(signature); + VeriSolAssert(dynamicTypeToFuncMap.ContainsKey(contract)); + FunctionDefinition fnDef = dynamicTypeToFuncMap[contract]; + string callee = null; + if (contract.Name.Equals("VeriSol")) + { + callee = $"{fnDef.Name}_{contract.Name}"; + } + else + { + callee = TransUtils.GetCanonicalFunctionName(fnDef, context); + } + + BoogieCallCmd callCmd = new BoogieCallCmd(callee, arguments, outParams); + currentStmtList.AddStatement(callCmd); + + /*string functionName = TransUtils.GetFuncNameFromFuncCall(node); + string callee = functionName + "_" + contract.Name; + BoogieCallCmd callCmd = new BoogieCallCmd(callee, arguments, outParams); + currentStmtList.AddStatement(callCmd);*/ + } + private void TranslateInternalFunctionCall(FunctionCall node, List outParams = null) { List arguments = TransUtils.GetDefaultArguments(); @@ -3603,12 +3643,7 @@ private void TranslateInternalFunctionCall(FunctionCall node, List Date: Thu, 8 Oct 2020 11:43:21 -0700 Subject: [PATCH 31/49] Adding ability to have variable declarations inside tuples --- Sources/SolToBoogie/FunctionCallHelper.cs | 4 ++ Sources/SolToBoogie/ProcedureTranslator.cs | 50 +++++++++++++++++++--- Sources/SolidityAST/SolidityAST.cs | 14 ++++-- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/Sources/SolToBoogie/FunctionCallHelper.cs b/Sources/SolToBoogie/FunctionCallHelper.cs index c325da8c..bc909d55 100644 --- a/Sources/SolToBoogie/FunctionCallHelper.cs +++ b/Sources/SolToBoogie/FunctionCallHelper.cs @@ -111,6 +111,10 @@ public static bool IsExternalFunctionCall(TranslatorContext context, FunctionCal //a[i].foo(..) return true; } + else if (memberAccess.Expression is TupleExpression) + { + return true; + } } return false; } diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index f5ee2c7f..d435074f 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -1826,23 +1826,59 @@ public override bool Visit(VariableDeclarationStatement node) // handle the initial value of variable declaration if (node.InitialValue != null) { - VeriSolAssert(node.Declarations.Count == 1, "Invalid multiple variable declarations"); - // de-sugar to variable declaration and an assignment - VariableDeclaration varDecl = node.Declarations[0]; + List components = new List(); + foreach (VariableDeclaration varDecl in node.Declarations) + { + Identifier ident = new Identifier(); + ident.Name = varDecl.Name; + ident.ReferencedDeclaration = varDecl.Id; + ident.TypeDescriptions = varDecl.TypeDescriptions; + + components.Add(ident); + } + + Expression lhs = null; + if (components.Count == 1) + { + lhs = components[0]; + } + else + { + TupleExpression tupleExpr = new TupleExpression(); + tupleExpr.Id = node.Id; + tupleExpr.IsLValue = true; + tupleExpr.LValueRequested = true; + tupleExpr.Components = components; + lhs = tupleExpr; + } + + Assignment assignment = new Assignment(); + assignment.LeftHandSide = lhs; + assignment.Operator = "="; + assignment.RightHandSide = node.InitialValue; + + // call the visitor for assignments + assignment.Accept(this); + + + /* + VeriSolAssert(node.Declarations.Count == 1, "Invalid multiple variable declarations"); + VariableDeclaration varDecl = node.Declarations[0]; + Identifier identifier = new Identifier(); identifier.Name = varDecl.Name; identifier.ReferencedDeclaration = varDecl.Id; identifier.TypeDescriptions = varDecl.TypeDescriptions; - + Assignment assignment = new Assignment(); assignment.LeftHandSide = identifier; assignment.Operator = "="; assignment.RightHandSide = node.InitialValue; - + // call the visitor for assignments - assignment.Accept(this); + assignment.Accept(this);*/ } else { @@ -3124,6 +3160,8 @@ private void TranslateCallStatement(FunctionCall node, List(IASTGenericVisitor visitor) public override string ToString() { StringBuilder builder = new StringBuilder(); - Debug.Assert(Declarations.Count == 1, $"Multiple variable declarations: {Declarations.Count}"); - builder.Append(Declarations[0]); + //Debug.Assert(Declarations.Count == 1, $"Multiple variable declarations: {Declarations.Count}"); + if (Declarations.Count > 1) + { + builder.Append($"({String.Join(", ", Declarations)})"); + } + else + { + builder.Append(Declarations[0]); + } + if (InitialValue != null) { builder.Append(" = ").Append(InitialValue); From bfe7d1618334f42bfe8e710167867fc2e29025b2 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Thu, 8 Oct 2020 13:54:43 -0700 Subject: [PATCH 32/49] Adding the ability to leave entries out of tuple in an assignment. i.e. (bool success, ) = call() --- Sources/SolToBoogie/MapArrayHelper.cs | 21 ++++++++ Sources/SolToBoogie/ProcedureTranslator.cs | 56 ++++++++++++++++++---- Sources/SolidityAST/SolidityAST.cs | 11 ++++- 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/Sources/SolToBoogie/MapArrayHelper.cs b/Sources/SolToBoogie/MapArrayHelper.cs index 88297c03..a9b7a691 100644 --- a/Sources/SolToBoogie/MapArrayHelper.cs +++ b/Sources/SolToBoogie/MapArrayHelper.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; +using Microsoft.Extensions.Primitives; using solidityAnalysis; using SolidityAST; @@ -87,6 +88,26 @@ public BoogieExpr GetSumExpr(VariableDeclaration decl, BoogieExpr varExpr) return sumSelect; } + public static BoogieType InferExprTypeFromTupleTypeString(string typeString, int ind) + { + if (!typeString.StartsWith("tuple")) + { + return null; + } + + int start = typeString.IndexOf("("); + int end = typeString.IndexOf(")"); + + String[] tupleTypes = typeString.Substring(start + 1, end - start - 1).Split(","); + + if (ind >= tupleTypes.Length) + { + return null; + } + + return InferExprTypeFromTypeString(tupleTypes[ind]); + } + public static BoogieType InferExprTypeFromTypeString(string typeString) { //TODO: use TypeDescriptions::IsInt/IsStruct etc., but need typeDescription instead of typeString diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index d435074f..b39571c3 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -1812,6 +1812,11 @@ public override bool Visit(VariableDeclarationStatement node) preTranslationAction(node); foreach (VariableDeclaration varDecl in node.Declarations) { + if (varDecl == null) + { + continue; + } + string name = TransUtils.GetCanonicalLocalVariableName(varDecl, context); BoogieType type = TransUtils.GetBoogieTypeFromSolidityTypeName(varDecl.TypeName); // Issue a warning for intXX variables in case /useModularArithemtic option is used: @@ -1830,12 +1835,18 @@ public override bool Visit(VariableDeclarationStatement node) List components = new List(); foreach (VariableDeclaration varDecl in node.Declarations) { - Identifier ident = new Identifier(); - ident.Name = varDecl.Name; - ident.ReferencedDeclaration = varDecl.Id; - ident.TypeDescriptions = varDecl.TypeDescriptions; - - components.Add(ident); + if (varDecl == null) + { + components.Add(null); + } + else + { + Identifier ident = new Identifier(); + ident.Name = varDecl.Name; + ident.ReferencedDeclaration = varDecl.Id; + ident.TypeDescriptions = varDecl.TypeDescriptions; + components.Add(ident); + } } Expression lhs = null; @@ -1954,6 +1965,11 @@ public void updateAssignedSums(BoogieBinaryOperation.Opcode op, List for (int i = 0; i < exprs.Count; i++) { Expression expr = exprs[i]; + if (expr == null) + { + continue; + } + var isInt = expr.TypeDescriptions.IsInt() || expr.TypeDescriptions.IsUint(); if (isInt && expr is IndexAccess access && boogieExprs[i] is BoogieMapSelect sel && sel.BaseExpr is BoogieMapSelect arrIdent) { @@ -2012,9 +2028,28 @@ public override bool Visit(Assignment node) if (node.LeftHandSide is TupleExpression tuple) { // we only handle the case (e1, e2, .., _, _) = funcCall(...) - lhs.AddRange(tuple.Components.ConvertAll(x => TranslateExpr(x))); + lhs.AddRange(tuple.Components.ConvertAll(x => x == null ? null : TranslateExpr(x))); isTupleAssignment = true; - lhsTypes.AddRange(tuple.Components.ConvertAll(x => MapArrayHelper.InferExprTypeFromTypeString(x.TypeDescriptions.TypeString))); + + lhsTypes = new List(); + for (int i = 0; i < tuple.Components.Count; i++) + { + Expression expr = tuple.Components[i]; + if (expr == null) + { + BoogieType rhsType = + MapArrayHelper.InferExprTypeFromTupleTypeString( + node.RightHandSide.TypeDescriptions.TypeString, i); + VeriSolAssert(rhsType != null, "Could not determine type within tuple from rhs string"); + lhsTypes.Add(rhsType); + } + else + { + lhsTypes.Add(MapArrayHelper.InferExprTypeFromTypeString(expr.TypeDescriptions.TypeString)); + } + } + + //lhsTypes.AddRange(tuple.Components.ConvertAll(x => MapArrayHelper.InferExprTypeFromTypeString(x.TypeDescriptions.TypeString))); lhsExprs = tuple.Components; } else @@ -2097,7 +2132,10 @@ public override bool Visit(Assignment node) { for (int i = 0; i < lhs.Count; ++i) { - currentStmtList.AddStatement(new BoogieAssignCmd(lhs[i], tmpVars[i])); + if (lhs[i] != null) + { + currentStmtList.AddStatement(new BoogieAssignCmd(lhs[i], tmpVars[i])); + } } } if (context.TranslateFlags.InstrumentSums) diff --git a/Sources/SolidityAST/SolidityAST.cs b/Sources/SolidityAST/SolidityAST.cs index ec374fd7..a86a48c2 100644 --- a/Sources/SolidityAST/SolidityAST.cs +++ b/Sources/SolidityAST/SolidityAST.cs @@ -1261,7 +1261,7 @@ public override string ToString() public class VariableDeclarationStatement : Statement { - public List Assignments { get; set; } + public List Assignments { get; set; } public List Declarations { get; set; } @@ -1272,7 +1272,14 @@ public override void Accept(IASTVisitor visitor) if (visitor.Visit(this)) { //Debug.Assert(Declarations.Count == 1); - Utils.AcceptList(Declarations, visitor); + //Utils.AcceptList(Declarations, visitor); + foreach (VariableDeclaration varDecl in Declarations) + { + if (varDecl != null) + { + varDecl.Accept(visitor); + } + } if (InitialValue != null) { InitialValue.Accept(visitor); From d18ad707e5b0ca467096f9e0c5671b6b80d70c36 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Fri, 9 Oct 2020 13:21:54 -0700 Subject: [PATCH 33/49] Added ability to define using for with contracts --- Sources/SolToBoogie/FunctionCallHelper.cs | 74 +++++++++++++++++++++- Sources/SolToBoogie/ProcedureTranslator.cs | 62 ++---------------- Sources/SolToBoogie/TransUtils.cs | 2 +- Sources/SolToBoogie/TranslatorContext.cs | 6 +- Sources/SolToBoogie/UsingCollector.cs | 32 ++++++---- 5 files changed, 99 insertions(+), 77 deletions(-) diff --git a/Sources/SolToBoogie/FunctionCallHelper.cs b/Sources/SolToBoogie/FunctionCallHelper.cs index bc909d55..e6a5edaf 100644 --- a/Sources/SolToBoogie/FunctionCallHelper.cs +++ b/Sources/SolToBoogie/FunctionCallHelper.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using SolidityAST; namespace SolToBoogie @@ -42,13 +44,81 @@ public static ContractDefinition GetStaticDispatchingContract(TranslatorContext VeriSolAssert(contract != null); return contract; } + + public static ContractDefinition GetUsedLibrary(TranslatorContext context, ContractDefinition curContract, + MemberAccess memberAccess) + { + FunctionDefinition fnDef = context.GetASTNodeById(memberAccess.ReferencedDeclaration.Value) as FunctionDefinition; + + if (fnDef == null || !context.FunctionToContractMap.ContainsKey(fnDef)) + { + return null; + } + + ContractDefinition fnContract = context.GetContractByFunction(fnDef); + + Dictionary usingLibs = new Dictionary(); + List contractIds = new List(); + contractIds.Add(curContract.Id); + contractIds.AddRange(curContract.LinearizedBaseContracts); + + foreach (int id in contractIds) + { + ContractDefinition baseContract = context.GetASTNodeById(id) as ContractDefinition; + + foreach (UserDefinedTypeName typeName in context.UsingMap[baseContract].Keys) + { + ContractDefinition libDef = context.GetASTNodeById(typeName.ReferencedDeclaration) as ContractDefinition; + if (!usingLibs.ContainsKey(libDef)) + { + usingLibs[libDef] = typeName; + } + } + } + + if (usingLibs.ContainsKey(fnContract)) + { + if (memberAccess.Expression.TypeDescriptions.IsContract() && + !memberAccess.Expression.TypeDescriptions.IsArray()) + { + //search sub-types + UserDefinedTypeName libType = usingLibs[fnContract]; + String contractName = memberAccess.Expression.TypeDescriptions.TypeString.Split(" ")[1]; + ContractDefinition contractDef = context.GetContractByName(contractName); + HashSet usedBy = context.UsingMap[curContract][libType].FindAll(t => + t is UserDefinedTypeName u && + context.GetASTNodeById(u.ReferencedDeclaration) is ContractDefinition).Select(c => + context.GetASTNodeById(((UserDefinedTypeName) (c)) + .ReferencedDeclaration) as ContractDefinition).ToHashSet(); + + bool usesLib = usedBy.Contains(contractDef); + + foreach (int id in contractDef.LinearizedBaseContracts) + { + ContractDefinition baseContract = context.GetASTNodeById(id) as ContractDefinition; + if (usedBy.Contains(baseContract)) + { + usesLib = true; + } + } + + return usesLib ? fnContract : null; + } + else + { + return fnContract; + } + } + + return null; + } - public static bool IsUsingBasedLibraryCall(MemberAccess memberAccess) + public static bool IsUsingBasedLibraryCall(TranslatorContext context, ContractDefinition curContract, MemberAccess memberAccess) { // since we only permit "using A for B" for non-contract types // this is sufficient, but not necessary in general since non // contracts (including libraries) do not have support methods - return !memberAccess.Expression.TypeDescriptions.IsContract(); + return GetUsedLibrary(context, curContract, memberAccess) != null; } public static ContractDefinition IsLibraryFunctionCall(TranslatorContext context, FunctionCall node) diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index b39571c3..75636b75 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -3531,7 +3531,7 @@ private void TranslateExternalFunctionCall(FunctionCall node, List usingRange = new HashSet(); - - // may need to look into base contracts as well (UsingInBase.sol) - foreach (int id in currentContract.LinearizedBaseContracts) - { - ContractDefinition baseContract = context.GetASTNodeById(id) as ContractDefinition; - Debug.Assert(baseContract != null); - if (!context.UsingMap.ContainsKey(baseContract)) continue; - foreach (var kv in context.UsingMap[baseContract]) - { - if (kv.Value.ToString().Equals(typedescr)) - { - usingRange.Add(kv.Key); - } - } - } - VeriSolAssert(usingRange.Count > 0, $"Expecting at least one using A for B for {typedescr}"); - - string signature = TransUtils.InferFunctionSignature(context, node); - VeriSolAssert(context.HasFuncSignature(signature), $"Cannot find a function with signature: {signature}"); - var dynamicTypeToFuncMap = context.GetAllFuncDefinitions(signature); - VeriSolAssert(dynamicTypeToFuncMap.Count > 0); - - //intersect the types with a matching function with usingRange - var candidateLibraries = new List>(); - foreach(var tf in dynamicTypeToFuncMap) - { - if (usingRange.Any(x => x.Name.Equals(tf.Key.Name.ToString()))) - { - candidateLibraries.Add(Tuple.Create(tf.Key, tf.Value)); - } - } - - VeriSolAssert(candidateLibraries.Count == 1, $"Expecting a library call to match exactly one function, found {candidateLibraries.Count}"); - var funcDefn = candidateLibraries[0]; + + var funcDefn = context.GetASTNodeById(memberAccess.ReferencedDeclaration.Value) as FunctionDefinition; //x.f(y1, y2) in Solidity becomes f_lib(this, this, 0, x, y1, y2) var arguments = new List() { @@ -3656,7 +3602,7 @@ private void TranslateUsingLibraryCall(FunctionCall node, List TranslateExpr(x))); - var callee = TransUtils.GetCanonicalFunctionName(funcDefn.Item2, context); + var callee = TransUtils.GetCanonicalFunctionName(funcDefn, context); var callCmd = new BoogieCallCmd(callee, arguments, outParams); currentStmtList.AddStatement(callCmd); // throw new NotImplementedException("not done implementing using A for B yet"); diff --git a/Sources/SolToBoogie/TransUtils.cs b/Sources/SolToBoogie/TransUtils.cs index efb27558..75460d7f 100644 --- a/Sources/SolToBoogie/TransUtils.cs +++ b/Sources/SolToBoogie/TransUtils.cs @@ -483,7 +483,7 @@ public static string GetCanonicalFunctionName(FunctionDefinition funcDef, Transl } else { - typeStr = paramDecl.TypeName.ToString(); + typeStr = paramDecl.TypeName.ToString().Replace(" ", ""); } paramStr = $"{paramStr}~{typeStr}"; diff --git a/Sources/SolToBoogie/TranslatorContext.cs b/Sources/SolToBoogie/TranslatorContext.cs index 54143ab0..32ced0c3 100644 --- a/Sources/SolToBoogie/TranslatorContext.cs +++ b/Sources/SolToBoogie/TranslatorContext.cs @@ -102,7 +102,7 @@ public class TranslatorContext // Options flags public TranslatorFlags TranslateFlags { get; private set; } - public Dictionary> UsingMap => usingMap; + public Dictionary>> UsingMap => usingMap; // num of fresh identifiers, should be incremented when making new fresh id private int freshIdentifierCount = 0; @@ -115,7 +115,7 @@ public class TranslatorContext // data structures for using // maps Contract C --> (source, dest), where source is a library type - private readonly Dictionary> usingMap; + private readonly Dictionary>> usingMap; public TranslatorContext(AST solidityAST, HashSet> ignoreMethods, bool _genInlineAttrInBpl, TranslatorFlags _translateFlags = null, String entryPointContract = "") { @@ -144,7 +144,7 @@ public TranslatorContext(AST solidityAST, HashSet> ignoreM ModifierToBoogiePreImpl = new Dictionary(); ModifierToBoogiePostImpl = new Dictionary(); ModifierToPreludeLocalVars = new Dictionary>(); - usingMap = new Dictionary>(); + usingMap = new Dictionary>>(); IgnoreMethods = ignoreMethods; genInlineAttrInBpl = _genInlineAttrInBpl; TranslateFlags = _translateFlags; diff --git a/Sources/SolToBoogie/UsingCollector.cs b/Sources/SolToBoogie/UsingCollector.cs index 0b37269b..f864b977 100644 --- a/Sources/SolToBoogie/UsingCollector.cs +++ b/Sources/SolToBoogie/UsingCollector.cs @@ -1,5 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. + +using System.Linq; + namespace SolToBoogie { using SolidityAST; @@ -21,25 +24,28 @@ public UsingCollector(TranslatorContext context) this.context = context; } - public override bool Visit(ContractDefinition node) - { - currentContract = node; - context.UsingMap[currentContract] = new Dictionary(); - return true; - } - - public override void EndVisit(ContractDefinition node) - { - currentContract = null; + public override bool Visit(ContractDefinition node) + { + currentContract = node; + context.UsingMap[currentContract] = new Dictionary>(); + return true; + } + + public override void EndVisit(ContractDefinition node) + { + currentContract = null; } public override bool Visit(UsingForDirective node) { - if (node.TypeName is UserDefinedTypeName userType) + if (context.UsingMap[currentContract].ContainsKey(node.LibraryName)) + { + context.UsingMap[currentContract][node.LibraryName].Add(node.TypeName); + } + else { - Debug.Assert(!userType.TypeDescriptions.IsContract(), $"VeriSol does not support using A for B where B is a contract name, found {userType.ToString()}"); + context.UsingMap[currentContract][node.LibraryName] = new List() {node.TypeName}; } - context.UsingMap[currentContract][node.LibraryName] = node.TypeName; return true; } } From 1343be01ee22506158fc418ce7c6f4e37bd7f4a4 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Tue, 13 Oct 2020 13:07:47 -0700 Subject: [PATCH 34/49] Fixing a bug caused by now.div() --- Sources/SolToBoogie/FunctionCallHelper.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Sources/SolToBoogie/FunctionCallHelper.cs b/Sources/SolToBoogie/FunctionCallHelper.cs index e6a5edaf..5b9571f2 100644 --- a/Sources/SolToBoogie/FunctionCallHelper.cs +++ b/Sources/SolToBoogie/FunctionCallHelper.cs @@ -154,12 +154,18 @@ public static bool IsExternalFunctionCall(TranslatorContext context, FunctionCal { return true; } + + if (!context.HasASTNodeId(identifier.ReferencedDeclaration)) + { + return true; + } + var contract = context.GetASTNodeById(identifier.ReferencedDeclaration) as ContractDefinition; if (contract == null) - { + { return true; } - } + } else if (memberAccess.Expression is MemberAccess structSelect) { //a.b.c.foo(...) From cbed00fe06f3e5fe47fd4068a72a222309ff144a Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Fri, 23 Oct 2020 10:07:03 -0700 Subject: [PATCH 35/49] Adding variable positions to config file and adding support for gasleft call --- Sources/SolToBoogie/ERC20SpecGenerator.cs | 32 ++++++++++++++++++++++ Sources/SolToBoogie/FunctionCallHelper.cs | 11 ++++++++ Sources/SolToBoogie/ProcedureTranslator.cs | 18 +++++++++++- 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/Sources/SolToBoogie/ERC20SpecGenerator.cs b/Sources/SolToBoogie/ERC20SpecGenerator.cs index 7b5200f4..6f253164 100644 --- a/Sources/SolToBoogie/ERC20SpecGenerator.cs +++ b/Sources/SolToBoogie/ERC20SpecGenerator.cs @@ -77,9 +77,22 @@ public ERC20SpecGenerator(TranslatorContext context, AST solidityAST, String ent } } + private int CompareVars(VariableDeclaration v1, VariableDeclaration v2) + { + int v1No = context.ASTNodeToSourceLineNumberMap[v1]; + int v2No = context.ASTNodeToSourceLineNumberMap[v2]; + + if (v1No != v2No) + { + return v1No < v2No ? -1 : 1; + } + + throw new Exception("Two variables declared on the same line"); + } public void GenerateSpec() { + List allVars = new List(otherVars); String filename = context.ASTNodeToSourcePathMap[entryContract]; StreamWriter writer = new StreamWriter($"{filename.Substring(0, filename.Length - 4)}.config"); @@ -88,16 +101,31 @@ public void GenerateSpec() { Console.WriteLine("Warning: Could not find totalSupply variable"); } + else + { + allVars.Add(varDecls["totalSupply"]); + } string bal = varDecls.ContainsKey("balanceOf") ? $"this.{varDecls["balanceOf"].Name}" : ""; if (String.IsNullOrEmpty(bal)) { Console.WriteLine("Warning: Could not find balance variable"); } + else + { + allVars.Add(varDecls["balanceOf"]); + } string allowances = varDecls.ContainsKey("allowance") ? $"this.{varDecls["allowance"].Name}" : ""; if (String.IsNullOrEmpty(allowances)) { Console.WriteLine("Warning: Could not find allowances variable"); } + else + { + allVars.Add(varDecls["allowance"]); + } + + allVars.Sort(CompareVars); + string totContract = fnContracts.ContainsKey("totalSupply") ? fnContracts["totalSupply"].Name : ""; string balContract = fnContracts.ContainsKey("balanceOf") ? fnContracts["balanceOf"].Name : ""; string allowanceContract = fnContracts.ContainsKey("allowance") ? fnContracts["allowance"].Name : ""; @@ -119,6 +147,10 @@ public void GenerateSpec() writer.WriteLine($"TRANSFER_CONTRACT={transferContract}"); writer.WriteLine($"TRANSFER_FROM_CONTRACT={transferFromContract}"); writer.WriteLine($"EXTRA_VARS=({extraVars})"); + for (int i = 0; i < allVars.Count; i++) + { + writer.WriteLine($"{allVars[i].Name}={i}"); + } writer.Close(); } diff --git a/Sources/SolToBoogie/FunctionCallHelper.cs b/Sources/SolToBoogie/FunctionCallHelper.cs index 5b9571f2..73c29359 100644 --- a/Sources/SolToBoogie/FunctionCallHelper.cs +++ b/Sources/SolToBoogie/FunctionCallHelper.cs @@ -200,6 +200,7 @@ public static bool IsImplicitFunc(FunctionCall node) return IsKeccakFunc(node) || IsAbiEncodePackedFunc(node) || + IsGasleft(node) || IsTypeCast(node) || IsStructConstructor(node) || IsContractConstructor(node); @@ -215,6 +216,16 @@ public static bool IsStructConstructor(FunctionCall node) return node.Kind.Equals("structConstructorCall"); } + public static bool IsGasleft(FunctionCall node) + { + if (node.Expression is Identifier ident) + { + return ident.Name.Equals("gasleft") && node.Arguments.Count == 0; + } + + return false; + } + public static bool IsKeccakFunc(FunctionCall node) { if (node.Expression is Identifier ident) diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 75636b75..72f25be1 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -2099,6 +2099,12 @@ public override bool Visit(Assignment node) TranslateKeccakFuncCall(funcCall, lhs[0]); //this is not a procedure call in Boogie usedTmpVar = false; } + else if (FunctionCallHelper.IsGasleft(funcCall)) + { + VeriSolAssert(!isTupleAssignment, "Not expecting a tuple for gasleft"); + TranslateGasleftCall(funcCall, lhs[0]); + usedTmpVar = false; + } else if (FunctionCallHelper.IsAbiEncodePackedFunc(funcCall)) { TranslateAbiEncodedFuncCall(funcCall, tmpVars[0]); //this is not a procedure call in Boogie @@ -2973,6 +2979,8 @@ public override bool Visit(FunctionCall node) TranslateAbiEncodedFuncCall(node, tmpVarExpr); } else if (FunctionCallHelper.IsKeccakFunc(node)) { TranslateKeccakFuncCall(node, tmpVarExpr); + } else if (FunctionCallHelper.IsGasleft(node)) { + TranslateGasleftCall(node, tmpVarExpr); } else if (FunctionCallHelper.IsStructConstructor(node)) { TranslateStructConstructor(node, tmpVarExpr); } else @@ -3153,13 +3161,21 @@ private BoogieIdentifierExpr MkNewLocalVariableWithType(BoogieType boogieTypeCal return tmpVarExpr; } - #region implicit functions + #region implicit functions + /// /// Implicit function calls /// /// /// + private void TranslateGasleftCall(FunctionCall node, BoogieExpr lhs) + { + currentStmtList.AddStatement(new BoogieCommentCmd("gasleft Translation")); + BoogieAssignCmd gasAssign = new BoogieAssignCmd(lhs, new BoogieIdentifierExpr("gas")); + currentStmtList.AddStatement(gasAssign); + } + private void TranslateKeccakFuncCall(FunctionCall funcCall, BoogieExpr lhs) { var expression = funcCall.Arguments[0]; From 1558b2813ff350fec3c75493f47f31e1d9d99e70 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Fri, 23 Oct 2020 11:14:49 -0700 Subject: [PATCH 36/49] Changing evmVersion --- Sources/SolidityAST/SolidityCompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SolidityAST/SolidityCompiler.cs b/Sources/SolidityAST/SolidityCompiler.cs index 7edbeefb..24b603f2 100644 --- a/Sources/SolidityAST/SolidityCompiler.cs +++ b/Sources/SolidityAST/SolidityCompiler.cs @@ -63,7 +63,7 @@ private string RunSolc(string solcPath, string derivedFilePath) p.Start(); string configString = "{ \"language\": \"Solidity\", \"sources\": { %SOLPLACEHOLDER%: { \"urls\": [ %URLPLACEHOLDER% ]}}," - + "\"settings\": {\"evmVersion\": \"istanbul\", \"outputSelection\": {\"*\": {\"\": [ \"ast\" ]}}}}"; + + "\"settings\": {\"evmVersion\": \"constantinople\", \"outputSelection\": {\"*\": {\"\": [ \"ast\" ]}}}}"; configString = configString.Replace("%SOLPLACEHOLDER%", "\"" + derivedFileName + "\"" /*, StringComparison.CurrentCulture*/); configString = configString.Replace("%URLPLACEHOLDER%", "\"" + derivedFilePath + "\""/*, StringComparison.CurrentCulture*/); From 656dc4ca49f3a0a4c06ebb3d7115a451ad2940dc Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Fri, 23 Oct 2020 11:40:46 -0700 Subject: [PATCH 37/49] Adding this. from generated specifications --- Sources/SolToBoogie/ERC20SpecGenerator.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/SolToBoogie/ERC20SpecGenerator.cs b/Sources/SolToBoogie/ERC20SpecGenerator.cs index 6f253164..ddd36418 100644 --- a/Sources/SolToBoogie/ERC20SpecGenerator.cs +++ b/Sources/SolToBoogie/ERC20SpecGenerator.cs @@ -96,7 +96,7 @@ public void GenerateSpec() String filename = context.ASTNodeToSourcePathMap[entryContract]; StreamWriter writer = new StreamWriter($"{filename.Substring(0, filename.Length - 4)}.config"); - string totSupply = varDecls.ContainsKey("totalSupply") ? $"this.{varDecls["totalSupply"].Name}" : ""; + string totSupply = varDecls.ContainsKey("totalSupply") ? $"{varDecls["totalSupply"].Name}" : ""; if (String.IsNullOrEmpty(totSupply)) { Console.WriteLine("Warning: Could not find totalSupply variable"); @@ -105,7 +105,7 @@ public void GenerateSpec() { allVars.Add(varDecls["totalSupply"]); } - string bal = varDecls.ContainsKey("balanceOf") ? $"this.{varDecls["balanceOf"].Name}" : ""; + string bal = varDecls.ContainsKey("balanceOf") ? $"{varDecls["balanceOf"].Name}" : ""; if (String.IsNullOrEmpty(bal)) { Console.WriteLine("Warning: Could not find balance variable"); @@ -114,7 +114,7 @@ public void GenerateSpec() { allVars.Add(varDecls["balanceOf"]); } - string allowances = varDecls.ContainsKey("allowance") ? $"this.{varDecls["allowance"].Name}" : ""; + string allowances = varDecls.ContainsKey("allowance") ? $"{varDecls["allowance"].Name}" : ""; if (String.IsNullOrEmpty(allowances)) { Console.WriteLine("Warning: Could not find allowances variable"); @@ -133,7 +133,7 @@ public void GenerateSpec() string transferContract = fnContracts.ContainsKey("transfer") ? fnContracts["transfer"].Name : ""; string transferFromContract = fnContracts.ContainsKey("transferFrom") ? fnContracts["transferFrom"].Name : ""; - string extraVars = String.Join(" ", otherVars.Select(v => $"this.{v.Name}")); + string extraVars = String.Join(" ", otherVars.Select(v => $"{v.Name}")); writer.WriteLine($"FILE_NAME={filename}"); writer.WriteLine($"CONTRACT_NAME={entryContract.Name}"); From 637e8e69d61007ad98daaada977dfae5a7679aa8 Mon Sep 17 00:00:00 2001 From: Jon Stephens Date: Thu, 29 Oct 2020 08:31:13 -0500 Subject: [PATCH 38/49] Fixing something in spec generator --- Sources/SolToBoogie/ERC20SpecGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SolToBoogie/ERC20SpecGenerator.cs b/Sources/SolToBoogie/ERC20SpecGenerator.cs index ddd36418..04cb4297 100644 --- a/Sources/SolToBoogie/ERC20SpecGenerator.cs +++ b/Sources/SolToBoogie/ERC20SpecGenerator.cs @@ -133,7 +133,7 @@ public void GenerateSpec() string transferContract = fnContracts.ContainsKey("transfer") ? fnContracts["transfer"].Name : ""; string transferFromContract = fnContracts.ContainsKey("transferFrom") ? fnContracts["transferFrom"].Name : ""; - string extraVars = String.Join(" ", otherVars.Select(v => $"{v.Name}")); + string extraVars = String.Join(" ", otherVars.Select(v => $"this.{v.Name}")); writer.WriteLine($"FILE_NAME={filename}"); writer.WriteLine($"CONTRACT_NAME={entryContract.Name}"); @@ -488,4 +488,4 @@ public override bool Visit(VariableDeclarationStatement node) } } } -} \ No newline at end of file +} From f9e8ff761663d714ada5cbd3276992a21e0cdaa0 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Fri, 6 Nov 2020 14:49:45 -0700 Subject: [PATCH 39/49] Fixing a bug in the spec generator --- Sources/SolToBoogie/ERC20SpecGenerator.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Sources/SolToBoogie/ERC20SpecGenerator.cs b/Sources/SolToBoogie/ERC20SpecGenerator.cs index 04cb4297..91b5ca41 100644 --- a/Sources/SolToBoogie/ERC20SpecGenerator.cs +++ b/Sources/SolToBoogie/ERC20SpecGenerator.cs @@ -79,7 +79,18 @@ public ERC20SpecGenerator(TranslatorContext context, AST solidityAST, String ent private int CompareVars(VariableDeclaration v1, VariableDeclaration v2) { - int v1No = context.ASTNodeToSourceLineNumberMap[v1]; + string[] v1Tokens = v1.Src.Split(':'); + int v1Pos = int.Parse(v1Tokens[0]); + string[] v2Tokens = v2.Src.Split(':'); + int v2Pos = int.Parse(v2Tokens[0]); + + if (v1Pos != v2Pos) + { + return v1Pos < v2Pos ? -1 : 1; + } + + throw new Exception("Two variables at the same position"); + /*int v1No = context.ASTNodeToSourceLineNumberMap[v1]; int v2No = context.ASTNodeToSourceLineNumberMap[v2]; if (v1No != v2No) @@ -87,7 +98,7 @@ private int CompareVars(VariableDeclaration v1, VariableDeclaration v2) return v1No < v2No ? -1 : 1; } - throw new Exception("Two variables declared on the same line"); + throw new Exception("Two variables declared on the same line");*/ } public void GenerateSpec() From a328927bd39f332fb8685952c1aa3742ba2366f6 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Tue, 10 Nov 2020 11:01:25 -0700 Subject: [PATCH 40/49] Fixing a few issues with the specification generation. Constants should not be included in vars and vars should be in inheritance order. --- Sources/SolToBoogie/ERC20SpecGenerator.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Sources/SolToBoogie/ERC20SpecGenerator.cs b/Sources/SolToBoogie/ERC20SpecGenerator.cs index 91b5ca41..0af83cdb 100644 --- a/Sources/SolToBoogie/ERC20SpecGenerator.cs +++ b/Sources/SolToBoogie/ERC20SpecGenerator.cs @@ -17,6 +17,7 @@ public class ERC20SpecGenerator private ContractDefinition entryContract; private Dictionary varDecls; private Dictionary fnContracts; + private Dictionary declToContractInd; private List otherVars; public ERC20SpecGenerator(TranslatorContext context, AST solidityAST, String entryPoint) @@ -26,6 +27,7 @@ public ERC20SpecGenerator(TranslatorContext context, AST solidityAST, String ent varDecls = new Dictionary(); fnContracts = new Dictionary(); otherVars = new List(); + declToContractInd = new Dictionary(); foreach (ContractDefinition def in context.ContractDefinitions) { @@ -34,14 +36,20 @@ public ERC20SpecGenerator(TranslatorContext context, AST solidityAST, String ent entryContract = def; } } - + + int contractInd = 0; foreach (int id in entryContract.LinearizedBaseContracts) { + contractInd++; ContractDefinition contract = context.GetASTNodeById(id) as ContractDefinition; if (context.ContractToStateVarsMap.ContainsKey(contract)) { otherVars.AddRange(context.ContractToStateVarsMap[contract]); + foreach (VariableDeclaration decl in context.ContractToStateVarsMap[contract]) + { + declToContractInd[decl] = contractInd; + } } if (!context.ContractToFunctionsMap.ContainsKey(contract)) @@ -75,15 +83,22 @@ public ERC20SpecGenerator(TranslatorContext context, AST solidityAST, String ent { otherVars.Remove(decl); } + + otherVars.RemoveAll(v => v.Constant); } private int CompareVars(VariableDeclaration v1, VariableDeclaration v2) { + if (declToContractInd[v1] != declToContractInd[v2]) + { + return declToContractInd[v1] > declToContractInd[v2] ? -1 : 1; + } + string[] v1Tokens = v1.Src.Split(':'); int v1Pos = int.Parse(v1Tokens[0]); string[] v2Tokens = v2.Src.Split(':'); int v2Pos = int.Parse(v2Tokens[0]); - + if (v1Pos != v2Pos) { return v1Pos < v2Pos ? -1 : 1; From 62fce1efed649166dc4b74b11015b1b56a6c757b Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Wed, 11 Nov 2020 16:48:27 -0700 Subject: [PATCH 41/49] Fixing a bug where constructor msg.value can be negative --- Sources/SolToBoogie/HarnessGenerator.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Sources/SolToBoogie/HarnessGenerator.cs b/Sources/SolToBoogie/HarnessGenerator.cs index 9b4c5024..c3a83fe9 100644 --- a/Sources/SolToBoogie/HarnessGenerator.cs +++ b/Sources/SolToBoogie/HarnessGenerator.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.Numerics; namespace SolToBoogie { @@ -162,6 +163,19 @@ private List GenerateConstructorCall(ContractDefinition contract) if (context.IsConstructorDefined(contract)) { FunctionDefinition ctor = context.GetConstructorByContract(contract); + if (ctor.StateMutability.Equals(EnumStateMutability.PAYABLE)) + { + BoogieExpr assumeExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, + new BoogieIdentifierExpr("msgvalue_MSG"), new BoogieLiteralExpr(BigInteger.Zero)); + localStmtList.Add(new BoogieAssumeCmd(assumeExpr)); + } + else + { + BoogieExpr assumeExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, + new BoogieIdentifierExpr("msgvalue_MSG"), new BoogieLiteralExpr(BigInteger.Zero)); + localStmtList.Add(new BoogieAssumeCmd(assumeExpr)); + } + foreach (VariableDeclaration param in ctor.Parameters.Parameters) { string name = TransUtils.GetCanonicalLocalVariableName(param, context); @@ -175,6 +189,12 @@ private List GenerateConstructorCall(ContractDefinition contract) } } } + else + { + BoogieExpr assumeExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.EQ, + new BoogieIdentifierExpr("msgvalue_MSG"), new BoogieLiteralExpr(BigInteger.Zero)); + localStmtList.Add(new BoogieAssumeCmd(assumeExpr)); + } if (context.TranslateFlags.InstrumentGas) { From c25c40057188ac90bc17ddf3538f0ae839acac28 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Wed, 11 Nov 2020 17:49:07 -0700 Subject: [PATCH 42/49] Adding error for unsupported abi functions --- Sources/SolToBoogie/FunctionCallHelper.cs | 18 +++++++++++++++++- Sources/SolToBoogie/ProcedureTranslator.cs | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Sources/SolToBoogie/FunctionCallHelper.cs b/Sources/SolToBoogie/FunctionCallHelper.cs index 73c29359..f296a468 100644 --- a/Sources/SolToBoogie/FunctionCallHelper.cs +++ b/Sources/SolToBoogie/FunctionCallHelper.cs @@ -203,7 +203,23 @@ public static bool IsImplicitFunc(FunctionCall node) IsGasleft(node) || IsTypeCast(node) || IsStructConstructor(node) || - IsContractConstructor(node); + IsContractConstructor(node) || + IsAbiFunction(node); + } + + public static bool IsAbiFunction(FunctionCall node) + { + if (node.Expression is MemberAccess member) + { + if (member.Expression is Identifier ident) + { + if (ident.Name.Equals("abi")) + { + return true; + } + } + } + return false; } public static bool IsContractConstructor(FunctionCall node) diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 72f25be1..c06aa490 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -2985,7 +2985,7 @@ public override bool Visit(FunctionCall node) TranslateStructConstructor(node, tmpVarExpr); } else { - VeriSolAssert(false, $"Unexpected implicit function {node.ToString()}"); + VeriSolAssert(false, $"Unsupported implicit function {node.ToString()}"); } if (!isElementaryCast) From 15cd34c00816a50dc75d6a4633a291c252babdc5 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Wed, 11 Nov 2020 19:02:54 -0700 Subject: [PATCH 43/49] Fixing bug where zero function isn't declared --- Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs | 10 ++++------ Sources/SolToBoogie/ProcedureTranslator.cs | 14 ++++++++++++-- Sources/SolToBoogie/TranslatorContext.cs | 3 +++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs index 60c72478..c6392021 100644 --- a/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs +++ b/Sources/SolToBoogie/GhostVarAndAxiomGenerator.cs @@ -16,8 +16,6 @@ public class GhostVarAndAxiomGenerator private MapArrayHelper mapHelper; private BoogieCtorType contractType = new BoogieCtorType("ContractName"); - - private HashSet addedDecls = new HashSet(); public GhostVarAndAxiomGenerator(TranslatorContext context, MapArrayHelper mapHelper) { @@ -58,10 +56,10 @@ private void GenerateFunctions() { BoogieFunction initFn = MapArrayHelper.GenerateMultiDimZeroFunction(decl); - if (!addedDecls.Contains(initFn.Name)) + if (!context.initFns.Contains(initFn.Name)) { context.Program.AddDeclaration(initFn); - addedDecls.Add(initFn.Name); + context.initFns.Add(initFn.Name); } } @@ -856,9 +854,9 @@ private void GenerateSingleMemoryVariable(VariableDeclaration decl, BoogieType k if (!generatedMaps.Contains(name)) { BoogieFunction initFn = MapArrayHelper.GenerateMultiDimZeroFunction(keyType, valType); - if (!addedDecls.Contains(initFn.Name)) + if (!context.initFns.Contains(initFn.Name)) { - addedDecls.Add(initFn.Name); + context.initFns.Add(initFn.Name); context.Program.AddDeclaration(initFn); } diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index c06aa490..f42f7db1 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -879,6 +879,10 @@ public BoogieStmtList GetMultiDimInitialization(VariableDeclaration decl, Boogie BoogieMapSelect lenAccess = new BoogieMapSelect(new BoogieIdentifierExpr(lenName), contractVar.Arguments); BoogieExpr lenZero = MapArrayHelper.GetCallExprForInit(initType, initVal); + /*if (lenZero is BoogieFuncCallExpr callExpr && !context.initFns.Contains(callExpr.Function)) + { + MapArrayHelper.Generate + }*/ /*if (lenType is BoogieMapType) { lenZero = @@ -1314,8 +1318,14 @@ private void InitializeNestedArrayMappingStateVar(VariableDeclaration varDecl, M BoogieMapSelect lhs = new BoogieMapSelect(new BoogieIdentifierExpr(allocName), index); if (context.TranslateFlags.QuantFreeAllocs) { - currentStmtList.AddStatement((new BoogieAssignCmd(lhs, - MapArrayHelper.GetCallExprForZeroInit(keyType, BoogieType.Bool)))); + BoogieFuncCallExpr zeroCall = + MapArrayHelper.GetCallExprForZeroInit(keyType, BoogieType.Bool); + if (!context.initFns.Contains(zeroCall.Function)) + { + context.initFns.Add(zeroCall.Function); + context.Program.AddDeclaration(MapArrayHelper.GenerateMultiDimZeroFunction(keyType, BoogieType.Bool)); + } + currentStmtList.AddStatement((new BoogieAssignCmd(lhs, zeroCall))); } else { diff --git a/Sources/SolToBoogie/TranslatorContext.cs b/Sources/SolToBoogie/TranslatorContext.cs index 32ced0c3..eabdf451 100644 --- a/Sources/SolToBoogie/TranslatorContext.cs +++ b/Sources/SolToBoogie/TranslatorContext.cs @@ -116,6 +116,8 @@ public class TranslatorContext // data structures for using // maps Contract C --> (source, dest), where source is a library type private readonly Dictionary>> usingMap; + + public HashSet initFns { get; set; } public TranslatorContext(AST solidityAST, HashSet> ignoreMethods, bool _genInlineAttrInBpl, TranslatorFlags _translateFlags = null, String entryPointContract = "") { @@ -150,6 +152,7 @@ public TranslatorContext(AST solidityAST, HashSet> ignoreM TranslateFlags = _translateFlags; EntryPointContract = entryPointContract; Analysis = new SolidityAnalyzer(solidityAST, ignoreMethods, entryPointContract); + initFns = new HashSet(); } public bool HasASTNodeId(int id) From 65a41bd7c116d583e12972142d46a45879ad4ccd Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Wed, 11 Nov 2020 19:32:11 -0700 Subject: [PATCH 44/49] Fixing a bug when determining if a public getter --- Sources/SolToBoogie/ProcedureTranslator.cs | 5 +++++ Sources/SolToBoogie/TranslatorContext.cs | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index f42f7db1..133f83db 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -3826,6 +3826,11 @@ private bool IsGetterForPublicVariable(FunctionCall node, out VariableDeclaratio contractDefinition = context.GetContractByName(contractTypeStr.Substring("contract ".Length)); VeriSolAssert(contractDefinition != null, $"Expecting a contract {contractTypeStr} to exist in context"); + if (!context.HasStateVar(memberAccess.MemberName, contractDefinition)) + { + return false; + } + var = context.GetStateVarByDynamicType(memberAccess.MemberName, contractDefinition); return true; } diff --git a/Sources/SolToBoogie/TranslatorContext.cs b/Sources/SolToBoogie/TranslatorContext.cs index eabdf451..9f6c5a21 100644 --- a/Sources/SolToBoogie/TranslatorContext.cs +++ b/Sources/SolToBoogie/TranslatorContext.cs @@ -480,6 +480,12 @@ public bool HasStateVarName(string varName) return StateVarNameResolutionMap.ContainsKey(varName); } + public bool HasStateVar(string varName, ContractDefinition dynamicType) + { + return StateVarNameResolutionMap.ContainsKey(varName) && + StateVarNameResolutionMap[varName].ContainsKey(dynamicType); + } + public VariableDeclaration GetStateVarByDynamicType(string varName, ContractDefinition dynamicType) { return StateVarNameResolutionMap[varName][dynamicType]; From 9aec6414170573bdf3d2e64d555d195a890b8949 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Wed, 11 Nov 2020 20:18:24 -0700 Subject: [PATCH 45/49] Fixing a bug with public getters. Essentially verisol didn't support public getters for mappings/arrays as it didn't take arguments into account. Have a workaround but only if generating the getters --- Sources/SolToBoogie/FunctionEventCollector.cs | 2 +- Sources/SolToBoogie/ProcedureTranslator.cs | 5 +++-- Sources/SolToBoogie/TransUtils.cs | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Sources/SolToBoogie/FunctionEventCollector.cs b/Sources/SolToBoogie/FunctionEventCollector.cs index cb5d5308..e00ac2e5 100644 --- a/Sources/SolToBoogie/FunctionEventCollector.cs +++ b/Sources/SolToBoogie/FunctionEventCollector.cs @@ -107,7 +107,7 @@ public FunctionDefinition GenerateGetter(VariableDeclaration varDecl) else if (curType is ArrayTypeName arr) { TypeDescription intDescription = new TypeDescription(); - intDescription.TypeString = "uint"; + intDescription.TypeString = "uint256"; ElementaryTypeName intTypeName = new ElementaryTypeName(); intTypeName.TypeDescriptions = intDescription; paramDecl.TypeName = intTypeName; diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index 133f83db..b0f3c0bb 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -3707,8 +3707,9 @@ private void TranslateDynamicDispatchCall(FunctionCall node, List subtypes = new List(context.GetSubTypesOfContract(contractDefn)); Debug.Assert(subtypes.Count > 0); @@ -3732,7 +3733,7 @@ private void TranslateDynamicDispatchCall(FunctionCall node, List dynamicTypeToFuncMap; - string signature = TransUtils.InferFunctionSignature(context, node); + string signature = TransUtils.InferFunctionSignature(context, node, IsGetterForPublicVariable(node, out varDecl, out contractDefn)); VeriSolAssert(context.HasFuncSignature(signature), $"Cannot find a function with signature: {signature}"); dynamicTypeToFuncMap = context.GetAllFuncDefinitions(signature); VeriSolAssert(dynamicTypeToFuncMap.Count > 0); diff --git a/Sources/SolToBoogie/TransUtils.cs b/Sources/SolToBoogie/TransUtils.cs index 75460d7f..185a7853 100644 --- a/Sources/SolToBoogie/TransUtils.cs +++ b/Sources/SolToBoogie/TransUtils.cs @@ -557,11 +557,12 @@ public static string ComputeEventSignature(EventDefinition eventDef) return builder.ToString(); } - public static string InferFunctionSignature(TranslatorContext context, FunctionCall node) + public static string InferFunctionSignature(TranslatorContext context, FunctionCall node, + bool forceNameLookup = false) { Debug.Assert(node.Arguments != null); - if (node.Expression is MemberAccess memberAccess) + if (!forceNameLookup && node.Expression is MemberAccess memberAccess) { Debug.Assert(memberAccess.ReferencedDeclaration != null); FunctionDefinition function = context.GetASTNodeById(memberAccess.ReferencedDeclaration.Value) as FunctionDefinition; From d7cc8e085009fe473c22a5a12fb896b59de4cf92 Mon Sep 17 00:00:00 2001 From: stephensj2 Date: Fri, 11 Dec 2020 10:44:23 -0700 Subject: [PATCH 46/49] Fixing an issue with payable functions --- Sources/SolToBoogie/FallbackGenerator.cs | 17 +++- Sources/SolToBoogie/HarnessGenerator.cs | 16 +++- Sources/SolToBoogie/ProcedureTranslator.cs | 29 +++++-- Sources/SolToBoogie/RevertLogicGenerator.cs | 90 ++++++++++++++++++++- Sources/SolToBoogie/TransUtils.cs | 22 ++++- 5 files changed, 157 insertions(+), 17 deletions(-) diff --git a/Sources/SolToBoogie/FallbackGenerator.cs b/Sources/SolToBoogie/FallbackGenerator.cs index 112f3da2..d8aade54 100644 --- a/Sources/SolToBoogie/FallbackGenerator.cs +++ b/Sources/SolToBoogie/FallbackGenerator.cs @@ -103,6 +103,17 @@ private BoogieStmtList CreateBodyOfSend(List inParams, List fbLocalV "CreateBodyOfUnknownFallback called in unexpected context"); var procBody = new BoogieStmtList(); - procBody.AddStatement(new BoogieCommentCmd("---- Logic for payable function START ")); + /*procBody.AddStatement(new BoogieCommentCmd("---- Logic for payable function START ")); var balnSender = new BoogieMapSelect(new BoogieIdentifierExpr("Balance"), new BoogieIdentifierExpr(inParams[0].Name)); var balnThis = new BoogieMapSelect(new BoogieIdentifierExpr("Balance"), new BoogieIdentifierExpr(inParams[1].Name)); var msgVal = new BoogieIdentifierExpr("amount"); @@ -143,7 +154,7 @@ private BoogieStmtList CreateBodyOfUnknownFallback(List fbLocalV procBody.AddStatement(new BoogieAssignCmd(balnSender, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, balnSender, msgVal))); //balance[to] = balance[to] + msg.value procBody.AddStatement(new BoogieAssignCmd(balnThis, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, balnThis, msgVal))); - procBody.AddStatement(new BoogieCommentCmd("---- Logic for payable function END ")); + procBody.AddStatement(new BoogieCommentCmd("---- Logic for payable function END "));*/ BoogieStmtList body = procBody; if (context.TranslateFlags.ModelStubsAsCallbacks() || @@ -196,7 +207,7 @@ private BoogieStmtList CreateBodyOfUnknownFallback(List fbLocalV foreach (ContractDefinition curDef in context.ContractDefinitions.ToList()) { BoogieIfCmd reentrantCalls = TransUtils.GenerateChoiceBlock(new List() {curDef}, - context, Tuple.Create(inParams[0].Name, inParams[1].Name)); + context, false, Tuple.Create(inParams[0].Name, inParams[1].Name)); if (reentrantCalls == null) { diff --git a/Sources/SolToBoogie/HarnessGenerator.cs b/Sources/SolToBoogie/HarnessGenerator.cs index c3a83fe9..6a27bf59 100644 --- a/Sources/SolToBoogie/HarnessGenerator.cs +++ b/Sources/SolToBoogie/HarnessGenerator.cs @@ -168,6 +168,18 @@ private List GenerateConstructorCall(ContractDefinition contract) BoogieExpr assumeExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, new BoogieIdentifierExpr("msgvalue_MSG"), new BoogieLiteralExpr(BigInteger.Zero)); localStmtList.Add(new BoogieAssumeCmd(assumeExpr)); + + localStmtList.Add(new BoogieCommentCmd("---- Logic for payable function START ")); + var balnSender = new BoogieMapSelect(new BoogieIdentifierExpr("Balance"), new BoogieIdentifierExpr("msgsender_MSG")); + var balnThis = new BoogieMapSelect(new BoogieIdentifierExpr("Balance"), new BoogieIdentifierExpr("this")); + var msgVal = new BoogieIdentifierExpr("msgvalue_MSG"); + //assume Balance[msg.sender] >= msg.value + localStmtList.Add(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, balnSender, msgVal))); + //balance[msg.sender] = balance[msg.sender] - msg.value + localStmtList.Add(new BoogieAssignCmd(balnSender, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, balnSender, msgVal))); + //balance[this] = balance[this] + msg.value + localStmtList.Add(new BoogieAssignCmd(balnThis, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, balnThis, msgVal))); + localStmtList.Add(new BoogieCommentCmd("---- Logic for payable function END ")); } else { @@ -327,7 +339,7 @@ private List CollectLocalVars(ContractDefinition contract) private BoogieIfCmd GenerateChoices(ContractDefinition contract) { BoogieExpr thisVal = new BoogieIdentifierExpr("this"); - Tuple curChoices = TransUtils.GeneratePartialChoiceBlock(new List() {contract}, context, thisVal, 0); + Tuple curChoices = TransUtils.GeneratePartialChoiceBlock(new List() {contract}, context, thisVal, 0, context.TranslateFlags.ModelReverts); if (context.TranslateFlags.TxnsOnFields) { HashSet contractFields = context.GetStateVarsByContract(contract); @@ -340,7 +352,7 @@ private BoogieIfCmd GenerateChoices(ContractDefinition contract) String fieldContractName = contractField.TypeName.ToString(); ContractDefinition fieldDef = context.GetContractByName(fieldContractName); curChoices = TransUtils.GeneratePartialChoiceBlock(new List() {fieldDef}, - context, fieldInstance, curChoices.Item2, curChoices.Item1); + context, fieldInstance, curChoices.Item2, context.TranslateFlags.ModelReverts, curChoices.Item1); } } } diff --git a/Sources/SolToBoogie/ProcedureTranslator.cs b/Sources/SolToBoogie/ProcedureTranslator.cs index b0f3c0bb..be39d939 100644 --- a/Sources/SolToBoogie/ProcedureTranslator.cs +++ b/Sources/SolToBoogie/ProcedureTranslator.cs @@ -514,6 +514,12 @@ public override bool Visit(FunctionDefinition node) { attributes.Add(new BoogieAttribute("public")); } + + if (node.StateMutability == EnumStateMutability.PAYABLE) + { + attributes.Add(new BoogieAttribute("payable")); + } + // generate inline attribute for a function only when /noInlineAttrs is specified if (genInlineAttrsInBpl) attributes.Add(new BoogieAttribute("inline", 1)); @@ -554,7 +560,7 @@ public override bool Visit(FunctionDefinition node) procBody.AppendStmtList(assumesForParamsAndReturn); // if payable, then modify the balance - if (node.StateMutability == EnumStateMutability.PAYABLE) + /*if (node.StateMutability == EnumStateMutability.PAYABLE) { procBody.AddStatement(new BoogieCommentCmd("---- Logic for payable function START ")); var balnSender = new BoogieMapSelect(new BoogieIdentifierExpr("Balance"), new BoogieIdentifierExpr("msgsender_MSG")); @@ -567,7 +573,7 @@ public override bool Visit(FunctionDefinition node) //balance[this] = balance[this] + msg.value procBody.AddStatement(new BoogieAssignCmd(balnThis, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, balnThis, msgVal))); procBody.AddStatement(new BoogieCommentCmd("---- Logic for payable function END ")); - } + }*/ // if (node.Modifiers.Count == 1) for (int i = 0; i < node.Modifiers.Count; ++i) @@ -783,11 +789,11 @@ private BoogieStmtList GenerateInitializationStmts(ContractDefinition contract) BoogieExpr assumeExpr = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.NEQ, assumeLhs, new BoogieIdentifierExpr("null")); BoogieAssumeCmd assumeCmd = new BoogieAssumeCmd(assumeExpr); currentStmtList.AddStatement(assumeCmd); - + BoogieAssignCmd balanceInit = new BoogieAssignCmd( new BoogieMapSelect(new BoogieIdentifierExpr("Balance"), new BoogieIdentifierExpr("this")), - new BoogieLiteralExpr(0)); + new BoogieIdentifierExpr("msgvalue_MSG")); currentStmtList.AddStatement(balanceInit); foreach (VariableDeclaration varDecl in context.GetStateVarsByContract(contract)) @@ -1468,7 +1474,7 @@ private void GenerateDefaultConstructor(ContractDefinition contract) }; BoogieProcedure procedure = new BoogieProcedure(procName, inParams, outParams, attributes); context.Program.AddDeclaration(procedure); - + BoogieStmtList procBody = GenerateInitializationStmts(contract); List localVars = boogieToLocalVarsMap[currentBoogieProc]; BoogieImplementation implementation = new BoogieImplementation(procName, inParams, outParams, localVars, procBody); @@ -3567,7 +3573,18 @@ private void TranslateExternalFunctionCall(FunctionCall node, List= msg.value + currentStmtList.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, balnSender, msgValueExpr))); + //balance[msg.sender] = balance[msg.sender] - msg.value + currentStmtList.AddStatement(new BoogieAssignCmd(balnSender, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, balnSender, msgValueExpr))); + //balance[this] = balance[this] + msg.value + currentStmtList.AddStatement(new BoogieAssignCmd(balnThis, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, balnThis, msgValueExpr))); + currentStmtList.AddStatement(new BoogieCommentCmd("---- Logic for payable function END ")); } else { diff --git a/Sources/SolToBoogie/RevertLogicGenerator.cs b/Sources/SolToBoogie/RevertLogicGenerator.cs index ecdcecde..5fbbba04 100644 --- a/Sources/SolToBoogie/RevertLogicGenerator.cs +++ b/Sources/SolToBoogie/RevertLogicGenerator.cs @@ -37,6 +37,21 @@ private bool isPublic(BoogieProcedure proc) return false; } + private bool isPayable(BoogieProcedure proc) + { + //return true; + if (proc.Attributes != null) + { + foreach (var attr in proc.Attributes) + { + if (attr.Key.Equals("payable")) + return true; + } + } + + return false; + } + private BoogieProcedure duplicateProcedure(BoogieProcedure proc, string newNameSuffix, bool dropPublic = false) { BoogieProcedure dup = new BoogieProcedure(proc.Name + "__" + newNameSuffix, @@ -402,6 +417,20 @@ public void Generate() // Call Successful version. BoogieStmtList successCallStmtList = new BoogieStmtList(); + if (isPayable(proc)) + { + successCallStmtList.AddStatement(new BoogieCommentCmd("---- Logic for payable function START ")); + var balnSender = new BoogieMapSelect(new BoogieIdentifierExpr("Balance"), new BoogieIdentifierExpr("msgsender_MSG")); + var balnThis = new BoogieMapSelect(new BoogieIdentifierExpr("Balance"), new BoogieIdentifierExpr("this")); + var msgVal = new BoogieIdentifierExpr("msgvalue_MSG"); + //assume Balance[msg.sender] >= msg.value + successCallStmtList.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, balnSender, msgVal))); + //balance[msg.sender] = balance[msg.sender] - msg.value + successCallStmtList.AddStatement(new BoogieAssignCmd(balnSender, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, balnSender, msgVal))); + //balance[this] = balance[this] + msg.value + successCallStmtList.AddStatement(new BoogieAssignCmd(balnThis, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, balnThis, msgVal))); + successCallStmtList.AddStatement(new BoogieCommentCmd("---- Logic for payable function END ")); + } successCallStmtList.AddStatement(new BoogieCallCmd(impl.Name + "__success", impl.InParams.Select(inParam => (BoogieExpr)new BoogieIdentifierExpr(inParam.Name)).ToList(), impl.OutParams.Select(outParam => new BoogieIdentifierExpr(outParam.Name)).ToList())); @@ -422,6 +451,21 @@ public void Generate() string shadowName = shadowGlobalPair.Value.Name; failCallStmtList.AddStatement(new BoogieAssignCmd(new BoogieIdentifierExpr(shadowName), new BoogieIdentifierExpr(origVarName))); } + + if (isPayable(proc)) + { + failCallStmtList.AddStatement(new BoogieCommentCmd("---- Logic for payable function START ")); + var balnSender = new BoogieMapSelect(new BoogieIdentifierExpr(shadowGlobals["Balance"].Name), new BoogieIdentifierExpr("msgsender_MSG")); + var balnThis = new BoogieMapSelect(new BoogieIdentifierExpr(shadowGlobals["Balance"].Name), new BoogieIdentifierExpr("this")); + var msgVal = new BoogieIdentifierExpr("msgvalue_MSG"); + //assume Balance[msg.sender] >= msg.value + failCallStmtList.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, balnSender, msgVal))); + //balance[msg.sender] = balance[msg.sender] - msg.value + failCallStmtList.AddStatement(new BoogieAssignCmd(balnSender, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, balnSender, msgVal))); + //balance[this] = balance[this] + msg.value + failCallStmtList.AddStatement(new BoogieAssignCmd(balnThis, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, balnThis, msgVal))); + failCallStmtList.AddStatement(new BoogieCommentCmd("---- Logic for payable function END ")); + } failCallStmtList.AddStatement(new BoogieCallCmd(impl.Name + "__fail", impl.InParams.Select(inParam => (BoogieExpr)new BoogieIdentifierExpr(inParam.Name)).ToList(), impl.OutParams.Select(outParam => new BoogieIdentifierExpr(outParam.Name)).ToList())); @@ -516,8 +560,20 @@ private BoogieImplementation CreateSendFail() exceptionCase.AddStatement(new BoogieAssignCmd(new BoogieIdentifierExpr(tmpLocalName), new BoogieIdentifierExpr(shadowName))); } - - exceptionCase.AddStatement(new BoogieIfCmd(checkTmpBalGuard, BoogieStmtList.MakeSingletonStmtList(callFailDispatch), null)); + + BoogieStmtList exStmtList = new BoogieStmtList(); + exStmtList.AddStatement(new BoogieCommentCmd("---- Logic for payable function START ")); + var exBalFrom = new BoogieMapSelect(new BoogieIdentifierExpr(shadowGlobals["Balance"].Name), new BoogieIdentifierExpr(inParams[0].Name)); + var exBalTo = new BoogieMapSelect(new BoogieIdentifierExpr(shadowGlobals["Balance"].Name), new BoogieIdentifierExpr(inParams[1].Name)); + var exMsgVal = new BoogieIdentifierExpr(inParams[2].Name); + //balance[msg.sender] = balance[msg.sender] - msg.value + exStmtList.AddStatement(new BoogieAssignCmd(exBalFrom, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, exBalFrom, exMsgVal))); + //balance[this] = balance[this] + msg.value + exStmtList.AddStatement(new BoogieAssignCmd(exBalTo, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, exBalTo, exMsgVal))); + exStmtList.AddStatement(new BoogieCommentCmd("---- Logic for payable function END ")); + exStmtList.AddStatement(callFailDispatch); + + exceptionCase.AddStatement(new BoogieIfCmd(checkTmpBalGuard, exStmtList, null)); exceptionCase.AddStatement(new BoogieAssignCmd(successId, new BoogieLiteralExpr(false))); BoogieExpr failAssumePred = revertId; if (context.TranslateFlags.InstrumentGas) @@ -540,6 +596,12 @@ private BoogieImplementation CreateSendFail() var successCase = new BoogieStmtList(); var successDispatchCall = new BoogieStmtList(); + successDispatchCall.AddStatement(new BoogieCommentCmd("---- Logic for payable function START ")); + //balance[msg.sender] = balance[msg.sender] - msg.value + successDispatchCall.AddStatement(new BoogieAssignCmd(exBalFrom, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, exBalFrom, exMsgVal))); + //balance[this] = balance[this] + msg.value + successDispatchCall.AddStatement(new BoogieAssignCmd(exBalTo, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, exBalTo, exMsgVal))); + successDispatchCall.AddStatement(new BoogieCommentCmd("---- Logic for payable function END ")); successDispatchCall.AddStatement(callFailDispatch); successDispatchCall.AddStatement(new BoogieAssignCmd(successId, new BoogieLiteralExpr(true))); @@ -632,9 +694,22 @@ private BoogieImplementation CreateSendSucess() var checkTmpBalGuard = new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, new BoogieMapSelect(new BoogieIdentifierExpr(shadowGlobals["Balance"].Name), fromId), amtId); + + + BoogieStmtList exceptionThen = new BoogieStmtList(); + exceptionThen.AddStatement(new BoogieCommentCmd("---- Logic for payable function START ")); + var exBalFrom = new BoogieMapSelect(new BoogieIdentifierExpr(shadowGlobals["Balance"].Name), new BoogieIdentifierExpr(inParams[0].Name)); + var exBalTo = new BoogieMapSelect(new BoogieIdentifierExpr(shadowGlobals["Balance"].Name), new BoogieIdentifierExpr(inParams[1].Name)); + var exMsgVal = new BoogieIdentifierExpr(inParams[2].Name); + //balance[msg.sender] = balance[msg.sender] - msg.value + exceptionThen.AddStatement(new BoogieAssignCmd(exBalFrom, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, exBalFrom, exMsgVal))); + //balance[this] = balance[this] + msg.value + exceptionThen.AddStatement(new BoogieAssignCmd(exBalTo, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, exBalTo, exMsgVal))); + exceptionThen.AddStatement(new BoogieCommentCmd("---- Logic for payable function END ")); var callFailDispatch = new BoogieCallCmd("FallbackDispatch__fail", new List() {fromId, toId, amtId}, null); + exceptionThen.AddStatement(callFailDispatch); - exceptionCase.AddStatement(new BoogieIfCmd(checkTmpBalGuard, BoogieStmtList.MakeSingletonStmtList(callFailDispatch), null)); + exceptionCase.AddStatement(new BoogieIfCmd(checkTmpBalGuard, exceptionThen, null)); exceptionCase.AddStatement(new BoogieAssignCmd(successId, new BoogieLiteralExpr(false))); @@ -654,6 +729,15 @@ private BoogieImplementation CreateSendSucess() amtId); var successCaseStmts = new BoogieStmtList(); + successCaseStmts.AddStatement(new BoogieCommentCmd("---- Logic for payable function START ")); + var balFrom = new BoogieMapSelect(new BoogieIdentifierExpr("Balance"), new BoogieIdentifierExpr(inParams[0].Name)); + var balTo = new BoogieMapSelect(new BoogieIdentifierExpr("Balance"), new BoogieIdentifierExpr(inParams[1].Name)); + var msgVal = new BoogieIdentifierExpr(inParams[2].Name); + //balance[msg.sender] = balance[msg.sender] - msg.value + successCaseStmts.AddStatement(new BoogieAssignCmd(balFrom, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, balFrom, msgVal))); + //balance[this] = balance[this] + msg.value + successCaseStmts.AddStatement(new BoogieAssignCmd(balTo, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, balTo, msgVal))); + successCaseStmts.AddStatement(new BoogieCommentCmd("---- Logic for payable function END ")); var callSuccessDispatch = new BoogieCallCmd("FallbackDispatch__success", new List(){fromId, toId, amtId}, null); successCaseStmts.AddStatement(callSuccessDispatch); successCaseStmts.AddStatement(new BoogieAssignCmd(successId, new BoogieLiteralExpr(true))); diff --git a/Sources/SolToBoogie/TransUtils.cs b/Sources/SolToBoogie/TransUtils.cs index 185a7853..2720b56f 100644 --- a/Sources/SolToBoogie/TransUtils.cs +++ b/Sources/SolToBoogie/TransUtils.cs @@ -811,12 +811,12 @@ public static List CollectLocalVars(List con /// /// If non-null, this will be the msg.sender for calling back into the contracts /// - public static BoogieIfCmd GenerateChoiceBlock(List contracts, TranslatorContext context, Tuple callBackTarget = null) + public static BoogieIfCmd GenerateChoiceBlock(List contracts, TranslatorContext context, bool canRevert, Tuple callBackTarget = null) { - return GeneratePartialChoiceBlock(contracts, context, new BoogieIdentifierExpr("this"), 0, null, callBackTarget).Item1; + return GeneratePartialChoiceBlock(contracts, context, new BoogieIdentifierExpr("this"), 0, canRevert, null, callBackTarget).Item1; } - public static Tuple GeneratePartialChoiceBlock(List contracts, TranslatorContext context, BoogieExpr thisExpr, int startingPoint, BoogieIfCmd alternatives = null, Tuple callBackTarget = null) + public static Tuple GeneratePartialChoiceBlock(List contracts, TranslatorContext context, BoogieExpr thisExpr, int startingPoint, bool canRevert, BoogieIfCmd alternatives = null, Tuple callBackTarget = null) { BoogieIfCmd ifCmd = alternatives; int j = startingPoint; @@ -900,6 +900,22 @@ public static Tuple GeneratePartialChoiceBlock(List= msg.value + thenBody.AddStatement(new BoogieAssumeCmd(new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.GE, balnSender, msgVal))); + //balance[msg.sender] = balance[msg.sender] - msg.value + thenBody.AddStatement(new BoogieAssignCmd(balnSender, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.SUB, balnSender, msgVal))); + //balance[this] = balance[this] + msg.value + thenBody.AddStatement(new BoogieAssignCmd(balnThis, new BoogieBinaryOperation(BoogieBinaryOperation.Opcode.ADD, balnThis, msgVal))); + thenBody.AddStatement(new BoogieCommentCmd("---- Logic for payable function END ")); + } + } else { From 1336bb0daa2c7d58ae4174b4f53ca04d414cd9dd Mon Sep 17 00:00:00 2001 From: stephensj2 <9577789+stephensj2@users.noreply.github.com> Date: Tue, 1 Jun 2021 11:15:20 -0700 Subject: [PATCH 47/49] Update INSTALL.md --- INSTALL.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index efda7f44..428b98a1 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -6,12 +6,6 @@ The following are dynamically installed at VeriSol runtime (the first time only) - Follow either **Nuget** package installation directly, or after building the **sources**. -### Install from nuget.org -Install to the **global** dotnet CLI tools cache so that you can run command `VeriSol` from anywhere: -``` -dotnet tool install VeriSol --version 0.1.1-alpha --global -``` - ### Install from sources Perform the following set of commands: From c3d8db784530a65fcd87052af22fa43b170b4bfc Mon Sep 17 00:00:00 2001 From: stephensj2 <9577789+stephensj2@users.noreply.github.com> Date: Tue, 1 Jun 2021 11:17:19 -0700 Subject: [PATCH 48/49] Update INSTALL.md --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index 428b98a1..826a4d30 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -10,7 +10,7 @@ The following are dynamically installed at VeriSol runtime (the first time only) Perform the following set of commands: ``` -git clone https://github.com/microsoft/verisol.git +git clone https://github.com/utopia-group/verisol.git ``` From %VERISOL_PATH% (the root folder of the repository), perform From 4f3a57ef4c67be047a3e34ac717f1f33f6976578 Mon Sep 17 00:00:00 2001 From: stephensj2 <9577789+stephensj2@users.noreply.github.com> Date: Tue, 8 Jun 2021 11:21:31 -0700 Subject: [PATCH 49/49] Create resetInstallwsl.sh --- resetInstallwsl.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 resetInstallwsl.sh diff --git a/resetInstallwsl.sh b/resetInstallwsl.sh new file mode 100644 index 00000000..dcb03f32 --- /dev/null +++ b/resetInstallwsl.sh @@ -0,0 +1,5 @@ +dotnet.exe tool uninstall --global VeriSol +dotnet.exe tool uninstall --global SolToBoogieTest +dotnet.exe build Sources/VeriSol.sln +dotnet.exe tool install VeriSol --version 0.1.1-alpha --global --add-source $(wslpath -w ./nupkg/) +dotnet.exe tool install --global SolToBoogieTest --version 0.1.1-alpha --add-source $(wslpath -w ./nupkg/)